Skip to content

vitest fails to compile under perry.compilePackages: Rollup-bundled CJS-inside-ESM module rejected as ImportExportInScript #851

@proggeramlug

Description

@proggeramlug

Surfaced by the #805 sweep, tier2 list.

Repro

```bash
mkdir /tmp/perry-vitest && cd /tmp/perry-vitest
cat > package.json <<'JSON'
{"name":"d","private":true,"type":"module","dependencies":{"vitest":"*"},"perry":{"compilePackages":["vitest"]}}
JSON
npm install
cat > main.ts <<'TS'
import { expect } from "vitest";
expect(1+1).toBe(2);
TS
perry main.ts -o out
```

Failure

```
Error: Failed to parse vitest/dist/chunks/test.DNmyFkvJ.js: Parse error: Error { error: (12817..12823, ImportExportInScript) }
```

Diagnosis

vitest's dist bundle is Rollup-produced. The top of the file is ESM (`import { ... } from '@vitest/runner'` etc.) but Rollup inlines CJS dependencies as nested IIFEs that contain the original `module.exports = ...` body:

```js
import { ... } from '@vitest/runner'; // ESM at top — module mode
...
function requireTypeDetect () { // around offset 12800
if (hasRequiredTypeDetect) return typeDetect$1.exports;
hasRequiredTypeDetect = 1;
(function (module, exports$1) {
(function (global, factory) {
module.exports = factory(); // CJS body, parser confused
```

`ImportExportInScript` strongly suggests Perry's parser saw `module.exports` patterns earlier and inferred Script mode, then choked when it encountered an `import`/`export` at the top (or vice versa — committed to one mode and rejected the other).

This is not a malformed file — Node and bundlers handle this hybrid bundle shape fine. It's how a lot of modern npm packages are shipped: tree-shaken ESM at the boundary with inlined CJS dependencies preserved inside.

Affected packages (estimated)

Anything built with Rollup's CJS-inlining (`@rollup/plugin-commonjs` with `requireReturnsDefault: "auto"` or similar). vitest is the canonical example, but it's also how Vite ships, how many recent React-tooling packages ship, and probably 20%+ of the current top-1k npm.

Suggested fix

Detect this shape during parse-mode selection: any file with a top-level `import` / `export` statement is always Module mode, regardless of what `module.exports`-style assignments appear deeper in the file. CJS-style references inside Module-mode functions are just plain JS identifiers — they should be parser-acceptable; the issue is only with the mode-selection heuristic.

Part of #793 + #805. Surfaced by the #805 sweep on tier2.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparityNode.js compatibility / parity gaps

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions