diff --git a/packages/schema/src/utils/ast-utils.ts b/packages/schema/src/utils/ast-utils.ts index 348752fae..c752c837d 100644 --- a/packages/schema/src/utils/ast-utils.ts +++ b/packages/schema/src/utils/ast-utils.ts @@ -18,6 +18,8 @@ import { import { isFromStdlib } from '@zenstackhq/sdk'; import { AstNode, getDocument, LangiumDocuments, Mutable } from 'langium'; import { URI, Utils } from 'vscode-uri'; +import { findNodeModulesFile } from './pkg-utils'; +import {isAbsolute} from 'node:path' export function extractDataModelsWithAllowRules(model: Model): DataModel[] { return model.declarations.filter( @@ -94,15 +96,21 @@ export function getDataModelFieldReference(expr: Expression): DataModelField | u } export function resolveImportUri(imp: ModelImport): URI | undefined { - if (imp.path === undefined || imp.path.length === 0) { - return undefined; + if (!imp.path) return undefined; // This will return true if imp.path is undefined, null, or an empty string (""). + + if (!imp.path.endsWith('.zmodel')) { + imp.path += '.zmodel'; } - const dirUri = Utils.dirname(getDocument(imp).uri); - let grammarPath = imp.path; - if (!grammarPath.endsWith('.zmodel')) { - grammarPath += '.zmodel'; + + if ( + !imp.path.startsWith('.') // Respect relative paths + && !isAbsolute(imp.path) // Respect Absolute paths + ) { + imp.path = findNodeModulesFile(imp.path) ?? imp.path; } - return Utils.resolvePath(dirUri, grammarPath); + + const dirUri = Utils.dirname(getDocument(imp).uri); + return Utils.resolvePath(dirUri, imp.path); } export function resolveTransitiveImports(documents: LangiumDocuments, model: Model): Model[] { diff --git a/packages/schema/src/utils/pkg-utils.ts b/packages/schema/src/utils/pkg-utils.ts index ce41dac34..99593dcd3 100644 --- a/packages/schema/src/utils/pkg-utils.ts +++ b/packages/schema/src/utils/pkg-utils.ts @@ -5,8 +5,8 @@ import { execSync } from './exec-utils'; export type PackageManagers = 'npm' | 'yarn' | 'pnpm'; /** - * A type named FindUp that takes a type parameter e which extends boolean. - * If e extends true, it returns a union type of string[] or undefined. + * A type named FindUp that takes a type parameter e which extends boolean. + * If e extends true, it returns a union type of string[] or undefined. * If e does not extend true, it returns a union type of string or undefined. * * @export @@ -14,9 +14,9 @@ export type PackageManagers = 'npm' | 'yarn' | 'pnpm'; */ export type FindUp = e extends true ? string[] | undefined : string | undefined /** - * Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path. - * Optionally return a single path or multiple paths. - * If multiple allowed, return all paths found. + * Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path. + * Optionally return a single path or multiple paths. + * If multiple allowed, return all paths found. * If no paths are found, return undefined. * * @export @@ -37,6 +37,30 @@ export function findUp(names: string[], cwd: string = return findUp(names, up, multiple, result); } + +/** + * Find a Node module/file given its name in a specific directory, with a fallback to the current working directory. + * If the name is empty, return undefined. + * Try to resolve the module/file using require.resolve with the specified directory as the starting point. + * Return the resolved path if successful, otherwise return undefined. + * + * @export + * @param {string} name The name of the module/file to find + * @param {string} [cwd=process.cwd()] + * @returns {*} Finds a specified module or file using require.resolve starting from a specified directory path, or the current working directory if not provided. + */ +export function findNodeModulesFile(name: string, cwd: string = process.cwd()) { + if (!name) return undefined; + try { + // Use require.resolve to find the module/file. The paths option allows specifying the directory to start from. + const resolvedPath = require.resolve(name, { paths: [cwd] }) + return resolvedPath + } catch (error) { + // If require.resolve fails to find the module/file, it will throw an error. + return undefined + } +} + function getPackageManager(projectPath = '.'): PackageManagers { const lockFile = findUp(['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json'], projectPath); @@ -106,7 +130,7 @@ export function ensurePackage( } /** - * A function that searches for the nearest package.json file starting from the provided search path or the current working directory if no search path is provided. + * A function that searches for the nearest package.json file starting from the provided search path or the current working directory if no search path is provided. * It iterates through the directory structure going one level up at a time until it finds a package.json file. If no package.json file is found, it returns undefined. * @deprecated Use findUp instead @see findUp */