1import * as _ from 'lodash-es' 3import yaml from 'js-yaml' 14let cachedWorkspaceH: any = null 15let cachedWorkspaceKey = '' 16let inFlightWorkspaceP: Promise<any> | null = null 18const getPnpmWorkspaceKey = () => { 20 const wsStat = fs.statSync(`${ptDir}/pnpm-workspace.yaml`) 21 const lockStat = fs.statSync(`${ptDir}/pnpm-lock.yaml`) 22 return `${wsStat.mtimeMs}:${lockStat.mtimeMs}:${lockStat.size}` 26// Memoized + in-flight-deduped. Workspace list is process-stable; invalidate on pnpm-workspace.yaml/pnpm-lock.yaml mtime. 27// Without this, recPkgJsonsPathA's parallel recursion spawned 60+ concurrent `pnpm m ls` processes per call. 28export const getPnpmWorkspaceH = async () => { 29 const key = getPnpmWorkspaceKey() 30 if (cachedWorkspaceH && key === cachedWorkspaceKey) return cachedWorkspaceH 31 if (inFlightWorkspaceP) return inFlightWorkspaceP 32 inFlightWorkspaceP = (async () => { 33 const {stdout} = await execFileThrow({cmdA: 'corepack pnpm m ls --depth -1 --json'.split(' ')}) 34 const h = _.chain(json1Parse(stdout as string) as any[]).map(({name, ...row}: any) => [name, row]).fromPairs().value() 36 cachedWorkspaceKey = key 37 inFlightWorkspaceP = null 40 return inFlightWorkspaceP 43// seenSet is shared across the whole traversal: workspace dep graphs may contain cycles 44// (mutual workspace: deps), and per-path ancestor guards explode combinatorially (EMFILE/multi-min hangs). 45export const recPkgJsonsPathA = async ({packageJsonPath, seenSet = new Set<string>()}: {packageJsonPath: string, seenSet?: Set<string>}): Promise<{packageJsonPath: string, pkgJson: any}[]> => { 46 if (seenSet.has(packageJsonPath)) return [] 47 seenSet.add(packageJsonPath) 51 const pkgName = pkgJson.name 53 const pnpmWorkspaceH = await getPnpmWorkspaceH() 54 if (!pnpmWorkspaceH[pkgName]) { 57 `Package "${pkgName}" at ${ptPath} not in pnpm workspace. Add and run pnpm install? y/n: ` 60 const workspacePath = `${ptDir}/pnpm-workspace.yaml` 61 const content = fs.readFileSync(workspacePath, 'utf8') 62 const workspaceYaml = yaml.load(content) as {packages: string[]} 63 workspaceYaml.packages.push(ptPath) 64 fs.writeFileSync(workspacePath, yaml.dump(workspaceYaml, {lineWidth: -1})) 65 console.log(`Added ${ptPath} to pnpm-workspace.yaml`) 67 console.log('Running pnpm install...') 70 console.log('Committing pnpm-workspace.yaml and pnpm-lock.yaml...') 71 await execFileThrow({cmdA: ['git', 'add', 'pnpm-workspace.yaml', 'pnpm-lock.yaml']}) 72 await execFileThrow({cmdA: ['git', 'commit', '-m', `Add ${pkgName} package to pnpm workspace`]}) 74 console.log('Done. Please re-run your command.') 78 `Package "${pkgName}" not in pnpm workspace.\n` + 79 `Add this line to pnpm-workspace.yaml:\n - "${ptPath}"\n` + 80 `Then run: pnpm install` 86 const depWorkspaceA = _.chain(pkgJson.dependencies).map((versionStr, depName) => { 87 if (_.startsWith(versionStr, 'workspace:')) return depName 91 const pnpmWorkspaceH = await getPnpmWorkspaceH() 93 return [{packageJsonPath, pkgJson}, ..._.flatten(await allPromCalls(depWorkspaceA, async (workspaceName) => { 94 const {path} = pnpmWorkspaceH[workspaceName] 95 return await recPkgJsonsPathA({ 97 packageJsonPath: path + '/package.json', 102export const getPkgJsonPtPathsA = async ({appPath}: {appPath: string}) => { 103 const packageJsonPath = `${ptDir}/${appPath}/package.json` 104 const pkgJsonA = await recPkgJsonsPathA({packageJsonPath}) 105 return _.map(_.map(pkgJsonA, 'packageJsonPath'), absPathToPtPath)