diff --git a/lib/compiler/dependencyBuilder.js b/lib/compiler/dependencyBuilder.js index 3e860ca..7b0e749 100644 --- a/lib/compiler/dependencyBuilder.js +++ b/lib/compiler/dependencyBuilder.js @@ -8,6 +8,7 @@ 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; /** * 构建项目依赖关系图 @@ -94,6 +95,82 @@ function destructVariableDeclaration(vd) { 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, { + arguments: [ + ...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) { @@ -127,7 +204,7 @@ function outputDependentExceptions(dependencies, briefNames, sourceFile, printer } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); - console.log('构建types/DependentException.ts文件成功'); + console.log(`构建${filename}文件成功`); } function outputRuntimeCxt(dependencies, briefNames, sourceFile, printer, filename) { let statements2 = []; @@ -185,7 +262,7 @@ function outputRuntimeCxt(dependencies, briefNames, sourceFile, printer, filenam } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); - console.log('构建types/RuntimeCxt.ts文件成功'); + console.log(`构建${filename}文件成功`); } function outputDependentContext(depGraph, printer, filename) { // 目前只支持单向依赖,未来可以利用mixin来实现多类的继承 @@ -199,7 +276,7 @@ function outputDependentContext(depGraph, printer, filename) { ]; 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('构建context/DependentContext.ts文件成功'); + console.log(`构建${filename}文件成功`); } /** * 生成initialize.prod.ts @@ -327,7 +404,7 @@ function outputIntializeProd(cwd, dependencies, briefNames, sourceFile, printer, } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); - console.log('构建initialize.prod.ts文件成功'); + console.log(`构建${filename}文件成功`); } /** * 生成initialize.dev.ts @@ -487,7 +564,7 @@ function outputIntializeDev(cwd, dependencies, briefNames, sourceFile, printer, } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); - console.log('构建initialize.dev.ts文件成功'); + console.log(`构建${filename}文件成功`); } function outputIntializeFeatures(cwd, dependencies, briefNames, sourceFile, printer, filename) { const { statements } = sourceFile; @@ -556,7 +633,111 @@ function outputIntializeFeatures(cwd, dependencies, briefNames, sourceFile, prin } const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); - console.log('构建initializeFeatures.ts文件成功'); + 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 @@ -577,7 +758,8 @@ function buildDependency(rebuild) { 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', '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 }); @@ -623,5 +805,14 @@ function buildDependency(rebuild) { 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; diff --git a/lib/compiler/routerBuilder.js b/lib/compiler/routerBuilder.js index bfd5c10..1a52b96 100644 --- a/lib/compiler/routerBuilder.js +++ b/lib/compiler/routerBuilder.js @@ -161,8 +161,8 @@ function judgeUseOakRouterBuilder(statements) { return ts.isExpressionStatement(stmt) && ts.isStringLiteral(stmt.expression) && stmt.expression.text === 'use oak router builder'; } function outputInWebAppDir(appDir) { - const routerFileName = (0, path_1.join)(appDir, 'router', 'allRouters.ts'); - const templateFileName = (0, path_1.join)(appDir, '../../../..', 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'allRouters.ts'); + const routerFileName = (0, path_1.join)(appDir, 'routers', 'allRouters.ts'); + 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, }); @@ -176,7 +176,7 @@ function outputInWebAppDir(appDir) { 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)) + initializer: makeWebAllRouters(namespaceDir, (0, path_1.join)(appDir, '../../..'), (0, path_1.dirname)(routerFileName)) }); } } @@ -188,14 +188,7 @@ function outputInWebAppDir(appDir) { } function outputInWebDir(dir) { const srcAppDir = (0, path_1.join)(dir, 'src', 'app'); - const apps = (0, fs_extra_1.readdirSync)(srcAppDir); - apps.forEach((app) => { - const appDir = (0, path_1.join)(srcAppDir, app); - const stat = (0, fs_extra_1.statSync)(appDir); - if (stat.isDirectory()) { - outputInWebAppDir(appDir); - } - }); + outputInWebAppDir(srcAppDir); } function watchDir(projectDir, startupDir, type) { const srcPageDir = (0, path_1.join)(projectDir, 'src', 'pages'); diff --git a/src/compiler/dependencyBuilder.ts b/src/compiler/dependencyBuilder.ts index 3dad9e1..37eef85 100644 --- a/src/compiler/dependencyBuilder.ts +++ b/src/compiler/dependencyBuilder.ts @@ -1,9 +1,10 @@ import assert from 'assert'; import { join as pathJoin } from 'path'; -import { existsSync, readFileSync, writeFileSync } from 'fs'; +import { existsSync, readFileSync, writeFileSync, readdirSync } from 'fs'; import * as ts from 'typescript'; import { firstLetterLowerCase, firstLetterUpperCase } from '../utils/string'; import { OAK_CLI_MODULE_NAME } from './env'; +import { copySync, mkdirSync } from 'fs-extra'; const { factory } = ts; type DepNode = { @@ -113,6 +114,168 @@ function destructVariableDeclaration(vd: ts.VariableDeclaration) { }; } +function outputPolyfillDts( + dependencies: string[], + briefNames: string[], + sourceFile: ts.SourceFile, + printer: ts.Printer, + filename: string +) { + let statements2: ts.Statement[] = []; + if (dependencies.length > 0) { + const { statements } = sourceFile; + assert(ts.isImportDeclaration(statements[5]) && ts.isModuleDeclaration(statements[6])); + + const importStatements: ts.Statement[] = []; + + dependencies.forEach( + (dep, idx) => { + importStatements.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + factory.createIdentifier("FeatureDict"), + factory.createIdentifier(`${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(`${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] as ts.ModuleDeclaration; + const { body } = stmt6; + const [ocStmt, featuresStmt] = (body).statements; + assert(ts.isVariableStatement(ocStmt) && ts.isVariableStatement(featuresStmt)); + const [ocVd] = ocStmt.declarationList.declarations; + const [featuresVd] = featuresStmt.declarationList.declarations; + assert(ts.isVariableDeclaration(ocVd) && ts.isIdentifier(ocVd.name) && ocVd.name.text === 'OakComponent'); + assert(ts.isVariableDeclaration(featuresVd) && ts.isIdentifier(featuresVd.name) && featuresVd.name.text === 'features'); + + const ocType = ocVd.type!; + assert(ts.isTypeReferenceNode(ocType) && ocType.typeArguments?.length === 5); + const aspectTypeNode = ocType.typeArguments[3]; + const featureTypeNode = ocType.typeArguments[4]; + assert(ts.isTypeReferenceNode(aspectTypeNode) && ts.isTypeReferenceNode(featureTypeNode)); + Object.assign(ocType, { + arguments: [ + ...ocType.typeArguments.slice(0, 3), + factory.createIntersectionTypeNode( + [ + aspectTypeNode, + ...briefNames.map( + (ele) => factory.createTypeReferenceNode( + factory.createIdentifier(`${firstLetterUpperCase(ele)}AspectDict`), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("EntityDict"), + undefined + ) + ] + ) + ) + ] + ), + factory.createIntersectionTypeNode( + [ + featureTypeNode, + ...briefNames.map( + (ele) => factory.createTypeReferenceNode( + factory.createIdentifier(`${firstLetterUpperCase(ele)}FeatureDict`), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("EntityDict"), + undefined + ), + factory.createTypeReferenceNode( + factory.createIdentifier("BackendRuntimeContext"), + undefined + ) + ] + ) + ) + ] + ) + ] + }); + + const featureType = featuresVd.type!; + assert(ts.isTypeReferenceNode(featureType)); + Object.assign(featuresVd, { + type: factory.createIntersectionTypeNode( + [ + featureType, + ...briefNames.map( + (ele) => factory.createTypeReferenceNode( + factory.createIdentifier(`${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); + + writeFileSync(filename, result, { flag: 'w' }); + console.log(`构建${filename}文件成功`); +} + function outputDependentExceptions( dependencies: string[], briefNames: string[], @@ -208,7 +371,7 @@ function outputDependentExceptions( sourceFile); writeFileSync(filename, result, { flag: 'w' }); - console.log('构建types/DependentException.ts文件成功'); + console.log(`构建${filename}文件成功`); } function outputRuntimeCxt( @@ -221,13 +384,13 @@ function outputRuntimeCxt( let statements2: ts.Statement[] = []; if (dependencies.length > 0) { const { statements } = sourceFile; - + const importStatements: ts.Statement[] = []; const stmt9 = statements[9]; const stmt10 = statements[10]; assert(ts.isTypeAliasDeclaration(stmt9) && ts.isIdentifier(stmt9.name) && stmt9.name.text === 'AAD'); assert(ts.isTypeAliasDeclaration(stmt10) && ts.isIdentifier(stmt10.name) && stmt10.name.text === 'AFD') - + assert(ts.isImportDeclaration(statements[5]) && ts.isTypeAliasDeclaration(statements[6])); const AADs = [] as string[]; const AFDs = [] as string[]; @@ -303,7 +466,7 @@ function outputRuntimeCxt( sourceFile); writeFileSync(filename, result, { flag: 'w' }); - console.log('构建types/RuntimeCxt.ts文件成功'); + console.log(`构建${filename}文件成功`); } function outputDependentContext(depGraph: DepGraph, printer: ts.Printer, filename: string) { @@ -339,7 +502,7 @@ function outputDependentContext(depGraph: DepGraph, printer: ts.Printer, filenam ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS)); writeFileSync(filename, result, { flag: 'w' }); - console.log('构建context/DependentContext.ts文件成功'); + console.log(`构建${filename}文件成功`); } /** @@ -591,7 +754,7 @@ function outputIntializeProd( sourceFile); writeFileSync(filename, result, { flag: 'w' }); - console.log('构建initialize.prod.ts文件成功'); + console.log(`构建${filename}文件成功`); } /** @@ -909,7 +1072,7 @@ function outputIntializeDev( sourceFile); writeFileSync(filename, result, { flag: 'w' }); - console.log('构建initialize.dev.ts文件成功'); + console.log(`构建${filename}文件成功`); } function outputIntializeFeatures( @@ -1055,7 +1218,163 @@ function outputIntializeFeatures( sourceFile); writeFileSync(filename, result, { flag: 'w' }); - console.log('构建initializeFeatures.ts文件成功'); + console.log(`构建${filename}文件成功`); +} + + +function injectDataIndexFile( + dataIndexFile: string, + briefNames: string[], + printer: ts.Printer, +) { + const sourceFile = ts.createSourceFile('index.ts', readFileSync(dataIndexFile, 'utf-8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TS); + + const { statements } = sourceFile; + const importStatements: ts.Statement[] = 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]; + assert(ts.isExportAssignment(exportStmt)); + const { expression } = exportStmt; + assert(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); + + writeFileSync(dataIndexFile, result, { flag: 'w' }); + console.log(`注入${dataIndexFile}文件成功,共注入了${briefNames.length}个初始化数据引用`); +} + +/** + * 尝试将pages目录下的页面移到项目目录中。 + * 目前简化处理,假设目录结构都是pages/namespace/entity结构,以entity目录作为单元,如果有就放弃,没有就移植 + * @param cwdPageDir + * @param modulePageDir + */ +function tryCopyPages( + cwdPageDir: string, + modulePageDir: string, +) { + // 各个namespace处理 + const nss = readdirSync(modulePageDir); + nss.forEach( + (namespace) => { + const pages = readdirSync(join(modulePageDir, namespace)); + + pages.forEach( + (page) => { + const destDir = join(cwdPageDir, namespace, page); + if (!existsSync(destDir)) { + mkdirSync(destDir); + const srcDir = join(modulePageDir, namespace, page); + console.log(`拷贝${srcDir}到${destDir}下`); + copySync(srcDir, destDir, { + recursive: true, + }); + } + } + ); + } + ) + +} + +/** + * 对各个依赖项目,可能有些文件需要被移植到项目目录下,逐步完善 + * @param cwd + * @param dependencies + * @param briefNames + */ +function tryCopyModuleTemplateFiles( + cwd: string, + dependencies: string[], + briefNames: string[], + printer: ts.Printer +) { + const injectDataIndexFileDependencies: string[] = []; + const injectDataIndexFileBriefNames: string[] = []; + + dependencies.forEach( + (dep, idx) => { + const moduleDir = join(cwd, 'node_modules', dep); + const moduleTemplateDir = join(moduleDir, 'template'); + if (existsSync(moduleTemplateDir)) { + const entitiesDir = join(moduleTemplateDir, 'entities'); + if (existsSync(entitiesDir)) { + // entities目录下的定义直接拷贝过去(如果尚不存在的话) + const prjEntitiesDir = join(cwd, 'src', 'entities'); + const result = readdirSync(entitiesDir); + result.forEach( + (filename) => { + if (!existsSync(join(prjEntitiesDir, filename))) { + console.log(`拷贝${join(entitiesDir, filename)}到${prjEntitiesDir}目录下`); + copySync(join(entitiesDir, filename), join(prjEntitiesDir, filename)); + } + } + ); + } + + // data.ts中规定的初始化数据,拷贝到data目录下,并注入到data/index.ts + const dataFile = join(moduleTemplateDir, 'data.ts'); + if (existsSync(dataFile)) { + const prjDataFile = join(cwd, 'src', 'data', `${briefNames[idx]}Data.ts`); + if (!existsSync(prjDataFile)) { + console.log(`拷贝${dataFile}到${prjDataFile}中`); + copySync(dataFile, prjDataFile); + injectDataIndexFileDependencies.push(dep); + injectDataIndexFileBriefNames.push(briefNames[idx]); + } + } + + // pages中设计的页面,拷贝到pages对应的目录下,考虑namespace + const pageDir = join(moduleTemplateDir, 'pages'); + if (existsSync(pageDir)) { + tryCopyPages(join(cwd, 'src', 'pages'), pageDir); + } + } + } + ); + + if (injectDataIndexFileBriefNames.length > 0) { + injectDataIndexFile(join(cwd, 'src', 'data', 'index.ts'), injectDataIndexFileBriefNames, printer); + } } /** @@ -1083,7 +1402,8 @@ export default function buildDependency(rebuild?: boolean) { join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.prod.ts'), join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'initializeFeatures.ts'), join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'RuntimeCxt.ts'), - join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'DependentExceptions.ts') + join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'DependentExceptions.ts'), + join(cwd, 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'polyfill.d.ts') ]; const program = ts.createProgram(templateFileList, {}); @@ -1130,7 +1450,7 @@ export default function buildDependency(rebuild?: boolean) { else { outputRuntimeCxt(dependencies, briefNames, program.getSourceFile(templateFileList[3])!, printer, runtimeCxtFile); } - + const dependentExceptionsFile = join(cwd, 'src', 'types', 'DependentExceptions.ts'); if (existsSync(dependentExceptionsFile) && !rebuild) { console.log(`[${dependentExceptionsFile}]文件已经存在,无需构建启动文件`); @@ -1138,4 +1458,15 @@ export default function buildDependency(rebuild?: boolean) { else { outputDependentExceptions(dependencies, briefNames, program.getSourceFile(templateFileList[4])!, printer, dependentExceptionsFile); } + + const polyfillDtsFile = join(cwd, 'typings', 'polyfill.d.ts'); + if (existsSync(polyfillDtsFile) && !rebuild) { + console.log(`[${polyfillDtsFile}]文件已经存在,无需构建启动文件`); + } + else { + outputPolyfillDts(dependencies, briefNames, program.getSourceFile(templateFileList[5])!, printer, polyfillDtsFile); + } + + // 把各个依赖项目的一些初始化的文件拷贝过去 + tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer); } \ No newline at end of file diff --git a/src/compiler/routerBuilder.ts b/src/compiler/routerBuilder.ts index 8ee153f..85128b2 100644 --- a/src/compiler/routerBuilder.ts +++ b/src/compiler/routerBuilder.ts @@ -352,8 +352,8 @@ function judgeUseOakRouterBuilder(statements: ts.NodeArray) { } function outputInWebAppDir(appDir: string) { - const routerFileName = join(appDir, 'router', 'allRouters.ts'); - const templateFileName = join(appDir, '../../../..', 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'allRouters.ts'); + const routerFileName = join(appDir, 'routers', 'allRouters.ts'); + const templateFileName = join(appDir, '../../..', 'node_modules', OAK_CLI_MODULE_NAME, 'templateFiles', 'allRouters.ts'); const program = ts.createProgram([templateFileName], { removeComments: false, }); @@ -370,7 +370,7 @@ function outputInWebAppDir(appDir: string) { ); if (declaration) { Object.assign(declaration, { - initializer: makeWebAllRouters(namespaceDir, join(appDir, '../../../..'), dirname(routerFileName)) + initializer: makeWebAllRouters(namespaceDir, join(appDir, '../../..'), dirname(routerFileName)) }); } } @@ -391,17 +391,7 @@ function outputInWebAppDir(appDir: string) { function outputInWebDir(dir: string) { const srcAppDir = join(dir, 'src', 'app'); - const apps = readdirSync(srcAppDir); - apps.forEach( - (app) => { - const appDir = join(srcAppDir, app); - const stat = statSync(appDir); - - if (stat.isDirectory()) { - outputInWebAppDir(appDir); - } - } - ) + outputInWebAppDir(srcAppDir); } function watchDir(projectDir: string, startupDir: string, type: 'native' | 'web' | 'wechatMp') {