package-paths.ts
1 import * as fs from 'node:fs'; 2 import * as path from 'node:path'; 3 4 export interface PackageJsonLike { 5 bin?: string | Record<string, string>; 6 main?: string; 7 } 8 9 export function findPackageRoot(startFile: string, fileExists: (candidate: string) => boolean = fs.existsSync): string { 10 let dir = path.dirname(startFile); 11 12 while (true) { 13 if (fileExists(path.join(dir, 'package.json'))) return dir; 14 const parent = path.dirname(dir); 15 if (parent === dir) { 16 throw new Error(`Could not find package.json above ${startFile}`); 17 } 18 dir = parent; 19 } 20 } 21 22 export function getBuiltEntryCandidates( 23 packageRoot: string, 24 readFile: (filePath: string) => string = (filePath) => fs.readFileSync(filePath, 'utf-8'), 25 ): string[] { 26 const candidates: string[] = []; 27 try { 28 const pkg = JSON.parse(readFile(path.join(packageRoot, 'package.json'))) as PackageJsonLike; 29 30 if (typeof pkg.bin === 'string') { 31 candidates.push(path.join(packageRoot, pkg.bin)); 32 } else if (pkg.bin && typeof pkg.bin === 'object' && typeof pkg.bin.opencli === 'string') { 33 candidates.push(path.join(packageRoot, pkg.bin.opencli)); 34 } 35 36 if (typeof pkg.main === 'string') { 37 candidates.push(path.join(packageRoot, pkg.main)); 38 } 39 } catch { 40 // Fall through to compatibility candidates below. 41 } 42 43 // Compatibility fallback for partially-built trees or older layouts. 44 candidates.push( 45 path.join(packageRoot, 'dist', 'src', 'main.js'), 46 path.join(packageRoot, 'dist', 'main.js'), 47 ); 48 49 return [...new Set(candidates)]; 50 } 51 52 export function getCliManifestPath(clisDir: string): string { 53 return path.resolve(clisDir, '..', 'cli-manifest.json'); 54 } 55 56 export function getFetchAdaptersScriptPath(packageRoot: string): string { 57 return path.join(packageRoot, 'scripts', 'fetch-adapters.js'); 58 }