@samitouri / QOS-React / commits / 0e0b69321a

Run Closure on non-minified prod builds, too (#28827)

In #26446 we started publishing non-minified versions of our production build artifacts, along with source maps, for easier debugging of React when running in production mode. The way it's currently set up is that these builds are generated *before* Closure compiler has run. Which means it's missing many of the optimizations that are in the final build, like dead code elimination. This PR changes the build process to run Closure on the non-minified production builds, too, by moving the sourcemap generation to later in the pipeline. The non-minified builds will still preserve the original symbol names, and we'll use Prettier to add back whitespace. This is the exact same approach we've been using for years to generate production builds for Meta. The idea is that the only difference between the minified and non- minified builds is whitespace and symbol mangling. The semantic structure of the program should be identical. To implement this, I disabled symbol mangling when running Closure compiler. Then, in a later step, the symbols are mangled by Terser. This is when the source maps are generated.

Andrew Clark committed Apr 19, 2024 at 14:22 UTC 0e0b69321a6fcfe8a3eaae3b1016beb110437b38
4 files changed +185 -158
package.json
+1
@@ -94,6 +94,7 @@
94 "shelljs": "^0.8.5",
95 "signedsource": "^2.0.0",
96 "targz": "^1.0.1",
97 + "terser": "^5.30.3",
98 "through2": "^3.0.1",
99 "tmp": "^0.1.0",
100 "typescript": "^3.7.5",
scripts/rollup/build.js
+129 -124
@@ -23,6 +23,7 @@ const Packaging = require('./packaging');
23 const {asyncRimRaf} = require('./utils');
24 const codeFrame = require('@babel/code-frame');
25 const Wrappers = require('./wrappers');
26 +const minify = require('terser').minify;
27
28 const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL;
29
@@ -455,137 +456,58 @@ function getPlugins(
456 );
457 },
458 },
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.
470 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
503 prettier({
504 parser: 'flow',
505 singleQuote: false,
506 trailingComma: 'none',
507 bracketSpacing: true,
508 }),
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',
511 renderChunk(source) {
512 return Wrappers.wrapWithLicenseHeader(
513 source,
@@ -596,6 +518,89 @@ function getPlugins(
518 );
519 },
520 },
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 + },
604 // Record bundle size.
605 sizes({
606 getSize: (size, gzip) => {
scripts/rollup/plugins/closure-plugin.js
+1 -5
@@ -19,20 +19,16 @@ function compile(flags) {
19 });
20 }
21
22 -module.exports = function closure(flags = {}, {needsSourcemaps}) {
22 +module.exports = function closure(flags = {}) {
23 return {
24 name: 'scripts/rollup/plugins/closure-plugin',
25 async renderChunk(code, chunk, options) {
26 const inputFile = tmp.fileSync();
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 -
28 // Tell Closure what JS source file to read, and optionally what sourcemap file to write
29 const finalFlags = {
30 ...flags,
31 js: inputFile.name,
35 - ...(needsSourcemaps && {create_source_map: sourcemapPath}),
32 };
33
34 await writeFileAsync(inputFile.name, code, 'utf8');
yarn.lock
+54 -29
@@ -2453,16 +2453,35 @@
2453 "@jridgewell/sourcemap-codec" "^1.4.10"
2454 "@jridgewell/trace-mapping" "^0.3.9"
2455
2456 +"@jridgewell/gen-mapping@^0.3.5":
2457 + version "0.3.5"
2458 + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
2459 + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
2460 + dependencies:
2461 + "@jridgewell/set-array" "^1.2.1"
2462 + "@jridgewell/sourcemap-codec" "^1.4.10"
2463 + "@jridgewell/trace-mapping" "^0.3.24"
2464 +
2465 "@jridgewell/resolve-uri@3.1.0":
2466 version "3.1.0"
2467 resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
2468 integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
2469
2470 +"@jridgewell/resolve-uri@^3.1.0":
2471 + version "3.1.2"
2472 + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
2473 + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
2474 +
2475 "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
2476 version "1.1.2"
2477 resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
2478 integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
2479
2480 +"@jridgewell/set-array@^1.2.1":
2481 + version "1.2.1"
2482 + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
2483 + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
2484 +
2485 "@jridgewell/source-map@^0.3.2":
2486 version "0.3.3"
2487 resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
@@ -2471,12 +2490,20 @@
2490 "@jridgewell/gen-mapping" "^0.3.0"
2491 "@jridgewell/trace-mapping" "^0.3.9"
2492
2493 +"@jridgewell/source-map@^0.3.3":
2494 + version "0.3.6"
2495 + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a"
2496 + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
2497 + dependencies:
2498 + "@jridgewell/gen-mapping" "^0.3.5"
2499 + "@jridgewell/trace-mapping" "^0.3.25"
2500 +
2501 "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
2502 version "1.4.14"
2503 resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
2504 integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
2505
2479 -"@jridgewell/sourcemap-codec@^1.4.15":
2506 +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
2507 version "1.4.15"
2508 resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
2509 integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@@ -2497,6 +2524,14 @@
2524 "@jridgewell/resolve-uri" "3.1.0"
2525 "@jridgewell/sourcemap-codec" "1.4.14"
2526
2527 +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
2528 + version "0.3.25"
2529 + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
2530 + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
2531 + dependencies:
2532 + "@jridgewell/resolve-uri" "^3.1.0"
2533 + "@jridgewell/sourcemap-codec" "^1.4.14"
2534 +
2535 "@leichtgewicht/ip-codec@^2.0.1":
2536 version "2.0.4"
2537 resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@@ -3689,6 +3724,11 @@ acorn@^8.1.0, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.1:
3724 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
3725 integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
3726
3727 +acorn@^8.8.2:
3728 + version "8.11.3"
3729 + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
3730 + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
3731 +
3732 adbkit-logcat@^1.1.0:
3733 version "1.1.0"
3734 resolved "https://registry.yarnpkg.com/adbkit-logcat/-/adbkit-logcat-1.1.0.tgz#01d7f9b0cef9093a30bcb3b007efff301508962f"
@@ -14555,7 +14595,7 @@ string-natural-compare@^3.0.1:
14595 resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
14596 integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
14597
14558 -"string-width-cjs@npm:string-width@^4.2.0":
14598 +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3:
14599 version "4.2.3"
14600 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
14601 integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -14590,15 +14630,6 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
14630 is-fullwidth-code-point "^3.0.0"
14631 strip-ansi "^6.0.0"
14632
14593 -string-width@^4.2.3:
14594 - version "4.2.3"
14595 - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
14596 - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
14597 - dependencies:
14598 - emoji-regex "^8.0.0"
14599 - is-fullwidth-code-point "^3.0.0"
14600 - strip-ansi "^6.0.1"
14601 -
14633 string-width@^5.0.1, string-width@^5.1.2:
14634 version "5.1.2"
14635 resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -14659,7 +14690,7 @@ string_decoder@~1.1.1:
14690 dependencies:
14691 safe-buffer "~5.1.0"
14692
14662 -"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
14693 +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
14694 version "6.0.1"
14695 resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
14696 integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -14694,13 +14725,6 @@ strip-ansi@^6.0.0:
14725 dependencies:
14726 ansi-regex "^5.0.0"
14727
14697 -strip-ansi@^6.0.1:
14698 - version "6.0.1"
14699 - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
14700 - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
14701 - dependencies:
14702 - ansi-regex "^5.0.1"
14703 -
14728 strip-ansi@^7.0.1:
14729 version "7.1.0"
14730 resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -15014,6 +15038,16 @@ terser@^5.16.8:
15038 commander "^2.20.0"
15039 source-map-support "~0.5.20"
15040
15041 +terser@^5.30.3:
15042 + version "5.30.3"
15043 + resolved "https://registry.yarnpkg.com/terser/-/terser-5.30.3.tgz#f1bb68ded42408c316b548e3ec2526d7dd03f4d2"
15044 + integrity sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==
15045 + dependencies:
15046 + "@jridgewell/source-map" "^0.3.3"
15047 + acorn "^8.8.2"
15048 + commander "^2.20.0"
15049 + source-map-support "~0.5.20"
15050 +
15051 test-exclude@^6.0.0:
15052 version "6.0.0"
15053 resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@@ -16136,7 +16170,7 @@ workerize-loader@^2.0.2:
16170 dependencies:
16171 loader-utils "^2.0.0"
16172
16139 -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
16173 +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
16174 version "7.0.0"
16175 resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
16176 integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -16154,15 +16188,6 @@ wrap-ansi@^6.2.0:
16188 string-width "^4.1.0"
16189 strip-ansi "^6.0.0"
16190
16157 -wrap-ansi@^7.0.0:
16158 - version "7.0.0"
16159 - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
16160 - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
16161 - dependencies:
16162 - ansi-styles "^4.0.0"
16163 - string-width "^4.1.0"
16164 - strip-ansi "^6.0.0"
16165 -
16191 wrap-ansi@^8.1.0:
16192 version "8.1.0"
16193 resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"