From 851e64fec0a0b12f54e2640af560070793bb731e Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Fri, 17 Feb 2023 09:33:52 -0800 Subject: [PATCH] metro-minify-terser: Don't mutate input options Summary: Mitigates https://github.com/terser/terser/issues/1341 Terser sets properties inlcuding `source_map` and `_destroy_ast` on the given `options.output` (or `options.format`) object, which can affect subsequent calls where we re-use the same config object. Here we take a shallow copy of the given configuration, so `terser` doesn't mutate `metro-minify-terser`'s input. Changelog: [Fix] Mitigate potential source map mismatches with concurrent transformations (Terser [#1341](https://github.com/terser/terser/issues/1341)) Reviewed By: jacdebug, motiz88 Differential Revision: D43362977 fbshipit-source-id: 019f417aa8cc7897c71a6ab2c7db5d0cf916e59d --- .../src/__tests__/terser-issue-1341-test.js | 92 +++++++++++++++++++ packages/metro-minify-terser/src/minifier.js | 5 + 2 files changed, 97 insertions(+) create mode 100644 packages/metro-minify-terser/src/__tests__/terser-issue-1341-test.js diff --git a/packages/metro-minify-terser/src/__tests__/terser-issue-1341-test.js b/packages/metro-minify-terser/src/__tests__/terser-issue-1341-test.js new file mode 100644 index 0000000000..52a2120155 --- /dev/null +++ b/packages/metro-minify-terser/src/__tests__/terser-issue-1341-test.js @@ -0,0 +1,92 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +import minify from '../minifier'; + +const config = { + mangle: { + toplevel: false, + }, + output: { + ascii_only: true, + quote_style: 3, + wrap_iife: true, + }, + sourceMap: { + includeSources: false, + }, + toplevel: false, + compress: { + reduce_funcs: false, + }, +}; + +const BAR = { + filename: '', + code: '__d(function(global,_$$_REQUIRE,_$$_IMPORT_DEFAULT,_$$_IMPORT_ALL,module,exports,_$$_METRO_DEPENDENCY_MAP){"use strict";function bar(){return new Promise(function(resolve){return resolve(_$$_REQUIRE(_$$_METRO_DEPENDENCY_MAP[0]));}).then(function onGlo(glo){makeItThrow(glo);});}function makeItThrow(glo){makeItThrowInner(glo);}function makeItThrowInner(glo){glo.throwSmth();}module.exports=bar;});', + map: { + version: 3, + sources: ['js/RKJSModules/bar.js'], + sourcesContent: [ + "'use strict';\n\nfunction bar() {\n return new Promise(resolve => resolve(require('./segmented/glo.js'))).then(function onGlo(glo) {\n makeItThrow(glo);\n });\n}\n\nfunction makeItThrow(glo) {\n makeItThrowInner(glo);\n}\n\nfunction makeItThrowInner(glo) {\n glo.throwSmth();\n}\n\nmodule.exports = bar;\n", + ], + names: [ + 'bar', + 'Promise', + 'resolve', + 'require', + 'then', + 'onGlo', + 'glo', + 'makeItThrow', + 'makeItThrowInner', + 'throwSmth', + 'module', + 'exports', + ], + mappings: + '2GAAA,YAAY,CAEZ,QAASA,IAAG,EAAG,CACb,MAAO,IAAIC,QAAO,CAAC,SAAAC,OAAO,QAAIA,QAAO,CAACC,WAAO,6BAAsB,CAAC,GAAC,CAACC,IAAI,CAAC,QAASC,MAAK,CAACC,GAAG,CAAE,CAC7FC,WAAW,CAACD,GAAG,CAAC,CAClB,CAAC,CAAC,CACJ,CAEA,QAASC,YAAW,CAACD,GAAG,CAAE,CACxBE,gBAAgB,CAACF,GAAG,CAAC,CACvB,CAEA,QAASE,iBAAgB,CAACF,GAAG,CAAE,CAC7BA,GAAG,CAACG,SAAS,EAAE,CACjB,CAEAC,MAAM,CAACC,OAAO,CAAGX,GAAG,CAAC', + }, + reserved: ['_$$_METRO_DEPENDENCY_MAP'], + config, +}; + +const GLO = { + filename: '', + code: '__d(function(global,_$$_REQUIRE,_$$_IMPORT_DEFAULT,_$$_IMPORT_ALL,module,exports,_$$_METRO_DEPENDENCY_MAP){"use strict";var biz=_$$_REQUIRE(_$$_METRO_DEPENDENCY_MAP[0]);module.exports={throwSmth:function throwSmth(){return biz.throwSmthInner();}};});', + map: { + version: 3, + sources: ['js/RKJSModules/segmented/glo.js'], + sourcesContent: [ + "'use strict';\n\nconst biz = require('./biz');\n\nmodule.exports = {\n throwSmth() {\n return biz.throwSmthInner();\n },\n};", + ], + names: [ + 'biz', + 'require', + 'module', + 'exports', + 'throwSmth', + 'throwSmthInner', + ], + mappings: + '2GAAA,YAAY,CAEZ,GAAMA,IAAG,CAAGC,WAAO,6BAAS,CAE5BC,MAAM,CAACC,OAAO,CAAG,CACfC,SAAS,qBAAG,CACV,MAAOJ,IAAG,CAACK,cAAc,EAAE,CAC7B,CACF,CAAC,CAAC', + }, + reserved: ['_$$_METRO_DEPENDENCY_MAP'], + config, +}; + +test('parallel calls do not clobber each other', async () => { + const [barResult, gloResult] = await Promise.all([minify(BAR), minify(GLO)]); + + const barMap = barResult.map; + const gloMap = gloResult.map; + + expect(gloMap).not.toEqual(barMap); +}); diff --git a/packages/metro-minify-terser/src/minifier.js b/packages/metro-minify-terser/src/minifier.js index b6efc3f477..e81e4c017d 100644 --- a/packages/metro-minify-terser/src/minifier.js +++ b/packages/metro-minify-terser/src/minifier.js @@ -36,6 +36,11 @@ async function minify({ }: MinifierOptions): Promise<{code: string, map: ?string}> { const options = { ...config, + output: { + // Mitigate https://github.com/terser/terser/issues/1341 - Terser may + // set its internal data on this object, so give it a shallow copy. + ...(config.output ?? {}), + }, mangle: config.mangle === false ? false