@o3r/schematics
Version:
Schematics module of the Otter framework
204 lines • 9.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAppModuleFilePath = getAppModuleFilePath;
exports.getMainFilePath = getMainFilePath;
exports.isApplicationThatUsesRouterModule = isApplicationThatUsesRouterModule;
exports.addImportToModuleFile = addImportToModuleFile;
exports.insertImportToModuleFile = insertImportToModuleFile;
exports.addProviderToModuleFile = addProviderToModuleFile;
exports.insertBeforeModule = insertBeforeModule;
const fs = require("node:fs");
const path = require("node:path");
const schematics_1 = require("@angular-devkit/schematics");
const ast_utils_1 = require("@schematics/angular/utility/ast-utils");
const change_1 = require("@schematics/angular/utility/change");
const globby_1 = require("globby");
const ts = require("typescript");
const ast_1 = require("./ast");
const loaders_1 = require("./loaders");
/**
* Get the path to the `app.module.ts`
* In case of standalone application, get the path to the `app.config.ts` instead
* @param tree File tree
* @param context Context of the rule
* @param projectName The name of the project where to search for an app module file
*/
function getAppModuleFilePath(tree, context, projectName) {
const workspaceProject = projectName ? (0, loaders_1.getWorkspaceConfig)(tree)?.projects[projectName] : undefined;
// exit if not an application
if (!workspaceProject || workspaceProject.projectType !== 'application') {
context.logger.debug('Aborted. App module file path will be searched only in application project.');
return undefined;
}
const mainFilePath = workspaceProject.architect.build.options.main ?? workspaceProject.architect.build.options.browser;
const mainFile = tree.read(mainFilePath).toString();
const bootstrapModuleRegexpResult = mainFile.match(/(?:bootstrapModule|bootstrapApplication)\((?:[^),]*,)*\s*([^ ),]*)\s*\)/m);
if (!bootstrapModuleRegexpResult || !bootstrapModuleRegexpResult[1]) {
throw new schematics_1.SchematicsException('Could not find bootstrap module or appConfig');
}
const bootstrapModule = bootstrapModuleRegexpResult[1];
const findSource = new RegExp(`import +\\{[^}]*${bootstrapModule}[^}]*\\} +from +['"]([^'"]+)['"] *;`, 'm');
const bootstrapModuleFileRegExpResult = mainFile.match(findSource);
if (!bootstrapModuleFileRegExpResult || !bootstrapModuleFileRegExpResult[1]) {
throw new schematics_1.SchematicsException('Could not find bootstrap module or appConfig');
}
/** Path to the main module file */
const moduleFilePath = path.join(path.dirname(mainFilePath), bootstrapModuleFileRegExpResult[1] + '.ts');
const exportAppModuleClassRegExp = new RegExp(`(?:class|const)\\s+${bootstrapModule}`, 'm');
if (tree.exists(moduleFilePath) && exportAppModuleClassRegExp.test(tree.read(moduleFilePath).toString())) {
return moduleFilePath;
}
const possibleAppModule = path.join(path.dirname(mainFilePath), path.dirname(bootstrapModuleFileRegExpResult[1]), 'app.module.ts');
if (tree.exists(possibleAppModule) && exportAppModuleClassRegExp.test(tree.read(possibleAppModule).toString())) {
return possibleAppModule;
}
// search app module source file in tree
const normalizedFilePath = moduleFilePath.split(path.sep).join(path.posix.sep);
const prog = ts.createProgram([normalizedFilePath], {});
const symbols = (0, ast_1.getExportedSymbolsFromFile)(prog, normalizedFilePath);
const bootstrapModuleSymbol = symbols.find((s) => s.name === bootstrapModule);
const checker = prog.getTypeChecker();
if (bootstrapModuleSymbol) {
const pathPlusModuleString = checker.getFullyQualifiedName(bootstrapModuleSymbol);
const filePath = pathPlusModuleString?.replace(new RegExp(`.${bootstrapModule}`), '').replace(/["']/g, '');
const relativeFilePath = filePath ? path.relative(path.dirname(mainFilePath), `${filePath}.ts`) : undefined;
const filePathInTree = relativeFilePath ? path.join(path.dirname(mainFilePath), relativeFilePath) : undefined;
if (filePathInTree && tree.exists(filePathInTree) && exportAppModuleClassRegExp.test(tree.read(filePathInTree).toString())) {
return filePathInTree;
}
}
throw new schematics_1.SchematicsException(`Could not find ${bootstrapModule} source file`);
}
/**
* Get the path to the main.ts
* @param tree File tree
* @param context Context of the rule
* @param projectName
*/
function getMainFilePath(tree, context, projectName) {
const workspaceProject = projectName ? (0, loaders_1.getWorkspaceConfig)(tree)?.projects[projectName] : undefined;
// exit if not an application
if (!workspaceProject) {
context.logger.debug('Register localization on main module only in application project');
return undefined;
}
const mainFilePath = workspaceProject.architect.build.options.main ?? workspaceProject.architect.build.options.browser;
return mainFilePath;
}
/**
* Returns true if the project is an application and contains a TS file that imports the angular RouterModule in
* one of its modules.
* @param tree
* @param options @see RuleFactory.options
* @param options.projectName
*/
function isApplicationThatUsesRouterModule(tree, options) {
const workspaceProject = options.projectName ? (0, loaders_1.getWorkspaceConfig)(tree)?.projects[options.projectName] : undefined;
const cwd = process.cwd().replace(/[/\\]+/g, '/');
const root = (workspaceProject?.root && cwd.endsWith(workspaceProject.root)) ? workspaceProject.root.replace(/[^/\\]+/g, '..') : '.';
return workspaceProject?.sourceRoot
&& (0, globby_1.sync)(path.posix.join(root, workspaceProject.sourceRoot, '**', '*.ts')).some((filePath) => {
const fileContent = fs.readFileSync(filePath).toString();
if (!/RouterModule/.test(fileContent)) {
return false;
}
const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.ES2015, true);
try {
return !!(0, ast_utils_1.getRouterModuleDeclaration)(sourceFile);
}
catch { }
return false;
});
}
/**
* Add import to the main module
* @param name
* @param file
* @param sourceFile
* @param sourceFileContent
* @param context
* @param recorder
* @param moduleFilePath
* @param moduleIndex
* @param moduleFunction
* @param override
*/
function addImportToModuleFile(name, file, sourceFile, sourceFileContent, context, recorder, moduleFilePath, moduleIndex, moduleFunction, override = false) {
const importMatch = sourceFileContent.slice(moduleIndex).match(new RegExp(`(${name})(\\.[a-zA-Z\\s\\n]+\\()?(,\\n?)?`));
if (!!importMatch && !override) {
context.logger.warn(`Skipped ${name} (already imported)`);
return recorder;
}
else if (importMatch?.[2]) {
context.logger.warn(`Skipped ${name}${moduleFunction || ''} (already imported with method). Cannot override automatically`);
return recorder;
}
else if (override && (0, ast_utils_1.isImported)(sourceFile, name, file) && !!importMatch && !Number.isNaN(importMatch.index)) {
recorder = recorder.remove(moduleIndex + importMatch.index, importMatch[0].length);
recorder = recorder.insertLeft(moduleIndex + importMatch.index, moduleIndex + importMatch.index > moduleIndex ? name + moduleFunction + (importMatch[3] || '') : name);
}
else {
(0, ast_utils_1.addImportToModule)(sourceFile, moduleFilePath, name, file)
.forEach((change) => {
if (change instanceof change_1.InsertChange) {
recorder = recorder.insertLeft(change.pos, moduleFunction && change.pos > moduleIndex ? change.toAdd.replace(name, name + moduleFunction) : change.toAdd);
}
});
}
return recorder;
}
/**
* Insert import on top of the main module file
* @param name
* @param file
* @param sourceFile
* @param recorder
* @param moduleFilePath
* @param isDefault
*/
function insertImportToModuleFile(name, file, sourceFile, recorder, moduleFilePath, isDefault) {
const importChange = (0, ast_utils_1.insertImport)(sourceFile, moduleFilePath, name, file, isDefault);
if (importChange instanceof change_1.InsertChange) {
return recorder.insertLeft(importChange.pos, importChange.toAdd);
}
return recorder;
}
/**
* Add providers to the main module
* @param name
* @param file
* @param sourceFile
* @param sourceFileContent
* @param context
* @param recorder
* @param moduleFilePath
* @param moduleIndex
* @param customProvider
*/
function addProviderToModuleFile(name, file, sourceFile, sourceFileContent, context, recorder, moduleFilePath, moduleIndex, customProvider) {
if (new RegExp(name).test(sourceFileContent.substr(moduleIndex))) {
context.logger.warn(`Skipped ${name} (already provided)`);
return recorder;
}
(0, ast_utils_1.addProviderToModule)(sourceFile, moduleFilePath, name, file)
.forEach((change) => {
if (change instanceof change_1.InsertChange) {
recorder = recorder.insertLeft(change.pos, customProvider && change.pos > moduleIndex ? change.toAdd.replace(name, customProvider) : change.toAdd);
}
});
return recorder;
}
/**
* Add custom code before the module definition
* @param line
* @param file
* @param recorder
* @param moduleIndex
*/
function insertBeforeModule(line, file, recorder, moduleIndex) {
if (!file.includes(line.replace(/\s*/g, ''))) {
return recorder.insertLeft(moduleIndex - 1, `${line}\n\n`);
}
return recorder;
}
//# sourceMappingURL=modules.js.map