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 2a0c38554..5143ca0dc 100644 Binary files a/packages/esm-shim/test/snapshots/test.js.snap and b/packages/esm-shim/test/snapshots/test.js.snap differ 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); +});