@@ -23,6 +23,7 @@ const Packaging = require('./packaging');
2323const  { asyncRimRaf}  =  require ( './utils' ) ; 
2424const  codeFrame  =  require ( '@babel/code-frame' ) ; 
2525const  Wrappers  =  require ( './wrappers' ) ; 
26+ const  minify  =  require ( 'terser' ) . minify ; 
2627
2728const  RELEASE_CHANNEL  =  process . env . RELEASE_CHANNEL ; 
2829
@@ -455,137 +456,58 @@ function getPlugins(
455456          ) ; 
456457        } , 
457458      } , 
458-       // License and haste headers for artifacts with sourcemaps 
459-       // For artifacts with sourcemaps we apply these headers 
460-       // before passing sources to the Closure compiler, which will be building sourcemaps 
461-       needsSourcemaps  &&  { 
462-         name : 'license-and-signature-header-for-artifacts-with-sourcemaps' , 
463-         renderChunk ( source )  { 
464-           return  Wrappers . wrapWithLicenseHeader ( 
465-             source , 
466-             bundleType , 
467-             globalName , 
468-             filename , 
469-             moduleType 
470-           ) ; 
471-         } , 
472-       } , 
473-       // Apply dead code elimination and/or minification. 
474-       // closure doesn't yet support leaving ESM imports intact 
459+       // For production builds, compile with Closure. We do this even for the 
460+       // "non-minified" production builds because Closure is much better at 
461+       // minification than what most applications use. During this step, we do 
462+       // preserve the original symbol names, though, so the resulting code is 
463+       // relatively readable. 
464+       // 
465+       // For the minified builds, the names will be mangled later. 
466+       // 
467+       // We don't bother with sourcemaps at this step. The sourcemaps we publish 
468+       // are only for whitespace and symbol renaming; they don't map back to 
469+       // before Closure was applied. 
475470      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 :  true , 
502-              renaming :  ! shouldStayReadable , 
503-           } , 
504-           { needsSourcemaps } 
505-         ) , 
506-       // Add the whitespace back if necessary. 
507-       shouldStayReadable   && 
471+         closure ( { 
472+           compilation_level :  'SIMPLE' , 
473+           language_in : 'ECMASCRIPT_2020 ' , 
474+           language_out : 
475+             bundleType   ===   NODE_ES2015 
476+               ?  'ECMASCRIPT_2020' 
477+               :  bundleType   ===   BROWSER_SCRIPT 
478+               ?  'ECMASCRIPT5' 
479+               :  'ECMASCRIPT5_STRICT' , 
480+           emit_use_strict : 
481+             bundleType   !==   BROWSER_SCRIPT   && 
482+             bundleType  !==  ESM_PROD  && 
483+             bundleType  !==  ESM_DEV , 
484+           env :  'CUSTOM' , 
485+           warning_level : 'QUIET ' , 
486+           source_map_include_content :  true , 
487+           use_types_for_optimization :  false , 
488+           process_common_js_modules : false , 
489+           rewrite_polyfills : false , 
490+           inject_libraries : false , 
491+           allow_dynamic_import :  true , 
492+ 
493+            // Don't let it create global variables in the browser. 
494+           // https://github.com/facebook/react/issues/10909  
495+           assume_function_wrapper :  true , 
496+ 
497+           // Don't rename symbols (variable names, functions, etc). This will 
498+           // be handled in a later step. 
499+           renaming :  false , 
500+         } ) , 
501+       needsMinifiedByClosure   && 
502+          // Add the whitespace back 
508503        prettier ( { 
509504          parser : 'flow' , 
510505          singleQuote : false , 
511506          trailingComma : 'none' , 
512507          bracketSpacing : true , 
513508        } ) , 
514-       needsSourcemaps  &&  { 
515-         name : 'generate-prod-bundle-sourcemaps' , 
516-         async  renderChunk ( minifiedCodeWithChangedHeader ,  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-           const  outputFolder  =  path . dirname ( options . file ) ; 
526- 
527-           // Read the sourcemap that Closure wrote to disk 
528-           const  sourcemapAfterClosure  =  JSON . parse ( 
529-             fs . readFileSync ( finalSourcemapPath ,  'utf8' ) 
530-           ) ; 
531- 
532-           // Represent the "original" bundle as a file with no `.min` in the name 
533-           const  filenameWithoutMin  =  filename . replace ( '.min' ,  '' ) ; 
534-           // There's _one_ artifact where the incoming filename actually contains 
535-           // a folder name: "use-sync-external-store-shim/with-selector.production.js". 
536-           // The output path already has the right structure, but we need to strip this 
537-           // down to _just_ the JS filename. 
538-           const  preMinifiedFilename  =  path . basename ( filenameWithoutMin ) ; 
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-           // that represents "the bundled source before minification". 
543-           sourcemapAfterClosure . sources  =  [ preMinifiedFilename ] ; 
544-           sourcemapAfterClosure . file  =  filename ; 
545- 
546-           // All our code is considered "third-party" and should be ignored by default. 
547-           sourcemapAfterClosure . ignoreList  =  [ 0 ] ; 
548- 
549-           // We'll write the pre-minified source to disk as a separate file. 
550-           // Because it sits on disk, there's no need to have it in the `sourcesContent` array. 
551-           // That also makes the file easier to read, and available for use by scripts. 
552-           // This should be the only file in the array. 
553-           const  [ preMinifiedBundleSource ]  = 
554-             sourcemapAfterClosure . sourcesContent ; 
555- 
556-           // Remove this entirely - we're going to write the file to disk instead. 
557-           delete  sourcemapAfterClosure . sourcesContent ; 
558- 
559-           const  preMinifiedBundlePath  =  path . join ( 
560-             outputFolder , 
561-             preMinifiedFilename 
562-           ) ; 
563- 
564-           // Write the original source to disk as a separate file 
565-           fs . writeFileSync ( preMinifiedBundlePath ,  preMinifiedBundleSource ) ; 
566- 
567-           // Overwrite the Closure-generated file with the final combined sourcemap 
568-           fs . writeFileSync ( 
569-             finalSourcemapPath , 
570-             JSON . stringify ( sourcemapAfterClosure ) 
571-           ) ; 
572- 
573-           // Add the sourcemap URL to the actual bundle, so that tools pick it up 
574-           const  sourceWithMappingUrl  = 
575-             minifiedCodeWithChangedHeader  + 
576-             `\n//# sourceMappingURL=${ finalSourcemapFilename }  ; 
577- 
578-           return  { 
579-             code : sourceWithMappingUrl , 
580-             map : null , 
581-           } ; 
582-         } , 
583-       } , 
584-       // License and haste headers for artifacts without sourcemaps 
585-       // Primarily used for FB-artifacts, which should preserve specific format of the header 
586-       // Which potentially can be changed by Closure minification 
587-       ! needsSourcemaps  &&  { 
588-         name : 'license-and-signature-header-for-artifacts-without-sourcemaps' , 
509+       { 
510+         name : 'license-and-signature-header' , 
589511        renderChunk ( source )  { 
590512          return  Wrappers . wrapWithLicenseHeader ( 
591513            source , 
@@ -596,6 +518,89 @@ function getPlugins(
596518          ) ; 
597519        } , 
598520      } , 
521+       isProduction  && 
522+         ! shouldStayReadable  &&  { 
523+           name : 'mangle-symbol-names' , 
524+           async  renderChunk ( code ,  chunk ,  options ,  meta )  { 
525+             // Minify the code by mangling symbol names. We already ran Closure 
526+             // on this code, so stuff like dead code elimination and inlining 
527+             // has already happened. This step is purely to rename the symbols, 
528+             // which we asked Closure to preserve. 
529+             // 
530+             // The only reason this is a separate step from Closure is so we 
531+             // can publish non-mangled versions of the code for easier debugging 
532+             // in production. We also publish sourcemaps that map back to the 
533+             // non-mangled code (*not* the pre-Closure code). 
534+ 
535+             const  outputFolder  =  path . dirname ( options . file ) ; 
536+ 
537+             // Represent the "original" bundle as a file with no `.min` in the name 
538+             const  filenameWithoutMin  =  filename . replace ( '.min' ,  '' ) ; 
539+             // There's _one_ artifact where the incoming filename actually contains 
540+             // a folder name: "use-sync-external-store-shim/with-selector.production.js". 
541+             // The output path already has the right structure, but we need to strip this 
542+             // down to _just_ the JS filename. 
543+             const  preMinifiedFilename  =  path . basename ( filenameWithoutMin ) ; 
544+             const  preMinifiedBundlePath  =  path . join ( 
545+               outputFolder , 
546+               preMinifiedFilename 
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+             const  finalSourcemapFilename  =  path . basename ( finalSourcemapPath ) ; 
552+ 
553+             const  terserOptions  =  { 
554+               // Don't bother compressing. Closure already did that. 
555+               compress : false , 
556+               toplevel : true , 
557+               // Mangle the symbol names. 
558+               mangle : { 
559+                 toplevel : true , 
560+               } , 
561+             } ; 
562+             if  ( needsSourcemaps )  { 
563+               terserOptions . sourceMap  =  { 
564+                 // Used to set the `file` field in the sourcemap 
565+                 filename : filename , 
566+                 // Used to set `# sourceMappingURL=` in the compiled code 
567+                 url : finalSourcemapFilename , 
568+               } ; 
569+             } 
570+ 
571+             const  minifiedResult  =  await  minify ( 
572+               { [ preMinifiedFilename ] : code } , 
573+               terserOptions 
574+             ) ; 
575+ 
576+             // Create the directory if it doesn't already exist 
577+             fs . mkdirSync ( outputFolder ,  { recursive : true } ) ; 
578+ 
579+             if  ( needsSourcemaps )  { 
580+               const  sourcemapJSON  =  JSON . parse ( minifiedResult . map ) ; 
581+ 
582+               // All our code is considered "third-party" and should be ignored 
583+               // by default 
584+               sourcemapJSON . ignoreList  =  [ 0 ] ; 
585+ 
586+               // Write the sourcemap to disk 
587+               fs . writeFileSync ( 
588+                 finalSourcemapPath , 
589+                 JSON . stringify ( sourcemapJSON ) 
590+               ) ; 
591+             } 
592+ 
593+             // Write the original source to disk as a separate file 
594+             fs . writeFileSync ( preMinifiedBundlePath ,  code ) ; 
595+ 
596+             return  { 
597+               code : minifiedResult . code , 
598+               // TODO: Maybe we should use Rollup's sourcemap feature instead 
599+               // of writing it to disk manually? 
600+               map : null , 
601+             } ; 
602+           } , 
603+         } , 
599604      // Record bundle size. 
600605      sizes ( { 
601606        getSize : ( size ,  gzip )  =>  { 
0 commit comments