UNPKG

@nx/react

Version:

The React plugin for Nx contains executors and generators for managing React applications and libraries within an Nx workspace. It provides: - Integration with libraries such as Jest, Vitest, Playwright, Cypress, and Storybook. - Generators for applica

198 lines (197 loc) • 8.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setupSsrGenerator = setupSsrGenerator; const devkit_1 = require("@nx/devkit"); const versions_1 = require("../../utils/versions"); const ast_utils_1 = require("../../utils/ast-utils"); const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescript"); const path_1 = require("path"); let tsModule; function readEntryFile(host, path) { if (!tsModule) { tsModule = (0, ensure_typescript_1.ensureTypescript)(); } const content = host.read(path, 'utf-8'); return { content, source: tsModule.createSourceFile(path, content, tsModule.ScriptTarget.Latest, true), }; } async function getProjectConfig(tree, projectName) { let maybeProjectConfig = (0, devkit_1.readProjectConfiguration)(tree, projectName); if (!maybeProjectConfig.targets?.build) { let projectGraph; try { projectGraph = (0, devkit_1.readCachedProjectGraph)(); } catch { projectGraph = await (0, devkit_1.createProjectGraphAsync)(); } maybeProjectConfig = projectGraph.nodes[projectName].data; } return maybeProjectConfig; } async function setupSsrGenerator(tree, options) { const projectConfig = await getProjectConfig(tree, options.project); const projectRoot = projectConfig.root; const appImportCandidates = [ options.appComponentImportPath ?? 'app/app', 'app', 'App', 'app/App', 'App/App', ].map((importPath) => { return { importPath, filePath: (0, devkit_1.joinPathFragments)(projectConfig.sourceRoot || projectConfig.root, `${importPath}.tsx`), }; }); const appComponentInfo = appImportCandidates.find((candidate) => tree.exists(candidate.filePath)); if (!appComponentInfo) { throw new Error(`Cannot find an import path for <App/> component. Try passing setting --appComponentImportPath option.`); } if (!projectConfig.targets.build || !projectConfig.targets.serve) { throw new Error(`Project ${options.project} does not have build and serve targets`); } if (projectConfig.targets.server) { throw new Error(`Project ${options.project} already has a server target.`); } const originalOutputPath = projectConfig.targets.build?.options?.outputPath ?? projectConfig.targets.build?.outputs[0]; if (!originalOutputPath) { throw new Error(`Project ${options.project} does not contain a outputPath for the build target.`); } // TODO(colum): We need to figure out how to handle this for Crystal if (projectConfig.targets.build.options?.outputPath) { projectConfig.targets.build.options.outputPath = (0, devkit_1.joinPathFragments)(originalOutputPath, 'browser'); } if (projectConfig.targets.build.executor === '@nx/rspack:rspack') { options.bundler = 'rspack'; } else if (projectConfig.targets.build.executor === '@nx/webpack:webpack') { options.bundler = 'webpack'; } projectConfig.targets = { ...projectConfig.targets, server: { dependsOn: ['build'], executor: options.bundler === 'rspack' ? '@nx/rspack:rspack' : '@nx/webpack:webpack', outputs: ['{options.outputPath}'], defaultConfiguration: 'production', options: { target: 'node', main: `${projectRoot}/server.ts`, outputPath: (0, devkit_1.joinPathFragments)(originalOutputPath, 'server'), outputFileName: 'server.js', tsConfig: `${projectRoot}/tsconfig.server.json`, compiler: 'babel', externalDependencies: 'all', outputHashing: 'none', ...(options.bundler === 'rspack' ? { rspackConfig: (0, devkit_1.joinPathFragments)(projectRoot, 'rspack.config.js') } : { webpackConfig: (0, devkit_1.joinPathFragments)(projectRoot, 'webpack.config.js'), }), }, configurations: { development: { optimization: false, sourceMap: true, }, production: { fileReplacements: [ { replace: `${projectRoot}/src/environments/environment.ts`, with: `${projectRoot}/src/environments/environment.prod.ts`, }, ], sourceMap: false, }, }, }, 'serve-browser': projectConfig.targets.serve, 'serve-server': { executor: '@nx/js:node', defaultConfiguration: 'development', options: { buildTarget: `${options.project}:server:development`, buildTargetOptions: { watch: true, }, }, configurations: { development: {}, production: { buildTarget: `${options.project}:server:production`, }, }, }, serve: { executor: options.bundler === 'rspack' ? '@nx/rspack:ssr-dev-server' : '@nx/webpack:ssr-dev-server', defaultConfiguration: 'development', options: { browserTarget: `${options.project}:build:development`, serverTarget: `${options.project}:serve-server:development`, port: options.serverPort, browserTargetOptions: { watch: true, }, }, configurations: { development: {}, production: { browserTarget: `${options.project}:build:production`, serverTarget: `${options.project}:serve-server:production`, }, }, }, }; (0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig); const nxJson = (0, devkit_1.readNxJson)(tree); if (nxJson.tasksRunnerOptions?.default && !nxJson.tasksRunnerOptions?.default.options.cacheableOperations.includes('server')) { nxJson.tasksRunnerOptions.default.options.cacheableOperations = [ ...nxJson.tasksRunnerOptions.default.options.cacheableOperations, 'server', ]; } nxJson.targetDefaults ??= {}; nxJson.targetDefaults['server'] ??= {}; nxJson.targetDefaults.server.cache = true; (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectRoot, { tmpl: '', port: Number(options?.serverPort) || 4200, extraInclude: options.extraInclude?.length > 0 ? `"${options.extraInclude.join('", "')}",` : '', appComponentImport: appComponentInfo.importPath, browserBuildOutputPath: projectConfig.targets.build?.options?.outputPath ?? projectConfig.targets.build?.outputs[0], }); // Add <StaticRouter> to server main if needed. // TODO: need to read main.server.tsx not main.tsx. const appContent = tree.read(appComponentInfo.filePath, 'utf-8'); const isRouterPresent = appContent.match(/react-router-dom/); if (isRouterPresent) { const serverEntry = (0, devkit_1.joinPathFragments)(projectRoot, 'src/main.server.tsx'); const { content, source } = readEntryFile(tree, serverEntry); const changes = (0, devkit_1.applyChangesToString)(content, (0, ast_utils_1.addStaticRouter)(serverEntry, source)); tree.write(serverEntry, changes); } (0, devkit_1.updateNxJson)(tree, nxJson); const installTask = (0, devkit_1.addDependenciesToPackageJson)(tree, { express: versions_1.expressVersion, isbot: versions_1.isbotVersion, cors: versions_1.corsVersion, }, { '@types/express': versions_1.typesExpressVersion, '@types/cors': versions_1.typesCorsVersion, }); await (0, devkit_1.formatFiles)(tree); return installTask; } exports.default = setupSsrGenerator;