Use detect-monorepo for workspace root and rush detection#175
Conversation
Replace the manual is-rush-workspace helper and the hardcoded "../.." workspaceRoot default with detect-monorepo. When workspaceRoot is not set, the monorepo root is now auto-detected by walking upward from the target package directory.
There was a problem hiding this comment.
Pull request overview
This PR replaces the repo’s manual monorepo/Rush detection and the hardcoded workspaceRoot: "../.." default with detect-monorepo, so the workspace root can be auto-detected by walking upward from the target package directory (unless explicitly overridden).
Changes:
- Replace the
is-rush-workspacehelper withdetectMonorepo(dir)?.kind === "rush"at Rush-specific branches. - Update
resolveWorkspacePathsto auto-detectworkspaceRootDirwhenworkspaceRootis omitted andtargetPackagePathisn’t used, throwing a clearer error on failure. - Update CLI/docs to describe auto-detection, and adjust unit tests to mock
detect-monorepo.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/lib/utils/is-rush-workspace.ts | Removes the old Rush detection helper. |
| src/lib/utils/index.ts | Stops exporting the removed helper. |
| src/lib/registry/create-packages-registry.ts | Uses detect-monorepo to branch for Rush workspaces when enumerating packages. |
| src/lib/patches/copy-patches.ts | Uses detect-monorepo to select Rush vs non-Rush lockfile location for patch hash extraction. |
| src/lib/patches/copy-patches.test.ts | Mocks detect-monorepo for patch-copy tests. |
| src/lib/package-manager/index.ts | Uses detect-monorepo for Rush-aware package manager detection. |
| src/lib/manifest/helpers/adopt-pnpm-fields-from-root.ts | Skips pnpm-field adoption for Rush via detect-monorepo. |
| src/lib/manifest/helpers/adopt-pnpm-fields-from-root.test.ts | Updates mocks/assertions to use detect-monorepo. |
| src/lib/lockfile/helpers/generate-yarn-lockfile.ts | Uses detect-monorepo to locate Rush Yarn lockfile. |
| src/lib/lockfile/helpers/generate-pnpm-lockfile.ts | Uses detect-monorepo to locate Rush PNPM lockfile directory. |
| src/lib/lockfile/helpers/generate-pnpm-lockfile.test.ts | Mocks detect-monorepo for PNPM lockfile generation tests. |
| src/lib/config.ts | Makes workspaceRoot optional; adds auto-detection + error when missing. |
| src/isolate.ts | Uses detect-monorepo to decide Rush-specific pnpm workspace generation behavior. |
| src/isolate-bin.ts | Updates --workspace-root help text and examples for auto-detection. |
| pnpm-lock.yaml | Locks the new detect-monorepo dependency. |
| package.json | Adds detect-monorepo to dependencies. |
| docs/configuration.md | Updates workspaceRoot docs to reflect auto-detection. |
| docs/api.md | Updates config examples to no longer rely on workspaceRoot defaults. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (4)
src/lib/registry/create-packages-registry.ts:80
detectMonorepo(workspaceRootDir)can return a Rush monorepo whoserootDiris above the provided directory. In that case this branch will still run, butrush.jsonis read fromworkspaceRootDirinstead of the detected root, which can cause a runtime read error or incorrect project listing. Consider using the returnedrootDirfor the Rush-specific paths (or requiredetected.rootDir === workspaceRootDirbefore treating it as Rush).
if (detectMonorepo(workspaceRootDir)?.kind === "rush") {
const rushConfig = readTypedJsonSync<RushConfig>(
path.join(workspaceRootDir, "rush.json"),
);
src/lib/patches/copy-patches.ts:157
detectMonorepo(workspaceRootDir)may detect a Rush monorepo withrootDirdifferent from the providedworkspaceRootDir(since it walks upward). If that happens,lockfileDirwill be computed relative to the wrong base directory. Consider basing Rush-specific paths ondetectMonorepo(...).rootDir(or only treating it as Rush when the detected root matchesworkspaceRootDir).
try {
const { majorVersion } = usePackageManager();
const useVersion9 = majorVersion >= 9;
const isRush = detectMonorepo(workspaceRootDir)?.kind === "rush";
const lockfileDir = isRush
? path.join(workspaceRootDir, "common/config/rush")
: workspaceRootDir;
src/lib/package-manager/index.ts:30
detectMonorepo(workspaceRootDir)can return a Rush monorepo whoserootDiris aboveworkspaceRootDir. In that scenario,common/config/rushwill be looked up under the wrong directory. Consider usingconst detected = detectMonorepo(workspaceRootDir)and basing the Rush lockfile/metadata directory ondetected.rootDir(or requiringdetected.rootDir === workspaceRootDir).
export function detectPackageManager(workspaceRootDir: string): PackageManager {
if (detectMonorepo(workspaceRootDir)?.kind === "rush") {
packageManager = inferFromFiles(
path.join(workspaceRootDir, "common/config/rush"),
);
} else {
src/lib/lockfile/helpers/generate-pnpm-lockfile.ts:72
isRushis derived fromdetectMonorepo(workspaceRootDir)?.kind, but the Rush lockfile directory is still constructed fromworkspaceRootDir. BecausedetectMonorepowalks upward, it can report Rush even whenworkspaceRootDiris a subdirectory; in that case thecommon/config/rushpath will be wrong. Consider basing Rush paths on the detectedrootDir(or requiringdetected.rootDir === workspaceRootDir).
try {
const isRush = detectMonorepo(workspaceRootDir)?.kind === "rush";
const lockfile = useVersion9
? await readWantedLockfile_v9(
isRush
? path.join(workspaceRootDir, "common/config/rush")
: workspaceRootDir,
{
ignoreIncompatible: false,
},
)
: await readWantedLockfile_v8(
isRush
? path.join(workspaceRootDir, "common/config/rush")
: workspaceRootDir,
{
ignoreIncompatible: false,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
detectMonorepo walks upward, so its result may describe a monorepo whose rootDir sits above the passed-in workspaceRootDir. Build all Rush-specific paths from detected.rootDir instead of the input so the paths resolve correctly even when called with a subdirectory.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8381fa0. Configure here.
The previous commit switched Rush-specific paths to use detected.rootDir from detectMonorepo, but detectMonorepo walks upward, so its rootDir can diverge from the workspaceRootDir that downstream code (notably lockfile importer id computation) is keyed to. Restoring the strict rush.json check at the passed-in directory keeps all Rush paths consistent with the rest of the pipeline. detect-monorepo remains used in resolveWorkspacePaths for workspace root auto-detection.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Check for undefined instead of truthiness so an empty string passed as workspaceRoot is treated as an explicit "same directory" override rather than falling through to auto-detection.

Use
detect-monorepoto auto-detect the workspace root, replacing the hardcodedworkspaceRoot: "../.."default.When
workspaceRootis not set explicitly (andtargetPackagePathis not used),resolveWorkspacePathsnow walks upward from the target package directory looking forpnpm-workspace.yaml, apackage.jsonwith aworkspacesfield, orrush.json. If detection fails and noworkspaceRootis configured, a clear error asks the user to set it explicitly.The
workspaceRootconfig option remains supported as an override for cases where auto-detection fails (e.g. unusually deep nesting).The existing
isRushWorkspacehelper is kept as a strictrush.jsoncheck at the passed-in directory. It is intentionally not replaced withdetectMonorepo, becausedetectMonorepowalks upward and its rootDir can diverge fromworkspaceRootDir, which would break callers that compute lockfile importer ids or other paths relative to the same directory.Docs and CLI help text updated to reflect the new auto-detect behavior.
Scope: packages (isolate-package)
Visibility: user-facing