Expand pnpm resolution support (based on #22) #23
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
pnpm Dependency Resolution Plugin
This PR introduces a custom Vite plugin that solves transitive dependency resolution issues when using pnpm package manager, workspaces, or symlinked modules in ApostropheCMS projects.
Problem Statement
pnpm uses a different dependency resolution strategy than npm/yarn - it doesn't hoist all packages to the top-level
node_modules. This causes Vite builds to fail when trying to resolve transitive dependencies, particularly ESM-only packages that your direct dependencies require but aren't explicitly listed in your project'spackage.json.Example: Your project depends on
package-a, which depends onesm-utils. With pnpm,package-acan resolveesm-utilsfrom its ownnode_modules, but your project root cannot resolve it directly, causing Vite build failures.Key Features
Fallback-First Resolution
Multi-Root Resolution Strategy
When fallback is needed, the plugin tries Node resolution from multiple roots in order:
Symlink-Aware
fs.realpathSync()preserveSymlinks: falsewhen active to prevent module duplication and failing esbuild optimizationsTwo-Phase Operation
Flexible Configuration
Three configuration methods with priority cascade:
app.jsapos.vite.config.js/mjsaposVitekeyConfiguration values:
true- Enable with defaults (['@apostrophecms/vite', 'apostrophe'])['pkg1', 'pkg2']- Enable with custom packages (merged with defaults)falseor[]- Explicitly disableHow It Works
Plugin Architecture
The plugin implements two Vite hooks:
Resolution Root Discovery
pnpm-workspace.yamlorpnpm-lock.yamlpackage.jsonConfiguration Loading
Priority cascade in
computeResolvePackages()(internals.js):Console Logging (Intentional)
The plugin includes console logs that are intentionally left in to provide visibility:
These help developers:
You'll see these logs during:
npm run build)Important Considerations
Clear Cache When Switching Package Managers
IMPORTANT: If you switch between pnpm and npm, or make significant dependency changes, run:
This clears Vite's cache directory to prevent stale resolution artifacts. Note that Vite typically handles dependency changes automatically, but switching package managers requires manual cache clearing.
Plugin Disabled by Default
You must explicitly enable the plugin - it won't activate automatically. This ensures no unexpected behavior changes for existing projects.
preserveSymlinks Impact
The base config sets
preserveSymlinks: trueby default. When the plugin is active, it overrides this tofalse. Setting it totruewith pnpm causes significant module duplication (4x larger bundle sizes) and crashes the dev server.Configuration Priority
Module options take precedence over all other config. If you're not seeing expected behavior, check your
app.jsmodule configuration first.Array Configs Merge with Defaults
When you provide
['my-package'], the result is['@apostrophecms/vite', 'apostrophe', 'my-package']. Arrays are always merged with the default packages.esbuild Plugin Currently Disabled
The esbuild portion (lines 47-84 in
vite-plugin-pnpm-resolve.mjs) is commented out. SettingpreserveSymlinks: falseplus standard Vite resolution handles most cases. The esbuild plugin can be uncommented if needed for edge cases during dev/HMR.Backward Compatibility
This change is fully backward compatible. Existing projects using npm will continue to work without any modifications:
preserveSymlinks: true(existing behavior)resolvePackageRootsconfigurationTesting in the Playground
Quick Start - Vite demo
Quick Start - Vite demo workspaces
The demo is a monorepo with workspace packages
After
pnpm install, verify the workspace is properly linked:The demo contains "counter" widgets (client side) developed in Vue and React (Svelte plugin is disabled due to outdated setup). This is a front-end build and HMR test as well. Use
node app @apostrophecms/user:add admin adminto add admin user to your project and edit home page or create Counter Apps Page, add Vue counter widget(s) and ensure:pretty-msLocal Development Setup
To modify the plugin and see changes in real-time:
The demo contains "counter" widgets (client side) developed in Vue and React (Svelte plugin is disabled due to outdated setup). This is a front-end build and HMR test as well. Add plugins to the home page or a new Counter Apps Page to ensure frontent works correctly. The counters should work and be persisted after page reloads.
Uncommenting
hmr: 'apos'inmodules/@apostrophecms/asset/index.jswill switch the dev server HMR from public source (the widgets) to the admin UI.Hot reload workflow:
../vite/lib/vite-plugin-pnpm-resolve.mjsrsin the vite-demo terminal (restarts server via nodemon)Testing Different Configurations
Try different plugin configurations in
vite-demo/app.js:Also test configurations via
apos.vite.config.jsandpackage.jsonto see priority in action.After each change, use
rsto restart and observe the console logs.Documentation
Full documentation added to README.md:
resolvePackageRootsconfiguration