oak-domain/lib/compiler/schemalBuilder.js

5860 lines
340 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAnalizedSchema = void 0;
exports.constructAttributes = constructAttributes;
exports.translateLocaleObject = translateLocaleObject;
exports.registerIgnoredForeignKeyMap = registerIgnoredForeignKeyMap;
exports.registerFreeEntities = registerFreeEntities;
exports.registerIgnoredRelationPathMap = registerIgnoredRelationPathMap;
exports.registerFixedDestinationPathMap = registerFixedDestinationPathMap;
exports.registerDeducedRelationMap = registerDeducedRelationMap;
exports.analyzeEntities = analyzeEntities;
exports.getProjectionKeys = getProjectionKeys;
exports.buildSchema = buildSchema;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const assert_1 = tslib_1.__importDefault(require("assert"));
const fs_1 = require("fs");
const fs_extra_1 = require("fs-extra");
const lodash_1 = require("lodash");
const ts = tslib_1.__importStar(require("typescript"));
const { factory } = ts;
const env_1 = require("./env");
const string_1 = require("../utils/string");
const Schema = {};
const OneToMany = {};
const ManyToOne = {};
const ReversePointerEntities = {};
const ReversePointerRelations = {};
const ActionImportStatements = () => [
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("ActionDef"))])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)()}Action`), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("GenericAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("AppendOnlyAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ReadOnlyAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ExcludeUpdateAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ExcludeRemoveAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("RelationAction")),
])), factory.createStringLiteral((0, env_1.ACTION_CONSTANT_IN_OAK_DOMAIN)()), undefined)
];
const StyleAsts = {};
const ActionAsts = {};
const SchemaAsts = {};
/**
* 部分项目目前存在引用了Schema但是不依赖于其包因此目前先在此去重。
* 后续要修正这种行为让继承的schema分层编译
* @param many
* @param one
* @param key
* @param notNull
*/
function addRelationship(many, one, key, notNull) {
const { [many]: manySet } = ManyToOne;
const one2 = one === 'Schema' ? many : one;
if (manySet) {
if (!manySet.find(ele => ele[1] === key && ele[0] === one2)) {
manySet.push([one2, key, notNull]);
}
}
else {
(0, lodash_1.assign)(ManyToOne, {
[many]: [[one2, key, notNull]],
});
}
const { [one2]: oneSet } = OneToMany;
if (oneSet) {
if (!oneSet.find(ele => ele[1] === key && ele[0] === many)) {
oneSet.push([many, key, notNull]);
}
}
else {
(0, lodash_1.assign)(OneToMany, {
[one2]: [[many, key, notNull]],
});
}
}
/**
* 对relationship去重。一旦发生对象重定义这里就有可能重复
*/
function uniqRelationships() {
for (const entity in ManyToOne) {
ManyToOne[entity] = (0, lodash_1.uniqBy)(ManyToOne[entity], (ele) => `${ele[0]}-${ele[1]}`);
}
for (const entity in OneToMany) {
OneToMany[entity] = (0, lodash_1.uniqBy)(OneToMany[entity], (ele) => `${ele[0]}-${ele[1]}`);
}
for (const entity in ReversePointerRelations) {
OneToMany[entity] = (0, lodash_1.uniq)(OneToMany[entity]);
}
}
function createForeignRef(entity, foreignKey, ref) {
if (entity === foreignKey) {
return factory.createIdentifier(ref);
}
return factory.createQualifiedName(factory.createIdentifier(foreignKey), factory.createIdentifier(ref));
}
function createIndexedForeignRef(foreignEntity, ref) {
return factory.createIndexedAccessTypeNode(factory.createTypeReferenceNode(factory.createIdentifier(foreignEntity), undefined), factory.createLiteralTypeNode(factory.createStringLiteral(ref)));
}
function pushStatementIntoActionAst(moduleName, node, sourceFile) {
// let actionNames;
let actionDefName;
/* if (ts.isTypeAliasDeclaration(node) && node.name.text === 'ParticularAction') {
const { type } = node;
if (ts.isUnionTypeNode(type)) {
actionNames = type.types.map(
(ele) => {
assert(ts.isTypeReferenceNode(ele));
const text = (<ts.Identifier>ele.typeName).text;
assert(text.endsWith('Action'));
return firstLetterLowerCase(text.slice(0, text.length - 6));
}
)
}
else {
assert(ts.isTypeReferenceNode(type));
const text = (<ts.Identifier>type.typeName).text;
assert(text.endsWith('Action'));
actionNames = [firstLetterLowerCase(text.slice(0, text.length - 6))];
}
} */
if (ts.isVariableStatement(node)) {
const { declarationList: { declarations } } = node;
declarations.forEach((declaration) => {
if (ts.isIdentifier(declaration.name) && declaration.name.text.endsWith('ActionDef')) {
const { text } = declaration.name;
actionDefName = (0, string_1.firstLetterLowerCase)(text.slice(0, text.length - 9));
}
});
}
if (ActionAsts[moduleName]) {
ActionAsts[moduleName].statements.push(node);
/* if (actionNames) {
ActionAsts[moduleName].actionNames = actionNames;
} */
if (actionDefName) {
ActionAsts[moduleName].actionDefNames.push(actionDefName);
}
}
else {
(0, lodash_1.assign)(ActionAsts, {
[moduleName]: {
statements: [...ActionImportStatements(), node],
sourceFile,
importActionFrom: {},
importStateFrom: {},
importActionDefFrom: {},
// actionNames,
actionDefNames: actionDefName ? [actionDefName] : [],
}
});
}
}
function pushStatementIntoSchemaAst(moduleName, statement, sourceFile) {
if (SchemaAsts[moduleName]) {
SchemaAsts[moduleName].statements.push(statement);
if (!SchemaAsts[moduleName].sourceFile && sourceFile) {
SchemaAsts[moduleName].sourceFile = sourceFile;
}
}
else {
(0, lodash_1.assign)(SchemaAsts, {
[moduleName]: {
statements: [statement],
sourceFile,
}
});
}
}
/**
* 检查ActionDef是否满足合法的定义
* 1、ActionDef, Action, State三者命名是否一致
* @param actionDefNode
*/
function checkActionDefNameConsistent(filename, actionDefNode) {
const { name, type } = actionDefNode;
(0, assert_1.default)(ts.isTypeReferenceNode(type), `${filename}」中的 ActionDef 定义错误:必须是类型引用,如 ActionDef<CreateAction, CreateState>`);
const { typeArguments } = type;
(0, assert_1.default)(typeArguments.length === 2, `${filename}」中的 ActionDef 定义错误:必须有两个类型参数 <Action, State>`);
const [actionNode, stateNode] = typeArguments;
(0, assert_1.default)(ts.isIdentifier(name), `${filename}」中的 ActionDef「${name.text}」不是有效的变量名`);
(0, assert_1.default)(name.text.endsWith('ActionDef'), `${filename}」中的变量「${name.text}」命名错误ActionDef 变量必须以 'ActionDef' 结尾`);
(0, assert_1.default)(ts.isTypeReferenceNode(actionNode) && ts.isTypeReferenceNode(stateNode), `${filename}」中的「${name.text}」类型参数错误Action 和 State 必须是类型引用`);
(0, assert_1.default)(ts.isIdentifier(actionNode.typeName) && ts.isIdentifier(stateNode.typeName), `${filename}」中的「${name.text}」类型参数错误Action 和 State 必须是简单标识符`);
(0, assert_1.default)(actionNode.typeName.text.endsWith('Action'), `${filename}」中的「${name.text}」引用的 Action 类型「${actionNode.typeName.text}」命名错误:必须以 'Action' 结尾`);
(0, assert_1.default)(stateNode.typeName.text.endsWith('State'), `${filename}」中的「${name.text}」引用的 State 类型「${stateNode.typeName.text}」命名错误:必须以 'State' 结尾`);
const adfName = name.text.slice(0, name.text.length - 9);
const aName = actionNode.typeName.text.slice(0, actionNode.typeName.text.length - 6);
const sName = stateNode.typeName.text.slice(0, stateNode.typeName.text.length - 5);
(0, assert_1.default)(adfName === aName && aName === sName, `${filename}」中的「${name.text}」命名不一致。\n` +
`要求ActionDef、Action、State 的前缀必须相同\n` +
`当前:${name.text}, ${actionNode.typeName.text}, ${stateNode.typeName.text}\n` +
`应为:${adfName}ActionDef, ${adfName}Action, ${adfName}State`);
}
function checkStringLiteralLegal(filename, obj, text, ele) {
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `${filename}」中的 ${obj}${text}」类型错误:必须是字符串字面量类型,如 'create' | 'update'`);
(0, assert_1.default)(!ele.literal.text.includes('$'), `${filename}」中的 ${obj}${text}」包含非法字符:「${ele.literal.text}」中不能包含 '$' 字符`);
(0, assert_1.default)(ele.literal.text.length > 0, `${filename}」中的 ${obj}${text}」不能为空字符串`);
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, `${filename}」中的 ${obj}${text}」的值「${ele.literal.text}」长度超限。\n` +
`当前长度:${ele.literal.text.length},最大允许:${env_1.STRING_LITERAL_MAX_LENGTH}`);
return ele.literal.text;
}
function addImportedFrom(moduleName, name, node) {
const ast = ActionAsts[moduleName];
let importFrom = typeof node === 'string' ? node : '';
let propertyName;
if (typeof node === 'object') {
const { moduleSpecifier, importClause } = node;
(0, assert_1.default)(ts.isStringLiteral(moduleSpecifier), `${moduleName}中的${name}未引用正确的moduleSpecifier`);
(0, assert_1.default)(importClause, `${moduleName}中的${name}未引用正确的importClause`);
const { namedBindings } = importClause;
(0, assert_1.default)(namedBindings, `${moduleName}中的${name}未引用正确的namedBindings`);
(0, assert_1.default)(ts.isNamedImports(namedBindings));
const importSpecifier = namedBindings.elements.find((ele) => ele.name.text === name);
(0, assert_1.default)(importSpecifier, `${moduleName}中的${name}未引用正确的importSpecifier`);
propertyName = importSpecifier.propertyName && importSpecifier.propertyName.text;
importFrom = moduleSpecifier.text;
}
if (name.endsWith('Action')) {
(0, lodash_1.assign)(ast.importActionFrom, {
[name]: [importFrom, propertyName],
});
}
else if (name.endsWith('ActionDef')) {
(0, lodash_1.assign)(ast.importActionDefFrom, {
[name]: [importFrom, propertyName],
});
}
else {
(0, assert_1.default)(name.endsWith("State"), `${moduleName}中的${name}未以State结尾`);
(0, lodash_1.assign)(ast.importStateFrom, {
[name]: [importFrom, propertyName],
});
}
}
/**
* 计算编译后文件的正确导入路径
* @param sourceFilePath 源文件路径(相对于项目根目录),例如: 'src/entities/User.ts'
* @param outputFilePath 输出文件路径(相对于项目根目录),例如: 'src/oak-app-domain/User/_baseschema.ts'
* @param importPath 原始导入路径,例如: '../types/Config' 或 '@/utils/helper'
* @param projectRoot 项目根目录,默认为当前目录
* @returns 新的相对导入路径
*/
function resolveCompiledImportPath(sourceFilePath, outputFilePath, importPath, projectRoot = '.', relative) {
// 如果不是相对路径(例如 node_modules 的包或别名路径),直接返回
if (!importPath.startsWith('.')) {
return importPath;
}
if (sourceFilePath.startsWith("node_modules")) {
// 如果是node_modules下的文件保持原有的处理逻辑
return importPath.startsWith('.') ? (relative
? path_1.default.join(relative, importPath).replace(/\\/g, '/')
: path_1.default.join('..', importPath).replace(/\\/g, '/')) : importPath;
}
// 1. 获取源文件所在目录
const sourceDir = path_1.default.dirname(sourceFilePath);
// 2. 解析原始导入路径,得到目标文件的绝对路径(相对于项目根目录)
const targetAbsolutePath = path_1.default.join(projectRoot, sourceDir, importPath);
const normalizedTargetPath = path_1.default.normalize(targetAbsolutePath);
// 3. 获取输出文件所在目录
const outputDir = path_1.default.dirname(outputFilePath);
const normalizedOutputDir = path_1.default.normalize(path_1.default.join(projectRoot, outputDir));
// 4. 计算从输出目录到目标文件的相对路径
let relativePath = path_1.default.relative(normalizedOutputDir, normalizedTargetPath);
// 5. 标准化路径分隔符为 '/'
relativePath = relativePath.replace(/\\/g, '/');
// 6. 确保相对路径以 './' 或 '../' 开头
if (!relativePath.startsWith('.')) {
relativePath = './' + relativePath;
}
return relativePath;
}
function analyzeExternalAttrImport(node, program, importAttrFrom, relativePath) {
const checker = program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(node.typeName);
let declaration = symbol?.getDeclarations()[0];
/* const typee = checker.getDeclaredTypeOfSymbol(symbol!);
const declaration = typee.aliasSymbol!.getDeclarations()![0]; */
if (ts.isImportSpecifier(declaration)) {
const name = declaration.name.text;
const importDeclartion = declaration.parent.parent.parent;
(0, assert_1.default)(ts.isImportDeclaration(importDeclartion), `未找到${name}的importDeclaration`);
const { moduleSpecifier, importClause } = importDeclartion;
(0, assert_1.default)(ts.isStringLiteral(moduleSpecifier), `未找到${name}的moduleSpecifier`);
(0, assert_1.default)(importClause, `未找到${name}的importClause`);
const { namedBindings } = importClause;
(0, assert_1.default)(namedBindings, `未找到${name}的namedBindings`);
(0, assert_1.default)(ts.isNamedImports(namedBindings), `未找到${name}的namedImports`);
const importSpecifier = namedBindings.elements.find((ele) => ele.name.text === name);
(0, assert_1.default)(importSpecifier, `未找到${name}的importSpecifier`);
const propertyName = importSpecifier.propertyName && importSpecifier.propertyName.text;
const importFrom = moduleSpecifier.text;
// const importFromRelatively = importFrom.startsWith('.') ? (relativePath
// ? PathLib.join(
// relativePath,
// importFrom
// ).replace(/\\/g, '/')
// : PathLib.join(
// '..',
// importFrom
// ).replace(/\\/g, '/')) : importFrom;
const sourceFilePath = declaration.getSourceFile().fileName;
(0, lodash_1.assign)(importAttrFrom, {
// [name]: [importFromRelatively, propertyName],
[name]: [importFrom, propertyName, sourceFilePath, relativePath],
});
}
else if (ts.isTypeAliasDeclaration(declaration)) {
const traverseTsAst = (node) => {
// 递归遍历每个节点
ts.forEachChild(node, (child) => {
if (ts.isTypeReferenceNode(child)) {
analyzeExternalAttrImport(child, program, importAttrFrom, relativePath);
}
else {
traverseTsAst(child);
}
});
};
traverseTsAst(declaration);
}
}
function tryGetStringLiteralValues(moduleName, filename, obj, node, program) {
const checker = program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(node.typeName);
let declaration = symbol?.getDeclarations()[0];
/* const typee = checker.getDeclaredTypeOfSymbol(symbol!);
const declaration = typee.aliasSymbol!.getDeclarations()![0]; */
const values = [];
if (ts.isImportSpecifier(declaration)) {
const typee = checker.getDeclaredTypeOfSymbol(symbol);
if (typee.isStringLiteral()) {
values.push(typee.value);
}
else if (typee.isUnion()) {
values.push(...typee.types.map(ele => {
if (ele.isStringLiteral()) {
return ele.value;
}
}).filter(ele => !!ele));
}
if (['state', 'action'].includes(obj)) {
(0, assert_1.default)(values.length > 0, `${filename}中的${obj} ${node.typeName.getText()}未定义`);
const importDeclartion = declaration.parent.parent.parent;
(0, assert_1.default)(ts.isImportDeclaration(importDeclartion), '未找到importDeclaration');
addImportedFrom(moduleName, declaration.name.text, importDeclartion);
}
}
else if (ts.isTypeAliasDeclaration(declaration)) {
// 本地定义的type
const { name, type } = declaration;
if (ts.isUnionTypeNode(type)) {
values.push(...(type.types.map(ele => {
if (ts.isLiteralTypeNode(ele)) {
return checkStringLiteralLegal(filename, obj, name.text, ele);
}
else if (ts.isTypeReferenceNode(ele)) {
return tryGetStringLiteralValues(moduleName, filename, obj, ele, program);
}
else {
throw new Error(`暂时不能处理的type${ele.getText()}`);
}
})).flat());
}
else if (ts.isLiteralTypeNode(type)) {
const action = checkStringLiteralLegal(filename, obj, name.text, type);
values.push(action);
}
if (['state', 'action'].includes(obj)) {
(0, assert_1.default)(values.length > 0, `${filename}中的${obj} ${node.typeName.getText()}未定义`);
const ast = ActionAsts[moduleName];
addImportedFrom(moduleName, declaration.name.text, './');
}
}
return values;
}
const RESERVED_ACTION_NAMES = ['GenericAction', 'ParticularAction', 'ExcludeRemoveAction', 'ExcludeUpdateAction', 'ReadOnlyAction', 'AppendOnlyAction', 'RelationAction'];
const action_1 = require("../actions/action");
const DataType_1 = require("../types/DataType");
const Entity_1 = require("../types/Entity");
const uuid_1 = require("../utils/uuid");
const OriginActionDict = {
'crud': 'GenericAction',
'excludeUpdate': 'ExcludeUpdateAction',
'excludeRemove': 'ExcludeRemoveAction',
'appendOnly': 'AppendOnlyAction',
'readOnly': 'ReadOnlyAction',
};
function dealWithActionTypeNode(moduleName, filename, actionTypeNode, program, sourceFile) {
const actionTexts = action_1.genericActions.map(ele => ele);
if (moduleName === 'User') {
actionTexts.push(...action_1.relationActions);
}
if (ts.isUnionTypeNode(actionTypeNode)) {
const actionNames = actionTypeNode.types.map(ele => {
if (ts.isTypeReferenceNode(ele) && ts.isIdentifier(ele.typeName)) {
return ele.typeName.text;
}
}).filter(ele => !!ele);
(0, assert_1.default)((0, lodash_1.intersection)(actionNames, RESERVED_ACTION_NAMES).length === 0, `${filename}」中的 Action 命名冲突:不能使用保留名称。\n` +
`保留名称:${RESERVED_ACTION_NAMES.join(', ')}\n` +
`冲突的名称:${(0, lodash_1.intersection)(actionNames, RESERVED_ACTION_NAMES).join(', ')}`);
actionTypeNode.types.forEach(ele => {
if (ts.isTypeReferenceNode(ele)) {
// 这里递归了类型定义去查找所有的action字符串
const actionStrings = tryGetStringLiteralValues(moduleName, filename, 'action', ele, program);
(0, assert_1.default)(actionStrings.length > 0, `${filename}」中的 Action 类型「${ele.typeName.text}」定义为空,必须至少包含一个 action`);
actionTexts.push(...actionStrings);
}
else {
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal), `${moduleName}】action的定义既非Type也不是string`);
actionTexts.push(ele.literal.text);
}
});
}
else if (ts.isTypeReferenceNode(actionTypeNode)) {
if (ts.isIdentifier(actionTypeNode.typeName)) {
(0, assert_1.default)((0, lodash_1.intersection)([actionTypeNode.typeName.text], RESERVED_ACTION_NAMES).length === 0, `${filename}」中的 Action 命名冲突:不能使用保留名称。\n` +
`保留名称:${RESERVED_ACTION_NAMES.join(', ')}\n` +
`冲突的名称:${(0, lodash_1.intersection)([actionTypeNode.typeName.text], RESERVED_ACTION_NAMES).join(', ')}`);
}
const actionStrings = tryGetStringLiteralValues(moduleName, filename, 'action', actionTypeNode, program);
(0, assert_1.default)(actionStrings.length > 0, `${filename}中的Action定义为空`);
actionTexts.push(...actionStrings);
}
else {
(0, assert_1.default)(ts.isLiteralTypeNode(actionTypeNode) && ts.isStringLiteral(actionTypeNode.literal), `${moduleName}】action的定义既非Type也不是string`);
actionTexts.push(actionTypeNode.literal.text);
}
// 所有的action定义不能有重名
const ActionDict = {};
actionTexts.forEach((action) => {
(0, assert_1.default)(action.length <= env_1.STRING_LITERAL_MAX_LENGTH, `${filename}」中的 Action「${action}」命名长度超限。\n` +
`当前长度:${action.length},最大允许:${env_1.STRING_LITERAL_MAX_LENGTH}`);
(0, assert_1.default)(/^[a-z][a-zA-Z]+$/.test(action), `${filename}」中的 Action「${action}」命名格式错误:必须以小写字母开头且只能包含字母`);
if (ActionDict.hasOwnProperty(action)) {
throw new Error(`文件${filename}Action定义上的【${action}】动作存在同名`);
}
else {
(0, lodash_1.assign)(ActionDict, {
[action]: 1,
});
}
});
pushStatementIntoActionAst(moduleName, factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("actions"), undefined, undefined, factory.createArrayLiteralExpression(actionTexts.map(ele => factory.createStringLiteral(ele)), false))], ts.NodeFlags.Const)), sourceFile);
}
function dealWithActionDefInitializer(moduleName, initializer, program) {
if (ts.isIdentifier(initializer) || ts.isCallExpression(initializer)) {
// 是从别处的引用注入到importActionDefFrom
const checker = program.getTypeChecker();
const identifier = ts.isIdentifier(initializer) ? initializer : initializer.expression;
(0, assert_1.default)(ts.isIdentifier(identifier), "ActionDef的initializer不是一个Identifier");
const symbol = checker.getSymbolAtLocation(identifier);
const declaration = symbol?.getDeclarations()[0];
(0, assert_1.default)(ts.isImportSpecifier(declaration), "ActionDef的initializer不是一个ImportSpecifier");
const importDeclartion = declaration.parent.parent.parent;
addImportedFrom(moduleName, identifier.text, importDeclartion);
// 如果是函数调用CallExpression无法在编译时确定is直接返回false
if (ts.isCallExpression(initializer)) {
return false;
}
// 如果是直接引用Identifier尝试分析外部引用的actionDef中的is
const aliasedSymbol = checker.getAliasedSymbol(symbol);
const aliasedDeclaration = aliasedSymbol?.getDeclarations()?.[0];
// 如果无法获取到声明返回false可能是第三方库
if (!aliasedDeclaration || !ts.isVariableDeclaration(aliasedDeclaration)) {
return false;
}
const { initializer: aliDecInit } = aliasedDeclaration;
// 如果没有初始化器,可能是.d.ts声明文件尝试从对应的.js文件读取
if (!aliDecInit) {
const sourceFile = aliasedDeclaration.getSourceFile();
const sourceFileName = sourceFile.fileName;
// 检查是否是.d.ts文件
if (sourceFileName.endsWith('.d.ts')) {
const jsFileName = sourceFileName.replace(/\.d\.ts$/, '.js');
// 尝试读取对应的.js文件
if ((0, fs_1.existsSync)(jsFileName)) {
try {
const jsContent = require('fs').readFileSync(jsFileName, 'utf-8');
// 获取原始的导出名称(处理 as 别名的情况)
// 如果有 propertyName说明使用了 as应该使用 propertyName
// 否则使用 name
const exportName = declaration.propertyName?.text || declaration.name.text;
// 尝试从JS文件中解析is属性
const hasIs = tryParseIsFromJsFile(jsContent, exportName);
return hasIs;
}
catch (error) {
console.warn(`无法解析JS文件 ${jsFileName}:`, error);
return false;
}
}
}
return false;
}
// 如果是对象字面量检查is属性
if (ts.isObjectLiteralExpression(aliDecInit)) {
const { properties } = aliDecInit;
const isProp = properties.find((ele) => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'is' && ts.isStringLiteral(ele.initializer));
return !!isProp;
}
// 其他情况返回false
return false;
}
else {
// 本地定义的actionDef不用处理
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), moduleName);
if (ts.isObjectLiteralExpression(initializer)) {
const { properties } = initializer;
// 检查is属性是否存在并且是string literal
const isProp = properties.find((ele) => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'is' && ts.isStringLiteral(ele.initializer));
return !!isProp;
}
return false;
}
}
/**
* 尝试从JavaScript文件内容中解析导出对象的is属性
* @param jsContent JavaScript文件内容
* @param exportName 导出的变量名
* @returns 是否包含is属性
*/
function tryParseIsFromJsFile(jsContent, exportName) {
// 创建一个临时的SourceFile来解析JavaScript
const jsSourceFile = ts.createSourceFile('temp.js', jsContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
let hasIs = false;
// 遍历AST查找导出
const visit = (node) => {
// 处理 exports.xxx = {...} 或 module.exports.xxx = {...}
if (ts.isExpressionStatement(node)) {
const expr = node.expression;
if (ts.isBinaryExpression(expr) && expr.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
const left = expr.left;
const right = expr.right;
// 检查是否是 exports.exportName 或 module.exports.exportName
if (ts.isPropertyAccessExpression(left)) {
const propertyName = left.name.text;
if (propertyName === exportName) {
// 检查右侧是否是对象字面量
if (ts.isObjectLiteralExpression(right)) {
hasIs = checkObjectHasIsProperty(right);
return;
}
}
}
}
}
// 处理 const xxx = {...}; exports.xxx = xxx;
if (ts.isVariableStatement(node)) {
const declarations = node.declarationList.declarations;
for (const decl of declarations) {
if (ts.isIdentifier(decl.name) && decl.name.text === exportName) {
if (decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {
hasIs = checkObjectHasIsProperty(decl.initializer);
return;
}
}
}
}
ts.forEachChild(node, visit);
};
visit(jsSourceFile);
return hasIs;
}
/**
* 检查对象字面量是否包含is属性
*/
function checkObjectHasIsProperty(objLiteral) {
const { properties } = objLiteral;
const isProp = properties.find((ele) => {
if (ts.isPropertyAssignment(ele)) {
const name = ele.name;
if (ts.isIdentifier(name) && name.text === 'is') {
// 检查值是否是字符串字面量
return ts.isStringLiteral(ele.initializer);
}
}
return false;
});
return !!isProp;
}
/**
* entity的引用一定要以 import { Schema as XXX } from '..../XXX'这种形式
* @param declaration
* @param filename
* @returns
*/
function getEntityImported(declaration) {
const { moduleSpecifier, importClause } = declaration;
let entityImported;
if (ts.isStringLiteral(moduleSpecifier)) {
const { name: importedFileName } = path_1.default.parse(moduleSpecifier.text);
const { namedBindings } = importClause;
if (namedBindings && ts.isNamedImports(namedBindings)) {
const { elements } = namedBindings;
if (elements.find(ele => ts.isImportSpecifier(ele) && ele.name.text === importedFileName && ele.propertyName?.text === 'Schema')) {
entityImported = importedFileName;
}
}
}
return entityImported;
}
function checkLocaleEnumAttrs(node, attrs, filename) {
const { members } = node;
const memberKeys = members.map((ele) => {
(0, assert_1.default)(ts.isPropertySignature(ele) && ts.isIdentifier(ele.name), 'locale定义中的属性必须是identifier');
return ele.name.text;
});
const lack = (0, lodash_1.difference)(attrs, memberKeys);
if (lack.length > 0) {
throw new Error(`${filename}中缺少了对${lack.join(',')}属性的locale定义`);
}
}
function checkLocaleExpressionPropertyExists(root, attr, exists, filename) {
const { properties } = root;
properties.forEach((ele) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele) && (ts.isIdentifier(ele.name) || ts.isStringLiteral(ele.name)) && ts.isObjectLiteralExpression(ele.initializer));
const { properties: p2 } = ele.initializer;
const pp = p2.find((ele2) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele2) && ts.isIdentifier(ele2.name), 'locale定义中的属性必须是identifier');
return ele2.name.text === attr;
});
if (exists && !pp) {
throw new Error(`${filename}中的locale定义中的${ele.name.text}中缺少了${attr}的定义`);
}
else if (!exists && pp) {
throw new Error(`${filename}中的locale定义中的${ele.name.text}中有多余的${attr}定义`);
}
});
}
/* function getStringEnumValues(filename: string, program: ts.Program, obj: string, node: ts.TypeReferenceNode) {
const checker = program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(node.typeName);
let declaration = symbol?.getDeclarations()![0]!;
if (ts.isImportSpecifier(declaration)) {
const typee = checker.getDeclaredTypeOfSymbol(symbol!);
declaration = typee.aliasSymbol?.getDeclarations()![0]!;
}
if (declaration && ts.isTypeAliasDeclaration(declaration)) {
if (ts.isUnionTypeNode(declaration.type) && ts.isLiteralTypeNode(declaration.type.types[0])) {
return declaration.type.types.map(
ele => checkStringLiteralLegal(filename, obj, (<ts.TypeAliasDeclaration>declaration).name.text, ele)
)
}
if (ts.isLiteralTypeNode(declaration.type)) {
const value = checkStringLiteralLegal(filename, obj, declaration.name.text, declaration.type);
return [value];
}
}
} */
function checkNameLegal(filename, attrName, upperCase) {
(0, assert_1.default)(attrName.length <= env_1.ENTITY_NAME_MAX_LENGTH, `${filename}」中的名称「${attrName}」长度超限。\n` +
`当前长度:${attrName.length},最大允许:${env_1.ENTITY_NAME_MAX_LENGTH}`);
if (upperCase) {
(0, assert_1.default)(/[A-Z][a-z|A-Z|0-9]+/i.test(attrName), `${filename}」中的名称「${attrName}」格式错误必须以大写字母开头只能包含字母和数字UserProfile`);
}
else if (upperCase === false) {
(0, assert_1.default)(/[a-z][a-z|A-Z|0-9]+/i.test(attrName), `${filename}」中的名称「${attrName}」格式错误必须以小写字母开头只能包含字母和数字userName`);
}
else {
(0, assert_1.default)(/[a-z|A-Z][a-z|A-Z|0-9]+/i.test(attrName), `${filename}」中的名称「${attrName}」格式错误:必须以字母开头,只能包含字母和数字`);
}
}
/**
* 分析import语句这里注意要去重
* @param node
* @param referencedSchemas
* @param additionalImports
* @param relativePath
*/
function analyzeImportDeclaration(node, referencedSchemas, additionalImports, filename, relativePath) {
const entityImported = getEntityImported(node);
if (entityImported) {
referencedSchemas.push(entityImported);
}
else {
/* const { moduleSpecifier, importClause } = node;
assert(ts.isStringLiteral(moduleSpecifier), `${filename}中的import出现了非stringLiteral类型的import module specifier无法处理`);
const { text } = moduleSpecifier;
// 和数据类型相关的会自动引入这里忽略见initialStatements
// 如果是相对路径,编译后的路径默认要深一层
const moduleSpecifier2Text = text.startsWith('.') ? (relativePath
? PathLib.join(
relativePath,
text
).replace(/\\/g, '/')
: PathLib.join(
'..',
text
).replace(/\\/g, '/')) : text;
// 去重
if (importClause) {
const { name, namedBindings } = importClause;
assert(namedBindings && ts.isNamedImports(namedBindings));
assert(!name, `「${filename}」schema定义中出现了import NAME from请避免`);
const unrepeatedSpecifiers: ts.ImportSpecifier[] = [];
for (const ele2 of namedBindings.elements) {
let repeated = false;
for (const imports of additionalImports) {
const { moduleSpecifier: ms, importClause: ic } = imports;
assert(ts.isStringLiteral(ms));
if (ic && ic.namedBindings) {
assert(ts.isNamedImports(ic.namedBindings));
const { elements } = ic.namedBindings;
for (const ele of elements) {
const { name: n, propertyName: pn } = ele;
if (n.text === ele2.name.text || (pn?.text && pn?.text === ele2.propertyName?.text)) {
assert(n.text === ele2.name.text && pn?.text === ele2.propertyName?.text, `解析「${filename}」时发现了name和propertyName不全等的import语句。${[n.text, pn?.text, ele2.name.text, ele2.propertyName?.text].filter(ele => !!ele).join(',')}可能是在Schema的继承链上出现请修正`);
assert(moduleSpecifier.text === ms.text, `解析「${filename}」时发现了name和propertyName完全相等的import语句但它们的from目标不完全相同。${[moduleSpecifier.text, ms.text].join(',')}可能是在Schema的继承链上出现请修正`);
repeated = true;
break;
}
}
}
if (repeated) {
break;
}
}
if (!repeated) {
unrepeatedSpecifiers.push(ele2);
}
}
if (unrepeatedSpecifiers.length > 0) {
additionalImports.push(
factory.updateImportDeclaration(
node,
undefined,
factory.createImportClause(false, undefined, factory.createNamedImports(unrepeatedSpecifiers)),
factory.createStringLiteral(moduleSpecifier2Text),
undefined
)
);
}
}
else {
additionalImports.push(
factory.updateImportDeclaration(
node,
undefined,
undefined,
factory.createStringLiteral(moduleSpecifier2Text),
undefined
)
);
} */
}
}
/**
* 处理模块的导入得到导入文件的sourcefile
*/
function dealImportedFile(path, fileSpecifierPath, filename, program) {
let importedFilepath = '';
const getExistedFileName = () => {
if ((0, fs_1.existsSync)(`${importedFilepath}.ts`)) {
return `${importedFilepath}.ts`;
}
else if ((0, fs_1.existsSync)(`${importedFilepath}.d.ts`)) {
return `${importedFilepath}.d.ts`;
}
return '';
};
if (fileSpecifierPath.startsWith('.')) {
importedFilepath = path_1.default.join(path, fileSpecifierPath);
const importedFilename = getExistedFileName();
(0, assert_1.default)(importedFilename, `${filename}」中import路径${fileSpecifierPath}找不到对应的声明`);
const sourceFile = program.getSourceFile(importedFilename);
(0, assert_1.default)(sourceFile, `${filename}」中import路径${fileSpecifierPath}找不到对应的声明`);
return [sourceFile, importedFilename];
}
// 创建编译器主机
const compilerHost = ts.createCompilerHost(program.getCompilerOptions());
// 解析模块
const resolvedModule = ts.resolveModuleName(fileSpecifierPath, filename, program.getCompilerOptions(), compilerHost);
(0, assert_1.default)(resolvedModule.resolvedModule, `${filename}」中import路径${fileSpecifierPath}找不到对应的声明`);
const pathName = resolvedModule.resolvedModule.resolvedFileName;
const sourceFile = program.getSourceFile(pathName);
(0, assert_1.default)(sourceFile, `${filename}」中import路径${fileSpecifierPath}找不到对应的声明`);
return [sourceFile, pathName];
}
/**
* 在path下的filename文件中import了fileSpecifierPath要找到其对应的引用路径
* 这里关键是要处理形如file:的依赖声明
* @param path
* @param fileSpecifierPath
* @param filename
* @returns
*/
// function getImportedFilePath(path: string, fileSpecifierPath: string, filename: string) {
// let importedFilepath = '';
// const getExistedFileName = () => {
// if (existsSync(`${importedFilepath}.ts`)) {
// return `${importedFilepath}.ts`;
// }
// else if (existsSync(`${importedFilepath}.d.ts`)) {
// return `${importedFilepath}.d.ts`;
// }
// return '';
// };
// if (fileSpecifierPath.startsWith('.')) {
// importedFilepath = PathLib.join(path, fileSpecifierPath);
// const importedFilename = getExistedFileName();
// assert(importedFilename, `「${filename}」中import路径${fileSpecifierPath}找不到对应的声明`);
// return importedFilename;
// }
// else {
// const cwd = process.cwd();
// const fileSpecifierPaths = fileSpecifierPath.split('/');
// const moduleName = fileSpecifierPaths[0] || fileSpecifierPaths[1];
// assert(moduleName, '「${filename}」中import路径不合法');
// // 从path向外找package.json -> node_modules直至找到fileSpecifier
// const paths = path.split('/');
// for (let iter = paths.length; iter >= 0; iter--) {
// const paths2 = paths.slice(0, iter);
// const pkgJsonPath = PathLib.join(cwd, ...paths2, 'package.json');
// if (existsSync(pkgJsonPath)) {
// const pkgJson = require(pkgJsonPath);
// if (pkgJson.dependencies?.hasOwnProperty(moduleName)) {
// const dependentPath = pkgJson.dependencies[moduleName] as string;
// if (dependentPath.trimStart().startsWith('file:')) {
// const dependentFilePath = dependentPath.trimStart().slice(5);
// importedFilepath = PathLib.join(pkgJsonPath, '..', dependentFilePath, ...(fileSpecifierPaths[0] ? fileSpecifierPaths.slice(1) : (fileSpecifierPaths.slice(2))));
// }
// else {
// importedFilepath = PathLib.join(pkgJsonPath, '..', 'node_modules', fileSpecifierPath);
// }
// const importedFilename = getExistedFileName();
// if (importedFilename) {
// return importedFilename;
// }
// }
// }
// }
// }
// assert(false, `「${filename}」中import路径${fileSpecifierPath}找不到对应的声明`);
// }
function analyzeSchemaDefinition(node, moduleName, filename, path, program, referencedSchemas, schemaAttrDict, enumAttributes, importAttrFrom, relativePath) {
let hasEntityAttr = false;
let hasEntityIdAttr = false;
let toModi = false;
let toLog = false;
const extendsFrom = [];
const { members, heritageClauses } = node;
(0, assert_1.default)(heritageClauses, `${filename}」中的 Schema 定义错误:必须继承 EntityShape 或其他实体。\n` +
`示例export interface Schema extends EntityShape { ... }`);
const heritagedResult = heritageClauses.map((clause) => {
const { expression } = clause.types[0];
(0, assert_1.default)(ts.isIdentifier(expression), `${expression}不是一个合法的继承类型`);
if (expression.text === 'EntityShape') {
// import { EntityShape } from 'oak-domain/lib/types/Entities; 所有schema的公共祖先类型不用处理
return;
}
// 从其它文件的Schema类继承这里需要将所继承的对象的属性进行访问并展开
(0, assert_1.default)(referencedSchemas.includes(expression.text), `${filename}」中的 Schema 继承了未导入的实体「${expression.text}」。\n` +
`提示请先导入该实体import { Schema as ${expression.text} } from '...'`);
const checker = program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(expression);
let declaration = symbol?.getDeclarations()[0];
(0, assert_1.default)(ts.isImportSpecifier(declaration), `${declaration.getText()}应该是导入的类型`);
const importDeclaration = declaration.parent.parent.parent;
(0, assert_1.default)(ts.isImportDeclaration(importDeclaration), `${declaration.getText()}应该是导入的类型`);
const { moduleSpecifier } = importDeclaration;
(0, assert_1.default)(ts.isStringLiteral(moduleSpecifier), `${declaration.getText()}应该是导入的类型`);
const { text: from } = moduleSpecifier;
extendsFrom.push(from);
const [sourceFile, pathName] = dealImportedFile(path, from, filename, program);
const relativeFilename = path_1.default.relative(process.cwd(), pathName);
const result = analyzeReferenceSchemaFile(moduleName, path_1.default.basename(relativeFilename), path_1.default.dirname(relativeFilename), sourceFile, program, referencedSchemas, schemaAttrDict, enumAttributes, importAttrFrom, path_1.default.join(from, '..'));
return result;
}).filter(ele => !!ele);
// assert(['EntityShape'].includes((<ts.Identifier>heritageClauses![0].types![0].expression).text), moduleName);
members.forEach((attrNode) => {
const { type, name, questionToken } = attrNode;
const attrName = name.text;
checkNameLegal(filename, attrName, false);
if (ts.isTypeReferenceNode(type)
&& ts.isIdentifier(type.typeName)) {
if ((referencedSchemas.includes(type.typeName.text) || type.typeName.text === 'Schema')) {
addRelationship(moduleName, type.typeName.text, attrName, !!questionToken);
// schemaAttrs.push(attrNode);
(0, assert_1.default)(!schemaAttrDict[attrName], `${filename}」的属性定义「${attrName}」发生了重复`);
schemaAttrDict[attrName] = attrNode;
}
else if (type.typeName.text === 'Array') {
// 这是一对多的反向指针的引用,需要特殊处理
const { typeArguments } = type;
(0, assert_1.default)(typeArguments && typeArguments.length === 1, `${filename}」的属性「${attrName}Array 类型必须有且仅有一个类型参数`);
const elementType = typeArguments[0];
(0, assert_1.default)(ts.isTypeReferenceNode(elementType), `${filename}」的属性「${attrName}Array<${elementType.getText()}> 的元素类型必须是实体引用,用于记录反向指针关系。\n` +
`提示:\n` +
` - 如需存储字符串数组,请使用 string[] 类型或者自定义类型存储 JSONtags?: string[]\n` +
` - 如需关联实体请使用实体类型attachments?: Array<ExtraFile>`);
(0, assert_1.default)(ts.isIdentifier(elementType.typeName), `${filename}」的属性「${attrName}Array 的元素类型必须是简单标识符`);
const elementTypeName = elementType.typeName.text;
(0, assert_1.default)(referencedSchemas.includes(elementTypeName), `${filename}」的属性「${attrName}Array<${elementTypeName}> 中的 ${elementTypeName} 不是有效的实体引用。\n` +
`提示:\n` +
` - 请确保已导入该实体import { Schema as ${elementTypeName} } from '...';\n` +
` - 或改用 Object 类型或者自定义类型存储 JSON 数据`);
const reverseEntity = elementTypeName;
if (ReversePointerRelations[reverseEntity]) {
if (!ReversePointerRelations[reverseEntity].includes(moduleName)) {
ReversePointerRelations[reverseEntity].push(moduleName);
}
}
else {
(0, lodash_1.assign)(ReversePointerRelations, {
[reverseEntity]: [moduleName],
});
}
if (reverseEntity === 'Modi') {
toModi = true;
}
else if (reverseEntity === 'Log') {
toLog = true;
}
}
else {
// schemaAttrs.push(attrNode);
(0, assert_1.default)(!schemaAttrDict[attrName], `${filename}」的属性定义「${attrName}」发生了重复`);
schemaAttrDict[attrName] = attrNode;
const enumStringValues = tryGetStringLiteralValues(moduleName, filename, `attr-${attrName}`, type, program);
if (enumStringValues.length > 0) {
enumAttributes[attrName] = enumStringValues;
}
analyzeExternalAttrImport(type, program, importAttrFrom, relativePath);
}
}
else if (ts.isArrayTypeNode(type) && ts.isTypeReferenceNode(type.elementType) && ts.isIdentifier(type.elementType.typeName)) {
const { typeName } = type.elementType;
if (referencedSchemas.includes(typeName.text)) {
// 这也是一对多的反指定义
const reverseEntity = typeName.text;
if (ReversePointerRelations[reverseEntity]) {
if (!ReversePointerRelations[reverseEntity].includes(moduleName)) {
ReversePointerRelations[reverseEntity].push(moduleName);
}
}
else {
(0, lodash_1.assign)(ReversePointerRelations, {
[reverseEntity]: [moduleName],
});
}
if (reverseEntity === 'Modi') {
toModi = true;
}
else if (reverseEntity === 'Log') {
toLog = true;
}
}
else {
// schemaAttrs.push(attrNode);
(0, assert_1.default)(!schemaAttrDict[attrName], `${filename}」的属性定义「${attrName}」发生了重复`);
schemaAttrDict[attrName] = attrNode;
analyzeExternalAttrImport(type.elementType, program, importAttrFrom, relativePath);
// throw new Error(`对象${moduleName}中定义的属性${attrName}是不可识别的数组类别`);
}
}
else {
// schemaAttrs.push(attrNode);
(0, assert_1.default)(!schemaAttrDict[attrName], `${filename}」的属性定义「${attrName}」发生了重复`);
schemaAttrDict[attrName] = attrNode;
if (ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0]) && ts.isStringLiteral(type.types[0].literal)) {
(0, assert_1.default)(ts.isIdentifier(name), `${filename}」中的属性定义不是String类型`);
const { types } = type;
const enumValues = types.map((ele) => checkStringLiteralLegal(filename, '属性', name.text, ele));
enumAttributes[name.text] = enumValues;
}
else if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) {
// 单个字符串的情形,目前应该没有,没测试过,先写着 by Xc 20230221
(0, assert_1.default)(ts.isIdentifier(name));
const enumValues = [
checkStringLiteralLegal(filename, '属性', name.text, type)
];
enumAttributes[name.text] = enumValues;
}
}
if (attrName === 'entity') {
(0, assert_1.default)(ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName), `${moduleName}」中entity属性的定义不是String<32>类型entity是系统用于表示反指指针的保留属性请勿他用`);
const { typeArguments } = type;
(0, assert_1.default)(type.typeName.text === 'String'
&& typeArguments
&& typeArguments.length === 1, `${moduleName}」中entity属性的定义不是String<32>类型entity是系统用于表示反指指针的保留属性请勿他用`);
const [node] = typeArguments;
if (ts.isLiteralTypeNode(node) && ts.isNumericLiteral(node.literal)) {
if (parseInt(node.literal.text) > 32) {
(0, assert_1.default)(false, `${moduleName}」中entity属性的定义不是String<32>类型entity是系统用于表示反指指针的保留属性请勿他用`);
}
else {
hasEntityAttr = true;
}
}
}
if (attrName === 'entityId') {
(0, assert_1.default)(ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName), `${moduleName}」中entityId属性的定义不是String<64>类型entityId是系统用于表示反指指针的保留属性请勿他用`);
const { typeArguments } = type;
(0, assert_1.default)(type.typeName.text === 'String' && typeArguments && typeArguments.length === 1, `${moduleName}」中entityId属性的定义不是String<64>类型entityId是系统用于表示反指指针的保留属性请勿他用`);
const [node] = typeArguments;
if (ts.isLiteralTypeNode(node) && ts.isNumericLiteral(node.literal)) {
if (parseInt(node.literal.text) !== 64) {
(0, assert_1.default)(false, `${moduleName}」中entityId属性的定义不是String<64>类型entityId是系统用于表示反指指针的保留属性请勿他用`);
}
else {
hasEntityIdAttr = true;
}
}
}
});
// 如果继承的对象属性中有相应的定义,一样起效
heritagedResult.forEach(ele => {
if (ele?.hasEntityAttr) {
(0, assert_1.default)(!hasEntityAttr && !hasEntityIdAttr, `${filename}」继承的对象中已经声明了entity和entityId不可重复声明`);
(0, assert_1.default)(ele.hasEntityIdAttr);
hasEntityAttr = true;
hasEntityIdAttr = true;
}
else if (ele?.hasEntityIdAttr) {
(0, assert_1.default)(false, `${filename}」所继承的对象中的entity和entityId没有成对出现`);
}
if (ele?.toModi) {
(0, assert_1.default)(!hasEntityAttr && !hasEntityIdAttr, `${filename}」继承的对象中已经声明了modi不可重复声明`);
toModi = true;
}
if (ele?.toLog) {
toLog = true;
}
});
return {
hasEntityAttr,
hasEntityIdAttr,
toModi,
toLog,
extendsFrom,
};
}
function analyzeReferenceSchemaFile(moduleName, filename, path, sourceFile, program, referencedSchemas, schemaAttrDict, enumAttributes, importAttrFrom, relativePath) {
let result;
ts.forEachChild(sourceFile, (node) => {
if (ts.isImportDeclaration(node)) {
analyzeImportDeclaration(node, referencedSchemas, [], filename, relativePath);
}
if (ts.isInterfaceDeclaration(node)) {
// schema 定义
if (node.name.text === 'Schema') {
result = analyzeSchemaDefinition(node, moduleName, filename, path, program, referencedSchemas, schemaAttrDict, enumAttributes, importAttrFrom, relativePath);
}
else if (!node.name.text.endsWith('Relation') && !node.name.text.endsWith('Action') && !node.name.text.endsWith('State')) {
// 本地规定的一些形状定义,直接使用
pushStatementIntoSchemaAst(moduleName, node);
}
}
if (ts.isTypeAliasDeclaration(node)) {
if (!node.name.text.endsWith('Relation') && !node.name.text.endsWith('Action') && !node.name.text.endsWith('State')) {
// 本地规定的一些形状定义,直接使用
pushStatementIntoSchemaAst(moduleName, node);
}
}
});
(0, assert_1.default)(result, `${filename}」中缺少 Schema 定义。\n` +
`提示:每个实体文件必须包含 'export interface Schema extends EntityShape { ... }'`);
return result;
}
function analyzeEntity(filename, path, program, relativePath) {
const fullPath = `${path}/${filename}`;
const sourceFile = program.getSourceFile(fullPath);
(0, assert_1.default)(sourceFile, `${filename}」文件不存在`);
const moduleName = filename.split('.')[0];
if (Schema.hasOwnProperty(moduleName)) {
delete ActionAsts[moduleName];
delete SchemaAsts[moduleName];
delete StyleAsts[moduleName];
// removeFromRelationShip(moduleName);
console.warn(`出现了同名的Entity定义「${moduleName}」,将使用${fullPath}取代掉默认对象,请检查新的对象结构及相关常量定义与原有的兼容,否则原有对象的相关逻辑会出现不可知异常`);
}
checkNameLegal(filename, moduleName, true);
const referencedSchemas = [];
// const schemaAttrs: ts.TypeElement[] = [];
const schemaAttrDict = {};
let hasFulltextIndex = false;
let indexes;
let beforeSchema = true;
let hasActionDef = false;
let relations = false;
let hasActionOrStateDef = false;
let actionType = 'crud';
let _static = false;
const enumAttributes = {};
const importAttrFrom = {};
let localeDef = undefined;
let extendsFrom;
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
let toModi = false;
let toLog = false;
ts.forEachChild(sourceFile, (node) => {
if (ts.isImportDeclaration(node)) {
analyzeImportDeclaration(node, referencedSchemas, [], filename, relativePath);
}
if (ts.isInterfaceDeclaration(node)) {
// schema 定义
if (node.name.text === 'Schema') {
beforeSchema = false;
const result = analyzeSchemaDefinition(node, moduleName, filename, path, program, referencedSchemas, schemaAttrDict, enumAttributes, importAttrFrom, relativePath);
if (result.hasEntityAttr && result.hasEntityIdAttr) {
(0, lodash_1.assign)(ReversePointerEntities, {
[moduleName]: 1,
});
}
else if (result.hasEntityAttr || result.hasEntityIdAttr) {
throw new Error(`文件「${filename}」:属性 定义中只包含${result.hasEntityAttr ? 'entity' : 'entityId'}不符合定义规范。entity/entityId必须联合出现代表不定对象的反向指针`);
}
beforeSchema = false;
toModi = result.toModi;
toLog = result.toLog;
extendsFrom = result.extendsFrom;
// 对于不是Modi和Oper的对象全部建立和ModiEntity的反指关系
/* if (!['Modi', 'Oper', 'OperEntity', 'ModiEntity'].includes(moduleName) && !toModi) {
if (ReversePointerRelations['ModiEntity']) {
if (!ReversePointerRelations['ModiEntity'].includes(moduleName)) {
ReversePointerRelations['ModiEntity'].push(moduleName);
}
}
else {
assign(ReversePointerRelations, {
['ModiEntity']: [moduleName],
});
}
if (ReversePointerRelations['OperEntity']) {
if (!ReversePointerRelations['OperEntity'].includes(moduleName)) {
ReversePointerRelations['OperEntity'].push(moduleName);
}
}
else {
assign(ReversePointerRelations, {
['OperEntity']: [moduleName],
});
}
} */
}
else if (beforeSchema) {
// 本地规定的一些形状定义,直接使用
pushStatementIntoSchemaAst(moduleName, node, sourceFile);
}
}
if (ts.isTypeAliasDeclaration(node)) {
// action 定义
if (node.name.text === 'Action') {
(0, assert_1.default)(!localeDef, `${filename}】locale定义须在Action之后`);
hasActionDef = true;
const modifiers = [factory.createModifier(ts.SyntaxKind.ExportKeyword)];
pushStatementIntoActionAst(moduleName, factory.updateTypeAliasDeclaration(node, modifiers, factory.createIdentifier('ParticularAction'), node.typeParameters, node.type), sourceFile);
dealWithActionTypeNode(moduleName, filename, node.type, program, sourceFile);
}
else if (node.name.text === 'Relation') {
(0, assert_1.default)(!localeDef, `${filename}】locale定义须在Relation之后`);
const relationValues = [];
if (ts.isLiteralTypeNode(node.type)) {
(0, assert_1.default)(ts.isStringLiteral(node.type.literal), `${filename}中的Relation的定义只能是string类型`);
(0, assert_1.default)(node.type.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${env_1.STRING_LITERAL_MAX_LENGTH}${filename}${node.type.literal.text}`);
relationValues.push(node.type.literal.text);
}
else if (ts.isTypeReferenceNode(node.type)) {
const relationStrings = tryGetStringLiteralValues(moduleName, filename, 'relation', node.type, program);
(0, assert_1.default)(relationStrings.length > 0, `文件${filename}中的relation${node.type.typeName.text}定义未找到字符串类型`);
relationValues.push(...relationStrings);
}
else {
(0, assert_1.default)(ts.isUnionTypeNode(node.type), `Relation的定义只能是string类型或者string union类型或者两者的union${filename}`);
node.type.types.forEach((ele) => {
if (ts.isLiteralTypeNode(ele)) {
(0, assert_1.default)(ts.isStringLiteral(ele.literal), `Relation的定义只能是string类型${filename}`);
(0, assert_1.default)(ele.literal.text.length < env_1.STRING_LITERAL_MAX_LENGTH, `Relation定义的字符串长度不长于${env_1.STRING_LITERAL_MAX_LENGTH}${filename}${ele.literal.text}`);
relationValues.push(ele.literal.text);
}
else {
(0, assert_1.default)(ts.isTypeReferenceNode(ele), `Relation的定义只能是string类型或者string union类型或者两者的union${filename}`);
const relationStrings = tryGetStringLiteralValues(moduleName, filename, 'relation', ele, program);
(0, assert_1.default)(relationStrings.length > 0, `文件${filename}中的relation${ele.typeName.text}定义未找到字符串类型`);
relationValues.push(...relationStrings);
}
});
}
// 对UserEntityGrant对象建立相应的反指关系
// UserEntityGrant结构改变不再建立by Xc 20231101
/* if (ReversePointerRelations['UserEntityGrant']) {
if (!ReversePointerRelations['UserEntityGrant'].includes(moduleName)) {
ReversePointerRelations['UserEntityGrant'].push(moduleName);
}
}
else {
assign(ReversePointerRelations, {
['UserEntityGrant']: [moduleName],
});
} */
// 对Relation对象建立相应的反指关系
if (ReversePointerRelations['Relation']) {
if (!ReversePointerRelations['Relation'].includes(moduleName)) {
ReversePointerRelations['Relation'].push(moduleName);
}
}
else {
(0, lodash_1.assign)(ReversePointerRelations, {
['Relation']: [moduleName],
});
}
// 对UserRelation对象建立相应的反指关系
if (ReversePointerRelations['UserRelation']) {
if (!ReversePointerRelations['UserRelation'].includes(moduleName)) {
ReversePointerRelations['UserRelation'].push(moduleName);
}
}
else {
(0, lodash_1.assign)(ReversePointerRelations, {
['UserRelation']: [moduleName],
});
}
relations = relationValues;
}
else if (node.name.text.endsWith('Action') || node.name.text.endsWith('State')) {
(0, assert_1.default)(!localeDef, `${filename}】locale定义须在Action/State之后`);
hasActionOrStateDef = true;
const { type } = node;
if (ts.isUnionTypeNode(type)) {
pushStatementIntoActionAst(moduleName, factory.updateTypeAliasDeclaration(node, [factory.createModifier(ts.SyntaxKind.ExportKeyword)], node.name, node.typeParameters, process.env.COMPLING_AS_LIB ? factory.createUnionTypeNode([
...type.types,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) : type), sourceFile);
}
else {
(0, assert_1.default)(ts.isLiteralTypeNode(type) || ts.isTypeReferenceNode(type), `文件${filename}中的${type.getText()}应该是字面量或引用类型`);
pushStatementIntoActionAst(moduleName, factory.updateTypeAliasDeclaration(node, [factory.createModifier(ts.SyntaxKind.ExportKeyword)], node.name, node.typeParameters, process.env.COMPLING_AS_LIB ? factory.createUnionTypeNode([
type,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) : type), sourceFile);
}
}
else if (beforeSchema) {
// 本地规定的一些形状定义,直接使用
pushStatementIntoSchemaAst(moduleName, node, sourceFile);
}
}
if (ts.isVariableStatement(node)) {
const { declarationList: { declarations } } = node;
const dealWithActionDef = (declaration) => {
checkActionDefNameConsistent(filename, declaration);
const { typeArguments } = declaration.type;
(0, assert_1.default)(typeArguments.length === 2, `文件${filename}中的action定义不是两个泛型参数`);
const [actionNode, stateNode] = typeArguments;
(0, assert_1.default)(ts.isTypeReferenceNode(actionNode), `文件${filename}中的action定义不是TypeReferenceNode`);
// 这里有可能引用的Action不是本文件的定义要调用这个函数加到importFrom中
tryGetStringLiteralValues(moduleName, filename, 'action', actionNode, program);
(0, assert_1.default)(ts.isTypeReferenceNode(stateNode), `文件${filename}中的action定义不是TypeReferenceNode`);
const enumStateValues = tryGetStringLiteralValues(moduleName, filename, 'state', stateNode, program);
(0, assert_1.default)(enumStateValues.length > 0, `文件${filename}中的state${stateNode.typeName.text}定义不是字符串类型`);
pushStatementIntoActionAst(moduleName, node, sourceFile);
const isDefined = dealWithActionDefInitializer(moduleName, declaration.initializer, program);
(0, assert_1.default)(ts.isIdentifier(declaration.name));
const adName = declaration.name.text.slice(0, declaration.name.text.length - 9);
const attr = adName.concat('State');
(0, assert_1.default)(!schemaAttrDict[attr], `文件${filename}中的${attr}有显式定义与ActionDef需要生成的属性冲突`);
schemaAttrDict[attr] = factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(attr), isDefined ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(attr));
enumAttributes[(0, string_1.firstLetterLowerCase)(attr)] = enumStateValues;
};
const dealWithIndexes = (declaration) => {
const indexNameDict = {};
// 检查索引的属性是否合法
const { elements } = declaration;
elements.forEach((ele) => {
let isFulltextIndex = false;
(0, assert_1.default)(ts.isObjectLiteralExpression(ele), `${filename}」中索引定义应该是对象字面量`);
const { properties } = ele;
const attrProperty = properties.find((ele2) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele2), `${filename}」中索引的属性不为PropertyAssignment`);
return ele2.name.getText() === 'attributes';
});
(0, assert_1.default)(ts.isArrayLiteralExpression(attrProperty.initializer), `${filename}」中索引的attributes属性应该是数组字面量`);
const nameProperty = properties.find((ele2) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele2));
return ele2.name.getText() === 'name';
});
(0, assert_1.default)(ts.isStringLiteral(nameProperty.initializer), '「${filename}」中索引的name属性应该是字符串字面量');
const nameText = nameProperty.initializer.text;
if (indexNameDict[nameText]) {
throw new Error(`${filename}」索引定义重名「${nameText}`);
}
(0, lodash_1.assign)(indexNameDict, {
[nameText]: true,
});
const configProperty = properties.find((ele2) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele2), `${filename}」中索引的属性不为PropertyAssignment`);
return ele2.name.getText() === 'config';
});
if (configProperty) {
(0, assert_1.default)(ts.isObjectLiteralExpression(configProperty.initializer), `${filename}」中索引的config属性应该是对象字面量`);
const { properties: properties2 } = configProperty.initializer;
const typeProperty = properties2.find((ele2) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele2), `${filename}」中索引的属性不为PropertyAssignment`);
return ele2.name.getText() === 'type';
});
if (typeProperty && typeProperty.initializer.text === 'fulltext') {
// 定义了全文索引
if (hasFulltextIndex) {
throw new Error(`${filename}」只能定义一个全文索引`);
}
hasFulltextIndex = true;
isFulltextIndex = true;
}
}
const { elements } = attrProperty.initializer;
// 每个属性都应该在schema中有值且对象类型是可索引值
elements.forEach((ele2) => {
(0, assert_1.default)(ts.isObjectLiteralExpression(ele2));
const { properties: properties2 } = ele2;
const nameProperty = properties2.find((ele3) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele3));
return ele3.name.getText() === 'name';
});
const indexAttrName = nameProperty.initializer.text;
if (!Entity_1.initinctiveAttributes.includes(indexAttrName)) {
const schemaNode = Object.values(schemaAttrDict).find((ele3) => {
(0, assert_1.default)(ts.isPropertySignature(ele3));
return ele3.name.text === indexAttrName;
});
if (!schemaNode) {
throw new Error(`${filename}」中索引「${nameText}」的属性「${indexAttrName}」定义非法`);
}
const { type, name } = schemaNode;
const entity = moduleName;
const { [entity]: manyToOneSet } = ManyToOne;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity, attrName]) => refEntity === text2 && attrName === name.text);
if (!manyToOneItem) {
// 如果不是外键则不能是Text, File
if (isFulltextIndex) {
(0, assert_1.default)(['Text', 'String'].includes(text2), `${filename}」中全文索引「${nameText}」定义的属性「${indexAttrName}」类型非法只能是Text/String`);
}
else {
(0, assert_1.default)(!DataType_1.unIndexedTypes.includes(text2), `${filename}」中索引「${nameText}」的属性「${indexAttrName}」的类型为「${text2}」,不可索引`);
}
}
else {
(0, assert_1.default)(!isFulltextIndex, `${filename}」中全文索引「${nameText}」的属性「${indexAttrName}」类型非法只能为Text/String`);
// 在这里把外键加上Id这样storageSchema才能正常通过
// 这里的写法不太好未来TS版本高了可能会有问题。by Xc 20230131
Object.assign(nameProperty, {
initializer: factory.createStringLiteral(`${indexAttrName}Id`),
});
}
}
else {
(0, assert_1.default)(false); // 这是什么case不确定
}
}
else {
(0, assert_1.default)(!isFulltextIndex, `${filename}」中全文索引「${nameText}」的属性「${indexAttrName}」类型只能为Text/String`);
(0, assert_1.default)(ts.isUnionTypeNode(type) || ts.isLiteralTypeNode(type), `${entity}中索引「${nameText}」的属性${name.text}有定义非法`);
}
}
});
});
indexes = declaration;
};
const dealWithLocales = (declaration) => {
if (hasActionDef) {
// 检查每种locale定义中都应该有'action'域
checkLocaleExpressionPropertyExists(declaration, 'action', true, filename);
}
else {
checkLocaleExpressionPropertyExists(declaration, 'action', false, filename);
}
if (relations) {
// 检查每种locale定义中都应该有'r'域
checkLocaleExpressionPropertyExists(declaration, 'r', true, filename);
}
else {
checkLocaleExpressionPropertyExists(declaration, 'r', false, filename);
}
const allEnumStringAttrs = Object.keys(enumAttributes);
if (allEnumStringAttrs.length > 0) {
// 检查每种locale定义中都应该有'v'域
checkLocaleExpressionPropertyExists(declaration, 'v', true, filename);
}
else {
// 检查每种locale定义中都应该有'v'域
checkLocaleExpressionPropertyExists(declaration, 'v', false, filename);
}
localeDef = declaration;
};
const dealWithConfiguration = (declaration) => {
// assert(!hasActionDef, `${moduleName}中的Configuration定义在Action之后`);
const { properties } = declaration;
const atProperty = properties.find(ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'actionType');
const staticProperty = properties.find(ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'static');
if (atProperty) {
actionType = atProperty.initializer.text;
}
if (staticProperty) {
_static = true; // static如果有值只能为true
}
};
const dealWithStyleDesc = (declaration) => {
StyleAsts[moduleName] = declaration;
};
const dealWithEntityDesc = (declaration) => {
if (ts.isObjectLiteralExpression(declaration)) {
const { properties } = declaration;
const localesProperty = properties.find(ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'locales');
(0, assert_1.default)(ts.isPropertyAssignment(localesProperty), `${filename}」中entityDesc的locales应该是属性声明`);
dealWithLocales(localesProperty.initializer);
const indexesProperty = properties.find(ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'indexes');
if (indexesProperty) {
(0, assert_1.default)(ts.isPropertyAssignment(indexesProperty), `${filename}」中entityDesc的indexes应该是属性声明`);
dealWithIndexes(indexesProperty.initializer);
}
const configurationProperty = properties.find(ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'configuration');
if (configurationProperty) {
(0, assert_1.default)(ts.isPropertyAssignment(configurationProperty), `${filename}」中entityDesc的configuration应该是属性声明`);
dealWithConfiguration(configurationProperty.initializer);
}
const styleDescProperty = properties.find(ele => ts.isPropertyAssignment(ele) && ts.isIdentifier(ele.name) && ele.name.text === 'style');
if (styleDescProperty) {
(0, assert_1.default)(ts.isPropertyAssignment(styleDescProperty), `${filename}」中entityDesc的style应该是属性声明`);
dealWithStyleDesc(styleDescProperty.initializer);
}
}
else if (ts.isIdentifier(declaration)) {
const checker = program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(declaration);
const original = checker.getAliasedSymbol(symbol);
/**
* 目前只研究出来怎么得到originSymbol但只能拿到其类型定义在d.ts中
* 拿不到数据定义(在js中)(original.declaration.initializer是undefined)
*/
(0, assert_1.default)(false, '用变量赋值给entityDesc暂时还解析不了');
}
};
declarations.forEach((declaration) => {
if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ActionDef') {
dealWithActionDef(declaration);
}
else if (declaration.type && (ts.isArrayTypeNode(declaration.type)
&& ts.isTypeReferenceNode(declaration.type.elementType)
&& ts.isIdentifier(declaration.type.elementType.typeName)
&& declaration.type.elementType.typeName.text === 'Index'
|| ts.isTypeReferenceNode(declaration.type)
&& ts.isIdentifier(declaration.type.typeName)
&& declaration.type.typeName.text === 'Array'
&& ts.isTypeReferenceNode(declaration.type.typeArguments[0])
&& ts.isIdentifier(declaration.type.typeArguments[0].typeName)
&& declaration.type.typeArguments[0].typeName.text === 'Index')) {
// 对索引Index的定义
console.log(`${filename}」直接定义indexes的写法已经过时请定义在entityDesc中`);
(0, assert_1.default)(ts.isArrayLiteralExpression(declaration.initializer), `${filename}」中索引定义应该是数组字面量`);
dealWithIndexes(declaration.initializer);
}
else if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'LocaleDef') {
// locale定义
console.log(`${filename}」直接定义locales的写法已经过时请定义在entityDesc中`);
const { type, initializer } = declaration;
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), `${filename}」中locale定义应该是对象字面量`);
const { properties } = initializer;
(0, assert_1.default)(properties.length > 0, `${filename}至少需要有一种locale定义`);
const allEnumStringAttrs = Object.keys(enumAttributes);
const { typeArguments } = type;
(0, assert_1.default)(typeArguments &&
ts.isTypeReferenceNode(typeArguments[0])
&& ts.isIdentifier(typeArguments[0].typeName) && typeArguments[0].typeName.text === 'Schema', `${filename}中缺少locale定义或者locale类型定义的第一个参数不是Schema`);
if (hasActionDef) {
(0, assert_1.default)(ts.isTypeReferenceNode(typeArguments[1])
&& ts.isIdentifier(typeArguments[1].typeName) && typeArguments[1].typeName.text === 'Action', `${filename}中locale类型定义的第二个参数不是Action`);
}
else {
(0, assert_1.default)(ts.isLiteralTypeNode(typeArguments[1])
&& ts.isStringLiteral(typeArguments[1].literal), `${filename}中locale类型定义的第二个参数不是字符串`);
}
if (relations) {
(0, assert_1.default)(ts.isTypeReferenceNode(typeArguments[2])
&& ts.isIdentifier(typeArguments[2].typeName)
&& typeArguments[2].typeName.text === 'Relation', `${filename}中的locale类型定义的第三个参数不是Relation`);
}
else {
(0, assert_1.default)(ts.isLiteralTypeNode(typeArguments[2])
&& ts.isStringLiteral(typeArguments[2].literal), `${filename}中locale类型定义的第三个参数不是空字符串`);
}
if (allEnumStringAttrs.length > 0) {
(0, assert_1.default)(ts.isTypeLiteralNode(typeArguments[3]), `${filename}中的locale类型定义的第四个参数不是{}`);
checkLocaleEnumAttrs(typeArguments[3], allEnumStringAttrs, filename);
}
else {
(0, assert_1.default)(ts.isTypeLiteralNode(typeArguments[3]), `${filename}中的locale类型定义的第四个参数不是{}`);
(0, assert_1.default)(typeArguments[3].members.length == 0, `${filename}中locale类型定义的第四个参数不应存在相应的v定义`);
}
dealWithLocales(initializer);
}
else if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'Configuration') {
console.log(`${filename}」直接定义configuration的写法已经过时请定义在entityDesc中`);
(0, assert_1.default)(ts.isObjectLiteralExpression(declaration.initializer), `${filename}」中configuration定义应该是对象字面量`);
dealWithConfiguration(declaration.initializer);
}
else if (declaration.type && ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'EntityDesc') {
const { initializer } = declaration;
(0, assert_1.default)(initializer, `${filename}」中entityDesc的定义不应该为空`);
dealWithEntityDesc(initializer);
}
else {
throw new Error(`${moduleName}:不能理解的定义内容${declaration.name.text}`);
}
});
}
});
// 要等configuration确定了actionType后再处理
if (hasActionDef) {
const actionDefNodes = [
factory.createTypeReferenceNode(OriginActionDict[actionType], undefined),
factory.createTypeReferenceNode('ParticularAction', undefined)
];
if (moduleName === 'User') {
actionDefNodes.push(factory.createTypeReferenceNode('RelationAction', undefined));
}
if (process.env.COMPLING_AS_LIB) {
actionDefNodes.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
pushStatementIntoActionAst(moduleName, factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Action"), undefined, factory.createUnionTypeNode(actionDefNodes)), sourceFile);
}
if (!hasActionDef && hasActionOrStateDef) {
throw new Error(`${filename}中有Action或State定义但没有定义完整的Action类型`);
}
if (hasActionDef && actionType !== 'crud') {
throw new Error(`${filename}中有Action定义但却定义了actionType不是crud`);
}
const schemaAttrs = Object.values(schemaAttrDict);
(0, assert_1.default)(schemaAttrs.length > 0, `对象${moduleName}没有任何属性定义`);
const schema = {
schemaAttrs,
sourceFile,
toModi,
actionType,
static: _static,
relations,
enumAttributes,
importAttrFrom,
extendsFrom,
};
if (hasFulltextIndex) {
(0, lodash_1.assign)(schema, {
fulltextIndex: true,
});
}
if (indexes) {
(0, lodash_1.assign)(schema, {
indexes,
});
}
if (!localeDef) {
throw new Error(`${filename}中缺少了locale定义`);
}
else {
(0, lodash_1.assign)(schema, {
locale: localeDef,
});
}
/* if (hasRelationDef) {
if(!relationHierarchy && !reverseCascadeRelationHierarchy){
console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义请注意自主编写权限分配的checker`);
}
if (relationHierarchy) {
assign(schema, {
relationHierarchy,
});
}
if (reverseCascadeRelationHierarchy) {
assign(schema, {
reverseCascadeRelationHierarchy,
});
}
}
else {
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
} */
if (toLog) {
(0, lodash_1.assign)(schema, {
toLog,
});
}
(0, lodash_1.assign)(Schema, {
[moduleName]: schema,
});
}
/**
* 生成Schema
* @param statements
* @param schemaAttrs
* @param entity
*/
function constructSchema(statements, entity) {
const { schemaAttrs } = Schema[entity];
const members = [];
const members2 = [];
const { [entity]: manyToOneSet } = ManyToOne;
const { [entity]: oneToManySet } = OneToMany;
const referenceEntities = [];
for (const attr of schemaAttrs) {
const { type, name, questionToken } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity, attrName2]) => refEntity === text2 && attrName2 === attrName);
if (manyToOneItem) {
referenceEntities.push(text2);
members2.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, text2, 'Schema')),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createTypeReferenceNode(createForeignRef(entity, text2, 'Schema'))));
const foreignKey = `${attrName}Id`;
members.push(factory.createPropertySignature(undefined, factory.createIdentifier(foreignKey), questionToken, questionToken ? factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('ForeignKey'), [
factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2)))
]),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createTypeReferenceNode(factory.createIdentifier('ForeignKey'), [
factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2)))
])));
}
else {
// assert(types.includes(text), `${entity}中的属性${name.toString()}有非法的属性类型定义`);
// 处理entity这种特殊情况
if (ReversePointerRelations[entity] && attrName === 'entity') {
const entityUnionTypeNode = ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(ele))));
if (process.env.COMPLING_AS_LIB) {
// 如果是建立 base-domain还要容纳可能的其它对象引用
entityUnionTypeNode.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
members.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
factory.createUnionTypeNode(entityUnionTypeNode),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createUnionTypeNode(entityUnionTypeNode)));
}
else {
members.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
type,
factory.createLiteralTypeNode(factory.createNull())
]) : type));
}
}
}
else {
(0, assert_1.default)(false); // 这是什么case不确定
members.push(attr);
}
}
else {
// assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${entity}有非法的属性类型定义${(<ts.Identifier>name).text}`);
members.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
type,
factory.createLiteralTypeNode(factory.createNull())
]) : type));
}
}
// 处理reverserPointer
const reverseOnes = ReversePointerRelations[entity];
if (reverseOnes) {
reverseOnes.forEach((one) => {
referenceEntities.push(one);
members2.push(factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(one), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one, 'Schema'))));
});
}
const foreignKeySet = {};
if (oneToManySet) {
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (referenceEntities.indexOf(entityName) === -1) {
referenceEntities.push(entityName);
}
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
members2.push(factory.createPropertySignature(undefined, identifier, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Schema'), undefined)])));
});
}
}
(0, lodash_1.uniq)(referenceEntities).forEach((ele) => {
if (ele !== entity) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamespaceImport(factory.createIdentifier(ele))), factory.createStringLiteral(`../${ele}/Schema`)));
}
});
// 在这里把需要直接拷贝过来的语句写入
if (SchemaAsts[entity]) {
statements.push(...SchemaAsts[entity].statements);
}
statements.push(factory.createTypeAliasDeclaration([
factory.createModifier(ts.SyntaxKind.ExportKeyword)
], factory.createIdentifier('OpSchema'), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode('EntityShape'),
factory.createTypeLiteralNode(members)
])), factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpAttr"), undefined, factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined))));
const otmAggrMappedNodes = [];
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const typeAliasIdentifier = `${entityName}$${foreignKey}Aggr`;
const typeStringLiterals = [`${entityNameLc}$${foreignKey}$$aggr`];
let i = 0;
while (i < 10) {
typeStringLiterals.push(`${entityNameLc}$${foreignKey}$$${i}$$aggr`);
i++;
}
statements.push(factory.createTypeAliasDeclaration(undefined, factory.createIdentifier(typeAliasIdentifier), undefined, factory.createUnionTypeNode(typeStringLiterals.map(literal => factory.createLiteralTypeNode(factory.createStringLiteral(literal))))));
otmAggrMappedNodes.push(factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("A"), factory.createTypeReferenceNode(factory.createIdentifier(typeAliasIdentifier), undefined), undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(factory.createIdentifier("AggregationResult"), [factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Schema'), undefined)]), undefined));
});
}
statements.push(factory.createTypeAliasDeclaration([
factory.createModifier(ts.SyntaxKind.ExportKeyword)
], factory.createIdentifier('Schema'), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode('OpSchema'),
factory.createTypeLiteralNode(members2),
factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("A"), factory.createTypeReferenceNode(factory.createIdentifier("ExpressionKey"), undefined), undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), undefined),
...otmAggrMappedNodes,
])));
}
function _constructOpSchema(statements, entity) {
const { schemaAttrs } = Schema[entity];
const members = [];
const members2 = [];
const { [entity]: manyToOneSet } = ManyToOne;
const { [entity]: oneToManySet } = OneToMany;
const referenceEntities = [];
for (const attr of schemaAttrs) {
const { type, name, questionToken } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity, attrName2]) => refEntity === text2 && attrName2 === attrName);
if (manyToOneItem) {
referenceEntities.push(text2);
members2.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, text2, 'Schema')),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createTypeReferenceNode(createForeignRef(entity, text2, 'Schema'))));
const foreignKey = `${attrName}Id`;
members.push(factory.createPropertySignature(undefined, factory.createIdentifier(foreignKey), questionToken, questionToken ? factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('ForeignKey'), [
factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2)))
]),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createTypeReferenceNode(factory.createIdentifier('ForeignKey'), [
factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2)))
])));
}
else {
// assert(types.includes(text), `${entity}中的属性${name.toString()}有非法的属性类型定义`);
// 处理entity这种特殊情况
if (ReversePointerRelations[entity] && attrName === 'entity') {
const entityUnionTypeNode = ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(ele))));
if (process.env.COMPLING_AS_LIB) {
// 如果是建立 base-domain还要容纳可能的其它对象引用
entityUnionTypeNode.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
members.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
factory.createUnionTypeNode(entityUnionTypeNode),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createUnionTypeNode(entityUnionTypeNode)));
}
else {
members.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
type,
factory.createLiteralTypeNode(factory.createNull())
]) : type));
}
}
}
else {
(0, assert_1.default)(false); // 这是什么case不确定
members.push(attr);
}
}
else {
// assert(ts.isUnionTypeNode(type!) || ts.isLiteralTypeNode(type!), `${entity}有非法的属性类型定义${(<ts.Identifier>name).text}`);
members.push(factory.createPropertySignature(undefined, name, questionToken, questionToken ? factory.createUnionTypeNode([
type,
factory.createLiteralTypeNode(factory.createNull())
]) : type));
}
}
// 在这里把需要直接拷贝过来的语句写入
if (SchemaAsts[entity]) {
statements.push(...SchemaAsts[entity].statements);
}
statements.push(factory.createTypeAliasDeclaration([
factory.createModifier(ts.SyntaxKind.ExportKeyword)
], factory.createIdentifier('OpSchema'), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode('EntityShape'),
factory.createTypeLiteralNode(members),
factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("A"), factory.createTypeReferenceNode(factory.createIdentifier("ExpressionKey"), undefined), undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), undefined),
])), factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpAttr"), undefined, factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined))));
}
/**
* 生成Query
* @param statements
* @param schemaAttrs
* @param entity
*/
function constructFilter(statements, entity) {
const { schemaAttrs, fulltextIndex, enumAttributes } = Schema[entity];
const members = [
// id: Q_StringValue
factory.createPropertySignature(undefined, factory.createIdentifier('id'), undefined, factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('Q_StringValue')),
/* factory.createTypeReferenceNode(
factory.createQualifiedName(
factory.createIdentifier("SubQuery"),
factory.createIdentifier(`${entity}IdSubQuery`)
)
) */
])),
// $$createAt$$: Q_DateValue
factory.createPropertySignature(undefined, factory.createIdentifier('$$createAt$$'), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_DateValue'))),
// $$seq$$: Q_StringValue
factory.createPropertySignature(undefined, factory.createIdentifier('$$seq$$'), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_NumberValue'))),
// $$updateAt$$: Q_DateValue
factory.createPropertySignature(undefined, factory.createIdentifier('$$updateAt$$'), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_DateValue')))
];
const { [entity]: manyToOneSet } = ManyToOne;
const entityUnionTypeNodes = ReversePointerRelations[entity] && ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(ele))));
if (process.env.COMPLING_AS_LIB) {
entityUnionTypeNodes && entityUnionTypeNodes.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
let type2;
switch (text) {
case 'String':
case 'Text':
case 'Image':
case 'File': {
if (ReversePointerRelations[entity] && attrName === 'entity') {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_EnumValue'), [
factory.createUnionTypeNode(entityUnionTypeNodes)
]);
}
else {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_StringValue'));
}
break;
}
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Price':
case 'Decimal': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_NumberValue'));
break;
}
case 'Boolean': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_BooleanValue'));
break;
}
case 'Datetime': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_DateValue'));
break;
}
case 'SingleGeo':
case 'Geo': {
// geo类型暂时只支持通过expr查询
break;
}
case 'Object': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Object'));
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
// 外键可能落到相应的子查询中
members.push(factory.createPropertySignature(undefined, `${name.text}Id`, undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_StringValue'))));
type2 = factory.createTypeReferenceNode(createForeignRef(entity, text2, 'Filter'));
}
else if (enumAttributes && enumAttributes[attrName] || ts.isUnionTypeNode(type)) {
// 这里应该都是引用某个UnionType类型的定义了如何判断
// const words = getStringTextFromUnionStringLiterals();
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_EnumValue'), [
factory.createTypeReferenceNode(factory.createIdentifier(text), undefined)
]);
}
else {
// 非枚举类型的非结构化属性用JSONFilter来形式化
type2 = factory.createTypeReferenceNode(factory.createIdentifier('JsonFilter'), [
factory.createTypeReferenceNode(factory.createIdentifier(text), undefined)
]);
}
}
}
if (type2) {
members.push(factory.createPropertySignature(undefined, name, undefined, type2));
}
}
}
else if (ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type)) {
members.push(factory.createPropertySignature(undefined, name, undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_EnumValue'), [
type
])));
}
else {
// 此时应当是引用本地定义的shape
(0, assert_1.default)(type, `${entity}」的属性「${name.toString()}」缺少类型定义。\n` +
`提示每个属性都必须明确指定类型name: String<32>`);
members.push(factory.createPropertySignature(undefined, name, undefined, factory.createTypeReferenceNode(factory.createIdentifier('JsonFilter'), [
type
])));
}
}
// type AttrFilter = {};
if (ReversePointerRelations[entity]) {
// 有反向指针将反向指针关联的对象的Filter也注入
ReversePointerRelations[entity].forEach(ele => members.push(factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(ele), undefined, factory.createTypeReferenceNode(createForeignRef(entity, ele, 'Filter')))));
}
// 一对多的生成子查询
const { [entity]: oneToManySet } = OneToMany;
if (oneToManySet) {
const foreignKeySet = {};
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
members.push(factory.createPropertySignature(undefined, identifier, undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Filter'), undefined),
factory.createTypeReferenceNode('SubQueryPredicateMetadata')
])));
});
}
}
statements.push(factory.createTypeAliasDeclaration(undefined, factory.createIdentifier('AttrFilter'), undefined, factory.createTypeLiteralNode(members)));
/**
*
export type Filter = AttrFilter | Partial<ExprOp<OpSchema> | {
[F in Q_LogicKey]: Filter[];
} | {
[F in Q_FullTextKey]: Q_FullTextValue;
}>;
*/
const types = [
factory.createTypeReferenceNode(factory.createIdentifier("AttrFilter")),
factory.createTypeReferenceNode(factory.createIdentifier("ExprOp"), [
process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr')),
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) :
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr'))
]),
];
// 如果还有其它类型的查询如全文则加在types数组中
if (fulltextIndex) {
types.push(factory.createTypeReferenceNode('FulltextFilter'));
}
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Filter"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("MakeFilter"), [factory.createIntersectionTypeNode(types)])));
}
function _constructOpFilter(statements, entity) {
const { schemaAttrs, fulltextIndex, enumAttributes } = Schema[entity];
const members = [
// id: Q_StringValue
factory.createPropertySignature(undefined, factory.createIdentifier('id'), undefined, factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('Q_StringValue')),
/* factory.createTypeReferenceNode(
factory.createQualifiedName(
factory.createIdentifier("SubQuery"),
factory.createIdentifier(`${entity}IdSubQuery`)
)
) */
])),
// $$createAt$$: Q_DateValue
factory.createPropertySignature(undefined, factory.createIdentifier('$$createAt$$'), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_DateValue'))),
// $$seq$$: Q_StringValue
factory.createPropertySignature(undefined, factory.createIdentifier('$$seq$$'), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_NumberValue'))),
// $$updateAt$$: Q_DateValue
factory.createPropertySignature(undefined, factory.createIdentifier('$$updateAt$$'), undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_DateValue')))
];
const entityUnionTypeNodes = ReversePointerRelations[entity] && ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(ele))));
if (process.env.COMPLING_AS_LIB) {
entityUnionTypeNodes && entityUnionTypeNodes.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
let type2;
switch (text) {
case 'String':
case 'Text':
case 'Image':
case 'File': {
if (ReversePointerRelations[entity] && attrName === 'entity') {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_EnumValue'), [
factory.createUnionTypeNode(entityUnionTypeNodes)
]);
}
else {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_StringValue'));
}
break;
}
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Price':
case 'Decimal': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_NumberValue'));
break;
}
case 'Boolean': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_BooleanValue'));
break;
}
case 'Datetime': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_DateValue'));
break;
}
case 'SingleGeo':
case 'Geo': {
// geo类型暂时只支持通过expr查询
break;
}
case 'Object': {
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Object'));
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
// 外键可能落到相应的子查询中
members.push(factory.createPropertySignature(undefined, `${name.text}Id`, undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_StringValue'))));
}
else if (enumAttributes && enumAttributes[attrName] || ts.isUnionTypeNode(type)) {
// 这里应该都是引用某个UnionType类型的定义了如何判断
// const words = getStringTextFromUnionStringLiterals();
type2 = factory.createTypeReferenceNode(factory.createIdentifier('Q_EnumValue'), [
factory.createTypeReferenceNode(factory.createIdentifier(text), undefined)
]);
}
else {
// 非枚举类型的非结构化属性用JSONFilter来形式化
type2 = factory.createTypeReferenceNode(factory.createIdentifier('JsonFilter'), [
factory.createTypeReferenceNode(factory.createIdentifier(text), undefined)
]);
}
}
}
if (type2) {
members.push(factory.createPropertySignature(undefined, name, undefined, type2));
}
}
}
else if (ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type)) {
members.push(factory.createPropertySignature(undefined, name, undefined, factory.createTypeReferenceNode(factory.createIdentifier('Q_EnumValue'), [
type
])));
}
else {
// 此时应当是引用本地定义的shape
(0, assert_1.default)(type, `${entity}中的属性${name.toString()}有非法的属性类型定义`);
members.push(factory.createPropertySignature(undefined, name, undefined, factory.createTypeReferenceNode(factory.createIdentifier('JsonFilter'), [
type
])));
}
}
const types = [
factory.createTypeLiteralNode(members),
factory.createTypeReferenceNode(factory.createIdentifier("ExprOp"), [
process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr')),
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) :
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr'))
]),
];
// 如果还有其它类型的查询如全文则加在types数组中
if (fulltextIndex) {
types.push(factory.createTypeReferenceNode('FulltextFilter'));
}
statements.push(factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpFilter"), undefined, factory.createIntersectionTypeNode(types)));
}
/**
* 构造Projection和OneAttrProjection
* @param statements
* @param entity
*/
function constructProjection(statements, entity) {
const { schemaAttrs, enumAttributes } = Schema[entity];
const properties = [
['id', false],
['$$createAt$$', false],
['$$updateAt$$', false],
['$$seq$$', false],
];
const foreignKeyProperties = {
[entity]: [''],
};
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'SingleGeo':
case 'Geo':
case 'Price':
case 'Decimal': {
properties.push([name, false]);
break;
}
case 'Object': {
properties.push([name, false, factory.createUnionTypeNode([
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
factory.createTypeReferenceNode(factory.createIdentifier("Object"), undefined)
])]);
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
// 外键投影
properties.push([`${attrName}Id`, false, undefined], [name, false, factory.createTypeReferenceNode(createForeignRef(entity, text2, 'Projection')) /* , factory.createTypeReferenceNode(
createForeignRef(entity, text2, 'ExportProjection')
) */
]);
if (foreignKeyProperties.hasOwnProperty(text2)) {
foreignKeyProperties[text2].push(attrName);
}
else {
(0, lodash_1.assign)(foreignKeyProperties, {
[text2]: [attrName],
});
}
}
else {
if (!enumAttributes || !enumAttributes[attrName]) {
// 引用的非enum类型shape
properties.push([name, false, factory.createUnionTypeNode([
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
factory.createTypeReferenceNode(factory.createIdentifier("JsonProjection"), [type])
])]);
}
else {
// 引用的enum类型shape
properties.push([name, false, undefined]);
}
}
}
}
}
else {
(0, assert_1.default)(false);
}
}
else {
// 增加了本身object的shape定义
// assert(ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type!));
if (enumAttributes && enumAttributes[attrName] || ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0])) {
properties.push([name, false, undefined]);
}
else {
// 如果是非枚举类型的其它对象的union定义加上JsonProjection
properties.push([name, false, factory.createUnionTypeNode([
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
factory.createTypeReferenceNode(factory.createIdentifier("JsonProjection"), [type])
])]);
}
}
}
if (ReversePointerRelations[entity]) {
ReversePointerRelations[entity].forEach((one) => {
const text2 = one === 'Schema' ? entity : one;
properties.push([(0, string_1.firstLetterLowerCase)(one), false, factory.createTypeReferenceNode(createForeignRef(entity, one, 'Projection')) /* , factory.createTypeReferenceNode(
createForeignRef(entity, one, 'ExportProjection')
) */
]);
if (foreignKeyProperties.hasOwnProperty(one)) {
foreignKeyProperties[text2].push('entity');
}
else {
(0, lodash_1.assign)(foreignKeyProperties, {
[text2]: ['entity'],
});
}
});
}
// 一对多的projection
const otmAggrMappedNodes = [];
const { [entity]: oneToManySet } = OneToMany;
if (oneToManySet) {
const foreignKeySet = {};
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
const typeAliasIdentifier = `${entityName}$${foreignKey}Aggr`;
properties.push([identifier, false,
factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Selection'), undefined),
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier("$entity"), undefined, factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(entityName))))
])
]) /* ,
factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(
createForeignRef(entity, entityName, 'Exportation'),
undefined
),
factory.createTypeLiteralNode([
factory.createPropertySignature(
undefined,
factory.createIdentifier("$entity"),
undefined,
factory.createLiteralTypeNode(factory.createStringLiteral(firstLetterLowerCase(entityName)))
)
])
]) */
]);
otmAggrMappedNodes.push(factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("A"), factory.createTypeReferenceNode(factory.createIdentifier(typeAliasIdentifier), undefined), undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Aggregation'), undefined),
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier("$entity"), undefined, factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(entityName))))
])
]), undefined));
});
}
}
const exprNode = factory.createTypeReferenceNode(factory.createIdentifier("Partial"), [
factory.createTypeReferenceNode(factory.createIdentifier("ExprOp"), [
process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr')),
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) :
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr'))
])
]);
const MetaPropertySignatures = [
factory.createPropertySignature(undefined, factory.createStringLiteral("#id"), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode('NodeId'))
];
if (process.env.COMPLING_AS_LIB) {
MetaPropertySignatures.push(factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("k"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)));
}
// Projection正常查询的投影
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Projection"), undefined, factory.createIntersectionTypeNode([
factory.createTypeLiteralNode(MetaPropertySignatures.concat(properties.map(([n, q, v]) => {
return factory.createPropertySignature(undefined, n, q ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken), v || factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
}))),
exprNode,
...otmAggrMappedNodes,
])));
// ExportProjection下载查询的投影
// 已经废弃。By Xc 2023.01.08
/* statements.push(
factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
factory.createIdentifier("ExportProjection"),
undefined,
factory.createIntersectionTypeNode([
factory.createTypeLiteralNode(
MetaPropertySignaturs.concat(
properties.map(
([n, q, v, v2]) => {
return factory.createPropertySignature(
undefined,
n,
factory.createToken(ts.SyntaxKind.QuestionToken),
v2 || factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
)
}
)
)
),
exprNode,
])
)
); */
// ${Entity}Projection外键查询的专用投影
for (const foreignKey in foreignKeyProperties) {
const identifier = `${foreignKey}IdProjection`;
statements.push(factory.createTypeAliasDeclaration(undefined, factory.createIdentifier(identifier), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OneOf"), [
factory.createTypeLiteralNode(foreignKeyProperties[foreignKey].map((attr) => factory.createPropertySignature(undefined, attr ? factory.createIdentifier(`${attr}Id`) : 'id', undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))))
])));
}
}
function _constructOpProjection(statements, entity) {
const { schemaAttrs, enumAttributes } = Schema[entity];
const properties = [
['id', false],
['$$createAt$$', false],
['$$updateAt$$', false],
['$$seq$$', false],
];
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'SingleGeo':
case 'Geo':
case 'Price':
case 'Decimal': {
properties.push([name, false]);
break;
}
case 'Object': {
properties.push([name, false, factory.createUnionTypeNode([
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
factory.createTypeReferenceNode(factory.createIdentifier("Object"), undefined)
])]);
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
// 外键投影
properties.push([`${attrName}Id`, false, undefined]);
}
else {
if (!enumAttributes || !enumAttributes[attrName]) {
// 引用的非enum类型shape
properties.push([name, false, factory.createUnionTypeNode([
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
factory.createTypeReferenceNode(factory.createIdentifier("JsonProjection"), [type])
])]);
}
else {
// 引用的enum类型shape
properties.push([name, false, undefined]);
}
}
}
}
}
else {
(0, assert_1.default)(false);
}
}
else {
// 增加了本身object的shape定义
// assert(ts.isUnionTypeNode(type!) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type!));
if (enumAttributes && enumAttributes[attrName] || ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0])) {
properties.push([name, false, undefined]);
}
else {
// 如果是非枚举类型的其它对象的union定义加上JsonProjection
properties.push([name, false, factory.createUnionTypeNode([
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
factory.createTypeReferenceNode(factory.createIdentifier("JsonProjection"), [type])
])]);
}
}
}
const exprNode = factory.createTypeReferenceNode(factory.createIdentifier("Partial"), [
factory.createTypeReferenceNode(factory.createIdentifier("ExprOp"), [
process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr')),
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) :
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr'))
])
]);
const MetaPropertySignatures = [
factory.createPropertySignature(undefined, factory.createStringLiteral("#id"), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode('NodeId'))
];
if (process.env.COMPLING_AS_LIB) {
MetaPropertySignatures.push(factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("k"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)));
}
// OpProjection原始属性投影
statements.push(factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpProjection"), undefined, factory.createIntersectionTypeNode([
factory.createTypeLiteralNode(MetaPropertySignatures.concat(properties.map(([n, q, v]) => {
return factory.createPropertySignature(undefined, n, q ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken), v || factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
}))),
exprNode,
])));
}
function getOpProjectionKeys(entity) {
const { schemaAttrs, enumAttributes } = Schema[entity];
const { [entity]: manyToOneSet } = ManyToOne;
const result = [
'id',
'$$createAt$$',
'$$updateAt$$',
'$$seq$$',
];
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const typeStr = typeName.text;
switch (typeStr) {
case 'String':
case 'Text':
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'SingleGeo':
case 'Geo':
case 'Price':
case 'Decimal':
result.push(attrName);
break;
case 'Object':
result.push(attrName);
break;
default: {
const refEntity = typeStr === 'Schema' ? entity : typeStr;
const isManyToOne = manyToOneSet?.some(([e]) => e === refEntity);
if (isManyToOne) {
result.push(`${attrName}Id`);
}
else if (!enumAttributes?.[attrName]) {
result.push(attrName);
}
else {
result.push(attrName);
}
}
}
}
}
else {
result.push(attrName);
}
}
return result;
}
/**
* 构造Query
* @param statements
* @param entity
*/
function constructQuery(statements, entity) {
const entityLc = (0, string_1.firstLetterLowerCase)(entity);
/* statements.push(
factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
factory.createIdentifier("Query"),
undefined,
factory.createIntersectionTypeNode([
factory.createTypeLiteralNode([
// 这里可以不写entity了
factory.createPropertySignature(
undefined,
factory.createIdentifier("projection"),
undefined,
factory.createTypeReferenceNode(
factory.createIdentifier("Projection"),
undefined
)
),
factory.createPropertySignature(
undefined,
factory.createIdentifier("sort"),
factory.createToken(ts.SyntaxKind.QuestionToken),
factory.createTypeReferenceNode(
factory.createIdentifier("Sorter"),
undefined
)
)
]),
factory.createTypeReferenceNode(
factory.createIdentifier("OakFilter"),
[
factory.createLiteralTypeNode(factory.createStringLiteral("select")),
factory.createTypeReferenceNode(
factory.createIdentifier("Filter"),
undefined
)
]
),
factory.createTypeReferenceNode(
factory.createIdentifier("OakOperation"),
[
factory.createTypeReferenceNode(
factory.createIdentifier("Operation"),
undefined
)
]
)
])
)
); */
/**
* export type ExportQuery = {
entity: 'user';
projection: ExportProjection;
filter?: Filter;
sort?: Sorter;
indexFrom?: number;
count?: number;
};
*/
/* statements.push(
factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
factory.createIdentifier("ExportQuery"),
undefined,
factory.createIntersectionTypeNode([
factory.createTypeLiteralNode([
factory.createPropertySignature(
undefined,
factory.createIdentifier("projection"),
undefined,
factory.createTypeReferenceNode(
factory.createIdentifier("ExportProjection"),
undefined
)
),
factory.createPropertySignature(
undefined,
factory.createIdentifier("sort"),
factory.createToken(ts.SyntaxKind.QuestionToken),
factory.createTypeReferenceNode(
factory.createIdentifier("Sorter"),
undefined
)
)
]),
factory.createTypeReferenceNode(
factory.createIdentifier("OakFilter"),
[
factory.createLiteralTypeNode(factory.createStringLiteral("select")),
factory.createTypeReferenceNode(
factory.createIdentifier("Filter"),
undefined
)
]
)
])
)
); */
// 对每个可能的外键的子查询,建立相应的${Entity}IdSubQuery
const { [entity]: manyToOneSet } = ManyToOne;
let manyToSelf = false;
if (manyToOneSet) {
(0, lodash_1.uniqBy)(manyToOneSet, ([a]) => a).forEach(([oneEntity, foreignKey]) => {
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(`${oneEntity}IdSubQuery`), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Selection"), [factory.createTypeReferenceNode(factory.createIdentifier(`${oneEntity}IdProjection`), undefined)])));
if (oneEntity === entity) {
manyToSelf = true;
}
});
}
// 主键可能产生的子查询
if (!manyToSelf) {
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(`${entity}IdSubQuery`), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Selection"), [factory.createTypeReferenceNode(factory.createIdentifier(`${entity}IdProjection`), undefined)])));
}
}
/**
* 构造Sort
* @param statements
* @param entity
*/
function constructSorter(statements, entity) {
const { schemaAttrs } = Schema[entity];
const members = [
// id: 1
factory.createTypeLiteralNode([factory.createPropertySignature(undefined, factory.createIdentifier("id"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))]),
// $$createAt$$: 1
factory.createTypeLiteralNode([factory.createPropertySignature(undefined, factory.createIdentifier("$$createAt$$"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))]),
// $$seq$$: 1
factory.createTypeLiteralNode([factory.createPropertySignature(undefined, factory.createIdentifier("$$seq$$"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))]),
// $$updateAt$$: 1
factory.createTypeLiteralNode([factory.createPropertySignature(undefined, factory.createIdentifier("$$updateAt$$"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))]),
];
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name, questionToken } = attr;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
let type2;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'Price': {
type2 = factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
type2 = factory.createTypeReferenceNode(createForeignRef(entity, text2, 'SortAttr'));
members.push(factory.createTypeLiteralNode([factory.createPropertySignature(undefined, factory.createIdentifier(`${name.text}Id`), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))]));
}
else if (!['Object'].includes(text)) {
// todo 对State的专门处理
type2 = factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
}
}
}
if (type2) {
members.push(factory.createTypeLiteralNode([factory.createPropertySignature(undefined, name, undefined, type2)]));
}
}
}
else if (ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type)) {
members.push(factory.createTypeLiteralNode([factory.createPropertySignature(undefined, name, undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))]));
}
else {
// 本地规定的shape非结构化属性不参与排序
}
}
if (ReversePointerRelations[entity]) {
ReversePointerRelations[entity].forEach((one) => {
members.push(factory.createTypeLiteralNode([factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(one), undefined, factory.createTypeReferenceNode(createForeignRef(entity, one, 'SortAttr')))]));
});
}
if (process.env.COMPLING_AS_LIB) {
members.push(factory.createTypeLiteralNode([factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("k"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword))]));
}
members.push(factory.createTypeReferenceNode(factory.createIdentifier("OneOf"), [factory.createTypeReferenceNode(factory.createIdentifier("ExprOp"), [
process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr')),
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) :
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr'))
])]));
/**
*
export type SortAttr = {
id: 1;
} | {
$$createAt$$: 1;
} | {
$$updateAt$$: 1;
} | {
modiId: 1;
} | {
modi: Modi.SortAttr;
} | {
[k: string]: any;
} | OneOf<ExprOp<OpAttr>>
*/
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("SortAttr"), undefined, factory.createUnionTypeNode(members)));
/**
* export type SortNode = {
$attr: SortAttr;
$direction?: 'asc' | 'desc';
};
*/
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("SortNode"), undefined, factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier("$attr"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("SortAttr"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("$direction"), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
factory.createLiteralTypeNode(factory.createStringLiteral("asc")),
factory.createLiteralTypeNode(factory.createStringLiteral("desc"))
]))
])));
/**
* export type Sorter = SortNode[];
*/
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Sorter"), undefined, factory.createArrayTypeNode(factory.createTypeReferenceNode(factory.createIdentifier("SortNode"), undefined))));
}
function _constructOpSortAttr(statements, entity) {
const { schemaAttrs } = Schema[entity];
const members = [
// id: 1
factory.createPropertySignature(undefined, factory.createIdentifier("id"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
// $$createAt$$: 1
factory.createPropertySignature(undefined, factory.createIdentifier("$$createAt$$"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
// $$seq$$: 1
factory.createPropertySignature(undefined, factory.createIdentifier("$$seq$$"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
// $$updateAt$$: 1
factory.createPropertySignature(undefined, factory.createIdentifier("$$updateAt$$"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
];
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name, questionToken } = attr;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
let type2;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'Price': {
type2 = factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
members.push(factory.createPropertySignature(undefined, factory.createIdentifier(`${name.text}Id`), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)));
}
else if (!['Object'].includes(text)) {
// todo 对State的专门处理
type2 = factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
}
}
}
if (type2) {
members.push(factory.createPropertySignature(undefined, name, undefined, type2));
}
}
}
else if (ts.isUnionTypeNode(type) && ts.isLiteralTypeNode(type.types[0]) || ts.isLiteralTypeNode(type)) {
members.push(factory.createPropertySignature(undefined, name, undefined, factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)));
}
else {
// 本地规定的shape非结构化属性不参与排序
}
}
if (process.env.COMPLING_AS_LIB) {
members.push(factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("k"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)));
}
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpSortAttr"), undefined, factory.createTypeReferenceNode('Partial', [
factory.createUnionTypeNode([
factory.createTypeLiteralNode(members),
factory.createTypeReferenceNode(factory.createIdentifier("ExprOp"), [
process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr')),
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) :
factory.createTypeReferenceNode(factory.createIdentifier('OpAttr'))
])
])
])));
}
function constructOperations(statements, entity) {
// Selection
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("SelectOperation"), [
factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("P"), factory.createTypeReferenceNode(factory.createIdentifier("Object"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined))
], factory.createTypeReferenceNode(factory.createIdentifier("OakSelection"), [
factory.createLiteralTypeNode(factory.createStringLiteral("select")),
factory.createTypeReferenceNode(factory.createIdentifier("P"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined)
])), factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Selection"), [
factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("P"), factory.createTypeReferenceNode(factory.createIdentifier("Object"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined))
], factory.createTypeReferenceNode(factory.createIdentifier("SelectOperation"), [
factory.createTypeReferenceNode(factory.createIdentifier("P"), undefined)
])), factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Aggregation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("DeduceAggregation"), [
factory.createTypeReferenceNode(factory.createIdentifier("Projection"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined)
])));
// Exportation
// 已经废弃by Xc 2023.01.08
/* statements.push(
factory.createTypeAliasDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
factory.createIdentifier("Exportation"),
undefined,
factory.createTypeReferenceNode(
factory.createIdentifier("OakOperation"),
[
factory.createLiteralTypeNode(factory.createStringLiteral("export")),
factory.createTypeReferenceNode(
factory.createIdentifier("ExportProjection"),
undefined
),
factory.createTypeReferenceNode(
factory.createIdentifier("Filter"),
undefined
),
factory.createTypeReferenceNode(
factory.createIdentifier("Sorter"),
undefined
)
]
)
)
); */
const { [entity]: manyToOneSet } = ManyToOne;
const { [entity]: oneToManySet } = OneToMany;
const foreignKeySet = {};
if (oneToManySet) {
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
}
// CreateOperationData
let foreignKeyAttr = [];
if (ReversePointerEntities[entity]) {
foreignKeyAttr.push('entity', 'entityId');
}
if (manyToOneSet) {
for (const one of manyToOneSet) {
if (one[1] !== 'entity') {
foreignKeyAttr.push(`${one[1]}Id`);
}
}
}
let adNodes = [
factory.createTypeReferenceNode(factory.createIdentifier("FormCreateData"), [
foreignKeyAttr.length > 0
? factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined),
factory.createUnionTypeNode((0, lodash_1.uniq)(foreignKeyAttr).map(ele => factory.createLiteralTypeNode(factory.createStringLiteral(ele))))
])
: factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined)
])
];
if (manyToOneSet) {
/**
* create的多对一有两种case
* 如果关联对象是create则对象的外键由关联对象的id决定
* 如果关联对象是update则关联对象的filter由对象决定其主键
* 见cascadeStore
*/
const upsertOneNodes = [];
for (const one of manyToOneSet) {
if (one[1] !== 'entity') {
const oneEntity = one[0];
const cascadeCreateNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), one[2] ? factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'CreateSingleOperation')))
]);
const cascadeUpdateNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), undefined, factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createLiteralTypeNode(factory.createStringLiteral(one[1]))])),
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'UpdateOperation')))
]);
const noCascadeNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), one[2] ? factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createLiteralTypeNode(factory.createStringLiteral(one[1]))]))
]);
if (Schema[oneEntity].static) {
upsertOneNodes.push(noCascadeNode);
}
else {
switch (Schema[oneEntity].actionType) {
case 'crud':
case 'excludeRemove': {
upsertOneNodes.push(factory.createUnionTypeNode([cascadeCreateNode, cascadeUpdateNode, noCascadeNode]));
break;
}
case 'excludeUpdate':
case 'appendOnly': {
upsertOneNodes.push(factory.createUnionTypeNode([cascadeCreateNode, noCascadeNode]));
break;
}
case 'readOnly': {
upsertOneNodes.push(noCascadeNode);
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
}
}
if (upsertOneNodes.length > 0) {
adNodes.push(factory.createIntersectionTypeNode(upsertOneNodes));
}
}
const reverseOneNodes = [];
if (ReversePointerEntities[entity]) {
if (ReversePointerRelations[entity]) {
const { schemaAttrs } = Schema[entity];
const { questionToken: entityQuestionToken } = schemaAttrs.find(ele => {
const { name } = ele;
return name.text === 'entity';
});
const { questionToken: entityIdQuestionToken } = schemaAttrs.find(ele => {
const { name } = ele;
return name.text === 'entityId';
});
for (const one of ReversePointerRelations[entity]) {
const cascadeCreateNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), entityQuestionToken || entityIdQuestionToken, factory.createTypeReferenceNode(createForeignRef(entity, one, 'CreateSingleOperation')))
]);
const cascadeUpdateNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), entityQuestionToken, factory.createLiteralTypeNode(factory.createStringLiteral(`${(0, string_1.firstLetterLowerCase)(one)}`))),
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), entityIdQuestionToken, factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createLiteralTypeNode(factory.createStringLiteral(one))])),
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')))
]);
const noCascadeNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), entityQuestionToken, factory.createLiteralTypeNode(factory.createStringLiteral(`${(0, string_1.firstLetterLowerCase)(one)}`))),
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), entityIdQuestionToken, factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createLiteralTypeNode(factory.createStringLiteral(one))])),
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))
]);
if (Schema[one].static) {
reverseOneNodes.push(noCascadeNode);
}
else {
switch (Schema[one].actionType) {
case 'crud':
case 'excludeRemove': {
reverseOneNodes.push(cascadeCreateNode, cascadeUpdateNode, noCascadeNode);
break;
}
case 'appendOnly':
case 'excludeUpdate': {
reverseOneNodes.push(cascadeCreateNode, noCascadeNode);
break;
}
case 'readOnly': {
reverseOneNodes.push(noCascadeNode);
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
}
}
if (process.env.COMPLING_AS_LIB) {
// 如果是base要包容更多可能的反指
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("K"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword))
]));
}
if (reverseOneNodes.length > 0) {
adNodes.push(factory.createUnionTypeNode(reverseOneNodes));
}
}
// 一对多
const propertySignatures = [];
if (oneToManySet) {
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
const otmCreateOperationDataNode = factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'CreateOperationData'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
]);
const otmCreateSingleOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("create")),
otmCreateOperationDataNode
]);
const otmCreateMultipleOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("create")),
factory.createArrayTypeNode(otmCreateOperationDataNode)
]);
const otmUpdateOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createIndexedAccessTypeNode(factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'UpdateOperation'), undefined), factory.createLiteralTypeNode(factory.createStringLiteral("action"))),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'UpdateOperationData'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
]),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Filter'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
])
]);
if (!Schema[entityName].static) {
switch (Schema[entityName].actionType) {
case 'crud': {
propertySignatures.push(factory.createPropertySignature(undefined, factory.createIdentifier(identifier), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
otmUpdateOperationNode,
otmCreateMultipleOperationNode,
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createUnionTypeNode([
otmCreateSingleOperationNode,
otmUpdateOperationNode
])])
])));
break;
}
case 'appendOnly':
case 'excludeUpdate': {
propertySignatures.push(factory.createPropertySignature(undefined, factory.createIdentifier(identifier), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
otmCreateMultipleOperationNode,
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [otmCreateSingleOperationNode])
])));
break;
}
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
});
}
}
if (propertySignatures.length > 0) {
adNodes.push(factory.createTypeLiteralNode(propertySignatures));
}
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("CreateOperationData"), undefined, factory.createIntersectionTypeNode(adNodes)));
// CreateOperation
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("CreateSingleOperation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("create")),
factory.createTypeReferenceNode(factory.createIdentifier("CreateOperationData"))
])), factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("CreateMultipleOperation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("create")),
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createTypeReferenceNode(factory.createIdentifier("CreateOperationData"))])
])), factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("CreateOperation"), undefined, factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("CreateSingleOperation")),
factory.createTypeReferenceNode(factory.createIdentifier("CreateMultipleOperation"))
])));
// UpdateOperationData
foreignKeyAttr = [];
if (ReversePointerRelations[entity]) {
foreignKeyAttr.push('entity', 'entityId');
}
if (manyToOneSet) {
for (const one of manyToOneSet) {
if (one[1] !== 'entity') {
foreignKeyAttr.push(`${one[1]}Id`);
}
}
}
adNodes = [
factory.createTypeReferenceNode(factory.createIdentifier("FormUpdateData"), [
foreignKeyAttr.length > 0 ? factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined),
factory.createUnionTypeNode((0, lodash_1.uniq)(foreignKeyAttr).map(ele => factory.createLiteralTypeNode(factory.createStringLiteral(ele))))
]) : factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined)
])
];
if (manyToOneSet) {
/**
* update的多对一有三种case
* 如果关联对象是create则对象的外键由关联对象的id决定
* 如果关联对象是update或者remove则关联对象的filter由对象(的原行!注意这里的外键是不能变的!)决定其主键
* 见cascadeStore
*/
const upsertOneNodes = [];
for (const one of manyToOneSet) {
if (one[1] !== 'entity') {
const cascadeCreateNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'CreateSingleOperation'))),
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
]);
const cascadeUpdateNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'UpdateOperation'))),
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
]);
const cascadeRemoveNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'RemoveOperation'))),
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
]);
const noCascadeNode = factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier(`${one[1]}Id`), factory.createToken(ts.SyntaxKind.QuestionToken), one[2] ? factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createLiteralTypeNode(factory.createStringLiteral(one[1]))]),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createLiteralTypeNode(factory.createStringLiteral(one[1]))])),
]);
if (Schema[one[0]].static) {
upsertOneNodes.push(noCascadeNode);
}
else {
switch (Schema[one[0]].actionType) {
case 'crud': {
upsertOneNodes.push(factory.createUnionTypeNode([cascadeCreateNode, cascadeUpdateNode, cascadeRemoveNode, noCascadeNode]));
break;
}
case 'excludeUpdate': {
upsertOneNodes.push(factory.createUnionTypeNode([cascadeCreateNode, cascadeRemoveNode, noCascadeNode]));
break;
}
case 'appendOnly': {
upsertOneNodes.push(factory.createUnionTypeNode([cascadeCreateNode, noCascadeNode]));
break;
}
case 'readOnly': {
upsertOneNodes.push(noCascadeNode);
break;
}
case 'excludeRemove': {
upsertOneNodes.push(factory.createUnionTypeNode([cascadeCreateNode, cascadeUpdateNode, noCascadeNode]));
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
}
}
if (upsertOneNodes.length > 0) {
adNodes.push(factory.createIntersectionTypeNode(upsertOneNodes));
}
const reverseOneNodes = [];
if (ReversePointerRelations[entity]) {
const refEntityLitrals = [];
for (const one of ReversePointerRelations[entity]) {
refEntityLitrals.push(factory.createLiteralTypeNode(factory.createStringLiteral(`${(0, string_1.firstLetterLowerCase)(one)}`)));
const actionNodes = [];
if (!Schema[one].static) {
switch (Schema[one].actionType) {
case 'crud': {
actionNodes.push(factory.createTypeReferenceNode(createForeignRef(entity, one, 'CreateSingleOperation')), factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')), factory.createTypeReferenceNode(createForeignRef(entity, one, 'RemoveOperation')));
break;
}
case 'excludeUpdate': {
actionNodes.push(factory.createTypeReferenceNode(createForeignRef(entity, one, 'CreateSingleOperation')), factory.createTypeReferenceNode(createForeignRef(entity, one, 'RemoveOperation')));
break;
}
case 'excludeRemove': {
actionNodes.push(factory.createTypeReferenceNode(createForeignRef(entity, one, 'CreateSingleOperation')), factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')));
break;
}
case 'appendOnly': {
actionNodes.push(factory.createTypeReferenceNode(createForeignRef(entity, one, 'CreateSingleOperation')));
break;
}
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
if (actionNodes.length > 0) {
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode(actionNodes)),
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)),
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))
]));
}
}
const { schemaAttrs } = Schema[entity];
const { questionToken: entityQuestionToken } = schemaAttrs.find(ele => {
const { name } = ele;
return name.text === 'entity';
});
const { questionToken: entityIdQuestionToken } = schemaAttrs.find(ele => {
const { name } = ele;
return name.text === 'entityId';
});
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier('entity'), factory.createToken(ts.SyntaxKind.QuestionToken), !entityQuestionToken ? factory.createUnionTypeNode([
// 如果是作为lib要包容更多可能的反指
factory.createUnionTypeNode(process.env.COMPLING_AS_LIB ? refEntityLitrals.concat(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)) : refEntityLitrals),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createUnionTypeNode(process.env.COMPLING_AS_LIB ? refEntityLitrals.concat(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)) : refEntityLitrals)),
factory.createPropertySignature(undefined, factory.createIdentifier('entityId'), factory.createToken(ts.SyntaxKind.QuestionToken), !entityIdQuestionToken ? factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createUnionTypeNode(ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral(ele))))]),
factory.createLiteralTypeNode(factory.createNull())
]) : factory.createTypeReferenceNode(factory.createIdentifier("ForeignKey"), [factory.createUnionTypeNode(ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral(ele))))]))
].concat(ReversePointerRelations[entity].map((one) => factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))))));
}
if (reverseOneNodes.length > 0) {
adNodes.push(factory.createUnionTypeNode(reverseOneNodes));
}
}
const propertySignatures2 = [];
if (process.env.COMPLING_AS_LIB) {
propertySignatures2.push(factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("k"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)));
}
if (oneToManySet) {
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
const otmCreateOperationDataNode = factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'CreateOperationData'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
]);
const otmCreateSingleOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("create")),
otmCreateOperationDataNode
]);
const otmCreateMultipleOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("create")),
factory.createArrayTypeNode(otmCreateOperationDataNode)
]);
const otmUpdateOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createIndexedAccessTypeNode(factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'UpdateOperation'), undefined), factory.createLiteralTypeNode(factory.createStringLiteral("action"))),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'UpdateOperationData'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
]),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Filter'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
])
]);
const otmRemoveOperationNode = factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createIndexedAccessTypeNode(factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'RemoveOperation'), undefined), factory.createLiteralTypeNode(factory.createStringLiteral("action"))),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'RemoveOperationData'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
]),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
factory.createTypeReferenceNode(createForeignRef(entity, entityName, 'Filter'), undefined),
factory.createUnionTypeNode(foreignKey === 'entity' ? [
factory.createLiteralTypeNode(factory.createStringLiteral("entity")),
factory.createLiteralTypeNode(factory.createStringLiteral("entityId"))
] : [
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey)),
factory.createLiteralTypeNode(factory.createStringLiteral(`${foreignKey}Id`))
])
])
]);
if (!Schema[entityName].static) {
switch (Schema[entityName].actionType) {
case 'crud': {
propertySignatures2.push(factory.createPropertySignature(undefined, factory.createIdentifier(identifier), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
otmUpdateOperationNode,
otmRemoveOperationNode,
otmCreateMultipleOperationNode,
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createUnionTypeNode([
otmCreateSingleOperationNode,
otmUpdateOperationNode,
otmRemoveOperationNode
])])
])));
break;
}
case 'excludeUpdate': {
propertySignatures2.push(factory.createPropertySignature(undefined, factory.createIdentifier(identifier), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
otmRemoveOperationNode,
otmCreateMultipleOperationNode,
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createUnionTypeNode([
otmCreateSingleOperationNode,
otmRemoveOperationNode
])])
])));
break;
}
case 'excludeRemove': {
propertySignatures2.push(factory.createPropertySignature(undefined, factory.createIdentifier(identifier), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
otmUpdateOperationNode,
otmCreateMultipleOperationNode,
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createUnionTypeNode([
otmCreateSingleOperationNode,
otmUpdateOperationNode
])])
])));
break;
}
case 'appendOnly': {
propertySignatures2.push(factory.createPropertySignature(undefined, factory.createIdentifier(identifier), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
otmCreateMultipleOperationNode,
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [factory.createUnionTypeNode([
otmCreateSingleOperationNode
])])
])));
break;
}
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
});
}
}
if (propertySignatures2.length > 0) {
adNodes.push(factory.createTypeLiteralNode(propertySignatures2));
}
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("UpdateOperationData"), undefined, factory.createIntersectionTypeNode(adNodes)));
// UpdateOperation
const actionTypeNodes = [factory.createLiteralTypeNode(factory.createStringLiteral("update"))];
if (ActionAsts[entity]) {
actionTypeNodes.push(factory.createTypeReferenceNode('ParticularAction'));
}
if (Schema[entity].relations || entity === 'User') {
actionTypeNodes.push(factory.createTypeReferenceNode('RelationAction'));
}
if (process.env.COMPLING_AS_LIB) {
actionTypeNodes.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("UpdateOperation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createUnionTypeNode(actionTypeNodes),
factory.createTypeReferenceNode(factory.createIdentifier("UpdateOperationData")),
factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined)
])));
// RemoveOperationData
adNodes = [
factory.createTypeLiteralNode([])
];
if (manyToOneSet) {
/**
* remove的多对一有两种case
* 如果关联对象动作是update或者remove则关联对象的filter由对象(的原行!注意这里的外键是不能变的!)决定其主键
* 见cascadeStore
*/
const upsertOneNodes = [];
for (const one of manyToOneSet) {
if (one[1] !== 'entity') {
if (!Schema[one[0]].static) {
switch (Schema[one[0]].actionType) {
case 'crud': {
upsertOneNodes.push(factory.createUnionTypeNode([
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'UpdateOperation')),
factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'RemoveOperation'))
]))
])
]));
break;
}
case 'excludeUpdate': {
upsertOneNodes.push(factory.createUnionTypeNode([
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'RemoveOperation')))
])
]));
break;
}
case 'excludeRemove': {
upsertOneNodes.push(factory.createUnionTypeNode([
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier(one[1]), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one[0], 'UpdateOperation')))
])
]));
break;
}
case 'appendOnly':
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
}
}
const reverseOneNodes = [];
if (ReversePointerRelations[entity]) {
const refEntityLitrals = [];
for (const one of ReversePointerRelations[entity]) {
refEntityLitrals.push(factory.createLiteralTypeNode(factory.createStringLiteral(`${(0, string_1.firstLetterLowerCase)(one)}`)));
if (!Schema[one].static) {
switch (Schema[one].actionType) {
case 'crud': {
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')),
factory.createTypeReferenceNode(createForeignRef(entity, one, 'RemoveOperation'))
]))
]));
break;
}
case 'excludeUpdate': {
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one, 'RemoveOperation')))
]));
break;
}
case 'excludeRemove': {
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier((0, string_1.firstLetterLowerCase)(one)), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(createForeignRef(entity, one, 'UpdateOperation')))
]));
break;
}
case 'appendOnly':
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
}
if (process.env.COMPLING_AS_LIB) {
reverseOneNodes.push(factory.createTypeLiteralNode([
factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("k"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword))
]));
}
}
if (upsertOneNodes.length > 0) {
adNodes.push(factory.createIntersectionTypeNode(upsertOneNodes));
}
if (reverseOneNodes.length > 0) {
adNodes.push(factory.createUnionTypeNode(reverseOneNodes));
}
}
/**
* remove的同时进行cascade update或者cascade remove感觉用触发器会更自然因为在用户界面上似乎不会有对应的操作。
* 这部分代码暂时封闭 by Xc 20220501
**/
/* const propertySignatures3: ts.TypeElement[] = [];
if (process.env.COMPLING_AS_LIB) {
propertySignatures3.push(
factory.createIndexSignature(
undefined,
undefined,
[factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier("k"),
undefined,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
undefined
)],
factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
)
);
}
if (oneToManySet) {
for (const entityName in foreignKeySet) {
const entityNameLc = firstLetterLowerCase(entityName);
foreignKeySet[entityName].forEach(
(foreignKey) => {
const identifier = `${entityNameLc}s$${foreignKey}`;
propertySignatures3.push(
factory.createPropertySignature(
undefined,
factory.createIdentifier(identifier),
factory.createToken(ts.SyntaxKind.QuestionToken),
factory.createUnionTypeNode([
factory.createTypeReferenceNode(
createForeignRef(entity, entityName, 'UpdateOperation'),
undefined
),
factory.createTypeReferenceNode(
createForeignRef(entity, entityName, 'RemoveOperation'),
undefined
),
factory.createTypeReferenceNode(
factory.createIdentifier("Array"),
[factory.createUnionTypeNode([
factory.createTypeReferenceNode(
createForeignRef(entity, entityName, 'UpdateOperation'),
undefined
),
factory.createTypeReferenceNode(
createForeignRef(entity, entityName, 'RemoveOperation'),
undefined
)
])]
)
])
)
);
}
);
}
}
if (propertySignatures3.length > 0) {
adNodes.push(
factory.createTypeLiteralNode(
propertySignatures3
)
);
} */
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("RemoveOperationData"), undefined, factory.createIntersectionTypeNode(adNodes)));
// RemoveOperation
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("RemoveOperation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral("remove")),
factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperationData"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Filter"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("Sorter"), undefined)
])));
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("Operation"), undefined, factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("CreateOperation"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("UpdateOperation"), undefined),
factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperation"), undefined)
])));
}
const initialStatements = (level) => [
// import { String, Text, Int, SpecificKey } from 'oak-domain/types/DataType';
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier('PrimaryKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ForeignKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('JsonProjection'))
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}DataType`)),
/* import {
Q_DateValue, Q_LogicKey, Q_NumberValue, FnCallKey, FnCallValue,
Q_StringValue, Q_FullTextKey, Q_FullTextValue, FnCallValueAs,
Q_BooleanValue,
} from 'oak-domain/types/Demand'; */
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_DateValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_BooleanValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_NumberValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_StringValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_EnumValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('NodeId')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('MakeFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('FulltextFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ExprOp')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ExpressionKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('JsonFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('SubQueryPredicateMetadata')),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}Demand`)),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OneOf")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ValueOf"))
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}Polyfill`)),
// import * as SubQuery from '../_SubQuery';
/* factory.createImportDeclaration(
undefined,
undefined,
factory.createImportClause(
false,
undefined,
factory.createNamespaceImport(factory.createIdentifier("SubQuery"))
),
factory.createStringLiteral("../_SubQuery")
), */
// import { Filter as OakFilter } from 'oak-domain/src/types/Entity';
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("FormCreateData")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("FormUpdateData")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("DeduceAggregation")),
factory.createImportSpecifier(false, factory.createIdentifier("Operation"), factory.createIdentifier("OakOperation")),
factory.createImportSpecifier(false, factory.createIdentifier("Selection"), factory.createIdentifier("OakSelection")),
factory.createImportSpecifier(false, factory.createIdentifier("MakeAction"), factory.createIdentifier("OakMakeAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("AggregationResult")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityShape")),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}Entity`), undefined)
];
const initialStatements2 = () => [
// import { String, Text, Int, SpecificKey } from 'oak-domain/types/DataType';
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier('PrimaryKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ForeignKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('JsonProjection'))
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)()}DataType`)),
/* import {
Q_DateValue, Q_LogicKey, Q_NumberValue, FnCallKey, FnCallValue,
Q_StringValue, Q_FullTextKey, Q_FullTextValue, FnCallValueAs,
Q_BooleanValue,
} from 'oak-domain/types/Demand'; */
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_DateValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_BooleanValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_NumberValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_StringValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('Q_EnumValue')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('NodeId')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('MakeFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('FulltextFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ExprOp')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ExpressionKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('JsonFilter')),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)()}Demand`)),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, factory.createIdentifier("MakeAction"), factory.createIdentifier("OakMakeAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityShape")),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)()}Entity`), undefined)
];
const initialStatements3 = (level) => [
// import { String, Text, Int, SpecificKey } from 'oak-domain/types/DataType';
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier('PrimaryKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ForeignKey')),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}DataType`)),
/* import {
Q_DateValue, Q_LogicKey, Q_NumberValue, FnCallKey, FnCallValue,
Q_StringValue, Q_FullTextKey, Q_FullTextValue, FnCallValueAs,
Q_BooleanValue,
} from 'oak-domain/types/Demand'; */
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier('NodeId')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('MakeFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('FulltextFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ExprOp')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('ExpressionKey')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('JsonFilter')),
factory.createImportSpecifier(false, undefined, factory.createIdentifier('SubQueryPredicateMetadata')),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}Demand`)),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("FormCreateData")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("FormUpdateData")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("DeduceAggregation")),
factory.createImportSpecifier(false, factory.createIdentifier("Operation"), factory.createIdentifier("OakOperation")),
factory.createImportSpecifier(false, factory.createIdentifier("Selection"), factory.createIdentifier("OakSelection")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("AggregationResult")),
])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(level)}Entity`), undefined)
];
function outputSubQuery(outputDir, printer) {
const statements = [];
if (process.env.COMPLING_AS_LIB) {
}
for (const entity in Schema) {
// import * as User from '../User/Schema';
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamespaceImport(factory.createIdentifier(entity))), factory.createStringLiteral(`./${entity}/Schema`)));
}
const entities = (0, lodash_1.keys)(Schema);
// 每个有manyToOne的Entity都会输出${One}IdSubQuery
for (const one in Schema) {
const identifier = `${one}IdSubQuery`;
const fromEntites = OneToMany[one] ? (0, lodash_1.uniq)(OneToMany[one]
/* .filter(
([e, f]) => f !== 'entity'
) */ .map(([e]) => e)) : [];
fromEntites.push(one);
const inUnionTypeNode = fromEntites.map(ele => factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(ele), factory.createIdentifier(identifier)), undefined),
factory.createTypeLiteralNode([factory.createPropertySignature(undefined, factory.createIdentifier("entity"), undefined, factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(ele))))])
]));
if (process.env.COMPLING_AS_LIB) {
// 如果是建立 base这里要加上额外可能的对象信息
inUnionTypeNode.push(factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword));
}
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(identifier), undefined, factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("K"), factory.createUnionTypeNode([
factory.createLiteralTypeNode(factory.createStringLiteral("$in")),
factory.createLiteralTypeNode(factory.createStringLiteral("$nin"))
]), undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode(inUnionTypeNode), undefined)));
}
const resultFile = ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS);
const result = printer.printNode(ts.EmitHint.Unspecified, factory.createSourceFile(statements, factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None), resultFile);
const fileName = path_1.default.join(outputDir, '_SubQuery.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
function outputEntityDict(outputDir, printer) {
const statements = [];
const propertySignatures = [];
for (const entity in Schema) {
// import * as User from '../User/Schema';
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("EntityDef"), factory.createIdentifier(entity))])), factory.createStringLiteral(`./${entity}/Schema`)));
const entityLc = (0, string_1.firstLetterLowerCase)(entity);
propertySignatures.push(factory.createPropertySignature(undefined, factory.createIdentifier(entityLc), undefined, factory.createTypeReferenceNode(entity)));
}
if ( /* process.env.COMPLING_AS_LIB */false) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityDef"))])), factory.createStringLiteral("../types/Entity"), undefined), factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("EntityDict"), undefined, factory.createIntersectionTypeNode([
factory.createTypeLiteralNode(propertySignatures),
factory.createTypeLiteralNode([
factory.createIndexSignature(undefined, [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("E"), undefined, factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined)], factory.createTypeReferenceNode(factory.createIdentifier("EntityDef"), undefined))
])
])));
}
else {
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("EntityDict"), undefined, factory.createTypeLiteralNode(propertySignatures)));
}
const resultFile = ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS);
const result = printer.printNode(ts.EmitHint.Unspecified, factory.createSourceFile(statements, factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None), resultFile);
const fileName = path_1.default.join(outputDir, 'EntityDict.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
function outputSchema(outputDir, printer) {
for (const entity in Schema) {
const { importAttrFrom } = Schema[entity];
const statements = initialStatements();
if (ActionAsts[entity]) {
const { importStateFrom } = ActionAsts[entity];
const fromLocalActionSpecifiers = ['Action', 'ParticularAction'];
const fromExtenalStates = {};
for (const state in importStateFrom) {
(0, assert_1.default)(state.endsWith('State'), `${state} should end with State`);
if (importStateFrom[state][0] === './') {
// 本地定义的State从 ./Action 中获取
fromLocalActionSpecifiers.push(state);
}
else {
// 否则从原来的import specifiers当中获取
// todo 在additionalImports中如果有则去重
const [from, propertyName] = importStateFrom[state];
if (fromExtenalStates[from]) {
fromExtenalStates[from].push([state, propertyName]);
}
else {
fromExtenalStates[from] = [[state, propertyName]];
}
}
}
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromLocalActionSpecifiers.map(ele => factory.createImportSpecifier(false, undefined, factory.createIdentifier(ele))))), factory.createStringLiteral('./Action'), undefined), factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("RelationAction")),
])), factory.createStringLiteral((0, env_1.ACTION_CONSTANT_IN_OAK_DOMAIN)()), undefined));
for (const external in fromExtenalStates) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromExtenalStates[external].map(ele => factory.createImportSpecifier(false, ele[1] === undefined ? undefined : factory.createIdentifier(ele[1]), factory.createIdentifier(ele[0]))))), factory.createStringLiteral(external), undefined));
}
}
else {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("GenericAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("AppendOnlyAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ReadOnlyAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ExcludeUpdateAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ExcludeRemoveAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("RelationAction")),
])), factory.createStringLiteral((0, env_1.ACTION_CONSTANT_IN_OAK_DOMAIN)()), undefined));
}
// 从外部引入的属性
const fromExternalImportAttrs = {};
for (const attr in importAttrFrom) {
const [from, propertyName] = importAttrFrom[attr];
if (fromExternalImportAttrs[from]) {
fromExternalImportAttrs[from].push([attr, propertyName]);
}
else {
fromExternalImportAttrs[from] = [[attr, propertyName]];
}
}
for (const external in fromExternalImportAttrs) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromExternalImportAttrs[external].map(ele => factory.createImportSpecifier(false, ele[1] === undefined ? undefined : factory.createIdentifier(ele[1]), factory.createIdentifier(ele[0]))))), factory.createStringLiteral(external), undefined));
}
// Relation定义加入
/* if (typeof Schema[entity].hasRelationDef === 'object' && ts.isTypeAliasDeclaration(Schema[entity].hasRelationDef as ts.Node)) {
const node = Schema[entity].hasRelationDef as ts.TypeAliasDeclaration;
statements.push(
factory.updateTypeAliasDeclaration(
node,
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
node.name,
node.typeParameters,
node.type
)
);
} */
constructSchema(statements, entity);
constructFilter(statements, entity);
constructProjection(statements, entity);
constructSorter(statements, entity);
constructOperations(statements, entity);
constructQuery(statements, entity);
// 现在FullAttrs和NativeAttrs似乎没什么用还会引起递归
// constructFullAttrs(statements, entity);
const makeActionArguments = [];
if (ActionAsts[entity]) {
makeActionArguments.push(factory.createTypeReferenceNode('Action'));
}
else {
makeActionArguments.push(factory.createTypeReferenceNode(OriginActionDict[Schema[entity].actionType]));
}
if (Schema[entity].relations || entity === 'User') {
makeActionArguments.push(factory.createTypeReferenceNode('RelationAction'));
}
const actionTypeNode = factory.createTypeReferenceNode(factory.createIdentifier('OakMakeAction'), makeActionArguments.length === 1 ? makeActionArguments : [factory.createUnionTypeNode(makeActionArguments)]);
const EntityDefAttrs = [
factory.createPropertySignature(undefined, factory.createIdentifier("Schema"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Schema"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("OpSchema"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("Action"), undefined, process.env.COMPLING_AS_LIB ?
factory.createUnionTypeNode([
actionTypeNode,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
]) : actionTypeNode),
factory.createPropertySignature(undefined, factory.createIdentifier("Selection"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Selection"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("Aggregation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Aggregation"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("Operation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("Operation"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("Create"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("CreateOperation"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("Update"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("UpdateOperation"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("Remove"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("RemoveOperation"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("CreateSingle"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("CreateSingleOperation"), undefined)),
factory.createPropertySignature(undefined, factory.createIdentifier("CreateMulti"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("CreateMultipleOperation"), undefined)),
];
if (ActionAsts[entity]) {
EntityDefAttrs.push(factory.createPropertySignature(undefined, factory.createIdentifier("ParticularAction"), undefined, factory.createTypeReferenceNode(factory.createIdentifier('ParticularAction'), undefined)));
}
/* if (typeof Schema[entity].hasRelationDef === 'object' && ts.isTypeAliasDeclaration(Schema[entity].hasRelationDef as ts.Node)) {
EntityDefAttrs.push(
factory.createPropertySignature(
undefined,
factory.createIdentifier("Relation"),
undefined,
factory.createTypeReferenceNode(
factory.createIdentifier('Relation'),
undefined
)
)
);
} */
statements.push(factory.createTypeAliasDeclaration([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("EntityDef"), undefined, factory.createTypeLiteralNode(EntityDefAttrs)));
// 现在支持Schema extends其继承的对象解析来自另一个文件故不能再用原sourceFile来生成否则printer会用sourceFile中的originText去取stringLiteral
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), undefined);
const fileName = path_1.default.join(outputDir, entity, 'Schema.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
}
function _outputBaseSchema(outputDir, printer) {
for (const entity in Schema) {
const { importAttrFrom } = Schema[entity];
const statements = initialStatements2();
if (ActionAsts[entity]) {
const { importStateFrom } = ActionAsts[entity];
const fromLocalActionSpecifiers = ['Action', 'ParticularAction'];
const fromExtenalStates = {};
for (const state in importStateFrom) {
(0, assert_1.default)(state.endsWith('State'), `${state} should end with State`);
if (importStateFrom[state][0] === './') {
// 本地定义的State从 ./Action 中获取
fromLocalActionSpecifiers.push(state);
}
else {
// 否则从原来的import specifiers当中获取
// todo 在additionalImports中如果有则去重
const [from, propertyName] = importStateFrom[state];
if (fromExtenalStates[from]) {
fromExtenalStates[from].push([state, propertyName]);
}
else {
fromExtenalStates[from] = [[state, propertyName]];
}
}
}
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromLocalActionSpecifiers.map(ele => factory.createImportSpecifier(false, undefined, factory.createIdentifier(ele))))), factory.createStringLiteral('./Action'), undefined), factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("RelationAction")),
])), factory.createStringLiteral((0, env_1.ACTION_CONSTANT_IN_OAK_DOMAIN)()), undefined));
for (const external in fromExtenalStates) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromExtenalStates[external].map(ele => factory.createImportSpecifier(false, ele[1] === undefined ? undefined : factory.createIdentifier(ele[1]), factory.createIdentifier(ele[0]))))), factory.createStringLiteral(external), undefined));
}
}
else {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("GenericAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("AppendOnlyAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ReadOnlyAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ExcludeUpdateAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("ExcludeRemoveAction")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("RelationAction")),
])), factory.createStringLiteral((0, env_1.ACTION_CONSTANT_IN_OAK_DOMAIN)()), undefined));
}
// 从外部引入的属性
const fromExternalImportAttrs = {};
for (const attr in importAttrFrom) {
// const [from, propertyName] = importAttrFrom[attr];
// if (fromExternalImportAttrs[from]) {
// fromExternalImportAttrs[from].push([attr, propertyName]);
// }
// else {
// fromExternalImportAttrs[from] = [[attr, propertyName]];
// }
const [from, propertyName, sourceFilePath, relative] = importAttrFrom[attr];
// ===== 在这里进行路径转换 =====
const outputFilePath = path_1.default.join(outputDir, entity, '_baseSchema.ts'); // 输出文件路径
// 使用路径解析算法计算新的导入路径
const newImportPath = resolveCompiledImportPath(path_1.default.relative(process.cwd(), sourceFilePath), path_1.default.relative(process.cwd(), outputFilePath), from, // 原始导入路径
".", relative);
// console.log('resolve import path:', {
// sourceFilePath: PathLib.relative(process.cwd(), sourceFilePath),
// outputFilePath: PathLib.relative(process.cwd(), outputFilePath),
// from,
// newImportPath,
// });
if (fromExternalImportAttrs[newImportPath]) {
fromExternalImportAttrs[newImportPath].push([attr, propertyName]);
}
else {
fromExternalImportAttrs[newImportPath] = [[attr, propertyName]];
}
}
for (const external in fromExternalImportAttrs) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromExternalImportAttrs[external].map(ele => factory.createImportSpecifier(false, ele[1] === undefined ? undefined : factory.createIdentifier(ele[1]), factory.createIdentifier(ele[0]))))), factory.createStringLiteral(external), undefined));
}
_constructOpSchema(statements, entity);
_constructOpFilter(statements, entity);
_constructOpProjection(statements, entity);
_constructOpSortAttr(statements, entity);
const makeActionArguments = [];
const makeUpdateActionArguments = [factory.createLiteralTypeNode(factory.createStringLiteral('update'))];
if (ActionAsts[entity]) {
makeActionArguments.push(factory.createTypeReferenceNode('Action'));
makeUpdateActionArguments.push(factory.createTypeReferenceNode('ParticularAction'));
}
else {
makeActionArguments.push(factory.createTypeReferenceNode(OriginActionDict[Schema[entity].actionType]));
}
if (Schema[entity].relations || entity === 'User') {
makeActionArguments.push(factory.createTypeReferenceNode('RelationAction'));
}
if (process.env.COMPLING_AS_LIB) {
makeActionArguments.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
makeUpdateActionArguments.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
statements.push(factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpAction"), undefined, factory.createTypeReferenceNode(factory.createIdentifier('OakMakeAction'), [factory.createUnionTypeNode(makeActionArguments)])), factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("OpUpdateAction"), undefined, factory.createUnionTypeNode(makeUpdateActionArguments)));
// 现在支持Schema extends其继承的对象解析来自另一个文件故不能再用原sourceFile来生成否则printer会用sourceFile中的originText去取stringLiteral
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), undefined);
const fileName = path_1.default.join(outputDir, entity, '_baseSchema.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
}
function outputAction(outputDir, printer) {
const actionDictStatements = [];
const propertyAssignments = [];
for (const entity in ActionAsts) {
const { sourceFile, statements, importActionFrom, importStateFrom, importActionDefFrom, actionDefNames } = ActionAsts[entity];
const importStatements = [];
const fromExternalImports = {};
for (const state in importStateFrom) {
(0, assert_1.default)(state.endsWith('State'), `${state} should end with State`);
const [from, propertyName] = importStateFrom[state];
if (from !== './') {
if (fromExternalImports[from]) {
fromExternalImports[from].push([state, propertyName]);
}
else {
fromExternalImports[from] = [[state, propertyName]];
}
}
}
for (const action in importActionFrom) {
(0, assert_1.default)(action.endsWith('Action'), `${action} should end with Action`);
const [from, propertyName] = importActionFrom[action];
if (from !== './') {
if (fromExternalImports[from]) {
fromExternalImports[from].push([action, propertyName]);
}
else {
fromExternalImports[from] = [[action, propertyName]];
}
}
}
for (const def in importActionDefFrom) {
(0, assert_1.default)(def.endsWith('ActionDef'), `${def} should end with ActionDef`);
const [from, propertyName] = importActionDefFrom[def];
if (from !== './') {
if (fromExternalImports[from]) {
fromExternalImports[from].push([def, propertyName]);
}
else {
fromExternalImports[from] = [[def, propertyName]];
}
}
}
for (const external in fromExternalImports) {
statements.splice(0, 0, factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromExternalImports[external].map(ele => factory.createImportSpecifier(false, ele[1] === undefined ? undefined : factory.createIdentifier(ele[1]), factory.createIdentifier(ele[0]))))), factory.createStringLiteral(external), undefined));
}
statements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("actionDefDict"), undefined, undefined, factory.createObjectLiteralExpression(actionDefNames.map(ele => factory.createPropertyAssignment(factory.createIdentifier(`${ele}State`), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}ActionDef`))), true))], ts.NodeFlags.Const)));
/* const result = printer.printNode(
ts.EmitHint.Unspecified,
factory.createSourceFile(statements,
factory.createToken(ts.SyntaxKind.EndOfFileToken),
ts.NodeFlags.None),
sourceFile
); */
// 这里如果用printNodestringLiteral的输出始终有个bug不知道如何处理
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(importStatements.concat(statements)), sourceFile);
const filename = path_1.default.join(outputDir, entity, 'Action.ts');
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
actionDictStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("actionDefDict"), factory.createIdentifier((0, string_1.firstLetterLowerCase)(entity)))])), factory.createStringLiteral(`./${entity}/Action`)));
propertyAssignments.push(factory.createShorthandPropertyAssignment(factory.createIdentifier((0, string_1.firstLetterLowerCase)(entity))));
}
actionDictStatements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("actionDefDict"), undefined, undefined, factory.createObjectLiteralExpression(propertyAssignments, true))], ts.NodeFlags.Const)));
const resultFile = ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS);
const result = printer.printNode(ts.EmitHint.Unspecified, factory.createSourceFile(actionDictStatements, factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None), resultFile);
const fileName = path_1.default.join(outputDir, 'ActionDefDict.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
function constructAttributes(entity) {
const { schemaAttrs, enumAttributes } = Schema[entity];
const { [entity]: manyToOneSet } = ManyToOne;
const result = [];
schemaAttrs.forEach((attr) => {
const attrAssignments = [];
const { name, type, questionToken: allowNull } = attr;
if (!allowNull) {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("notNull"), factory.createTrue()));
}
let name2 = name;
if (ts.isTypeReferenceNode(type)) {
const { typeName, typeArguments } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
switch (text) {
case 'String': {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("varchar")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier("length"), factory.createNumericLiteral(typeArguments[0].literal.text)),
], true)));
// 如果是entity在这里处理一下ref
if (ts.isIdentifier(name) && name.text === 'entity') {
const mtoRelations = ReversePointerRelations[entity];
if (mtoRelations) {
const mtoEntities = mtoRelations.map(ele => (0, string_1.firstLetterLowerCase)(ele));
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("ref"), factory.createArrayLiteralExpression(mtoEntities.map(ele => factory.createStringLiteral(ele)), false)));
}
}
break;
}
case 'Text':
case 'Image':
case 'File': {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("text")));
break;
}
case 'Int':
case 'Uint': {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("int")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier("width"), factory.createNumericLiteral(typeArguments[0].literal.text)),
factory.createPropertyAssignment(factory.createIdentifier("signed"), text === 'Uint' ? factory.createFalse() : factory.createTrue())
], true)));
break;
}
case 'Double':
case 'Float':
case 'Decimal': {
if (['Double', 'Float'].includes(text)) {
console.warn(`${entity}对象中还有${text}类型定义现在统一用Decimal进行存储`);
}
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("decimal")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier("precision"), factory.createNumericLiteral(typeArguments[0].literal.text)),
factory.createPropertyAssignment(factory.createIdentifier("scale"), factory.createNumericLiteral(typeArguments[1].literal.text))
], true)));
break;
}
case 'Boolean': {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("boolean")));
break;
}
case 'Price': {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("money")));
break;
}
case 'Datetime': {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("datetime")));
break;
}
case 'SingleGeo':
case 'Geo': {
// object类型暂不支持查询
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("geometry")));
break;
}
case 'Object': {
// object类型暂不支持查询
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("object")));
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity, attrName]) => refEntity === text2 && attrName === attrName);
if (manyToOneItem) {
// 外键
name2 = factory.createIdentifier(`${name.text}Id`);
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("ref")), factory.createPropertyAssignment(factory.createIdentifier("ref"), factory.createStringLiteral((0, string_1.firstLetterLowerCase)(text2))));
}
else {
if (enumAttributes && enumAttributes[name.text]) {
attrAssignments.push(factory.createPropertyAssignment('type', factory.createStringLiteral("enum")), factory.createPropertyAssignment('enumeration', factory.createArrayLiteralExpression(enumAttributes[name.text].map(ele => factory.createStringLiteral(ele)))));
}
else {
// todo 引用的非string定义目前没有处理int类型的引用等遇到了再处理
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("object")));
}
}
}
}
}
else {
(0, assert_1.default)(false);
}
}
else {
if (ts.isUnionTypeNode(type)) {
if (ts.isLiteralTypeNode(type.types[0])) {
if (ts.isStringLiteral(type.types[0].literal)) {
(0, assert_1.default)(enumAttributes && enumAttributes[name.text], `${entity}」的属性「${name.text}」缺少枚举值定义。\n` +
`提示:枚举类型属性需要在 enumAttributes 中定义可选值`);
attrAssignments.push(factory.createPropertyAssignment('type', factory.createStringLiteral("enum")), factory.createPropertyAssignment('enumeration', factory.createArrayLiteralExpression(enumAttributes[name.text].map(ele => factory.createStringLiteral(ele)))));
}
else {
(0, assert_1.default)(ts.isNumericLiteral(type.types[0].literal), `${entity}」的属性类型错误:「${type.types[0].literal.getText()}」不是有效的数字字面量`);
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("int")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier("width"), factory.createNumericLiteral(env_1.INT_LITERL_DEFAULT_WIDTH))
], true)));
}
}
else {
// 否则是本地规定的shape直接用object
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("object")));
}
}
else {
if (ts.isLiteralTypeNode(type)) {
if (ts.isStringLiteral(type.literal)) {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("varchar")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([factory.createPropertyAssignment(factory.createIdentifier("length"), factory.createNumericLiteral(env_1.STRING_LITERAL_MAX_LENGTH))], true)));
}
else {
(0, assert_1.default)(ts.isNumericLiteral(type.literal), `${entity}」的属性「${name.text}」类型错误:必须是数字字面量类型`);
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("precision")), factory.createPropertyAssignment(factory.createIdentifier("params"), factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier("precision"), factory.createNumericLiteral(env_1.NUMERICAL_LITERL_DEFAULT_PRECISION)),
factory.createPropertyAssignment(factory.createIdentifier("scale"), factory.createNumericLiteral(env_1.NUMERICAL_LITERL_DEFAULT_SCALE))
], true)));
}
}
else {
// 否则是本地规定的shape直接用object
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("type"), factory.createStringLiteral("object")));
}
}
}
result.push(factory.createPropertyAssignment(name2, factory.createObjectLiteralExpression(attrAssignments, true)));
});
return result;
}
function translateLocaleObject(locale) {
const result = {};
locale.properties.forEach((ele) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele) && (ts.isIdentifier(ele.name) || ts.isStringLiteral(ele.name)), `locale对象中的属性定义不正确`);
const name = ele.name.text;
if (ts.isStringLiteral(ele.initializer)) {
result[name] = ele.initializer.text;
}
else if (ts.isObjectLiteralExpression(ele.initializer)) {
const subObj = translateLocaleObject(ele.initializer);
result[name] = subObj;
}
else {
throw new Error(`locale对象中的属性${name}的定义不正确`);
}
});
return result;
}
function outputLocale(outputDir, printer) {
const locales = {};
const entities = [];
for (const entity in Schema) {
const { locale, sourceFile } = Schema[entity];
if (locale) {
const { properties } = locale;
properties.forEach((ele) => {
(0, assert_1.default)(ts.isPropertyAssignment(ele) && (ts.isIdentifier(ele.name) || ts.isStringLiteral(ele.name)) && ts.isObjectLiteralExpression(ele.initializer), `${entity}对象中的locale属性定义不正确`);
const lng = ele.name.text;
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray([
factory.createReturnStatement(ele.initializer)
]), sourceFile);
const data = Function(result)();
const filename = path_1.default.join(outputDir, entity, 'locales', `${lng}.json`);
(0, fs_1.writeFileSync)(filename, JSON.stringify(data, null, 2), { flag: 'w' });
if (locales[lng]) {
locales[lng].push(entity);
}
else {
locales[lng] = [entity];
}
});
entities.push(entity);
}
}
for (const lng in locales) {
if (locales[lng].length < entities.length) {
const lack = (0, lodash_1.difference)(entities, locales[lng]);
throw new Error(`${lng}语言定义中缺少了对象${lack.join(',')}的定义,请检查相应的定义文件`);
}
/* const statements: ts.Statement[] = locales[lng].map(
(entity) => factory.createImportDeclaration(
undefined,
undefined,
factory.createImportClause(
false,
factory.createIdentifier(firstLetterLowerCase(entity)),
undefined
),
factory.createStringLiteral(`../${entity}/locales/${lng}`),
undefined
)
);
statements.push(
factory.createExportAssignment(
undefined,
undefined,
undefined,
factory.createObjectLiteralExpression(
locales[lng].map(
ele => factory.createShorthandPropertyAssignment(
factory.createIdentifier(firstLetterLowerCase(ele)),
undefined
)
),
true
)
)
);
const result = printer.printList(
ts.ListFormat.SourceFileStatements,
factory.createNodeArray(statements),
ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS));
const filename = path.join(outputDir, '_locales', `${lng}.ts`);
writeFileSync(filename, result, { flag: 'w' }); */
}
}
function outputStorage(outputDir, printer) {
const importStatements = [
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StorageSchema"))])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(1)}Storage`), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityDict"))])), factory.createStringLiteral("./EntityDict"), undefined)
];
const entityAssignments = [];
for (const entity in Schema) {
const indexExpressions = [];
const { sourceFile, inModi, indexes, toModi, toLog, actionType, static: _static, relations } = Schema[entity];
const fromSchemaSpecifiers = [
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema"))
];
/* if (relationHierarchy || reverseCascadeRelationHierarchy) {
fromSchemaSpecifiers.push(
factory.createImportSpecifier(
false,
undefined,
factory.createIdentifier("Relation")
)
);
} */
const statements = [
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StorageDesc"))])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)()}Storage`), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromSchemaSpecifiers)), factory.createStringLiteral("./Schema"), undefined)
];
const needImportActions = [];
switch (actionType) {
case 'readOnly': {
needImportActions.push(factory.createImportSpecifier(false, factory.createIdentifier("readOnlyActions"), factory.createIdentifier("actions")));
break;
}
case 'appendOnly': {
needImportActions.push(factory.createImportSpecifier(false, factory.createIdentifier("appendOnlyActions"), factory.createIdentifier("actions")));
break;
}
case 'excludeUpdate': {
needImportActions.push(factory.createImportSpecifier(false, factory.createIdentifier("excludeUpdateActions"), factory.createIdentifier("actions")));
break;
}
default: {
if (ActionAsts[entity]) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("actions"))])), factory.createStringLiteral("./Action"), undefined));
}
else {
needImportActions.push(factory.createImportSpecifier(false, factory.createIdentifier("genericActions"), factory.createIdentifier("actions")));
}
}
}
if (Schema[entity].relations || entity === 'User') {
needImportActions.push(factory.createImportSpecifier(false, undefined, factory.createIdentifier("relationActions")));
}
if (needImportActions.length > 0) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports(needImportActions)), factory.createStringLiteral((0, env_1.ACTION_CONSTANT_IN_OAK_DOMAIN)()), undefined));
}
const propertyAssignments = [];
const attributes = constructAttributes(entity);
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("attributes"), factory.createObjectLiteralExpression(attributes, true)));
if (indexes) {
indexExpressions.push(...indexes.elements);
}
if (toModi) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("toModi"), factory.createTrue()));
}
if (toLog) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("toLog"), factory.createTrue()));
}
if (inModi) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("inModi"), factory.createTrue()));
}
if (_static || actionType === 'readOnly') {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("static"), factory.createTrue()));
}
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("actionType"), factory.createStringLiteral(actionType)));
propertyAssignments.push(factory.createShorthandPropertyAssignment(factory.createIdentifier("actions"), undefined));
if (indexExpressions.length > 0) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true)));
}
/* if (relationHierarchy) {
propertyAssignments.push(
factory.createPropertyAssignment(
factory.createIdentifier("relationHierarchy"),
relationHierarchy,
)
);
}
if (reverseCascadeRelationHierarchy) {
propertyAssignments.push(
factory.createPropertyAssignment(
factory.createIdentifier("reverseCascadeRelationHierarchy"),
reverseCascadeRelationHierarchy,
)
);
} */
if (relations) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relation"), factory.createArrayLiteralExpression(relations.map(ele => factory.createStringLiteral(ele)))));
}
const sdTypeArguments = [
factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined)
];
statements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("desc"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StorageDesc"), sdTypeArguments), factory.createObjectLiteralExpression(propertyAssignments, true))], ts.NodeFlags.Const)));
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), sourceFile);
const filename = path_1.default.join(outputDir, entity, 'Storage.ts');
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, factory.createIdentifier("desc"), factory.createIdentifier(`${(0, string_1.firstLetterLowerCase)(entity)}Desc`))
])), factory.createStringLiteral(`./${entity}/Storage`), undefined));
entityAssignments.push(factory.createPropertyAssignment((0, string_1.firstLetterLowerCase)(entity), factory.createIdentifier(`${(0, string_1.firstLetterLowerCase)(entity)}Desc`)));
}
importStatements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("storageSchema"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StorageSchema"), [
factory.createTypeReferenceNode('EntityDict')
]), factory.createObjectLiteralExpression(entityAssignments, true))], ts.NodeFlags.Const)));
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(importStatements), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS));
const filename = path_1.default.join(outputDir, 'Storage.ts');
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
}
function resetOutputDir(outputDir) {
(0, fs_extra_1.emptydirSync)(outputDir);
for (const moduleName in Schema) {
(0, fs_1.mkdirSync)(path_1.default.join(outputDir, moduleName));
(0, fs_1.mkdirSync)(path_1.default.join(outputDir, moduleName, 'locales'));
}
(0, fs_1.mkdirSync)(path_1.default.join(outputDir, '_locales'));
}
function addReverseRelationship() {
for (const reverseEntity in ReversePointerRelations) {
if (!ReversePointerEntities.hasOwnProperty(reverseEntity)) {
throw new Error(`${reverseEntity}」被引用为一个反指对象但其定义中的entity和entityId不符合要求`);
}
for (const one of ReversePointerRelations[reverseEntity]) {
addRelationship(reverseEntity, one, 'entity', false);
}
}
}
function outputIndexTs(outputDir) {
const indexTs = `export * from './EntityDict';
export * from './Storage';
export * from './ActionDefDict';
export * from './Relation';
export * from './StyleDict';
`;
const filename = path_1.default.join(outputDir, 'index.ts');
(0, fs_1.writeFileSync)(filename, indexTs, { flag: 'w' });
}
function outputPackageJson(outputDir) {
const pj = {
"name": process.env.COMPLING_AS_LIB ? "general-app-domain" : "oak-app-domain",
"main": "index.ts"
};
const filename = path_1.default.join(outputDir, 'package.json');
(0, fs_1.writeFileSync)(filename, JSON.stringify(pj), { flag: 'w' });
}
/**
* 从toModi的对象开始分析可能被modi指向的对象
*/
function analyzeInModi() {
const getRelateEntities = (entity) => {
let result = [];
if (ManyToOne[entity]) {
// 用反指指针指向的对象可以忽略,因为前端不可能设计出这样的更新页面
result = ManyToOne[entity].filter(ele => ele[1] !== 'entity').map(ele => ele[0]);
}
if (OneToMany[entity]) {
result.push(...OneToMany[entity].map(ele => ele[0]));
}
return (0, lodash_1.uniq)(result);
};
const setInModi = (entity) => {
if (['Modi', 'ModiEntity', 'Oper', 'OperEntity', 'User'].includes(entity)) {
return;
}
const schema = Schema[entity];
if (schema.toModi || schema.inModi || schema.actionType === 'readOnly' || schema.static) {
return;
}
schema.inModi = true;
const related = getRelateEntities(entity);
related.forEach(ele => setInModi(ele));
};
for (const entity in Schema) {
if (Schema[entity].toModi) {
const related = getRelateEntities(entity);
related.forEach(ele => setInModi(ele));
}
}
}
/**
* 此部分功能不再使用
* @param map
*/
let IGNORED_FOREIGN_KEY_MAP = {};
let IGNORED_RELATION_PATH_MAP = {};
let DEDUCED_RELATION_MAP = {};
let SELECT_FREE_ENTITIES = [];
let CREATE_FREE_ENTITIES = [];
let UPDATE_FREE_ENTITIES = [];
let FIXED_DESTINATION_PATH_MAP = {};
let FIXED_FOR_ALL_DESTINATION_PATH_ENTITIES = [];
/**
* 此函数不再使用
* @param map
*/
function registerIgnoredForeignKeyMap(map) {
IGNORED_FOREIGN_KEY_MAP = map;
}
/**
* 此函数不再使用
* @param map
*/
function registerFreeEntities(selectFreeEntities = [], createFreeEntities = [], updateFreeEntities = []) {
SELECT_FREE_ENTITIES = selectFreeEntities;
CREATE_FREE_ENTITIES = createFreeEntities;
UPDATE_FREE_ENTITIES = updateFreeEntities;
}
/**
* 此函数不再使用
* @param map
*/
function registerIgnoredRelationPathMap(map) {
for (const k in map) {
IGNORED_RELATION_PATH_MAP[(0, string_1.firstLetterUpperCase)(k)] = map[k];
}
}
/**
* 很多路径虽然最后指向同一对象但不能封掉封了会导致查询的时候找不到对应的路径path
* @param map
*/
function registerFixedDestinationPathMap(map) {
for (const k in map) {
if (k === '.') {
FIXED_FOR_ALL_DESTINATION_PATH_ENTITIES.push(...map[k]);
}
else if (FIXED_DESTINATION_PATH_MAP[k]) {
FIXED_DESTINATION_PATH_MAP[k].push(...map[k]);
}
else {
FIXED_DESTINATION_PATH_MAP[k] = map[k];
}
}
}
/**
* 此函数不再使用
* @param map
*/
function registerDeducedRelationMap(map) {
for (const k in map) {
const entity = (0, string_1.firstLetterUpperCase)(k);
(0, assert_1.default)(Schema.hasOwnProperty(entity), `config/relation.ts中配置的DeducedRelationMap包含不合法的对象名称「${k}`);
// 定义的deduce的属性一定是多对一的外键此时ReversePointerEntities还未处理
if (ReversePointerEntities[entity] && map[k] === 'entity') {
}
else {
const mto = ManyToOne[entity].find(ele => ele[1] === map[k]);
(0, assert_1.default)(mto, `config/relation.ts中配置的DeducedRelationMap所定义的「${k}」的deduce属性「${map[k]}」不是一个有效的外键指针`);
}
DEDUCED_RELATION_MAP[entity] = map[k];
}
}
/**
* 输出所有和User相关的对象的后继
* 此函数不再使用
*/
// function outputRelation(outputDir: string, printer: ts.Printer) {
// const ExcludedEntities = ['Oper', 'User', 'OperEntity', 'Modi', 'ModiEntity', 'UserRelation', 'Relation', 'RelationAuth', 'ActionAuth'];
// const actionPath: [string, string, string, boolean, string[]][] = [];
// const relationPath: [string, string, string, boolean][] = [];
// const outputRecursively = (root: string, entity: string, path: string, paths: string[], isRelation: boolean) => {
// if (ExcludedEntities.includes(entity)) {
// return;
// }
// if (IGNORED_RELATION_PATH_MAP[entity]?.find(
// (ele) => path.includes(ele)
// )) {
// return;
// }
// if (paths.length > 12) {
// throw new Error('对象之间的关系深度过长,请优化设计加以避免');
// }
// actionPath.push([firstLetterLowerCase(entity), path, root, isRelation, paths]);
// if (Schema[entity].hasRelationDef) {
// // assert(!DEDUCED_RELATION_MAP[entity], `${entity}对象定义了deducedRelationMap但它有relation`);
// relationPath.push([firstLetterLowerCase(entity), path, root, isRelation]);
// }
// const { [entity]: parent } = OneToMany;
// if (parent) {
// parent.forEach(
// ([child, foreignKey]) => {
// const child2 = firstLetterLowerCase(child);
// if (child === entity) {
// // 如果有层级关系对象最多找3层。同时这里只找本身存在relation关系的对象因为如果对象上没有relation则其上的公共路径应当可以维护住层级关系
// // 例如在jichuang项目中house上没有relation通过其park外键所维护的路径不需要遍历其父亲。而parkCluster因为有relation所以必须构造以之为根的所有的可能路径
// // 如果不是以之为根的同样可以根据其上的公共路径去查找parkCluster.system和parkCluster.parent.system必然是一样的
// if (!Schema[entity].hasRelationDef) {
// return;
// }
// if (paths.find(ele => ele !== child2) || paths.length > 2) {
// return;
// }
// }
// else if (paths.indexOf(child2) >= 0) {
// // 除了层级之外的递归直接忽略
// return;
// }
// if (IGNORED_FOREIGN_KEY_MAP[child2]?.includes(foreignKey)) {
// // 忽略的路径放弃
// return;
// }
// if (DEDUCED_RELATION_MAP[child] === foreignKey) {
// // 如果子对象本身由父对象推定,也放弃
// return;
// }
// const fk = foreignKey === 'entity' ? firstLetterLowerCase(entity) : foreignKey;
// const path2 = path ? `${fk}.${path}` : fk;
// outputRecursively(root, child, path2, paths.concat([firstLetterLowerCase(entity)]), isRelation);
// }
// );
// }
// };
// // 所有属性中有指向user的对象
// const { User } = OneToMany;
// User.forEach(
// ([entity3, foreignKey]) => {
// const fk = foreignKey === 'entity' ? 'user' : foreignKey;
// if (!IGNORED_FOREIGN_KEY_MAP[firstLetterLowerCase(entity3)]?.includes(foreignKey)) {
// outputRecursively(firstLetterLowerCase(entity3), entity3, fk, [fk], false);
// }
// }
// );
// // 所有带relation的对象
// const hasRelationEntities = Object.keys(Schema).filter(
// (entity) => Schema[entity].hasRelationDef
// );
// hasRelationEntities.forEach(
// (entity3) => {
// outputRecursively(firstLetterLowerCase(entity3), entity3, '', [], true);
// }
// );
// actionPath.sort(
// (ele1, ele2) => {
// // 先按sourceEntity来排序
// if (ele1[0] > ele2[0]) {
// return 1;
// }
// else if (ele1[0] < ele2[0]) {
// return -1;
// }
// else {
// // 再按destEntity
// if (ele1[2] > ele2[2]) {
// return 1;
// }
// else if (ele1[2] < ele2[2]) {
// return -1;
// }
// else {
// // 最后按paths的长度倒排
// return ele1[4].length - ele2[4].length;
// }
// }
// }
// );
// const entityRelations: [string, string[]][] = [];
// for (const entity in Schema) {
// const { hasRelationDef } = Schema[entity];
// if (hasRelationDef) {
// const { type } = hasRelationDef;
// if (ts.isUnionTypeNode(type)) {
// const { types } = type;
// const relations = types.map(
// ele => {
// assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal));
// return ele.literal.text;
// }
// );
// entityRelations.push([firstLetterLowerCase(entity), relations]);
// }
// else {
// assert(ts.isLiteralTypeNode(type));
// assert(ts.isStringLiteral(type.literal));
// const relations = [type.literal.text];
// entityRelations.push([firstLetterLowerCase(entity), relations]);
// }
// }
// }
// const stmts: ts.Statement[] = [
// factory.createImportDeclaration(
// undefined,
// factory.createImportClause(
// false,
// undefined,
// factory.createNamedImports([
// factory.createImportSpecifier(
// false,
// undefined,
// factory.createIdentifier("AuthCascadePath")
// ),
// factory.createImportSpecifier(
// false,
// undefined,
// factory.createIdentifier("AuthDeduceRelationMap")
// )
// ])
// ),
// factory.createStringLiteral(`${TYPE_PATH_IN_OAK_DOMAIN(1)}Entity`),
// undefined
// ),
// factory.createImportDeclaration(
// undefined,
// factory.createImportClause(
// false,
// undefined,
// factory.createNamedImports([factory.createImportSpecifier(
// false,
// undefined,
// factory.createIdentifier("EntityDict")
// )])
// ),
// factory.createStringLiteral("./EntityDict"),
// undefined
// ),
// factory.createImportDeclaration(
// undefined,
// factory.createImportClause(
// false,
// undefined,
// factory.createNamedImports([factory.createImportSpecifier(
// false,
// factory.createIdentifier("CreateOperationData"),
// factory.createIdentifier("Relation")
// )])
// ),
// factory.createStringLiteral("./Relation/Schema"),
// undefined
// ),
// factory.createVariableStatement(
// [factory.createToken(ts.SyntaxKind.ExportKeyword)],
// factory.createVariableDeclarationList(
// [factory.createVariableDeclaration(
// factory.createIdentifier("ActionCascadePathGraph"),
// undefined,
// factory.createArrayTypeNode(factory.createTypeReferenceNode(
// factory.createIdentifier("AuthCascadePath"),
// [factory.createTypeReferenceNode(
// factory.createIdentifier("EntityDict"),
// undefined
// )]
// )),
// factory.createArrayLiteralExpression(
// actionPath.map(
// ([entity, path, root, isRelation]) => factory.createArrayLiteralExpression(
// [
// factory.createStringLiteral(entity),
// factory.createStringLiteral(path),
// factory.createStringLiteral(root),
// isRelation ? factory.createTrue() : factory.createFalse()
// ],
// false
// )
// ),
// true
// )
// )],
// ts.NodeFlags.Const
// )
// ),
// factory.createVariableStatement(
// [factory.createToken(ts.SyntaxKind.ExportKeyword)],
// factory.createVariableDeclarationList(
// [factory.createVariableDeclaration(
// factory.createIdentifier("RelationCascadePathGraph"),
// undefined,
// factory.createArrayTypeNode(factory.createTypeReferenceNode(
// factory.createIdentifier("AuthCascadePath"),
// [factory.createTypeReferenceNode(
// factory.createIdentifier("EntityDict"),
// undefined
// )]
// )),
// factory.createArrayLiteralExpression(
// relationPath.map(
// ([entity, path, root, isRelation]) => factory.createArrayLiteralExpression(
// [
// factory.createStringLiteral(entity),
// factory.createStringLiteral(path),
// factory.createStringLiteral(root),
// isRelation ? factory.createTrue() : factory.createFalse()
// ],
// false
// )
// ),
// true
// )
// )],
// ts.NodeFlags.Const
// )
// ),
// factory.createVariableStatement(
// [factory.createToken(ts.SyntaxKind.ExportKeyword)],
// factory.createVariableDeclarationList(
// [factory.createVariableDeclaration(
// factory.createIdentifier("relations"),
// undefined,
// factory.createArrayTypeNode(factory.createTypeReferenceNode(
// factory.createIdentifier("Relation"),
// undefined
// )),
// factory.createArrayLiteralExpression(
// flatten(entityRelations.map(
// ([entity, relations]) => relations.map(
// (relation) => factory.createObjectLiteralExpression(
// [
// factory.createPropertyAssignment(
// factory.createIdentifier("id"),
// factory.createStringLiteral(formUuid(entity, relation))
// ),
// factory.createPropertyAssignment(
// factory.createIdentifier("entity"),
// factory.createStringLiteral(entity)
// ),
// factory.createPropertyAssignment(
// factory.createIdentifier("name"),
// factory.createStringLiteral(relation)
// )
// ],
// true
// )
// )
// )),
// true
// )
// )],
// ts.NodeFlags.Const
// )
// )
// ];
// stmts.push(
// factory.createVariableStatement(
// [
// factory.createToken(ts.SyntaxKind.ExportKeyword)
// ],
// factory.createVariableDeclarationList(
// [
// factory.createVariableDeclaration(
// factory.createIdentifier("deducedRelationMap"),
// undefined,
// factory.createTypeReferenceNode(
// factory.createIdentifier("AuthDeduceRelationMap"),
// [factory.createTypeReferenceNode(
// factory.createIdentifier("EntityDict"),
// undefined
// )]
// ),
// factory.createObjectLiteralExpression(
// Object.keys(DEDUCED_RELATION_MAP).map(
// ele => factory.createPropertyAssignment(
// factory.createIdentifier(firstLetterLowerCase(ele)),
// factory.createStringLiteral(DEDUCED_RELATION_MAP[ele])
// )
// ),
// true
// )
// )
// ],
// ts.NodeFlags.Const
// )
// )
// );
// stmts.push(
// factory.createVariableStatement(
// [
// factory.createToken(ts.SyntaxKind.ExportKeyword)
// ],
// factory.createVariableDeclarationList(
// [factory.createVariableDeclaration(
// factory.createIdentifier("selectFreeEntities"),
// undefined,
// factory.createArrayTypeNode(
// factory.createParenthesizedType(
// factory.createTypeOperatorNode(
// ts.SyntaxKind.KeyOfKeyword,
// factory.createTypeReferenceNode(
// factory.createIdentifier("EntityDict"),
// undefined
// )
// )
// )
// ),
// factory.createArrayLiteralExpression(
// SELECT_FREE_ENTITIES.map(
// ele => factory.createStringLiteral(ele)
// ),
// false
// )
// )],
// ts.NodeFlags.Const
// )
// ),
// factory.createVariableStatement(
// [
// factory.createToken(ts.SyntaxKind.ExportKeyword)
// ],
// factory.createVariableDeclarationList(
// [factory.createVariableDeclaration(
// factory.createIdentifier("updateFreeEntities"),
// undefined,
// factory.createArrayTypeNode(
// factory.createParenthesizedType(
// factory.createTypeOperatorNode(
// ts.SyntaxKind.KeyOfKeyword,
// factory.createTypeReferenceNode(
// factory.createIdentifier("EntityDict"),
// undefined
// )
// )
// )
// ),
// factory.createArrayLiteralExpression(
// UPDATE_FREE_ENTITIES.map(
// ele => factory.createStringLiteral(ele)
// ),
// false
// )
// )],
// ts.NodeFlags.Const
// )
// ),
// factory.createVariableStatement(
// [
// factory.createToken(ts.SyntaxKind.ExportKeyword)
// ],
// factory.createVariableDeclarationList(
// [factory.createVariableDeclaration(
// factory.createIdentifier("createFreeEntities"),
// undefined,
// factory.createArrayTypeNode(
// factory.createParenthesizedType(
// factory.createTypeOperatorNode(
// ts.SyntaxKind.KeyOfKeyword,
// factory.createTypeReferenceNode(
// factory.createIdentifier("EntityDict"),
// undefined
// )
// )
// )
// ),
// factory.createArrayLiteralExpression(
// CREATE_FREE_ENTITIES.map(
// ele => factory.createStringLiteral(ele)
// ),
// false
// )
// )],
// ts.NodeFlags.Const
// )
// )
// );
// const result = printer.printList(
// ts.ListFormat.SourceFileStatements,
// factory.createNodeArray(stmts),
// ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS));
// const filename = PathLib.join(outputDir, 'Relation.ts');
// writeFileSync(filename, result, { flag: 'w' });
// }
/**
* 输出oak-app-domain中的Relation.ts文件
* 不再输出actionAuthGraph和relationAuthGraph两个复杂的对象
* @param outputDir
* @param printer
*/
function outputRelation2(outputDir, printer) {
const entityRelations = [];
const stmts = [
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityDict"))])), factory.createStringLiteral("./EntityDict"), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("CreateOperationData"), factory.createIdentifier("Relation"))])), factory.createStringLiteral("./Relation/Schema"), undefined),
];
const enumDelarations = [];
for (const entity in Schema) {
const { relations } = Schema[entity];
if (relations) {
const entity2 = (0, string_1.firstLetterLowerCase)(entity);
const enumMembers = [];
relations.forEach((relation) => {
const id = (0, uuid_1.formUuid)(entity2, relation);
entityRelations.push([entity2, relation, id]);
enumMembers.push(factory.createEnumMember((0, string_1.firstLetterUpperCase)(relation), factory.createStringLiteral(id)));
});
enumDelarations.push(factory.createEnumDeclaration([
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.ConstKeyword)
], factory.createIdentifier(entity), enumMembers));
}
}
stmts.push(factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("relations"), undefined, factory.createArrayTypeNode(factory.createTypeReferenceNode(factory.createIdentifier("Relation"), undefined)), factory.createArrayLiteralExpression(entityRelations.map(([entity, relation, id]) => factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier("id"), factory.createStringLiteral(id)),
factory.createPropertyAssignment(factory.createIdentifier("entity"), factory.createStringLiteral(entity)),
factory.createPropertyAssignment(factory.createIdentifier("name"), factory.createStringLiteral(relation))
], true)), true))], ts.NodeFlags.Const)));
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(stmts), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS));
const filename = path_1.default.join(outputDir, 'Relation.ts');
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
// 输出RelationIds.ts
const stmts2 = [
factory.createModuleDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("RelationId"), factory.createModuleBlock(enumDelarations), ts.NodeFlags.Namespace),
];
const result2 = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(stmts2), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS));
const filename2 = path_1.default.join(outputDir, 'RelationId.ts');
(0, fs_1.writeFileSync)(filename2, result2, { flag: 'w' });
}
/**
* 输出oak-app-domain中的StyleDict.ts文件
* @param outputDir
* @param printer
*/
function outputStyleDict(outputDir, printer) {
for (const entity in StyleAsts) {
const stmts = [
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("Action")),
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema"))
])), factory.createStringLiteral("./Schema"), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StyleDef"))])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)()}Style`), undefined),
factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("style"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StyleDef"), [
factory.createTypeReferenceNode('OpSchema'),
factory.createTypeReferenceNode('Action')
]), StyleAsts[entity])], ts.NodeFlags.Const))
];
const { sourceFile } = Schema[entity];
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(stmts), sourceFile);
const filename = path_1.default.join(outputDir, entity, 'Style.ts');
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
}
const stmts = [
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityDict"))
])), factory.createStringLiteral("./EntityDict"), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StyleDict"))])), factory.createStringLiteral(`${(0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(1)}Style`), undefined),
...Object.keys(StyleAsts).map((entity) => factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("style"), factory.createIdentifier((0, string_1.firstLetterLowerCase)(entity)))])), factory.createStringLiteral(`./${entity}/Style`), undefined)),
factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("styleDict"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StyleDict"), [factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined)]), factory.createObjectLiteralExpression(Object.keys(StyleAsts).map((entity) => factory.createShorthandPropertyAssignment(factory.createIdentifier((0, string_1.firstLetterLowerCase)(entity)), undefined)), true))], ts.NodeFlags.Const))
];
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(stmts), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS));
const filename = path_1.default.join(outputDir, 'StyleDict.ts');
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
}
const getAnalizedSchema = () => {
return Schema;
};
exports.getAnalizedSchema = getAnalizedSchema;
function analyzeEntities(inputDir, relativePath) {
const files = (0, fs_1.readdirSync)(inputDir);
const fullFilenames = files.map(ele => {
const entity = ele.slice(0, ele.indexOf('.'));
if (env_1.RESERVED_ENTITY_NAMES.includes(entity) || env_1.RESERVED_ENTITY_NAMES.find(ele2 => entity.startsWith(ele2))) {
throw new Error(`${ele}是系统保留字,请勿使用其当对象名或对象名前缀`);
}
return `${inputDir}/${ele}`;
});
const program = ts.createProgram(fullFilenames, { allowJs: true });
files.forEach((filename) => {
analyzeEntity(filename, inputDir, program, relativePath);
});
analyzeInModi();
uniqRelationships();
}
function _getAggrKey(entity, foreignKey) {
const aggrKey = `${entity}$${foreignKey}`;
if (process.env.COMPLING_AS_LIB) {
return `${aggrKey}$$aggr`;
}
return factory.createUnionTypeNode([
factory.createLiteralTypeNode(factory.createStringLiteral(`${aggrKey}$$aggr`)),
factory.createTemplateLiteralType(factory.createTemplateHead(`${aggrKey}$$`, `${aggrKey}$$`), [factory.createTemplateLiteralTypeSpan(factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), factory.createTemplateTail("$$aggr", "$$aggr"))])
]);
}
function _outputEntityDict(outputDir, printer) {
const statements = initialStatements3(1);
for (const entity in Schema) {
statements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamespaceImport(factory.createIdentifier(`Base${entity}`))), factory.createStringLiteral(`./${entity}/_baseSchema`), undefined));
}
for (const entity in Schema) {
const entityLc = (0, string_1.firstLetterLowerCase)(entity);
const getSchema = () => {
const { schemaAttrs } = Schema[entity];
const { [entity]: manyToOneSet } = ManyToOne;
const { [entity]: oneToManySet } = OneToMany;
const members = [];
for (const attr of schemaAttrs) {
const { type, name, questionToken } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity, attrName2]) => refEntity === text2 && attrName2 === attrName);
if (manyToOneItem) {
const foreignKeyNode = createIndexedForeignRef(text2, 'Schema');
members.push(factory.createPropertySignature(undefined, name, questionToken, foreignKeyNode));
}
}
else {
(0, assert_1.default)(false); // 这是什么case不确定
}
}
}
// 处理reverserPointer
const reverseOnes = ReversePointerRelations[entity];
if (reverseOnes) {
reverseOnes.forEach((one) => {
members.push(factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(one), factory.createToken(ts.SyntaxKind.QuestionToken), createIndexedForeignRef(one, 'Schema')));
});
}
const foreignKeySet = {};
if (oneToManySet) {
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
members.push(factory.createPropertySignature(undefined, identifier, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createTypeReferenceNode(factory.createIdentifier("Array"), [
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'Schema'),
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey))
])
])));
});
}
}
const otmAggrMappedNodes = [];
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const aggrKeyNode = _getAggrKey(entityNameLc, foreignKey);
const aggrNode = factory.createTypeReferenceNode(factory.createIdentifier("AggregationResult"), [factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'Schema'),
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey))
])]);
if (typeof aggrKeyNode === 'string') {
members.push(factory.createPropertySignature(undefined, aggrKeyNode, factory.createToken(ts.SyntaxKind.QuestionToken), aggrNode));
}
else {
otmAggrMappedNodes.push(factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("A"), aggrKeyNode, undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), aggrNode, undefined));
}
});
}
return factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSchema"))),
factory.createTypeLiteralNode(members),
...otmAggrMappedNodes,
]);
};
const getProjection = () => {
const { schemaAttrs, enumAttributes } = Schema[entity];
const properties = [];
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'SingleGeo':
case 'Geo':
case 'Price':
case 'Decimal':
case 'Object': {
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
// 外键投影
properties.push([name, false, createIndexedForeignRef(text2, 'Projection')]);
}
}
}
}
else {
(0, assert_1.default)(false);
}
}
}
if (ReversePointerRelations[entity]) {
ReversePointerRelations[entity].forEach((one) => {
const text2 = one === 'Schema' ? entity : one;
properties.push([(0, string_1.firstLetterLowerCase)(one), false, createIndexedForeignRef(text2, 'Projection')]);
});
}
// 一对多的projection
const otmAggrMappedNodes = [];
const { [entity]: oneToManySet } = OneToMany;
if (oneToManySet) {
const foreignKeySet = {};
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
const aggrKey = _getAggrKey(entityNameLc, foreignKey);
properties.push([identifier, false,
factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("OakSelection"), [
factory.createLiteralTypeNode(factory.createStringLiteral("select")),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'Projection'),
factory.createLiteralTypeNode(factory.createStringLiteral(entityLc))
]),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'Filter'),
factory.createLiteralTypeNode(factory.createStringLiteral(entityLc))
]),
createIndexedForeignRef(entityName, 'Sorter')
]),
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier("$entity"), undefined, factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(entityName))))
])
])
]);
const otmAggrNode = factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("DeduceAggregation"), [
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'Projection'),
factory.createLiteralTypeNode(factory.createStringLiteral(entityLc))
]),
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'Filter'),
factory.createLiteralTypeNode(factory.createStringLiteral(entityLc))
]),
createIndexedForeignRef(entityName, 'Sorter')
]),
factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier("$entity"), undefined, factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(entityName))))
])
]);
if (typeof aggrKey === 'string') {
properties.push([
aggrKey,
false,
otmAggrNode
]);
}
else {
otmAggrMappedNodes.push(factory.createMappedTypeNode(undefined, factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("A"), aggrKey, undefined), undefined, factory.createToken(ts.SyntaxKind.QuestionToken), otmAggrNode, undefined));
}
});
}
}
return factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpProjection"))),
factory.createTypeLiteralNode(properties.map(([n, q, v]) => {
return factory.createPropertySignature(undefined, n, q ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken), v || factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
})),
...otmAggrMappedNodes,
]);
};
const getFilterUnit = () => {
const { schemaAttrs, fulltextIndex } = Schema[entity];
const members = [];
const { [entity]: manyToOneSet } = ManyToOne;
const entityUnionTypeNodes = ReversePointerRelations[entity] && ReversePointerRelations[entity].map(ele => factory.createLiteralTypeNode(factory.createStringLiteral((0, string_1.firstLetterLowerCase)(ele))));
if (process.env.COMPLING_AS_LIB) {
entityUnionTypeNodes && entityUnionTypeNodes.push(factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
}
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
let type2;
switch (text) {
case 'String':
case 'Text':
case 'Image':
case 'File':
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Price':
case 'Decimal':
case 'Boolean':
case 'Datetime':
case 'SingleGeo':
case 'Geo':
case 'Object': {
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
type2 = factory.createTypeReferenceNode('MakeFilter', [
createIndexedForeignRef(text2, 'FilterUnit')
]);
}
}
}
if (type2) {
members.push(factory.createPropertySignature(undefined, name, undefined, type2));
}
}
}
}
// type AttrFilter = {};
if (ReversePointerRelations[entity]) {
// 有反向指针将反向指针关联的对象的Filter也注入
ReversePointerRelations[entity].forEach(ele => members.push(factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(ele), undefined, factory.createTypeReferenceNode('MakeFilter', [
createIndexedForeignRef(ele, 'FilterUnit')
]))));
}
// 一对多的生成子查询
const { [entity]: oneToManySet } = OneToMany;
if (oneToManySet) {
const foreignKeySet = {};
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
foreignKeySet[entityName].forEach((foreignKey) => {
const identifier = `${entityNameLc}$${foreignKey}`;
members.push(factory.createPropertySignature(undefined, identifier, undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode('MakeFilter', [
factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [
createIndexedForeignRef(entityName, 'FilterUnit'),
factory.createLiteralTypeNode(factory.createStringLiteral(foreignKey))
])
]),
factory.createTypeReferenceNode('SubQueryPredicateMetadata')
])));
});
}
}
const types = [
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpFilter"))),
factory.createTypeLiteralNode(members)
];
// 如果还有其它类型的查询如全文则加在types数组中
if (fulltextIndex) {
types.push(factory.createTypeReferenceNode('FulltextFilter'));
}
return factory.createIntersectionTypeNode(types);
};
const getSortAttr = () => {
const { schemaAttrs } = Schema[entity];
const members = [];
const { [entity]: manyToOneSet } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name, questionToken } = attr;
if (ts.isTypeReferenceNode(type)) {
const { typeName } = type;
if (ts.isIdentifier(typeName)) {
const { text } = typeName;
let type2;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'Price': {
break;
}
default: {
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet && manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
type2 = createIndexedForeignRef(text2, 'SortAttr');
}
}
}
if (type2) {
members.push(factory.createPropertySignature(undefined, name, undefined, type2));
}
}
}
}
if (ReversePointerRelations[entity]) {
ReversePointerRelations[entity].forEach((one) => {
members.push(factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(one), undefined, createIndexedForeignRef(one, 'SortAttr')));
});
}
return factory.createTypeReferenceNode('Partial', [
factory.createUnionTypeNode([
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSortAttr"))),
factory.createTypeLiteralNode(members),
])
]);
};
const getOperations = () => {
const { [entity]: manyToOneSet } = ManyToOne;
const { [entity]: oneToManySet } = OneToMany;
const foreignKeySet = {};
if (oneToManySet) {
for (const oneToManyItem of oneToManySet) {
const [entityName, foreignKey] = oneToManyItem;
if (foreignKeySet.hasOwnProperty(entityName)) {
foreignKeySet[entityName].push(foreignKey);
}
else {
foreignKeySet[entityName] = [foreignKey];
}
}
}
const cascadeCreateElements = [];
const cascadeUpdateElements = [];
{
const addManyToOneCascade = (foreignEntity, foreignKey) => {
// 切断modiEntity和其它对象的关联太多了
if (entity === 'ModiEntity' && foreignEntity !== 'Modi') {
return;
}
if (entity === 'OperEntity' && foreignEntity !== 'Oper') {
return;
}
const propertyName = foreignKey === 'entity' ? (0, string_1.firstLetterLowerCase)(foreignEntity) : foreignKey;
const createCasNode = factory.createTypeReferenceNode('OakOperation', [
factory.createLiteralTypeNode(factory.createStringLiteral('create')),
createIndexedForeignRef(foreignEntity, 'CreateOperationData')
]);
const updateCasNode = factory.createTypeReferenceNode('OakOperation', [
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${foreignEntity}`), factory.createIdentifier("OpUpdateAction"))),
createIndexedForeignRef(foreignEntity, 'UpdateOperationData'),
createIndexedForeignRef(foreignEntity, 'Filter'),
]);
const removeCasNode = factory.createTypeReferenceNode('OakOperation', [
factory.createLiteralTypeNode(factory.createStringLiteral('remove')),
createIndexedForeignRef(foreignEntity, 'RemoveOperationData'),
createIndexedForeignRef(foreignEntity, 'Filter')
]);
if (!Schema[foreignEntity].static) {
switch (Schema[foreignEntity].actionType) {
case 'crud': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
createCasNode,
updateCasNode
])));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
createCasNode,
updateCasNode,
removeCasNode,
])));
break;
}
case 'excludeRemove': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
createCasNode,
updateCasNode
])));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
createCasNode,
updateCasNode,
])));
break;
}
case 'excludeUpdate': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), createCasNode));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
createCasNode,
removeCasNode,
])));
break;
}
case 'appendOnly': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), createCasNode));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, propertyName, factory.createToken(ts.SyntaxKind.QuestionToken), createCasNode));
break;
}
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
};
// 多对一
if (manyToOneSet) {
for (const one of manyToOneSet) {
addManyToOneCascade(one[0], one[1]);
}
}
// 一对多
const addOneToManyCascade = (foreignEntity, foreignKey) => {
// 切断modiEntity和其它对象的关联太多了
if (foreignEntity === 'ModiEntity' && entity !== 'Modi') {
return;
}
if (foreignEntity === 'OperEntity' && entity !== 'Oper') {
return;
}
const foreignEntityLc = (0, string_1.firstLetterLowerCase)(foreignEntity);
const otmKey = `${foreignEntityLc}$${foreignKey}`;
const mtoKey = foreignKey === 'entity' ? (0, string_1.firstLetterLowerCase)(entity) : foreignKey;
const otmCreateData = factory.createTypeReferenceNode('Omit', [
createIndexedForeignRef(foreignEntity, 'CreateOperationData'),
factory.createLiteralTypeNode(factory.createStringLiteral(mtoKey))
]);
const otmUpdateData = factory.createTypeReferenceNode('Omit', [
createIndexedForeignRef(foreignEntity, 'UpdateOperationData'),
factory.createLiteralTypeNode(factory.createStringLiteral(mtoKey))
]);
const createCasNode = factory.createTypeReferenceNode('OakOperation', [
factory.createLiteralTypeNode(factory.createStringLiteral('create')),
factory.createUnionTypeNode([
otmCreateData,
factory.createArrayTypeNode(otmCreateData)
])
]);
const updateCasNode = factory.createTypeReferenceNode('OakOperation', [
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${foreignEntity}`), factory.createIdentifier("OpUpdateAction"))),
otmUpdateData,
createIndexedForeignRef(foreignEntity, 'Filter'),
]);
const removeCasNode = factory.createTypeReferenceNode('OakOperation', [
factory.createLiteralTypeNode(factory.createStringLiteral('remove')),
createIndexedForeignRef(foreignEntity, 'RemoveOperationData'),
createIndexedForeignRef(foreignEntity, 'Filter')
]);
if (!Schema[foreignEntity].static) {
switch (Schema[foreignEntity].actionType) {
case 'crud': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(factory.createUnionTypeNode([
createCasNode,
updateCasNode
]))));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(factory.createUnionTypeNode([
createCasNode,
updateCasNode,
removeCasNode,
]))));
break;
}
case 'excludeRemove': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(factory.createUnionTypeNode([
createCasNode,
updateCasNode
]))));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(factory.createUnionTypeNode([
createCasNode,
updateCasNode,
]))));
break;
}
case 'excludeUpdate': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(createCasNode)));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(factory.createUnionTypeNode([
createCasNode,
removeCasNode,
]))));
break;
}
case 'appendOnly': {
cascadeCreateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(createCasNode)));
cascadeUpdateElements.push(factory.createPropertySignature(undefined, otmKey, factory.createToken(ts.SyntaxKind.QuestionToken), factory.createArrayTypeNode(createCasNode)));
break;
}
case 'readOnly': {
break;
}
default: {
(0, assert_1.default)(false);
}
}
}
};
if (oneToManySet) {
for (const entityName in foreignKeySet) {
foreignKeySet[entityName].forEach((foreignKey) => {
addOneToManyCascade(entityName, foreignKey);
});
}
}
if (process.env.COMPLING_AS_LIB) {
// 如果是base要包容更多可能的反指
/* cascadeCreateElements.push(
factory.createIndexSignature(
undefined,
[factory.createParameterDeclaration(
undefined,
undefined,
factory.createIdentifier("K"),
undefined,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
undefined
)],
factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
)
);
cascadeUpdateElements.push(
factory.createIndexSignature(
undefined,
[factory.createParameterDeclaration(
undefined,
undefined,
factory.createIdentifier("K"),
undefined,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
undefined
)],
factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
)
); */
}
}
return {
CreateOperationData: factory.createTypeReferenceNode('FormCreateData', [
cascadeCreateElements.length ? factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSchema"))),
factory.createTypeLiteralNode(cascadeCreateElements),
]) : factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSchema")))
]),
UpdateOperationData: factory.createTypeReferenceNode('FormUpdateData', [
cascadeUpdateElements.length ? factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSchema"))),
factory.createTypeLiteralNode(cascadeUpdateElements),
]) : factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSchema")))
]),
RemoveOperationData: factory.createTypeLiteralNode([]), // RemoveOperate暂时不支持Cascade
};
};
const { CreateOperationData, UpdateOperationData, RemoveOperationData, } = getOperations();
statements.push(factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(entity), undefined, factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, 'OpSchema', undefined, factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpSchema")))),
factory.createPropertySignature(undefined, 'Action', undefined, factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpAction")))),
factory.createPropertySignature(undefined, factory.createIdentifier("Schema"), undefined, getSchema()),
factory.createPropertySignature(undefined, factory.createIdentifier("Projection"), undefined, getProjection()),
factory.createPropertySignature(undefined, factory.createIdentifier("FilterUnit"), undefined, getFilterUnit()),
factory.createPropertySignature(undefined, factory.createIdentifier("Filter"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("MakeFilter"), [
createIndexedForeignRef(entity, 'FilterUnit')
])),
factory.createPropertySignature(undefined, factory.createIdentifier("SortAttr"), undefined, getSortAttr()),
factory.createPropertySignature(undefined, factory.createIdentifier("SortNode"), undefined, factory.createTypeLiteralNode([
factory.createPropertySignature(undefined, factory.createIdentifier("$attr"), undefined, createIndexedForeignRef(entity, 'SortAttr')),
factory.createPropertySignature(undefined, factory.createIdentifier("$direction"), factory.createToken(ts.SyntaxKind.QuestionToken), factory.createUnionTypeNode([
factory.createLiteralTypeNode(factory.createStringLiteral("asc")),
factory.createLiteralTypeNode(factory.createStringLiteral("desc"))
]))
])),
factory.createPropertySignature(undefined, factory.createIdentifier("Sorter"), undefined, factory.createArrayTypeNode(createIndexedForeignRef(entity, 'SortNode'))),
factory.createPropertySignature(undefined, factory.createIdentifier("Selection"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakSelection"), [
factory.createLiteralTypeNode(factory.createStringLiteral("select")),
createIndexedForeignRef(entity, 'Projection'),
createIndexedForeignRef(entity, 'Filter'),
createIndexedForeignRef(entity, 'Sorter')
])),
factory.createPropertySignature(undefined, factory.createIdentifier("Aggregation"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("DeduceAggregation"), [
createIndexedForeignRef(entity, 'Projection'),
createIndexedForeignRef(entity, 'Filter'),
createIndexedForeignRef(entity, 'Sorter')
])),
factory.createPropertySignature(undefined, factory.createIdentifier("CreateOperationData"), undefined, CreateOperationData),
factory.createPropertySignature(undefined, factory.createIdentifier("CreateSingle"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral('create')),
createIndexedForeignRef(entity, 'CreateOperationData')
])),
factory.createPropertySignature(undefined, factory.createIdentifier("CreateMulti"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral('create')),
factory.createTypeReferenceNode(factory.createIdentifier("Array"), [
createIndexedForeignRef(entity, 'CreateOperationData')
])
])),
factory.createPropertySignature(undefined, factory.createIdentifier("Create"), undefined, factory.createUnionTypeNode([
createIndexedForeignRef(entity, 'CreateSingle'),
createIndexedForeignRef(entity, 'CreateMulti')
])),
factory.createPropertySignature(undefined, factory.createIdentifier("UpdateOperationData"), undefined, UpdateOperationData),
factory.createPropertySignature(undefined, factory.createIdentifier("Update"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(`Base${entity}`), factory.createIdentifier("OpUpdateAction"))),
createIndexedForeignRef(entity, 'UpdateOperationData'),
createIndexedForeignRef(entity, 'Filter'),
createIndexedForeignRef(entity, 'Sorter'),
])),
factory.createPropertySignature(undefined, factory.createIdentifier("RemoveOperationData"), undefined, RemoveOperationData),
factory.createPropertySignature(undefined, factory.createIdentifier("Remove"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("OakOperation"), [
factory.createLiteralTypeNode(factory.createStringLiteral('remove')),
createIndexedForeignRef(entity, 'RemoveOperationData'),
createIndexedForeignRef(entity, 'Filter'),
createIndexedForeignRef(entity, 'Sorter'),
])),
factory.createPropertySignature(undefined, factory.createIdentifier("Operation"), undefined, factory.createUnionTypeNode([
createIndexedForeignRef(entity, 'Create'),
createIndexedForeignRef(entity, 'Update'),
createIndexedForeignRef(entity, 'Remove'),
])),
])));
}
statements.push(factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier("EntityDict"), undefined, factory.createTypeLiteralNode(Object.keys(Schema).map((ele) => factory.createPropertySignature(undefined, (0, string_1.firstLetterLowerCase)(ele), undefined, factory.createTypeReferenceNode(factory.createIdentifier(ele)))))));
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), undefined);
const fileName = path_1.default.join(outputDir, 'EntityDict.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
function getProjectionKeys(entity) {
const keys = [];
const { schemaAttrs } = Schema[entity];
const { [entity]: manyToOneSet = [] } = ManyToOne;
for (const attr of schemaAttrs) {
const { type, name } = attr;
const attrName = name.text;
if (ts.isTypeReferenceNode(type)) {
const typeName = type.typeName;
if (ts.isIdentifier(typeName)) {
const text = typeName.text;
switch (text) {
case 'String':
case 'Text':
case 'Int':
case 'Uint':
case 'Float':
case 'Double':
case 'Boolean':
case 'Datetime':
case 'Image':
case 'File':
case 'SingleGeo':
case 'Geo':
case 'Price':
case 'Decimal':
case 'Object':
break;
default:
const text2 = text === 'Schema' ? entity : text;
const manyToOneItem = manyToOneSet.find(([refEntity]) => refEntity === text2);
if (manyToOneItem) {
keys.push(attrName); // 外键属性
}
}
}
}
}
if (ReversePointerRelations[entity]) {
for (const one of ReversePointerRelations[entity]) {
const text2 = one === 'Schema' ? entity : one;
keys.push((0, string_1.firstLetterLowerCase)(one));
}
}
const { [entity]: oneToManySet = [] } = OneToMany;
const foreignKeySet = {};
for (const [entityName, foreignKey] of oneToManySet) {
if (!foreignKeySet[entityName]) {
foreignKeySet[entityName] = [];
}
foreignKeySet[entityName].push(foreignKey);
}
for (const entityName in foreignKeySet) {
const entityNameLc = (0, string_1.firstLetterLowerCase)(entityName);
for (const foreignKey of foreignKeySet[entityName]) {
const identifier = `${entityNameLc}$${foreignKey}`;
keys.push(identifier);
const aggrKey = _getAggrKey(entityNameLc, foreignKey);
if (typeof aggrKey === 'string') {
keys.push(aggrKey);
}
else {
// 如果是 union 类型,用映射表达式模拟(如 ["xxx$$aggr", `xxx$$${number}$$aggr`]
keys.push(`${identifier}$$aggr`);
}
}
}
return [...new Set([...keys, ...getOpProjectionKeys(entity)])];
}
function _outputSchema(outputDir, printer) {
for (const entity in Schema) {
const statements = [
factory.createExportDeclaration(undefined, false, undefined, factory.createStringLiteral("./_baseSchema"), undefined),
factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, undefined, factory.createIdentifier(entity))
])), factory.createStringLiteral("../EntityDict"), undefined),
];
const keys = [
'Schema', 'Action', 'Projection', 'Filter', 'SortNode', 'Sorter', 'Selection',
'Aggregation', 'CreateOperationData', 'CreateSingle', 'CreateMulti', 'Create',
'UpdateOperationData', 'Update', 'RemoveOperationData', 'Remove', 'Operation'
];
statements.push(...keys.map((ele) => factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(ele), undefined, factory.createIndexedAccessTypeNode(factory.createTypeReferenceNode(factory.createIdentifier(entity), undefined), factory.createLiteralTypeNode(factory.createStringLiteral(ele))))));
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), undefined);
const fileName = path_1.default.join(outputDir, entity, 'Schema.ts');
(0, fs_1.writeFileSync)(fileName, result, { flag: 'w' });
}
}
function buildSchema(outputDir) {
addReverseRelationship();
// setRelationEntities();
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
resetOutputDir(outputDir);
outputAction(outputDir, printer);
_outputBaseSchema(outputDir, printer);
outputLocale(outputDir, printer);
_outputEntityDict(outputDir, printer);
_outputSchema(outputDir, printer);
outputStorage(outputDir, printer);
outputRelation2(outputDir, printer);
outputStyleDict(outputDir, printer);
outputIndexTs(outputDir);
}