diff --git a/AGENTS.md b/AGENTS.md index 076940d3590d84..1515e1290293e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -69,6 +69,7 @@ For full architecture details, see `docs/explanations/architecture/`. ## Common pitfalls +- Do not add dependencies to the root `package.json`. Add them to the workspace that uses them, or create a new workspace under `tools/` (or `test/` for test infrastructure). See [Workspace Development](docs/contributors/code/workspace-development.md). - PHP features in `lib/compat/` MUST target a specific `wordpress-X.Y/` subdirectory. - Avoid using private APIs in bundled packages (packages without `wpScript` or `wpModuleExports`). Private APIs are intended for Core usage; bundled packages may also be imported via npm into plugin scripts, causing incompatibilities. - `block-editor` is a WordPress-agnostic package. NEVER add `core-data` dependencies or direct REST API calls to it. diff --git a/docs/contributors/code/README.md b/docs/contributors/code/README.md index 7a251e2530c585..c44c21b87fa716 100644 --- a/docs/contributors/code/README.md +++ b/docs/contributors/code/README.md @@ -22,6 +22,7 @@ Browse [the issues list](https://github.com/wordpress/gutenberg/issues) to find - [Testing Overview](/docs/contributors/code/testing-overview.md) for PHP and JavaScript development in Gutenberg. - [Accessibility Testing](/docs/contributors/accessibility-testing.md) documents the process of testing accessibility in Gutenberg. - [Managing Packages](/docs/contributors/code/managing-packages.md) documents the process for managing the npm packages. +- [Workspace Development](/docs/contributors/code/workspace-development.md) explains how to create and work with internal workspaces under `tools/` and `test/`, and why dependencies belong in a workspace rather than at the repo root. - [Gutenberg Release Process](/docs/contributors/code/release/README.md) - a checklist for the different types of releases for the Gutenberg project. - [React Native mobile editor](/docs/contributors/code/react-native/README.md) - a guide on contributing to the React Native mobile editor. - [React Native Integration Test Guide](/docs/contributors/code/react-native/integration-test-guide.md) - a guide on creating integration tests for the mobile editor. diff --git a/docs/contributors/code/managing-packages.md b/docs/contributors/code/managing-packages.md index 25f1e109fa9932..6e135eea857d04 100644 --- a/docs/contributors/code/managing-packages.md +++ b/docs/contributors/code/managing-packages.md @@ -5,3 +5,7 @@ This repository uses [npm workspaces](https://docs.npmjs.com/cli/v10/using-npm/w Maintaining dozens of npm packages is difficult—it can be tough to keep track of changes. That's why we use `CHANGELOG.md` files for each package to simplify the release process. As a contributor, you should add an entry to the aforementioned file each time you contribute adding production code as described in [Maintaining Changelogs](https://github.com/WordPress/gutenberg/blob/HEAD/packages/README.md#maintaining-changelogs) section. Publishing WordPress packages to npm is automated by synchronizing it with the bi-weekly Gutenberg plugin RC1 release. You can learn more about this process and other ways to publish new versions of npm packages in the [Gutenberg Release Process document](/docs/contributors/code/release/README.md#packages-releases-to-npm-and-wordpress-core-updates). + +## Internal workspaces (tools and tests) + +The repository also contains internal workspaces under `tools/` and `test/` for development tooling and test infrastructure. When you need to add a new tool, script, or dependency for repo-level work, create a workspace under `tools/` (or add to an existing one) instead of adding dependencies to the root `package.json`. See the [Workspace Development guide](/docs/contributors/code/workspace-development.md) for the conversion pattern, CI conventions, and reference examples. diff --git a/docs/contributors/code/workspace-development.md b/docs/contributors/code/workspace-development.md new file mode 100644 index 00000000000000..ef887303681fd8 --- /dev/null +++ b/docs/contributors/code/workspace-development.md @@ -0,0 +1,152 @@ +# Workspace Development + +The Gutenberg repository is an [npm workspaces](https://docs.npmjs.com/cli/v10/using-npm/workspaces) monorepo. Beyond the published `@wordpress/*` packages under `packages/`, the repo also contains internal workspaces for development tools (`tools/*`), test infrastructure (`test/*`), and related projects (`storybook`, `widgets/*`, `routes/*`). + +This guide explains how to add or modify those internal workspaces, and why dependencies should land in a workspace rather than in the root `package.json`. + +## Why workspaces, not root dependencies + +Anything added to the root `package.json` becomes implicitly available to every workspace in the repo, which makes it hard to tell which workspace actually needs a given dependency. Keeping root lean has several benefits: + +- **Separation of concerns.** Each workspace declares the dependencies it needs to function, making the relationship between code and dependencies explicit. +- **Cleaner root.** The root `package.json` only contains repo-wide tooling (lint, format, type-check, git hooks, monorepo orchestration). Reviewing dependency changes is easier when the root is small. +- **Fewer merge conflicts.** Contributors can update a workspace's dependencies without touching the root. +- **Phantom dependency prevention.** With npm's hoisted install strategy, dependencies are hoisted to the root `node_modules`, so a workspace can accidentally import a dependency it never declared. A clean root keeps these relationships honest now, and is a prerequisite for moving to an isolated dependency approach in the future (one proposal under discussion is a [migration to pnpm](https://github.com/WordPress/gutenberg/pull/74689)), where workspaces can only see their own declared dependencies. + +The default answer for "where does this dependency go?" is **a workspace**, not the root. If you find yourself reaching for the root `package.json`, consider whether the dependency can live in: + +- An **existing workspace** under `tools/` or `test/` that already covers the use case (for example, `@wordpress/eslint-tools`, `@wordpress/release-tools`, `@wordpress/validation-tools`, `@wordpress/unit-tests`). +- A **new workspace** under `tools/` (or `test/` for test infrastructure) if no existing one fits. + +## Where workspaces live + +| Location | Purpose | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| `packages/*` | Published `@wordpress/*` npm packages. See [Managing Packages](/docs/contributors/code/managing-packages.md). | +| `tools/*` | Internal development tools (ESLint config, release CLI, API doc generator, validation, etc.). Not published to npm. | +| `test/*` | Test infrastructure (unit, integration, e2e, performance, native, storybook-playwright). | +| `storybook` | The Gutenberg Storybook host. | +| `routes/*` | Editor route entry points. | +| `widgets/*` | Widget bundles. | + +The set of registered workspace globs lives in the `"workspaces"` array of the root `package.json`. Any directory matched by an existing glob (for example, `tools/*`) is picked up automatically once it contains a `package.json`. + +## Creating a new workspace + +This pattern was established in [#74640](https://github.com/WordPress/gutenberg/pull/74640) (Storybook conversion) and is the model for subsequent conversions. + +### 1. Add a `package.json` in the workspace directory + +For an internal tool that is not published to npm: + +```json +{ + "name": "@wordpress/", + "version": "0.0.0", + "description": "", + "private": true, + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/tools/", + "repository": { + "type": "git", + "url": "git+https://github.com/WordPress/gutenberg.git", + "directory": "tools/" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "devDependencies": {}, + "scripts": {} +} +``` + +Notes: + +- Use `"private": true` for workspaces that should never be published. +- List only the dependencies this workspace actually imports or executes. +- To depend on another workspace in the monorepo, use a `file:` reference (for example, `"@wordpress/scripts": "file:../../packages/scripts"`). + +### 2. (Optional) Add a `tsconfig.json` + +If the workspace contains TypeScript, extend the shared base config: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "./", + "outDir": "./build" + }, + "include": [ "./**/*.ts" ] +} +``` + +Then add a project reference for the new workspace in the root `tsconfig.json`. + +### 3. Register the workspace (if needed) + +If the new workspace lives under a path already covered by a glob in the root `package.json` (for example, `tools/*`), it is registered automatically. Otherwise, add an entry in `workspaces` in the root `package.json`. + +### 4. Expose scripts from the repo root + +Forward any scripts contributors should run from the repo root using `npm run --workspace`: + +```json +"scripts": { + "my-task": "npm run --workspace @wordpress/ my-task --" +} +``` + +The trailing `--` forwards extra CLI arguments through to the workspace script (for example, `npm run my-task -- --watch`). + +### 5. Add a README + +Include a `README.md` in the workspace directory describing what the workspace does, the scripts it exposes, and any non-obvious setup. + +### 6. Update CI workflows + +CI workflows under `.github/workflows/` should invoke the workspace through the root `npm run` wrappers added in step 4, rather than `cd`-ing into the workspace directory. Keeping CI aligned with the root scripts means contributors run the same commands locally that CI runs. + +```yaml +- run: npm ci +- run: npm run my-task +``` + +## Working with workspaces day-to-day + +### Adding or removing a dependency in a workspace + +Always scope dependency changes to the workspace that uses them: + +```bash +npm install --workspace @wordpress/ +npm uninstall --workspace @wordpress/ +``` + +These commands update the workspace's `package.json` and the root `package-lock.json`, without touching the root `package.json`. + +### Running a workspace script + +From the repo root: + +```bash +npm run