Repository navigation
Idea: Clean up root package.json and convert tools and tests to workspace packages #75041
Description
Benefits
Converting tools and test directories to workspace packages provides several key advantages:
-
Separation of Concerns: Each tool/test suite declares its own dependencies, making it clear what each component needs to function.
-
Cleaner Root
package.json: Reduces the rootpackage.jsonfrom 81 devDependencies to only essential shared tools (ESLint, Stylelint, Husky, TypeScript, etc.). -
Improved Maintainability: Changes to one tool's dependencies don't require updating the root configuration, reducing merge conflicts and simplifying updates.
-
Better Dependency Visibility: Makes it easier to audit and understand what dependencies are used by each tool, improving security review processes.
-
Prepares for Stricter Dependency Isolation: With npm, dependencies get hoisted to the root
node_modules, so workspaces can accidentally access dependencies from anywhere in the monorepo ("phantom dependencies"). By moving dependencies to workspaces that need them and cleaning up root, we make dependency relationships explicit. This is good practice now and becomes even more impactful when we later migrate to pnpm, where workspaces can only access their own declared dependencies plus the monorepo root - making a clean root the only way to fully prevent phantom dependencies.
Already Converted (Reference)
Merged:
-
storybook→@wordpress/storybook(PR Infrastructure: Convert storybook to a workspace package #74640) -
test/e2e→@wordpress/e2e-tests-playwright -
test/performance→@wordpress/performance-tests -
test/storybook-playwright→@wordpress/storybook-playwright
Conversion Pattern
Each workspace conversion follows this pattern (established in PR #74640):
-
Create
package.jsonin the target directory with:- Name:
@wordpress/<workspace-name> "private": truefor non-published packages- Only required
devDependencies - Workspace-specific scripts
- Name:
-
Create/update
tsconfig.jsonextending../../tsconfig.base.json -
Update root
package.json:- Add to
"workspaces"array - Update scripts to use
npm run --workspace @wordpress/<name>syntax - Remove migrated dependencies (once all consumers are converted)
- Add to
-
Update root
tsconfig.jsonwith project reference -
Update CI workflows to use workspace-scoped commands
I asked Claude Code to come up with a phased plan for the conversion. Here is what it came up with:
Phase 1: Test Infrastructure
1.1 Unit Tests Workspace
- Create
test/unitworkspace →@wordpress/unit-tests- refactor: migrate tests/unit to npm workspace @wordpress/unit-tests #77063
1.2 Native Tests Workspace
- Create
test/nativeworkspace →@wordpress/native-tests- Migrate native tests to workspace #77425
1.3 Integration Tests Workspace
- Create
test/integrationworkspace →@wordpress/integration-tests- Test(Integration): migratetest/integrationinto@wordpress/integration-testsworkspace #77556
Phase 2: Documentation Tools
2.1 API Documentation Generator
- Create
tools/api-docsworkspace →@wordpress/api-docs-generator- refactor: migrate bin/api-docs to tools/api-docs as workspace@wordpress/api-docs-generator#77019
Phase 3: Release & Plugin Tools
3.1 Plugin CLI Tools
- Create
tools/releaseworkspace →@wordpress/release-tools- Tools(Release): migrate bin/plugin into @wordpress/release-tools workspace #77695
3.2 Cherry-Pick Tool
- Include in
tools/releaseor create separate workspace - Add cherry-pick script and update release tools in package.json #78560
3.3 PHP Sync Issue Generator
- Include in
tools/releaseworkspace - Move PHP Sync Issue Generator file to @wordpress/release-tools #78456
Phase 4: Build & Validation Tools
4.1 Build Tools
-
Create
tools/buildworkspace →@wordpress/build-tools- Build Tools: Move build scripts to@wordpress/build-scriptsworkspace package #78509Consideration: These scripts are core to the build process and have deep integration with lerna and the package build system. May need careful evaluation of whether separation is beneficial.
4.2 Validation Tools
- Create
tools/validationworkspace →@wordpress/validation-tools- Refactor validation tools and update related scripts #77522
Phase 5: ESLint Configuration
5.1 ESLint Tools
- Create
tools/eslintworkspace →@wordpress/eslint-tools- ESLint: Consolidate config into tools/eslint/ workspace package #77215
Phase 6: Miscellaneous Tools
6.1 Create Block Test
- Move to
tools/validation- Migratecreate-test-block.shto@wordpress/validation-toolsworkspace #78665
Current State:
bin/test-create-block.sh- Shell script for testing create-block- CI:
create-block.ymlworkflow
Tasks:
- Move
bin/test-create-block.sh→tools/validation/test-create-block.mjs. Usechild_processto invoke npm/bins vianpm exec --no -- <bin>(no hardcoded paths). Wire it up: tools/validation/package.json: add@wordpress/create-block(file:../../packages/create-block) devDep and"test-create-block": "node ./test-create-block.mjs"script.- Root
package.json:"test:create-block": "npm run --workspace @wordpress/validation-tools test-create-block --". .github/workflows/create-block.yml:run: npm run test:create-block.
6.2 Performance Results Logger
- Include in
tools/releaseworkspace - feat: Migrate performance results to tools release #78761
Phase 7: Cleanup & Finalization
7.1 Root Package.json Cleanup
Tasks
-
Remove
.browserslistrcfrom root - feat: Migrate the browserlintrc file topackages/postcss-plugins-preset#78764- Delete
.browserslistrc - Add
@wordpress/browserslist-configtopackages/postcss-plugins-preset/package.json - Add
overrideBrowserslist: require( '@wordpress/browserslist-config' )torequire( 'autoprefixer' )()call aftergrid: true
- Delete
-
Move babel deps out of root Refactor workspace configuration for Babel dependencies #78974
- Verify that there are no babel related deps in root
-
Move test/jest deps out of root - Refactor: Remove jest/test deps from root package.json #78801
- Remove test/jest related deps from root
- Add the required deps to all the workspaces that need them
-
Move
@types/prettierout of root - Remove migrated dependencies from root package.json #78813- Remove
@types/prettierfrom root package.json - Add
@types/prettiertodevDependenciesinpackages/prettier-config/package.json
- Remove
-
Move
@types/nodeout of root - Remove migrated dependencies from root package.json #78813- Remove
@types/nodefrom root package.json - Remove
typeRootsentry fromtest/performance/tsconfig.json, because that was the last package to use@types/nodefrom the root. Removing that entry will make it rely on its own declared dependency in isolated mode.
- Remove
-
Move
reactrelated types out of root. - refactor: move React dependencies to workspace configuration and upda… #78981- Remove
reactandreact-domfrom root package.json - Move
react-docgen-typescriptfrom root tostorybook/package.json - Add
reactorreact-domas workspace dependencies/devDependencies/peerDependencies as needed - Update build-scripts build vendor script to resolve dependencies from the workspace directory, instead of root.
- Update the consumer routes, if needed.
- Remove
-
Move
globout of root - Refactor to move 'glob' dependency to appropriate workspaces #79145- Remove
globfrom root package.json - Add
"glob": "^13.0.6"topackages/dependency-extraction-webpack-plugin/andpackages/readable-js-assets-webpack-plugin/package.json
- Remove
-
Convert
routes/lock-unlock.tsto a workspace - Add lock-unlock route as a workspace and update dependencies #79138- Create a workspace for the file -
routes/lock-unlock/route.tsjust like other routes - Add an appropriate
package.jsonfile like other routes. - Declare
@wordpress/private-apisas the only dependency - Update the consumer routes, if needed.
- Create a workspace for the file -
-
Move eslint and relevant deps out of root - Eslint: move deps from root into tools/eslint and packages/eslint-plugin #79110
- Remove
eslint,eslint-formatter-compact, and@eslint/eslintrcfrom root package.json - Add
eslintandeslint-formatter-compacttotools/eslint/package.json - Add
eslintto dev deps inpackages/eslint-plugin/package.json - Update
tools/eslint/lint-js.cjsto resolve formatters from the workspace directory
- Remove
-
Move
prettierout of root - Refactor Prettier configuration and update dependencies #79219- Remove
prettierfrom root package.json - Change
require( '@wordpress/prettier-config' )torequire( './packages/prettier-config' )in.prettierrc.js - Add
"prettier": "npm:wp-prettier@^3.0.3"topackages/theme/package.json
- Remove
-
Add missing
devDependenciesto root, as they are used there. - fix: Add missing root devDependencies for WordPress packages #79221"@playwright/test": "^1.58.2","@wordpress/env": "file:./packages/env","@wordpress/scripts": "file:./packages/scripts",
-
Decouple
.npmpackagejsonlintrcfrom root. - Refactor npm-package-json-lint configuration and update dependencies #79223- Remove
.npmpackagejsonlintrc.json - Add
@wordpress/npm-package-json-lint-configtopackages/stylelint-config/package.json - Create
.npmpackagejsonlintrc.js
- Remove
-
Move stylelint config and deps out of root - Refactor: move stylelint config and deps to tools/stylelint workspace #79226
- Remove
stylelint-plugin-logical-cssfrom root package.json - Create
tools/stylelintworkspace and move.stylelingrc.jstotools/stylelint/config.js - Expose the moved config via package.json exports
- Create
.stylelintrc.mjsin root which extends'@wordpress/stylelint-tools/config'
- Remove
-
Remove
@wordpress/vipsfrom root
7.2 Dependencies to Keep in Root
Let us aim to remove as many dependencies from the root as possible.
husky- Git hooks require root installationlint-staged- Pre-commit linting runs from rootlerna- Monorepo managementpatch-package- Post-install patches run from rooteslint- Core linting tool used repo-widestylelint- Core style linting tool used repo-widetypescript- Type checking used across all packages
Move to Workspaces:
@types/*- Move to specific workspaces that need themprettier- Move to ESLint/formatting workspace- Build tools (
sass,babel-*, loaders) - Move to build workspace concurrently,wait-on,npm-run-all- Move to workspaces that use them
Verification
After all workspaces are created and verified:
- Remove migrated dependencies from root
package.json - Verify all scripts work correctly
- Update documentation
- Add a CI check for root deps which fails when new dep is added to root. - CI: Disallow new dependencies in the root package.json #78616
7.3 CI Workflow Updates Summary
Workflows that need updates:
-
unit-test.yml- Use@wordpress/unit-testsworkspace -
static-checks.yml- Use validation tools workspace -
performance.yml- Use release tools workspace -
publish-npm-packages.yml- Use release tools workspace -
create-block.yml- Potentially update paths
7.4 Documentation
Done via #78615
- Update contributing documentation, especially highlighting to create workspaces in tools if something needs for the repo, instead of adding dependencies to the root.
- Add workspace development guide
- Update CI documentation
Implementation Order
Follow the phases sequentially:
- Phase 1: Test Infrastructure (Unit, Native, Integration tests)
- Phase 2: Documentation Tools (API docs generator)
- Phase 3: Release & Plugin Tools (CLI, cherry-pick, PHP sync)
- Phase 4: Build & Validation Tools
- Phase 5: ESLint Configuration
- Phase 6: Miscellaneous Tools
- Phase 7: Cleanup & Finalization
Notes
- Each phase can be implemented as a separate PR for easier review
- Test thoroughly in CI before merging
- Keep backward compatibility during transition (old script paths can delegate to new workspaces)
- Monitor CI performance after each change
Related PRs
- Infrastructure: Convert storybook to a workspace package #74640 - Storybook workspace conversion (reference implementation)
- Convert directories in test/ to workspaces #74684 - [In Progress] - Additional workspace conversions
- Framework: Update Node.js version to v24 LTS #72973 - Many of these tools can later be converted to TypeScript when we use Node v24 with native type stripping
Activity
manzoorwanijk commentedon Jan 29, 2026
CC: @jsnajdr @aduth @youknowriad
USERSATOSHI commentedon Apr 2, 2026
Hi, would it be okay if I work on @wordpress/unit-tests?
youknowriad commentedon Apr 2, 2026
Just noting that there's already an e2e-tests package, it used to contain the puppeteer tests before they get removed and replaced by the top level playwright tests. So this reverts the direction here, we can just reuse that package again.
Overall, I think this is a good direction but I'd start by focusing on the things that are really tools (like docs generation tool...). It's a bit more unclear to me when it comes to the actual tests.
USERSATOSHI commentedon Apr 2, 2026
I see, in that case, starting with
tools/api-docssounds like a good approach.USERSATOSHI commentedon Apr 2, 2026
Hi, I started converting bin/api-docs into a workspace package.
It looks like we need to explicitly add all the dependencies used there into its package.json—even ones like chalk, execa, etc., which are already present at the root.
Should I go ahead and add them to make the checks pass, or is there a preferred way to handle shared dependencies in this setup?
manzoorwanijk commentedon Apr 2, 2026
If a workspace uses any dependency, it must be declared in its package.json
manzoorwanijk commentedon Apr 2, 2026
The main goal here is to avoid workspace package having access to root deps when we implement the isolated dependencies via #75814 or via pnpm adoption.
kushagra-goyal-14 commentedon Jun 5, 2026
Hi @manzoorwanijk , I was checking -
Move react related types out of rootand had some questions. The diff in the issue description targets React 19 (^19.2.4), but since #78940 reverted the React 19 upgrade, trunk is back at 18. Should we adapt to 18 version?manzoorwanijk commentedon Jun 5, 2026
Yes, we need to adapt according to the current React version used by the repo.
There is also that "Move babel deps out of root" task, which is independent of that React version.
kushagra-goyal-14 commentedon Jun 5, 2026
@manzoorwanijk While adapting the React diff to v18, I found two entries from the issue's diff that don't seem to apply to current trunk:
packages/private-apis— the diff addsreactas a devDependency, but I couldn't find any react usage in the package (src, tests, or types).tools/build-scripts— the diff adds@wordpress/elementas a dependency, but the only reference inbuild-vendors.mjsis a commented-out line (// ' ...require("@wordpress/element/react-polyfill"),').Should I leave both of these out, or is there a reason to include them?
manzoorwanijk commentedon Jun 5, 2026
Yes, we can leave those out.
kushagra-goyal-14 commentedon Jun 7, 2026
Hi @manzoorwanijk, I was looking into the
routes/lock-unlock/route.tstask and had a small question about the file name. The task mentionsroutes/lock-unlock/route.ts, but sincelock-unlockisn't actually a route (it just re-exportslock/unlockfor the routes group), I thinkindex.tsmight be a better fit thanroute.ts.From what I can see in the build,
getRouteFiles()treats any file namedroute.{ext}as a route entry and esbuild-bundles it into aroute.min.js(+ asset file), even though the router's manifest step skips the folder since itspackage.jsonhas noroutefield. So it will create a build that won't get used.Using
index.tsshould avoid that.Does that sound reasonable, or am I missing something?
Also noticed react-scanner (the component-usage-stats script + react-scanner.config.js) still lives in root and isn't in the task list. Could it be folded into tools/validation? I also noticed: the config's crawlFrom: './', the exclude list, and outputTo: './results/gutenberg.json' are all relative to repo root, so they'd need adjusting (or running with the repo root as cwd) after the move. Happy to take it on if it makes sense.
manzoorwanijk commentedon Jun 8, 2026
@kushagra-goyal-14 that makes sense. Let us do that.
USERSATOSHI commentedon Jun 11, 2026
@manzoorwanijk, I accidentally got assigned since I forgot to change
closestorelatedin PR desc. Can you unassign me?kushagra-goyal-14 commentedon Jun 12, 2026
Hi @manzoorwanijk , I checked the "Move glob out of root" task and have a question.
The task says to add "glob": "^13.0.6" to dependency-extraction-webpack-plugin and readable-js-assets-webpack-plugin. That part works fine as both only use require( 'glob' ).sync, which v13 still supports, and their tests pass.
However, four other workspaces also declare glob at ^7.1.2 (tools/docs, tools/eslint, tools/validation, test/unit), so npm run lint:deps (syncpack) fails with a version mismatch once the two packages move to v13. I checked more and it seems those are not just version changes-
Should I bump all to V13? Any suggestions?
manzoorwanijk commentedon Jun 12, 2026
Let us stick to the version in root and do the version upgrade later.
Notes: Move emojibase copy script to tools/build-scripts workspace
manzoorwanijk commentedon Jun 16, 2026
We are pretty close to wrapping this up. Only three remaining items left. Thank you, everyone, for your contribution.
manzoorwanijk commentedon Jun 16, 2026
Once we complete those items, we can then enable ship #78616 as a safety guard.
manzoorwanijk commentedon Jun 16, 2026
I had another look at the root deps, I think we can get rid of
@wordpress/vipsfrom the root package.json. It doens't need to be moved anywhere. It's already where it needs to be.Move typescript and rimraf out of the root package.json
Move typescript and rimraf out of the root package.json