mlld
Version:
mlld: llm scripting language
295 lines (292 loc) • 8.96 kB
JavaScript
import { logger } from './chunk-M3R2H5KU.mjs';
import { MlldResolutionError, MlldFileNotFoundError } from './chunk-V5XE5YB5.mjs';
import { __name, __publicField } from './chunk-NJQT543K.mjs';
import * as path from 'path';
import * as os from 'os';
async function findProjectRoot(startPath, fileSystem) {
let currentDir = path.resolve(startPath);
const homeDir = os.homedir();
logger.debug(`Finding project root from: ${startPath}`);
while (currentDir !== homeDir && currentDir !== path.dirname(currentDir)) {
const configFiles = [
"mlld-config.json",
"mlld-lock.json",
"mlld.lock.json"
// Backward compatibility
];
for (const configFile of configFiles) {
const configPath = path.join(currentDir, configFile);
if (await fileSystem.exists(configPath)) {
logger.debug(`Found ${configFile} at: ${currentDir}`);
return currentDir;
}
}
const fallbackIndicators = [
"package.json",
".git",
"pyproject.toml",
"Cargo.toml"
];
for (const indicator of fallbackIndicators) {
if (await fileSystem.exists(path.join(currentDir, indicator))) {
logger.warn(`Found project root at ${currentDir} but no mlld config files found`);
return currentDir;
}
}
currentDir = path.dirname(currentDir);
}
logger.debug(`No project root found, using original path: ${startPath}`);
return startPath;
}
__name(findProjectRoot, "findProjectRoot");
// core/resolvers/ProjectPathResolver.ts
var _ProjectPathResolver = class _ProjectPathResolver {
constructor(fileSystem) {
__publicField(this, "fileSystem");
__publicField(this, "name", "base");
__publicField(this, "aliases", [
"root"
]);
__publicField(this, "description", "Resolves @base and @root references to project root files");
__publicField(this, "type", "io");
__publicField(this, "capabilities", {
io: {
read: true,
write: false,
list: false
},
contexts: {
import: true,
path: true,
output: false
},
supportedContentTypes: [
"text",
"module"
],
defaultContentType: "text",
priority: 1,
cache: {
strategy: "none"
}
// Project path is static
});
this.fileSystem = fileSystem;
}
canResolve(ref, config) {
return ref.startsWith("@base") || ref.startsWith("@root") || !!config;
}
/**
* Resolve a @base reference
*/
async resolve(ref, config) {
let basePath = config?.basePath;
if (!basePath || !await this.isProjectRoot(basePath)) {
logger.debug(`Detecting project root (basePath: ${basePath})`);
basePath = await this.findProjectRootFromCwd();
}
if (!basePath) {
throw new MlldResolutionError("ProjectPathResolver: Unable to determine project root. This usually means the resolver registry was not properly configured. Check that @base prefix is mapped to base resolver with basePath.", {
reference: ref,
availableConfig: Object.keys(config || {})
});
}
if (!config || !config.context || config.context === "variable") {
if (ref === "@base" || ref === "base" || ref === "@root" || ref === "root") {
const metadata = {
source: "base",
timestamp: /* @__PURE__ */ new Date()
};
return {
content: basePath,
contentType: "text",
mx: metadata,
metadata
};
}
}
if (config.context === "path") {
let relativePath;
if (ref.startsWith("@base/")) {
relativePath = ref.substring("@base/".length);
} else if (ref.startsWith("@base")) {
relativePath = ref.substring("@base".length);
} else if (ref.startsWith("@root/")) {
relativePath = ref.substring("@root/".length);
} else if (ref.startsWith("@root")) {
relativePath = ref.substring("@root".length);
} else {
relativePath = ref;
}
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
if (!relativePath) {
const metadata = {
source: "base",
timestamp: /* @__PURE__ */ new Date()
};
return {
content: basePath,
contentType: "text",
mx: metadata,
metadata
};
}
const fullPath = path.resolve(basePath, relativePath);
const normalizedBasePath = path.resolve(basePath);
const normalizedFullPath = path.resolve(fullPath);
if (!normalizedFullPath.startsWith(normalizedBasePath)) {
throw new MlldResolutionError(`Path outside project directory: ${relativePath}`, {});
}
if (!await this.fileSystem.exists(fullPath)) {
if (!path.extname(fullPath)) {
const withMld = fullPath + ".mld";
if (await this.fileSystem.exists(withMld)) {
const content = await this.fileSystem.readFile(withMld);
const contentType = await this.detectContentType(withMld, content);
const metadata = {
source: withMld,
timestamp: /* @__PURE__ */ new Date(),
originalRef: ref
};
return {
content,
contentType,
mx: metadata,
metadata
};
}
}
throw new MlldFileNotFoundError(`File not found: ${fullPath}`, {
path: fullPath
});
}
try {
const content = await this.fileSystem.readFile(fullPath);
const contentType = await this.detectContentType(fullPath, content);
const metadata = {
source: fullPath,
timestamp: /* @__PURE__ */ new Date(),
originalRef: ref
};
return {
content,
contentType,
mx: metadata,
metadata
};
} catch (error) {
throw new MlldFileNotFoundError(`Failed to read file: ${fullPath}`, {
path: fullPath
});
}
}
if (config.context === "import") {
const result = await this.resolve(ref, {
...config,
context: "path"
});
if (result.contentType !== "module") {
throw new MlldResolutionError(`Import target is not a module: ${ref}`, {});
}
return result;
}
throw new MlldResolutionError(`base resolver does not support context: ${config.context}`, {});
}
/**
* Validate resolver configuration
*/
validateConfig(config) {
const errors = [];
if (!config) {
errors.push("Configuration is required");
return errors;
}
if (!config.basePath) {
errors.push("basePath is required");
} else if (typeof config.basePath !== "string") {
errors.push("basePath must be a string");
}
return errors;
}
/**
* Check if an operation is allowed
*/
async checkAccess(ref, operation, config) {
if (operation === "write" && config?.readonly) {
return false;
}
return operation === "read";
}
/**
* Detect content type based on file extension and content
*/
async detectContentType(filePath, content) {
if (filePath.endsWith(".mld.md") || filePath.endsWith(".mld") || filePath.endsWith(".mlld.md") || filePath.endsWith(".mlld")) {
return "module";
}
if (filePath.endsWith(".json")) {
return "data";
}
try {
const { parse } = await import('./parser-6HNWFG6W.mjs');
const result = await parse(content);
if (result.success && this.hasModuleExports(result.ast)) {
return "module";
}
} catch {
}
try {
JSON.parse(content);
return "data";
} catch {
}
return "text";
}
/**
* Check if AST has module exports
*/
hasModuleExports(ast) {
if (!ast || !Array.isArray(ast)) return false;
return ast.some((node) => node && node.type === "Directive" && [
"var",
"exe",
"path"
].includes(node.kind));
}
/**
* Check if a path is likely a project root
*/
async isProjectRoot(path3) {
const indicators = [
"package.json",
".git",
"mlld.config.json",
"mlld.lock.json"
];
for (const indicator of indicators) {
if (await this.fileSystem.exists(path3 + "/" + indicator)) {
return true;
}
}
return false;
}
/**
* Find project root from current working directory
*/
async findProjectRootFromCwd() {
try {
const projectRoot = await findProjectRoot(process.cwd(), this.fileSystem);
return projectRoot;
} catch (error) {
logger.warn("Failed to find project root:", error);
return null;
}
}
};
__name(_ProjectPathResolver, "ProjectPathResolver");
var ProjectPathResolver = _ProjectPathResolver;
export { ProjectPathResolver, findProjectRoot };
//# sourceMappingURL=chunk-2CD5MH5K.mjs.map
//# sourceMappingURL=chunk-2CD5MH5K.mjs.map