Skip to content

Commit 7d71363

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

File tree

3 files changed

+119
-46
lines changed

3 files changed

+119
-46
lines changed

scripts/rollup/build.js

+94-40
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,16 @@ function getPlugins(
386388

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

391+
const needsMinifiedByClosure = isProduction && bundleType !== ESM_PROD;
392+
// Only generate sourcemaps for true "production" build artifacts
393+
// that will be used by bundlers, such as `react-dom.production.min.js`.
394+
// UMD and "profiling" builds are rarely used and not worth having sourcemaps.
395+
const needsSourcemaps =
396+
needsMinifiedByClosure &&
397+
!isProfiling &&
398+
!isUMDBundle &&
399+
!shouldStayReadable;
400+
389401
return [
390402
// Keep dynamic imports as externals
391403
dynamicImports(),
@@ -395,7 +407,7 @@ function getPlugins(
395407
const transformed = flowRemoveTypes(code);
396408
return {
397409
code: transformed.toString(),
398-
map: transformed.generateMap(),
410+
map: null,
399411
};
400412
},
401413
},
@@ -424,6 +436,7 @@ function getPlugins(
424436
),
425437
// Remove 'use strict' from individual source files.
426438
{
439+
name: "remove 'use strict'",
427440
transform(source) {
428441
return source.replace(/['"]use strict["']/g, '');
429442
},
@@ -443,47 +456,9 @@ function getPlugins(
443456
// I'm going to port "art" to ES modules to avoid this problem.
444457
// Please don't enable this for anything else!
445458
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-
}),
485459
// License and haste headers, top-level `if` blocks.
486460
{
461+
name: 'license-and-headers',
487462
renderChunk(source) {
488463
return Wrappers.wrapBundle(
489464
source,
@@ -495,6 +470,85 @@ function getPlugins(
495470
);
496471
},
497472
},
473+
// Apply dead code elimination and/or minification.
474+
// closure doesn't yet support leaving ESM imports intact
475+
needsMinifiedByClosure &&
476+
closure(
477+
{
478+
compilation_level: 'SIMPLE',
479+
language_in: 'ECMASCRIPT_2020',
480+
language_out:
481+
bundleType === NODE_ES2015
482+
? 'ECMASCRIPT_2020'
483+
: bundleType === BROWSER_SCRIPT
484+
? 'ECMASCRIPT5'
485+
: 'ECMASCRIPT5_STRICT',
486+
emit_use_strict:
487+
bundleType !== BROWSER_SCRIPT &&
488+
bundleType !== ESM_PROD &&
489+
bundleType !== ESM_DEV,
490+
env: 'CUSTOM',
491+
warning_level: 'QUIET',
492+
source_map_include_content: true,
493+
use_types_for_optimization: false,
494+
process_common_js_modules: false,
495+
rewrite_polyfills: false,
496+
inject_libraries: false,
497+
allow_dynamic_import: true,
498+
499+
// Don't let it create global variables in the browser.
500+
// https://github.com/facebook/react/issues/10909
501+
assume_function_wrapper: !isUMDBundle,
502+
renaming: !shouldStayReadable,
503+
},
504+
{needsSourcemaps}
505+
),
506+
// Add the whitespace back if necessary.
507+
shouldStayReadable &&
508+
prettier({
509+
parser: 'flow',
510+
singleQuote: false,
511+
trailingComma: 'none',
512+
bracketSpacing: true,
513+
}),
514+
needsSourcemaps && {
515+
name: 'generate-prod-bundle-sourcemaps',
516+
async renderChunk(codeAfterLicense, chunk, options, meta) {
517+
// We want to generate a sourcemap that shows the production bundle source
518+
// as it existed before Closure Compiler minified that chunk, rather than
519+
// showing the "original" individual source files. This better shows
520+
// what is actually running in the app.
521+
522+
// Use a path like `node_modules/react/cjs/react.production.min.js.map` for the sourcemap file
523+
const finalSourcemapPath = options.file.replace('.js', '.js.map');
524+
const finalSourcemapFilename = path.basename(finalSourcemapPath);
525+
526+
// Read the sourcemap that Closure wrote to disk
527+
const sourcemapAfterClosure = JSON.parse(
528+
fs.readFileSync(finalSourcemapPath, 'utf8')
529+
);
530+
531+
// CC generated a file list that only contains the tempfile name.
532+
// Replace that with a more meaningful "source" name for this bundle.
533+
sourcemapAfterClosure.sources = [filename];
534+
sourcemapAfterClosure.file = filename;
535+
536+
// Overwrite the Closure-generated file with the final combined sourcemap
537+
fs.writeFileSync(
538+
finalSourcemapPath,
539+
JSON.stringify(sourcemapAfterClosure)
540+
);
541+
542+
// Add the sourcemap URL to the actual bundle, so that tools pick it up
543+
const sourceWithMappingUrl =
544+
codeAfterLicense + `\n//# sourceMappingURL=${finalSourcemapFilename}`;
545+
546+
return {
547+
code: sourceWithMappingUrl,
548+
map: null,
549+
};
550+
},
551+
},
498552
// Record bundle size.
499553
sizes({
500554
getSize: (size, gzip) => {

scripts/rollup/plugins/closure-plugin.js

+16-6
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

+9
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)