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 all 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,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
) {
TGTGamer marked this conversation as resolved.
Show resolved Hide resolved
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()) {
TGTGamer marked this conversation as resolved.
Show resolved Hide resolved
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
Loading