import { isNonEmptyArray } from '@sindresorhus/is'; import { findPackages } from 'find-packages'; import upath from 'upath'; import { GlobalConfig } from '../../../config/global.ts'; import { logger } from '../../../logger/index.ts'; import { parseJson } from '../../../util/common.ts'; import { getSiblingFileName, readLocalFile } from '../../../util/fs/index.ts'; import { ensureLocalPath } from '../../../util/fs/util.ts'; import { extractPackageJson } from '../npm/extract/common/package-file.ts'; import type { NpmPackage } from '../npm/extract/types.ts'; import type { PackageFile } from '../types.ts'; import type { DenoManagerData } from './types.ts'; export async function extractDenoCompatiblePackageJson( packageFile: string, ): Promise | null> { const packageFileContent = await readLocalFile(packageFile, 'utf8'); if (!packageFileContent) { logger.debug({ packageFile }, 'Deno: No package.json found'); return null; } let packageJson: NpmPackage; try { packageJson = parseJson(packageFileContent, packageFile) as NpmPackage; } catch (err) { logger.error({ err, packageFile }, 'Error parsing package.json'); return null; } const extracted = extractPackageJson(packageJson, packageFile); if (!extracted) { return null; } const result = extracted as PackageFile; result.managerData = { packageName: extracted.managerData?.packageJsonName, workspaces: extracted.managerData?.workspaces, }; result.packageFile = packageFile; return result; } // referring to lib/modules/manager/npm/extract/pnpm.ts detectPnpmWorkspaces() export async function detectNodeCompatWorkspaces({ managerData, packageFile, }: Pick, 'managerData' | 'packageFile'>): Promise<{ workspaces?: string[]; packagePaths: string[]; }> { logger.debug(`Detecting deno's node compat Workspaces`); let filters: string[] | undefined; // npm workspace if (isNonEmptyArray(managerData?.workspaces)) { filters = managerData?.workspaces; } // findPackages() matches every package.json below the directory when it is given no patterns if (!filters) { return { packagePaths: [] }; } const localDir = GlobalConfig.get('localDir'); const packages = await findPackages( upath.dirname(ensureLocalPath(packageFile)), { patterns: filters, // Match the ignores used in @pnpm/find-workspace-packages ignore: ['**/node_modules/**', '**/bower_components/**'], }, ); const packagePaths = packages.map((pkg) => { const pkgPath = upath.join(pkg.dir, 'package.json'); return upath.relative(localDir, pkgPath); }); return { workspaces: filters, packagePaths, }; } export async function collectPackageJson( lockFile: string, ): Promise[] | null> { const lockFiles = [lockFile]; const packageFiles: PackageFile[] = []; const rootPackageJson = getSiblingFileName(lockFile, 'package.json'); const rootPackageFile = await extractDenoCompatiblePackageJson(rootPackageJson); if (rootPackageFile) { rootPackageFile.lockFiles = lockFiles; // detect node compat workspaces const result = await detectNodeCompatWorkspaces(rootPackageFile); const { workspaces, packagePaths } = result; rootPackageFile.managerData = { packageName: rootPackageFile.managerData?.packageName, // override workspace workspaces, }; packageFiles.push(rootPackageFile); for (const packagePath of packagePaths) { const packageFile = await extractDenoCompatiblePackageJson(packagePath); if (packageFile) { packageFile.lockFiles = lockFiles; packageFiles.push(packageFile); } } } return packageFiles; }