diff --git a/.github/workflows/build-and-tests.yml b/.github/workflows/build-and-tests.yml index 7ee8730c68e..842d047fed5 100644 --- a/.github/workflows/build-and-tests.yml +++ b/.github/workflows/build-and-tests.yml @@ -51,7 +51,7 @@ jobs: - name: Lint run: npm run ci:lint - name: Vulnerabilities - run: npm audit + run: npm audit --audit-level moderate - name: Optional Dependencies run: npm run test:package - name: CLI Docs diff --git a/CHANGELOG.md b/CHANGELOG.md index cb81c72371d..968c5076dcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # rollup changelog +## 4.43.0 + +_2025-06-11_ + +### Features + +- Provide new `fs` option and `this.fs` API to replace file system (#5944) + +### Pull Requests + +- [#5944](https://github.com/rollup/rollup/pull/5944): feat(options): Add an option for overriding the file system module in the JS API (@EggDice, @lukastaegert) + ## 4.42.0 _2025-06-06_ diff --git a/browser/package.json b/browser/package.json index a0457d6f846..d21e7e22968 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.42.0", + "version": "4.43.0", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/browser/src/fs.ts b/browser/src/fs.ts index 290663f7457..0dff3bf6cf8 100644 --- a/browser/src/fs.ts +++ b/browser/src/fs.ts @@ -1,5 +1,20 @@ +import type { RollupFsModule } from '../../src/rollup/types'; import { throwNoFileSystem } from './error'; +import type * as FsType from './fs.ts'; +// Ensure this satisfies the RollupFsModule API, will be removed by tree-shaking +const _typeTest = null as unknown as typeof FsType satisfies RollupFsModule; + +export const appendFile = throwNoFileSystem('fs.appendFile'); +export const copyFile = throwNoFileSystem('fs.copyFile'); export const mkdir = throwNoFileSystem('fs.mkdir'); +export const mkdtemp = throwNoFileSystem('fs.mkdtemp'); +export const readdir = throwNoFileSystem('fs.readdir'); export const readFile = throwNoFileSystem('fs.readFile'); +export const realpath = throwNoFileSystem('fs.realpath'); +export const rename = throwNoFileSystem('fs.rename'); +export const rmdir = throwNoFileSystem('fs.rmdir'); +export const stat = throwNoFileSystem('fs.stat'); +export const lstat = throwNoFileSystem('fs.lstat'); +export const unlink = throwNoFileSystem('fs.unlink'); export const writeFile = throwNoFileSystem('fs.writeFile'); diff --git a/browser/src/resolveId.ts b/browser/src/resolveId.ts deleted file mode 100644 index fb0d2337832..00000000000 --- a/browser/src/resolveId.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { ModuleLoaderResolveId } from '../../src/ModuleLoader'; -import type { CustomPluginOptions, Plugin, ResolveIdResult } from '../../src/rollup/types'; -import type { PluginDriver } from '../../src/utils/PluginDriver'; -import { resolveIdViaPlugins } from '../../src/utils/resolveIdViaPlugins'; -import { throwNoFileSystem } from './error'; - -export async function resolveId( - source: string, - importer: string | undefined, - _preserveSymlinks: boolean, - pluginDriver: PluginDriver, - moduleLoaderResolveId: ModuleLoaderResolveId, - skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null, - customOptions: CustomPluginOptions | undefined, - isEntry: boolean, - assertions: Record -): Promise { - const pluginResult = await resolveIdViaPlugins( - source, - importer, - pluginDriver, - moduleLoaderResolveId, - skip, - customOptions, - isEntry, - assertions - ); - if (pluginResult == null) { - return throwNoFileSystem('path.resolve')(); - } - return pluginResult[0]; -} diff --git a/build-plugins/replace-browser-modules.ts b/build-plugins/replace-browser-modules.ts index f1cecf2d18e..84a59f85ad2 100644 --- a/build-plugins/replace-browser-modules.ts +++ b/build-plugins/replace-browser-modules.ts @@ -5,15 +5,7 @@ import type { Plugin } from 'vite'; const resolve = (path: string) => fileURLToPath(new URL(`../${path}`, import.meta.url)); -const JS_REPLACED_MODULES = [ - 'fs', - 'hookActions', - 'path', - 'performance', - 'process', - 'resolveId', - 'initWasm' -]; +const JS_REPLACED_MODULES = ['fs', 'hookActions', 'path', 'performance', 'process', 'initWasm']; type ModulesMap = [string, string][]; diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index b2f2ea58ff2..3479625f337 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -117,6 +117,10 @@ export default defineConfig({ link: '/es-module-syntax/', text: 'ES Module Syntax' }, + { + link: '/browser/', + text: 'Running Rollup in a Browser' + }, { link: '/faqs/', text: 'Frequently Asked Questions' diff --git a/docs/browser/index.md b/docs/browser/index.md new file mode 100755 index 00000000000..ac5b61758ab --- /dev/null +++ b/docs/browser/index.md @@ -0,0 +1,134 @@ +--- +title: Running Rollup in a Browser +--- + +# {{ $frontmatter.title }} + +[[toc]] + +## The browser build + +While the regular Rollup build relies on some NodeJS builtin libraries, there is also a browser build available that only uses browser APIs. You can install it via + +```shell +npm install @rollup/browser +``` + +and in your script, import it via + +```js +import { rollup } from '@rollup/browser'; +``` + +Alternatively, you can import from a CDN, e.g. for the ESM build + +```js +import * as rollup from 'https://unpkg.com/@rollup/browser/dist/es/rollup.browser.js'; +``` + +and for the UMD build + +```html + +``` + +which will create a global variable `window.rollup`. Note that in each case, you need to make sure that the file `dist/bindings_wasm_bg.wasm` from the `@rollup/browser` package is served next to where the browser build is served. + +As the browser build cannot access the file system, you either need to provide an [in-memory file system](#using-an-in-memory-file-system) via the [`fs`](../configuration-options/index.md#fs) option, or you need to [provide plugins](#using-plugins-to-resolve-and-load-modules) that resolve and load all modules you want to bundle. + +## Using an in-memory file system + +Rollup allows you to provide an in-memory file system implementation that needs to implement at least a certain sub-set of the NodeJS `fs` API, cf. the [`fs`](../configuration-options/index.md#fs) option. This makes the browser build behave very similar to the NodeJS build and even allows you to use certain plugins that rely on the file system, provided they only access it via the [`this.fs`](../plugin-development/index.md#this-fs) plugin context property. Here is an example that uses [`memfs`](https://www.npmjs.com/package/memfs): + +```js twoslash +/** @type {import('rollup')} */ +var rollup; +// ---cut--- +import { rollup } from '@rollup/browser'; +import { Volume } from 'memfs'; + +const vol = Volume.fromJSON({ + '/main.js': "import foo from 'foo.js'; console.log(foo);", + '/foo.js': 'export default 42;' +}); + +rollup + .rollup({ + input: '/main.js', + fs: vol.promises + }) + .then(bundle => bundle.generate({ format: 'es' })) + .then(({ output }) => console.log(output[0].code)); +``` + +## Using plugins to resolve and load modules + +You can also resolve and load all modules via plugins. Here is how you could do this: + +```js twoslash +/** @type {import('rollup')} */ +var rollup; +// ---cut--- +const modules = { + 'main.js': "import foo from 'foo.js'; console.log(foo);", + 'foo.js': 'export default 42;' +}; + +rollup + .rollup({ + input: 'main.js', + plugins: [ + { + name: 'loader', + resolveId(source) { + if (modules.hasOwnProperty(source)) { + return source; + } + }, + load(id) { + if (modules.hasOwnProperty(id)) { + return modules[id]; + } + } + } + ] + }) + .then(bundle => bundle.generate({ format: 'es' })) + .then(({ output }) => console.log(output[0].code)); +``` + +This example only supports two imports, `"main.js"` and `"foo.js"`, and no relative imports. Here is another example that uses absolute URLs as entry points and supports relative imports. In that case, we are just re-bundling Rollup itself, but it could be used on any other URL that exposes an ES module: + +```js twoslash +/** @type {import('rollup')} */ +var rollup; +// ---cut--- +rollup + .rollup({ + input: 'https://unpkg.com/rollup/dist/es/rollup.js', + plugins: [ + { + name: 'url-resolver', + resolveId(source, importer) { + if (source[0] !== '.') { + try { + new URL(source); + // If it is a valid URL, return it + return source; + } catch { + // Otherwise make it external + return { id: source, external: true }; + } + } + return new URL(source, importer).href; + }, + async load(id) { + const response = await fetch(id); + return response.text(); + } + } + ] + }) + .then(bundle => bundle.generate({ format: 'es' })) + .then(({ output }) => console.log(output)); +``` diff --git a/docs/configuration-options/index.md b/docs/configuration-options/index.md index 96e4d94a8b0..edc3b05d678 100755 --- a/docs/configuration-options/index.md +++ b/docs/configuration-options/index.md @@ -2851,6 +2851,107 @@ Whether to collect performance timings. When used from the command line or a con For each key, the first number represents the elapsed time while the second represents the change in memory consumption, and the third represents the total memory consumption after this step. The order of these steps is the order used by `Object.keys`. Top level keys start with `#` and contain the timings of nested steps, i.e. in the example above, the 698ms of the `# BUILD` step include the 538ms of the `## parse modules` step. +### fs + +| | | +| -------: | :--------------------------------------------------- | +| Type: | `RollupFsModule` | +| Default: | `node:fs.promises` in NodeJS, no default in browsers | + +If you want to use a custom file system module, you can set this option to an object that implements the same API as the `RollupFsModule` interface. This is useful if you want to use a different file system implementation such as [`memfs`](https://www.npmjs.com/package/memfs), if you want to mock the file system for testing purposes, or if you use the [browser build](../browser/index.md) of Rollup. + +```typescript +interface RollupFsModule { + appendFile( + path: string, + data: string | Uint8Array, + options?: { + encoding?: BufferEncoding | null; + mode?: string | number; + flag?: string | number; + } + ): Promise; + + copyFile( + source: string, + destination: string, + mode?: string | number + ): Promise; + + mkdir( + path: string, + options?: { recursive?: boolean; mode?: string | number } + ): Promise; + + mkdtemp(prefix: string): Promise; + + readdir( + path: string, + options?: { withFileTypes?: boolean } + ): Promise<(string | RollupDirectoryEntry)[]>; + + readFile( + path: string, + options?: { + encoding?: BufferEncoding | null; + flag?: string | number; + signal?: AbortSignal; + } + ): Promise; + + realpath(path: string): Promise; + + rename(oldPath: string, newPath: string): Promise; + + rmdir(path: string, options?: { recursive?: boolean }): Promise; + + stat(path: string): Promise; + + lstat(path: string): Promise; + + unlink(path: string): Promise; + + writeFile( + path: string, + data: string | ArrayBuffer | ArrayBufferView, + options?: { + encoding?: BufferEncoding | null; + mode?: string | number; + flag?: string | number; + } + ): Promise; +} + +type BufferEncoding = + | 'ascii' + | 'utf8' + | 'utf16le' + | 'ucs2' + | 'base64' + | 'base64url' + | 'latin1' + | 'binary' + | 'hex'; + +export interface RollupDirectoryEntry { + isFile(): boolean; + isDirectory(): boolean; + isSymbolicLink(): boolean; + name: string; +} + +interface RollupFileStats { + isFile(): boolean; + isDirectory(): boolean; + isSymbolicLink(): boolean; + size: number; + mtime: Date; + ctime: Date; + atime: Date; + birthtime: Date; +} +``` + ## watch | | | diff --git a/docs/faqs/index.md b/docs/faqs/index.md index 87f32463b95..23903116d7b 100755 --- a/docs/faqs/index.md +++ b/docs/faqs/index.md @@ -106,102 +106,6 @@ This is however a problem for polyfills, as those usually need to be executed fi Rollup is already used by many major JavaScript libraries, and can also be used to build the vast majority of applications. However, if you want to use code-splitting or dynamic imports with older browsers, you will need an additional runtime to handle loading missing chunks. We recommend using the [SystemJS Production Build](https://github.com/systemjs/systemjs#browser-production) as it integrates nicely with Rollup's system format output and is capable of properly handling all the ES module live bindings and re-export edge cases. Alternatively, an AMD loader can be used as well. -## How do I run Rollup itself in a browser - -While the regular Rollup build relies on some NodeJS features, there is also a browser build available that only uses browser APIs. You can install it via - -```shell -npm install @rollup/browser -``` - -and in your script, import it via - -```js -import { rollup } from '@rollup/browser'; -``` - -Alternatively, you can import from a CDN, e.g. for the ESM build - -```js -import * as rollup from 'https://unpkg.com/@rollup/browser/dist/es/rollup.browser.js'; -``` - -and for the UMD build - -```html - -``` - -which will create a global variable `window.rollup`. As the browser build cannot access the file system, you need to provide plugins that resolve and load all modules you want to bundle. Here is a contrived example that does this: - -```js twoslash -/** @type {import('rollup')} */ -var rollup; -// ---cut--- -const modules = { - 'main.js': "import foo from 'foo.js'; console.log(foo);", - 'foo.js': 'export default 42;' -}; - -rollup - .rollup({ - input: 'main.js', - plugins: [ - { - name: 'loader', - resolveId(source) { - if (modules.hasOwnProperty(source)) { - return source; - } - }, - load(id) { - if (modules.hasOwnProperty(id)) { - return modules[id]; - } - } - } - ] - }) - .then(bundle => bundle.generate({ format: 'es' })) - .then(({ output }) => console.log(output[0].code)); -``` - -This example only supports two imports, `"main.js"` and `"foo.js"`, and no relative imports. Here is another example that uses absolute URLs as entry points and supports relative imports. In that case, we are just re-bundling Rollup itself, but it could be used on any other URL that exposes an ES module: - -```js twoslash -/** @type {import('rollup')} */ -var rollup; -// ---cut--- -rollup - .rollup({ - input: 'https://unpkg.com/rollup/dist/es/rollup.js', - plugins: [ - { - name: 'url-resolver', - resolveId(source, importer) { - if (source[0] !== '.') { - try { - new URL(source); - // If it is a valid URL, return it - return source; - } catch { - // Otherwise make it external - return { id: source, external: true }; - } - } - return new URL(source, importer).href; - }, - async load(id) { - const response = await fetch(id); - return response.text(); - } - } - ] - }) - .then(bundle => bundle.generate({ format: 'es' })) - .then(({ output }) => console.log(output)); -``` - ## Who made the Rollup logo? It's lovely. [Julian Lloyd](https://github.com/jlmakes)! diff --git a/docs/guide/en/slugs-and-pages-by-legacy-slugs.json b/docs/guide/en/slugs-and-pages-by-legacy-slugs.json index 62071ac40dd..40bb9bffd07 100644 --- a/docs/guide/en/slugs-and-pages-by-legacy-slugs.json +++ b/docs/guide/en/slugs-and-pages-by-legacy-slugs.json @@ -1 +1 @@ -{"--bundleconfigascjs":["command-line-interface","bundleconfigascjs"],"--configplugin-plugin":["command-line-interface","configplugin-plugin"],"--environment-values":["command-line-interface","environment-values"],"--failafterwarnings":["command-line-interface","failafterwarnings"],"--no-stdin":["command-line-interface","no-stdin"],"--silent":["command-line-interface","silent"],"--stdinext":["command-line-interface","stdin-ext"],"--waitforbundleinput":["command-line-interface","waitforbundleinput"],"--watchonstart-cmd---watchonbundlestart-cmd---watchonbundleend-cmd---watchonend-cmd---watchonerror-cmd":["command-line-interface","watch-onstart-cmd-watch-onbundlestart-cmd-watch-onbundleend-cmd-watch-onend-cmd-watch-onerror-cmd"],"-h--help":["command-line-interface","h-help"],"-p-plugin---plugin-plugin":["command-line-interface","p-plugin-plugin-plugin"],"-v--version":["command-line-interface","v-version"],"-w--watch":["command-line-interface","w-watch"],"a-simple-example":["plugin-development","a-simple-example"],"advanced-functionality":["configuration-options","advanced-functionality"],"augmentchunkhash":["plugin-development","augmentchunkhash"],"avoiding-eval":["troubleshooting","avoiding-eval"],"babel":["tools","babel"],"banner":["plugin-development","banner"],"big-list-of-options":["configuration-options",""],"build-hooks":["plugin-development","build-hooks"],"buildend":["plugin-development","buildend"],"buildstart":["plugin-development","buildstart"],"cache":["configuration-options","cache"],"caveats-when-using-native-node-es-modules":["command-line-interface","caveats-when-using-native-node-es-modules"],"changed-defaults":["migration","changed-defaults"],"changes-to-the-plugin-api":["migration","changes-to-the-plugin-api-1"],"closebundle":["plugin-development","closebundle"],"closewatcher":["plugin-development","closewatcher"],"code-splitting":["tutorial","code-splitting"],"command-line-flags":["command-line-interface","command-line-flags"],"command-line-reference":["command-line-interface",""],"compatibility":["introduction","compatibility"],"config-intellisense":["command-line-interface","config-intellisense"],"configuration-files":["command-line-interface","configuration-files"],"context":["configuration-options","context"],"conventions":["plugin-development","conventions"],"core-functionality":["configuration-options","core-functionality"],"creating-your-first-bundle":["tutorial","creating-your-first-bundle"],"custom-module-meta-data":["plugin-development","custom-module-meta-data"],"custom-resolver-options":["plugin-development","custom-resolver-options"],"danger-zone":["configuration-options","danger-zone"],"default-export":["es-module-syntax","default-export"],"default-import":["es-module-syntax","default-import"],"deno":["tools","deno"],"deprecated-options":["configuration-options","deprecated-options"],"differences-to-the-javascript-api":["command-line-interface","differences-to-the-javascript-api"],"direct-plugin-communication":["plugin-development","direct-plugin-communication"],"dynamic-import":["es-module-syntax","dynamic-import"],"dynamic-import-in-commonjs-output":["migration","dynamic-import-in-commonjs-output"],"empty-import":["es-module-syntax","empty-import"],"error-emfile-too-many-open-files":["troubleshooting","error-emfile-too-many-open-files"],"error-javascript-heap-out-of-memory":["troubleshooting","error-javascript-heap-out-of-memory"],"error-name-is-not-exported-by-module":["troubleshooting","error-name-is-not-exported-by-module"],"error-node-tried-to-load-your-configuration-file-as-commonjs-even-though-it-is-likely-an-es-module":["troubleshooting","error-node-tried-to-load-your-configuration-file-as-commonjs-even-though-it-is-likely-an-es-module"],"error-this-is-undefined":["troubleshooting","error-this-is-undefined"],"es-module-syntax":["es-module-syntax",""],"eval2--eval":["troubleshooting","eval2-eval"],"example-transformer":["plugin-development","example-transformer"],"experimental-options":["configuration-options","experimental-options"],"experimentalcacheexpiry":["configuration-options","experimentalcacheexpiry"],"exporting":["es-module-syntax","exporting"],"external":["configuration-options","external"],"faqs":["faqs",""],"file-urls":["plugin-development","file-urls"],"footer":["plugin-development","footer"],"generatebundle":["plugin-development","generatebundle"],"getting-the-current-directory":["command-line-interface","getting-the-current-directory"],"gulp":["tools","gulp"],"how-bindings-work":["es-module-syntax","how-bindings-work"],"how-do-i-add-polyfills-to-a-rollup-bundle":["faqs","how-do-i-add-polyfills-to-a-rollup-bundle"],"how-do-i-run-rollup-itself-in-a-browser":["faqs","how-do-i-run-rollup-itself-in-a-browser"],"how-do-i-use-rollup-in-nodejs-with-commonjs-modules":["faqs","how-do-i-use-rollup-in-node-js-with-commonjs-modules"],"importing":["es-module-syntax","importing"],"importing-commonjs":["introduction","importing-commonjs"],"importing-packagejson":["command-line-interface","importing-package-json"],"input":["configuration-options","input"],"inputoptions-object":["javascript-api","inputoptions-object"],"installation":["introduction","installation"],"installing-rollup-locally":["tutorial","installing-rollup-locally"],"inter-plugin-communication":["plugin-development","inter-plugin-communication"],"intro":["plugin-development","intro"],"introduction":["introduction",""],"is-rollup-meant-for-building-libraries-or-applications":["faqs","is-rollup-meant-for-building-libraries-or-applications"],"javascript-api":["javascript-api",""],"load":["plugin-development","load"],"loading-a-configuration-from-a-node-package":["command-line-interface","loading-a-configuration-from-a-node-package"],"makeabsoluteexternalsrelative":["configuration-options","makeabsoluteexternalsrelative"],"maxparallelfileops":["configuration-options","maxparallelfileops"],"migration":["migration",""],"modulecontext":["configuration-options","modulecontext"],"moduleparsed":["plugin-development","moduleparsed"],"more-changed-options":["migration","more-changed-options"],"name":["plugin-development","name"],"named-exports":["es-module-syntax","named-exports"],"named-imports":["es-module-syntax","named-imports"],"namespace-imports":["es-module-syntax","namespace-imports"],"new-function":["troubleshooting","new-function"],"onwarn":["configuration-options","onwarn"],"options":["plugin-development","options"],"output-generation-hooks":["plugin-development","output-generation-hooks"],"outputamd":["configuration-options","output-amd"],"outputassetfilenames":["configuration-options","output-assetfilenames"],"outputbanneroutputfooter":["configuration-options","output-banner-output-footer"],"outputchunkfilenames":["configuration-options","output-chunkfilenames"],"outputcompact":["configuration-options","output-compact"],"outputdir":["configuration-options","output-dir"],"outputdynamicimportincjs":["configuration-options","output-dynamicimportincjs"],"outputentryfilenames":["configuration-options","output-entryfilenames"],"outputesmodule":["configuration-options","output-esmodule"],"outputexports":["configuration-options","output-exports"],"outputextend":["configuration-options","output-extend"],"outputexternalimportassertions":["configuration-options","output-externalimportassertions"],"outputexternallivebindings":["configuration-options","output-externallivebindings"],"outputfile":["configuration-options","output-file"],"outputformat":["configuration-options","output-format"],"outputfreeze":["configuration-options","output-freeze"],"outputgeneratedcode":["configuration-options","output-generatedcode"],"outputglobals":["configuration-options","output-globals"],"outputhoisttransitiveimports":["configuration-options","output-hoisttransitiveimports"],"outputindent":["configuration-options","output-indent"],"outputinlinedynamicimports":["configuration-options","output-inlinedynamicimports"],"outputinterop":["configuration-options","output-interop"],"outputintrooutputoutro":["configuration-options","output-intro-output-outro"],"outputmanualchunks":["configuration-options","output-manualchunks"],"outputminifyinternalexports":["configuration-options","output-minifyinternalexports"],"outputname":["configuration-options","output-name"],"outputnoconflict":["configuration-options","output-noconflict"],"outputoptions":["plugin-development","outputoptions"],"outputoptions-object":["javascript-api","outputoptions-object"],"outputpaths":["configuration-options","output-paths"],"outputplugins":["configuration-options","output-plugins"],"outputpreservemodules":["configuration-options","output-preservemodules"],"outputpreservemodulesroot":["configuration-options","output-preservemodulesroot"],"outputsanitizefilename":["configuration-options","output-sanitizefilename"],"outputsourcemap":["configuration-options","output-sourcemap"],"outputsourcemapbaseurl":["configuration-options","output-sourcemapbaseurl"],"outputsourcemapexcludesources":["configuration-options","output-sourcemapexcludesources"],"outputsourcemapfile":["configuration-options","output-sourcemapfile"],"outputsourcemappathtransform":["configuration-options","output-sourcemappathtransform"],"outputstrict":["configuration-options","output-strict"],"outputsystemnullsetters":["configuration-options","output-systemnullsetters"],"outputvalidate":["configuration-options","output-validate"],"outro":["plugin-development","outro"],"overview":["introduction","overview"],"peer-dependencies":["tools","peer-dependencies"],"perf":["configuration-options","perf"],"plugin-context":["plugin-development","plugin-context"],"plugin-development":["plugin-development",""],"plugins":["configuration-options","plugins"],"plugins-overview":["plugin-development","plugins-overview"],"prerequisites":["migration","prerequisites-1"],"preserveentrysignatures":["configuration-options","preserveentrysignatures"],"preservesymlinks":["configuration-options","preservesymlinks"],"programmatically-loading-a-config-file":["javascript-api","programmatically-loading-a-config-file"],"properties":["plugin-development","properties"],"publishing-es-modules":["introduction","publishing-es-modules"],"quick-start":["introduction","quick-start"],"reading-a-file-from-stdin":["command-line-interface","reading-a-file-from-stdin"],"renderchunk":["plugin-development","renderchunk"],"renderdynamicimport":["plugin-development","renderdynamicimport"],"rendererror":["plugin-development","rendererror"],"renderstart":["plugin-development","renderstart"],"resolvedynamicimport":["plugin-development","resolvedynamicimport"],"resolvefileurl":["plugin-development","resolvefileurl"],"resolveid":["plugin-development","resolveid"],"resolveimportmeta":["plugin-development","resolveimportmeta"],"rollupplugin-commonjs":["tools","rollup-plugin-commonjs"],"rollupplugin-node-resolve":["tools","rollup-plugin-node-resolve"],"rolluprollup":["javascript-api","rollup-rollup"],"rollupwatch":["javascript-api","rollup-watch"],"shimmissingexports":["configuration-options","shimmissingexports"],"shouldtransformcachedmodule":["plugin-development","shouldtransformcachedmodule"],"source-code-transformations":["plugin-development","source-code-transformations"],"strictdeprecations":["configuration-options","strictdeprecations"],"synthetic-named-exports":["plugin-development","synthetic-named-exports"],"the-why":["introduction","the-why"],"thisaddwatchfile":["plugin-development","this-addwatchfile"],"thisemitfile":["plugin-development","this-emitfile"],"thiserror":["plugin-development","this-error"],"thisgetcombinedsourcemap":["plugin-development","this-getcombinedsourcemap"],"thisgetfilename":["plugin-development","this-getfilename"],"thisgetmoduleids":["plugin-development","this-getmoduleids"],"thisgetmoduleinfo":["plugin-development","this-getmoduleinfo"],"thisgetwatchfiles":["plugin-development","this-getwatchfiles"],"thisload":["plugin-development","this-load"],"thismeta":["plugin-development","this-meta"],"thisparse":["plugin-development","this-parse"],"thisresolve":["plugin-development","this-resolve"],"thissetassetsource":["plugin-development","this-setassetsource"],"thiswarn":["plugin-development","this-warn"],"tools":["tools",""],"transform":["plugin-development","transform"],"transformers":["plugin-development","transformers"],"tree-shaking":["introduction","tree-shaking"],"tree-shaking-doesnt-seem-to-be-working":["troubleshooting","tree-shaking-doesn-t-seem-to-be-working"],"treeshake":["configuration-options","treeshake"],"troubleshooting":["troubleshooting",""],"tutorial":["tutorial",""],"using-config-files":["tutorial","using-config-files"],"using-configuration-files":["migration","using-configuration-files"],"using-output-plugins":["tutorial","using-output-plugins"],"using-plugins":["tutorial","using-plugins"],"warning-sourcemap-is-likely-to-be-incorrect":["troubleshooting","warning-sourcemap-is-likely-to-be-incorrect"],"warning-treating-module-as-external-dependency":["troubleshooting","warning-treating-module-as-external-dependency"],"watch-options":["configuration-options","watch"],"watchbuilddelay":["configuration-options","watch-builddelay"],"watchchange":["plugin-development","watchchange"],"watchchokidar":["configuration-options","watch-chokidar"],"watchclearscreen":["configuration-options","watch-clearscreen"],"watchexclude":["configuration-options","watch-exclude"],"watchinclude":["configuration-options","watch-include"],"watchoptions":["javascript-api","watchoptions"],"watchskipwrite":["configuration-options","watch-skipwrite"],"what-is-tree-shaking":["faqs","what-is-tree-shaking"],"who-made-the-rollup-logo-its-lovely":["faqs","who-made-the-rollup-logo-it-s-lovely"],"why-are-es-modules-better-than-commonjs-modules":["faqs","why-are-es-modules-better-than-commonjs-modules"],"why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting":["faqs","why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting"],"why-isnt-node-resolve-a-built-in-feature":["faqs","why-isn-t-node-resolve-a-built-in-feature"],"with-npm-packages":["tools","with-npm-packages"],"writebundle":["plugin-development","writebundle"]} \ No newline at end of file +{"--bundleconfigascjs":["command-line-interface","bundleconfigascjs"],"--configplugin-plugin":["command-line-interface","configplugin-plugin"],"--environment-values":["command-line-interface","environment-values"],"--failafterwarnings":["command-line-interface","failafterwarnings"],"--no-stdin":["command-line-interface","no-stdin"],"--silent":["command-line-interface","silent"],"--stdinext":["command-line-interface","stdin-ext"],"--waitforbundleinput":["command-line-interface","waitforbundleinput"],"--watchonstart-cmd---watchonbundlestart-cmd---watchonbundleend-cmd---watchonend-cmd---watchonerror-cmd":["command-line-interface","watch-onstart-cmd-watch-onbundlestart-cmd-watch-onbundleend-cmd-watch-onend-cmd-watch-onerror-cmd"],"-h--help":["command-line-interface","h-help"],"-p-plugin---plugin-plugin":["command-line-interface","p-plugin-plugin-plugin"],"-v--version":["command-line-interface","v-version"],"-w--watch":["command-line-interface","w-watch"],"a-simple-example":["plugin-development","a-simple-example"],"advanced-functionality":["configuration-options","advanced-functionality"],"augmentchunkhash":["plugin-development","augmentchunkhash"],"avoiding-eval":["troubleshooting","avoiding-eval"],"babel":["tools","babel"],"banner":["plugin-development","banner"],"big-list-of-options":["configuration-options",""],"build-hooks":["plugin-development","build-hooks"],"buildend":["plugin-development","buildend"],"buildstart":["plugin-development","buildstart"],"cache":["configuration-options","cache"],"caveats-when-using-native-node-es-modules":["command-line-interface","caveats-when-using-native-node-es-modules"],"changed-defaults":["migration","changed-defaults"],"changes-to-the-plugin-api":["migration","changes-to-the-plugin-api-1"],"closebundle":["plugin-development","closebundle"],"closewatcher":["plugin-development","closewatcher"],"code-splitting":["tutorial","code-splitting"],"command-line-flags":["command-line-interface","command-line-flags"],"command-line-reference":["command-line-interface",""],"compatibility":["introduction","compatibility"],"config-intellisense":["command-line-interface","config-intellisense"],"configuration-files":["command-line-interface","configuration-files"],"context":["configuration-options","context"],"conventions":["plugin-development","conventions"],"core-functionality":["configuration-options","core-functionality"],"creating-your-first-bundle":["tutorial","creating-your-first-bundle"],"custom-module-meta-data":["plugin-development","custom-module-meta-data"],"custom-resolver-options":["plugin-development","custom-resolver-options"],"danger-zone":["configuration-options","danger-zone"],"default-export":["es-module-syntax","default-export"],"default-import":["es-module-syntax","default-import"],"deno":["tools","deno"],"deprecated-options":["configuration-options","deprecated-options"],"differences-to-the-javascript-api":["command-line-interface","differences-to-the-javascript-api"],"direct-plugin-communication":["plugin-development","direct-plugin-communication"],"dynamic-import":["es-module-syntax","dynamic-import"],"dynamic-import-in-commonjs-output":["migration","dynamic-import-in-commonjs-output"],"empty-import":["es-module-syntax","empty-import"],"error-emfile-too-many-open-files":["troubleshooting","error-emfile-too-many-open-files"],"error-javascript-heap-out-of-memory":["troubleshooting","error-javascript-heap-out-of-memory"],"error-name-is-not-exported-by-module":["troubleshooting","error-name-is-not-exported-by-module"],"error-node-tried-to-load-your-configuration-file-as-commonjs-even-though-it-is-likely-an-es-module":["troubleshooting","error-node-tried-to-load-your-configuration-file-as-commonjs-even-though-it-is-likely-an-es-module"],"error-this-is-undefined":["troubleshooting","error-this-is-undefined"],"es-module-syntax":["es-module-syntax",""],"eval2--eval":["troubleshooting","eval2-eval"],"example-transformer":["plugin-development","example-transformer"],"experimental-options":["configuration-options","experimental-options"],"experimentalcacheexpiry":["configuration-options","experimentalcacheexpiry"],"exporting":["es-module-syntax","exporting"],"external":["configuration-options","external"],"faqs":["faqs",""],"file-urls":["plugin-development","file-urls"],"footer":["plugin-development","footer"],"generatebundle":["plugin-development","generatebundle"],"getting-the-current-directory":["command-line-interface","getting-the-current-directory"],"gulp":["tools","gulp"],"how-bindings-work":["es-module-syntax","how-bindings-work"],"how-do-i-add-polyfills-to-a-rollup-bundle":["faqs","how-do-i-add-polyfills-to-a-rollup-bundle"],"how-do-i-use-rollup-in-nodejs-with-commonjs-modules":["faqs","how-do-i-use-rollup-in-node-js-with-commonjs-modules"],"importing":["es-module-syntax","importing"],"importing-commonjs":["introduction","importing-commonjs"],"importing-packagejson":["command-line-interface","importing-package-json"],"input":["configuration-options","input"],"inputoptions-object":["javascript-api","inputoptions-object"],"installation":["introduction","installation"],"installing-rollup-locally":["tutorial","installing-rollup-locally"],"inter-plugin-communication":["plugin-development","inter-plugin-communication"],"intro":["plugin-development","intro"],"introduction":["introduction",""],"is-rollup-meant-for-building-libraries-or-applications":["faqs","is-rollup-meant-for-building-libraries-or-applications"],"javascript-api":["javascript-api",""],"load":["plugin-development","load"],"loading-a-configuration-from-a-node-package":["command-line-interface","loading-a-configuration-from-a-node-package"],"makeabsoluteexternalsrelative":["configuration-options","makeabsoluteexternalsrelative"],"maxparallelfileops":["configuration-options","maxparallelfileops"],"migration":["migration",""],"modulecontext":["configuration-options","modulecontext"],"moduleparsed":["plugin-development","moduleparsed"],"more-changed-options":["migration","more-changed-options"],"name":["plugin-development","name"],"named-exports":["es-module-syntax","named-exports"],"named-imports":["es-module-syntax","named-imports"],"namespace-imports":["es-module-syntax","namespace-imports"],"new-function":["troubleshooting","new-function"],"onwarn":["configuration-options","onwarn"],"options":["plugin-development","options"],"output-generation-hooks":["plugin-development","output-generation-hooks"],"outputamd":["configuration-options","output-amd"],"outputassetfilenames":["configuration-options","output-assetfilenames"],"outputbanneroutputfooter":["configuration-options","output-banner-output-footer"],"outputchunkfilenames":["configuration-options","output-chunkfilenames"],"outputcompact":["configuration-options","output-compact"],"outputdir":["configuration-options","output-dir"],"outputdynamicimportincjs":["configuration-options","output-dynamicimportincjs"],"outputentryfilenames":["configuration-options","output-entryfilenames"],"outputesmodule":["configuration-options","output-esmodule"],"outputexports":["configuration-options","output-exports"],"outputextend":["configuration-options","output-extend"],"outputexternalimportassertions":["configuration-options","output-externalimportassertions"],"outputexternallivebindings":["configuration-options","output-externallivebindings"],"outputfile":["configuration-options","output-file"],"outputformat":["configuration-options","output-format"],"outputfreeze":["configuration-options","output-freeze"],"outputgeneratedcode":["configuration-options","output-generatedcode"],"outputglobals":["configuration-options","output-globals"],"outputhoisttransitiveimports":["configuration-options","output-hoisttransitiveimports"],"outputindent":["configuration-options","output-indent"],"outputinlinedynamicimports":["configuration-options","output-inlinedynamicimports"],"outputinterop":["configuration-options","output-interop"],"outputintrooutputoutro":["configuration-options","output-intro-output-outro"],"outputmanualchunks":["configuration-options","output-manualchunks"],"outputminifyinternalexports":["configuration-options","output-minifyinternalexports"],"outputname":["configuration-options","output-name"],"outputnoconflict":["configuration-options","output-noconflict"],"outputoptions":["plugin-development","outputoptions"],"outputoptions-object":["javascript-api","outputoptions-object"],"outputpaths":["configuration-options","output-paths"],"outputplugins":["configuration-options","output-plugins"],"outputpreservemodules":["configuration-options","output-preservemodules"],"outputpreservemodulesroot":["configuration-options","output-preservemodulesroot"],"outputsanitizefilename":["configuration-options","output-sanitizefilename"],"outputsourcemap":["configuration-options","output-sourcemap"],"outputsourcemapbaseurl":["configuration-options","output-sourcemapbaseurl"],"outputsourcemapexcludesources":["configuration-options","output-sourcemapexcludesources"],"outputsourcemapfile":["configuration-options","output-sourcemapfile"],"outputsourcemappathtransform":["configuration-options","output-sourcemappathtransform"],"outputstrict":["configuration-options","output-strict"],"outputsystemnullsetters":["configuration-options","output-systemnullsetters"],"outputvalidate":["configuration-options","output-validate"],"outro":["plugin-development","outro"],"overview":["introduction","overview"],"peer-dependencies":["tools","peer-dependencies"],"perf":["configuration-options","perf"],"plugin-context":["plugin-development","plugin-context"],"plugin-development":["plugin-development",""],"plugins":["configuration-options","plugins"],"plugins-overview":["plugin-development","plugins-overview"],"prerequisites":["migration","prerequisites-1"],"preserveentrysignatures":["configuration-options","preserveentrysignatures"],"preservesymlinks":["configuration-options","preservesymlinks"],"programmatically-loading-a-config-file":["javascript-api","programmatically-loading-a-config-file"],"properties":["plugin-development","properties"],"publishing-es-modules":["introduction","publishing-es-modules"],"quick-start":["introduction","quick-start"],"reading-a-file-from-stdin":["command-line-interface","reading-a-file-from-stdin"],"renderchunk":["plugin-development","renderchunk"],"renderdynamicimport":["plugin-development","renderdynamicimport"],"rendererror":["plugin-development","rendererror"],"renderstart":["plugin-development","renderstart"],"resolvedynamicimport":["plugin-development","resolvedynamicimport"],"resolvefileurl":["plugin-development","resolvefileurl"],"resolveid":["plugin-development","resolveid"],"resolveimportmeta":["plugin-development","resolveimportmeta"],"rollupplugin-commonjs":["tools","rollup-plugin-commonjs"],"rollupplugin-node-resolve":["tools","rollup-plugin-node-resolve"],"rolluprollup":["javascript-api","rollup-rollup"],"rollupwatch":["javascript-api","rollup-watch"],"shimmissingexports":["configuration-options","shimmissingexports"],"shouldtransformcachedmodule":["plugin-development","shouldtransformcachedmodule"],"source-code-transformations":["plugin-development","source-code-transformations"],"strictdeprecations":["configuration-options","strictdeprecations"],"synthetic-named-exports":["plugin-development","synthetic-named-exports"],"the-why":["introduction","the-why"],"thisaddwatchfile":["plugin-development","this-addwatchfile"],"thisemitfile":["plugin-development","this-emitfile"],"thiserror":["plugin-development","this-error"],"thisgetcombinedsourcemap":["plugin-development","this-getcombinedsourcemap"],"thisgetfilename":["plugin-development","this-getfilename"],"thisgetmoduleids":["plugin-development","this-getmoduleids"],"thisgetmoduleinfo":["plugin-development","this-getmoduleinfo"],"thisgetwatchfiles":["plugin-development","this-getwatchfiles"],"thisload":["plugin-development","this-load"],"thismeta":["plugin-development","this-meta"],"thisparse":["plugin-development","this-parse"],"thisresolve":["plugin-development","this-resolve"],"thissetassetsource":["plugin-development","this-setassetsource"],"thiswarn":["plugin-development","this-warn"],"tools":["tools",""],"transform":["plugin-development","transform"],"transformers":["plugin-development","transformers"],"tree-shaking":["introduction","tree-shaking"],"tree-shaking-doesnt-seem-to-be-working":["troubleshooting","tree-shaking-doesn-t-seem-to-be-working"],"treeshake":["configuration-options","treeshake"],"troubleshooting":["troubleshooting",""],"tutorial":["tutorial",""],"using-config-files":["tutorial","using-config-files"],"using-configuration-files":["migration","using-configuration-files"],"using-output-plugins":["tutorial","using-output-plugins"],"using-plugins":["tutorial","using-plugins"],"warning-sourcemap-is-likely-to-be-incorrect":["troubleshooting","warning-sourcemap-is-likely-to-be-incorrect"],"warning-treating-module-as-external-dependency":["troubleshooting","warning-treating-module-as-external-dependency"],"watch-options":["configuration-options","watch"],"watchbuilddelay":["configuration-options","watch-builddelay"],"watchchange":["plugin-development","watchchange"],"watchchokidar":["configuration-options","watch-chokidar"],"watchclearscreen":["configuration-options","watch-clearscreen"],"watchexclude":["configuration-options","watch-exclude"],"watchinclude":["configuration-options","watch-include"],"watchoptions":["javascript-api","watchoptions"],"watchskipwrite":["configuration-options","watch-skipwrite"],"what-is-tree-shaking":["faqs","what-is-tree-shaking"],"who-made-the-rollup-logo-its-lovely":["faqs","who-made-the-rollup-logo-it-s-lovely"],"why-are-es-modules-better-than-commonjs-modules":["faqs","why-are-es-modules-better-than-commonjs-modules"],"why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting":["faqs","why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting"],"why-isnt-node-resolve-a-built-in-feature":["faqs","why-isn-t-node-resolve-a-built-in-feature"],"with-npm-packages":["tools","with-npm-packages"],"writebundle":["plugin-development","writebundle"]} \ No newline at end of file diff --git a/docs/javascript-api/index.md b/docs/javascript-api/index.md index 6f2446cb677..081fedec0a3 100755 --- a/docs/javascript-api/index.md +++ b/docs/javascript-api/index.md @@ -167,7 +167,8 @@ const inputOptions = { // experimental experimentalCacheExpiry, experimentalLogSideEffects, - perf + perf, + fs }; ``` diff --git a/docs/plugin-development/index.md b/docs/plugin-development/index.md index b822473a6b0..3606a87f1a5 100644 --- a/docs/plugin-development/index.md +++ b/docs/plugin-development/index.md @@ -1596,6 +1596,14 @@ function myPlugin() { When used in the `transform` hook, the `id` of the current module will also be added and a `position` can be supplied. This is a character index or file location which will be used to augment the log with `pos`, `loc` (a standard `{ file, line, column }` object) and `frame` (a snippet of code showing the location). +### this.fs + +| | | +| ----: | :--------------- | +| Type: | `RollupFsModule` | + +Provides abstract access to the file system. For the `RollupFsModule` type, see the documentation of the [`fs` option](../configuration-options/index.md#fs). If plugins use this instead of directly importing `node:fs`, then they can be used in browser builds that provide an in-memory file system via the `fs` option. + ### this.getCombinedSourcemap | | | diff --git a/package-lock.json b/package-lock.json index a316e408cd6..e82217701d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.42.0", + "version": "4.43.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.42.0", + "version": "4.43.0", "license": "MIT", "dependencies": { "@types/estree": "1.0.7" @@ -69,6 +69,7 @@ "lint-staged": "^16.1.0", "locate-character": "^3.0.0", "magic-string": "^0.30.17", + "memfs": "^4.17.0", "mocha": "^11.5.0", "nodemon": "^3.1.10", "nyc": "^17.1.0", @@ -2342,6 +2343,63 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz", + "integrity": "sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz", + "integrity": "sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/@lezer/common": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", @@ -3780,17 +3838,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.1.tgz", - "integrity": "sha512-TDCXj+YxLgtvxvFlAvpoRv9MAncDLBV2oT9Bd7YBGC/b/sEURoOYuIwLI99rjWOfY3QtDzO+mk0n4AmdFExW8A==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", + "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/type-utils": "8.33.1", - "@typescript-eslint/utils": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/type-utils": "8.34.0", + "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3804,7 +3862,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.33.1", + "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -3820,16 +3878,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.1.tgz", - "integrity": "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", + "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4" }, "engines": { @@ -3845,14 +3903,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.1.tgz", - "integrity": "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.33.1", - "@typescript-eslint/types": "^8.33.1", + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", "debug": "^4.3.4" }, "engines": { @@ -3867,14 +3925,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.1.tgz", - "integrity": "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1" + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3885,9 +3943,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.1.tgz", - "integrity": "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", "dev": true, "license": "MIT", "engines": { @@ -3902,14 +3960,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.1.tgz", - "integrity": "sha512-1cG37d9xOkhlykom55WVwG2QRNC7YXlxMaMzqw2uPeJixBFfKWZgaP/hjAObqMN/u3fr5BrTwTnc31/L9jQ2ww==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", + "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/utils": "8.33.1", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/utils": "8.34.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3926,9 +3984,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", - "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "dev": true, "license": "MIT", "engines": { @@ -3940,16 +3998,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.1.tgz", - "integrity": "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.33.1", - "@typescript-eslint/tsconfig-utils": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3969,16 +4027,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.1.tgz", - "integrity": "sha512-52HaBiEQUaRYqAXpfzWSR2U3gxk92Kw006+xZpElaPMg3C4PgM+A5LqwoQI1f9E5aZ/qlxAZxzm42WX+vn92SQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1" + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3993,13 +4051,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.1.tgz", - "integrity": "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/types": "8.34.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -8573,6 +8631,16 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -10031,6 +10099,26 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/memfs": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz", + "integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -10749,9 +10837,9 @@ } }, "node_modules/mocha": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.5.0.tgz", - "integrity": "sha512-VKDjhy6LMTKm0WgNEdlY77YVsD49LZnPSXJAaPNL9NRYQADxvORsyG1DIQY6v53BKTnlNbEE2MbVCDbnxr4K3w==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.6.0.tgz", + "integrity": "sha512-i0JVb+OUBqw63X/1pC3jCyJsqYisgxySBbsQa8TKvefpA1oEnw7JXxXnftfMHRsw7bEEVGRtVlHcDYXBa7FzVw==", "dev": true, "license": "MIT", "dependencies": { @@ -10771,7 +10859,7 @@ "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", + "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" @@ -14003,6 +14091,19 @@ "node": ">=0.8" } }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, "node_modules/thread-stream": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", @@ -14101,6 +14202,23 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tree-dump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz", + "integrity": "sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -14299,15 +14417,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.1.tgz", - "integrity": "sha512-AgRnV4sKkWOiZ0Kjbnf5ytTJXMUZQ0qhSVdQtDNYLPLnjsATEYhaO94GlRQwi4t4gO8FfjM6NnikHeKjUm8D7A==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", + "integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.33.1", - "@typescript-eslint/parser": "8.33.1", - "@typescript-eslint/utils": "8.33.1" + "@typescript-eslint/eslint-plugin": "8.34.0", + "@typescript-eslint/parser": "8.34.0", + "@typescript-eslint/utils": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -15309,9 +15427,9 @@ } }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz", + "integrity": "sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A==", "dev": true, "license": "Apache-2.0" }, diff --git a/package.json b/package.json index 7e1fe7129c4..1cc4dd08e53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.42.0", + "version": "4.43.0", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", @@ -171,6 +171,7 @@ "lint-staged": "^16.1.0", "locate-character": "^3.0.0", "magic-string": "^0.30.17", + "memfs": "^4.17.0", "mocha": "^11.5.0", "nodemon": "^3.1.10", "nyc": "^17.1.0", diff --git a/scripts/prepare-release.js b/scripts/prepare-release.js index f7696ab4ffc..b879fce10da 100755 --- a/scripts/prepare-release.js +++ b/scripts/prepare-release.js @@ -193,7 +193,7 @@ function getDummyLogSection(headline, pr) { */ async function installDependenciesAndLint() { await runWithEcho('npm', ['ci', '--ignore-scripts']); - await runWithEcho('npm', ['audit']); + await runWithEcho('npm', ['audit', '--audit-level', 'moderate']); await runWithEcho('npm', ['run', 'ci:lint']); } diff --git a/src/ModuleLoader.ts b/src/ModuleLoader.ts index 210c1148bcb..b1aea19e92f 100644 --- a/src/ModuleLoader.ts +++ b/src/ModuleLoader.ts @@ -15,9 +15,7 @@ import type { ResolvedId, ResolveIdResult } from './rollup/types'; -import type { PluginDriver } from './utils/PluginDriver'; import { EMPTY_OBJECT } from './utils/blank'; -import { readFile } from './utils/fs'; import { LOGLEVEL_WARN } from './utils/logging'; import { error, @@ -39,6 +37,7 @@ import { getAttributesFromImportExpression } from './utils/parseImportAttributes'; import { isAbsolute, isRelative, resolve } from './utils/path'; +import type { PluginDriver } from './utils/PluginDriver'; import relativeId from './utils/relativeId'; import { resolveId } from './utils/resolveId'; import stripBom from './utils/stripBom'; @@ -228,7 +227,8 @@ export class ModuleLoader { skip, customOptions, typeof isEntry === 'boolean' ? isEntry : !importer, - attributes + attributes, + this.options.fs ), importer, source @@ -282,7 +282,7 @@ export class ModuleLoader { const content = await this.pluginDriver.hookFirst('load', [id]); if (content !== null) return content; this.graph.watchFiles[id] = true; - return await readFile(id, 'utf8'); + return (await this.options.fs.readFile(id, { encoding: 'utf8' })) as string; }); } catch (error_: any) { let message = `Could not load ${id}`; @@ -674,7 +674,8 @@ export class ModuleLoader { null, EMPTY_OBJECT, true, - EMPTY_OBJECT + EMPTY_OBJECT, + this.options.fs ); if (resolveIdResult == null) { return error( diff --git a/src/rollup/rollup.ts b/src/rollup/rollup.ts index f60fd5b4f7a..8a8bd76221d 100644 --- a/src/rollup/rollup.ts +++ b/src/rollup/rollup.ts @@ -1,7 +1,6 @@ import { version as rollupVersion } from 'package.json'; import Bundle from '../Bundle'; import Graph from '../Graph'; -import { mkdir, writeFile } from '../utils/fs'; import { catchUnfinishedHookActions } from '../utils/hookActions'; import initWasm from '../utils/initWasm'; import { getLogger } from '../utils/logger'; @@ -215,7 +214,7 @@ async function handleGenerateWrite( } await Promise.all( Object.values(generated).map(chunk => - graph.fileOperationQueue.run(() => writeOutputFile(chunk, outputOptions)) + graph.fileOperationQueue.run(() => writeOutputFile(chunk, outputOptions, inputOptions)) ) ); await outputPluginDriver.hookParallel('writeBundle', [outputOptions, generated]); @@ -310,7 +309,8 @@ function getSortingFileType(file: OutputAsset | OutputChunk): SortingFileType { async function writeOutputFile( outputFile: OutputAsset | OutputChunk, - outputOptions: NormalizedOutputOptions + outputOptions: NormalizedOutputOptions, + { fs: { mkdir, writeFile } }: NormalizedInputOptions ): Promise { const fileName = resolve(outputOptions.dir || dirname(outputOptions.file!), outputFile.fileName); diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 0ec31f59bab..566c67117cf 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -244,6 +244,7 @@ export interface PluginContext extends MinimalPluginContext { debug: LoggingFunction; emitFile: EmitFile; error: (error: RollupError | string) => never; + fs: RollupFsModule; getFileName: (fileReferenceId: string) => string; getModuleIds: () => IterableIterator; getModuleInfo: GetModuleInfo; @@ -671,6 +672,7 @@ export interface InputOptions { experimentalCacheExpiry?: number; experimentalLogSideEffects?: boolean; external?: ExternalOption; + fs?: RollupFsModule; input?: InputOption; jsx?: false | JsxPreset | JsxOptions; logLevel?: LogLevelOption; @@ -699,6 +701,7 @@ export interface NormalizedInputOptions { experimentalCacheExpiry: number; experimentalLogSideEffects: boolean; external: IsExternal; + fs: RollupFsModule; input: string[] | Record; jsx: false | NormalizedJsxOptions; logLevel: LogLevelOption; @@ -1103,3 +1106,76 @@ export function defineConfig(optionsFunction: RollupOptionsFunction): RollupOpti export type RollupOptionsFunction = ( commandLineArguments: Record ) => MaybePromise; + +export interface RollupFsModule { + appendFile( + path: string, + data: string | Uint8Array, + options?: { encoding?: BufferEncoding | null; mode?: string | number; flag?: string | number } + ): Promise; + + copyFile(source: string, destination: string, mode?: string | number): Promise; + + mkdir(path: string, options?: { recursive?: boolean; mode?: string | number }): Promise; + + mkdtemp(prefix: string): Promise; + + readdir(path: string, options?: { withFileTypes?: false }): Promise; + readdir(path: string, options?: { withFileTypes: true }): Promise; + + readFile( + path: string, + options?: { encoding?: null; flag?: string | number; signal?: AbortSignal } + ): Promise; + readFile( + path: string, + options?: { encoding: BufferEncoding; flag?: string | number; signal?: AbortSignal } + ): Promise; + + realpath(path: string): Promise; + + rename(oldPath: string, newPath: string): Promise; + + rmdir(path: string, options?: { recursive?: boolean }): Promise; + + stat(path: string): Promise; + + lstat(path: string): Promise; + + unlink(path: string): Promise; + + writeFile( + path: string, + data: string | Uint8Array, + options?: { encoding?: BufferEncoding | null; mode?: string | number; flag?: string | number } + ): Promise; +} + +export type BufferEncoding = + | 'ascii' + | 'utf8' + | 'utf16le' + | 'ucs2' + | 'base64' + | 'base64url' + | 'latin1' + | 'binary' + | 'hex'; + +export interface RollupDirectoryEntry { + isFile(): boolean; + isDirectory(): boolean; + isSymbolicLink(): boolean; + name: string; +} + +export interface RollupFileStats { + isFile(): boolean; + isDirectory(): boolean; + isSymbolicLink(): boolean; + size: number; + mtime: Date; + ctime: Date; + atime: Date; + birthtime: Date; +} diff --git a/src/utils/PluginContext.ts b/src/utils/PluginContext.ts index 7f038fd233a..7590b89bf5a 100644 --- a/src/utils/PluginContext.ts +++ b/src/utils/PluginContext.ts @@ -61,6 +61,7 @@ export function getPluginContext( error(error_): never { return error(logPluginError(normalizeLog(error_), plugin.name)); }, + fs: options.fs, getFileName: fileEmitter.getFileName, getModuleIds: () => graph.modulesById.keys(), getModuleInfo: graph.getModuleInfo, diff --git a/src/utils/logs.ts b/src/utils/logs.ts index 7c09d04a16f..e40f097d3d3 100644 --- a/src/utils/logs.ts +++ b/src/utils/logs.ts @@ -911,7 +911,7 @@ export function logRedeclarationError(name: string): RollupLog { export function logReservedNamespace(namespace: string): RollupLog { return { code: RESERVED_NAMESPACE, - message: `You have overided reserved namespace "${namespace}"` + message: `You have overridden reserved namespace "${namespace}"` }; } diff --git a/src/utils/options/mergeOptions.ts b/src/utils/options/mergeOptions.ts index c427babcbe3..89beea51705 100644 --- a/src/utils/options/mergeOptions.ts +++ b/src/utils/options/mergeOptions.ts @@ -69,7 +69,7 @@ export async function mergeOptions( warnUnknownOptions( command, [ - ...Object.keys(inputOptions), + ...Object.keys(inputOptions).filter(option => option !== 'fs'), ...Object.keys(outputOptions[0]).filter( option => option !== 'sourcemapIgnoreList' && option !== 'sourcemapPathTransform' ), @@ -135,6 +135,7 @@ function mergeInputOptions( experimentalCacheExpiry: getOption('experimentalCacheExpiry'), experimentalLogSideEffects: getOption('experimentalLogSideEffects'), external: getExternal(config, overrides), + fs: getOption('fs'), input: getOption('input') || [], jsx: getObjectOption( config, diff --git a/src/utils/options/normalizeInputOptions.ts b/src/utils/options/normalizeInputOptions.ts index 85127200c0e..9446c5d2f3c 100644 --- a/src/utils/options/normalizeInputOptions.ts +++ b/src/utils/options/normalizeInputOptions.ts @@ -3,10 +3,12 @@ import type { InputOptions, ModuleSideEffectsOption, NormalizedInputOptions, - RollupBuild + RollupBuild, + RollupFsModule } from '../../rollup/types'; import { EMPTY_ARRAY } from '../blank'; import { ensureArray } from '../ensureArray'; +import * as fs from '../fs'; import { getLogger } from '../logger'; import { LOGLEVEL_INFO } from '../logging'; import { error, logInvalidOption } from '../logs'; @@ -50,6 +52,7 @@ export async function normalizeInputOptions( experimentalCacheExpiry: config.experimentalCacheExpiry ?? 10, experimentalLogSideEffects: config.experimentalLogSideEffects || false, external: getIdMatcher(config.external), + fs: config.fs ?? (fs as RollupFsModule), input: getInput(config), jsx: getJsx(config), logLevel, diff --git a/src/utils/resolveId.ts b/src/utils/resolveId.ts index 812f2e9012e..766da800c09 100644 --- a/src/utils/resolveId.ts +++ b/src/utils/resolveId.ts @@ -1,8 +1,7 @@ import type { ModuleLoaderResolveId } from '../ModuleLoader'; -import type { CustomPluginOptions, Plugin, ResolveIdResult } from '../rollup/types'; -import type { PluginDriver } from './PluginDriver'; -import { lstat, readdir, realpath } from './fs'; +import type { CustomPluginOptions, Plugin, ResolveIdResult, RollupFsModule } from '../rollup/types'; import { basename, dirname, isAbsolute, resolve } from './path'; +import type { PluginDriver } from './PluginDriver'; import { resolveIdViaPlugins } from './resolveIdViaPlugins'; export async function resolveId( @@ -14,7 +13,8 @@ export async function resolveId( skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null, customOptions: CustomPluginOptions | undefined, isEntry: boolean, - attributes: Record + attributes: Record, + fs: RollupFsModule ): Promise { const pluginResult = await resolveIdViaPlugins( source, @@ -54,30 +54,36 @@ export async function resolveId( // See https://nodejs.org/api/path.html#path_path_resolve_paths return addJsExtensionIfNecessary( importer ? resolve(dirname(importer), source) : resolve(source), - preserveSymlinks + preserveSymlinks, + fs ); } async function addJsExtensionIfNecessary( file: string, - preserveSymlinks: boolean + preserveSymlinks: boolean, + fs: RollupFsModule ): Promise { return ( - (await findFile(file, preserveSymlinks)) ?? - (await findFile(file + '.mjs', preserveSymlinks)) ?? - (await findFile(file + '.js', preserveSymlinks)) + (await findFile(file, preserveSymlinks, fs)) ?? + (await findFile(file + '.mjs', preserveSymlinks, fs)) ?? + (await findFile(file + '.js', preserveSymlinks, fs)) ); } -async function findFile(file: string, preserveSymlinks: boolean): Promise { +async function findFile( + file: string, + preserveSymlinks: boolean, + fs: RollupFsModule +): Promise { try { - const stats = await lstat(file); + const stats = await fs.lstat(file); if (!preserveSymlinks && stats.isSymbolicLink()) - return await findFile(await realpath(file), preserveSymlinks); + return await findFile(await fs.realpath(file), preserveSymlinks, fs); if ((preserveSymlinks && stats.isSymbolicLink()) || stats.isFile()) { // check case const name = basename(file); - const files = await readdir(dirname(file)); + const files = await fs.readdir(dirname(file)); if (files.includes(name)) return file; } diff --git a/test/browser/samples/missing-default-fs/_config.js b/test/browser/samples/missing-default-fs/_config.js new file mode 100644 index 00000000000..2a9277fe0ba --- /dev/null +++ b/test/browser/samples/missing-default-fs/_config.js @@ -0,0 +1,23 @@ +module.exports = defineTest({ + description: 'fails when accessing fs from a browser without supplying it via option', + options: { + plugins: { + name: 'test', + resolveId(source) { + return source; + }, + load(id) { + return this.fs.readFile(id, { encoding: 'utf8' }); + } + } + }, + error: { + code: 'PLUGIN_ERROR', + hook: 'load', + message: + 'Could not load main: Cannot access the file system (via "fs.readFile") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.', + plugin: 'test', + pluginCode: 'NO_FS_IN_BROWSER', + url: 'https://rollupjs.org/plugin-development/#a-simple-example' + } +}); diff --git a/test/browser/samples/missing-dependency-resolution/_config.js b/test/browser/samples/missing-dependency-resolution/_config.js index a683f11c2b7..671cebb52cd 100644 --- a/test/browser/samples/missing-dependency-resolution/_config.js +++ b/test/browser/samples/missing-dependency-resolution/_config.js @@ -1,17 +1,36 @@ const { loader } = require('../../../testHelpers.js'); +const assert = require('node:assert/strict'); + +const logs = []; module.exports = defineTest({ - description: 'fails if a dependency cannot be resolved', + description: 'warns if a dependency cannot be resolved', options: { - plugins: loader({ - main: `import {foo} from 'dep'; + onLog(level, log) { + logs.push({ level, log }); + }, + plugins: [ + loader({ + main: `import {foo} from 'dep'; console.log(foo);` - }) - }, - error: { - code: 'NO_FS_IN_BROWSER', - message: - 'Cannot access the file system (via "path.resolve") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.', - url: 'https://rollupjs.org/plugin-development/#a-simple-example' + }), + { + buildEnd() { + assert.deepEqual(logs, [ + { + level: 'warn', + log: { + code: 'UNRESOLVED_IMPORT', + exporter: 'dep', + id: 'main', + message: + '"dep" is imported by "main", but could not be resolved – treating it as an external dependency.', + url: 'https://rollupjs.org/troubleshooting/#warning-treating-module-as-external-dependency' + } + } + ]); + } + } + ] } }); diff --git a/test/browser/samples/missing-dependency-resolution/_expected/main.js b/test/browser/samples/missing-dependency-resolution/_expected/main.js new file mode 100644 index 00000000000..37f4a46d1b5 --- /dev/null +++ b/test/browser/samples/missing-dependency-resolution/_expected/main.js @@ -0,0 +1,3 @@ +import { foo } from 'dep'; + +console.log(foo); diff --git a/test/browser/samples/missing-entry-resolution/_config.js b/test/browser/samples/missing-entry-resolution/_config.js index f122cdb59e1..e41dbc2165e 100644 --- a/test/browser/samples/missing-entry-resolution/_config.js +++ b/test/browser/samples/missing-entry-resolution/_config.js @@ -1,9 +1,7 @@ module.exports = defineTest({ description: 'fails if an entry cannot be resolved', error: { - code: 'NO_FS_IN_BROWSER', - message: - 'Cannot access the file system (via "path.resolve") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.', - url: 'https://rollupjs.org/plugin-development/#a-simple-example' + code: 'UNRESOLVED_ENTRY', + message: 'Could not resolve entry module "main".' } }); diff --git a/test/browser/samples/provide-fs/_config.js b/test/browser/samples/provide-fs/_config.js new file mode 100644 index 00000000000..a053d83a8d3 --- /dev/null +++ b/test/browser/samples/provide-fs/_config.js @@ -0,0 +1,12 @@ +const { Volume } = require('memfs'); + +const vol = Volume.fromJSON({ + main: "console.log('Hello, Rollup!');" +}); + +module.exports = defineTest({ + description: 'allows to provide an in-memory fs via option', + options: { + fs: vol.promises + } +}); diff --git a/test/browser/samples/provide-fs/_expected/main.js b/test/browser/samples/provide-fs/_expected/main.js new file mode 100644 index 00000000000..f5d3e627b54 --- /dev/null +++ b/test/browser/samples/provide-fs/_expected/main.js @@ -0,0 +1 @@ +console.log('Hello, Rollup!'); diff --git a/test/chunking-form/samples/max-parallel-file-operations/_config.js b/test/chunking-form/samples/max-parallel-file-operations/_config.js index b9d5da1de4d..f2cc3f55711 100644 --- a/test/chunking-form/samples/max-parallel-file-operations/_config.js +++ b/test/chunking-form/samples/max-parallel-file-operations/_config.js @@ -10,7 +10,8 @@ module.exports = defineTest({ description: 'maxParallelFileOps limits write operations', options: { maxParallelFileOps: 3, - output: { preserveModules: true } + output: { preserveModules: true }, + fs }, before() { fs.writeFile = async (path, content) => { diff --git a/test/function/samples/max-parallel-file-operations/default/_config.js b/test/function/samples/max-parallel-file-operations/default/_config.js index cfe030764e2..5710cdd1a45 100644 --- a/test/function/samples/max-parallel-file-operations/default/_config.js +++ b/test/function/samples/max-parallel-file-operations/default/_config.js @@ -8,6 +8,9 @@ let maxReads = 0; module.exports = defineTest({ description: 'maxParallelFileOps not set', + options: { + fs + }, before() { fs.readFile = async (path, options) => { currentReads++; diff --git a/test/function/samples/max-parallel-file-operations/error/_config.js b/test/function/samples/max-parallel-file-operations/error/_config.js index 2ed14956ea1..ff46cae7b60 100644 --- a/test/function/samples/max-parallel-file-operations/error/_config.js +++ b/test/function/samples/max-parallel-file-operations/error/_config.js @@ -10,7 +10,8 @@ module.exports = defineTest({ input: 'main', plugins: loader({ main: `import {foo} from './dep';` - }) + }), + fs }, before() { fs.readFile = (path, options) => { diff --git a/test/function/samples/max-parallel-file-operations/infinity/_config.js b/test/function/samples/max-parallel-file-operations/infinity/_config.js index fb6b8aeb9cd..a672fb4ad56 100644 --- a/test/function/samples/max-parallel-file-operations/infinity/_config.js +++ b/test/function/samples/max-parallel-file-operations/infinity/_config.js @@ -9,7 +9,8 @@ let maxReads = 0; module.exports = defineTest({ description: 'maxParallelFileOps set to infinity', options: { - maxParallelFileOps: 0 + maxParallelFileOps: 0, + fs }, before() { fs.readFile = async (path, options) => { diff --git a/test/function/samples/max-parallel-file-operations/set/_config.js b/test/function/samples/max-parallel-file-operations/set/_config.js index 5c631ef9ca9..8dfac8f3f26 100644 --- a/test/function/samples/max-parallel-file-operations/set/_config.js +++ b/test/function/samples/max-parallel-file-operations/set/_config.js @@ -9,7 +9,8 @@ let maxReads = 0; module.exports = defineTest({ description: 'maxParallelFileOps set to 3', options: { - maxParallelFileOps: 3 + maxParallelFileOps: 3, + fs }, before() { fs.readFile = async (path, options) => { diff --git a/test/function/samples/options-hook/_config.js b/test/function/samples/options-hook/_config.js index ef6bdc0398e..ee4c6c73daf 100644 --- a/test/function/samples/options-hook/_config.js +++ b/test/function/samples/options-hook/_config.js @@ -10,7 +10,10 @@ module.exports = defineTest({ { name: 'test-plugin', buildStart(options) { - assert.deepStrictEqual(JSON.parse(JSON.stringify(options)), { + // The fs option is not json stringifiable + const { fs, ...restOptions } = options; + assert.ok(fs); + assert.deepStrictEqual(JSON.parse(JSON.stringify(restOptions)), { context: 'undefined', experimentalCacheExpiry: 10, experimentalLogSideEffects: false, diff --git a/test/misc/fs-override.js b/test/misc/fs-override.js new file mode 100644 index 00000000000..f83f9156549 --- /dev/null +++ b/test/misc/fs-override.js @@ -0,0 +1,56 @@ +const assert = require('node:assert'); +const { Volume } = require('memfs'); +const rollup = require('../../dist/rollup'); + +describe('fs-override', () => { + it('uses fs from options', async () => { + const vol = Volume.fromJSON( + { + '/input.js': "console.log('Hello, Rollup!');" + }, + __dirname + ); + const bundle = await rollup.rollup({ + input: '/input.js', + fs: vol.promises + }); + + await bundle.write({ + file: '/output.js', + format: 'esm' + }); + + const generatedCode = vol.readFileSync('/output.js', 'utf8'); + assert.strictEqual(generatedCode.trim(), "console.log('Hello, Rollup!');"); + }); + + it('passes fs from options to plugin context', async () => { + const vol = Volume.fromJSON( + { + '/input.js': "console.log('Hello, Rollup!');" + }, + __dirname + ); + const bundle = await rollup.rollup({ + input: '/input.js', + fs: vol.promises, + plugins: [ + { + name: 'test-plugin', + async transform(code, _id) { + assert.equal(await this.fs.readFile('/input.js'), "console.log('Hello, Rollup!');"); + return { + code, + map: null + }; + } + } + ] + }); + + await bundle.write({ + file: '/output.js', + format: 'esm' + }); + }); +}); diff --git a/test/misc/index.js b/test/misc/index.js index 8950e1f0584..8bc305f6193 100644 --- a/test/misc/index.js +++ b/test/misc/index.js @@ -1,4 +1,5 @@ require('./bundle-information'); +require('./fs-override'); require('./get-log-filter'); require('./parse-ast'); require('./iife'); diff --git a/test/misc/optionList.js b/test/misc/optionList.js index d559168cfb7..42ed455ab5d 100644 --- a/test/misc/optionList.js +++ b/test/misc/optionList.js @@ -1,5 +1,5 @@ exports.input = - 'cache, context, experimentalCacheExpiry, experimentalLogSideEffects, external, input, jsx, logLevel, makeAbsoluteExternalsRelative, maxParallelFileOps, moduleContext, onLog, onwarn, perf, plugins, preserveEntrySignatures, preserveSymlinks, shimMissingExports, strictDeprecations, treeshake, watch'; + 'cache, context, experimentalCacheExpiry, experimentalLogSideEffects, external, fs, input, jsx, logLevel, makeAbsoluteExternalsRelative, maxParallelFileOps, moduleContext, onLog, onwarn, perf, plugins, preserveEntrySignatures, preserveSymlinks, shimMissingExports, strictDeprecations, treeshake, watch'; exports.flags = 'amd, assetFileNames, banner, bundleConfigAsCjs, c, cache, chunkFileNames, compact, config, configImportAttributesKey, configPlugin, context, d, dir, dynamicImportInCjs, e, entryFileNames, environment, esModule, experimentalCacheExpiry, experimentalLogSideEffects, experimentalMinChunkSize, exports, extend, external, externalImportAssertions, externalImportAttributes, externalLiveBindings, f, failAfterWarnings, file, filterLogs, footer, forceExit, format, freeze, g, generatedCode, globals, h, hashCharacters, hoistTransitiveImports, i, importAttributesKey, indent, inlineDynamicImports, input, interop, intro, jsx, logLevel, m, makeAbsoluteExternalsRelative, manualChunks, maxParallelFileOps, minifyInternalExports, moduleContext, n, name, noConflict, o, onLog, onwarn, outro, p, paths, perf, plugin, plugins, preserveEntrySignatures, preserveModules, preserveModulesRoot, preserveSymlinks, reexportProtoFromExternal, sanitizeFileName, shimMissingExports, silent, sourcemap, sourcemapBaseUrl, sourcemapDebugIds, sourcemapExcludeSources, sourcemapFile, sourcemapFileNames, stdin, strict, strictDeprecations, systemNullSetters, treeshake, v, validate, virtualDirname, w, waitForBundleInput, watch'; exports.output =