UNPKG

@nx/angular

Version:

The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: - Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Playwright and Cypre

379 lines (378 loc) • 17.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AppMigrator = void 0; const devkit_1 = require("@nx/devkit"); const js_1 = require("@nx/js"); const path_1 = require("path"); const utilities_1 = require("../../utilities"); const builders_1 = require("../builders"); const e2e_migrator_1 = require("./e2e.migrator"); const project_migrator_1 = require("./project.migrator"); const supportedTargets = { build: { builders: [ '@angular/build:application', '@angular-devkit/build-angular:application', '@angular-devkit/build-angular:browser', '@angular-devkit/build-angular:browser-esbuild', ], }, e2e: { acceptMultipleDefinitions: true, builders: [ '@angular-devkit/build-angular:protractor', '@cypress/schematic:cypress', ], }, i18n: { builders: [ '@angular/build:extract-i18n', '@angular-devkit/build-angular:extract-i18n', ], }, prerender: { builders: [ '@nguniversal/builders:prerender', '@angular-devkit/build-angular:prerender', ], }, serve: { builders: [ '@angular/build:dev-server', '@angular-devkit/build-angular:dev-server', ], }, server: { builders: ['@angular-devkit/build-angular:server'] }, serveSsr: { builders: [ '@nguniversal/builders:ssr-dev-server', '@angular-devkit/build-angular:ssr-dev-server', ], }, }; // TODO(leo): this will replace `supportedTargets` once the full refactor is done. const supportedBuilderMigrators = [ builders_1.AngularBuildUnitTestMigrator, builders_1.AngularDevkitKarmaMigrator, builders_1.AngularEslintLintMigrator, ]; class AppMigrator extends project_migrator_1.ProjectMigrator { constructor(tree, options, project, logger) { super(tree, options, supportedTargets, project, 'apps', logger, supportedBuilderMigrators); const eslintBuilderMigrator = this.builderMigrators[supportedBuilderMigrators.indexOf(builders_1.AngularEslintLintMigrator)]; this.e2eMigrator = new e2e_migrator_1.E2eMigrator(tree, options, project, eslintBuilderMigrator.targets.size ? Object.keys(eslintBuilderMigrator.targets)[0] : undefined); } async migrate() { await super.migrate(); if (this.skipMigration === true) { return; } this.updateProjectConfiguration(); await this.e2eMigrator.migrate(); this.moveProjectFiles(); this.updateProjectConfigurationTargets(); for (const builderMigrator of this.builderMigrators ?? []) { await builderMigrator.migrate(); } this.updateTsConfigs(); this.setCacheableOperations(); } validate() { const errors = [ ...(super.validate() ?? []), ...(this.e2eMigrator.validate() ?? []), ]; for (const builderMigrator of this.builderMigrators) { errors.push(...(builderMigrator.validate() ?? [])); } return errors.length ? errors : null; } moveProjectFiles() { // project is self-contained and can be safely moved if (this.project.oldRoot !== '') { this.moveDir(this.project.oldRoot, this.project.newRoot); return; } // we need to pick what to move because the project is in the workspace root // it is not required to have a browserslist this.moveProjectRootFile('browserslist', false); this.moveProjectRootFile('.browserslistrc', false); if (this.targetNames.build) { this.moveFilePathsFromTargetToProjectRoot(this.projectConfig.targets[this.targetNames.build], ['tsConfig', 'webWorkerTsConfig', 'ngswConfigPath']); } if (this.targetNames.server) { this.moveFilePathsFromTargetToProjectRoot(this.projectConfig.targets[this.targetNames.server], ['tsConfig']); } this.moveDir(this.project.oldSourceRoot, this.project.newSourceRoot); } setCacheableOperations() { const toCache = []; if (this.targetNames.build && !this.shouldSkipTargetTypeMigration('build')) { toCache.push(this.targetNames.build); } if (this.targetNames.e2e && !this.shouldSkipTargetTypeMigration('e2e')) { toCache.push(this.targetNames.e2e); } if (toCache.length) { this.updateCacheableOperations(toCache); } } updateProjectConfiguration() { (0, utilities_1.convertToNxProject)(this.tree, this.project.name); this.moveFile((0, devkit_1.joinPathFragments)(this.project.oldRoot, 'project.json'), (0, devkit_1.joinPathFragments)(this.project.newRoot, 'project.json')); this.projectConfig.root = this.project.newRoot; this.projectConfig.sourceRoot = this.project.newSourceRoot; (0, devkit_1.updateProjectConfiguration)(this.tree, this.project.name, { ...this.projectConfig, }); } updateProjectConfigurationTargets() { if (!this.projectConfig.targets || !Object.keys(this.projectConfig.targets).length) { this.logger.warn('The project does not have any targets configured. Skipping updating targets.'); return; } this.updateBuildTargetConfiguration(); this.updateServerTargetConfiguration(); this.updatePrerenderTargetConfiguration(); this.updateServeSsrTargetConfiguration(); (0, devkit_1.updateProjectConfiguration)(this.tree, this.project.name, { ...this.projectConfig, }); } updateTsConfigs() { const rootTsConfigFile = (0, js_1.getRootTsConfigPathInTree)(this.tree); const projectOffsetFromRoot = (0, devkit_1.offsetFromRoot)(this.projectConfig.root); this.updateTsConfigFileUsedByBuildTarget(rootTsConfigFile, projectOffsetFromRoot); this.updateTsConfigFileUsedByServerTarget(projectOffsetFromRoot); } convertBuildOptions(buildOptions, target, updateOutputs = true) { const { executor } = target; const isApplicationBuilder = executor === '@angular/build:application' || executor === '@angular-devkit/build-angular:application'; if (updateOutputs) { if (buildOptions.outputPath) { if (isApplicationBuilder) { if (typeof buildOptions.outputPath === 'string') { buildOptions.outputPath = (0, devkit_1.joinPathFragments)('dist', this.project.newRoot); } else if (buildOptions.outputPath.base) { buildOptions.outputPath.base = (0, devkit_1.joinPathFragments)('dist', this.project.newRoot); } } else { buildOptions.outputPath = (0, devkit_1.joinPathFragments)('dist', this.project.newRoot, this.targetNames.server ? 'browser' : ''); } } else if (isApplicationBuilder) { buildOptions.outputPath = (0, devkit_1.joinPathFragments)('dist', this.projectName); } if (typeof buildOptions.outputPath === 'string') { target.outputs = ['{options.outputPath}']; } else if (buildOptions.outputPath?.base) { target.outputs = ['{options.outputPath.base}']; } } if (buildOptions.index) { if (typeof buildOptions.index === 'string') { buildOptions.index = this.convertAsset(buildOptions.index); } else { buildOptions.index.input = buildOptions.index.input && this.convertAsset(buildOptions.index.input); } } buildOptions.main = buildOptions.main && this.convertAsset(buildOptions.main); buildOptions.browser = buildOptions.browser && this.convertAsset(buildOptions.browser); buildOptions.server = buildOptions.server && this.convertAsset(buildOptions.server); buildOptions.polyfills = buildOptions.polyfills && (Array.isArray(buildOptions.polyfills) ? buildOptions.polyfills.map((asset) => this.convertSourceRootPath(asset)) : this.convertSourceRootPath(buildOptions.polyfills)); buildOptions.tsConfig = buildOptions.tsConfig && (0, devkit_1.joinPathFragments)(this.project.newRoot, (0, path_1.basename)(buildOptions.tsConfig)); buildOptions.assets = buildOptions.assets && buildOptions.assets.map((asset) => this.convertAsset(asset)); buildOptions.styles = buildOptions.styles && buildOptions.styles.map((style) => this.convertAsset(style)); buildOptions.scripts = buildOptions.scripts && buildOptions.scripts.map((script) => this.convertAsset(script)); buildOptions.fileReplacements = buildOptions.fileReplacements && buildOptions.fileReplacements.map((replacement) => ({ replace: this.convertAsset(replacement.replace), with: this.convertAsset(replacement.with), })); buildOptions.serviceWorker = buildOptions.serviceWorker && typeof buildOptions.serviceWorker === 'string' && this.convertAsset(buildOptions.serviceWorker); buildOptions.ngswConfigPath = buildOptions.ngswConfigPath && this.convertAsset(buildOptions.ngswConfigPath); if (buildOptions.prerender?.routesFile) { buildOptions.prerender.routesFile = this.convertAsset(buildOptions.prerender.routesFile); } buildOptions.ssr = buildOptions.ssr && typeof buildOptions.ssr === 'string' && this.convertAsset(buildOptions.ssr); } convertServerOptions(serverOptions) { serverOptions.outputPath = serverOptions.outputPath && (0, devkit_1.joinPathFragments)('dist', this.project.newRoot, 'server'); serverOptions.main = serverOptions.main && this.convertPath(serverOptions.main); serverOptions.tsConfig = serverOptions.tsConfig && (0, devkit_1.joinPathFragments)(this.project.newRoot, (0, path_1.basename)(serverOptions.tsConfig)); serverOptions.fileReplacements = serverOptions.fileReplacements && serverOptions.fileReplacements.map((replacement) => ({ replace: this.convertAsset(replacement.replace), with: this.convertAsset(replacement.with), })); } updateBuildTargetConfiguration() { if (!this.targetNames.build) { this.logger.warn('There is no build target in the project configuration. Skipping updating the build target configuration.'); return; } if (this.shouldSkipTargetTypeMigration('build')) { return; } const buildTarget = this.projectConfig.targets[this.targetNames.build]; if (!buildTarget.options && (!buildTarget.configurations || !Object.keys(buildTarget.configurations).length)) { this.logger.warn(`The target "${this.targetNames.build}" is not specifying any options or configurations. Skipping updating the target configuration.`); return; } const buildDevTsConfig = buildTarget.options?.tsConfig ?? buildTarget.configurations?.development?.tsConfig; if (!buildDevTsConfig) { this.logger.warn(`The "${this.targetNames.build}" target does not have the "tsConfig" option configured. Skipping updating the tsConfig file.`); } else { const newBuildDevTsConfig = this.convertPath(buildDevTsConfig); if (!this.tree.exists(newBuildDevTsConfig)) { this.logger.warn(`The tsConfig file "${buildDevTsConfig}" specified in the "${this.targetNames.build}" target could not be found. Skipping updating the tsConfig file.`); } } this.convertBuildOptions(buildTarget.options ?? {}, buildTarget); Object.values(buildTarget.configurations ?? {}).forEach((config) => this.convertBuildOptions(config, buildTarget, false)); } updatePrerenderTargetConfiguration() { if (!this.targetNames.prerender || this.shouldSkipTargetTypeMigration('prerender')) { return; } const prerenderTarget = this.projectConfig.targets[this.targetNames.prerender]; if (!prerenderTarget.options) { this.logger.warn(`The target "${this.targetNames.prerender}" is not specifying any options. Skipping updating the target configuration.`); return; } prerenderTarget.options.routesFile = prerenderTarget.options.routesFile && this.convertPath(prerenderTarget.options.routesFile); } updateServerTargetConfiguration() { if (!this.targetNames.server || this.shouldSkipTargetTypeMigration('server')) { return; } const serverTarget = this.projectConfig.targets[this.targetNames.server]; if (!serverTarget.options && (!serverTarget.configurations || !Object.keys(serverTarget.configurations).length)) { this.logger.warn(`The target "${this.targetNames.server}" is not specifying any options or configurations. Skipping updating the target configuration.`); return; } const serverDevTsConfig = serverTarget.options?.tsConfig ?? serverTarget.configurations?.development?.tsConfig; if (!serverDevTsConfig) { this.logger.warn(`The "${this.targetNames.server}" target does not have the "tsConfig" option configured. Skipping updating the tsConfig file.`); } else { const newServerDevTsConfig = this.convertPath(serverDevTsConfig); if (!this.tree.exists(newServerDevTsConfig)) { this.logger.warn(`The tsConfig file "${serverDevTsConfig}" specified in the "${this.targetNames.server}" target could not be found. Skipping updating the tsConfig file.`); } } this.convertServerOptions(serverTarget.options ?? {}); Object.values(serverTarget.configurations ?? {}).forEach((config) => this.convertServerOptions(config)); } updateServeSsrTargetConfiguration() { if (!this.targetNames.serveSsr || this.shouldSkipTargetTypeMigration('serveSsr')) { return; } const ssrTarget = this.targetNames.serveSsr ?? this.targetNames['serve-ssr']; const serveSsrTarget = this.projectConfig.targets[ssrTarget]; if (!serveSsrTarget.options && (!serveSsrTarget.configurations || !Object.keys(serveSsrTarget.configurations).length)) { this.logger.warn(`The target "${ssrTarget}" is not specifying any options or configurations. Skipping updating the target configuration.`); return; } ['sslKey', 'sslCert', 'proxyConfig'].forEach((option) => { if (serveSsrTarget.options) { serveSsrTarget.options[option] = serveSsrTarget.options[option] && this.convertPath(serveSsrTarget.options[option]); } for (const configuration of Object.values(serveSsrTarget.configurations ?? {})) { serveSsrTarget.configurations[configuration][option] = serveSsrTarget.configurations[configuration][option] && this.convertPath(serveSsrTarget.configurations[configuration][option]); } }); } updateTsConfigFileUsedByBuildTarget(rootTsConfigFile, projectOffsetFromRoot) { if (!this.targetNames.build || this.shouldSkipTargetTypeMigration('build')) { return; } const tsConfigPath = this.projectConfig.targets[this.targetNames.build].options?.tsConfig ?? this.projectConfig.targets[this.targetNames.build].configurations ?.development?.tsConfig; if (!tsConfigPath || !this.tree.exists(tsConfigPath)) { // we already logged a warning for these cases, so just return return; } this.updateTsConfigFile(tsConfigPath, rootTsConfigFile, projectOffsetFromRoot); } updateTsConfigFileUsedByServerTarget(projectOffsetFromRoot) { if (!this.targetNames.server || this.shouldSkipTargetTypeMigration('server')) { return; } const tsConfigPath = this.projectConfig.targets[this.targetNames.server].options?.tsConfig ?? this.projectConfig.targets[this.targetNames.server].configurations ?.development?.tsConfig; if (!tsConfigPath || !this.tree.exists(tsConfigPath)) { // we already logged a warning for these cases, so just return return; } (0, devkit_1.updateJson)(this.tree, tsConfigPath, (json) => { json.compilerOptions = json.compilerOptions ?? {}; json.compilerOptions.outDir = `${projectOffsetFromRoot}dist/out-tsc`; return json; }); } } exports.AppMigrator = AppMigrator;