diff --git a/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/package.json b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/package.json new file mode 100644 index 0000000000..11394423a1 --- /dev/null +++ b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/package.json @@ -0,0 +1,10 @@ +{ + "name": "command-pack-picomatch-hoist-false", + "version": "1.0.0", + "type": "module", + "devDependencies": { + "typescript": "^6.0.3", + "vite-plus": "0.1.23" + }, + "packageManager": "pnpm@11.5.0" +} diff --git a/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/pnpm-workspace.yaml b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/pnpm-workspace.yaml new file mode 100644 index 0000000000..9b21cd4885 --- /dev/null +++ b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/pnpm-workspace.yaml @@ -0,0 +1 @@ +hoist: false diff --git a/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/snap.txt b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/snap.txt new file mode 100644 index 0000000000..d34c3488b2 --- /dev/null +++ b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/snap.txt @@ -0,0 +1,7 @@ +> vp install --ignore-scripts +> vp pack src/index.ts 2>&1 +ℹ entry: src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in ms diff --git a/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/src/index.ts b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/src/index.ts new file mode 100644 index 0000000000..efeee5db16 --- /dev/null +++ b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/src/index.ts @@ -0,0 +1 @@ +export const value = 1; diff --git a/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/steps.json b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/steps.json new file mode 100644 index 0000000000..7b95ee9a26 --- /dev/null +++ b/packages/cli/snap-tests-global/command-pack-picomatch-hoist-false/steps.json @@ -0,0 +1,11 @@ +{ + "linkCheckoutPackages": true, + "commands": [ + { + "command": "vp install --ignore-scripts", + "ignoreOutput": true, + "timeout": 120000 + }, + "vp pack src/index.ts 2>&1" + ] +} diff --git a/packages/core/build-support/find-create-require.ts b/packages/core/build-support/find-create-require.ts index 81e376d2eb..40cb631d45 100644 --- a/packages/core/build-support/find-create-require.ts +++ b/packages/core/build-support/find-create-require.ts @@ -54,14 +54,11 @@ export async function replaceThirdPartyCjsRequires( const thirdPartyModules = new Set(); // Find all createRequire patterns and their require calls - const results = [ - findCreateRequireInStaticImports(ast), - findCreateRequireInGlobalModule(ast), - ].filter((result): result is { requireVarName: string; calls: RequireCall[] } => Boolean(result)); + const results = [findCreateRequireInStaticImports(ast), findCreateRequireInGlobalModule(ast)]; // Collect all third-party require calls const replacements: RequireCall[] = []; - for (const { calls } of results) { + for (const calls of results) { for (const call of calls) { if (isThirdPartyModule(call.module)) { const parts = call.module.split('/'); @@ -106,21 +103,51 @@ function findRequireCalls(ast: ParseResult, requireVarName: string): RequireCall return; } - // Extract the first argument (module specifier) - if (node.arguments.length === 0) { - return; + const call = getLiteralRequireArgument(node); + if (call) { + calls.push(call); } - const arg = node.arguments[0]; - if (arg.type !== 'Literal') { + }, + }); + + visitor.visit(ast.program); + return calls; +} + +function getLiteralRequireArgument(node: CallExpression): RequireCall | undefined { + if (node.arguments.length === 0) { + return undefined; + } + const arg = node.arguments[0]; + if (arg.type !== 'Literal') { + return undefined; + } + const value = (arg as { value: unknown; start: number; end: number }).value; + if (typeof value !== 'string') { + return undefined; + } + return { + module: value, + start: arg.start, + end: arg.end, + }; +} + +function findDirectCreateRequireCalls( + ast: ParseResult, + isCreateRequireCall: (node: CallExpression) => boolean, +): RequireCall[] { + const calls: RequireCall[] = []; + + const visitor = new Visitor({ + CallExpression(node: CallExpression) { + if (node.callee.type !== 'CallExpression' || !isCreateRequireCall(node.callee)) { return; } - const value = (arg as { value: unknown; start: number; end: number }).value; - if (typeof value === 'string') { - calls.push({ - module: value, - start: arg.start, - end: arg.end, - }); + + const call = getLiteralRequireArgument(node); + if (call) { + calls.push(call); } }, }); @@ -133,16 +160,14 @@ function findRequireCalls(ast: ParseResult, requireVarName: string): RequireCall * Find createRequire from static imports and return the require variable name + all require calls * Handles: `import { createRequire } from "node:module"` then `const require = createRequire(...)` */ -function findCreateRequireInStaticImports( - ast: ParseResult, -): { requireVarName: string; calls: RequireCall[] } | undefined { +function findCreateRequireInStaticImports(ast: ParseResult): RequireCall[] { // Find import from 'module' or 'node:module' const importFromModule = ast.module.staticImports.find((imt) => { const { value } = imt.moduleRequest; return value === 'node:module' || value === 'module'; }); if (!importFromModule) { - return undefined; + return []; } // Find the createRequire import entry @@ -150,7 +175,7 @@ function findCreateRequireInStaticImports( return entry.importName.name === 'createRequire'; }); if (!createRequireEntry) { - return undefined; + return []; } const createRequireLocalName = createRequireEntry.localName.value; @@ -174,14 +199,14 @@ function findCreateRequireInStaticImports( }); varVisitor.visit(ast.program); - if (!requireVarName) { - return undefined; - } - - // Find all calls to the require variable - const calls = findRequireCalls(ast, requireVarName); + const calls = requireVarName ? findRequireCalls(ast, requireVarName) : []; + calls.push( + ...findDirectCreateRequireCalls(ast, (node) => { + return node.callee.type === 'Identifier' && node.callee.name === createRequireLocalName; + }), + ); - return { requireVarName, calls }; + return calls; } // Helper to check if an expression is `process` or `globalThis.process` @@ -240,9 +265,7 @@ function isGetBuiltinModuleCall(expr: Expression): boolean { * Handles: `const require = globalThis.process.getBuiltinModule("module").createRequire(import.meta.url)` * Or: `const require = process.getBuiltinModule("module").createRequire(import.meta.url)` */ -function findCreateRequireInGlobalModule( - ast: ParseResult, -): { requireVarName: string; calls: RequireCall[] } | undefined { +function findCreateRequireInGlobalModule(ast: ParseResult): RequireCall[] { let requireVarName: string | undefined; const visitor = new Visitor({ @@ -276,12 +299,16 @@ function findCreateRequireInGlobalModule( visitor.visit(ast.program); - if (!requireVarName) { - return undefined; - } - - // Find all calls to the require variable - const calls = findRequireCalls(ast, requireVarName); + const calls = requireVarName ? findRequireCalls(ast, requireVarName) : []; + calls.push( + ...findDirectCreateRequireCalls(ast, (node) => { + if (node.callee.type !== 'MemberExpression' || node.callee.computed) { + return false; + } + const callee = node.callee as StaticMemberExpression; + return callee.property.name === 'createRequire' && isGetBuiltinModuleCall(callee.object); + }), + ); - return { requireVarName, calls }; + return calls; }