Skip to content

fix(pack): guard shims banner in unbundle mode to prevent ESM imports leaking into CJS output#2098

Open
AmariahAK wants to merge 1 commit into
voidzero-dev:mainfrom
AmariahAK:main
Open

fix(pack): guard shims banner in unbundle mode to prevent ESM imports leaking into CJS output#2098
AmariahAK wants to merge 1 commit into
voidzero-dev:mainfrom
AmariahAK:main

Conversation

@AmariahAK

Copy link
Copy Markdown

Description

What was the issue

When pack.shims: true and pack.unbundle: true are combined, the generated .cjs file contains raw ESM statements at the top:

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

This is not valid CommonJS — require()-ing the built .cjs throws SyntaxError: Cannot use import statement outside a module at module load. The .mjs output is fine; only the CJS artifact is broken.

Where was the issue

In tsdown's resolveInputOptions(), the unbundle code path pushes the shimsPlugin (which injects an ESM banner) unconditionally — without checking the output format. The bundle-mode path (getShimsInject) already has a format === 'es' && platform === 'node' guard, but the unbundle path never did.

The bug is upstream: rolldown/tsdown#991. The fix was merged but not included in the published tsdown@0.22.4 release.

How I fixed it

Added a build-time patch in bundleTsdown() inside packages/core/build.ts. It runs before the tsdown re-bundle and injects the same format guard that getShimsInject already uses:

 if (shims && !cjsDts) if (unbundle) {
+    if (format === 'es' && platform === 'node') {
         define = { ...define, ...shimsDefine };
         plugins.push(shimsPlugin);
+    }
 } else inject = getShimsInject(format, platform);

Also bumped the tsdown catalog from ^0.22.3 to ^0.22.4 (with @tsdown/css and @tsdown/exe), and updated bundledVersions.tsdown to 0.22.4.

Why this approach

  • Reuse-first: the patch follows the same pattern as brandTsdown(), which already applies string replacements to the bundled tsdown output.
  • Minimal: 35 lines, build-time only, zero runtime overhead.
  • Reversible: marked with a comment referencing the upstream PR and a note to re-evaluate after tsdown >=0.22.5.
  • Alternative considered: waiting for the upstream release — but the fix is one line and the build-time patch is the established mechanism in this codebase.

How to test locally

  1. Initialize submodules:

    git submodule update --init --recursive
  2. Rebuild the core package (the patch runs here):

    pnpm -C packages/core build

    Expected output: Patched shims+unbundle CJS leak in .../build-*.mjs

  3. Create a minimal test project with this config:

    // vite.config.ts
    import { defineConfig } from 'vite-plus';
    export default defineConfig({
      pack: {
        entry: 'src/index.ts',
        shims: true,
        unbundle: true,
        format: ['esm', 'cjs'],
      },
    });
  4. Build and verify:

    vp pack
    node -e "require('./dist/index.cjs')"  # should NOT throw
    node --check dist/index.cjs             # should pass
  5. Run the full test suite:

    pnpm test

Closes #2090

When pack.shims and pack.unbundle are both enabled, the ESM shims
banner (import 'node:path', import.meta.url) leaks into .cjs output,
making it invalid CommonJS.  This patches the bundled tsdown build
during core package build to add a format guard (format === 'es' &&
platform === 'node') around the shimsPlugin in unbundle mode, matching
the existing guard in getShimsInject.

Also bumps tsdown catalog from ^0.22.3 to ^0.22.4 (with @tsdown/css
and @tsdown/exe) and updates bundledVersions.tsdown to 0.22.4.

Upstream fix: rolldown/tsdown#991 (merged but not yet in a published
release).  Re-evaluate after tsdown >=0.22.5.

Fixes voidzero-dev#2090

Co-authored-by: atlarix-agent <agent@atlarix.dev>
@netlify

netlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit bb98d24
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a4e40bd501c5b00081ad1c0

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​tsdown/​exe@​0.22.4711007297100
Addednpm/​@​tsdown/​css@​0.22.4751008097100
Addednpm/​tsdown@​0.22.4981008897100

View full report

@fengmk2

fengmk2 commented Jul 8, 2026

Copy link
Copy Markdown
Member

We should wait for tsdown fix it then upgrade to the fix version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

3 participants