Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow users to import from node_modules #1021

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,7 @@ import {
import { isFromStdlib } from '@zenstackhq/sdk';
import { AstNode, getDocument, LangiumDocuments, Mutable } from 'langium';
import { URI, Utils } from 'vscode-uri';
import { findNodeModulesFile } from './pkg-utils';

export function extractDataModelsWithAllowRules(model: Model): DataModel[] {
return model.declarations.filter(
Expand Down Expand Up @@ -94,15 +95,22 @@ 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';
}

if (
!imp.path.startsWith('.') // Respect relative paths
&& !imp.path.startsWith('/') // Respect absolute paths (Unix)
&& !/^[a-zA-Z]:\\/.test(imp.path) // Respect absolute paths (Windows)
) {
TGTGamer marked this conversation as resolved.
Show resolved Hide resolved
imp.path = findNodeModulesFile(imp.path) ?? imp.path;
}

const dirUri = Utils.dirname(getDocument(imp).uri);
let grammarPath = imp.path;
if (!grammarPath.endsWith('.zmodel')) {
grammarPath += '.zmodel';
}
return Utils.resolvePath(dirUri, grammarPath);
return Utils.resolvePath(dirUri, imp.path);
ymc9 marked this conversation as resolved.
Show resolved Hide resolved
}

export function resolveTransitiveImports(documents: LangiumDocuments, model: Model): Model[] {
Expand Down
38 changes: 32 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,32 @@ export function findUp<e extends boolean = false>(names: string[], cwd: string =
return findUp(names, up, multiple, result);
}

/**
* A function that finds a file within the node_modules directory by the provided name.
* It first searches for the node_modules directory using the findUp function, and if not found, returns undefined.
* It then splits the provided name into folder and file parts.
* It finds the location of the folder in the node_modules directory by checking for its existence.
* If the folder is not found, it returns undefined.
* It then constructs the file location by joining the folder location and the file name with a '.zmodel' extension if the file does not already have an extension.
* Returns the file location if it exists, otherwise returns undefined.
* @author Jonathan Stevens (TGTGamer)
TGTGamer marked this conversation as resolved.
Show resolved Hide resolved
*
* @export
* @param name A string representing the name of the file to find in the node_modules folder
* @returns Path to a specific file within the node_modules directory
*/
export function findNodeModulesFile(name: string, cwd: string = process.cwd()) {
TGTGamer marked this conversation as resolved.
Show resolved Hide resolved
if (!name) return undefined;
const nodeModules = findUp(['node_modules'], cwd, true);
if (!nodeModules) return undefined;
const split = name.split('/')
const folder = split.slice(0, -1).join('/');
const folderLocation = nodeModules.find((dir) =>fs.existsSync(path.join(dir, folder)));
if (!folderLocation) return undefined;
const fileLocation = path.join(folderLocation, name);
return fs.existsSync(fileLocation) ? fileLocation : undefined;
}
ymc9 marked this conversation as resolved.
Show resolved Hide resolved

function getPackageManager(projectPath = '.'): PackageManagers {
const lockFile = findUp(['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json'], projectPath);

Expand Down Expand Up @@ -106,7 +132,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
Loading