chore(vendor): import pi 0.85.1
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
86
third_party/pi/packages/coding-agent/src/experimental/source-resolver.ts
vendored
Normal file
86
third_party/pi/packages/coding-agent/src/experimental/source-resolver.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||
import { registerHooks } from "node:module";
|
||||
import { extname, resolve, sep } from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
// Node strips TypeScript natively, but it does not apply the workspace source
|
||||
// aliases from tsconfig.json. Internal source processes preload this resolver so
|
||||
// they cannot silently fall through to stale package dist files.
|
||||
|
||||
interface TsConfig {
|
||||
readonly compilerOptions?: {
|
||||
readonly paths?: Readonly<Record<string, readonly string[]>>;
|
||||
};
|
||||
}
|
||||
|
||||
interface SourceAlias {
|
||||
readonly pattern: string;
|
||||
readonly prefix: string;
|
||||
readonly suffix: string;
|
||||
readonly replacements: readonly string[];
|
||||
}
|
||||
|
||||
const repositoryRoot = fileURLToPath(new URL("../../../..", import.meta.url));
|
||||
const tsconfigPath = resolve(repositoryRoot, "tsconfig.json");
|
||||
const tsconfig = JSON.parse(readFileSync(tsconfigPath, "utf8")) as TsConfig;
|
||||
const paths = tsconfig.compilerOptions?.paths;
|
||||
if (!paths) throw new Error(`Source runtime requires compilerOptions.paths in ${tsconfigPath}`);
|
||||
|
||||
const aliases: SourceAlias[] = Object.entries(paths)
|
||||
.filter(([pattern]) => pattern.startsWith("@earendil-works/"))
|
||||
.map(([pattern, replacements]) => {
|
||||
const wildcard = pattern.indexOf("*");
|
||||
if (wildcard !== -1 && pattern.indexOf("*", wildcard + 1) !== -1) {
|
||||
throw new Error(`Source runtime does not support multiple wildcards in ${pattern}`);
|
||||
}
|
||||
return {
|
||||
pattern,
|
||||
prefix: wildcard === -1 ? pattern : pattern.slice(0, wildcard),
|
||||
suffix: wildcard === -1 ? "" : pattern.slice(wildcard + 1),
|
||||
replacements,
|
||||
};
|
||||
})
|
||||
.sort((left, right) => right.pattern.length - left.pattern.length);
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
let matchedPattern: string | undefined;
|
||||
for (const alias of aliases) {
|
||||
const wildcard = matchAlias(alias, specifier);
|
||||
if (wildcard === undefined) continue;
|
||||
matchedPattern ??= alias.pattern;
|
||||
for (const replacement of alias.replacements) {
|
||||
const resolved = resolveSourcePath(replacement.replace("*", wildcard));
|
||||
if (resolved) return { url: pathToFileURL(resolved).href, shortCircuit: true };
|
||||
}
|
||||
}
|
||||
if (matchedPattern) {
|
||||
throw new Error(`Source runtime could not resolve ${specifier} through tsconfig path ${matchedPattern}`);
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
|
||||
function matchAlias(alias: SourceAlias, specifier: string): string | undefined {
|
||||
if (!alias.pattern.includes("*")) return specifier === alias.pattern ? "" : undefined;
|
||||
if (!specifier.startsWith(alias.prefix) || !specifier.endsWith(alias.suffix)) return undefined;
|
||||
return specifier.slice(alias.prefix.length, specifier.length - alias.suffix.length);
|
||||
}
|
||||
|
||||
function resolveSourcePath(replacement: string): string | undefined {
|
||||
const basePath = resolve(repositoryRoot, replacement);
|
||||
const rootPrefix = repositoryRoot.endsWith(sep) ? repositoryRoot : `${repositoryRoot}${sep}`;
|
||||
if (!basePath.startsWith(rootPrefix)) return undefined;
|
||||
const extension = extname(basePath);
|
||||
const sourceExtension =
|
||||
extension === ".js" ? ".ts" : extension === ".mjs" ? ".mts" : extension === ".cjs" ? ".cts" : undefined;
|
||||
const candidates = sourceExtension
|
||||
? [basePath, `${basePath.slice(0, -extension.length)}${sourceExtension}`]
|
||||
: extension === ".ts" || extension === ".mts" || extension === ".cts" || extension === ".json"
|
||||
? [basePath]
|
||||
: [basePath, `${basePath}.ts`, resolve(basePath, "index.ts")];
|
||||
for (const candidate of candidates) {
|
||||
if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user