UNPKG

@o3r/schematics

Version:

Schematics module of the Otter framework

204 lines • 9.74 kB
"use strict"; 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