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

185 lines (171 loc) • 7.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = default_1; const devkit_1 = require("@nx/devkit"); const path_1 = require("path"); const version_utils_1 = require("../../generators/utils/version-utils"); const targets_1 = require("../../utils/targets"); const projects_1 = require("../utils/projects"); const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup"); const UNIVERSAL_PACKAGES = [ '@nguniversal/common', '@nguniversal/express-engine', ]; /** * Regexp to match Universal packages. * @nguniversal/common/engine * @nguniversal/common * @nguniversal/express-engine **/ const NGUNIVERSAL_PACKAGE_REGEXP = /@nguniversal\/(common(\/engine)?|express-engine)/g; const serverExecutors = [ '@angular-devkit/build-angular:server', '@nx/angular:webpack-server', ]; async function default_1(tree) { const packageJson = (0, devkit_1.readJson)(tree, 'package.json'); if (!UNIVERSAL_PACKAGES.some((pkg) => packageJson.dependencies?.[pkg] || packageJson.devDependencies?.[pkg])) { return; } const projects = await (0, projects_1.getProjectsFilteredByDependencies)([ 'npm:@nguniversal/common', 'npm:@nguniversal/express-engine', ]); for (const { data: project } of projects) { if (project.projectType !== 'application') { continue; } const serverMainFiles = new Map(); for (const target of Object.values(project.targets ?? {})) { if (!serverExecutors.includes(target.executor)) { continue; } const outputPath = project.targets.build?.options?.outputPath; for (const [, { main }] of (0, targets_1.allTargetOptions)(target)) { if (typeof main === 'string' && typeof outputPath === 'string' && tree.read(main, 'utf-8').includes('ngExpressEngine')) { serverMainFiles.set(main, outputPath); } } } // Replace all import specifiers in all files. let hasExpressTokens = false; const root = (0, ts_solution_setup_1.getProjectSourceRoot)(project, tree); const tokensFilePath = `${root}/express.tokens.ts`; (0, devkit_1.visitNotIgnoredFiles)(tree, root, (path) => { if (!path.endsWith('.ts') || path.endsWith('.d.ts')) { return; } let content = tree.read(path, 'utf8'); if (!content.includes('@nguniversal/')) { return; } // Check if file is importing tokens if (content.includes('@nguniversal/express-engine/tokens')) { hasExpressTokens ||= true; let tokensFileRelativePath = (0, devkit_1.normalizePath)((0, path_1.relative)((0, path_1.dirname)(path), tokensFilePath)); if (tokensFileRelativePath.charAt(0) !== '.') { tokensFileRelativePath = './' + tokensFileRelativePath; } content = content.replaceAll('@nguniversal/express-engine/tokens', tokensFileRelativePath.slice(0, -3)); } content = content.replaceAll(NGUNIVERSAL_PACKAGE_REGEXP, '@angular/ssr'); tree.write(path, content); }); // Replace server file and add tokens file if needed for (const [path, outputPath] of serverMainFiles.entries()) { tree.rename(path, path + '.bak'); tree.write(path, getServerFileContents(outputPath, hasExpressTokens)); if (hasExpressTokens) { tree.write(tokensFilePath, TOKENS_FILE_CONTENT); } } } // Remove universal packages from deps for (const name of UNIVERSAL_PACKAGES) { (0, devkit_1.removeDependenciesFromPackageJson)(tree, [name], [name]); } const pkgVersions = (0, version_utils_1.versions)(tree); (0, devkit_1.addDependenciesToPackageJson)(tree, { '@angular/ssr': (0, version_utils_1.getInstalledPackageVersionInfo)(tree, '@angular-devkit/build-angular') ?.version ?? pkgVersions.angularDevkitVersion, }, {}); await (0, devkit_1.formatFiles)(tree); } const TOKENS_FILE_CONTENT = ` import { InjectionToken } from '@angular/core'; import { Request, Response } from 'express'; export const REQUEST = new InjectionToken<Request>('REQUEST'); export const RESPONSE = new InjectionToken<Response>('RESPONSE'); `; function getServerFileContents(outputPath, hasExpressTokens) { return (` import 'zone.js/node'; import { APP_BASE_HREF } from '@angular/common'; import { CommonEngine } from '@angular/ssr'; import * as express from 'express'; import { existsSync } from 'node:fs'; import { join } from 'node:path'; import bootstrap from './src/main.server';` + (hasExpressTokens ? `\nimport { REQUEST, RESPONSE } from './src/express.tokens';` : '') + ` // The Express app is exported so that it can be used by serverless Functions. export function app(): express.Express { const server = express(); const distFolder = join(process.cwd(), '${outputPath}'); const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? join(distFolder, 'index.original.html') : join(distFolder, 'index.html'); const commonEngine = new CommonEngine(); server.set('view engine', 'html'); server.set('views', distFolder); // Example Express Rest API endpoints // server.get('/api/**', (req, res) => { }); // Serve static files from /browser server.get('*.*', express.static(distFolder, { maxAge: '1y' })); // All regular routes use the Angular engine server.get('*', (req, res, next) => { const { protocol, originalUrl, baseUrl, headers } = req; commonEngine .render({ bootstrap, documentFilePath: indexHtml, url: \`\${protocol}://\${headers.host}\${originalUrl}\`, publicPath: distFolder, providers: [ { provide: APP_BASE_HREF, useValue: baseUrl },` + (hasExpressTokens ? '\n { provide: RESPONSE, useValue: res },\n { provide: REQUEST, useValue: req }\n' : '') + `], }) .then((html) => res.send(html)) .catch((err) => next(err)); }); return server; } function run(): void { const port = process.env['PORT'] || 4000; // Start up the Node server const server = app(); server.listen(port, () => { console.log(\`Node Express server listening on http://localhost:\${port}\`); }); } // Webpack will replace 'require' with '__webpack_require__' // '__non_webpack_require__' is a proxy to Node 'require' // The below code is to ensure that the server is run only when not requiring the bundle. declare const __non_webpack_require__: NodeRequire; const mainModule = __non_webpack_require__.main; const moduleFilename = mainModule && mainModule.filename || ''; if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { run(); } export default bootstrap; `); }