oak-domain/lib/compiler/dependencyBuilder.js

1033 lines
53 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeDepedency = analyzeDepedency;
exports.default = buildDependency;
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 lodash_1 = require("lodash");
const { factory } = ts;
/**
* 构建项目依赖关系图
* @param cwd
* @returns
*/
function analyzeDepedency(cwd) {
const depGraph = {
nodes: {},
dependencies: {},
ascOrder: [],
};
function analyzeOne(dir, name) {
if (name) {
if (!depGraph.nodes[name]) {
depGraph.nodes[name] = 1;
depGraph.dependencies[name] = {};
}
}
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).default;
}
else {
// 没有依赖文件,直接返回
return;
}
}
dependencies.forEach((dep) => {
if (name) {
depGraph.dependencies[name][dep] = 1;
}
if (!depGraph.nodes[dep]) {
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);
}
});
}
analyzeOne(cwd, '');
// 输出一个依赖关系从底向上的序列,类似于图中的反向遍历算法
do {
const deps = Object.keys(depGraph.nodes);
if (deps.length === 0) {
break;
}
const freeNodes = [];
for (const n of deps) {
if (Object.keys(depGraph.dependencies[n]).length === 0) {
freeNodes.push(n);
}
}
(0, assert_1.default)(freeNodes.length > 0, '依赖关系成环!');
freeNodes.forEach((n) => {
(0, lodash_1.unset)(depGraph.nodes, n);
(0, lodash_1.unset)(depGraph.dependencies, n);
for (const n2 in depGraph.dependencies) {
(0, lodash_1.unset)(depGraph.dependencies[n2], n);
}
depGraph.ascOrder.push(n);
});
} while (true);
return depGraph;
}
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: nonNullExpression } = vd;
(0, assert_1.default)(ts.isNonNullExpression(nonNullExpression));
const { expression: callExpression } = nonNullExpression;
(0, assert_1.default)(ts.isCallExpression(callExpression));
(0, assert_1.default)(ts.isIdentifier(callExpression.expression) && callExpression.expression.text === 'mergeConcatMany');
(0, assert_1.default)(callExpression.arguments.length === 1);
const [arg] = callExpression.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<EntityDict, BackendRuntimeContext>,
FeatureDict & OgbFeatureDict<EntityDict>
>;
const features: FeatureDict & OgbFeatureDict<EntityDict>;
}
*/
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)
]))
])
]
});
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)
]))
])
});
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("ED"), 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 outputContext(depGraph, sourceFile, printer, filename) {
// 目前只支持单向依赖未来可以利用mixin来实现多类的继承
// assert(depGraph.roots.length <= 1);
let root = depGraph.ascOrder.length > 0 ? depGraph.ascOrder[depGraph.ascOrder.length - 1] : 'oak-frontend-base';
const { statements } = sourceFile;
const isBackend = filename.includes('BackendRuntimeContext');
const statements2 = [
factory.createImportDeclaration(undefined, factory.createImportClause(false, factory.createIdentifier(isBackend ? "BaseBackendRuntimeContext" : "BaseFrontendRuntimeContext"), undefined), factory.createStringLiteral(`@${root}/context/${isBackend ? 'BackendRuntimeContext' : 'FrontendRuntimeContext'}`), undefined),
...statements
];
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile);
(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.js'))) {
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];
}
}
// 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(dependencies, briefNames, sourceFile, printer, filename, isModule) {
const { statements } = sourceFile;
let statements2 = [];
if (dependencies.length > 0) {
const importStatements = [];
const fdNames = [];
const adNames = [];
dependencies.forEach((dep, idx) => {
const fdName = `${(0, string_1.firstLetterUpperCase)(briefNames[idx])}FeatureDict`;
const adName = `${(0, string_1.firstLetterUpperCase)(briefNames[idx])}AspectDict`;
// 导入FeatureDict和AspectDict
importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([
factory.createImportSpecifier(false, factory.createIdentifier("FeatureDict"), factory.createIdentifier(fdName)),
factory.createImportSpecifier(false, factory.createIdentifier("AspectDict"), factory.createIdentifier(adName))
])), factory.createStringLiteral(dep), undefined));
fdNames.push(fdName);
adNames.push(adName);
});
// 导入自己的AspectDictimport { AspectDict } from '../aspects/AspectDict';
importStatements.push(factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("AspectDict"), factory.createIdentifier("ProjectAspectDict"))])), factory.createStringLiteral("../aspects/AspectDict"), undefined));
// 创建一个这样的type: type MergeAspectDict = ProjectAspectDict & GenernalAspectDict<EntityDict>;
// 除了ProjectAspectDict还有其他的AspectDict需要<EntityDict>参数
const adTypeDeclaration = factory.createTypeAliasDeclaration(undefined, factory.createIdentifier("MergeAspectDict"), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("ProjectAspectDict"), undefined),
...adNames.map(ad => {
return factory.createTypeReferenceNode(factory.createIdentifier(ad), [factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined)]);
})
]));
let i = 0;
while (true) {
const stmt = statements[i];
if (ts.isFunctionDeclaration(stmt)) {
break;
}
i++;
}
const stmt3 = statements[i - 1], stmt4 = statements[i];
(0, assert_1.default)(ts.isImportDeclaration(stmt3) && ts.isFunctionDeclaration(stmt4));
const { name, parameters } = stmt4;
(0, assert_1.default)(name && ts.isIdentifier(name) && name.text === 'create' && parameters.length === 1);
const [param] = parameters;
const { name: paramName, type } = param;
(0, assert_1.default)(ts.isIdentifier(paramName) && paramName.text === 'features' && ts.isTypeReferenceNode(type));
Object.assign(param, {
type: factory.createIntersectionTypeNode([
type,
...fdNames.map(ele => factory.createTypeReferenceNode(ele, [
factory.createTypeReferenceNode('EntityDict')
]))
])
});
statements2 = [
...statements.slice(0, i),
...importStatements,
adTypeDeclaration,
...statements.slice(i)
];
if (isModule) {
statements2.push(factory.createFunctionDeclaration([
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.AsyncKeyword)
], undefined, factory.createIdentifier("initialize"), [
factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("ED"), factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined), undefined)
], [
factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("features"), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("FeatureDict"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
factory.createTypeReferenceNode(factory.createIdentifier("BasicFeatures"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]))
]), undefined)
], undefined, factory.createBlock([], true)));
}
}
else {
statements2 = [...statements];
}
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile);
(0, fs_1.writeFileSync)(filename, result, { flag: 'w' });
console.log(`构建${filename}文件成功`);
}
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')
])))
});
}
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}个初始化数据引用`);
}
/**
* 将依赖项目的目录去覆盖原来的目录
* @param fromDir 依赖项目的目录
* @param toDir 当前项目的目录
*/
function tryCopyFilesRecursively(fromDir, toDir, rebuild) {
const files = (0, fs_1.readdirSync)(fromDir);
files.forEach((file) => {
const fromFile = join(fromDir, file);
const toFile = join(toDir, file);
const stat = (0, fs_1.statSync)(fromFile);
let skip = false;
if (stat.isFile()) {
if ((0, fs_1.existsSync)(join(toDir, file))) {
if (rebuild) {
console.log(`覆盖文件${toFile}`);
}
else {
console.log(`忽略文件${toFile}`);
skip = true;
}
}
else {
console.log(`拷贝文件${toFile}`);
}
!skip && (0, fs_extra_1.copySync)(fromFile, toFile, {
overwrite: true,
});
}
else {
if (!(0, fs_1.existsSync)(toFile)) {
console.log(`创建文件夹${toFile}`);
(0, fs_extra_1.mkdirSync)(toFile);
}
tryCopyFilesRecursively(fromFile, toFile, rebuild);
}
});
}
/**
* 对各个依赖项目,可能有些文件需要被移植到项目目录下,逐步完善
* @param cwd
* @param dependencies
* @param briefNames
*/
function tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer, rebuild) {
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)) {
// 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) || rebuild) {
console.log(`拷贝${dataFile}${prjDataFile}`);
(0, fs_extra_1.copySync)(dataFile, prjDataFile);
injectDataIndexFileDependencies.push(dep);
injectDataIndexFileBriefNames.push(briefNames[idx]);
}
}
// src下面的文件是可以拷贝到项目中的
const srcDir = join(moduleTemplateDir, 'src');
if ((0, fs_1.existsSync)(srcDir)) {
tryCopyFilesRecursively(srcDir, join(cwd, 'src'), rebuild);
}
}
});
if (injectDataIndexFileBriefNames.length > 0) {
injectDataIndexFile(join(cwd, 'src', 'data', 'index.ts'), injectDataIndexFileBriefNames, printer);
}
}
/**
* 对于module类型的项目在feature/index.ts中注入initialize函数
* @param cwd
* @param dependencies
* @param briefNames
*/
function injectInitializeToFeatureIndex(cwd, dependencies, briefNames, printer) {
const featureIndexFile = join(cwd, 'src', 'features', 'index.ts');
const sourceFile = ts.createSourceFile('index.ts', (0, fs_1.readFileSync)(featureIndexFile, 'utf-8'), ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
const { statements } = sourceFile;
const initializeStmt = statements.find((stmt) => ts.isFunctionDeclaration(stmt) && ts.isIdentifier(stmt.name) && stmt.name.text === 'initialize');
if (!initializeStmt) {
const statements2 = [
...(dependencies.map((dep, idx) => 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), undefined))),
...statements,
factory.createFunctionDeclaration([
factory.createToken(ts.SyntaxKind.ExportKeyword),
factory.createToken(ts.SyntaxKind.AsyncKeyword)
], undefined, factory.createIdentifier("initialize"), [
factory.createTypeParameterDeclaration(undefined, factory.createIdentifier("ED"), factory.createTypeReferenceNode(factory.createIdentifier("EntityDict"), undefined), undefined)
], [
factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier("features"), undefined, factory.createIntersectionTypeNode([
factory.createTypeReferenceNode(factory.createIdentifier("FeatureDict"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
factory.createTypeReferenceNode(factory.createIdentifier("BasicFeatures"), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]),
...briefNames.map((ele) => factory.createTypeReferenceNode(factory.createIdentifier(`${(0, string_1.firstLetterUpperCase)(ele)}FeatureDict`), [factory.createTypeReferenceNode(factory.createIdentifier("ED"), undefined)]))
]), undefined)
], undefined, factory.createBlock([], true))
];
const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements2), sourceFile);
(0, fs_1.writeFileSync)(featureIndexFile, result, { flag: 'w' });
console.log(`注入${featureIndexFile}文件成功用户可以自己修正initialize函数的参数和逻辑`);
}
}
/**
* 本函数用于构建src/initialize.dev, src/initialize.prod, src/initializeFeatures, src/context/FrontendContext, src/contextBackendContext
* 这些和dependency相关的项目文件
*/
function buildDependency(rebuild) {
const cwd = process.cwd();
const webDir = join(cwd, 'web');
const isModule = !(0, fs_1.existsSync)(webDir); // 如果没有web目录说明是module不需要处理模块级别的文件注入
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.frontend.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'initialize.server.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'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'features/index.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'BackendRuntimeContext.ts'),
join(cwd, 'node_modules', env_1.OAK_CLI_MODULE_NAME, 'templateFiles', 'FrontendRuntimeContext.ts')
];
const program = ts.createProgram(templateFileList, {});
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
let output = true;
if (!isModule) {
const initDevFile = join(cwd, 'src', 'initialize.frontend.ts');
if ((0, fs_1.existsSync)(initDevFile)) {
let tips = `[${initDevFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建';
}
console.log(tips);
}
if (output) {
outputIntializeDev(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[0]), printer, initDevFile);
}
output = true;
const initProdFile = join(cwd, 'src', 'initialize.server.ts');
if ((0, fs_1.existsSync)(initProdFile) && !rebuild) {
let tips = `[${initProdFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建';
}
console.log(tips);
}
if (output) {
outputIntializeProd(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[1]), printer, initProdFile);
}
output = true;
const initFeaturesFile = join(cwd, 'src', 'initializeFeatures.ts');
if ((0, fs_1.existsSync)(initFeaturesFile) && !rebuild) {
let tips = `[${initFeaturesFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建,注意如果自定义过初始化过程,需要重新输入';
}
console.log(tips);
}
if (output) {
outputIntializeFeatures(cwd, dependencies, briefNames, program.getSourceFile(templateFileList[2]), printer, initFeaturesFile);
}
}
output = true;
const runtimeCxtFile = join(cwd, 'src', 'types', 'RuntimeCxt.ts');
if ((0, fs_1.existsSync)(runtimeCxtFile) && !rebuild) {
let tips = `[${runtimeCxtFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建';
}
console.log(tips);
}
if (output) {
outputRuntimeCxt(dependencies, briefNames, program.getSourceFile(templateFileList[3]), printer, runtimeCxtFile);
}
output = true;
const dependentExceptionsFile = join(cwd, 'src', 'types', 'DependentExceptions.ts');
if ((0, fs_1.existsSync)(dependentExceptionsFile) && !rebuild) {
let tips = `[${dependentExceptionsFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建';
}
console.log(tips);
}
if (output) {
outputDependentExceptions(dependencies, briefNames, program.getSourceFile(templateFileList[4]), printer, dependentExceptionsFile);
}
output = true;
const polyfillDtsFile = join(cwd, 'typings', 'polyfill.d.ts');
if ((0, fs_1.existsSync)(polyfillDtsFile) && !rebuild) {
let tips = `[${polyfillDtsFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建';
}
console.log(tips);
}
if (output) {
outputPolyfillDts(dependencies, briefNames, program.getSourceFile(templateFileList[5]), printer, polyfillDtsFile);
}
output = true;
const featureIndexFile = join(cwd, 'src', 'features', 'index.ts');
if ((0, fs_1.existsSync)(featureIndexFile) && !rebuild) {
let tips = `[${featureIndexFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建';
}
console.log(tips);
}
else {
outputFeatureIndex(dependencies, briefNames, program.getSourceFile(templateFileList[6]), printer, featureIndexFile, isModule);
}
output = true;
let contextFile = join(cwd, 'src', 'context', 'BackendRuntimeContext.ts');
if ((0, fs_1.existsSync)(contextFile) && !rebuild) {
let tips = `[${contextFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建,注意如果自定义过上下文的其它方法或属性,需要重新输入';
}
console.log(tips);
}
if (output) {
outputContext(depGraph, program.getSourceFile(templateFileList[7]), printer, contextFile);
}
output = true;
contextFile = join(cwd, 'src', 'context', 'FrontendRuntimeContext.ts');
if ((0, fs_1.existsSync)(contextFile) && !rebuild) {
let tips = `[${contextFile}]文件已经存在`;
if (!rebuild) {
tips += ',忽略构建。';
output = false;
}
else {
tips += ',将重新构建,注意如果自定义过上下文的其它方法或属性,需要重新输入';
}
console.log(tips);
}
if (output) {
outputContext(depGraph, program.getSourceFile(templateFileList[8]), printer, contextFile);
}
// 把各个依赖项目的一些初始化的文件拷贝过去
if (!isModule) {
tryCopyModuleTemplateFiles(cwd, dependencies, briefNames, printer, rebuild);
}
}