"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.analyzeDepedency = void 0; 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 fs_extra_1 = require("fs-extra"); const { factory } = ts; /** * 构建项目依赖关系图 * @param cwd * @returns */ function analyzeDepedency(cwd) { const depGraph = { nodeDict: {}, roots: [], ascOrder: [], }; function analyzeOne(dir, name, parent) { const node = { name, parent, }; if (name) { depGraph.nodeDict[name] = node; if (!parent) { depGraph.roots.push(node); } } let dependencies = []; const depConfigTsFile = join(dir, 'src', 'configuration', 'dependency.ts'); if ((0, fs_1.existsSync)(depConfigTsFile)) { // 这里依赖配置是ts文件,得翻译成js再读取 const result = ts.transpileModule((0, fs_1.readFileSync)(depConfigTsFile, 'utf-8'), { compilerOptions: { module: ts.ModuleKind.CommonJS } }); dependencies = eval(result.outputText); } else { const depConfigJsFile = join(dir, 'lib', 'configuration', 'dependency.js'); if ((0, fs_1.existsSync)(depConfigJsFile)) { dependencies = require(depConfigJsFile); } else { // 没有依赖文件,直接返回 return; } } dependencies.forEach((dep) => { const n2 = depGraph.nodeDict[dep]; if (n2) { (0, assert_1.default)(name); } else { let dir2 = join(cwd, 'node_modules', dep); if (!(0, fs_1.existsSync)(dir2)) { dir2 = join(dir, 'node_modules', dep); if (!(0, fs_1.existsSync)(dir2)) { throw new Error(`找不到依赖包${dep}的安装位置,当前包是${dir}`); } } analyzeOne(dir2, dep, name ? node : undefined); } }); } analyzeOne(cwd, ''); // 输出一个从底向上的序列,因为当前的项目中最多只有一个依赖,所以暂时不写 const deps = Object.keys(depGraph.nodeDict); (0, assert_1.default)(deps.length <= 1); depGraph.ascOrder = deps; return depGraph; } exports.analyzeDepedency = analyzeDepedency; 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, }; } function outputPolyfillDts(dependencies, briefNames, sourceFile, printer, filename) { let statements2 = []; if (dependencies.length > 0) { const { statements } = sourceFile; (0, assert_1.default)(ts.isImportDeclaration(statements[5]) && ts.isModuleDeclaration(statements[6])); const importStatements = []; dependencies.forEach((dep, idx) => { importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`))])), factory.createStringLiteral(`${dep}/es/features`), undefined), factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("AspectDict"), factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(briefNames[idx])}AspectDict`))])), factory.createStringLiteral(`${dep}/es/aspects`), undefined)); }); /** * declare global { const OakComponent: MakeOakComponent< EntityDict, BackendRuntimeContext, FrontendRuntimeContext, AspectDict & OgbAspectDict, FeatureDict & OgbFeatureDict >; const features: FeatureDict & OgbFeatureDict; } */ const stmt6 = statements[6]; const { body } = stmt6; const [ocStmt, featuresStmt] = body.statements; (0, assert_1.default)(ts.isVariableStatement(ocStmt) && ts.isVariableStatement(featuresStmt)); const [ocVd] = ocStmt.declarationList.declarations; const [featuresVd] = featuresStmt.declarationList.declarations; (0, assert_1.default)(ts.isVariableDeclaration(ocVd) && ts.isIdentifier(ocVd.name) && ocVd.name.text === 'OakComponent'); (0, assert_1.default)(ts.isVariableDeclaration(featuresVd) && ts.isIdentifier(featuresVd.name) && featuresVd.name.text === 'features'); const ocType = ocVd.type; (0, assert_1.default)(ts.isTypeReferenceNode(ocType) && ocType.typeArguments?.length === 5); const aspectTypeNode = ocType.typeArguments[3]; const featureTypeNode = ocType.typeArguments[4]; (0, assert_1.default)(ts.isTypeReferenceNode(aspectTypeNode) && ts.isTypeReferenceNode(featureTypeNode)); Object.assign(ocType, { typeArguments: [ ...ocType.typeArguments.slice(0, 3), factory.createIntersectionTypeNode([ aspectTypeNode, ...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}AspectDict`), [ factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined) ])) ]), factory.createIntersectionTypeNode([ featureTypeNode, ...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`), [ factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("BackendRuntimeContext"), undefined) ])) ]) ] }); const featureType = featuresVd.type; (0, assert_1.default)(ts.isTypeReferenceNode(featureType)); Object.assign(featuresVd, { type: factory.createIntersectionTypeNode([ featureType, ...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`), [ factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined), factory.createTypeReferenceNode(factory.createIdentifier("BackendRuntimeContext"), undefined) ])) ]) }); statements2 = [ ...statements.slice(0, 6), ...importStatements, ...statements.slice(6) ]; } else { statements2 = [...sourceFile.statements]; } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } function outputDependentExceptions(dependencies, briefNames, sourceFile, printer, filename) { let statements2 = []; if (dependencies.length > 0) { const { statements } = sourceFile; (0, assert_1.default)(ts.isImportDeclaration(statements[2]) && ts.isFunctionDeclaration(statements[3])); const importStatements = []; dependencies.forEach((dep, idx) => { importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("makeException"), factory.createIdentifier(`make${(0, string_1.firstLetterUpperCase)(briefNames[idx])}Exception`))])), factory.createStringLiteral(dep), undefined)); }); const stmt3 = statements[3]; const funcStmt0 = stmt3.body?.statements[0]; (0, assert_1.default)(ts.isVariableStatement(funcStmt0)); const vd = funcStmt0.declarationList.declarations[0]; const { name, initializer } = vd; (0, assert_1.default)(ts.isIdentifier(name) && name.text === 'e'); (0, assert_1.default)(ts.isCallExpression(initializer)); const callExpressions = briefNames.map(ele => factory.createCallExpression(factory.createIdentifier(`make${(0, string_1.firstLetterUpperCase)(ele)}Exception`), [factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined)], [factory.createIdentifier("data")])); const rightExpression = callExpressions.length === 1 ? callExpressions[0] : callExpressions.length === 2 ? factory.createBinaryExpression(callExpressions[0], factory.createToken(ts.SyntaxKind.BarBarToken), callExpressions[1]) : callExpressions.slice(2).reduce((prev, next) => factory.createBinaryExpression(prev, factory.createToken(ts.SyntaxKind.BarBarToken), next), factory.createBinaryExpression(callExpressions[0], factory.createToken(ts.SyntaxKind.BarBarToken), callExpressions[1])); Object.assign(vd, { initializer: factory.createBinaryExpression(initializer, factory.createToken(ts.SyntaxKind.BarBarToken), rightExpression) }); statements2 = [ ...statements.slice(0, 3), ...importStatements, ...statements.slice(3) ]; } else { statements2 = [...sourceFile.statements]; } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } function outputRuntimeCxt(dependencies, briefNames, sourceFile, printer, filename) { let statements2 = []; if (dependencies.length > 0) { const { statements } = sourceFile; const importStatements = []; const stmt9 = statements[9]; const stmt10 = statements[10]; (0, assert_1.default)(ts.isTypeAliasDeclaration(stmt9) && ts.isIdentifier(stmt9.name) && stmt9.name.text === 'AAD'); (0, assert_1.default)(ts.isTypeAliasDeclaration(stmt10) && ts.isIdentifier(stmt10.name) && stmt10.name.text === 'AFD'); (0, assert_1.default)(ts.isImportDeclaration(statements[5]) && ts.isTypeAliasDeclaration(statements[6])); const AADs = []; const AFDs = []; dependencies.forEach((dep, idx) => { const featureName = `${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`; const aspectName = `${(0, string_1.firstLetterUpperCase)(briefNames[idx])}AspectDict`; importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(featureName)), factory.createImportSpecifier(false, factory.createIdentifier("AspectDict"), factory.createIdentifier(aspectName)) ])), factory.createStringLiteral(dep), undefined)); AFDs.push(featureName); AADs.push(aspectName); }); { const { type } = stmt9; (0, assert_1.default)(ts.isTypeReferenceNode(type)); Object.assign(stmt9, { type: factory.createIntersectionTypeNode([ type, ...AADs.map(ele => factory.createTypeReferenceNode(factory.createIdentifier(ele), [factory.createTypeReferenceNode('EntityDict')])) ]) }); } { const { type } = stmt10; (0, assert_1.default)(ts.isIntersectionTypeNode(type)); const { types } = type; Object.assign(type, { types: [ ...types, ...AFDs.map(ele => factory.createTypeReferenceNode(factory.createIdentifier(ele), [factory.createTypeReferenceNode('EntityDict')])) ] }); } statements2 = [ ...statements.slice(0, 6), ...importStatements, ...statements.slice(6) ]; } else { statements2 = [ ...sourceFile.statements ]; } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } function outputDependentContext(depGraph, printer, filename) { // 目前只支持单向依赖,未来可以利用mixin来实现多类的继承 (0, assert_1.default)(depGraph.roots.length <= 1); let root = depGraph.roots[0] ? depGraph.roots[0].name : 'oak-frontend-base'; const statements = [ factory.createExportDeclaration(undefined, false, factory.createNamedExports([ factory.createExportSpecifier(false, undefined, factory.createIdentifier("BackendRuntimeContext")), factory.createExportSpecifier(false, undefined, factory.createIdentifier("FrontendRuntimeContext")) ]), factory.createStringLiteral(root), undefined) ]; const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS)); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } /** * 生成initialize.prod.ts * @param cwd * @param dependencies * @param briefNames * @param sourceFile * @param printer */ function outputIntializeProd(cwd, dependencies, briefNames, sourceFile, printer, filename) { 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); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } /** * 生成initialize.dev.ts * @param cwd * @param dependencies * @param briefNames * @param sourceFile * @param printer */ function outputIntializeDev(cwd, dependencies, briefNames, sourceFile, printer, filename) { 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); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } /** * 根据依赖关系,输出features/index.ts * @param cwd * @param dependencies * @param briefNames * @param printer * @param filename */ function outputFeatureIndex(cwd, dependencies, briefNames, printer, filename) { // todo } function outputIntializeFeatures(cwd, dependencies, briefNames, sourceFile, printer, filename) { 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); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); console.log(`构建${filename}文件成功`); } function injectDataIndexFile(dataIndexFile, briefNames, printer) { const sourceFile = ts.createSourceFile('index.ts', (0, fs_1.readFileSync)(dataIndexFile, 'utf-8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TS); const { statements } = sourceFile; const importStatements = briefNames.map((ele) => factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(`${ele}Data`), undefined), factory.createStringLiteral(`./${ele}Data`), undefined)); /** * 在文件末尾的这个位置上注入引用 export default { relation: relations, actionAuth, relationAuth, path, i18n, }; */ const exportStmt = statements[statements.length - 1]; (0, assert_1.default)(ts.isExportAssignment(exportStmt)); const { expression } = exportStmt; (0, assert_1.default)(ts.isObjectLiteralExpression(expression)); const { properties } = expression; Object.assign(expression, { properties: [ ...properties, ...briefNames.map((ele) => factory.createSpreadAssignment(factory.createIdentifier(`${ele}Data`))) ] }); const statements2 = [ ...importStatements, ...statements, ]; const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(dataIndexFile, result, { flag: 'w' }); console.log(`注入${dataIndexFile}文件成功,共注入了${briefNames.length}个初始化数据引用`); } /** * 尝试将pages目录下的页面移到项目目录中。 * 目前简化处理,假设目录结构都是pages/namespace/entity结构,以entity目录作为单元,如果有就放弃,没有就移植 * @param cwdPageDir * @param modulePageDir */ function tryCopyPages(cwdPageDir, modulePageDir) { // 各个namespace处理 const nss = (0, fs_1.readdirSync)(modulePageDir); nss.forEach((namespace) => { const pages = (0, fs_1.readdirSync)(join(modulePageDir, namespace)); pages.forEach((page) => { const destDir = join(cwdPageDir, namespace, page); if (!(0, fs_1.existsSync)(destDir)) { (0, fs_extra_1.mkdirSync)(destDir); const srcDir = join(modulePageDir, namespace, page); console.log(`拷贝${srcDir}到${destDir}下`); (0, fs_extra_1.copySync)(srcDir, destDir, { recursive: true, }); } }); }); } /** * 对各个依赖项目,可能有些文件需要被移植到项目目录下,逐步完善 * @param cwd * @param dependencies * @param briefNames */ function tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer) { const injectDataIndexFileDependencies = []; const injectDataIndexFileBriefNames = []; dependencies.forEach((dep, idx) => { const moduleDir = join(cwd, 'node_modules', dep); const moduleTemplateDir = join(moduleDir, 'template'); if ((0, fs_1.existsSync)(moduleTemplateDir)) { const entitiesDir = join(moduleTemplateDir, 'entities'); if ((0, fs_1.existsSync)(entitiesDir)) { // entities目录下的定义直接拷贝过去(如果尚不存在的话) const prjEntitiesDir = join(cwd, 'src', 'entities'); const result = (0, fs_1.readdirSync)(entitiesDir); result.forEach((filename) => { if (!(0, fs_1.existsSync)(join(prjEntitiesDir, filename))) { console.log(`拷贝${join(entitiesDir, filename)}到${prjEntitiesDir}目录下`); (0, fs_extra_1.copySync)(join(entitiesDir, filename), join(prjEntitiesDir, filename)); } }); } // data.ts中规定的初始化数据,拷贝到data目录下,并注入到data/index.ts const dataFile = join(moduleTemplateDir, 'data.ts'); if ((0, fs_1.existsSync)(dataFile)) { const prjDataFile = join(cwd, 'src', 'data', `${briefNames[idx]}Data.ts`); if (!(0, fs_1.existsSync)(prjDataFile)) { console.log(`拷贝${dataFile}到${prjDataFile}中`); (0, fs_extra_1.copySync)(dataFile, prjDataFile); injectDataIndexFileDependencies.push(dep); injectDataIndexFileBriefNames.push(briefNames[idx]); } } // pages中设计的页面,拷贝到pages对应的目录下,考虑namespace const pageDir = join(moduleTemplateDir, 'pages'); if ((0, fs_1.existsSync)(pageDir)) { tryCopyPages(join(cwd, 'src', 'pages'), pageDir); } } }); if (injectDataIndexFileBriefNames.length > 0) { injectDataIndexFile(join(cwd, 'src', 'data', 'index.ts'), injectDataIndexFileBriefNames, printer); } } /** * 本函数用于构建src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext * 这些和dependency相关的项目文件 */ function buildDependency(rebuild) { const cwd = process.cwd(); const depConfigFile = join(cwd, 'src', 'configuration', 'dependency.ts'); if (!(0, fs_1.existsSync)(depConfigFile)) { console.error(`${depConfigFile}不存在,无法构建启动文件`); } const depGraph = analyzeDepedency(cwd); // 依赖如果是树形关系,应当从底层的被依赖者开始初始化 const dependencies = depGraph.ascOrder; 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'), join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'RuntimeCxt.ts'), join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'DependentExceptions.ts'), join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'polyfill.d.ts') ]; const program = ts.createProgram(templateFileList, {}); const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); const initDevFile = join(cwd, 'src', 'initialize.dev.ts'); if ((0, fs_1.existsSync)(initDevFile) && !rebuild) { console.log(`[${initDevFile}]文件已经存在,无需构建启动文件`); } else { outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0]), printer, initDevFile); } const initProdFile = join(cwd, 'src', 'initialize.prod.ts'); if ((0, fs_1.existsSync)(initProdFile) && !rebuild) { console.log(`[${initProdFile}]文件已经存在,无需构建启动文件`); } else { outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1]), printer, initProdFile); } const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts'); if ((0, fs_1.existsSync)(initFeaturesFile) && !rebuild) { console.log(`[${initFeaturesFile}]文件已经存在,无需构建启动文件`); } else { outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2]), printer, initFeaturesFile); } const dependentContextFile = join(cwd, 'src', 'context', 'DependentContext.ts'); if ((0, fs_1.existsSync)(dependentContextFile) && !rebuild) { console.log(`[${dependentContextFile}]文件已经存在,无需构建启动文件`); } else { outputDependentContext(depGraph, printer, dependentContextFile); } const runtimeCxtFile = join(cwd, 'src', 'types', 'RuntimeCxt.ts'); if ((0, fs_1.existsSync)(runtimeCxtFile) && !rebuild) { console.log(`[${runtimeCxtFile}]文件已经存在,无需构建启动文件`); } else { outputRuntimeCxt(dependencies, briefNames, program.getSourceFile(templateFileList[3]), printer, runtimeCxtFile); } const dependentExceptionsFile = join(cwd, 'src', 'types', 'DependentExceptions.ts'); if ((0, fs_1.existsSync)(dependentExceptionsFile) && !rebuild) { console.log(`[${dependentExceptionsFile}]文件已经存在,无需构建启动文件`); } else { outputDependentExceptions(dependencies, briefNames, program.getSourceFile(templateFileList[4]), printer, dependentExceptionsFile); } const polyfillDtsFile = join(cwd, 'typings', 'polyfill.d.ts'); if ((0, fs_1.existsSync)(polyfillDtsFile) && !rebuild) { console.log(`[${polyfillDtsFile}]文件已经存在,无需构建启动文件`); } else { outputPolyfillDts(dependencies, briefNames, program.getSourceFile(templateFileList[5]), printer, polyfillDtsFile); } // 把各个依赖项目的一些初始化的文件拷贝过去 tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer); } exports.default = buildDependency;