@@ -10,6 +10,8 @@ const replace = require('@rollup/plugin-replace');
10
10
const stripBanner = require ( 'rollup-plugin-strip-banner' ) ;
11
11
const chalk = require ( 'chalk' ) ;
12
12
const resolve = require ( '@rollup/plugin-node-resolve' ) . nodeResolve ;
13
+ const MagicString = require ( 'magic-string' ) ;
14
+ const remapping = require ( '@ampproject/remapping' ) ;
13
15
const fs = require ( 'fs' ) ;
14
16
const argv = require ( 'minimist' ) ( process . argv . slice ( 2 ) ) ;
15
17
const Modules = require ( './modules' ) ;
@@ -148,6 +150,7 @@ function getBabelConfig(
148
150
presets : [ ] ,
149
151
plugins : [ ...babelPlugins ] ,
150
152
babelHelpers : 'bundled' ,
153
+ sourcemap : false ,
151
154
} ;
152
155
if ( isDevelopment ) {
153
156
options . plugins . push (
@@ -381,6 +384,21 @@ function getPlugins(
381
384
382
385
const { isUMDBundle, shouldStayReadable} = getBundleTypeFlags ( bundleType ) ;
383
386
387
+ const needsMinifiedByClosure = isProduction && bundleType !== ESM_PROD ;
388
+
389
+ // Only generate sourcemaps for true "production" build artifacts
390
+ // that will be used by bundlers, such as `react-dom.production.min.js`.
391
+ // UMD and "profiling" builds are rarely used and not worth having sourcemaps.
392
+ const needsSourcemaps =
393
+ needsMinifiedByClosure &&
394
+ ! isProfiling &&
395
+ ! isUMDBundle &&
396
+ ! shouldStayReadable ;
397
+
398
+ // For builds with sourcemaps, capture the minified code Closure generated
399
+ // so it can be used to help construct the final sourcemap contents.
400
+ let chunkCodeAfterClosureCompiler = undefined ;
401
+
384
402
return [
385
403
// Keep dynamic imports as externals
386
404
dynamicImports ( ) ,
@@ -390,7 +408,7 @@ function getPlugins(
390
408
const transformed = flowRemoveTypes ( code ) ;
391
409
return {
392
410
code : transformed . toString ( ) ,
393
- map : transformed . generateMap ( ) ,
411
+ map : null ,
394
412
} ;
395
413
} ,
396
414
} ,
@@ -419,6 +437,7 @@ function getPlugins(
419
437
) ,
420
438
// Remove 'use strict' from individual source files.
421
439
{
440
+ name : "remove 'use strict'" ,
422
441
transform ( source ) {
423
442
return source . replace ( / [ ' " ] u s e s t r i c t [ " ' ] / g, '' ) ;
424
443
} ,
@@ -440,35 +459,44 @@ function getPlugins(
440
459
isUMDBundle && entry === 'react-art' && commonjs ( ) ,
441
460
// Apply dead code elimination and/or minification.
442
461
// closure doesn't yet support leaving ESM imports intact
443
- isProduction &&
444
- bundleType !== ESM_PROD &&
445
- closure ( {
446
- compilation_level : 'SIMPLE' ,
447
- language_in : 'ECMASCRIPT_2020' ,
448
- language_out :
449
- bundleType === NODE_ES2015
450
- ? 'ECMASCRIPT_2020'
451
- : bundleType === BROWSER_SCRIPT
452
- ? 'ECMASCRIPT5'
453
- : 'ECMASCRIPT5_STRICT' ,
454
- emit_use_strict :
455
- bundleType !== BROWSER_SCRIPT &&
456
- bundleType !== ESM_PROD &&
457
- bundleType !== ESM_DEV ,
458
- env : 'CUSTOM' ,
459
- warning_level : 'QUIET' ,
460
- apply_input_source_maps : false ,
461
- use_types_for_optimization : false ,
462
- process_common_js_modules : false ,
463
- rewrite_polyfills : false ,
464
- inject_libraries : false ,
465
- allow_dynamic_import : true ,
466
-
467
- // Don't let it create global variables in the browser.
468
- // https://github.com/facebook/react/issues/10909
469
- assume_function_wrapper : ! isUMDBundle ,
470
- renaming : ! shouldStayReadable ,
471
- } ) ,
462
+ needsMinifiedByClosure &&
463
+ closure (
464
+ {
465
+ compilation_level : 'SIMPLE' ,
466
+ language_in : 'ECMASCRIPT_2020' ,
467
+ language_out :
468
+ bundleType === NODE_ES2015
469
+ ? 'ECMASCRIPT_2020'
470
+ : bundleType === BROWSER_SCRIPT
471
+ ? 'ECMASCRIPT5'
472
+ : 'ECMASCRIPT5_STRICT' ,
473
+ emit_use_strict :
474
+ bundleType !== BROWSER_SCRIPT &&
475
+ bundleType !== ESM_PROD &&
476
+ bundleType !== ESM_DEV ,
477
+ env : 'CUSTOM' ,
478
+ warning_level : 'QUIET' ,
479
+ source_map_include_content : true ,
480
+ use_types_for_optimization : false ,
481
+ process_common_js_modules : false ,
482
+ rewrite_polyfills : false ,
483
+ inject_libraries : false ,
484
+ allow_dynamic_import : true ,
485
+
486
+ // Don't let it create global variables in the browser.
487
+ // https://github.com/facebook/react/issues/10909
488
+ assume_function_wrapper : ! isUMDBundle ,
489
+ renaming : ! shouldStayReadable ,
490
+ } ,
491
+ { needsSourcemaps}
492
+ ) ,
493
+ needsSourcemaps && {
494
+ name : 'chunk-after-closure' ,
495
+ renderChunk ( code , config , options ) {
496
+ // Side effect - grab the code as Closure mangled it
497
+ chunkCodeAfterClosureCompiler = code ;
498
+ } ,
499
+ } ,
472
500
// Add the whitespace back if necessary.
473
501
shouldStayReadable &&
474
502
prettier ( {
@@ -479,6 +507,7 @@ function getPlugins(
479
507
} ) ,
480
508
// License and haste headers, top-level `if` blocks.
481
509
{
510
+ name : 'license-and-headers' ,
482
511
renderChunk ( source ) {
483
512
return Wrappers . wrapBundle (
484
513
source ,
@@ -490,6 +519,69 @@ function getPlugins(
490
519
) ;
491
520
} ,
492
521
} ,
522
+ needsSourcemaps && {
523
+ name : 'generate-prod-bundle-sourcemaps' ,
524
+ async renderChunk ( codeAfterLicense , chunk , options , meta ) {
525
+ // We want to generate a sourcemap that shows the production bundle source
526
+ // as it existed before Closure Compiler minified that chunk.
527
+ // We also need to apply any license/wrapper text adjustments to that
528
+ // sourcemap, so that the mapped locations line up correctly.
529
+
530
+ // We can split the final chunk code to figure out what got added around
531
+ // the code from the Closure step.
532
+ const [ licensePrefix , licensePostfix ] = codeAfterLicense . split (
533
+ chunkCodeAfterClosureCompiler
534
+ ) ;
535
+
536
+ const transformedSource = new MagicString (
537
+ chunkCodeAfterClosureCompiler
538
+ ) ;
539
+
540
+ // Apply changes so we can generate a sourcemap for this step
541
+ if ( licensePrefix ) {
542
+ transformedSource . prepend ( licensePrefix ) ;
543
+ }
544
+
545
+ if ( licensePostfix ) {
546
+ transformedSource . append ( licensePostfix ) ;
547
+ }
548
+
549
+ // Use a path like `node_modules/react/cjs/react.production.min.js.map` for the sourcemap file
550
+ const finalSourcemapPath = options . file . replace ( '.js' , '.js.map' ) ;
551
+
552
+ // Read the sourcemap that Closure wrote to disk
553
+ const sourcemapAfterClosure = JSON . parse (
554
+ fs . readFileSync ( finalSourcemapPath , 'utf8' )
555
+ ) ;
556
+
557
+ // Use a name like `react.production.js` for the "pre-minified" sourcemap contents
558
+ const fileWithoutMin = filename . replace ( '.min' , '' ) ;
559
+
560
+ // CC generated a file list that only contains the tempfile name.
561
+ // Replace that with a more meaningful "source" name for this bundle.
562
+ sourcemapAfterClosure . sources = [ fileWithoutMin ] ;
563
+ sourcemapAfterClosure . file = filename ;
564
+
565
+ // Create an additional sourcemap adjusted for the license header contents
566
+ const mapAfterLicense = transformedSource . generateMap ( {
567
+ file : filename ,
568
+ includeContent : true ,
569
+ hires : true ,
570
+ } ) ;
571
+
572
+ // Merge the Closure sourcemap and the with-license sourcemap together
573
+ const finalCombinedSourcemap = remapping (
574
+ [ mapAfterLicense , sourcemapAfterClosure ] ,
575
+ ( ) => null
576
+ ) ;
577
+
578
+ // Overwrite the Closure-generated file with the final combined sourcemap
579
+ fs . writeFileSync (
580
+ finalSourcemapPath ,
581
+ JSON . stringify ( finalCombinedSourcemap )
582
+ ) ;
583
+ } ,
584
+ } ,
493
585
// Record bundle size.
494
586
sizes ( {
495
587
getSize : ( size , gzip ) => {
0 commit comments