Originally detected in #74135
Description
The @wordpress/build package fails to compile SCSS files when used in a pnpm monorepo because the SASS load paths are hardcoded to assume npm's hoisted node_modules structure.
When using pnpm (which doesn't hoist dependencies by default), SCSS imports like @import "@wordpress/base-styles/mixins" fail because the SASS compiler can't find the dependency in the expected location.
Expected behavior: SCSS compilation should work regardless of the package manager used.
Actual behavior: SCSS compilation fails with errors like:
Error: Can't find stylesheet to import.
@import "@wordpress/base-styles/mixins";
Resproduction link
Step-by-step reproduction instructions
- Create a new pnpm monorepo:
mkdir my-wp-plugin && cd my-wp-plugin
pnpm init
- Create
pnpm-workspace.yaml:
- Create a package at
packages/my-editor/package.json:
{
"name": "@my-plugin/editor",
"version": "1.0.0",
"wpScript": true,
"main": "build/index.cjs",
"module": "build-module/index.js",
"dependencies": {
"@wordpress/base-styles": "^5.0.0"
}
}
- Create
packages/my-editor/src/index.js:
export const hello = 'world';
- Create
packages/my-editor/src/style.scss:
@import "@wordpress/base-styles/mixins";
.my-editor {
@include break-small() {
padding: 16px;
}
}
- Add root
package.json config:
{
"name": "my-wp-plugin",
"private": true,
"wpPlugin": {
"scriptGlobal": "myPlugin",
"packageNamespace": "my-plugin"
},
"devDependencies": {
"@wordpress/build": "^0.4.0"
}
}
- Install dependencies and build:
pnpm install
pnpm exec wp-build
Result: Build fails with the following error:
🔨 Building packages...
📝 Phase 1: Transpiling packages...
✘ [ERROR] Error: Can't find stylesheet to import.
╷
1 │ @import "@wordpress/base-styles/mixins";
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
╵
- 1:9 root stylesheet [plugin sass-plugin]
❌ Build failed: Error: Build failed with 1 error
This happens because @wordpress/base-styles is in packages/editor/node_modules/@wordpress/base-styles (pnpm's non-hoisted structure), but @wordpress/build only looks in the root node_modules/.
Root Cause
In packages/wp-build/src/build.mjs, the SASS load paths are hardcoded:
|
loadPaths: [ 'node_modules', path.join( PACKAGES_DIR, 'base-styles' ) ], |
This assumes:
- All dependencies are hoisted to the root
node_modules (npm behavior)
@wordpress/base-styles lives in packages/base-styles (Gutenberg's internal structure)
Neither assumption holds for external consumers using pnpm.
Proposed Solution
Use @manypkg/get-packages to dynamically discover all workspace packages and include each package's node_modules directory in the SASS load paths:
import { getPackages } from '@manypkg/get-packages';
async function getSassLoadPaths() {
const { packages, rootDir } = await getPackages( ROOT_DIR );
return [
// Each workspace package's node_modules (for pnpm's non-hoisted deps)
...packages.map( ( pkg ) => path.join( pkg.dir, 'node_modules' ) ),
// Root node_modules
path.join( rootDir, 'node_modules' ),
// Direct path to base-styles for @wordpress/base-styles imports
path.join( PACKAGES_DIR, 'base-styles' ),
];
}
This ensures SCSS compilation works with:
- npm (hoisted dependencies)
- pnpm (non-hoisted, isolated dependencies)
- yarn (various hoisting modes)
Environment info
@wordpress/build version: 0.4.0 (trunk)
- Node.js: 20.x
- pnpm: 9.x / 10.x
Checklist
Originally detected in #74135
Description
The
@wordpress/buildpackage fails to compile SCSS files when used in a pnpm monorepo because the SASS load paths are hardcoded to assume npm's hoistednode_modulesstructure.When using pnpm (which doesn't hoist dependencies by default), SCSS imports like
@import "@wordpress/base-styles/mixins"fail because the SASS compiler can't find the dependency in the expected location.Expected behavior: SCSS compilation should work regardless of the package manager used.
Actual behavior: SCSS compilation fails with errors like:
Resproduction link
pnpm install && pnpm run buildStep-by-step reproduction instructions
pnpm-workspace.yaml:packages/my-editor/package.json:{ "name": "@my-plugin/editor", "version": "1.0.0", "wpScript": true, "main": "build/index.cjs", "module": "build-module/index.js", "dependencies": { "@wordpress/base-styles": "^5.0.0" } }packages/my-editor/src/index.js:packages/my-editor/src/style.scss:package.jsonconfig:{ "name": "my-wp-plugin", "private": true, "wpPlugin": { "scriptGlobal": "myPlugin", "packageNamespace": "my-plugin" }, "devDependencies": { "@wordpress/build": "^0.4.0" } }pnpm install pnpm exec wp-buildResult: Build fails with the following error:
This happens because
@wordpress/base-stylesis inpackages/editor/node_modules/@wordpress/base-styles(pnpm's non-hoisted structure), but@wordpress/buildonly looks in the rootnode_modules/.Root Cause
In
packages/wp-build/src/build.mjs, the SASS load paths are hardcoded:gutenberg/packages/wp-build/src/build.mjs
Line 119 in 3cd52a4
This assumes:
node_modules(npm behavior)@wordpress/base-styleslives inpackages/base-styles(Gutenberg's internal structure)Neither assumption holds for external consumers using pnpm.
Proposed Solution
Use
@manypkg/get-packagesto dynamically discover all workspace packages and include each package'snode_modulesdirectory in the SASS load paths:This ensures SCSS compilation works with:
Environment info
@wordpress/buildversion: 0.4.0 (trunk)Checklist