Skip to content

Commit

Permalink
feat: allow users to import from node_modules (#1021)
Browse files Browse the repository at this point in the history
  • Loading branch information
TGTGamer authored Mar 16, 2024
1 parent d49731b commit f8f214d
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 13 deletions.
22 changes: 15 additions & 7 deletions packages/schema/src/utils/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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[] {
Expand Down
36 changes: 30 additions & 6 deletions packages/schema/src/utils/pkg-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ 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
* @template e A type parameter that extends boolean
*/
export type FindUp<e extends boolean> = 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
Expand All @@ -37,6 +37,30 @@ export function findUp<e extends boolean = false>(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);

Expand Down Expand Up @@ -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
*/
Expand Down

0 comments on commit f8f214d

Please sign in to comment.