Skip to content

Commit e485811

Browse files
committed
Generate sourcemaps for production build artifacts
1 parent 3da7a99 commit e485811

File tree

3 files changed

+128
-46
lines changed

3 files changed

+128
-46
lines changed

scripts/rollup/build.js

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const stripBanner = require('rollup-plugin-strip-banner');
1111
const chalk = require('chalk');
1212
const resolve = require('@rollup/plugin-node-resolve').nodeResolve;
1313
const fs = require('fs');
14+
const path = require('path');
1415
const argv = require('minimist')(process.argv.slice(2));
1516
const Modules = require('./modules');
1617
const Bundles = require('./bundles');
@@ -148,6 +149,7 @@ function getBabelConfig(
148149
presets: [],
149150
plugins: [...babelPlugins],
150151
babelHelpers: 'bundled',
152+
sourcemap: false,
151153
};
152154
if (isDevelopment) {
153155
options.plugins.push(
@@ -386,6 +388,25 @@ function getPlugins(
386388

387389
const {isUMDBundle, shouldStayReadable} = getBundleTypeFlags(bundleType);
388390

391+
const needsMinifiedByClosure = isProduction && bundleType !== ESM_PROD;
392+
393+
// Any other packages that should specifically _not_ have sourcemaps
394+
const sourcemapPackageExcludes = [
395+
// Having `//#sourceMappingUrl` in this file breaks `ReactDevToolsHooksIntegration-test.js`,
396+
// and this is an internal
397+
'react-debug-tools',
398+
];
399+
400+
// Only generate sourcemaps for true "production" build artifacts
401+
// that will be used by bundlers, such as `react-dom.production.min.js`.
402+
// UMD and "profiling" builds are rarely used and not worth having sourcemaps.
403+
const needsSourcemaps =
404+
needsMinifiedByClosure &&
405+
!isProfiling &&
406+
!isUMDBundle &&
407+
!sourcemapPackageExcludes.includes(entry) &&
408+
!shouldStayReadable;
409+
389410
return [
390411
// Keep dynamic imports as externals
391412
dynamicImports(),
@@ -395,7 +416,7 @@ function getPlugins(
395416
const transformed = flowRemoveTypes(code);
396417
return {
397418
code: transformed.toString(),
398-
map: transformed.generateMap(),
419+
map: null,
399420
};
400421
},
401422
},
@@ -424,6 +445,7 @@ function getPlugins(
424445
),
425446
// Remove 'use strict' from individual source files.
426447
{
448+
name: "remove 'use strict'",
427449
transform(source) {
428450
return source.replace(/['"]use strict["']/g, '');
429451
},
@@ -443,47 +465,9 @@ function getPlugins(
443465
// I'm going to port "art" to ES modules to avoid this problem.
444466
// Please don't enable this for anything else!
445467
isUMDBundle && entry === 'react-art' && commonjs(),
446-
// Apply dead code elimination and/or minification.
447-
// closure doesn't yet support leaving ESM imports intact
448-
isProduction &&
449-
bundleType !== ESM_PROD &&
450-
closure({
451-
compilation_level: 'SIMPLE',
452-
language_in: 'ECMASCRIPT_2020',
453-
language_out:
454-
bundleType === NODE_ES2015
455-
? 'ECMASCRIPT_2020'
456-
: bundleType === BROWSER_SCRIPT
457-
? 'ECMASCRIPT5'
458-
: 'ECMASCRIPT5_STRICT',
459-
emit_use_strict:
460-
bundleType !== BROWSER_SCRIPT &&
461-
bundleType !== ESM_PROD &&
462-
bundleType !== ESM_DEV,
463-
env: 'CUSTOM',
464-
warning_level: 'QUIET',
465-
apply_input_source_maps: false,
466-
use_types_for_optimization: false,
467-
process_common_js_modules: false,
468-
rewrite_polyfills: false,
469-
inject_libraries: false,
470-
allow_dynamic_import: true,
471-
472-
// Don't let it create global variables in the browser.
473-
// https://github.com/facebook/react/issues/10909
474-
assume_function_wrapper: !isUMDBundle,
475-
renaming: !shouldStayReadable,
476-
}),
477-
// Add the whitespace back if necessary.
478-
shouldStayReadable &&
479-
prettier({
480-
parser: 'flow',
481-
singleQuote: false,
482-
trailingComma: 'none',
483-
bracketSpacing: true,
484-
}),
485468
// License and haste headers, top-level `if` blocks.
486469
{
470+
name: 'license-and-headers',
487471
renderChunk(source) {
488472
return Wrappers.wrapBundle(
489473
source,
@@ -495,6 +479,85 @@ function getPlugins(
495479
);
496480
},
497481
},
482+
// Apply dead code elimination and/or minification.
483+
// closure doesn't yet support leaving ESM imports intact
484+
needsMinifiedByClosure &&
485+
closure(
486+
{
487+
compilation_level: 'SIMPLE',
488+
language_in: 'ECMASCRIPT_2020',
489+
language_out:
490+
bundleType === NODE_ES2015
491+
? 'ECMASCRIPT_2020'
492+
: bundleType === BROWSER_SCRIPT
493+
? 'ECMASCRIPT5'
494+
: 'ECMASCRIPT5_STRICT',
495+
emit_use_strict:
496+
bundleType !== BROWSER_SCRIPT &&
497+
bundleType !== ESM_PROD &&
498+
bundleType !== ESM_DEV,
499+
env: 'CUSTOM',
500+
warning_level: 'QUIET',
501+
source_map_include_content: true,
502+
use_types_for_optimization: false,
503+
process_common_js_modules: false,
504+
rewrite_polyfills: false,
505+
inject_libraries: false,
506+
allow_dynamic_import: true,
507+
508+
// Don't let it create global variables in the browser.
509+
// https://github.com/facebook/react/issues/10909
510+
assume_function_wrapper: !isUMDBundle,
511+
renaming: !shouldStayReadable,
512+
},
513+
{needsSourcemaps}
514+
),
515+
// Add the whitespace back if necessary.
516+
shouldStayReadable &&
517+
prettier({
518+
parser: 'flow',
519+
singleQuote: false,
520+
trailingComma: 'none',
521+
bracketSpacing: true,
522+
}),
523+
needsSourcemaps && {
524+
name: 'generate-prod-bundle-sourcemaps',
525+
async renderChunk(codeAfterLicense, chunk, options, meta) {
526+
// We want to generate a sourcemap that shows the production bundle source
527+
// as it existed before Closure Compiler minified that chunk, rather than
528+
// showing the "original" individual source files. This better shows
529+
// what is actually running in the app.
530+
531+
// Use a path like `node_modules/react/cjs/react.production.min.js.map` for the sourcemap file
532+
const finalSourcemapPath = options.file.replace('.js', '.js.map');
533+
const finalSourcemapFilename = path.basename(finalSourcemapPath);
534+
535+
// Read the sourcemap that Closure wrote to disk
536+
const sourcemapAfterClosure = JSON.parse(
537+
fs.readFileSync(finalSourcemapPath, 'utf8')
538+
);
539+
540+
// CC generated a file list that only contains the tempfile name.
541+
// Replace that with a more meaningful "source" name for this bundle.
542+
sourcemapAfterClosure.sources = [filename];
543+
sourcemapAfterClosure.file = filename;
544+
545+
// Overwrite the Closure-generated file with the final combined sourcemap
546+
fs.writeFileSync(
547+
finalSourcemapPath,
548+
JSON.stringify(sourcemapAfterClosure)
549+
);
550+
551+
// Add the sourcemap URL to the actual bundle, so that tools pick it up
552+
const sourceWithMappingUrl =
553+
codeAfterLicense + `\n//# sourceMappingURL=${finalSourcemapFilename}`;
554+
555+
return {
556+
code: sourceWithMappingUrl,
557+
map: null,
558+
};
559+
},
560+
},
498561
// Record bundle size.
499562
sizes({
500563
getSize: (size, gzip) => {

scripts/rollup/plugins/closure-plugin.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,25 @@ function compile(flags) {
1919
});
2020
}
2121

22-
module.exports = function closure(flags = {}) {
22+
module.exports = function closure(flags = {}, {needsSourcemaps}) {
2323
return {
2424
name: 'scripts/rollup/plugins/closure-plugin',
25-
async renderChunk(code) {
25+
async renderChunk(code, chunk, options) {
2626
const inputFile = tmp.fileSync();
27-
const tempPath = inputFile.name;
28-
flags = Object.assign({}, flags, {js: tempPath});
29-
await writeFileAsync(tempPath, code, 'utf8');
30-
const compiledCode = await compile(flags);
27+
28+
// Use a path like `node_modules/react/cjs/react.production.min.js.map` for the sourcemap file
29+
const sourcemapPath = options.file.replace('.js', '.js.map');
30+
31+
// Tell Closure what JS source file to read, and optionally what sourcemap file to write
32+
const finalFlags = {
33+
...flags,
34+
js: inputFile.name,
35+
...(needsSourcemaps && {create_source_map: sourcemapPath}),
36+
};
37+
38+
await writeFileAsync(inputFile.name, code, 'utf8');
39+
const compiledCode = await compile(finalFlags);
40+
3141
inputFile.removeCallback();
3242
return {code: compiledCode};
3343
},

scripts/rollup/wrappers.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ ${source}`;
191191
/****************** FB_WWW_DEV ******************/
192192
[FB_WWW_DEV](source, globalName, filename, moduleType) {
193193
return `/**
194+
* @preserve
194195
${license}
195196
*
196197
* @noflow
@@ -211,6 +212,7 @@ ${source}
211212
/****************** FB_WWW_PROD ******************/
212213
[FB_WWW_PROD](source, globalName, filename, moduleType) {
213214
return `/**
215+
* @preserve
214216
${license}
215217
*
216218
* @noflow
@@ -225,6 +227,7 @@ ${source}`;
225227
/****************** FB_WWW_PROFILING ******************/
226228
[FB_WWW_PROFILING](source, globalName, filename, moduleType) {
227229
return `/**
230+
* @preserve
228231
${license}
229232
*
230233
* @noflow
@@ -239,6 +242,7 @@ ${source}`;
239242
/****************** RN_OSS_DEV ******************/
240243
[RN_OSS_DEV](source, globalName, filename, moduleType) {
241244
return `/**
245+
* @preserve
242246
${license}
243247
*
244248
* @noflow
@@ -260,6 +264,7 @@ ${source}
260264
/****************** RN_OSS_PROD ******************/
261265
[RN_OSS_PROD](source, globalName, filename, moduleType) {
262266
return `/**
267+
* @preserve
263268
${license}
264269
*
265270
* @noflow
@@ -275,6 +280,7 @@ ${source}`;
275280
/****************** RN_OSS_PROFILING ******************/
276281
[RN_OSS_PROFILING](source, globalName, filename, moduleType) {
277282
return `/**
283+
* @preserve
278284
${license}
279285
*
280286
* @noflow
@@ -290,6 +296,7 @@ ${source}`;
290296
/****************** RN_FB_DEV ******************/
291297
[RN_FB_DEV](source, globalName, filename, moduleType) {
292298
return `/**
299+
* @preserve
293300
${license}
294301
*
295302
* @noflow
@@ -310,6 +317,7 @@ ${source}
310317
/****************** RN_FB_PROD ******************/
311318
[RN_FB_PROD](source, globalName, filename, moduleType) {
312319
return `/**
320+
* @preserve
313321
${license}
314322
*
315323
* @noflow
@@ -324,6 +332,7 @@ ${source}`;
324332
/****************** RN_FB_PROFILING ******************/
325333
[RN_FB_PROFILING](source, globalName, filename, moduleType) {
326334
return `/**
335+
* @preserve
327336
${license}
328337
*
329338
* @noflow

0 commit comments

Comments
 (0)