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