diff --git a/packages/react/src/generators/application/lib/normalize-options.ts b/packages/react/src/generators/application/lib/normalize-options.ts index cffc5efb7f8646..2677e3d3333aed 100644 --- a/packages/react/src/generators/application/lib/normalize-options.ts +++ b/packages/react/src/generators/application/lib/normalize-options.ts @@ -13,10 +13,10 @@ export function normalizeProjectName(options: Schema) { return normalizeDirectory(options).replace(new RegExp('/', 'g'), '-'); } -export function normalizeOptions( +export function normalizeOptions( host: Tree, options: Schema -): NormalizedSchema { +): NormalizedSchema { const appDirectory = normalizeDirectory(options); const appProjectName = normalizeProjectName(options); const e2eProjectName = options.rootProject diff --git a/packages/react/src/generators/application/schema.d.ts b/packages/react/src/generators/application/schema.d.ts index ead0831abf0917..1c21a302b299fb 100644 --- a/packages/react/src/generators/application/schema.d.ts +++ b/packages/react/src/generators/application/schema.d.ts @@ -33,7 +33,7 @@ export interface Schema { bundler?: 'webpack' | 'vite'; } -export interface NormalizedSchema extends Schema { +export interface NormalizedSchema extends T { projectName: string; appProjectRoot: string; e2eProjectName: string; diff --git a/packages/react/src/generators/host/host.ts b/packages/react/src/generators/host/host.ts index 06e9accfc9f5b1..109d20009cf7d7 100644 --- a/packages/react/src/generators/host/host.ts +++ b/packages/react/src/generators/host/host.ts @@ -1,15 +1,19 @@ -import { formatFiles, Tree } from '@nrwl/devkit'; +import { formatFiles, GeneratorCallback, Tree } from '@nrwl/devkit'; import applicationGenerator from '../application/application'; import { normalizeOptions } from '../application/lib/normalize-options'; import { updateModuleFederationProject } from '../../rules/update-module-federation-project'; import { addModuleFederationFiles } from './lib/add-module-federation-files'; import { updateModuleFederationE2eProject } from './lib/update-module-federation-e2e-project'; + import { Schema } from './schema'; import remoteGenerator from '../remote/remote'; +import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; +import { addSsr } from './lib/add-ssr'; export async function hostGenerator(host: Tree, schema: Schema) { - const options = normalizeOptions(host, schema); + const tasks: GeneratorCallback[] = []; + const options = normalizeOptions(host, schema); const initTask = await applicationGenerator(host, { ...options, @@ -18,6 +22,12 @@ export async function hostGenerator(host: Tree, schema: Schema) { // Only webpack works with module federation for now. bundler: 'webpack', }); + tasks.push(initTask); + + if (options.ssr) { + let ssrInstallTask = await addSsr(host, options, options.projectName); + tasks.push(ssrInstallTask); + } const remotesWithPorts: { name: string; port: number }[] = []; @@ -47,7 +57,7 @@ export async function hostGenerator(host: Tree, schema: Schema) { await formatFiles(host); } - return initTask; + return runTasksInSerial(...tasks); } export default hostGenerator; diff --git a/packages/react/src/generators/host/lib/add-ssr.ts b/packages/react/src/generators/host/lib/add-ssr.ts new file mode 100644 index 00000000000000..2b2f0e0cc6c12f --- /dev/null +++ b/packages/react/src/generators/host/lib/add-ssr.ts @@ -0,0 +1,69 @@ +import type { Tree } from '@nrwl/devkit'; +import { + addDependenciesToPackageJson, + generateFiles, + joinPathFragments, + readProjectConfiguration, + updateProjectConfiguration, +} from '@nrwl/devkit'; +import type { Schema } from '../schema'; + +import setupSsr from '../../setup-ssr/setup-ssr'; +import { + corsVersion, + expressVersion, + moduleFederationNodeVersion, +} from '../../../utils/versions'; + +export async function addSsr(tree: Tree, options: Schema, appName: string) { + let project = readProjectConfiguration(tree, appName); + + await setupSsr(tree, { + project: appName, + }); + + tree.rename( + joinPathFragments(project.sourceRoot, 'main.server.ts'), + joinPathFragments(project.sourceRoot, 'bootstrap.server.ts') + ); + tree.write( + joinPathFragments(project.root, 'server.ts'), + "import('./src/main.server');" + ); + + tree.rename( + joinPathFragments(project.sourceRoot, 'main.ts'), + joinPathFragments(project.sourceRoot, 'bootstrap.ts') + ); + tree.write( + joinPathFragments(project.sourceRoot, 'main.ts'), + `import("./bootstrap")` + ); + + generateFiles(tree, joinPathFragments(__dirname, '../files'), project.root, { + appName, + tmpl: '', + }); + + // update project.json + project = readProjectConfiguration(tree, appName); + + project.targets.server.executor = '@nrwl/angular:webpack-server'; + project.targets.server.options.customWebpackConfig = { + path: joinPathFragments(project.root, 'webpack.server.config.js'), + }; + + updateProjectConfiguration(tree, appName, project); + + const installTask = addDependenciesToPackageJson( + tree, + { + cors: corsVersion, + express: expressVersion, + '@module-federation/node': moduleFederationNodeVersion, + }, + {} + ); + + return installTask; +} diff --git a/packages/react/src/generators/host/schema.d.ts b/packages/react/src/generators/host/schema.d.ts index 6a49187dd294e6..f140f675665441 100644 --- a/packages/react/src/generators/host/schema.d.ts +++ b/packages/react/src/generators/host/schema.d.ts @@ -2,29 +2,30 @@ import { Linter } from '@nrwl/linter'; import { SupportedStyles } from '../../../typings'; export interface Schema { - name: string; - style: SupportedStyles; - skipFormat: boolean; + classComponent?: boolean; + compiler?: 'babel' | 'swc'; + devServerPort?: number; directory?: string; - tags?: string; - unitTestRunner: 'jest' | 'vitest' | 'none'; e2eTestRunner: 'cypress' | 'none'; + globalCss?: boolean; + js?: boolean; linter: Linter; + name: string; pascalCaseFiles?: boolean; - classComponent?: boolean; - skipWorkspaceJson?: boolean; - js?: boolean; - globalCss?: boolean; - strict?: boolean; + remotes?: string[]; setParserOptionsProject?: boolean; + skipFormat: boolean; + skipWorkspaceJson?: boolean; + ssr?: boolean; standaloneConfig?: boolean; - compiler?: 'babel' | 'swc'; - devServerPort?: number; - remotes?: string[]; + strict?: boolean; + style: SupportedStyles; + tags?: string; + unitTestRunner: 'jest' | 'vitest' | 'none'; } export interface NormalizedSchema extends Schema { - projectName: string; appProjectRoot: string; e2eProjectName: string; + projectName: string; } diff --git a/packages/react/src/generators/host/schema.json b/packages/react/src/generators/host/schema.json index 9ff31ea0fdb852..30d6fa70b73f41 100644 --- a/packages/react/src/generators/host/schema.json +++ b/packages/react/src/generators/host/schema.json @@ -148,6 +148,11 @@ "devServerPort": { "type": "number", "description": "The port for the dev server of the remote app." + }, + "ssr": { + "description": "Whether to configure SSR for the host application", + "type": "boolean", + "default": false } }, "required": ["name"], diff --git a/packages/react/src/generators/setup-ssr/schema.d.ts b/packages/react/src/generators/setup-ssr/schema.d.ts index 50b3735c93d4e0..1902ff886f8018 100644 --- a/packages/react/src/generators/setup-ssr/schema.d.ts +++ b/packages/react/src/generators/setup-ssr/schema.d.ts @@ -1,6 +1,6 @@ export interface Schema { project: string; - appComponentImportPath: string; + appComponentImportPath?: string; serverPort?: number; skipFormat?: boolean; } diff --git a/packages/react/src/utils/versions.ts b/packages/react/src/utils/versions.ts index 53a06b7dbdc4c2..2b9cf0c5cd4b25 100755 --- a/packages/react/src/utils/versions.ts +++ b/packages/react/src/utils/versions.ts @@ -41,6 +41,9 @@ export const postcssVersion = '8.4.19'; export const tailwindcssVersion = '3.2.4'; export const autoprefixerVersion = '10.4.13'; -export const expressVersion = '^4.18.1'; +// SSR and Module Federation +export const expressVersion = '~4.18.2'; export const typesExpressVersion = '4.17.14'; export const isbotVersion = '^3.6.5'; +export const corsVersion = '~2.8.5'; +export const moduleFederationNodeVersion = '~0.9.6';