|
| 1 | +import { dirname, relative, isAbsolute } from 'path' |
| 2 | +import { promises as fs } from 'fs' |
| 3 | +import findUp from 'find-up' |
| 4 | + |
| 5 | +/** |
| 6 | + * Determines if the `child` path is under the `parent` path. |
| 7 | + */ |
| 8 | +function isInDirectory(parent: string, child: string): boolean { |
| 9 | + const relativePath = relative(parent, child) |
| 10 | + return !relativePath.startsWith('..') && !isAbsolute(relativePath) |
| 11 | +} |
| 12 | + |
| 13 | +/** |
| 14 | + * Iterates over package.json file paths recursively found in parent directories, starting from the |
| 15 | + * current working directory. If the current working directory is in a git repository, then package.json |
| 16 | + * files outside of the git repository will not be yielded. |
| 17 | + */ |
| 18 | +export async function* findPackagePaths(): AsyncGenerator<string> { |
| 19 | + // Find git root if in git repository |
| 20 | + const gitDirectoryPath: string | undefined = await findUp('.git', { type: 'directory' }) |
| 21 | + const gitRootPath: string | undefined = gitDirectoryPath === undefined |
| 22 | + ? undefined |
| 23 | + : dirname(gitDirectoryPath) |
| 24 | + |
| 25 | + function isInGitDirectory(path: string): boolean { |
| 26 | + return gitRootPath === undefined || isInDirectory(gitRootPath, path) |
| 27 | + } |
| 28 | + |
| 29 | + let cwd: string = process.cwd() |
| 30 | + let packagePath: string | undefined |
| 31 | + |
| 32 | + while ( |
| 33 | + (packagePath = await findUp('package.json', { type: 'file', cwd })) && |
| 34 | + isInGitDirectory(packagePath) |
| 35 | + ) { |
| 36 | + yield packagePath |
| 37 | + cwd = dirname(dirname(packagePath)) |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +export async function findDependencies( |
| 42 | + { packagePaths, keys, warnings }: { |
| 43 | + packagePaths: AsyncIterable<string> | Iterable<string>, |
| 44 | + keys: string[], |
| 45 | + warnings: string[] |
| 46 | + } |
| 47 | +): Promise<string[]> { |
| 48 | + const dependencies: Set<string> = new Set() |
| 49 | + |
| 50 | + for await (const packagePath of packagePaths) { |
| 51 | + try { |
| 52 | + const pkg: { [key in PropertyKey]: any } = JSON.parse((await fs.readFile(packagePath)).toString()) ?? {} |
| 53 | + |
| 54 | + for (const key of keys) { |
| 55 | + const dependenciesToVersions: { [key in PropertyKey]: any } = pkg[key] ?? {} |
| 56 | + |
| 57 | + for (const dependency of Object.keys(dependenciesToVersions)) { |
| 58 | + dependencies.add(dependency) |
| 59 | + } |
| 60 | + } |
| 61 | + } catch { |
| 62 | + warnings.push(`Couldn't process '${packagePath}'. Make sure it is a valid JSON or use the 'packagePath' option`) |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + return Array.from(dependencies) |
| 67 | +} |
0 commit comments