Engineering Journal
Ginexys
Ginexys

Marketplace Launch Errorfix

2026-05-30

vsce package slurps your whole pnpm workspace

TLDR

If you use pnpm workspaces and run vsce package, the default .vscodeignore is not enough. vsce traverses pnpm's symlinked node_modules and pulls in every sibling package, your workspace root files, and any scripts directory. Fix: add explicit ../<sibling>/* patterns and ../../ workspace exclusions to every package's .vscodeignore.

Symptom

You run vsce package in extension/packages/core and get either:

ERROR  invalid relative path: extension/../../pnpm-workspace.yaml

Or a successful package that turns out to be 55 MB because it contains:

Your supposedly-216KB core extension ships with 55 MB of unrelated workspace code.

Root cause

pnpm represents workspace dependencies as symlinks inside node_modules. When packages/core has node_modules/.pnpm/... with symlinks pointing at sibling packages, vsce's file traversal follows them. Most ignore patterns target single-directory exclusions (node_modules/, src/) and miss the parent-directory paths produced by symlink traversal.

The standard .vscodeignore template covers:

src/
node_modules/
tsconfig.json
.vscode/
*/.map
pnpm-lock.yaml
package-lock.json

This is fine for a standalone extension. It fails for pnpm workspaces because:

  1. node_modules/ excludes the local node_modules but vsce can still resolve paths through pnpm's .pnpm/ cache.
  2. The traversal produces paths like extension/../../pnpm-workspace.yaml which escape the package root.
  3. Sibling packages appear as ../tafne/, ../pdf/, etc. and need explicit patterns.

Fix

Add to every package's .vscodeignore:

# Source — only the compiled output (out/) ships
src/
tsconfig.json

Sibling workspace packages — pnpm symlinks would otherwise pull them in

../core/** ../tafne/** ../pdf/** ../schema/** ../pack/**

Workspace root files — vsce would slurp them via symlink traversal

../../* ../../.vscode/** ../../scripts/**

Dev artifacts

node_modules/ .vscode/ */.map */.ts !out/*/.d.ts pnpm-lock.yaml package-lock.json .gitignore .eslintrc*

Each sibling pattern explicitly excludes the directory by relative path. The workspace root patterns ../../ and ../../.vscode/* block traversal beyond the package's own grandparent. You list every sibling because vsce traverses symlinks individually.

After this change, vsce package core produces a 216 KB .vsix with only:

extension/
├── LICENSE.txt
├── changelog.md
├── icon.png
├── package.json
├── readme.md
├── media/vsc-bridge.js
└── out/
    ├── extension.js
    ├── auth/AuthProvider.js
    ├── mcp/tools.js
    ├── router/GinexysRouter.js
    ├── sync/SyncClient.js
    └── webview/html-rewriter.js

Compiled output, license, readme, package manifest. Nothing else.

Guard

Always run vsce ls before vsce package. The ls subcommand prints every file the package will contain. If you see anything starting with ../, your .vscodeignore has a hole.

cd packages/core
vsce ls | head -30

If the first lines contain ../tafne/ or ../../package.json, you have not yet fixed the symlink traversal. Add more patterns. Re-run vsce ls. Iterate until the output contains only the files you intend to ship.

The other early-warning sign is package size. A standalone VS Code extension with no bundled assets should be under 1 MB. If your .vsix is 10+ MB without a clear reason (large worker scripts, embedded media), pnpm workspaces are probably leaking in.

Lesson

vsce was designed for single-package extensions with traditional node_modules. pnpm workspaces violate that assumption by representing siblings as symlinks. The ignore patterns that worked for npm and yarn projects do not transfer cleanly.

There is a --no-dependencies flag on vsce package that disables npm/yarn/pnpm dependency detection. It does not help here, because the issue is filesystem traversal of pnpm's symlink graph, not declared dependencies. The symlinks exist as physical paths, and vsce treats them as files.

The fix is purely declarative: tell vsce which paths to never look at, using the package's own relative position as the anchor. Make those exclusions exhaustive (every sibling, every workspace root file). Then verify with vsce ls before you publish.

You will run this once per extension. After that, the .vscodeignore keeps working. The fix is the cost of using a modern package manager with a tool that has not caught up.

Read this post in the full Engineering Journal →