From cb579c8ab61aaace9bd748f7f3f3755a9bf052c1 Mon Sep 17 00:00:00 2001 From: XiaoPi <530257315@qq.com> Date: Sun, 15 Oct 2023 23:36:33 +0800 Subject: [PATCH] fix(commonjs): Keep the shebang at the top of the file content (#1610) --- packages/commonjs/src/index.js | 11 ++++++++++- packages/commonjs/src/proxies.js | 10 +++++++--- packages/commonjs/src/transform-commonjs.js | 11 +++++++++-- .../test/fixtures/samples/shebang/main.js | 2 ++ packages/commonjs/test/test.js | 15 +++++++++++++++ 5 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 packages/commonjs/test/fixtures/samples/shebang/main.js diff --git a/packages/commonjs/src/index.js b/packages/commonjs/src/index.js index da40131b5..a7fb65938 100644 --- a/packages/commonjs/src/index.js +++ b/packages/commonjs/src/index.js @@ -271,7 +271,16 @@ export default function commonjs(options = {}) { // entry suffix is just appended to not mess up relative external resolution if (id.endsWith(ENTRY_SUFFIX)) { const acutalId = id.slice(0, -ENTRY_SUFFIX.length); - return getEntryProxy(acutalId, getDefaultIsModuleExports(acutalId), this.getModuleInfo); + const { + meta: { commonjs: commonjsMeta } + } = this.getModuleInfo(acutalId); + const shebang = commonjsMeta?.shebang ?? ''; + return getEntryProxy( + acutalId, + getDefaultIsModuleExports(acutalId), + this.getModuleInfo, + shebang + ); } if (isWrappedId(id, ES_IMPORT_SUFFIX)) { diff --git a/packages/commonjs/src/proxies.js b/packages/commonjs/src/proxies.js index 1cad2e0d5..57b71b867 100644 --- a/packages/commonjs/src/proxies.js +++ b/packages/commonjs/src/proxies.js @@ -44,7 +44,7 @@ export async function getStaticRequireProxy(id, requireReturnsDefault, loadModul return `export { default } from ${JSON.stringify(id)};`; } -export function getEntryProxy(id, defaultIsModuleExports, getModuleInfo) { +export function getEntryProxy(id, defaultIsModuleExports, getModuleInfo, shebang) { const { meta: { commonjs: commonjsMeta }, hasDefaultExport @@ -55,9 +55,13 @@ export function getEntryProxy(id, defaultIsModuleExports, getModuleInfo) { if (hasDefaultExport) { code += `export { default } from ${stringifiedId};`; } - return code; + return shebang + code; } - return getEsImportProxy(id, defaultIsModuleExports); + const result = getEsImportProxy(id, defaultIsModuleExports); + return { + ...result, + code: shebang + result.code + }; } export function getEsImportProxy(id, defaultIsModuleExports) { diff --git a/packages/commonjs/src/transform-commonjs.js b/packages/commonjs/src/transform-commonjs.js index 892e3b943..fd0fe0a9d 100644 --- a/packages/commonjs/src/transform-commonjs.js +++ b/packages/commonjs/src/transform-commonjs.js @@ -478,6 +478,13 @@ export default async function transformCommonjs( magicString.remove(0, commentEnd).trim(); } + let shebang = ''; + if (code.startsWith('#!')) { + const shebangEndPosition = code.indexOf('\n') + 1; + shebang = code.slice(0, shebangEndPosition); + magicString.remove(0, shebangEndPosition).trim(); + } + const exportMode = isEsModule ? 'none' : shouldWrap @@ -561,13 +568,13 @@ function ${requireName} () { magicString .trim() - .prepend(leadingComment + importBlock) + .prepend(shebang + leadingComment + importBlock) .append(exportBlock); return { code: magicString.toString(), map: sourceMap ? magicString.generateMap() : null, syntheticNamedExports: isEsModule || usesRequireWrapper ? false : '__moduleExports', - meta: { commonjs: commonjsMeta } + meta: { commonjs: { ...commonjsMeta, shebang } } }; } diff --git a/packages/commonjs/test/fixtures/samples/shebang/main.js b/packages/commonjs/test/fixtures/samples/shebang/main.js new file mode 100644 index 000000000..f36482021 --- /dev/null +++ b/packages/commonjs/test/fixtures/samples/shebang/main.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +module.exports = 1; diff --git a/packages/commonjs/test/test.js b/packages/commonjs/test/test.js index 1367e9a8a..deb0a810c 100644 --- a/packages/commonjs/test/test.js +++ b/packages/commonjs/test/test.js @@ -1291,3 +1291,18 @@ test('allows the config to be reused', async (t) => { ['bar.js'] ); }); + +test('keep the shebang at the top of the file content', async (t) => { + const bundle = await rollup({ + input: ['fixtures/samples/shebang/main.js'], + plugins: [commonjs()] + }); + + const { output } = await bundle.generate({ + exports: 'auto', + format: 'cjs', + chunkFileNames: '[name].js' + }); + + t.is(output[0].code.startsWith('#!/usr/bin/env node\n'), true); +});