From d43f3f4c9ffb9999466226ba67a3af4bf4d4e304 Mon Sep 17 00:00:00 2001 From: Xc Date: Tue, 9 Apr 2024 20:51:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86dependencyBuilder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/actions/relation.d.ts | 5 - lib/actions/relation.js | 25 - .../UserEntityGrant/Action.d.ts | 5 - lib/base-app-domain/UserEntityGrant/Action.js | 5 - lib/compiler/dependencyBuilder.d.ts | 5 + lib/compiler/dependencyBuilder.js | 422 ++++++++++ lib/compiler/env.d.ts | 1 + lib/compiler/env.js | 3 +- lib/compiler/routerBuilder.js | 48 +- src/compiler/dependencyBuilder.ts | 782 ++++++++++++++++++ src/compiler/env.ts | 4 +- src/compiler/routerBuilder.ts | 68 +- 12 files changed, 1269 insertions(+), 104 deletions(-) delete mode 100644 lib/actions/relation.d.ts delete mode 100644 lib/actions/relation.js delete mode 100644 lib/base-app-domain/UserEntityGrant/Action.d.ts delete mode 100644 lib/base-app-domain/UserEntityGrant/Action.js create mode 100644 lib/compiler/dependencyBuilder.d.ts create mode 100644 lib/compiler/dependencyBuilder.js create mode 100644 src/compiler/dependencyBuilder.ts diff --git a/lib/actions/relation.d.ts b/lib/actions/relation.d.ts deleted file mode 100644 index 1f379bb..0000000 --- a/lib/actions/relation.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { CascadeRelationItem, RelationHierarchy, EntityDict } from "../types/Entity"; -export type GenericRelation = 'owner'; -export declare function convertHierarchyToAuth(entity: T, hierarchy: RelationHierarchy>): { - [K in NonNullable]?: CascadeRelationItem; -}; diff --git a/lib/actions/relation.js b/lib/actions/relation.js deleted file mode 100644 index 6f5ad9a..0000000 --- a/lib/actions/relation.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertHierarchyToAuth = void 0; -function convertHierarchyToAuth(entity, hierarchy) { - const reverseHierarchy = {}; - for (const r in hierarchy) { - for (const r2 of hierarchy[r]) { - if (reverseHierarchy[r2]) { - reverseHierarchy[r2]?.push(r); - } - else { - reverseHierarchy[r2] = [r]; - } - } - } - const result = {}; - for (const r in reverseHierarchy) { - result[r] = { - cascadePath: '', - relations: reverseHierarchy[r], - }; - } - return result; -} -exports.convertHierarchyToAuth = convertHierarchyToAuth; diff --git a/lib/base-app-domain/UserEntityGrant/Action.d.ts b/lib/base-app-domain/UserEntityGrant/Action.d.ts deleted file mode 100644 index fc559ed..0000000 --- a/lib/base-app-domain/UserEntityGrant/Action.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { GenericAction } from "../../actions/action"; -export type ParticularAction = 'confirm'; -export declare const actions: string[]; -export type Action = GenericAction | ParticularAction | string; -export declare const ActionDefDict: {}; diff --git a/lib/base-app-domain/UserEntityGrant/Action.js b/lib/base-app-domain/UserEntityGrant/Action.js deleted file mode 100644 index d644bc4..0000000 --- a/lib/base-app-domain/UserEntityGrant/Action.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ActionDefDict = exports.actions = void 0; -exports.actions = ["count", "stat", "download", "select", "aggregate", "create", "remove", "update", "confirm"]; -exports.ActionDefDict = {}; diff --git a/lib/compiler/dependencyBuilder.d.ts b/lib/compiler/dependencyBuilder.d.ts new file mode 100644 index 0000000..5ae44ba --- /dev/null +++ b/lib/compiler/dependencyBuilder.d.ts @@ -0,0 +1,5 @@ +/** + * 本函数用于构建src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext + * 这些和dependency相关的项目文件 + */ +export default function buildDependency(rebuild?: boolean): void; diff --git a/lib/compiler/dependencyBuilder.js b/lib/compiler/dependencyBuilder.js new file mode 100644 index 0000000..9e5e584 --- /dev/null +++ b/lib/compiler/dependencyBuilder.js @@ -0,0 +1,422 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const tslib_1 = require("tslib"); +const assert_1 = tslib_1.__importDefault(require("assert")); +const path_1 = require("path"); +const fs_1 = require("fs"); +const ts = tslib_1.__importStar(require("typescript")); +const string_1 = require("../utils/string"); +const env_1 = require("./env"); +const { factory } = ts; +function join(...paths) { + const path = (0, path_1.join)(...paths); + return path.replaceAll('\\', '/'); +} +function destructVariableDeclaration(vd) { + (0, assert_1.default)(ts.isIdentifier(vd.name)); + (0, assert_1.default)(vd.name.text.startsWith('total')); + const on = (0, string_1.firstLetterLowerCase)(vd.name.text.slice(5)); + const { initializer } = vd; + (0, assert_1.default)(ts.isCallExpression(initializer)); + (0, assert_1.default)(ts.isIdentifier(initializer.expression) && initializer.expression.text === 'mergeConcatMany'); + (0, assert_1.default)(initializer.arguments.length === 1); + const [arg] = initializer.arguments; + (0, assert_1.default)(ts.isAsExpression(arg)); + const { expression } = arg; + (0, assert_1.default)(ts.isArrayLiteralExpression(expression)); + return { + on, + expression, + }; +} +/** + * 生成initialize.prod.ts + * @param cwd + * @param dependencies + * @param briefNames + * @param sourceFile + * @param printer + */ +function outputIntializeProd(cwd, dependencies, briefNames, sourceFile, printer) { + const { statements } = sourceFile; + const objectDict = {}; + // 所有的import + const importStatements = []; + dependencies.forEach((dep, idx) => { + const depDir = join(cwd, 'node_modules', dep); + if (!(0, fs_1.existsSync)(depDir)) { + throw new Error(`依赖模块${dep}未能找到相应的安装目录【${depDir}】`); + } + const esDir = join(depDir, 'es'); + const libDir = join(depDir, 'lib'); + const esDirExisted = (0, fs_1.existsSync)(esDir); + const libDirExisted = (0, fs_1.existsSync)(libDir); + if (!esDirExisted && !libDirExisted) { + throw new Error(`依赖模块${dep}中没有es或者lib目录`); + } + const destDir = esDirExisted ? esDir : libDir; + const destDirName = esDirExisted ? 'es' : 'lib'; + const objectDirs = ['checkers']; + objectDirs.forEach((o) => { + if ((0, fs_1.existsSync)(join(destDir, o))) { + const variableName = `${briefNames[idx]}${(0, string_1.firstLetterUpperCase)(o)}`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, o)), undefined)); + if (objectDict[o]) { + objectDict[o].push(variableName); + } + else { + objectDict[o] = [variableName]; + } + } + }); + // common + if ((0, fs_1.existsSync)(join(destDir, 'configuration'))) { + const variableName = `${briefNames[idx]}Common`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, 'configuration')), undefined)); + if (objectDict.common) { + objectDict.common.push(variableName); + } + else { + objectDict.common = [variableName]; + } + } + // render + if ((0, fs_1.existsSync)(join(destDir, 'configuration', 'render.js'))) { + const variableName = `${briefNames[idx]}Render`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, 'configuration', 'render')), undefined)); + if (objectDict.render) { + objectDict.render.push(variableName); + } + else { + objectDict.render = [variableName]; + } + } + // features + if ((0, fs_1.existsSync)(join(destDir, 'features'))) { + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ + factory.createImportSpecifier(false, factory.createIdentifier("create"), factory.createIdentifier(`create${(0, string_1.firstLetterUpperCase)(briefNames[idx])}Features`)), + factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`)) + ])), factory.createStringLiteral(join(dep, destDirName, 'features')), undefined)); + if (objectDict.features) { + objectDict.features.push(briefNames[idx]); + } + else { + objectDict.features = [briefNames[idx]]; + } + } + }); + const funcStmt = statements.find((stmt) => ts.isFunctionDeclaration(stmt) && stmt.modifiers?.find(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) && stmt.modifiers.find(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword)); + (0, assert_1.default)(funcStmt); + const idx = statements.indexOf(funcStmt); + const statements2 = [ + ...statements.slice(0, idx), + ...importStatements, + ...statements.slice(idx) + ]; + const stmt0 = funcStmt.body?.statements[0]; + (0, assert_1.default)(ts.isVariableStatement(stmt0)); + let vdl = stmt0.declarationList; + vdl.declarations.forEach((declaration) => { + const { on, expression } = destructVariableDeclaration(declaration); + if (objectDict[on]) { + const { elements } = expression; + Object.assign(expression, { + elements: elements.concat(...objectDict[on].map(ele => factory.createIdentifier(ele))) + }); + } + }); + if (objectDict.features) { + const stmt1 = funcStmt.body?.statements[1]; + (0, assert_1.default)(ts.isVariableStatement(stmt1)); + const tfDec = stmt1.declarationList.declarations[0]; + const { name, initializer } = tfDec; + (0, assert_1.default)(ts.isIdentifier(name) && name.text === 'totalFeatures'); + (0, assert_1.default)(ts.isAsExpression(initializer)); + const { type } = initializer; + (0, assert_1.default)(ts.isIntersectionTypeNode(type)); + Object.assign(type, { + types: type.types.concat(objectDict.features.map(ele => factory.createTypeReferenceNode(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`, [ + factory.createTypeReferenceNode('EntityDict') + ]))) + }); + Object.assign(funcStmt.body, { + statements: [ + ...funcStmt.body.statements.slice(0, 4), + ...objectDict.features.map((ele) => [ + factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(`${ele}Features`), undefined, undefined, factory.createCallExpression(factory.createIdentifier(`create${(0, string_1.firstLetterUpperCase)(ele)}Features`), undefined, [factory.createIdentifier("totalFeatures")]))], ts.NodeFlags.Const)), + factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("Object"), factory.createIdentifier("assign")), undefined, [ + factory.createIdentifier("totalFeatures"), + factory.createIdentifier(`${ele}Features`) + ])) + ]).flat(), + ...funcStmt.body.statements.slice(4), + ] + }); + } + const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); + const filename = join(cwd, 'src', 'initialize.prod.ts'); + (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); + console.log('构建initialize.prod.ts文件成功'); +} +/** + * 生成initialize.dev.ts + * @param cwd + * @param dependencies + * @param briefNames + * @param sourceFile + * @param printer + */ +function outputIntializeDev(cwd, dependencies, briefNames, sourceFile, printer) { + const { statements } = sourceFile; + const objectDict = {}; + // 所有的import + const importStatements = []; + dependencies.forEach((dep, idx) => { + const depDir = join(cwd, 'node_modules', dep); + if (!(0, fs_1.existsSync)(depDir)) { + throw new Error(`依赖模块${dep}未能找到相应的安装目录【${depDir}】`); + } + const esDir = join(depDir, 'es'); + const libDir = join(depDir, 'lib'); + const esDirExisted = (0, fs_1.existsSync)(esDir); + const libDirExisted = (0, fs_1.existsSync)(libDir); + if (!esDirExisted && !libDirExisted) { + throw new Error(`依赖模块${dep}中没有es或者lib目录`); + } + const destDir = esDirExisted ? esDir : libDir; + const destDirName = esDirExisted ? 'es' : 'lib'; + const objectDirs = ['triggers', 'checkers', 'watchers', 'timers', 'data', 'aspects']; + objectDirs.forEach((o) => { + if ((0, fs_1.existsSync)(join(destDir, o))) { + const variableName = `${briefNames[idx]}${(0, string_1.firstLetterUpperCase)(o)}`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, o)), undefined)); + if (objectDict[o]) { + objectDict[o].push(variableName); + } + else { + objectDict[o] = [variableName]; + } + } + }); + // startRoutine + if ((0, fs_1.existsSync)(join(destDir, 'routines', 'start'))) { + const variableName = `${briefNames[idx]}StartRoutines`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, 'routines/start')), undefined)); + if (objectDict.startRoutines) { + objectDict.startRoutines.push(variableName); + } + else { + objectDict.startRoutines = [variableName]; + } + } + // common + if ((0, fs_1.existsSync)(join(destDir, 'configuration'))) { + const variableName = `${briefNames[idx]}Common`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, 'configuration')), undefined)); + if (objectDict.common) { + objectDict.common.push(variableName); + } + else { + objectDict.common = [variableName]; + } + } + // render + if ((0, fs_1.existsSync)(join(destDir, 'configuration', 'render.js'))) { + const variableName = `${briefNames[idx]}Render`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(variableName), undefined), factory.createStringLiteral(join(dep, destDirName, 'configuration', 'render')), undefined)); + if (objectDict.render) { + objectDict.render.push(variableName); + } + else { + objectDict.render = [variableName]; + } + } + // features + if ((0, fs_1.existsSync)(join(destDir, 'features'))) { + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ + factory.createImportSpecifier(false, factory.createIdentifier("create"), factory.createIdentifier(`create${(0, string_1.firstLetterUpperCase)(briefNames[idx])}Features`)), + factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`)) + ])), factory.createStringLiteral(join(dep, destDirName, 'features')), undefined)); + if (objectDict.features) { + objectDict.features.push(briefNames[idx]); + } + else { + objectDict.features = [briefNames[idx]]; + } + } + // ports + if ((0, fs_1.existsSync)(join(destDir, 'ports'))) { + const importVariableName = `${briefNames[idx]}Importations`; + const exportVariableName = `${briefNames[idx]}Exportations`; + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ + factory.createImportSpecifier(false, factory.createIdentifier("importations"), factory.createIdentifier(importVariableName)), + factory.createImportSpecifier(false, factory.createIdentifier("exportations"), factory.createIdentifier(exportVariableName)) + ])), factory.createStringLiteral(join(dep, destDirName, 'ports')), undefined)); + if (objectDict.importations) { + objectDict.importations.push(importVariableName); + } + else { + objectDict.importations = [importVariableName]; + } + if (objectDict.exportations) { + objectDict.exportations.push(exportVariableName); + } + else { + objectDict.exportations = [exportVariableName]; + } + } + }); + const funcStmt = statements.find((stmt) => ts.isFunctionDeclaration(stmt) && stmt.modifiers?.find(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) && stmt.modifiers.find(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword)); + (0, assert_1.default)(funcStmt); + const idx = statements.indexOf(funcStmt); + const statements2 = [ + ...statements.slice(0, idx), + ...importStatements, + ...statements.slice(idx) + ]; + const stmt0 = funcStmt.body?.statements[0]; + (0, assert_1.default)(ts.isVariableStatement(stmt0)); + let vdl = stmt0.declarationList; + vdl.declarations.forEach((declaration) => { + const { on, expression } = destructVariableDeclaration(declaration); + if (objectDict[on]) { + const { elements } = expression; + Object.assign(expression, { + elements: elements.concat(...objectDict[on].map(ele => factory.createIdentifier(ele))) + }); + } + }); + if (objectDict.features) { + const stmt2 = funcStmt.body?.statements[2]; + (0, assert_1.default)(ts.isVariableStatement(stmt2)); + const tfDec = stmt2.declarationList.declarations[0]; + const { name, initializer } = tfDec; + (0, assert_1.default)(ts.isIdentifier(name) && name.text === 'totalFeatures'); + (0, assert_1.default)(ts.isAsExpression(initializer)); + const { type } = initializer; + (0, assert_1.default)(ts.isIntersectionTypeNode(type)); + Object.assign(type, { + types: type.types.concat(objectDict.features.map(ele => factory.createTypeReferenceNode(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`, [ + factory.createTypeReferenceNode('EntityDict') + ]))) + }); + Object.assign(funcStmt.body, { + statements: [ + ...funcStmt.body.statements.slice(0, 5), + ...objectDict.features.map((ele) => [ + factory.createVariableStatement(undefined, factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(`${ele}Features`), undefined, undefined, factory.createCallExpression(factory.createIdentifier(`create${(0, string_1.firstLetterUpperCase)(ele)}Features`), undefined, [factory.createIdentifier("totalFeatures")]))], ts.NodeFlags.Const)), + factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("Object"), factory.createIdentifier("assign")), undefined, [ + factory.createIdentifier("totalFeatures"), + factory.createIdentifier(`${ele}Features`) + ])) + ]).flat(), + ...funcStmt.body.statements.slice(5), + ] + }); + } + const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); + const filename = join(cwd, 'src', 'initialize.dev.ts'); + (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); + console.log('构建initialize.dev.ts文件成功'); +} +function outputIntializeFeatures(cwd, dependencies, briefNames, sourceFile, printer) { + const { statements } = sourceFile; + const features = []; + // 所有的import + const importStatements = []; + // 如果有oak-general-business,需要AccessConfiguration,自动注入 + if (dependencies.includes('oak-general-business')) { + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier("accessConfiguration"), undefined), factory.createStringLiteral("@project/configuration/access"), undefined)); + } + dependencies.forEach((dep, idx) => { + const depDir = join(cwd, 'node_modules', dep); + if (!(0, fs_1.existsSync)(depDir)) { + throw new Error(`依赖模块${dep}未能找到相应的安装目录【${depDir}】`); + } + const esDir = join(depDir, 'es'); + const libDir = join(depDir, 'lib'); + const esDirExisted = (0, fs_1.existsSync)(esDir); + const libDirExisted = (0, fs_1.existsSync)(libDir); + if (!esDirExisted && !libDirExisted) { + throw new Error(`依赖模块${dep}中没有es或者lib目录`); + } + const destDir = esDirExisted ? esDir : libDir; + const destDirName = esDirExisted ? 'es' : 'lib'; + // features + if ((0, fs_1.existsSync)(join(destDir, 'features'))) { + importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ + factory.createImportSpecifier(false, factory.createIdentifier("initialize"), factory.createIdentifier(`initialize${(0, string_1.firstLetterUpperCase)(briefNames[idx])}Features`)), + factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`)) + ])), factory.createStringLiteral(join(dep, destDirName, 'features')), undefined)); + features.push(briefNames[idx]); + } + }); + const funcStmt = statements.find((stmt) => ts.isFunctionDeclaration(stmt) && stmt.modifiers?.find(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) && stmt.modifiers.find(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword)); + (0, assert_1.default)(funcStmt); + const idx = statements.indexOf(funcStmt); + const statements2 = [ + ...statements.slice(0, idx), + ...importStatements, + ...statements.slice(idx) + ]; + if (features.length > 0) { + (0, assert_1.default)(funcStmt.parameters.length === 1); + const [param] = funcStmt.parameters; + const { type } = param; + (0, assert_1.default)(ts.isIntersectionTypeNode(type)); + Object.assign(type, { + types: type.types.concat(features.map(ele => factory.createTypeReferenceNode(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`, [ + factory.createTypeReferenceNode('EntityDict') + ]))) + }); + Object.assign(funcStmt.body, { + statements: [ + ...features.map((ele, idx) => { + const args = [ + factory.createIdentifier("features"), + ]; + if (dependencies[idx] === 'oak-general-business') { + args.push(factory.createIdentifier("accessConfiguration")); + } + return factory.createExpressionStatement(factory.createAwaitExpression(factory.createCallExpression(factory.createIdentifier(`initialize${(0, string_1.firstLetterUpperCase)(ele)}Features`), undefined, args))); + }).flat(), + ...funcStmt.body.statements, + ] + }); + } + const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); + const filename = join(cwd, 'src', 'initializeFeatures.ts'); + (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); + console.log('构建initializeFeatures.ts文件成功'); +} +/** + * 本函数用于构建src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext + * 这些和dependency相关的项目文件 + */ +function buildDependency(rebuild) { + const cwd = process.cwd(); + const initDevFile = join(cwd, 'src', 'initialize.dev.ts'); + if ((0, fs_1.existsSync)(initDevFile) && !rebuild) { + console.log('src/initialize.dev.ts文件已经存在,无需构建启动文件'); + return; + } + const depConfigFile = join(cwd, 'src', 'configuration', 'dependency.ts'); + if (!(0, fs_1.existsSync)(depConfigFile)) { + console.error(`${depConfigFile}不存在,无法构建启动文件`); + } + // 这里依赖配置是ts文件,得翻译成js再读取 + const result = ts.transpileModule((0, fs_1.readFileSync)(depConfigFile, 'utf-8'), { compilerOptions: { module: ts.ModuleKind.CommonJS } }); + const dependencies = eval(result.outputText); + const briefNames = dependencies.map((dep, idx) => `${dep.split('-').map(ele => ele[0]).join('')}${idx}`); + const templateFileList = [ + join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.dev.ts'), + join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.prod.ts'), + join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initializeFeatures.ts'), + ]; + const program = ts.createProgram(templateFileList, {}); + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0]), printer); + outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1]), printer); + outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2]), printer); +} +exports.default = buildDependency; diff --git a/lib/compiler/env.d.ts b/lib/compiler/env.d.ts index 1e66172..5f00a5f 100644 --- a/lib/compiler/env.d.ts +++ b/lib/compiler/env.d.ts @@ -11,3 +11,4 @@ export declare const NUMERICAL_LITERL_DEFAULT_SCALE = 2; export declare const INT_LITERL_DEFAULT_WIDTH = 4; export declare const OAK_EXTERNAL_LIBS_FILEPATH: (path: string) => string; export * from './entities'; +export declare const OAK_CLI_MODULE_NAME = "@xuchangzju/oak-cli"; diff --git a/lib/compiler/env.js b/lib/compiler/env.js index d8c30e9..9c15f4d 100644 --- a/lib/compiler/env.js +++ b/lib/compiler/env.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.OAK_EXTERNAL_LIBS_FILEPATH = exports.INT_LITERL_DEFAULT_WIDTH = exports.NUMERICAL_LITERL_DEFAULT_SCALE = exports.NUMERICAL_LITERL_DEFAULT_PRECISION = exports.STRING_LITERAL_MAX_LENGTH = exports.ENTITY_NAME_MAX_LENGTH = exports.ACTION_CONSTANT_IN_OAK_DOMAIN = exports.TYPE_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_GENERAL_BUSINESS = exports.LIB_PATH = exports.LIB_OAK_DOMAIN = void 0; +exports.OAK_CLI_MODULE_NAME = exports.OAK_EXTERNAL_LIBS_FILEPATH = exports.INT_LITERL_DEFAULT_WIDTH = exports.NUMERICAL_LITERL_DEFAULT_SCALE = exports.NUMERICAL_LITERL_DEFAULT_PRECISION = exports.STRING_LITERAL_MAX_LENGTH = exports.ENTITY_NAME_MAX_LENGTH = exports.ACTION_CONSTANT_IN_OAK_DOMAIN = exports.TYPE_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_DOMAIN = exports.ENTITY_PATH_IN_OAK_GENERAL_BUSINESS = exports.LIB_PATH = exports.LIB_OAK_DOMAIN = void 0; const tslib_1 = require("tslib"); const path_1 = tslib_1.__importDefault(require("path")); exports.LIB_OAK_DOMAIN = 'oak-domain'; @@ -43,3 +43,4 @@ const OAK_EXTERNAL_LIBS_FILEPATH = (path) => { }; exports.OAK_EXTERNAL_LIBS_FILEPATH = OAK_EXTERNAL_LIBS_FILEPATH; tslib_1.__exportStar(require("./entities"), exports); +exports.OAK_CLI_MODULE_NAME = '@xuchangzju/oak-cli'; diff --git a/lib/compiler/routerBuilder.js b/lib/compiler/routerBuilder.js index d0ec179..bfd5c10 100644 --- a/lib/compiler/routerBuilder.js +++ b/lib/compiler/routerBuilder.js @@ -7,6 +7,7 @@ const fs_extra_1 = require("fs-extra"); const assert_1 = tslib_1.__importDefault(require("assert")); const ts = tslib_1.__importStar(require("typescript")); const node_watch_1 = tslib_1.__importDefault(require("node-watch")); +const env_1 = require("./env"); const { factory } = ts; const NameSpaceDescDict = {}; function checkPageDir(dir, relativePath, ns, type) { @@ -161,33 +162,28 @@ function judgeUseOakRouterBuilder(statements) { } function outputInWebAppDir(appDir) { const routerFileName = (0, path_1.join)(appDir, 'router', 'allRouters.ts'); - const templateFileName = (0, path_1.join)(appDir, 'router', 'allRoutersTemplate.ts'); - if ((0, fs_extra_1.existsSync)(templateFileName)) { - const program = ts.createProgram([templateFileName], { - removeComments: false, - }); - const routerFile = program.getSourceFile(templateFileName); - (0, assert_1.default)(routerFile); - const namespaceDir = (0, path_1.join)(appDir, 'namespaces'); - const { statements } = routerFile; - if (judgeUseOakRouterBuilder(statements)) { - statements.forEach((statement) => { - if (ts.isVariableStatement(statement)) { - const declaration = statement.declarationList.declarations.find(declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'allRouters'); - if (declaration) { - Object.assign(declaration, { - initializer: makeWebAllRouters(namespaceDir, (0, path_1.join)(appDir, '../../../..'), (0, path_1.dirname)(templateFileName)) - }); - } + const templateFileName = (0, path_1.join)(appDir, '../../../..', 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'allRouters.ts'); + const program = ts.createProgram([templateFileName], { + removeComments: false, + }); + const routerFile = program.getSourceFile(templateFileName); + (0, assert_1.default)(routerFile); + const namespaceDir = (0, path_1.join)(appDir, 'namespaces'); + const { statements } = routerFile; + if (judgeUseOakRouterBuilder(statements)) { + statements.forEach((statement) => { + if (ts.isVariableStatement(statement)) { + const declaration = statement.declarationList.declarations.find(declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'allRouters'); + if (declaration) { + Object.assign(declaration, { + initializer: makeWebAllRouters(namespaceDir, (0, path_1.join)(appDir, '../../../..'), (0, path_1.dirname)(routerFileName)) + }); } - }); - const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments: false }); - const result = printer.printNode(ts.EmitHint.Unspecified, routerFile, routerFile); - (0, fs_extra_1.writeFileSync)(routerFileName, result); - } - } - else { - console.warn(`${appDir}的目录结构未按照标准建立,缺少了${templateFileName},请从模板中补充`); + } + }); + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments: false }); + const result = printer.printNode(ts.EmitHint.Unspecified, routerFile, routerFile); + (0, fs_extra_1.writeFileSync)(routerFileName, result); } } function outputInWebDir(dir) { diff --git a/src/compiler/dependencyBuilder.ts b/src/compiler/dependencyBuilder.ts new file mode 100644 index 0000000..824852c --- /dev/null +++ b/src/compiler/dependencyBuilder.ts @@ -0,0 +1,782 @@ +import assert from 'assert'; +import { join as pathJoin } from 'path'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; +import * as ts from 'typescript'; +import { firstLetterLowerCase, firstLetterUpperCase } from '../utils/string'; +import { OAK_CLI_MODULE_NAME } from './env'; +const { factory } = ts; + +function join(...paths: string[]) { + const path = pathJoin(...paths); + return path.replaceAll('\\', '/'); +} + +function destructVariableDeclaration(vd: ts.VariableDeclaration) { + assert(ts.isIdentifier(vd.name)) + assert(vd.name.text.startsWith('total')); + const on = firstLetterLowerCase(vd.name.text.slice(5)); + const { initializer } = vd; + assert(ts.isCallExpression(initializer!)); + assert(ts.isIdentifier(initializer.expression) && initializer.expression.text === 'mergeConcatMany'); + assert(initializer.arguments.length === 1); + const [arg] = initializer.arguments; + assert(ts.isAsExpression(arg)); + const { expression } = arg; + assert(ts.isArrayLiteralExpression(expression)); + return { + on, + expression, + }; +} + +/** + * 生成initialize.prod.ts + * @param cwd + * @param dependencies + * @param briefNames + * @param sourceFile + * @param printer + */ +function outputIntializeProd( + cwd: string, + dependencies: string[], + briefNames: string[], + sourceFile: ts.SourceFile, + printer: ts.Printer +) { + const { statements } = sourceFile; + + const objectDict: Record = {}; + // 所有的import + const importStatements: ts.Statement[] = []; + dependencies.forEach( + (dep, idx) => { + const depDir = join(cwd, 'node_modules', dep); + if (!existsSync(depDir)) { + throw new Error(`依赖模块${dep}未能找到相应的安装目录【${depDir}】`); + } + const esDir = join(depDir, 'es'); + const libDir = join(depDir, 'lib'); + const esDirExisted = existsSync(esDir); + const libDirExisted = existsSync(libDir); + if (!esDirExisted && !libDirExisted) { + throw new Error(`依赖模块${dep}中没有es或者lib目录`); + } + const destDir = esDirExisted ? esDir : libDir; + const destDirName = esDirExisted ? 'es' : 'lib'; + + const objectDirs = ['checkers']; + objectDirs.forEach( + (o) => { + if (existsSync(join(destDir, o))) { + const variableName = `${briefNames[idx]}${firstLetterUpperCase(o)}`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, o)), + undefined + ) + ); + if (objectDict[o]) { + objectDict[o].push(variableName); + } + else { + objectDict[o] = [variableName]; + } + } + } + ); + + // common + if (existsSync(join(destDir, 'configuration'))) { + const variableName = `${briefNames[idx]}Common`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, 'configuration')), + undefined + ) + ); + + if (objectDict.common) { + objectDict.common.push(variableName); + } + else { + objectDict.common = [variableName]; + } + } + + // render + if (existsSync(join(destDir, 'configuration', 'render.js'))) { + const variableName = `${briefNames[idx]}Render`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, 'configuration', 'render')), + undefined + ) + ); + + if (objectDict.render) { + objectDict.render.push(variableName); + } + else { + objectDict.render = [variableName]; + } + } + + // features + if (existsSync(join(destDir, 'features'))) { + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + factory.createIdentifier("create"), + factory.createIdentifier(`create${firstLetterUpperCase(briefNames[idx])}Features`) + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("FeatureDict"), + factory.createIdentifier(`${firstLetterUpperCase(briefNames[idx])}FeatureDict`) + ) + ]) + ), + factory.createStringLiteral(join(dep, destDirName, 'features')), + undefined + ) + ); + if (objectDict.features) { + objectDict.features.push(briefNames[idx]); + } + else { + objectDict.features = [briefNames[idx]]; + } + } + } + ); + + + const funcStmt = statements.find( + (stmt) => ts.isFunctionDeclaration(stmt) && stmt.modifiers?.find( + modifier => modifier.kind === ts.SyntaxKind.ExportKeyword + ) && stmt.modifiers.find( + modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword + ) + ) as ts.FunctionDeclaration; + + assert(funcStmt); + const idx = statements.indexOf(funcStmt); + const statements2 = [ + ...statements.slice(0, idx), + ...importStatements, + ...statements.slice(idx) + ]; + + const stmt0 = funcStmt.body?.statements[0]; + assert(ts.isVariableStatement(stmt0!)); + let vdl = stmt0.declarationList; + vdl.declarations.forEach( + (declaration) => { + const { on, expression } = destructVariableDeclaration(declaration); + if (objectDict[on]) { + const { elements } = expression; + + Object.assign(expression, { + elements: elements.concat(...objectDict[on].map( + ele => factory.createIdentifier(ele) + )) + }); + } + } + ); + + if (objectDict.features) { + const stmt1 = funcStmt.body?.statements[1]; + assert(ts.isVariableStatement(stmt1!)); + const tfDec = stmt1.declarationList.declarations[0]; + const { name, initializer } = tfDec; + assert(ts.isIdentifier(name) && name.text === 'totalFeatures'); + assert(ts.isAsExpression(initializer!)); + const { type } = initializer; + assert(ts.isIntersectionTypeNode(type)); + Object.assign(type, { + types: type.types.concat( + objectDict.features.map( + ele => factory.createTypeReferenceNode( + `${firstLetterUpperCase(ele)}FeatureDict`, + [ + factory.createTypeReferenceNode('EntityDict') + ] + ) + ) + ) + }); + + Object.assign(funcStmt.body!, { + statements: [ + ...funcStmt.body!.statements.slice(0, 4), + ...objectDict.features.map( + (ele) => [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier(`${ele}Features`), + undefined, + undefined, + factory.createCallExpression( + factory.createIdentifier(`create${firstLetterUpperCase(ele)}Features`), + undefined, + [factory.createIdentifier("totalFeatures")] + ) + )], + ts.NodeFlags.Const + ) + ), + factory.createExpressionStatement(factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("Object"), + factory.createIdentifier("assign") + ), + undefined, + [ + factory.createIdentifier("totalFeatures"), + factory.createIdentifier(`${ele}Features`) + ] + )) + ] + ).flat(), + ...funcStmt.body!.statements.slice(4), + ] + }) + } + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(statements2), + sourceFile); + const filename = join(cwd, 'src', 'initialize.prod.ts'); + writeFileSync(filename, result, { flag: 'w' }); + console.log('构建initialize.prod.ts文件成功'); +} + +/** + * 生成initialize.dev.ts + * @param cwd + * @param dependencies + * @param briefNames + * @param sourceFile + * @param printer + */ +function outputIntializeDev( + cwd: string, + dependencies: string[], + briefNames: string[], + sourceFile: ts.SourceFile, + printer: ts.Printer +) { + const { statements } = sourceFile; + + const objectDict: Record = {}; + // 所有的import + const importStatements: ts.Statement[] = []; + dependencies.forEach( + (dep, idx) => { + const depDir = join(cwd, 'node_modules', dep); + if (!existsSync(depDir)) { + throw new Error(`依赖模块${dep}未能找到相应的安装目录【${depDir}】`); + } + const esDir = join(depDir, 'es'); + const libDir = join(depDir, 'lib'); + const esDirExisted = existsSync(esDir); + const libDirExisted = existsSync(libDir); + if (!esDirExisted && !libDirExisted) { + throw new Error(`依赖模块${dep}中没有es或者lib目录`); + } + const destDir = esDirExisted ? esDir : libDir; + const destDirName = esDirExisted ? 'es' : 'lib'; + + const objectDirs = ['triggers', 'checkers', 'watchers', 'timers', 'data', 'aspects']; + objectDirs.forEach( + (o) => { + if (existsSync(join(destDir, o))) { + const variableName = `${briefNames[idx]}${firstLetterUpperCase(o)}`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, o)), + undefined + ) + ); + if (objectDict[o]) { + objectDict[o].push(variableName); + } + else { + objectDict[o] = [variableName]; + } + } + } + ); + + // startRoutine + if (existsSync(join(destDir, 'routines', 'start'))) { + const variableName = `${briefNames[idx]}StartRoutines`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, 'routines/start')), + undefined + ) + ); + + if (objectDict.startRoutines) { + objectDict.startRoutines.push(variableName); + } + else { + objectDict.startRoutines = [variableName]; + } + } + + // common + if (existsSync(join(destDir, 'configuration'))) { + const variableName = `${briefNames[idx]}Common`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, 'configuration')), + undefined + ) + ); + + if (objectDict.common) { + objectDict.common.push(variableName); + } + else { + objectDict.common = [variableName]; + } + } + + // render + if (existsSync(join(destDir, 'configuration', 'render.js'))) { + const variableName = `${briefNames[idx]}Render`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier(variableName), + undefined + ), + factory.createStringLiteral(join(dep, destDirName, 'configuration', 'render')), + undefined + ) + ); + + if (objectDict.render) { + objectDict.render.push(variableName); + } + else { + objectDict.render = [variableName]; + } + } + + // features + if (existsSync(join(destDir, 'features'))) { + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + factory.createIdentifier("create"), + factory.createIdentifier(`create${firstLetterUpperCase(briefNames[idx])}Features`) + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("FeatureDict"), + factory.createIdentifier(`${firstLetterUpperCase(briefNames[idx])}FeatureDict`) + ) + ]) + ), + factory.createStringLiteral(join(dep, destDirName, 'features')), + undefined + ) + ); + if (objectDict.features) { + objectDict.features.push(briefNames[idx]); + } + else { + objectDict.features = [briefNames[idx]]; + } + } + + // ports + if (existsSync(join(destDir, 'ports'))) { + const importVariableName = `${briefNames[idx]}Importations`; + const exportVariableName = `${briefNames[idx]}Exportations`; + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + factory.createIdentifier("importations"), + factory.createIdentifier(importVariableName) + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("exportations"), + factory.createIdentifier(exportVariableName) + ) + ]) + ), + factory.createStringLiteral(join(dep, destDirName, 'ports')), + undefined + ) + ); + if (objectDict.importations) { + objectDict.importations.push(importVariableName); + } + else { + objectDict.importations = [importVariableName]; + } + + if (objectDict.exportations) { + objectDict.exportations.push(exportVariableName); + } + else { + objectDict.exportations = [exportVariableName]; + } + } + } + ); + + + const funcStmt = statements.find( + (stmt) => ts.isFunctionDeclaration(stmt) && stmt.modifiers?.find( + modifier => modifier.kind === ts.SyntaxKind.ExportKeyword + ) && stmt.modifiers.find( + modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword + ) + ) as ts.FunctionDeclaration; + + assert(funcStmt); + const idx = statements.indexOf(funcStmt); + const statements2 = [ + ...statements.slice(0, idx), + ...importStatements, + ...statements.slice(idx) + ]; + + const stmt0 = funcStmt.body?.statements[0]; + assert(ts.isVariableStatement(stmt0!)); + let vdl = stmt0.declarationList; + vdl.declarations.forEach( + (declaration) => { + const { on, expression } = destructVariableDeclaration(declaration); + if (objectDict[on]) { + const { elements } = expression; + + Object.assign(expression, { + elements: elements.concat(...objectDict[on].map( + ele => factory.createIdentifier(ele) + )) + }); + } + } + ); + + if (objectDict.features) { + const stmt2 = funcStmt.body?.statements[2]; + assert(ts.isVariableStatement(stmt2!)); + const tfDec = stmt2.declarationList.declarations[0]; + const { name, initializer } = tfDec; + assert(ts.isIdentifier(name) && name.text === 'totalFeatures'); + assert(ts.isAsExpression(initializer!)); + const { type } = initializer; + assert(ts.isIntersectionTypeNode(type)); + Object.assign(type, { + types: type.types.concat( + objectDict.features.map( + ele => factory.createTypeReferenceNode( + `${firstLetterUpperCase(ele)}FeatureDict`, + [ + factory.createTypeReferenceNode('EntityDict') + ] + ) + ) + ) + }); + + Object.assign(funcStmt.body!, { + statements: [ + ...funcStmt.body!.statements.slice(0, 5), + ...objectDict.features.map( + (ele) => [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier(`${ele}Features`), + undefined, + undefined, + factory.createCallExpression( + factory.createIdentifier(`create${firstLetterUpperCase(ele)}Features`), + undefined, + [factory.createIdentifier("totalFeatures")] + ) + )], + ts.NodeFlags.Const + ) + ), + factory.createExpressionStatement(factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("Object"), + factory.createIdentifier("assign") + ), + undefined, + [ + factory.createIdentifier("totalFeatures"), + factory.createIdentifier(`${ele}Features`) + ] + )) + ] + ).flat(), + ...funcStmt.body!.statements.slice(5), + ] + }) + } + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(statements2), + sourceFile); + const filename = join(cwd, 'src', 'initialize.dev.ts'); + writeFileSync(filename, result, { flag: 'w' }); + console.log('构建initialize.dev.ts文件成功'); +} + +function outputIntializeFeatures( + cwd: string, + dependencies: string[], + briefNames: string[], + sourceFile: ts.SourceFile, + printer: ts.Printer +) { + + const { statements } = sourceFile; + + const features: string[] = []; + // 所有的import + const importStatements: ts.Statement[] = []; + // 如果有oak-general-business,需要AccessConfiguration,自动注入 + if (dependencies.includes('oak-general-business')) { + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier("accessConfiguration"), + undefined + ), + factory.createStringLiteral("@project/configuration/access"), + undefined + ) + ); + } + dependencies.forEach( + (dep, idx) => { + const depDir = join(cwd, 'node_modules', dep); + if (!existsSync(depDir)) { + throw new Error(`依赖模块${dep}未能找到相应的安装目录【${depDir}】`); + } + const esDir = join(depDir, 'es'); + const libDir = join(depDir, 'lib'); + const esDirExisted = existsSync(esDir); + const libDirExisted = existsSync(libDir); + if (!esDirExisted && !libDirExisted) { + throw new Error(`依赖模块${dep}中没有es或者lib目录`); + } + const destDir = esDirExisted ? esDir : libDir; + const destDirName = esDirExisted ? 'es' : 'lib'; + + // features + if (existsSync(join(destDir, 'features'))) { + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + factory.createIdentifier("initialize"), + factory.createIdentifier(`initialize${firstLetterUpperCase(briefNames[idx])}Features`) + ), + factory.createImportSpecifier( + false, + factory.createIdentifier("FeatureDict"), + factory.createIdentifier(`${firstLetterUpperCase(briefNames[idx])}FeatureDict`) + ) + ]) + ), + factory.createStringLiteral(join(dep, destDirName, 'features')), + undefined + ) + ); + features.push(briefNames[idx]); + } + } + ); + + const funcStmt = statements.find( + (stmt) => ts.isFunctionDeclaration(stmt) && stmt.modifiers?.find( + modifier => modifier.kind === ts.SyntaxKind.ExportKeyword + ) && stmt.modifiers.find( + modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword + ) + ) as ts.FunctionDeclaration; + + assert(funcStmt); + const idx = statements.indexOf(funcStmt); + const statements2 = [ + ...statements.slice(0, idx), + ...importStatements, + ...statements.slice(idx) + ]; + + if (features.length > 0) { + assert(funcStmt.parameters.length === 1); + const [param] = funcStmt.parameters; + const { type } = param!; + assert(ts.isIntersectionTypeNode(type!)); + Object.assign(type, { + types: type.types.concat( + features.map( + ele => factory.createTypeReferenceNode( + `${firstLetterUpperCase(ele)}FeatureDict`, + [ + factory.createTypeReferenceNode('EntityDict') + ] + ) + ) + ) + }); + + Object.assign(funcStmt.body!, { + statements: [ + ...features.map( + (ele, idx) => { + const args = [ + factory.createIdentifier("features"), + ]; + if (dependencies[idx] === 'oak-general-business') { + args.push( + factory.createIdentifier("accessConfiguration") + ); + } + return factory.createExpressionStatement( + factory.createAwaitExpression( + factory.createCallExpression( + factory.createIdentifier(`initialize${firstLetterUpperCase(ele)}Features`), + undefined, + args + ) + ) + ) + } + ).flat(), + ...funcStmt.body!.statements, + ] + }) + } + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(statements2), + sourceFile); + const filename = join(cwd, 'src', 'initializeFeatures.ts'); + writeFileSync(filename, result, { flag: 'w' }); + console.log('构建initializeFeatures.ts文件成功'); +} + +/** + * 本函数用于构建src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext + * 这些和dependency相关的项目文件 + */ +export default function buildDependency(rebuild?: boolean) { + const cwd = process.cwd(); + + const initDevFile = join(cwd, 'src', 'initialize.dev.ts'); + if (existsSync(initDevFile) && !rebuild) { + console.log('src/initialize.dev.ts文件已经存在,无需构建启动文件'); + return; + } + + const depConfigFile = join(cwd, 'src', 'configuration', 'dependency.ts'); + if (!existsSync(depConfigFile)) { + console.error(`${depConfigFile}不存在,无法构建启动文件`); + } + + // 这里依赖配置是ts文件,得翻译成js再读取 + const result = ts.transpileModule(readFileSync(depConfigFile, 'utf-8'), { compilerOptions: { module: ts.ModuleKind.CommonJS } }); + const dependencies = eval(result.outputText) as string[]; + const briefNames = dependencies.map( + (dep, idx) => `${dep.split('-').map(ele => ele[0]).join('')}${idx}` + ); + + const templateFileList = [ + join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.dev.ts'), + join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.prod.ts'), + join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initializeFeatures.ts'), + ]; + + const program = ts.createProgram(templateFileList, {}); + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + + outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0])!, printer); + outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1])!, printer); + outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2])!, printer); +} \ No newline at end of file diff --git a/src/compiler/env.ts b/src/compiler/env.ts index 369be20..d5c9e1b 100644 --- a/src/compiler/env.ts +++ b/src/compiler/env.ts @@ -41,4 +41,6 @@ export const OAK_EXTERNAL_LIBS_FILEPATH = (path: string) => { return Path.join(path, 'config/oakExternalLib.json'); } -export * from './entities'; \ No newline at end of file +export * from './entities'; + +export const OAK_CLI_MODULE_NAME = '@xuchangzju/oak-cli'; \ No newline at end of file diff --git a/src/compiler/routerBuilder.ts b/src/compiler/routerBuilder.ts index afe8661..8ee153f 100644 --- a/src/compiler/routerBuilder.ts +++ b/src/compiler/routerBuilder.ts @@ -3,6 +3,7 @@ import { readdirSync, statSync, existsSync, writeFileSync } from 'fs-extra'; import assert from 'assert'; import * as ts from 'typescript'; import NodeWatch from 'node-watch'; +import { OAK_CLI_MODULE_NAME } from './env'; const { factory } = ts; /** @@ -37,7 +38,7 @@ const { factory } = ts; * -------index.json (此namespace下的配置) * -------pageMap.json (编译器将pageMap注入到这里) * --router - * ----index.ts (编译器将router.ts注入到这里) + * ----index.ts (编译器将allRouters.ts注入到这里) * */ @@ -352,44 +353,39 @@ function judgeUseOakRouterBuilder(statements: ts.NodeArray) { function outputInWebAppDir(appDir: string) { const routerFileName = join(appDir, 'router', 'allRouters.ts'); - const templateFileName = join(appDir, 'router', 'allRoutersTemplate.ts'); - if (existsSync(templateFileName)) { - const program = ts.createProgram([templateFileName], { - removeComments: false, - }); - const routerFile = program.getSourceFile(templateFileName); - assert(routerFile); - const namespaceDir = join(appDir, 'namespaces'); - const { statements } = routerFile; - if (judgeUseOakRouterBuilder(statements)) { - statements.forEach( - (statement) => { - if (ts.isVariableStatement(statement)) { - const declaration = statement.declarationList.declarations.find( - declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'allRouters' - ); - if (declaration) { - Object.assign(declaration, { - initializer: makeWebAllRouters(namespaceDir, join(appDir, '../../../..'), dirname(templateFileName)) - }); - } + const templateFileName = join(appDir, '../../../..', 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'allRouters.ts'); + const program = ts.createProgram([templateFileName], { + removeComments: false, + }); + const routerFile = program.getSourceFile(templateFileName); + assert(routerFile); + const namespaceDir = join(appDir, 'namespaces'); + const { statements } = routerFile; + if (judgeUseOakRouterBuilder(statements)) { + statements.forEach( + (statement) => { + if (ts.isVariableStatement(statement)) { + const declaration = statement.declarationList.declarations.find( + declaration => ts.isIdentifier(declaration.name) && declaration.name.text === 'allRouters' + ); + if (declaration) { + Object.assign(declaration, { + initializer: makeWebAllRouters(namespaceDir, join(appDir, '../../../..'), dirname(routerFileName)) + }); } } - ); + } + ); - const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments: false }); - const result = printer.printNode( - ts.EmitHint.Unspecified, - routerFile, - routerFile, - ); + const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments: false }); + const result = printer.printNode( + ts.EmitHint.Unspecified, + routerFile, + routerFile, + ); - writeFileSync(routerFileName, result); - } - } - else { - console.warn(`${appDir}的目录结构未按照标准建立,缺少了${templateFileName},请从模板中补充`); + writeFileSync(routerFileName, result); } } @@ -415,7 +411,7 @@ function watchDir(projectDir: string, startupDir: string, type: 'native' | 'web' if (startupDir.startsWith('web')) { const srcAppDir = join(projectDir, startupDir, 'src', 'app'); const apps = readdirSync(srcAppDir); - const tryOutputAppDir = (ns: string) => { + const tryOutputAppDir = (ns: string) => { apps.forEach( (app) => { const appDir = join(srcAppDir, app); @@ -427,7 +423,7 @@ function watchDir(projectDir: string, startupDir: string, type: 'native' | 'web' } ); } - + NodeWatch(srcPageDir, { recursive: true, filter: new RegExp('web\.tsx|web\.pc\.tsx|index\.xml|render\.(native|ios|android)\.tsx'),