diff --git a/CHANGELOG.md b/CHANGELOG.md index 968c5076d..570a30d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # rollup changelog +## 4.44.0 + +_2025-06-19_ + +### Features + +- Remove limit on `maxParallelFileOps` as this could break watch mode with the commonjs plugin (#5986) + +### Bug Fixes + +- Provide better source mappings when coarse intermediate maps are used (#5985) + +### Pull Requests + +- [#5984](https://github.com/rollup/rollup/pull/5984): fix(deps): lock file maintenance minor/patch updates (@renovate[bot], @lukastaegert) +- [#5985](https://github.com/rollup/rollup/pull/5985): Improve approximation of coarse sourcemap segments (@TrickyPi) +- [#5986](https://github.com/rollup/rollup/pull/5986): Remove limit on max parallel file ops (@lukastaegert) + ## 4.43.0 _2025-06-11_ diff --git a/browser/package.json b/browser/package.json index 3ac406a38..07e9d89c6 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "4.43.0", + "version": "4.44.0", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/docs/configuration-options/index.md b/docs/configuration-options/index.md index b894a0782..08b22395b 100755 --- a/docs/configuration-options/index.md +++ b/docs/configuration-options/index.md @@ -633,6 +633,7 @@ buildWithCache() ### maxParallelFileOps {#maxparallelfileops} +<<<<<<< HEAD | | | | -----: | :------------------------------ | | 类型: | `number` | @@ -640,6 +641,15 @@ buildWithCache() | 默认: | 20 | 该选项限制 rollup 在读取模块或写入 chunk 时,同时能打开的文件数量。如果没有限制或者数值足够高,构建可能会失败,显示“EMFILE: Too many open files”(EMFILE:打开的文件数过多)。这取决于操作系统限制的句柄数(open file handles)大小。 +======= +| | | +| -------: | :------------------------------ | +| Type: | `number` | +| CLI: | `--maxParallelFileOps ` | +| Default: | `Infinity` | + +Limits the number of files rollup will open in parallel when reading modules or writing chunks. Without a limit or with a high enough value, builds can fail with an "EMFILE: too many open files". This depends on how many open file handles the operating system allows. If you set the limit too low and use plugins that rely on the [`this.load`](../plugin-development/index.md#this-load) context function, such as the `commonjs` plugin, then it can happen that builds stall without an error message as it limits the number of parallel `load` calls. +>>>>>>> fa4b2842c823f6a61f6b994a28b7fcb54419b6c6 ### onLog {#onlog} diff --git a/package-lock.json b/package-lock.json index 6f2249ce3..f30cca180 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rollup", - "version": "4.43.0", + "version": "4.44.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rollup", - "version": "4.43.0", + "version": "4.44.0", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" diff --git a/package.json b/package.json index 285f0efbd..757b6230b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "4.43.0", + "version": "4.44.0", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", diff --git a/src/utils/collapseSourcemaps.ts b/src/utils/collapseSourcemaps.ts index 8abd7b29c..d314e5eed 100644 --- a/src/utils/collapseSourcemaps.ts +++ b/src/utils/collapseSourcemaps.ts @@ -122,11 +122,22 @@ class Link { while (searchStart <= searchEnd) { const m = (searchStart + searchEnd) >> 1; - const segment = segments[m]; + let segment = segments[m]; // If a sourcemap does not have sufficient resolution to contain a - // necessary mapping, e.g. because it only contains line information, we - // use the best approximation we could find + // necessary mapping, e.g. because it only contains line information or + // the column is not precise (e.g. the sourcemap is generated by esbuild, segment[0] may be shorter than the location of the first letter), + // we approximate by finding the closest segment whose segment[0] is less than the given column + if (segment[0] !== column && searchStart === searchEnd) { + let approximatedSegmentIndex = 0; + for (let index = 0; index < segments.length; index++) { + if (segments[index][0] > column) { + break; + } + approximatedSegmentIndex = index; + } + segment = segments[approximatedSegmentIndex]; + } if (segment[0] === column || searchStart === searchEnd) { if (segment.length == 1) return null; const source = this.sources[segment[1]]; diff --git a/src/utils/getOriginalLocation.ts b/src/utils/getOriginalLocation.ts index 6ee15c45a..baeb44251 100644 --- a/src/utils/getOriginalLocation.ts +++ b/src/utils/getOriginalLocation.ts @@ -15,14 +15,18 @@ export function getOriginalLocation( (segment): segment is [number, number, number, number] => segment.length > 1 ); const lastSegment = filteredLine[filteredLine.length - 1]; - for (const segment of filteredLine) { + let previousSegment = filteredLine[0]; + for (let segment of filteredLine) { if (segment[0] >= location.column || segment === lastSegment) { + const notMatched = segment[0] !== location.column; + segment = notMatched ? previousSegment : segment; location = { column: segment[3], line: segment[2] + 1 }; continue traceSourcemap; } + previousSegment = segment; } } throw new Error("Can't resolve original location of error."); diff --git a/src/utils/options/normalizeInputOptions.ts b/src/utils/options/normalizeInputOptions.ts index 9446c5d2f..e35b9cf95 100644 --- a/src/utils/options/normalizeInputOptions.ts +++ b/src/utils/options/normalizeInputOptions.ts @@ -176,11 +176,8 @@ const getMaxParallelFileOps = ( config: InputOptions ): NormalizedInputOptions['maxParallelFileOps'] => { const maxParallelFileOps = config.maxParallelFileOps; - if (typeof maxParallelFileOps === 'number') { - if (maxParallelFileOps <= 0) return Infinity; - return maxParallelFileOps; - } - return 20; + if (typeof maxParallelFileOps !== 'number' || maxParallelFileOps <= 0) return Infinity; + return maxParallelFileOps; }; const getModuleContext = ( diff --git a/test/function/samples/options-hook/_config.js b/test/function/samples/options-hook/_config.js index ee4c6c73d..17846fad3 100644 --- a/test/function/samples/options-hook/_config.js +++ b/test/function/samples/options-hook/_config.js @@ -11,8 +11,9 @@ module.exports = defineTest({ name: 'test-plugin', buildStart(options) { // The fs option is not json stringifiable - const { fs, ...restOptions } = options; + const { fs, maxParallelFileOps, ...restOptions } = options; assert.ok(fs); + assert.strictEqual(maxParallelFileOps, Infinity); assert.deepStrictEqual(JSON.parse(JSON.stringify(restOptions)), { context: 'undefined', experimentalCacheExpiry: 10, @@ -21,7 +22,6 @@ module.exports = defineTest({ jsx: false, logLevel: 'info', makeAbsoluteExternalsRelative: 'ifRelativeSource', - maxParallelFileOps: 20, perf: false, plugins: [ { diff --git a/test/sourcemaps/samples/combined-sourcemap-2/_config.js b/test/sourcemaps/samples/combined-sourcemap-2/_config.js new file mode 100644 index 000000000..4d9fac29a --- /dev/null +++ b/test/sourcemaps/samples/combined-sourcemap-2/_config.js @@ -0,0 +1,82 @@ +const assert = require('node:assert'); +const { encode } = require('@jridgewell/sourcemap-codec'); +const terser = require('terser'); +const { SourceMapConsumer } = require('source-map'); +const getLocation = require('../../getLocation'); + +const originalCode = ` +export function App() { + return
{'.'}
; +} +`; + +module.exports = defineTest({ + description: 'get correct combined sourcemap in transforming', + formats: ['es'], + options: { + external: ['react/jsx-runtime'], + plugins: [ + { + resolveId(id) { + return id; + }, + load() { + return { + code: originalCode + }; + } + }, + { + async transform(code) { + return { + code: `import { jsx } from "react/jsx-runtime"; +export function App() { + return /* @__PURE__ */ jsx("div", { children: "." }); +} +`, + map: { + mappings: encode([ + [[0, 0, 2, 9]], + [ + [0, 0, 1, 7], + [16, 0, 1, 16], + [22, 0, 1, 22] + ], + [ + // coarse segment + [0, 0, 2, 2], + [9, 0, 2, 9], + [29, 0, 2, 10], + [38, 0, 2, 15], + [53, 0, 2, 19] + ], + [[0, 0, 3, 0]], + [] + ]), + sourcesContent: [code] + } + }; + } + }, + { + transform(code) { + return terser.minify(code, { + sourceMap: true + }); + } + }, + { + async transform(code) { + const generatedLoc = getLocation(code, code.indexOf('return')); + const map = this.getCombinedSourcemap(); + const smc = await new SourceMapConsumer(map); + const originalLoc = smc.originalPositionFor(generatedLoc); + const expectedOriginalLoc = getLocation(originalCode, originalCode.indexOf('return')); + assert.equal(originalLoc.line, expectedOriginalLoc.line); + assert.equal(originalLoc.column, expectedOriginalLoc.column); + } + } + ] + }, + async test() {} +}); diff --git a/test/sourcemaps/samples/warning-with-coarse-sourcemap/_config.js b/test/sourcemaps/samples/warning-with-coarse-sourcemap/_config.js new file mode 100644 index 000000000..3a2f1372d --- /dev/null +++ b/test/sourcemaps/samples/warning-with-coarse-sourcemap/_config.js @@ -0,0 +1,77 @@ +const { encode } = require('@jridgewell/sourcemap-codec'); +const terser = require('terser'); +const path = require('node:path'); +const ID_MAIN = path.join(__dirname, 'main.js'); + +const originalCode = ` +console.log( + this + 2 +) +`; + +module.exports = defineTest({ + description: 'get correct mapping location with coarse sourcemap', + formats: ['es'], + options: { + plugins: [ + { + resolveId() { + return ID_MAIN; + }, + load() { + return { + code: originalCode + }; + } + }, + { + transform(code) { + return { + code, + map: { + mappings: encode([ + [], + [ + [0, 0, 1, 0], + [8, 0, 1, 8] + ], + [ + // coarse segment + [0, 0, 2, 2], + [9, 0, 2, 9] + ], + [[0, 0, 3, 0]], + [] + ]), + sourcesContent: [code] + } + }; + } + }, + { + transform(code) { + return terser.minify(code, { + sourceMap: true + }); + } + } + ] + }, + warnings: [ + { + code: 'THIS_IS_UNDEFINED', + message: + "main.js (3:2): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten", + url: 'https://rollupjs.org/troubleshooting/#error-this-is-undefined', + id: ID_MAIN, + pos: 12, + loc: { + column: 2, + file: ID_MAIN, + line: 3 + }, + frame: ' 1:\n' + '2: console.log(\n' + '3: this + 2\n' + ' ^\n' + '4: )' + } + ], + async test() {} +});