From 0f73e2ace5774b5b3e78584c6d1ed90d204a319e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levente=20Krizs=C3=A1n?= Date: Sat, 26 Aug 2023 20:03:41 +0200 Subject: [PATCH] fix(esm-shim): pattern matching (#1560) * fix(esm-shim): correctly find where to place shim in file * test(esm-shim): add test cases for files with esm imports * test(esm-shim): use correct snapshots after linting change * test(esm-shim): use correct snapshots for testing --- packages/esm-shim/src/utils.ts | 30 ++++++------ .../test/fixtures/cjs-multiple-imports.js | 10 ++++ .../test/fixtures/cjs-single-import.js | 7 +++ packages/esm-shim/test/snapshots/test.js.md | 44 ++++++++++++++++++ packages/esm-shim/test/snapshots/test.js.snap | Bin 398 -> 538 bytes packages/esm-shim/test/test.js | 26 +++++++++++ 6 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 packages/esm-shim/test/fixtures/cjs-multiple-imports.js create mode 100644 packages/esm-shim/test/fixtures/cjs-single-import.js diff --git a/packages/esm-shim/src/utils.ts b/packages/esm-shim/src/utils.ts index ddf14be5d..a1bb1d504 100644 --- a/packages/esm-shim/src/utils.ts +++ b/packages/esm-shim/src/utils.ts @@ -13,24 +13,27 @@ function matchAllPolyfill(input: string, pattern: string | RegExp): RegExpMatchA return []; } + let idx = 0; for (let i = 0; i < result.length; i++) { - output.push(result[i].match(new RegExp(pattern)) || []); + const match: RegExpMatchArray = [result[i]]; + match.index = idx; + idx += result[i].length; + output.push(match); } return output; } -export function matchAll(regex: RegExp, input: string, addition: Record) { - const matches = []; - for (const match of matchAllPolyfill(input, regex)) { - matches.push({ - ...addition, - ...match.groups, - code: match[0], - start: match.index, - end: (match.index || 0) + match[0].length - }); +function findPositionToInsertShim(input: string, pattern: RegExp) { + let lastImport; + // mimicking behavior of `String.matchAll` as it returns an iterator, not an array + for (const match of matchAllPolyfill(input, pattern)) { + lastImport = match; } - return matches; + if (!lastImport) { + return 0; + } + + return (lastImport.index || 0) + lastImport[0].length; } export function provideCJSSyntax(code: string): Output | null { @@ -38,8 +41,7 @@ export function provideCJSSyntax(code: string): Output | null { return null; } - const lastESMImport = matchAll(ESMStaticImportRegex, code, { type: 'static' }).pop(); - const indexToAppend = lastESMImport ? lastESMImport.end : 0; + const indexToAppend = findPositionToInsertShim(code, ESMStaticImportRegex); const s = new MagicString(code); s.appendRight(indexToAppend, ESMShim); diff --git a/packages/esm-shim/test/fixtures/cjs-multiple-imports.js b/packages/esm-shim/test/fixtures/cjs-multiple-imports.js new file mode 100644 index 000000000..4026ed793 --- /dev/null +++ b/packages/esm-shim/test/fixtures/cjs-multiple-imports.js @@ -0,0 +1,10 @@ +import { constants } from 'node:crypto'; + +import MagicString from 'magic-string'; + +const child = require('child'); + +const s = new MagicString(''); +const c = constants.SEP; + +export { child, s, c }; diff --git a/packages/esm-shim/test/fixtures/cjs-single-import.js b/packages/esm-shim/test/fixtures/cjs-single-import.js new file mode 100644 index 000000000..a66296f07 --- /dev/null +++ b/packages/esm-shim/test/fixtures/cjs-single-import.js @@ -0,0 +1,7 @@ +import MagicString from 'magic-string'; + +const child = require('child'); + +const s = new MagicString(''); + +export { child, s }; diff --git a/packages/esm-shim/test/snapshots/test.js.md b/packages/esm-shim/test/snapshots/test.js.md index 2541760b5..e0176a0cb 100644 --- a/packages/esm-shim/test/snapshots/test.js.md +++ b/packages/esm-shim/test/snapshots/test.js.md @@ -49,3 +49,47 @@ Generated by [AVA](https://avajs.dev). ␊ exports.child = child;␊ ` + +## inject cjs shim for esm output with a single import statement + +> Snapshot 1 + + `import MagicString from 'magic-string';␊ + ␊ + ␊ + // -- Shims --␊ + import cjsUrl from 'url';␊ + import cjsPath from 'path';␊ + import cjsModule from 'module';␊ + const __filename = cjsUrl.fileURLToPath(import.meta.url);␊ + const __dirname = cjsPath.dirname(__filename);␊ + const require = cjsModule.createRequire(import.meta.url);␊ + const child = require('child');␊ + ␊ + const s = new MagicString('');␊ + ␊ + export { child, s };␊ + ` + +## inject cjs shim for esm output with multiple import statements + +> Snapshot 1 + + `import { constants } from 'node:crypto';␊ + import MagicString from 'magic-string';␊ + ␊ + ␊ + // -- Shims --␊ + import cjsUrl from 'url';␊ + import cjsPath from 'path';␊ + import cjsModule from 'module';␊ + const __filename = cjsUrl.fileURLToPath(import.meta.url);␊ + const __dirname = cjsPath.dirname(__filename);␊ + const require = cjsModule.createRequire(import.meta.url);␊ + const child = require('child');␊ + ␊ + const s = new MagicString('');␊ + const c = constants.SEP;␊ + ␊ + export { c, child, s };␊ + ` diff --git a/packages/esm-shim/test/snapshots/test.js.snap b/packages/esm-shim/test/snapshots/test.js.snap index 2a0c38554c07136c33a779968c86b13adf478699..5143ca0dc03c6ea36f374daf0e044269d9c0dabe 100644 GIT binary patch literal 538 zcmV+#0_FWdRzVIQrP6F}J(HlBEzy;zz;Fjki*R$B=N$Q$*rCMa!V6`V{Wv_fq*7yjoEvyyPfPR-R`Y ziKQxX%8L~SX=O;s-%)FcVfc3n>{8LZz?NP}&0PnSix!lxTPXhkWi)yd zXFcK0DYKxBjHzQrH%l+30Az ziHp@-$_iF@P!gnTDDo#nh2I5RHm?#tpGO_;3@#fY2@Yl zWCv-7Ko2N#?Z@$c6X3cmP`M7^&_Vp6Mf|=dPVK`wIVG{{h{4!gz7_t5L))<0O5?vt cBj$&mHJfbu;kc!lgO+R_0Ti5Ov;PMG09DEicK`qY literal 398 zcmV;90df98RzVdHOfy52mE)dFXYY8Y9XV|B#-qNc{BOUzv7R sYJ^?J)rURb=V7>MRCJY8j)Oxh82Wzx_O^~JDb3oA=PtD>Le~QT09G%(egFUf diff --git a/packages/esm-shim/test/test.js b/packages/esm-shim/test/test.js index 4419fa90f..3f9e5f8e4 100644 --- a/packages/esm-shim/test/test.js +++ b/packages/esm-shim/test/test.js @@ -41,3 +41,29 @@ test.serial('not inject cjs shim for cjs output', async (t) => { t.snapshot(output.code); t.falsy(output.map); }); + +test.serial('inject cjs shim for esm output with a single import statement', async (t) => { + const bundle = await rollup({ + input: 'test/fixtures/cjs-single-import.js', + plugins: [esmShim()], + external: ['magic-string'] + }); + const result = await bundle.generate({ format: 'es' }); + t.is(result.output.length, 1); + const [output] = result.output; + t.snapshot(output.code); + t.falsy(output.map); +}); + +test.serial('inject cjs shim for esm output with multiple import statements', async (t) => { + const bundle = await rollup({ + input: 'test/fixtures/cjs-multiple-imports.js', + plugins: [esmShim()], + external: ['magic-string', 'node:crypto'] + }); + const result = await bundle.generate({ format: 'es' }); + t.is(result.output.length, 1); + const [output] = result.output; + t.snapshot(output.code); + t.falsy(output.map); +});