Skip to content

Commit 4d24ebd

Browse files
authored
fix: use ["workspaces", "list", "--json"] instead of workspaceRoot for yarn v2+ package manager (#9415)
1 parent ada111e commit 4d24ebd

6 files changed

Lines changed: 53 additions & 24 deletions

File tree

.changeset/tall-needles-join.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": patch
3+
---
4+
5+
fix: use `projectRoot` instead of `workspaceRoot` for yarn_berry package manager

packages/app-builder-lib/src/node-module-collector/index.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { YarnBerryNodeModulesCollector } from "./yarnBerryNodeModulesCollector"
88
import { YarnNodeModulesCollector } from "./yarnNodeModulesCollector"
99
import { BunNodeModulesCollector } from "./bunNodeModulesCollector"
1010
import { Lazy } from "lazy-val"
11-
import { spawn, log } from "builder-util"
11+
import { spawn, log, exists } from "builder-util"
1212
import * as fs from "fs-extra"
1313
import * as path from "path"
1414

@@ -55,6 +55,10 @@ export const determinePackageManagerEnv = ({ projectDir, appDir, workspaceRoot }
5555
if (root != null) {
5656
// re-detect package manager from workspace root, this seems particularly necessary for pnpm workspaces
5757
const actualPm = await detectPackageManager([root])
58+
log.info(
59+
{ pm: actualPm.pm, config: actualPm.corepackConfig, resolved: actualPm.resolvedDirectory, projectDir },
60+
`detected workspace root for project using ${actualPm.detectionMethod}`
61+
)
5862
return {
5963
pm: actualPm.pm,
6064
workspaceRoot: Promise.resolve(actualPm.resolvedDirectory),
@@ -67,55 +71,65 @@ export const determinePackageManagerEnv = ({ projectDir, appDir, workspaceRoot }
6771
})
6872

6973
async function findWorkspaceRoot(pm: PM, cwd: string): Promise<string | undefined> {
70-
let command: { command: string; args: string[] } | undefined
74+
let command: { command: string; args: string[] }
7175

7276
switch (pm) {
7377
case PM.PNPM:
7478
command = { command: "pnpm", args: ["--workspace-root", "exec", "pwd"] }
7579
break
76-
7780
case PM.YARN_BERRY:
78-
command = { command: "yarn", args: ["config", "get", "workspaceRoot"] }
81+
command = { command: "yarn", args: ["workspaces", "list", "--json"] }
7982
break
80-
8183
case PM.YARN: {
8284
command = { command: "yarn", args: ["workspaces", "info", "--silent"] }
8385
break
8486
}
85-
8687
case PM.BUN:
8788
command = { command: "bun", args: ["pm", "ls", "--json"] }
8889
break
89-
9090
case PM.NPM:
9191
default:
9292
command = { command: "npm", args: ["prefix", "-w"] }
9393
break
9494
}
9595

9696
const output = await spawn(command.command, command.args, { cwd, stdio: ["ignore", "pipe", "ignore"] })
97-
.then(it => {
98-
const out = it?.trim()
97+
.then(async it => {
98+
const out: string | undefined = it?.trim()
99+
if (!out) {
100+
return undefined
101+
}
99102
if (pm === PM.YARN) {
100103
JSON.parse(out) // if JSON valid, workspace detected
101-
return findNearestWithWorkspacesField(cwd)
104+
return findNearestPackageJsonWithWorkspacesField(cwd)
102105
} else if (pm === PM.BUN) {
103106
const json = JSON.parse(out)
104107
if (Array.isArray(json) && json.length > 0) {
105-
return findNearestWithWorkspacesField(cwd)
108+
return findNearestPackageJsonWithWorkspacesField(cwd)
109+
}
110+
} else if (pm === PM.YARN_BERRY) {
111+
const lines = out
112+
.split("\n")
113+
.map(l => l.trim())
114+
.filter(Boolean)
115+
for (const line of lines) {
116+
const parsed = JSON.parse(line)
117+
if (parsed.location != null) {
118+
const potential = path.resolve(cwd, parsed.location)
119+
return (await exists(potential)) ? findNearestPackageJsonWithWorkspacesField(potential) : undefined
120+
}
106121
}
107122
}
108-
return !out?.length || out === "undefined" ? undefined : out
123+
return out.length === 0 || out === "undefined" ? undefined : out
109124
})
110-
.catch(() => findNearestWithWorkspacesField(cwd))
111-
112-
log.debug({ root: output || cwd }, output ? "workspace root detected" : "workspace root not detected, using project root")
125+
.catch(() => findNearestPackageJsonWithWorkspacesField(cwd))
113126
return output
114127
}
115128

116-
async function findNearestWithWorkspacesField(dir: string): Promise<string | undefined> {
129+
async function findNearestPackageJsonWithWorkspacesField(dir: string): Promise<string | undefined> {
117130
let current = dir
118131
while (true) {
132+
log.debug({ path: current }, "checking for potential workspace root")
119133
const pkgPath = path.join(current, "package.json")
120134
try {
121135
const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8"))

packages/app-builder-lib/src/node-module-collector/npmNodeModulesCollector.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@ export class NpmNodeModulesCollector extends NodeModulesCollector<NpmDependency,
122122

123123
if (!resolvedPackage) {
124124
log.warn({ package: pkg.name, dependency: depName, version: depVersion }, "dependency not found")
125+
continue
125126
}
126127

127-
const resolvedDepPath = await this.resolvePath(resolvedPackage!.packageDir)
128+
const resolvedDepPath = await this.resolvePath(resolvedPackage.packageDir)
128129
// Skip if this dependency resolves to the base directory or any parent we're already processing
129130
if (resolvedDepPath === resolvedPackageDir || resolvedDepPath === (await this.resolvePath(baseDir))) {
130131
log.debug({ package: pkg.name, dependency: depName, resolvedPath: resolvedDepPath }, "skipping self-referential dependency")

packages/app-builder-lib/src/node-module-collector/packageManager.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ export function getPackageManagerCommand(pm: PM) {
4545
return resolved
4646
}
4747

48-
export async function detectPackageManager(searchPaths: string[]): Promise<{ pm: PM; corepackConfig: string | undefined; resolvedDirectory: string | undefined }> {
48+
type PackageManagerSetup = {
49+
pm: PM
50+
corepackConfig: string | undefined
51+
resolvedDirectory: string | undefined
52+
detectionMethod: string
53+
}
54+
55+
export async function detectPackageManager(searchPaths: string[]): Promise<PackageManagerSetup> {
4956
let pm: PM | null = null
5057
const dedupedPaths = Array.from(new Set(searchPaths)) // reduce file operations, dedupe paths since primary use case has projectDir === appDir
5158

@@ -58,24 +65,22 @@ export async function detectPackageManager(searchPaths: string[]): Promise<{ pm:
5865
const [pm, version] = packageManager.split("@")
5966
if (Object.values(PM).includes(pm as PM)) {
6067
const resolvedPackageManager = await resolveIfYarn(pm as PM, version, dir)
61-
log.info({ resolvedPackageManager, packageManager, cwd: dir }, "packageManager field detected in package.json")
62-
return { pm: resolvedPackageManager, corepackConfig: packageManager, resolvedDirectory: dir }
68+
return { pm: resolvedPackageManager, corepackConfig: packageManager, resolvedDirectory: dir, detectionMethod: "packageManager field" }
6369
}
6470
}
6571

6672
pm = await detectPackageManagerByFile(dir)
6773
if (pm) {
6874
const resolvedPackageManager = await resolveIfYarn(pm, "", dir)
69-
log.info({ resolvedPackageManager, cwd: dir }, "packageManager detected by file")
70-
return { pm: resolvedPackageManager, resolvedDirectory: dir, corepackConfig: undefined }
75+
return { pm: resolvedPackageManager, resolvedDirectory: dir, corepackConfig: undefined, detectionMethod: "lock file" }
7176
}
7277
}
7378

7479
pm = detectPackageManagerByEnv() || PM.NPM
7580
const cwd = process.env.npm_package_json ? path.dirname(process.env.npm_package_json) : (process.env.INIT_CWD ?? process.cwd())
7681
const resolvedPackageManager = await resolveIfYarn(pm, "", cwd)
7782
log.info({ resolvedPackageManager, detected: cwd }, "packageManager not detected by file, falling back to environment detection")
78-
return { pm: resolvedPackageManager, resolvedDirectory: undefined, corepackConfig: undefined }
83+
return { pm: resolvedPackageManager, resolvedDirectory: undefined, corepackConfig: undefined, detectionMethod: "process environment" }
7984
}
8085

8186
function detectPackageManagerByEnv(): PM | null {

packages/app-builder-lib/src/node-module-collector/pnpmNodeModulesCollector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class PnpmNodeModulesCollector extends NodeModulesCollector<PnpmDependenc
3939
const pkgJsonPath = await this.resolvePackage(packageName, depTree.path)
4040

4141
if (pkgJsonPath == null) {
42-
log.warn({ packageName, path: depTree.path, version: depTree.version }, `Cannot find package.json for dependency`)
42+
log.debug({ packageName, path: depTree.path, version: depTree.version }, `Cannot find package.json for dependency`)
4343
return { path: depTree.path, prodDeps: {}, optionalDependencies: {} }
4444
}
4545
let packageJson: PackageJson

packages/app-builder-lib/src/util/appFileCopier.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ export async function computeNodeModuleFileSets(platformPackager: PlatformPackag
194194
deps = dirDeps
195195
break
196196
}
197+
const attempt = searchDirectories.indexOf(dir)
198+
if (attempt < searchDirectories.length - 1) {
199+
log.info({ searchDir: dir, attempt }, "no node modules found in collection, trying next search directory")
200+
}
197201
}
198202
if (deps.length === 0) {
199203
log.warn({ searchDirectories: searchDirectories.map(it => log.filePath(it)) }, "no node modules returned while searching directories")

0 commit comments

Comments
 (0)