From 3d78fdcb12285fec753364e7b915f5c05a67019a Mon Sep 17 00:00:00 2001 From: JG Date: Tue, 4 Apr 2023 22:58:38 +0100 Subject: [PATCH 01/11] feat: import module --- .vscode/launch.json | 2 +- packages/language/src/generated/ast.ts | 19 +- packages/language/src/generated/grammar.ts | 350 ++++++++++-------- packages/language/src/zmodel.langium | 5 + .../language/syntaxes/zmodel.tmLanguage.json | 2 +- .../src/language-server/zmodel-module.ts | 3 +- .../src/language-server/zmodel-scope.ts | 34 +- packages/schema/src/utils/ast-utils.ts | 15 + 8 files changed, 276 insertions(+), 154 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 45d27c896..193903fc4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,7 +41,7 @@ "request": "attach", "skipFiles": ["/**"], "sourceMaps": true, - "outFiles": ["${workspaceFolder}/bundle/**/*.js"] + "outFiles": ["${workspaceFolder}/packages/schema/bundle/**/*.js"] }, { "name": "Todo sample: debug server-side", diff --git a/packages/language/src/generated/ast.ts b/packages/language/src/generated/ast.ts index 8e00e671e..f485ee48d 100644 --- a/packages/language/src/generated/ast.ts +++ b/packages/language/src/generated/ast.ts @@ -409,6 +409,7 @@ export function isMemberAccessExpr(item: unknown): item is MemberAccessExpr { export interface Model extends AstNode { readonly $type: 'Model'; declarations: Array + imports: Array } export const Model = 'Model'; @@ -417,6 +418,18 @@ export function isModel(item: unknown): item is Model { return reflection.isInstance(item, Model); } +export interface ModelImport extends AstNode { + readonly $container: Model; + readonly $type: 'ModelImport'; + path: string +} + +export const ModelImport = 'ModelImport'; + +export function isModelImport(item: unknown): item is ModelImport { + return reflection.isInstance(item, ModelImport); +} + export interface NullExpr extends AstNode { readonly $container: Argument | ArrayExpr | AttributeArg | BinaryExpr | DataSourceField | FieldInitializer | FunctionDecl | GeneratorField | MemberAccessExpr | PluginField | UnaryExpr; readonly $type: 'NullExpr'; @@ -548,6 +561,7 @@ export interface ZModelAstType { LiteralExpr: LiteralExpr MemberAccessExpr: MemberAccessExpr Model: Model + ModelImport: ModelImport NullExpr: NullExpr ObjectExpr: ObjectExpr Plugin: Plugin @@ -563,7 +577,7 @@ export interface ZModelAstType { export class ZModelAstReflection extends AbstractAstReflection { getAllTypes(): string[] { - return ['AbstractDeclaration', 'Argument', 'ArrayExpr', 'Attribute', 'AttributeArg', 'AttributeAttribute', 'AttributeParam', 'AttributeParamType', 'BinaryExpr', 'DataModel', 'DataModelAttribute', 'DataModelField', 'DataModelFieldAttribute', 'DataModelFieldType', 'DataSource', 'DataSourceField', 'Enum', 'EnumField', 'Expression', 'FieldInitializer', 'FunctionDecl', 'FunctionParam', 'FunctionParamType', 'GeneratorDecl', 'GeneratorField', 'InvocationExpr', 'LiteralExpr', 'MemberAccessExpr', 'Model', 'NullExpr', 'ObjectExpr', 'Plugin', 'PluginField', 'ReferenceArg', 'ReferenceExpr', 'ReferenceTarget', 'ThisExpr', 'TypeDeclaration', 'UnaryExpr']; + return ['AbstractDeclaration', 'Argument', 'ArrayExpr', 'Attribute', 'AttributeArg', 'AttributeAttribute', 'AttributeParam', 'AttributeParamType', 'BinaryExpr', 'DataModel', 'DataModelAttribute', 'DataModelField', 'DataModelFieldAttribute', 'DataModelFieldType', 'DataSource', 'DataSourceField', 'Enum', 'EnumField', 'Expression', 'FieldInitializer', 'FunctionDecl', 'FunctionParam', 'FunctionParamType', 'GeneratorDecl', 'GeneratorField', 'InvocationExpr', 'LiteralExpr', 'MemberAccessExpr', 'Model', 'ModelImport', 'NullExpr', 'ObjectExpr', 'Plugin', 'PluginField', 'ReferenceArg', 'ReferenceExpr', 'ReferenceTarget', 'ThisExpr', 'TypeDeclaration', 'UnaryExpr']; } protected override computeIsSubtype(subtype: string, supertype: string): boolean { @@ -797,7 +811,8 @@ export class ZModelAstReflection extends AbstractAstReflection { return { name: 'Model', mandatory: [ - { name: 'declarations', type: 'array' } + { name: 'declarations', type: 'array' }, + { name: 'imports', type: 'array' } ] }; } diff --git a/packages/language/src/generated/grammar.ts b/packages/language/src/generated/grammar.ts index 14d2c86d4..25d4ec4ae 100644 --- a/packages/language/src/generated/grammar.ts +++ b/packages/language/src/generated/grammar.ts @@ -16,17 +16,35 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "Model", "entry": true, "definition": { - "$type": "Assignment", - "feature": "declarations", - "operator": "+=", - "terminal": { - "$type": "RuleCall", - "rule": { - "$ref": "#/rules@1" + "$type": "Group", + "elements": [ + { + "$type": "Assignment", + "feature": "imports", + "operator": "+=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@1" + }, + "arguments": [] + }, + "cardinality": "*" }, - "arguments": [] - }, - "cardinality": "*" + { + "$type": "Assignment", + "feature": "declarations", + "operator": "+=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@2" + }, + "arguments": [] + }, + "cardinality": "*" + } + ] }, "definesHiddenTokens": false, "fragment": false, @@ -34,6 +52,42 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "parameters": [], "wildcard": false }, + { + "$type": "ParserRule", + "name": "ModelImport", + "definition": { + "$type": "Group", + "elements": [ + { + "$type": "Keyword", + "value": "import" + }, + { + "$type": "Assignment", + "feature": "path", + "operator": "=", + "terminal": { + "$type": "RuleCall", + "rule": { + "$ref": "#/rules@58" + }, + "arguments": [] + } + }, + { + "$type": "Keyword", + "value": ";", + "cardinality": "?" + } + ] + }, + "definesHiddenTokens": false, + "entry": false, + "fragment": false, + "hiddenTokens": [], + "parameters": [], + "wildcard": false + }, { "$type": "ParserRule", "name": "AbstractDeclaration", @@ -43,49 +97,49 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@2" + "$ref": "#/rules@3" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@4" + "$ref": "#/rules@5" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@6" + "$ref": "#/rules@7" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@29" + "$ref": "#/rules@30" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@32" + "$ref": "#/rules@33" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@34" + "$ref": "#/rules@35" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@42" + "$ref": "#/rules@43" }, "arguments": [] } @@ -107,7 +161,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -123,7 +177,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -139,7 +193,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@3" + "$ref": "#/rules@4" }, "arguments": [] }, @@ -167,7 +221,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -179,7 +233,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -198,21 +252,21 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@9" + "$ref": "#/rules@10" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@16" + "$ref": "#/rules@17" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@10" + "$ref": "#/rules@11" }, "arguments": [] } @@ -237,7 +291,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -253,7 +307,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -269,7 +323,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@5" + "$ref": "#/rules@6" }, "arguments": [] }, @@ -297,7 +351,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -309,7 +363,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -328,14 +382,14 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@9" + "$ref": "#/rules@10" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@10" + "$ref": "#/rules@11" }, "arguments": [] } @@ -360,7 +414,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -376,7 +430,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -392,7 +446,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@7" + "$ref": "#/rules@8" }, "arguments": [] }, @@ -420,7 +474,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -432,7 +486,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -451,14 +505,14 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@9" + "$ref": "#/rules@10" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@10" + "$ref": "#/rules@11" }, "arguments": [] } @@ -480,7 +534,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "definition": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@23" + "$ref": "#/rules@24" }, "arguments": [] }, @@ -504,21 +558,21 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@53" + "$ref": "#/rules@54" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@58" + "$ref": "#/rules@59" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@57" + "$ref": "#/rules@58" }, "arguments": [] } @@ -552,7 +606,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -571,7 +625,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -605,7 +659,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@55" + "$ref": "#/rules@56" }, "arguments": [] } @@ -627,7 +681,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@54" + "$ref": "#/rules@55" }, "arguments": [] } @@ -657,7 +711,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] }, @@ -674,7 +728,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@14" + "$ref": "#/rules@15" }, "arguments": [] }, @@ -708,7 +762,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@15" + "$ref": "#/rules@16" }, "arguments": [] } @@ -727,7 +781,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@15" + "$ref": "#/rules@16" }, "arguments": [] } @@ -802,7 +856,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$ref": "#/rules@34" + "$ref": "#/rules@35" }, "deprecatedSyntax": false } @@ -814,7 +868,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@27" + "$ref": "#/rules@28" }, "arguments": [], "cardinality": "?" @@ -854,7 +908,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -881,7 +935,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@24" + "$ref": "#/rules@25" }, "arguments": [] }, @@ -911,7 +965,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$ref": "#/rules@30" + "$ref": "#/rules@31" }, "deprecatedSyntax": false } @@ -943,7 +997,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@18" + "$ref": "#/rules@19" }, "arguments": [] }, @@ -992,7 +1046,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -1026,7 +1080,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@19" + "$ref": "#/rules@20" }, "arguments": [] }, @@ -1058,7 +1112,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@19" + "$ref": "#/rules@20" }, "arguments": [] } @@ -1088,7 +1142,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@20" + "$ref": "#/rules@21" }, "arguments": [] }, @@ -1137,7 +1191,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@20" + "$ref": "#/rules@21" }, "arguments": [] } @@ -1167,7 +1221,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@21" + "$ref": "#/rules@22" }, "arguments": [] }, @@ -1208,7 +1262,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@21" + "$ref": "#/rules@22" }, "arguments": [] } @@ -1238,7 +1292,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@22" + "$ref": "#/rules@23" }, "arguments": [] }, @@ -1279,7 +1333,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@22" + "$ref": "#/rules@23" }, "arguments": [] } @@ -1316,7 +1370,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] }, @@ -1329,56 +1383,56 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@11" + "$ref": "#/rules@12" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@12" + "$ref": "#/rules@13" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@9" + "$ref": "#/rules@10" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@16" + "$ref": "#/rules@17" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@10" + "$ref": "#/rules@11" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@13" + "$ref": "#/rules@14" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@17" + "$ref": "#/rules@18" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@25" + "$ref": "#/rules@26" }, "arguments": [] } @@ -1411,7 +1465,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@26" + "$ref": "#/rules@27" }, "arguments": [] } @@ -1430,7 +1484,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@26" + "$ref": "#/rules@27" }, "arguments": [] } @@ -1472,7 +1526,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -1488,7 +1542,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -1516,7 +1570,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@28" + "$ref": "#/rules@29" }, "arguments": [] } @@ -1535,7 +1589,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@28" + "$ref": "#/rules@29" }, "arguments": [] } @@ -1567,7 +1621,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -1586,7 +1640,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -1613,7 +1667,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [] }, @@ -1630,7 +1684,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -1649,7 +1703,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@30" + "$ref": "#/rules@31" }, "arguments": [] } @@ -1661,7 +1715,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@46" + "$ref": "#/rules@47" }, "arguments": [] } @@ -1695,7 +1749,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [] }, @@ -1708,7 +1762,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -1720,7 +1774,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@31" + "$ref": "#/rules@32" }, "arguments": [] } @@ -1732,7 +1786,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@45" + "$ref": "#/rules@46" }, "arguments": [] }, @@ -1763,7 +1817,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@51" + "$ref": "#/rules@52" }, "arguments": [] } @@ -1780,7 +1834,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] }, @@ -1840,7 +1894,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [] }, @@ -1857,7 +1911,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -1876,7 +1930,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@33" + "$ref": "#/rules@34" }, "arguments": [] } @@ -1888,7 +1942,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@46" + "$ref": "#/rules@47" }, "arguments": [] } @@ -1922,7 +1976,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [] }, @@ -1935,7 +1989,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -1947,7 +2001,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@45" + "$ref": "#/rules@46" }, "arguments": [] }, @@ -1971,7 +2025,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -1987,7 +2041,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -2006,7 +2060,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@35" + "$ref": "#/rules@36" }, "arguments": [] } @@ -2025,7 +2079,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@35" + "$ref": "#/rules@36" }, "arguments": [] } @@ -2051,7 +2105,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@36" + "$ref": "#/rules@37" }, "arguments": [] } @@ -2067,7 +2121,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] }, @@ -2095,7 +2149,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -2107,7 +2161,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -2123,7 +2177,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@36" + "$ref": "#/rules@37" }, "arguments": [] } @@ -2163,7 +2217,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@50" + "$ref": "#/rules@51" }, "arguments": [] } @@ -2220,7 +2274,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] }, @@ -2237,14 +2291,14 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@51" + "$ref": "#/rules@52" }, "arguments": [] } @@ -2276,7 +2330,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@37" + "$ref": "#/rules@38" }, "arguments": [] } @@ -2303,7 +2357,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@37" + "$ref": "#/rules@38" }, "arguments": [] } @@ -2330,7 +2384,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@37" + "$ref": "#/rules@38" }, "arguments": [] } @@ -2353,21 +2407,21 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@39" + "$ref": "#/rules@40" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@40" + "$ref": "#/rules@41" }, "arguments": [] }, { "$type": "RuleCall", "rule": { - "$ref": "#/rules@38" + "$ref": "#/rules@39" }, "arguments": [] } @@ -2389,7 +2443,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -2405,7 +2459,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@41" + "$ref": "#/rules@42" }, "arguments": [] } @@ -2424,7 +2478,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@43" + "$ref": "#/rules@44" }, "arguments": [] } @@ -2443,7 +2497,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@43" + "$ref": "#/rules@44" }, "arguments": [] } @@ -2465,7 +2519,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@47" + "$ref": "#/rules@48" }, "arguments": [] }, @@ -2489,7 +2543,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -2511,7 +2565,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -2527,7 +2581,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@44" + "$ref": "#/rules@45" }, "arguments": [] } @@ -2560,7 +2614,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@50" + "$ref": "#/rules@51" }, "arguments": [] }, @@ -2591,7 +2645,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] }, @@ -2651,12 +2705,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$ref": "#/rules@42" + "$ref": "#/rules@43" }, "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@40" + "$ref": "#/rules@41" }, "arguments": [] }, @@ -2673,7 +2727,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@48" + "$ref": "#/rules@49" }, "arguments": [], "cardinality": "?" @@ -2703,7 +2757,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@59" + "$ref": "#/rules@60" }, "arguments": [], "cardinality": "*" @@ -2715,12 +2769,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$ref": "#/rules@42" + "$ref": "#/rules@43" }, "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@39" + "$ref": "#/rules@40" }, "arguments": [] }, @@ -2737,7 +2791,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@48" + "$ref": "#/rules@49" }, "arguments": [], "cardinality": "?" @@ -2771,12 +2825,12 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "CrossReference", "type": { - "$ref": "#/rules@42" + "$ref": "#/rules@43" }, "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@38" + "$ref": "#/rules@39" }, "arguments": [] }, @@ -2793,7 +2847,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "RuleCall", "rule": { - "$ref": "#/rules@48" + "$ref": "#/rules@49" }, "arguments": [], "cardinality": "?" @@ -2828,7 +2882,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@49" + "$ref": "#/rules@50" }, "arguments": [] } @@ -2847,7 +2901,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@49" + "$ref": "#/rules@50" }, "arguments": [] } @@ -2879,7 +2933,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@56" + "$ref": "#/rules@57" }, "arguments": [] } @@ -2898,7 +2952,7 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "terminal": { "$type": "RuleCall", "rule": { - "$ref": "#/rules@8" + "$ref": "#/rules@9" }, "arguments": [] } @@ -3137,19 +3191,19 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "SimpleType", "typeRef": { - "$ref": "#/rules@35" + "$ref": "#/rules@36" } }, { "$type": "SimpleType", "typeRef": { - "$ref": "#/rules@30" + "$ref": "#/rules@31" } }, { "$type": "SimpleType", "typeRef": { - "$ref": "#/rules@33" + "$ref": "#/rules@34" } } ] @@ -3164,13 +3218,13 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel { "$type": "SimpleType", "typeRef": { - "$ref": "#/rules@29" + "$ref": "#/rules@30" } }, { "$type": "SimpleType", "typeRef": { - "$ref": "#/rules@32" + "$ref": "#/rules@33" } } ] diff --git a/packages/language/src/zmodel.langium b/packages/language/src/zmodel.langium index 40566e697..f47b47019 100644 --- a/packages/language/src/zmodel.langium +++ b/packages/language/src/zmodel.langium @@ -1,10 +1,15 @@ grammar ZModel + entry Model: + imports+=ModelImport* ( declarations+=AbstractDeclaration )*; +ModelImport: + 'import' path=STRING ';'?; + AbstractDeclaration: DataSource | GeneratorDecl| Plugin | DataModel | Enum | FunctionDecl | Attribute; diff --git a/packages/language/syntaxes/zmodel.tmLanguage.json b/packages/language/syntaxes/zmodel.tmLanguage.json index a98fc252e..7c1856969 100644 --- a/packages/language/syntaxes/zmodel.tmLanguage.json +++ b/packages/language/syntaxes/zmodel.tmLanguage.json @@ -10,7 +10,7 @@ }, { "name": "keyword.control.zmodel", - "match": "\\b(Any|Asc|BigInt|Boolean|Bytes|ContextType|DateTime|Decimal|Desc|FieldReference|Float|Int|Json|Null|Object|String|TransitiveFieldReference|attribute|datasource|enum|function|generator|in|model|plugin|sort)\\b" + "match": "\\b(Any|Asc|BigInt|Boolean|Bytes|ContextType|DateTime|Decimal|Desc|FieldReference|Float|Int|Json|Null|Object|String|TransitiveFieldReference|attribute|datasource|enum|function|generator|import|in|model|plugin|sort)\\b" }, { "name": "string.quoted.double.zmodel", diff --git a/packages/schema/src/language-server/zmodel-module.ts b/packages/schema/src/language-server/zmodel-module.ts index 077675ed5..2f937acbc 100644 --- a/packages/schema/src/language-server/zmodel-module.ts +++ b/packages/schema/src/language-server/zmodel-module.ts @@ -23,7 +23,7 @@ import { ZModelValidationRegistry, ZModelValidator } from './validator/zmodel-va import { ZModelCodeActionProvider } from './zmodel-code-action'; import { ZModelFormatter } from './zmodel-formatter'; import { ZModelLinker } from './zmodel-linker'; -import { ZModelScopeComputation } from './zmodel-scope'; +import { ZModelScopeComputation, ZModelScopeProvider } from './zmodel-scope'; import ZModelWorkspaceManager from './zmodel-workspace-manager'; /** @@ -50,6 +50,7 @@ export const ZModelModule: Module new ZModelScopeComputation(services), Linker: (services) => new ZModelLinker(services), + ScopeProvider: (services) => new ZModelScopeProvider(services), }, validation: { ValidationRegistry: (services) => new ZModelValidationRegistry(services), diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index 51c7ae184..9d0a3cabb 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -1,14 +1,24 @@ -import { isEnumField } from '@zenstackhq/language/ast'; +import { isEnumField, isModel } from '@zenstackhq/language/ast'; import { AstNode, AstNodeDescription, DefaultScopeComputation, + DefaultScopeProvider, + EMPTY_SCOPE, + equalURI, + getContainerOfType, interruptAndCheck, LangiumDocument, LangiumServices, + ReferenceInfo, + Scope, + stream, streamAllContents, + StreamScope, } from 'langium'; import { CancellationToken } from 'vscode-jsonrpc'; +import { resolveImportUri } from '../utils/ast-utils'; +import { STD_LIB_MODULE_NAME } from './constants'; /** * Custom Langium ScopeComputation implementation which adds enum fields into global scope @@ -42,3 +52,25 @@ export class ZModelScopeComputation extends DefaultScopeComputation { return result; } } + +export class ZModelScopeProvider extends DefaultScopeProvider { + constructor(services: LangiumServices) { + super(services); + } + + protected override getGlobalScope(referenceType: string, context: ReferenceInfo): Scope { + const model = getContainerOfType(context.container, isModel); + if (!model) { + return EMPTY_SCOPE; + } + const importedUris = stream(model.imports).map(resolveImportUri).nonNullable(); + const importedElements = this.indexManager + .allElements(referenceType) + .filter( + (des) => + des.documentUri.path.endsWith(STD_LIB_MODULE_NAME) || + importedUris.some((importedUri) => equalURI(des.documentUri, importedUri)) + ); + return new StreamScope(importedElements); + } +} diff --git a/packages/schema/src/utils/ast-utils.ts b/packages/schema/src/utils/ast-utils.ts index dd295ae4c..fc85a3201 100644 --- a/packages/schema/src/utils/ast-utils.ts +++ b/packages/schema/src/utils/ast-utils.ts @@ -11,10 +11,13 @@ import { isMemberAccessExpr, isReferenceExpr, Model, + ModelImport, ReferenceExpr, } from '@zenstackhq/language/ast'; import type { PolicyOperationKind } from '@zenstackhq/runtime'; import { getLiteral } from '@zenstackhq/sdk'; +import { getDocument } from 'langium'; +import { URI, Utils } from 'vscode-uri'; import { isFromStdlib } from '../language-server/utils'; export function extractDataModelsWithAllowRules(model: Model): DataModel[] { @@ -141,3 +144,15 @@ export function getDataModelFieldReference(expr: Expression): DataModelField | u return undefined; } } + +export function resolveImportUri(imp: ModelImport): URI | undefined { + if (imp.path === undefined || imp.path.length === 0) { + return undefined; + } + const dirUri = Utils.dirname(getDocument(imp).uri); + let grammarPath = imp.path; + if (!grammarPath.endsWith('.zmodel')) { + grammarPath += '.zmodel'; + } + return Utils.resolvePath(dirUri, grammarPath); +} From f00c6091567d435c252b208b6f65db91d599ccaf Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 12:06:05 +0100 Subject: [PATCH 02/11] feat: CLI eager loading all the imported modules --- packages/schema/src/cli/cli-util.ts | 52 ++++++++++++++++--- .../validator/schema-validator.ts | 21 ++++++-- .../validator/zmodel-validator.ts | 4 +- .../src/language-server/zmodel-linker.ts | 12 +++-- .../src/language-server/zmodel-module.ts | 2 +- packages/schema/src/utils/ast-utils.ts | 51 +++++++++++++++++- .../schema/mutil-files/multi-files.test.ts | 12 +++++ .../tests/schema/mutil-files/schema.zmodel | 19 +++++++ .../tests/schema/mutil-files/user.zmodel | 10 ++++ 9 files changed, 167 insertions(+), 16 deletions(-) create mode 100644 packages/schema/tests/schema/mutil-files/multi-files.test.ts create mode 100644 packages/schema/tests/schema/mutil-files/schema.zmodel create mode 100644 packages/schema/tests/schema/mutil-files/user.zmodel diff --git a/packages/schema/src/cli/cli-util.ts b/packages/schema/src/cli/cli-util.ts index fe8603357..0f77b0a3f 100644 --- a/packages/schema/src/cli/cli-util.ts +++ b/packages/schema/src/cli/cli-util.ts @@ -3,7 +3,7 @@ import { getLiteral, PluginError } from '@zenstackhq/sdk'; import colors from 'colors'; import fs from 'fs'; import getLatestVersion from 'get-latest-version'; -import { LangiumDocument } from 'langium'; +import { getDocument, LangiumDocument, LangiumDocuments } from 'langium'; import { NodeFileSystem } from 'langium/node'; import ora from 'ora'; import path from 'path'; @@ -12,6 +12,7 @@ import { URI } from 'vscode-uri'; import { PLUGIN_MODULE_NAME, STD_LIB_MODULE_NAME } from '../language-server/constants'; import { createZModelServices, ZModelServices } from '../language-server/zmodel-module'; import { Context } from '../types'; +import { resolveImport, resolveTransitiveImports } from '../utils/ast-utils'; import { ensurePackage, installPackage, PackageManagers } from '../utils/pkg-utils'; import { getVersion } from '../utils/version-utils'; import { CliError } from './cli-error'; @@ -107,13 +108,22 @@ export async function loadDocument(fileName: string): Promise { // load documents provided by plugins const pluginDocuments = await getPluginDocuments(services, fileName); + const langiumDocuments = services.shared.workspace.LangiumDocuments; // load the document - const document = services.shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(path.resolve(fileName))); + const document = langiumDocuments.getOrCreateDocument(URI.file(path.resolve(fileName))); + + // load all imports + const importedURIs = eagerLoadAllImports(document, langiumDocuments); + + const importedDocuments = importedURIs.map((uri) => langiumDocuments.getOrCreateDocument(uri)); // build the document together with standard library and plugin modules - await services.shared.workspace.DocumentBuilder.build([stdLib, ...pluginDocuments, document], { - validationChecks: 'all', - }); + await services.shared.workspace.DocumentBuilder.build( + [stdLib, ...pluginDocuments, document, ...importedDocuments], + { + validationChecks: 'all', + } + ); const validationErrors = (document.diagnostics ?? []).filter((e) => e.severity === 1); if (validationErrors.length > 0) { @@ -130,7 +140,37 @@ export async function loadDocument(fileName: string): Promise { throw new CliError('schema validation errors'); } - return document.parseResult.value as Model; + const model = document.parseResult.value as Model; + + mergeImportsDeclarations(langiumDocuments, model); + return model; +} + +export function eagerLoadAllImports( + document: LangiumDocument, + documents: LangiumDocuments, + uris: Set = new Set() +) { + const uriString = document.uri.toString(); + if (!uris.has(uriString)) { + uris.add(uriString); + const model = document.parseResult.value as Model; + + for (const imp of model.imports) { + const importedModel = resolveImport(documents, imp); + if (importedModel) { + const importedDoc = getDocument(importedModel); + eagerLoadAllImports(importedDoc, documents, uris); + } + } + } + + return Array.from(uris).map((e) => URI.parse(e)); +} + +export function mergeImportsDeclarations(documents: LangiumDocuments, model: Model) { + const importedModels = resolveTransitiveImports(documents, model); + model.declarations.push(...importedModels.flatMap((m) => m.declarations)); } export async function getPluginDocuments(services: ZModelServices, fileName: string): Promise { diff --git a/packages/schema/src/language-server/validator/schema-validator.ts b/packages/schema/src/language-server/validator/schema-validator.ts index 3d3dc5836..922cc1ad9 100644 --- a/packages/schema/src/language-server/validator/schema-validator.ts +++ b/packages/schema/src/language-server/validator/schema-validator.ts @@ -1,16 +1,31 @@ import { PLUGIN_MODULE_NAME, STD_LIB_MODULE_NAME } from '../constants'; import { isDataSource, Model } from '@zenstackhq/language/ast'; import { AstValidator } from '../types'; -import { ValidationAcceptor } from 'langium'; +import { LangiumDocuments, ValidationAcceptor } from 'langium'; import { validateDuplicatedDeclarations } from './utils'; +import { resolveTransitiveImports } from '../../utils/ast-utils'; /** * Validates toplevel schema. */ export default class SchemaValidator implements AstValidator { + constructor(protected readonly documents: LangiumDocuments) {} validate(model: Model, accept: ValidationAcceptor): void { validateDuplicatedDeclarations(model.declarations, accept); + const importedModels = resolveTransitiveImports(this.documents, model); + + const importedNames = new Set(importedModels.flatMap((m) => m.declarations.map((d) => d.name))); + + for (const declaration of model.declarations) { + if (importedNames.has(declaration.name)) { + accept('error', `A ${declaration.name} already exists in an imported models`, { + node: declaration, + property: 'name', + }); + } + } + if ( !model.$document?.uri.path.endsWith(STD_LIB_MODULE_NAME) && !model.$document?.uri.path.endsWith(PLUGIN_MODULE_NAME) @@ -21,9 +36,7 @@ export default class SchemaValidator implements AstValidator { private validateDataSources(model: Model, accept: ValidationAcceptor) { const dataSources = model.declarations.filter((d) => isDataSource(d)); - if (dataSources.length === 0) { - accept('error', 'Model must define a datasource', { node: model }); - } else if (dataSources.length > 1) { + if (dataSources.length > 1) { accept('error', 'Multiple datasource declarations are not allowed', { node: dataSources[1] }); } } diff --git a/packages/schema/src/language-server/validator/zmodel-validator.ts b/packages/schema/src/language-server/validator/zmodel-validator.ts index 4ea4c59dd..3f4f740b7 100644 --- a/packages/schema/src/language-server/validator/zmodel-validator.ts +++ b/packages/schema/src/language-server/validator/zmodel-validator.ts @@ -42,6 +42,7 @@ export class ZModelValidationRegistry extends ValidationRegistry { * Implementation of custom validations. */ export class ZModelValidator { + constructor(protected readonly services: ZModelServices) {} private shouldCheck(node: AstNode) { let doc: LangiumDocument | undefined; let currNode: AstNode | undefined = node; @@ -57,7 +58,8 @@ export class ZModelValidator { } checkModel(node: Model, accept: ValidationAcceptor): void { - this.shouldCheck(node) && new SchemaValidator().validate(node, accept); + this.shouldCheck(node) && + new SchemaValidator(this.services.shared.workspace.LangiumDocuments).validate(node, accept); } checkDataSource(node: DataSource, accept: ValidationAcceptor): void { diff --git a/packages/schema/src/language-server/zmodel-linker.ts b/packages/schema/src/language-server/zmodel-linker.ts index 16748b119..0f7f22c49 100644 --- a/packages/schema/src/language-server/zmodel-linker.ts +++ b/packages/schema/src/language-server/zmodel-linker.ts @@ -42,6 +42,7 @@ import { streamContents, } from 'langium'; import { CancellationToken } from 'vscode-jsonrpc'; +import { getAllDeclarationsFromImports } from '../utils/ast-utils'; import { getContainingModel, isFromStdlib } from './utils'; import { mapBuiltinTypeToExpressionType } from './validator/utils'; @@ -244,9 +245,14 @@ export class ZModelLinker extends DefaultLinker { if (funcDecl.name === 'auth' && isFromStdlib(funcDecl)) { // auth() function is resolved to User model in the current document const model = getContainingModel(node); - const userModel = model?.declarations.find((d) => isDataModel(d) && d.name === 'User'); - if (userModel) { - node.$resolvedType = { decl: userModel, nullable: true }; + + if (model) { + const userModel = getAllDeclarationsFromImports(this.langiumDocuments(), model).find( + (d) => isDataModel(d) && d.name === 'User' + ); + if (userModel) { + node.$resolvedType = { decl: userModel, nullable: true }; + } } } else if (funcDecl.name === 'future' && isFromStdlib(funcDecl)) { // future() function is resolved to current model diff --git a/packages/schema/src/language-server/zmodel-module.ts b/packages/schema/src/language-server/zmodel-module.ts index 2f937acbc..e25f7015f 100644 --- a/packages/schema/src/language-server/zmodel-module.ts +++ b/packages/schema/src/language-server/zmodel-module.ts @@ -54,7 +54,7 @@ export const ZModelModule: Module new ZModelValidationRegistry(services), - ZModelValidator: () => new ZModelValidator(), + ZModelValidator: (services) => new ZModelValidator(services), }, lsp: { Formatter: () => new ZModelFormatter(), diff --git a/packages/schema/src/utils/ast-utils.ts b/packages/schema/src/utils/ast-utils.ts index fc85a3201..a97caf8f5 100644 --- a/packages/schema/src/utils/ast-utils.ts +++ b/packages/schema/src/utils/ast-utils.ts @@ -9,6 +9,7 @@ import { isEnumField, isInvocationExpr, isMemberAccessExpr, + isModel, isReferenceExpr, Model, ModelImport, @@ -16,7 +17,7 @@ import { } from '@zenstackhq/language/ast'; import type { PolicyOperationKind } from '@zenstackhq/runtime'; import { getLiteral } from '@zenstackhq/sdk'; -import { getDocument } from 'langium'; +import { getDocument, LangiumDocuments } from 'langium'; import { URI, Utils } from 'vscode-uri'; import { isFromStdlib } from '../language-server/utils'; @@ -156,3 +157,51 @@ export function resolveImportUri(imp: ModelImport): URI | undefined { } return Utils.resolvePath(dirUri, grammarPath); } + +export function resolveTransitiveImports(documents: LangiumDocuments, model: Model): Model[] { + return resolveTransitiveImportsInternal(documents, model); +} + +function resolveTransitiveImportsInternal( + documents: LangiumDocuments, + model: Model, + initialModel = model, + visited: Set = new Set(), + models: Set = new Set() +): Model[] { + const doc = getDocument(model); + if (initialModel !== model) { + models.add(model); + } + if (!visited.has(doc.uri)) { + visited.add(doc.uri); + for (const imp of model.imports) { + const importedGrammar = resolveImport(documents, imp); + if (importedGrammar) { + resolveTransitiveImportsInternal(documents, importedGrammar, initialModel, visited, models); + } + } + } + return Array.from(models); +} + +export function resolveImport(documents: LangiumDocuments, imp: ModelImport): Model | undefined { + const resolvedUri = resolveImportUri(imp); + try { + if (resolvedUri) { + const resolvedDocument = documents.getOrCreateDocument(resolvedUri); + const node = resolvedDocument.parseResult.value; + if (isModel(node)) { + return node; + } + } + } catch { + // NOOP + } + return undefined; +} + +export function getAllDeclarationsFromImports(documents: LangiumDocuments, model: Model) { + const imports = resolveTransitiveImports(documents, model); + return model.declarations.concat(...imports.map((imp) => imp.declarations)); +} diff --git a/packages/schema/tests/schema/mutil-files/multi-files.test.ts b/packages/schema/tests/schema/mutil-files/multi-files.test.ts new file mode 100644 index 000000000..b9c41ff19 --- /dev/null +++ b/packages/schema/tests/schema/mutil-files/multi-files.test.ts @@ -0,0 +1,12 @@ +import path from 'path'; +import { loadDocument } from '../../../src/cli/cli-util'; + +describe('Mutiple files Tests', () => { + it('model loading post', async () => { + await loadDocument(path.join(__dirname, './schema.zmodel')); + }); + + it('model loading user', async () => { + await loadDocument(path.join(__dirname, './user.zmodel')); + }); +}); diff --git a/packages/schema/tests/schema/mutil-files/schema.zmodel b/packages/schema/tests/schema/mutil-files/schema.zmodel new file mode 100644 index 000000000..1aa1a71db --- /dev/null +++ b/packages/schema/tests/schema/mutil-files/schema.zmodel @@ -0,0 +1,19 @@ +import "user" + +datasource db { + provider="sqlite" + url="file:./dev.db" +} + +generator client { + provider = "prisma-client-js" +} + + +model Post { + id Int @id() @default(autoincrement()) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? + // author has full access + @@allow('all', auth() == author) +} \ No newline at end of file diff --git a/packages/schema/tests/schema/mutil-files/user.zmodel b/packages/schema/tests/schema/mutil-files/user.zmodel new file mode 100644 index 000000000..79fbbe969 --- /dev/null +++ b/packages/schema/tests/schema/mutil-files/user.zmodel @@ -0,0 +1,10 @@ +import "schema" +model User { + id Int @id() @default(autoincrement()) + email String @unique() + name String? + posts Post[] + + // make user profile public + @@allow('read', true) +} \ No newline at end of file From 65611c63d29953333d8ab01ed00d99ab7904da2e Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 13:09:31 +0100 Subject: [PATCH 03/11] test: add generator test --- .../validator/schema-validator.ts | 4 ++-- .../tests/generator/prisma-generator.test.ts | 19 +++++++++++++++++++ .../tests/generator/zmodel/schema.zmodel | 19 +++++++++++++++++++ .../tests/generator/zmodel/user/user.zmodel | 17 +++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 packages/schema/tests/generator/zmodel/schema.zmodel create mode 100644 packages/schema/tests/generator/zmodel/user/user.zmodel diff --git a/packages/schema/src/language-server/validator/schema-validator.ts b/packages/schema/src/language-server/validator/schema-validator.ts index 922cc1ad9..99e6698ce 100644 --- a/packages/schema/src/language-server/validator/schema-validator.ts +++ b/packages/schema/src/language-server/validator/schema-validator.ts @@ -3,7 +3,7 @@ import { isDataSource, Model } from '@zenstackhq/language/ast'; import { AstValidator } from '../types'; import { LangiumDocuments, ValidationAcceptor } from 'langium'; import { validateDuplicatedDeclarations } from './utils'; -import { resolveTransitiveImports } from '../../utils/ast-utils'; +import { getAllDeclarationsFromImports, resolveTransitiveImports } from '../../utils/ast-utils'; /** * Validates toplevel schema. @@ -35,7 +35,7 @@ export default class SchemaValidator implements AstValidator { } private validateDataSources(model: Model, accept: ValidationAcceptor) { - const dataSources = model.declarations.filter((d) => isDataSource(d)); + const dataSources = getAllDeclarationsFromImports(this.documents, model).filter((d) => isDataSource(d)); if (dataSources.length > 1) { accept('error', 'Multiple datasource declarations are not allowed', { node: dataSources[1] }); } diff --git a/packages/schema/tests/generator/prisma-generator.test.ts b/packages/schema/tests/generator/prisma-generator.test.ts index 263c335e6..569c678ee 100644 --- a/packages/schema/tests/generator/prisma-generator.test.ts +++ b/packages/schema/tests/generator/prisma-generator.test.ts @@ -3,6 +3,8 @@ import { getDMMF } from '@prisma/internals'; import fs from 'fs'; import tmp from 'tmp'; +import path from 'path'; +import { loadDocument } from '../../src/cli/cli-util'; import PrismaSchemaGenerator from '../../src/plugins/prisma/schema-generator'; import { loadModel } from '../utils'; @@ -191,4 +193,21 @@ describe('Prisma generator test', () => { expect(content).toContain('@@schema("base")'); expect(content).toContain('schemas = ["base","transactional"]'); }); + + it('multi files', async () => { + const model = await loadDocument(path.join(__dirname, './zmodel/schema.zmodel')); + + const { name } = tmp.fileSync({ postfix: '.prisma' }); + await new PrismaSchemaGenerator().generate(model, { + provider: '@core/prisma', + schemaPath: 'schema.zmodel', + output: name, + }); + + const content = fs.readFileSync(name, 'utf-8'); + const dmmf = await getDMMF({ datamodel: content }); + + expect(dmmf.datamodel.models.length).toBe(2); + expect(dmmf.datamodel.enums[0].name).toBe('UserRole'); + }); }); diff --git a/packages/schema/tests/generator/zmodel/schema.zmodel b/packages/schema/tests/generator/zmodel/schema.zmodel new file mode 100644 index 000000000..e2bc3a433 --- /dev/null +++ b/packages/schema/tests/generator/zmodel/schema.zmodel @@ -0,0 +1,19 @@ +import "user/user" + +datasource db { + provider = 'postgresql' + url = env('URL') +} + +generator client { + provider = "prisma-client-js" +} + + +model Post { + id Int @id() @default(autoincrement()) + author User? @relation(fields: [authorId], references: [id]) + authorId Int? + // author has full access + @@allow('all', auth() == author) +} \ No newline at end of file diff --git a/packages/schema/tests/generator/zmodel/user/user.zmodel b/packages/schema/tests/generator/zmodel/user/user.zmodel new file mode 100644 index 000000000..c83eb6fbe --- /dev/null +++ b/packages/schema/tests/generator/zmodel/user/user.zmodel @@ -0,0 +1,17 @@ +import "../schema" +model User { + id Int @id() @default(autoincrement()) + email String @unique() + name String? + posts Post[] + role UserRole + + // make user profile public + @@allow('read', true) +} + + +enum UserRole { + USER + ADMIN +} \ No newline at end of file From 803e8ab7f69a11202878cc71cba7b13e4be9a741 Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 15:53:06 +0100 Subject: [PATCH 04/11] feat: fix relation action --- packages/schema/src/language-server/zmodel-code-action.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/schema/src/language-server/zmodel-code-action.ts b/packages/schema/src/language-server/zmodel-code-action.ts index d8d5d69bb..5638a7c2c 100644 --- a/packages/schema/src/language-server/zmodel-code-action.ts +++ b/packages/schema/src/language-server/zmodel-code-action.ts @@ -4,6 +4,7 @@ import { CodeActionProvider, findDeclarationNodeAtOffset, getContainerOfType, + getDocument, IndexManager, LangiumDocument, LangiumServices, @@ -112,6 +113,9 @@ export class ZModelCodeActionProvider implements CodeActionProvider { newText = '\n' + indent + `${fieldName} ${typeName}[]`; } + // the opposite model might be in the imported file + const targetDocument = getDocument(oppositeModel); + return { title: `Add opposite relation fields on ${oppositeModel.name}`, kind: CodeActionKind.QuickFix, @@ -119,7 +123,7 @@ export class ZModelCodeActionProvider implements CodeActionProvider { isPreferred: false, edit: { changes: { - [document.textDocument.uri]: [ + [targetDocument.textDocument.uri]: [ { range: { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion From a7d3bd1046a5b1f6eaea6d63202f2b26f165eec8 Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 16:18:51 +0100 Subject: [PATCH 05/11] fix: fix merge accident in ast-utils.ts --- packages/schema/src/utils/ast-utils.ts | 90 -------------------------- 1 file changed, 90 deletions(-) diff --git a/packages/schema/src/utils/ast-utils.ts b/packages/schema/src/utils/ast-utils.ts index a97caf8f5..a23ac63e6 100644 --- a/packages/schema/src/utils/ast-utils.ts +++ b/packages/schema/src/utils/ast-utils.ts @@ -1,10 +1,8 @@ import { DataModel, - DataModelAttribute, DataModelField, Expression, isArrayExpr, - isDataModel, isDataModelField, isEnumField, isInvocationExpr, @@ -15,98 +13,10 @@ import { ModelImport, ReferenceExpr, } from '@zenstackhq/language/ast'; -import type { PolicyOperationKind } from '@zenstackhq/runtime'; -import { getLiteral } from '@zenstackhq/sdk'; import { getDocument, LangiumDocuments } from 'langium'; import { URI, Utils } from 'vscode-uri'; import { isFromStdlib } from '../language-server/utils'; -export function extractDataModelsWithAllowRules(model: Model): DataModel[] { - return model.declarations.filter( - (d) => isDataModel(d) && d.attributes.some((attr) => attr.decl.ref?.name === '@@allow') - ) as DataModel[]; -} - -export function analyzePolicies(dataModel: DataModel) { - const allows = dataModel.attributes.filter((attr) => attr.decl.ref?.name === '@@allow'); - const denies = dataModel.attributes.filter((attr) => attr.decl.ref?.name === '@@deny'); - - const create = toStaticPolicy('create', allows, denies); - const read = toStaticPolicy('read', allows, denies); - const update = toStaticPolicy('update', allows, denies); - const del = toStaticPolicy('delete', allows, denies); - const hasFieldValidation = dataModel.fields.some((field) => - field.attributes.some((attr) => VALIDATION_ATTRIBUTES.includes(attr.decl.$refText)) - ); - - return { - allows, - denies, - create, - read, - update, - delete: del, - allowAll: create === true && read === true && update === true && del === true, - denyAll: create === false && read === false && update === false && del === false, - hasFieldValidation, - }; -} - -function toStaticPolicy( - operation: PolicyOperationKind, - allows: DataModelAttribute[], - denies: DataModelAttribute[] -): boolean | undefined { - const filteredDenies = forOperation(operation, denies); - if (filteredDenies.some((rule) => getLiteral(rule.args[1].value) === true)) { - // any constant true deny rule - return false; - } - - const filteredAllows = forOperation(operation, allows); - if (filteredAllows.length === 0) { - // no allow rule - return false; - } - - if ( - filteredDenies.length === 0 && - filteredAllows.some((rule) => getLiteral(rule.args[1].value) === true) - ) { - // any constant true allow rule - return true; - } - return undefined; -} - -function forOperation(operation: PolicyOperationKind, rules: DataModelAttribute[]) { - return rules.filter((rule) => { - const ops = getLiteral(rule.args[0].value); - if (!ops) { - return false; - } - if (ops === 'all') { - return true; - } - const splitOps = ops.split(',').map((p) => p.trim()); - return splitOps.includes(operation); - }); -} - -export const VALIDATION_ATTRIBUTES = [ - '@length', - '@regex', - '@startsWith', - '@endsWith', - '@email', - '@url', - '@datetime', - '@gt', - '@gte', - '@lt', - '@lte', -]; - export function getIdFields(dataModel: DataModel) { const fieldLevelId = dataModel.fields.find((f) => f.attributes.some((attr) => attr.decl.$refText === '@id')); if (fieldLevelId) { From 252ad723f0cdc3e50fe3f3d86e4fdce4c2170d3e Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 17:09:53 +0100 Subject: [PATCH 06/11] fix: global scope should include current model --- .../schema/src/language-server/zmodel-scope.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index 9d0a3cabb..0e70fb386 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -64,13 +64,15 @@ export class ZModelScopeProvider extends DefaultScopeProvider { return EMPTY_SCOPE; } const importedUris = stream(model.imports).map(resolveImportUri).nonNullable(); - const importedElements = this.indexManager - .allElements(referenceType) - .filter( - (des) => - des.documentUri.path.endsWith(STD_LIB_MODULE_NAME) || - importedUris.some((importedUri) => equalURI(des.documentUri, importedUri)) - ); + const importedElements = this.indexManager.allElements(referenceType).filter( + (des) => + // allow current document + equalURI(des.documentUri, model.$document?.uri) || + // allow stdlib + des.documentUri.path.endsWith(STD_LIB_MODULE_NAME) || + // allow imported documents + importedUris.some((importedUri) => (des.documentUri, importedUri)) + ); return new StreamScope(importedElements); } } From d51096fcdf387ee2fed36b298c9f4d9aa8533254 Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 17:31:17 +0100 Subject: [PATCH 07/11] fix: remove unused ut case --- .../schema/tests/schema/validation/schema-validation.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/schema/tests/schema/validation/schema-validation.test.ts b/packages/schema/tests/schema/validation/schema-validation.test.ts index 52d8bc731..1f1879dc2 100644 --- a/packages/schema/tests/schema/validation/schema-validation.test.ts +++ b/packages/schema/tests/schema/validation/schema-validation.test.ts @@ -1,9 +1,6 @@ import { loadModelWithError } from '../../utils'; -describe('Toplevel Schema Validation Tests', () => { - it('no datasource', async () => { - expect(await loadModelWithError('')).toContain('Model must define a datasource'); - }); +describe('Toplevel Schema Validation Tests', () => it('too many datasources', async () => { expect( From d9066158c772fc0080c28fa6dcc71b448f596236 Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 17:34:18 +0100 Subject: [PATCH 08/11] fix build error --- .../schema/tests/schema/validation/schema-validation.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/schema/tests/schema/validation/schema-validation.test.ts b/packages/schema/tests/schema/validation/schema-validation.test.ts index 1f1879dc2..0c063b30d 100644 --- a/packages/schema/tests/schema/validation/schema-validation.test.ts +++ b/packages/schema/tests/schema/validation/schema-validation.test.ts @@ -1,7 +1,6 @@ import { loadModelWithError } from '../../utils'; -describe('Toplevel Schema Validation Tests', () => - +describe('Toplevel Schema Validation Tests', () => { it('too many datasources', async () => { expect( await loadModelWithError(` From d77f4cfda07f293f443eb646244fcd8604860a48 Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 18:13:18 +0100 Subject: [PATCH 09/11] test: fix UT --- packages/schema/tests/generator/zmodel/schema.zmodel | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/schema/tests/generator/zmodel/schema.zmodel b/packages/schema/tests/generator/zmodel/schema.zmodel index e2bc3a433..be912e3fb 100644 --- a/packages/schema/tests/generator/zmodel/schema.zmodel +++ b/packages/schema/tests/generator/zmodel/schema.zmodel @@ -5,11 +5,6 @@ datasource db { url = env('URL') } -generator client { - provider = "prisma-client-js" -} - - model Post { id Int @id() @default(autoincrement()) author User? @relation(fields: [authorId], references: [id]) From 5e7b5fd18bafd7deef2d6d2aa2364d43d475f5d8 Mon Sep 17 00:00:00 2001 From: JG Date: Wed, 26 Apr 2023 18:47:08 +0100 Subject: [PATCH 10/11] fix: add plugin module in global scope --- packages/schema/src/language-server/zmodel-scope.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index 0e70fb386..dd8e50528 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -1,4 +1,4 @@ -import { isEnumField, isModel } from '@zenstackhq/language/ast'; +import { isEnumField, isModel, isPlugin } from '@zenstackhq/language/ast'; import { AstNode, AstNodeDescription, @@ -18,7 +18,7 @@ import { } from 'langium'; import { CancellationToken } from 'vscode-jsonrpc'; import { resolveImportUri } from '../utils/ast-utils'; -import { STD_LIB_MODULE_NAME } from './constants'; +import { PLUGIN_MODULE_NAME, STD_LIB_MODULE_NAME } from './constants'; /** * Custom Langium ScopeComputation implementation which adds enum fields into global scope @@ -63,6 +63,9 @@ export class ZModelScopeProvider extends DefaultScopeProvider { if (!model) { return EMPTY_SCOPE; } + + model.declarations.filter((x) => isPlugin(x)); + const importedUris = stream(model.imports).map(resolveImportUri).nonNullable(); const importedElements = this.indexManager.allElements(referenceType).filter( (des) => @@ -70,6 +73,8 @@ export class ZModelScopeProvider extends DefaultScopeProvider { equalURI(des.documentUri, model.$document?.uri) || // allow stdlib des.documentUri.path.endsWith(STD_LIB_MODULE_NAME) || + // allow plugin models + des.documentUri.path.endsWith(PLUGIN_MODULE_NAME) || // allow imported documents importedUris.some((importedUri) => (des.documentUri, importedUri)) ); From 3aebfd97edbf8d225d27ade939f797161a72a72b Mon Sep 17 00:00:00 2001 From: JG Date: Thu, 27 Apr 2023 20:36:45 +0100 Subject: [PATCH 11/11] fix: resolve comments --- packages/schema/src/cli/cli-util.ts | 16 +++++++++++++++- .../validator/schema-validator.ts | 2 +- .../schema/src/language-server/zmodel-scope.ts | 4 +--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/schema/src/cli/cli-util.ts b/packages/schema/src/cli/cli-util.ts index eb7929797..ccc658e43 100644 --- a/packages/schema/src/cli/cli-util.ts +++ b/packages/schema/src/cli/cli-util.ts @@ -1,4 +1,4 @@ -import { isPlugin, Model } from '@zenstackhq/language/ast'; +import { isDataSource, isPlugin, Model } from '@zenstackhq/language/ast'; import { getLiteral, PluginError } from '@zenstackhq/sdk'; import colors from 'colors'; import fs from 'fs'; @@ -143,9 +143,23 @@ export async function loadDocument(fileName: string): Promise { const model = document.parseResult.value as Model; mergeImportsDeclarations(langiumDocuments, model); + + validationAfterMerge(model); return model; } +// check global unique thing after merge imports +function validationAfterMerge(model: Model) { + const dataSources = model.declarations.filter((d) => isDataSource(d)); + if (dataSources.length == 0) { + console.error(colors.red('Validation errors: Model must define a datasource')); + throw new CliError('schema validation errors'); + } else if (dataSources.length > 1) { + console.error(colors.red('Validation errors: Multiple datasource declarations are not allowed')); + throw new CliError('schema validation errors'); + } +} + export function eagerLoadAllImports( document: LangiumDocument, documents: LangiumDocuments, diff --git a/packages/schema/src/language-server/validator/schema-validator.ts b/packages/schema/src/language-server/validator/schema-validator.ts index 99e6698ce..098489939 100644 --- a/packages/schema/src/language-server/validator/schema-validator.ts +++ b/packages/schema/src/language-server/validator/schema-validator.ts @@ -19,7 +19,7 @@ export default class SchemaValidator implements AstValidator { for (const declaration of model.declarations) { if (importedNames.has(declaration.name)) { - accept('error', `A ${declaration.name} already exists in an imported models`, { + accept('error', `A ${declaration.name} already exists in an imported module`, { node: declaration, property: 'name', }); diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index dd8e50528..3b8316fdb 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -1,4 +1,4 @@ -import { isEnumField, isModel, isPlugin } from '@zenstackhq/language/ast'; +import { isEnumField, isModel } from '@zenstackhq/language/ast'; import { AstNode, AstNodeDescription, @@ -64,8 +64,6 @@ export class ZModelScopeProvider extends DefaultScopeProvider { return EMPTY_SCOPE; } - model.declarations.filter((x) => isPlugin(x)); - const importedUris = stream(model.imports).map(resolveImportUri).nonNullable(); const importedElements = this.indexManager.allElements(referenceType).filter( (des) =>