diff --git a/browser/package.json b/browser/package.json index e5bc3c891b4..c566f478e7f 100644 --- a/browser/package.json +++ b/browser/package.json @@ -1,6 +1,6 @@ { "name": "@rollup/browser", - "version": "3.0.0-3", + "version": "3.0.0-4", "description": "Next-generation ES module bundler browser build", "main": "dist/rollup.browser.js", "module": "dist/es/rollup.browser.js", diff --git a/cli/run/build.ts b/cli/run/build.ts index b2c9418be27..9c8fb8784c1 100644 --- a/cli/run/build.ts +++ b/cli/run/build.ts @@ -5,7 +5,6 @@ import type { MergedRollupOptions } from '../../src/rollup/types'; import { bold, cyan, green } from '../../src/utils/colors'; import { errOnlyInlineSourcemapsForStdout } from '../../src/utils/error'; import relativeId from '../../src/utils/relativeId'; -import { SOURCEMAPPING_URL } from '../../src/utils/sourceMappingURL'; import { handleError, stderr } from '../logging'; import type { BatchWarnings } from './batchWarnings'; import { printTimings } from './timings'; @@ -37,20 +36,10 @@ export default async function build( if (output.sourcemap && output.sourcemap !== 'inline') { handleError(errOnlyInlineSourcemapsForStdout()); } - const { output: outputs } = await bundle.generate(output); for (const file of outputs) { - let source: string | Uint8Array; - if (file.type === 'asset') { - source = file.source; - } else { - source = file.code; - if (output.sourcemap === 'inline') { - source += `\n//# ${SOURCEMAPPING_URL}=${file.map!.toUrl()}\n`; - } - } if (outputs.length > 1) process.stdout.write(`\n${cyan(bold(`//→ ${file.fileName}:`))}\n`); - process.stdout.write(source as Buffer); + process.stdout.write(file.type === 'asset' ? file.source : file.code); } if (!silent) { warnings.flush(); diff --git a/package.json b/package.json index 800dab8a17c..70e6f3946c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup", - "version": "3.0.0-3", + "version": "3.0.0-4", "description": "Next-generation ES module bundler", "main": "dist/rollup.js", "module": "dist/es/rollup.js", diff --git a/src/rollup/rollup.ts b/src/rollup/rollup.ts index d039929464b..b0a8f441cd3 100644 --- a/src/rollup/rollup.ts +++ b/src/rollup/rollup.ts @@ -15,9 +15,8 @@ import { catchUnfinishedHookActions } from '../utils/hookActions'; import { normalizeInputOptions } from '../utils/options/normalizeInputOptions'; import { normalizeOutputOptions } from '../utils/options/normalizeOutputOptions'; import type { GenericConfigObject } from '../utils/options/options'; -import { basename, dirname, resolve } from '../utils/path'; +import { dirname, resolve } from '../utils/path'; import { ANONYMOUS_OUTPUT_PLUGIN_PREFIX, ANONYMOUS_PLUGIN_PREFIX } from '../utils/pluginUtils'; -import { SOURCEMAPPING_URL } from '../utils/sourceMappingURL'; import { getTimings, initialiseTimers, timeEnd, timeStart } from '../utils/timers'; import type { NormalizedInputOptions, @@ -282,31 +281,7 @@ async function writeOutputFile( // 'recursive: true' does not throw if the folder structure, or parts of it, already exist await fs.mkdir(dirname(fileName), { recursive: true }); - let writeSourceMapPromise: Promise | undefined; - let source: string | Uint8Array; - if (outputFile.type === 'asset') { - source = outputFile.source; - } else { - source = outputFile.code; - if (outputOptions.sourcemap && outputFile.map) { - let url: string; - if (outputOptions.sourcemap === 'inline') { - url = outputFile.map.toUrl(); - } else { - const { sourcemapBaseUrl } = outputOptions; - const sourcemapFileName = `${basename(outputFile.fileName)}.map`; - url = sourcemapBaseUrl - ? new URL(sourcemapFileName, sourcemapBaseUrl).toString() - : sourcemapFileName; - writeSourceMapPromise = fs.writeFile(`${fileName}.map`, outputFile.map.toString()); - } - if (outputOptions.sourcemap !== 'hidden') { - source += `//# ${SOURCEMAPPING_URL}=${url}\n`; - } - } - } - - return Promise.all([fs.writeFile(fileName, source), writeSourceMapPromise]); + return fs.writeFile(fileName, outputFile.type === 'asset' ? outputFile.source : outputFile.code); } /** diff --git a/src/utils/renderChunks.ts b/src/utils/renderChunks.ts index ac9ee1c6d4a..00ed35df629 100644 --- a/src/utils/renderChunks.ts +++ b/src/utils/renderChunks.ts @@ -19,7 +19,8 @@ import { replacePlaceholdersWithDefaultAndGetContainedPlaceholders, replaceSinglePlaceholder } from './hashPlaceholders'; -import { normalize, resolve } from './path'; +import { basename, normalize, resolve } from './path'; +import { SOURCEMAPPING_URL } from './sourceMappingURL'; import { timeEnd, timeStart } from './timers'; interface HashResult { @@ -70,7 +71,9 @@ export async function renderChunks( renderedChunksByPlaceholder, hashesByPlaceholder, outputBundle, - nonHashedChunksWithPlaceholders + nonHashedChunksWithPlaceholders, + pluginDriver, + outputOptions ); timeEnd('transform chunks', 2); @@ -286,20 +289,45 @@ function addChunksToBundle( renderedChunksByPlaceholder: Map, hashesByPlaceholder: Map, outputBundle: OutputBundleWithPlaceholders, - nonHashedChunksWithPlaceholders: RenderedChunkWithPlaceholders[] + nonHashedChunksWithPlaceholders: RenderedChunkWithPlaceholders[], + pluginDriver: PluginDriver, + options: NormalizedOutputOptions ) { for (const { chunk, code, fileName, map } of renderedChunksByPlaceholder.values()) { - const updatedCode = replacePlaceholders(code, hashesByPlaceholder); + let updatedCode = replacePlaceholders(code, hashesByPlaceholder); const finalFileName = replacePlaceholders(fileName, hashesByPlaceholder); if (map) { map.file = replacePlaceholders(map.file, hashesByPlaceholder); + updatedCode += emitSourceMapAndGetComment(finalFileName, map, pluginDriver, options); } outputBundle[finalFileName] = chunk.generateOutputChunk(updatedCode, map, hashesByPlaceholder); } for (const { chunk, code, fileName, map } of nonHashedChunksWithPlaceholders) { - const updatedCode = hashesByPlaceholder.size + let updatedCode = hashesByPlaceholder.size ? replacePlaceholders(code, hashesByPlaceholder) : code; + if (map) { + updatedCode += emitSourceMapAndGetComment(fileName, map, pluginDriver, options); + } outputBundle[fileName] = chunk.generateOutputChunk(updatedCode, map, hashesByPlaceholder); } } + +function emitSourceMapAndGetComment( + fileName: string, + map: SourceMap, + pluginDriver: PluginDriver, + { sourcemap, sourcemapBaseUrl }: NormalizedOutputOptions +) { + let url: string; + if (sourcemap === 'inline') { + url = map.toUrl(); + } else { + const sourcemapFileName = `${basename(fileName)}.map`; + url = sourcemapBaseUrl + ? new URL(sourcemapFileName, sourcemapBaseUrl).toString() + : sourcemapFileName; + pluginDriver.emitFile({ fileName: `${fileName}.map`, source: map.toString(), type: 'asset' }); + } + return sourcemap === 'hidden' ? '' : `//# ${SOURCEMAPPING_URL}=${url}\n`; +} diff --git a/test/cli/samples/stdout-single-input/_expected.js b/test/cli/samples/stdout-single-input/_expected.js index 70449540ecd..c7ae3534c56 100644 --- a/test/cli/samples/stdout-single-input/_expected.js +++ b/test/cli/samples/stdout-single-input/_expected.js @@ -1,3 +1,2 @@ console.log( 42 ); - //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibWFpbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zb2xlLmxvZyggNDIgKTtcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsRUFBRSJ9 diff --git a/test/function/samples/sourcemap-inline-generatebundle/_config.js b/test/function/samples/sourcemap-inline-generatebundle/_config.js new file mode 100644 index 00000000000..85720d5db90 --- /dev/null +++ b/test/function/samples/sourcemap-inline-generatebundle/_config.js @@ -0,0 +1,26 @@ +const assert = require('assert'); + +module.exports = { + description: 'includes inline sourcemap comments in generateBundle hook', + options: { + plugins: [ + { + name: 'test', + generateBundle(options, bundle) { + assert.deepStrictEqual(Object.keys(bundle), ['main.js']); + assert.strictEqual( + bundle['main.js'].code, + `'use strict'; + +var main = 42; + +module.exports = main; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibWFpbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCA0MjtcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLFdBQWUsRUFBRTs7OzsifQ== +` + ); + } + } + ], + output: { sourcemap: 'inline' } + } +}; diff --git a/test/function/samples/sourcemap-inline-generatebundle/main.js b/test/function/samples/sourcemap-inline-generatebundle/main.js new file mode 100644 index 00000000000..7a4e8a723a4 --- /dev/null +++ b/test/function/samples/sourcemap-inline-generatebundle/main.js @@ -0,0 +1 @@ +export default 42; diff --git a/test/function/samples/sourcemap-true-generatebundle/_config.js b/test/function/samples/sourcemap-true-generatebundle/_config.js new file mode 100644 index 00000000000..74edbb0d46a --- /dev/null +++ b/test/function/samples/sourcemap-true-generatebundle/_config.js @@ -0,0 +1,33 @@ +const assert = require('assert'); + +module.exports = { + description: 'emits sourcemaps before generateBundle hook', + options: { + plugins: [ + { + name: 'test', + generateBundle(options, bundle) { + assert.deepStrictEqual(Object.keys(bundle), ['main.js', 'main.js.map']); + assert.strictEqual( + bundle['main.js'].code, + `'use strict'; + +var main = 42; + +module.exports = main; +//# sourceMappingURL=main.js.map +` + ); + assert.deepStrictEqual(bundle['main.js.map'], { + fileName: 'main.js.map', + name: undefined, + source: + '{"version":3,"file":"main.js","sources":["main.js"],"sourcesContent":["export default 42;\\n"],"names":[],"mappings":";;AAAA,WAAe,EAAE;;;;"}', + type: 'asset' + }); + } + } + ], + output: { sourcemap: true } + } +}; diff --git a/test/function/samples/sourcemap-true-generatebundle/main.js b/test/function/samples/sourcemap-true-generatebundle/main.js new file mode 100644 index 00000000000..7a4e8a723a4 --- /dev/null +++ b/test/function/samples/sourcemap-true-generatebundle/main.js @@ -0,0 +1 @@ +export default 42; diff --git a/test/sourcemaps/samples/existing-external-sourcemaps-combined/_config.js b/test/sourcemaps/samples/existing-external-sourcemaps-combined/_config.js index 73a37f7cc12..3497a5ae1dc 100644 --- a/test/sourcemaps/samples/existing-external-sourcemaps-combined/_config.js +++ b/test/sourcemaps/samples/existing-external-sourcemaps-combined/_config.js @@ -3,6 +3,6 @@ const assert = require('assert'); module.exports = { description: 'removes sourcemap comments', async test(code) { - assert.ok(!code.includes('sourceMappingURL')); + assert.ok(!code.includes('sourceMappingURL=main.js.map')); } }; diff --git a/test/sourcemaps/samples/existing-inline-sourcemaps-combined/_config.js b/test/sourcemaps/samples/existing-inline-sourcemaps-combined/_config.js index 36c14ef2529..62926cc356a 100644 --- a/test/sourcemaps/samples/existing-inline-sourcemaps-combined/_config.js +++ b/test/sourcemaps/samples/existing-inline-sourcemaps-combined/_config.js @@ -3,6 +3,6 @@ const assert = require('assert'); module.exports = { description: 'removes existing inline sourcemaps', async test(code) { - assert.ok(!code.includes('sourceMappingURL')); + assert.ok(!code.includes('sourceMappingURL=data')); } };