|
| 1 | +# Drop root-matching workspace (#699) |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +When the workspace glob in `pnpm-workspace.yaml` (or the `workspaces` field) matches the |
| 6 | +**project root directory itself** — e.g. `packages: ["."]`, or a broad `./**` that |
| 7 | +re-includes the root — `@manypkg` returns the root as one of `packages.packages`. |
| 8 | +`createProject` (project-discovery.ts) then maps it to a sub-workspace with an empty |
| 9 | +`relativeDir`, producing a workspace with an empty id and empty relativePath that duplicates |
| 10 | +the root workspace. |
| 11 | + |
| 12 | +That degenerate workspace later fails label validation with a confusing message: |
| 13 | + |
| 14 | +``` |
| 15 | +Workspace "root" has label "" which conflicts with the ID of workspace "". |
| 16 | +``` |
| 17 | + |
| 18 | +This is the "single workspace in monorepo currently errors during detection" case behind the |
| 19 | +`it.todo` in `workspaces-detection.test.ts`. (A genuine single *sub-package* monorepo — root |
| 20 | +plus one `packages/foo` — already works.) |
| 21 | + |
| 22 | +## Decision |
| 23 | + |
| 24 | +Treat a root-matching workspace pattern as **valid**: drop the root match, since the root is |
| 25 | +already its own workspace. The user's config "just works" instead of erroring. |
| 26 | + |
| 27 | +## Design |
| 28 | + |
| 29 | +### Change |
| 30 | + |
| 31 | +In `createProject` (`packages/project-resolver/src/project-discovery.ts`), filter |
| 32 | +`packages.packages` to exclude any package whose absolute `dir` equals `packages.rootDir` |
| 33 | +before mapping to workspaces: |
| 34 | + |
| 35 | +```ts |
| 36 | +workspaces: packages.packages |
| 37 | + .filter((pkg) => pkg.dir !== packages.rootDir) |
| 38 | + .map((pkg) => createWorkspace(pkg)) |
| 39 | + .sort((a, b) => a.relativePath.localeCompare(b.relativePath)) |
| 40 | +``` |
| 41 | + |
| 42 | +`pkg.dir` and `packages.rootDir` are both absolute paths from `@manypkg`; equality is the |
| 43 | +precise "this discovered package is the root" condition, robust regardless of how the |
| 44 | +relative path stringifies (`""` vs `"."`). |
| 45 | + |
| 46 | +### Scope |
| 47 | + |
| 48 | +One `.filter` in one function. No new files, no public-API change. `createWorkspace`, |
| 49 | +`createRootWorkspace`, and the alias/label validation are untouched — the degenerate |
| 50 | +workspace simply never gets created, so the downstream label-collision error no longer |
| 51 | +fires. |
| 52 | + |
| 53 | +### Behavior after |
| 54 | + |
| 55 | +- `packages: ["."]` plus real sub-packages → project resolves to root + the real |
| 56 | + sub-packages, no empty workspace, exit 0. |
| 57 | +- A pattern matching only the root → project resolves to root with zero sub-workspaces. |
| 58 | +- Genuine sub-package monorepos are unaffected (their package dirs never equal rootDir). |
| 59 | + |
| 60 | +### Error handling |
| 61 | + |
| 62 | +None added — the case is now valid rather than an error. |
| 63 | + |
| 64 | +## Testing |
| 65 | + |
| 66 | +Implement the deferred `it.todo("single workspace in monorepo")` in |
| 67 | +`workspaces-detection.test.ts`: a fixture with `pnpm-workspace.yaml` matching `.` plus a |
| 68 | +real sub-package, asserting the project resolves cleanly (root + the real sub, no empty |
| 69 | +workspace) via `--show-config --config-key project` / `--list-workspaces`. Optionally a |
| 70 | +project-resolver-level assertion that no workspace has an empty relativePath. |
| 71 | + |
| 72 | +## Spec |
| 73 | + |
| 74 | +`spec/06-project.md` — Workspace Discovery gains the root-exclusion rule. CHANGELOG entry + |
| 75 | +version bump to 4.1.1 (PATCH: corrects detection behavior; #698's 4.1.0 already merged). |
0 commit comments