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

252 lines (251 loc) • 12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertToApplicationExecutor = convertToApplicationExecutor; const devkit_1 = require("@nx/devkit"); const posix_1 = require("node:path/posix"); const targets_1 = require("../../utils/targets"); const setup_ssr_1 = require("../setup-ssr/setup-ssr"); const validations_1 = require("../utils/validations"); const version_utils_1 = require("../utils/version-utils"); const executorsToConvert = new Set([ '@angular-devkit/build-angular:browser', '@angular-devkit/build-angular:browser-esbuild', '@nx/angular:webpack-browser', '@nx/angular:browser-esbuild', ]); const serverTargetExecutors = new Set([ '@angular-devkit/build-angular:server', '@nx/angular:webpack-server', ]); const redundantExecutors = new Set([ '@angular-devkit/build-angular:server', '@angular-devkit/build-angular:prerender', '@angular-devkit/build-angular:app-shell', '@angular-devkit/build-angular:ssr-dev-server', '@nx/angular:webpack-server', ]); async function convertToApplicationExecutor(tree, options) { let didAnySucceed = false; if (options.project) { (0, validations_1.validateProject)(tree, options.project); didAnySucceed = await convertProjectTargets(tree, options.project, true); } else { const projects = (0, devkit_1.getProjects)(tree); for (const [projectName] of projects) { devkit_1.logger.info(`Converting project "${projectName}"...`); const success = await convertProjectTargets(tree, projectName); if (success) { devkit_1.logger.info(`Project "${projectName}" converted successfully.`); } else { devkit_1.logger.info(`Project "${projectName}" could not be converted. See above for more information.`); } devkit_1.logger.info(''); didAnySucceed = didAnySucceed || success; } } if (!options.skipFormat) { await (0, devkit_1.formatFiles)(tree); } return didAnySucceed ? () => (0, devkit_1.installPackagesTask)(tree) : () => { }; } async function convertProjectTargets(tree, projectName, isProvidedProject = false) { function warnIfProvided(message) { if (isProvidedProject) { devkit_1.logger.warn(message); } } let project = (0, devkit_1.readProjectConfiguration)(tree, projectName); if (project.projectType !== 'application') { warnIfProvided(`The provided project "${projectName}" is not an application. Skipping conversion.`); return false; } const { buildTargetName, serverTargetName } = getTargetsToConvert(project.targets); if (!buildTargetName) { warnIfProvided(`The provided project "${projectName}" does not have any targets using on of the ` + `'@angular-devkit/build-angular:browser', '@angular-devkit/build-angular:browser-esbuild', ` + `'@nx/angular:browser' and '@nx/angular:browser-esbuild' executors. Skipping conversion.`); return false; } const useNxExecutor = project.targets[buildTargetName].executor.startsWith('@nx/angular:'); let newExecutor; if (useNxExecutor) { newExecutor = '@nx/angular:application'; } else { const { major: angularMajorVersion } = (0, version_utils_1.getInstalledAngularVersionInfo)(tree); newExecutor = angularMajorVersion >= 20 ? '@angular/build:application' : '@angular-devkit/build-angular:application'; } const buildTarget = project.targets[buildTargetName]; buildTarget.executor = newExecutor; if (buildTarget.outputs) { buildTarget.outputs = buildTarget.outputs.map((output) => output === '{options.outputPath}' ? '{options.outputPath.base}' : output); } for (const [, options] of (0, targets_1.allTargetOptions)(buildTarget)) { if (options['index'] === '') { options['index'] = false; } // Rename and transform options options['browser'] = options['main']; if (serverTargetName && typeof options['browser'] === 'string') { options['server'] = (0, posix_1.dirname)(options['browser']) + '/main.server.ts'; } options['serviceWorker'] = options['ngswConfigPath'] ?? options['serviceWorker']; if (typeof options['polyfills'] === 'string') { options['polyfills'] = [options['polyfills']]; } let outputPath = options['outputPath']; if (typeof outputPath === 'string') { if (!/\/browser\/?$/.test(outputPath)) { devkit_1.logger.warn(`The output location of the browser build has been updated from "${outputPath}" to ` + `"${(0, posix_1.join)(outputPath, 'browser')}". ` + 'You might need to adjust your deployment pipeline or, as an alternative, ' + 'set outputPath.browser to "" in order to maintain the previous functionality.'); } else { outputPath = outputPath.replace(/\/browser\/?$/, ''); } options['outputPath'] = { base: outputPath, }; if (typeof options['resourcesOutputPath'] === 'string') { const media = options['resourcesOutputPath'].replaceAll('/', ''); if (media && media !== 'media') { options['outputPath'] = { base: outputPath, media: media, }; } } } // Delete removed options delete options['vendorChunk']; delete options['commonChunk']; delete options['resourcesOutputPath']; delete options['buildOptimizer']; delete options['main']; delete options['ngswConfigPath']; } // Merge browser and server tsconfig if (serverTargetName) { const browserTsConfigPath = buildTarget?.options?.tsConfig; const serverTsConfigPath = project.targets['server']?.options?.tsConfig; if (typeof browserTsConfigPath !== 'string') { devkit_1.logger.warn(`Cannot update project "${projectName}" to use the application executor ` + `as the browser tsconfig cannot be located.`); } if (typeof serverTsConfigPath !== 'string') { devkit_1.logger.warn(`Cannot update project "${projectName}" to use the application executor ` + `as the server tsconfig cannot be located.`); } const browserTsConfigJson = (0, devkit_1.readJson)(tree, browserTsConfigPath); const serverTsConfigJson = (0, devkit_1.readJson)(tree, serverTsConfigPath); const serverFiles = ['src/main.server.ts', 'src/server.ts']; if (tree.exists((0, posix_1.join)(project.root, 'src/app/app.config.server.ts'))) { serverFiles.push('src/app/app.config.server.ts'); } const files = new Set([ ...(browserTsConfigJson.files ?? []), ...(serverTsConfigJson.files ?? []), ]); // Server files will be added later if needed by the setup-ssr generator files.delete('server.ts'); files.delete('src/server.ts'); files.delete('src/main.server.ts'); if (files.size) { browserTsConfigJson.files = Array.from(files); } else if (browserTsConfigJson.files) { delete browserTsConfigJson.files; } browserTsConfigJson.compilerOptions ?? {}; browserTsConfigJson.compilerOptions.types = Array.from(new Set([ ...(browserTsConfigJson.compilerOptions.types ?? []), ...(serverTsConfigJson.compilerOptions?.types ?? []), ])); if (browserTsConfigJson.exclude?.length) { const normalizeExclude = (exclude) => exclude.startsWith('./') ? exclude.slice(2) : exclude; browserTsConfigJson.exclude = browserTsConfigJson.exclude.filter((exclude) => !serverFiles.includes(normalizeExclude(exclude))); } (0, devkit_1.writeJson)(tree, browserTsConfigPath, browserTsConfigJson); // Delete server tsconfig tree.delete(serverTsConfigPath); } // Update project main tsconfig const projectRootTsConfigPath = (0, posix_1.join)(project.root, 'tsconfig.json'); if (tree.exists(projectRootTsConfigPath)) { const rootTsConfigJson = (0, devkit_1.readJson)(tree, projectRootTsConfigPath); rootTsConfigJson.compilerOptions ?? {}; rootTsConfigJson.compilerOptions.esModuleInterop = true; rootTsConfigJson.compilerOptions.downlevelIteration = undefined; rootTsConfigJson.compilerOptions.allowSyntheticDefaultImports = undefined; (0, devkit_1.writeJson)(tree, projectRootTsConfigPath, rootTsConfigJson); } // Update server file const ssrMainFile = project.targets['server']?.options?.['main']; if (typeof ssrMainFile === 'string') { tree.delete(ssrMainFile); // apply changes so the setup-ssr generator can access the updated project (0, devkit_1.updateProjectConfiguration)(tree, projectName, project); await (0, setup_ssr_1.setupSsr)(tree, { project: projectName, skipFormat: true }); // re-read project configuration as it might have changed project = (0, devkit_1.readProjectConfiguration)(tree, projectName); } // Delete all redundant targets for (const [targetName, target] of Object.entries(project.targets)) { if (redundantExecutors.has(target.executor)) { delete project.targets[targetName]; } } (0, devkit_1.updateProjectConfiguration)(tree, projectName, project); return true; } function getTargetsToConvert(targets) { let buildTargetName; let serverTargetName; for (const target of Object.keys(targets)) { if (targets[target].executor === '@nx/angular:application' || targets[target].executor === '@angular/build:application' || targets[target].executor === '@angular-devkit/build-angular:application') { devkit_1.logger.warn('The project is already using the application builder. Skipping conversion.'); return {}; } // build target if (executorsToConvert.has(targets[target].executor)) { for (const [, options] of (0, targets_1.allTargetOptions)(targets[target])) { if (options.customWebpackConfig) { devkit_1.logger.warn(`The project is using a custom webpack configuration which is not supported by the esbuild-based application executor. Skipping conversion.`); return {}; } } if (buildTargetName) { devkit_1.logger.warn('The project has more than one build target. Skipping conversion.'); return {}; } buildTargetName = target; } // server target if (serverTargetExecutors.has(targets[target].executor)) { if (targets[target].executor === '@nx/angular:webpack-server') { for (const [, options] of (0, targets_1.allTargetOptions)(targets[target])) { if (options.customWebpackConfig) { devkit_1.logger.warn(`The project is using a custom webpack configuration which is not supported by the esbuild-based application executor. Skipping conversion.`); return {}; } } } if (serverTargetName) { devkit_1.logger.warn('The project has more than one server target. Skipping conversion.'); return {}; } serverTargetName = target; } } return { buildTargetName, serverTargetName }; } exports.default = convertToApplicationExecutor;