Skip to content

pack: shims + unbundle emits raw ESM imports into .cjs, producing invalid CommonJS #2090

Description

@TheHolyWaffle

Describe the bug

With pack.shims: true and pack.unbundle: true, the generated CommonJS output (.cjs) contains bare ESM statements at the top of the file:

import "node:path";
import "node:url";
import.meta.url;

This is not valid CommonJS, so require()-ing the built .cjs throws at module load, before any code runs:

SyntaxError: Cannot use import statement outside a module

The shim preamble is emitted verbatim into both the .mjs and the .cjs. In the ESM output those statements are legal; in the CommonJS output they are not, and they are neither transformed to their CJS equivalents nor omitted. vp pack reports success (exit 0) and writes the broken artifact.

The trigger is the combination of the two flags. With shims: true and unbundle left at its default, import.meta.url / node: builtins are correctly compiled to their CJS forms (require("url").pathToFileURL(__filename).href, require("node:path")); it is only shims: true + unbundle: true together that leaks the raw ESM preamble.

Expected: the CommonJS build must be valid CommonJS — the shim preamble should be compiled to CJS-safe equivalents (or omitted) in the .cjs, exactly as it is when unbundle is off.

Reproduction

https://github.com/TheHolyWaffle/vite-plus-cjs-shim-leak

Steps to reproduce

npm install
npx vp pack
# build succeeds (exit 0)

# require the CommonJS output the way a CJS consumer would:
node -e "require('./dist/index.cjs')"
# -> SyntaxError: Cannot use import statement outside a module

# the ESM output is fine:
node --input-type=module -e "import('./dist/index.mjs').then(m => console.log(m.under('/tmp')))"
# -> /tmp/sub

Generated dist/index.cjs (note lines 1-3):

import "node:path";
import "node:url";
import.meta.url;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
let node_path = require("node:path");
//#region src/index.ts
function under(dir) {
	return (0, node_path.resolve)(dir, "sub");
}
//#endregion
exports.under = under;

Removing unbundle: true (keeping shims: true) produces valid CommonJS
(let node_path = require("node:path"), no leading import), which loads without error.

System Info

# `vp env current` is not a command in 0.2.3 (errors "Command 'env' not found"),
# so pasting `vp --version` (which includes the tool matrix) plus runtime versions:

$ npx vp --version
vp v0.2.3

Local vite-plus:
  vite-plus  v0.2.3

Tools:
  vite             v8.1.3
  rolldown         v1.1.4
  vitest           v4.1.9
  oxfmt            v0.57.0
  oxlint           v1.72.0
  oxlint-tsgolint  v0.24.0
  tsdown           v0.22.3

$ node --version
v24.16.0

$ npm --version
11.13.0

# OS: macOS (darwin 25.5.0)

Used Package Manager

npm

Logs

$ npx vp pack
ℹ entry: src/index.ts
ℹ Build start
ℹ [ESM] dist/index.mjs  ...
✔ Build complete
ℹ [CJS] dist/index.cjs  ...
✔ Build complete
# exit code: 0 — the invalid .cjs is written without any warning

$ node -e "require('./dist/index.cjs')"
.../dist/index.cjs:1
import "node:path";
^^^^^^
SyntaxError: Cannot use import statement outside a module
    at wrapSafe (node:internal/modules/cjs/loader:1691:18)
    at Module._compile (node:internal/modules/cjs/loader:1734:20)
    ...

Validations

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    Priority

    None yet

    Effort

    None yet

    Target date

    None yet

    Start date

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions