Skip to content

Commit f8f214d

Browse files
authored
feat: allow users to import from node_modules (#1021)
1 parent d49731b commit f8f214d

File tree

2 files changed

+45
-13
lines changed

2 files changed

+45
-13
lines changed

packages/schema/src/utils/ast-utils.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
import { isFromStdlib } from '@zenstackhq/sdk';
1919
import { AstNode, getDocument, LangiumDocuments, Mutable } from 'langium';
2020
import { URI, Utils } from 'vscode-uri';
21+
import { findNodeModulesFile } from './pkg-utils';
22+
import {isAbsolute} from 'node:path'
2123

2224
export function extractDataModelsWithAllowRules(model: Model): DataModel[] {
2325
return model.declarations.filter(
@@ -94,15 +96,21 @@ export function getDataModelFieldReference(expr: Expression): DataModelField | u
9496
}
9597

9698
export function resolveImportUri(imp: ModelImport): URI | undefined {
97-
if (imp.path === undefined || imp.path.length === 0) {
98-
return undefined;
99+
if (!imp.path) return undefined; // This will return true if imp.path is undefined, null, or an empty string ("").
100+
101+
if (!imp.path.endsWith('.zmodel')) {
102+
imp.path += '.zmodel';
99103
}
100-
const dirUri = Utils.dirname(getDocument(imp).uri);
101-
let grammarPath = imp.path;
102-
if (!grammarPath.endsWith('.zmodel')) {
103-
grammarPath += '.zmodel';
104+
105+
if (
106+
!imp.path.startsWith('.') // Respect relative paths
107+
&& !isAbsolute(imp.path) // Respect Absolute paths
108+
) {
109+
imp.path = findNodeModulesFile(imp.path) ?? imp.path;
104110
}
105-
return Utils.resolvePath(dirUri, grammarPath);
111+
112+
const dirUri = Utils.dirname(getDocument(imp).uri);
113+
return Utils.resolvePath(dirUri, imp.path);
106114
}
107115

108116
export function resolveTransitiveImports(documents: LangiumDocuments, model: Model): Model[] {

packages/schema/src/utils/pkg-utils.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import { execSync } from './exec-utils';
55
export type PackageManagers = 'npm' | 'yarn' | 'pnpm';
66

77
/**
8-
* A type named FindUp that takes a type parameter e which extends boolean.
9-
* If e extends true, it returns a union type of string[] or undefined.
8+
* A type named FindUp that takes a type parameter e which extends boolean.
9+
* If e extends true, it returns a union type of string[] or undefined.
1010
* If e does not extend true, it returns a union type of string or undefined.
1111
*
1212
* @export
1313
* @template e A type parameter that extends boolean
1414
*/
1515
export type FindUp<e extends boolean> = e extends true ? string[] | undefined : string | undefined
1616
/**
17-
* Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path.
18-
* Optionally return a single path or multiple paths.
19-
* If multiple allowed, return all paths found.
17+
* Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path.
18+
* Optionally return a single path or multiple paths.
19+
* If multiple allowed, return all paths found.
2020
* If no paths are found, return undefined.
2121
*
2222
* @export
@@ -37,6 +37,30 @@ export function findUp<e extends boolean = false>(names: string[], cwd: string =
3737
return findUp(names, up, multiple, result);
3838
}
3939

40+
41+
/**
42+
* Find a Node module/file given its name in a specific directory, with a fallback to the current working directory.
43+
* If the name is empty, return undefined.
44+
* Try to resolve the module/file using require.resolve with the specified directory as the starting point.
45+
* Return the resolved path if successful, otherwise return undefined.
46+
*
47+
* @export
48+
* @param {string} name The name of the module/file to find
49+
* @param {string} [cwd=process.cwd()]
50+
* @returns {*} Finds a specified module or file using require.resolve starting from a specified directory path, or the current working directory if not provided.
51+
*/
52+
export function findNodeModulesFile(name: string, cwd: string = process.cwd()) {
53+
if (!name) return undefined;
54+
try {
55+
// Use require.resolve to find the module/file. The paths option allows specifying the directory to start from.
56+
const resolvedPath = require.resolve(name, { paths: [cwd] })
57+
return resolvedPath
58+
} catch (error) {
59+
// If require.resolve fails to find the module/file, it will throw an error.
60+
return undefined
61+
}
62+
}
63+
4064
function getPackageManager(projectPath = '.'): PackageManagers {
4165
const lockFile = findUp(['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json'], projectPath);
4266

@@ -106,7 +130,7 @@ export function ensurePackage(
106130
}
107131

108132
/**
109-
* 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.
133+
* 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.
110134
* 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.
111135
* @deprecated Use findUp instead @see findUp
112136
*/

0 commit comments

Comments
 (0)