import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
const RESOURCE_ROOT_ENV_VAR = 'XCODEBUILDMCP_RESOURCE_ROOT';
let cachedPackageRoot: string | null = null;
let cachedResourceRoot: string | null = null;
export function resetResourceRootCacheForTests(): void {
cachedPackageRoot = null;
cachedResourceRoot = null;
}
function hasResourceLayout(root: string): boolean {
return fs.existsSync(path.join(root, 'manifests')) || fs.existsSync(path.join(root, 'bundled'));
}
function findPackageRootFrom(startDir: string): string | null {
let dir = startDir;
while (dir !== path.dirname(dir)) {
if (fs.existsSync(path.join(dir, 'package.json'))) {
return dir;
}
dir = path.dirname(dir);
}
return null;
}
export function getPackageRoot(): string {
if (cachedPackageRoot) {
return cachedPackageRoot;
}
const candidates: string[] = [];
const importMetaUrl = typeof import.meta.url === 'string' ? import.meta.url : null;
if (importMetaUrl) {
candidates.push(path.dirname(fileURLToPath(importMetaUrl)));
}
candidates.push(process.cwd());
const entry = process.argv[1];
if (entry) {
candidates.push(path.dirname(entry));
}
for (const candidate of candidates) {
const found = findPackageRootFrom(candidate);
if (found) {
cachedPackageRoot = found;
return found;
}
}
throw new Error('Could not find package root (no package.json found in parent directories)');
}
function getExecutableResourceRoot(): string | null {
const execPath = process.execPath;
const candidateDirs = [path.dirname(execPath), path.dirname(path.dirname(execPath))];
for (const candidate of candidateDirs) {
if (hasResourceLayout(candidate)) {
return candidate;
}
}
return null;
}
export function getResourceRoot(): string {
if (cachedResourceRoot) {
return cachedResourceRoot;
}
const explicitRoot = process.env[RESOURCE_ROOT_ENV_VAR];
if (explicitRoot) {
cachedResourceRoot = path.resolve(explicitRoot);
return cachedResourceRoot;
}
const executableRoot = getExecutableResourceRoot();
if (executableRoot) {
cachedResourceRoot = executableRoot;
return cachedResourceRoot;
}
cachedResourceRoot = getPackageRoot();
return cachedResourceRoot;
}
export function getManifestsDir(): string {
return path.join(getResourceRoot(), 'manifests');
}
export function getBundledAxePath(): string {
return path.join(getResourceRoot(), 'bundled', 'axe');
}
export function getBundledFrameworksDir(): string {
return path.join(getResourceRoot(), 'bundled', 'Frameworks');
}