@@ -10,6 +10,8 @@ const replace = require('@rollup/plugin-replace');
1010const stripBanner = require ( 'rollup-plugin-strip-banner' ) ;
1111const chalk = require ( 'chalk' ) ;
1212const resolve = require ( '@rollup/plugin-node-resolve' ) . nodeResolve ;
13+ const MagicString = require ( 'magic-string' ) ;
14+ const remapping = require ( '@ampproject/remapping' ) ;
1315const fs = require ( 'fs' ) ;
1416const argv = require ( 'minimist' ) ( process . argv . slice ( 2 ) ) ;
1517const Modules = require ( './modules' ) ;
@@ -148,6 +150,7 @@ function getBabelConfig(
148150 presets : [ ] ,
149151 plugins : [ ...babelPlugins ] ,
150152 babelHelpers : 'bundled' ,
153+ sourcemap : false ,
151154 } ;
152155 if ( isDevelopment ) {
153156 options . plugins . push (
@@ -381,6 +384,21 @@ function getPlugins(
381384
382385 const { isUMDBundle, shouldStayReadable} = getBundleTypeFlags ( bundleType ) ;
383386
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+
384402 return [
385403 // Keep dynamic imports as externals
386404 dynamicImports ( ) ,
@@ -390,7 +408,7 @@ function getPlugins(
390408 const transformed = flowRemoveTypes ( code ) ;
391409 return {
392410 code : transformed . toString ( ) ,
393- map : transformed . generateMap ( ) ,
411+ map : null ,
394412 } ;
395413 } ,
396414 } ,
@@ -419,6 +437,7 @@ function getPlugins(
419437 ) ,
420438 // Remove 'use strict' from individual source files.
421439 {
440+ name : "remove 'use strict'" ,
422441 transform ( source ) {
423442 return source . replace ( / [ ' " ] u s e s t r i c t [ " ' ] / g, '' ) ;
424443 } ,
@@ -440,35 +459,44 @@ function getPlugins(
440459 isUMDBundle && entry === 'react-art' && commonjs ( ) ,
441460 // Apply dead code elimination and/or minification.
442461 // 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+ } ,
472500 // Add the whitespace back if necessary.
473501 shouldStayReadable &&
474502 prettier ( {
@@ -479,6 +507,7 @@ function getPlugins(
479507 } ) ,
480508 // License and haste headers, top-level `if` blocks.
481509 {
510+ name : 'license-and-headers' ,
482511 renderChunk ( source ) {
483512 return Wrappers . wrapBundle (
484513 source ,
@@ -490,6 +519,66 @@ function getPlugins(
490519 ) ;
491520 } ,
492521 } ,
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+ // CC generated a file list that only contains the tempfile name.
558+ // Replace that with a more meaningful "source" name for this bundle.
559+ sourcemapAfterClosure . sources = [ filename ] ;
560+ sourcemapAfterClosure . file = filename ;
561+
562+ // Create an additional sourcemap adjusted for the license header contents
563+ const mapAfterLicense = transformedSource . generateMap ( {
564+ file : filename ,
565+ includeContent : true ,
566+ hires : true ,
567+ } ) ;
568+
569+ // Merge the Closure sourcemap and the with-license sourcemap together
570+ const finalCombinedSourcemap = remapping (
571+ [ mapAfterLicense , sourcemapAfterClosure ] ,
572+ ( ) => null
573+ ) ;
574+
575+ // Overwrite the Closure-generated file with the final combined sourcemap
576+ fs . writeFileSync (
577+ finalSourcemapPath ,
578+ JSON . stringify ( finalCombinedSourcemap )
579+ ) ;
580+ } ,
581+ } ,
493582 // Record bundle size.
494583 sizes ( {
495584 getSize : ( size , gzip ) => {
0 commit comments