zenstack
Version:
FullStack enhancement for Prisma ORM: seamless integration from database to UI
218 lines • 11 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZModelScopeProvider = exports.ZModelScopeComputation = void 0;
const ast_1 = require("@zenstackhq/language/ast");
const sdk_1 = require("@zenstackhq/sdk");
const langium_1 = require("langium");
const ts_pattern_1 = require("ts-pattern");
const ast_utils_1 = require("../utils/ast-utils");
const constants_1 = require("./constants");
const utils_1 = require("./validator/utils");
/**
* Custom Langium ScopeComputation implementation which adds enum fields into global scope
*/
class ZModelScopeComputation extends langium_1.DefaultScopeComputation {
constructor(services) {
super(services);
this.services = services;
}
computeExports(document, cancelToken) {
const _super = Object.create(null, {
computeExports: { get: () => super.computeExports }
});
return __awaiter(this, void 0, void 0, function* () {
const result = yield _super.computeExports.call(this, document, cancelToken);
// add enum fields so they can be globally resolved across modules
for (const node of (0, langium_1.streamAllContents)(document.parseResult.value)) {
if (cancelToken) {
yield (0, langium_1.interruptAndCheck)(cancelToken);
}
if ((0, ast_1.isEnumField)(node)) {
const desc = this.services.workspace.AstNodeDescriptionProvider.createDescription(node, node.name, document);
result.push(desc);
}
}
return result;
});
}
processNode(node, document, scopes) {
super.processNode(node, document, scopes);
if ((0, ast_1.isDataModel)(node) && !node.$baseMerged) {
// add base fields to the scope recursively
const bases = (0, sdk_1.getRecursiveBases)(node);
for (const base of bases) {
for (const field of base.fields) {
scopes.add(node, this.descriptions.createDescription(field, this.nameProvider.getName(field)));
}
}
}
}
}
exports.ZModelScopeComputation = ZModelScopeComputation;
class ZModelScopeProvider extends langium_1.DefaultScopeProvider {
constructor(services) {
super(services);
this.services = services;
}
getGlobalScope(referenceType, context) {
const model = (0, langium_1.getContainerOfType)(context.container, ast_1.isModel);
if (!model) {
return langium_1.EMPTY_SCOPE;
}
const importedUris = (0, langium_1.stream)(model.imports).map(ast_utils_1.resolveImportUri).nonNullable();
const importedElements = this.indexManager.allElements(referenceType).filter((des) => {
var _a;
// allow current document
return (0, langium_1.equalURI)(des.documentUri, (_a = model.$document) === null || _a === void 0 ? void 0 : _a.uri) ||
// allow stdlib
des.documentUri.path.endsWith(constants_1.STD_LIB_MODULE_NAME) ||
// allow plugin models
des.documentUri.path.endsWith(constants_1.PLUGIN_MODULE_NAME) ||
// allow imported documents
importedUris.some((importedUri) => (0, langium_1.equalURI)(des.documentUri, importedUri));
});
return new langium_1.StreamScope(importedElements);
}
getScope(context) {
if ((0, ast_1.isMemberAccessExpr)(context.container) && context.container.operand && context.property === 'member') {
return this.getMemberAccessScope(context);
}
if ((0, ast_1.isReferenceExpr)(context.container) && context.property === 'target') {
// when reference expression is resolved inside a collection predicate, the scope is the collection
const containerCollectionPredicate = getCollectionPredicateContext(context.container);
if (containerCollectionPredicate) {
return this.getCollectionPredicateScope(context, containerCollectionPredicate);
}
}
return super.getScope(context);
}
getMemberAccessScope(context) {
const referenceType = this.reflection.getReferenceType(context);
const globalScope = this.getGlobalScope(referenceType, context);
const node = context.container;
// typedef's fields are only added to the scope if the access starts with `auth().`
// or the member access resides inside a typedef
const allowTypeDefScope = (0, utils_1.isAuthOrAuthMemberAccess)(node.operand) || !!(0, langium_1.getContainerOfType)(node, ast_1.isTypeDef);
return (0, ts_pattern_1.match)(node.operand)
.when(ast_1.isReferenceExpr, (operand) => {
var _a;
// operand is a reference, it can only be a model/type-def field
const ref = operand.target.ref;
if ((0, ast_1.isDataModelField)(ref) || (0, ast_1.isTypeDefField)(ref)) {
return this.createScopeForContainer((_a = ref.type.reference) === null || _a === void 0 ? void 0 : _a.ref, globalScope, allowTypeDefScope);
}
return langium_1.EMPTY_SCOPE;
})
.when(ast_1.isMemberAccessExpr, (operand) => {
var _a, _b;
// operand is a member access, it must be resolved to a non-array model/typedef type
const ref = operand.member.ref;
if ((0, ast_1.isDataModelField)(ref) && !ref.type.array) {
return this.createScopeForContainer((_a = ref.type.reference) === null || _a === void 0 ? void 0 : _a.ref, globalScope, allowTypeDefScope);
}
if ((0, ast_1.isTypeDefField)(ref) && !ref.type.array) {
return this.createScopeForContainer((_b = ref.type.reference) === null || _b === void 0 ? void 0 : _b.ref, globalScope, allowTypeDefScope);
}
return langium_1.EMPTY_SCOPE;
})
.when(ast_1.isThisExpr, () => {
// operand is `this`, resolve to the containing model
return this.createScopeForContainingModel(node, globalScope);
})
.when(ast_1.isInvocationExpr, (operand) => {
// deal with member access from `auth()` and `future()
if ((0, sdk_1.isAuthInvocation)(operand)) {
// resolve to `User` or `@@auth` decl
return this.createScopeForAuth(node, globalScope);
}
if ((0, ast_utils_1.isFutureInvocation)(operand)) {
// resolve `future()` to the containing model
return this.createScopeForContainingModel(node, globalScope);
}
return langium_1.EMPTY_SCOPE;
})
.otherwise(() => langium_1.EMPTY_SCOPE);
}
getCollectionPredicateScope(context, collectionPredicate) {
const referenceType = this.reflection.getReferenceType(context);
const globalScope = this.getGlobalScope(referenceType, context);
const collection = collectionPredicate.left;
// typedef's fields are only added to the scope if the access starts with `auth().`
const allowTypeDefScope = (0, utils_1.isAuthOrAuthMemberAccess)(collection);
return (0, ts_pattern_1.match)(collection)
.when(ast_1.isReferenceExpr, (expr) => {
var _a;
// collection is a reference - model or typedef field
const ref = expr.target.ref;
if ((0, ast_1.isDataModelField)(ref) || (0, ast_1.isTypeDefField)(ref)) {
return this.createScopeForContainer((_a = ref.type.reference) === null || _a === void 0 ? void 0 : _a.ref, globalScope, allowTypeDefScope);
}
return langium_1.EMPTY_SCOPE;
})
.when(ast_1.isMemberAccessExpr, (expr) => {
var _a;
// collection is a member access, it can only be resolved to a model or typedef field
const ref = expr.member.ref;
if ((0, ast_1.isDataModelField)(ref) || (0, ast_1.isTypeDefField)(ref)) {
return this.createScopeForContainer((_a = ref.type.reference) === null || _a === void 0 ? void 0 : _a.ref, globalScope, allowTypeDefScope);
}
return langium_1.EMPTY_SCOPE;
})
.when(sdk_1.isAuthInvocation, (expr) => {
return this.createScopeForAuth(expr, globalScope);
})
.otherwise(() => langium_1.EMPTY_SCOPE);
}
createScopeForContainingModel(node, globalScope) {
const model = (0, langium_1.getContainerOfType)(node, ast_1.isDataModel);
if (model) {
return this.createScopeForContainer(model, globalScope);
}
else {
return langium_1.EMPTY_SCOPE;
}
}
createScopeForContainer(node, globalScope, includeTypeDefScope = false) {
if ((0, ast_1.isDataModel)(node)) {
return this.createScopeForNodes((0, sdk_1.getModelFieldsWithBases)(node), globalScope);
}
else if (includeTypeDefScope && (0, ast_1.isTypeDef)(node)) {
return this.createScopeForNodes(node.fields, globalScope);
}
else {
return langium_1.EMPTY_SCOPE;
}
}
createScopeForAuth(node, globalScope) {
// get all data models and type defs from loaded and reachable documents
const decls = (0, ast_utils_1.getAllLoadedAndReachableDataModelsAndTypeDefs)(this.services.shared.workspace.LangiumDocuments, (0, langium_1.getContainerOfType)(node, ast_1.isDataModel));
const authDecl = (0, sdk_1.getAuthDecl)(decls);
if (authDecl) {
return this.createScopeForContainer(authDecl, globalScope, true);
}
else {
return langium_1.EMPTY_SCOPE;
}
}
}
exports.ZModelScopeProvider = ZModelScopeProvider;
function getCollectionPredicateContext(node) {
let curr = node;
while (curr) {
if (curr.$container && (0, ast_utils_1.isCollectionPredicate)(curr.$container) && curr.$containerProperty === 'right') {
return curr.$container;
}
curr = curr.$container;
}
return undefined;
}
//# sourceMappingURL=zmodel-scope.js.map