/ src / package-paths.ts
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  }