DEV Community

usapop
usapop

Posted on

The Pitfall of npm publish with pnpm's workspace:* Protocol

TL;DR

  • pnpm's workspace:* is a local-only protocol
  • Publishing with npm publish as-is makes the package unusable for others
  • pnpm publish auto-replaces it, but requires proper setup
  • For simplicity, npm workspaces + normal version specifiers works fine

What Happened

I had a monorepo set up with pnpm, using workspace:* for inter-package dependencies.

// packages/react/package.json
{
  "name": "@my-lib/react",
  "dependencies": {
    "@my-lib/core": "workspace:*"
  }
}
Enter fullscreen mode Exit fullscreen mode

Everything worked fine in local development, so I ran npm publish...

npm warn Invalid dependency "@my-lib/core": "workspace:*"
Enter fullscreen mode Exit fullscreen mode

When someone tried to install the published package:

npm error Could not resolve dependency:
npm error peer workspace:* from @my-lib/react@0.1.0
Enter fullscreen mode Exit fullscreen mode

Why This Happens

workspace:* is a local-only protocol used by pnpm/npm/yarn workspace features.

  • Locally: "reference the package within this workspace"
  • After publishing: unresolvable (npm registry doesn't understand workspace:)

Solutions

Option 1: Use pnpm publish (requires setup)

pnpm can replace workspace:* with actual versions on publish.

workspace:* → *
workspace:^ → ^x.y.z
workspace:~ → ~x.y.z
Enter fullscreen mode Exit fullscreen mode

However, this isn't enabled by default. You need proper configuration:

# pnpm-workspace.yaml
packages:
  - 'packages/*'
Enter fullscreen mode Exit fullscreen mode

And you must use pnpm publish (not npm publish).

Option 2: Manually write versions

Replace workspace:* with actual versions before publishing.

{
  "dependencies": {
    "@my-lib/core": "^1.2.3"
  }
}
Enter fullscreen mode Exit fullscreen mode

Tedious but reliable.

Option 3: Switch to npm workspaces (simplest)

Drop pnpm and use npm workspaces with normal version specifiers from the start.

// package.json (root)
{
  "workspaces": [
    "packages/*"
  ]
}
Enter fullscreen mode Exit fullscreen mode
// packages/react/package.json
{
  "dependencies": {
    "@my-lib/core": "^1.2.3"
  }
}
Enter fullscreen mode Exit fullscreen mode

With npm workspaces, if a matching local package exists, it automatically symlinks. No special workspace:* notation needed.

pnpm and npm Publish to the Same Place

An easy point to misunderstand: both pnpm publish and npm publish go to the same npm registry.

Package managers are just client tools — the publish destination is shared. So:

  • Packages published with pnpm can be installed with npm
  • Packages published with npm can be installed with pnpm
  • Same goes for yarn

Conclusion

Approach Pros Cons
pnpm + workspace:* + pnpm publish Local dev is easy Complex setup, accidents with npm publish
pnpm + manual version specifiers Reliable Version updates are tedious
npm workspaces + normal versions Simple, fewer accidents Lacks pnpm's speed

For small monorepos, npm workspaces is probably enough. If you need pnpm's fast installs for a large project, you'll need to properly set up the pnpm publish workflow.

Hope this helps someone.

References

Top comments (0)