feat(angular): add --ssr flag to remote generator (#13370)
This commit is contained in:
parent
13602c3d3b
commit
2471768ce1
@ -1195,6 +1195,11 @@
|
|||||||
"description": "Whether to generate a remote application with standalone components.",
|
"description": "Whether to generate a remote application with standalone components.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"ssr": {
|
||||||
|
"description": "Whether to configure SSR for the remote application to be consumed by a host application using SSR.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -363,6 +363,40 @@ describe('Angular Projects', () => {
|
|||||||
expect(buildOutput).toContain('Successfully ran target build');
|
expect(buildOutput).toContain('Successfully ran target build');
|
||||||
}, 300000);
|
}, 300000);
|
||||||
|
|
||||||
|
it('MF - should serve a ssr remote app successfully', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const remoteApp1 = uniq('remote');
|
||||||
|
// generate remote apps
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:remote ${remoteApp1} --ssr --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
let process: ChildProcess;
|
||||||
|
|
||||||
|
try {
|
||||||
|
process = await runCommandUntil(`serve-ssr ${remoteApp1}`, (output) => {
|
||||||
|
return (
|
||||||
|
output.includes(`Browser application bundle generation complete.`) &&
|
||||||
|
output.includes(`Server application bundle generation complete.`) &&
|
||||||
|
output.includes(
|
||||||
|
`Angular Universal Live Development Server is listening`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// port and process cleanup
|
||||||
|
try {
|
||||||
|
if (process && process.pid) {
|
||||||
|
await promisifiedTreeKill(process.pid, 'SIGKILL');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBeFalsy();
|
||||||
|
}
|
||||||
|
}, 300000);
|
||||||
|
|
||||||
it('Custom Webpack Config for SSR - should serve the app correctly', async () => {
|
it('Custom Webpack Config for SSR - should serve the app correctly', async () => {
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
const ssrApp = uniq('app');
|
const ssrApp = uniq('app');
|
||||||
|
|||||||
@ -1,5 +1,187 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 1`] = `
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AppComponent],
|
||||||
|
imports: [
|
||||||
|
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||||
|
RouterModule.forRoot([{
|
||||||
|
path: '',
|
||||||
|
loadChildren: () => import('./remote-entry/entry.module').then(m => m.RemoteEntryModule)
|
||||||
|
}], { initialNavigation: 'enabledBlocking' }),
|
||||||
|
],
|
||||||
|
providers: [],
|
||||||
|
bootstrap: [AppComponent],
|
||||||
|
})
|
||||||
|
export class AppModule {}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 2`] = `
|
||||||
|
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.error(err));
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 3`] = `
|
||||||
|
"/***************************************************************************************************
|
||||||
|
* Initialize the server environment - for example, adding DOM built-in types to the global scope.
|
||||||
|
*
|
||||||
|
* NOTE:
|
||||||
|
* This import must come before any imports (direct or transitive) that rely on DOM built-ins being
|
||||||
|
* available, such as \`@angular/elements\`.
|
||||||
|
*/
|
||||||
|
import '@angular/platform-server/init';
|
||||||
|
|
||||||
|
export { AppServerModule } from './app/app.server.module';
|
||||||
|
export { renderModule } from '@angular/platform-server';"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 4`] = `
|
||||||
|
"import 'zone.js/dist/zone-node';
|
||||||
|
|
||||||
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
|
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||||
|
import * as express from 'express';
|
||||||
|
import * as cors from 'cors';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { AppServerModule } from './bootstrap.server';
|
||||||
|
|
||||||
|
// The Express app is exported so that it can be used by serverless Functions.
|
||||||
|
export function app(): express.Express {
|
||||||
|
const server = express();
|
||||||
|
const browserBundles = join(process.cwd(), 'dist/apps/test/browser');
|
||||||
|
const serverBundles = join(process.cwd(), 'dist/apps/test/server');
|
||||||
|
|
||||||
|
server.use(cors());
|
||||||
|
const indexHtml = existsSync(join(browserBundles, 'index.original.html'))
|
||||||
|
? 'index.original.html'
|
||||||
|
: 'index';
|
||||||
|
|
||||||
|
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
|
||||||
|
server.engine(
|
||||||
|
'html',
|
||||||
|
ngExpressEngine({
|
||||||
|
bootstrap: AppServerModule,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
server.set('view engine', 'html');
|
||||||
|
server.set('views', browserBundles);
|
||||||
|
|
||||||
|
|
||||||
|
// Example Express Rest API endpoints
|
||||||
|
// server.get('/api/**', (req, res) => { });
|
||||||
|
// Serve static files from /browser
|
||||||
|
// serve static files
|
||||||
|
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
|
||||||
|
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
|
||||||
|
|
||||||
|
// All regular routes use the Universal engine
|
||||||
|
server.get('*', (req, res) => {
|
||||||
|
|
||||||
|
res.render(indexHtml, {
|
||||||
|
req,
|
||||||
|
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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}\`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
export * from './bootstrap.server';"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 5`] = `"import('./src/main.server');"`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 6`] = `
|
||||||
|
"module.exports = {
|
||||||
|
name: 'test',
|
||||||
|
exposes: {
|
||||||
|
'./Module': 'apps/test/src/app/remote-entry/entry.module.ts',
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 7`] = `
|
||||||
|
"const { withModuleFederationForSSR } = require('@nrwl/angular/module-federation');
|
||||||
|
const config = require('./module-federation.config');
|
||||||
|
module.exports = withModuleFederationForSSR(config)"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 8`] = `
|
||||||
|
"import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'proj-test-entry',
|
||||||
|
template: \`<proj-nx-welcome></proj-nx-welcome>\`
|
||||||
|
})
|
||||||
|
export class RemoteEntryComponent {}
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 9`] = `
|
||||||
|
"import { Route } from '@angular/router';
|
||||||
|
|
||||||
|
export const appRoutes: Route[] = [
|
||||||
|
{path: '', loadChildren: () => import('./remote-entry/entry.module').then(m => m.RemoteEntryModule)},]"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 10`] = `
|
||||||
|
"import { Route } from '@angular/router';
|
||||||
|
import { RemoteEntryComponent } from './entry.component';
|
||||||
|
|
||||||
|
export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }];"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`MF Remote App Generator --ssr should generate the correct files 11`] = `
|
||||||
|
Object {
|
||||||
|
"configurations": Object {
|
||||||
|
"development": Object {
|
||||||
|
"extractLicenses": false,
|
||||||
|
"optimization": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
},
|
||||||
|
"production": Object {
|
||||||
|
"outputHashing": "media",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production",
|
||||||
|
"executor": "@nrwl/angular:webpack-server",
|
||||||
|
"options": Object {
|
||||||
|
"customWebpackConfig": Object {
|
||||||
|
"path": "apps/test/webpack.server.config.js",
|
||||||
|
},
|
||||||
|
"main": "apps/test/server.ts",
|
||||||
|
"outputPath": "dist/apps/test/server",
|
||||||
|
"tsConfig": "apps/test/tsconfig.server.json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`MF Remote App Generator should generate a remote mf app with a host 1`] = `
|
exports[`MF Remote App Generator should generate a remote mf app with a host 1`] = `
|
||||||
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
|
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
|
||||||
const config = require('./module-federation.config');
|
const config = require('./module-federation.config');
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
import 'zone.js/dist/zone-node';
|
||||||
|
|
||||||
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
|
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||||
|
import * as express from 'express';
|
||||||
|
import * as cors from 'cors';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { AppServerModule } from './bootstrap.server';
|
||||||
|
|
||||||
|
// The Express app is exported so that it can be used by serverless Functions.
|
||||||
|
export function app(): express.Express {
|
||||||
|
const server = express();
|
||||||
|
const browserBundles = join(process.cwd(), 'dist/apps/<%= appName %>/browser');
|
||||||
|
const serverBundles = join(process.cwd(), 'dist/apps/<%= appName %>/server');
|
||||||
|
|
||||||
|
server.use(cors());
|
||||||
|
const indexHtml = existsSync(join(browserBundles, 'index.original.html'))
|
||||||
|
? 'index.original.html'
|
||||||
|
: 'index';
|
||||||
|
|
||||||
|
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
|
||||||
|
server.engine(
|
||||||
|
'html',
|
||||||
|
ngExpressEngine({
|
||||||
|
bootstrap: AppServerModule,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
server.set('view engine', 'html');
|
||||||
|
server.set('views', browserBundles);
|
||||||
|
|
||||||
|
|
||||||
|
// Example Express Rest API endpoints
|
||||||
|
// server.get('/api/**', (req, res) => { });
|
||||||
|
// Serve static files from /browser
|
||||||
|
// serve static files
|
||||||
|
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
|
||||||
|
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
|
||||||
|
|
||||||
|
// All regular routes use the Universal engine
|
||||||
|
server.get('*', (req, res) => {
|
||||||
|
|
||||||
|
res.render(indexHtml, {
|
||||||
|
req,
|
||||||
|
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
export * from './bootstrap.server';
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
const { withModuleFederationForSSR } = require('@nrwl/angular/module-federation');
|
||||||
|
const config = require('./module-federation.config');
|
||||||
|
module.exports = withModuleFederationForSSR(config)
|
||||||
58
packages/angular/src/generators/remote/lib/add-ssr.ts
Normal file
58
packages/angular/src/generators/remote/lib/add-ssr.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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,
|
||||||
|
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');"
|
||||||
|
);
|
||||||
|
|
||||||
|
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,
|
||||||
|
'@module-federation/node': moduleFederationNodeVersion,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
return installTask;
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import { readProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
import { getMFProjects } from '../../../utils/get-mf-projects';
|
||||||
|
|
||||||
|
export function findNextAvailablePort(tree: Tree) {
|
||||||
|
const mfProjects = getMFProjects(tree);
|
||||||
|
|
||||||
|
const ports = new Set<number>([4200]);
|
||||||
|
for (const mfProject of mfProjects) {
|
||||||
|
const { targets } = readProjectConfiguration(tree, mfProject);
|
||||||
|
const port = targets?.serve?.options?.port ?? 4200;
|
||||||
|
ports.add(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextAvailablePort = Math.max(...ports) + 1;
|
||||||
|
|
||||||
|
return nextAvailablePort;
|
||||||
|
}
|
||||||
2
packages/angular/src/generators/remote/lib/index.ts
Normal file
2
packages/angular/src/generators/remote/lib/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './find-next-available-port';
|
||||||
|
export * from './add-ssr';
|
||||||
@ -195,4 +195,52 @@ describe('MF Remote App Generator', () => {
|
|||||||
'proj-test-entry'
|
'proj-test-entry'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('--ssr', () => {
|
||||||
|
it('should generate the correct files', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
ssr: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const project = readProjectConfiguration(tree, 'test');
|
||||||
|
expect(
|
||||||
|
tree.exists(`apps/test/src/app/remote-entry/entry.module.ts`)
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/app/app.module.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/bootstrap.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/bootstrap.server.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/main.server.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(tree.read(`apps/test/server.ts`, 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/module-federation.config.js`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/webpack.server.config.js`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/app/remote-entry/entry.component.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/app/app.routes.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read(`apps/test/src/app/remote-entry/entry.routes.ts`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(project.targets.server).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,30 +1,11 @@
|
|||||||
import {
|
import { formatFiles, getProjects, Tree } from '@nrwl/devkit';
|
||||||
formatFiles,
|
|
||||||
getProjects,
|
|
||||||
readProjectConfiguration,
|
|
||||||
Tree,
|
|
||||||
} from '@nrwl/devkit';
|
|
||||||
import type { Schema } from './schema';
|
import type { Schema } from './schema';
|
||||||
import applicationGenerator from '../application/application';
|
import applicationGenerator from '../application/application';
|
||||||
import { getMFProjects } from '../../utils/get-mf-projects';
|
|
||||||
import { normalizeProjectName } from '../utils/project';
|
import { normalizeProjectName } from '../utils/project';
|
||||||
import { setupMf } from '../setup-mf/setup-mf';
|
import { setupMf } from '../setup-mf/setup-mf';
|
||||||
import { E2eTestRunner } from '../../utils/test-runners';
|
import { E2eTestRunner } from '../../utils/test-runners';
|
||||||
|
import { addSsr, findNextAvailablePort } from './lib';
|
||||||
function findNextAvailablePort(tree: Tree) {
|
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||||
const mfProjects = getMFProjects(tree);
|
|
||||||
|
|
||||||
const ports = new Set<number>([4200]);
|
|
||||||
for (const mfProject of mfProjects) {
|
|
||||||
const { targets } = readProjectConfiguration(tree, mfProject);
|
|
||||||
const port = targets?.serve?.options?.port ?? 4200;
|
|
||||||
ports.add(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextAvailablePort = Math.max(...ports) + 1;
|
|
||||||
|
|
||||||
return nextAvailablePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function remote(tree: Tree, options: Schema) {
|
export async function remote(tree: Tree, options: Schema) {
|
||||||
const projects = getProjects(tree);
|
const projects = getProjects(tree);
|
||||||
@ -37,7 +18,7 @@ export async function remote(tree: Tree, options: Schema) {
|
|||||||
const appName = normalizeProjectName(options.name, options.directory);
|
const appName = normalizeProjectName(options.name, options.directory);
|
||||||
const port = options.port ?? findNextAvailablePort(tree);
|
const port = options.port ?? findNextAvailablePort(tree);
|
||||||
|
|
||||||
const installTask = await applicationGenerator(tree, {
|
const appInstallTask = await applicationGenerator(tree, {
|
||||||
...options,
|
...options,
|
||||||
routing: true,
|
routing: true,
|
||||||
skipDefaultProject: true,
|
skipDefaultProject: true,
|
||||||
@ -60,11 +41,17 @@ export async function remote(tree: Tree, options: Schema) {
|
|||||||
standalone: options.standalone,
|
standalone: options.standalone,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let installTasks = [appInstallTask];
|
||||||
|
if (options.ssr) {
|
||||||
|
let ssrInstallTask = await addSsr(tree, options, appName);
|
||||||
|
installTasks.push(ssrInstallTask);
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.skipFormat) {
|
if (!options.skipFormat) {
|
||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
return installTask;
|
return runTasksInSerial(...installTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default remote;
|
export default remote;
|
||||||
|
|||||||
@ -25,4 +25,5 @@ export interface Schema {
|
|||||||
viewEncapsulation?: 'Emulated' | 'Native' | 'None';
|
viewEncapsulation?: 'Emulated' | 'Native' | 'None';
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
standalone?: boolean;
|
standalone?: boolean;
|
||||||
|
ssr?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,6 +149,11 @@
|
|||||||
"description": "Whether to generate a remote application with standalone components.",
|
"description": "Whether to generate a remote application with standalone components.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"ssr": {
|
||||||
|
"description": "Whether to configure SSR for the remote application to be consumed by a host application using SSR.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export function updateAppModule(tree: Tree, schema: Schema) {
|
|||||||
browserModuleNode.getEnd()
|
browserModuleNode.getEnd()
|
||||||
)}.withServerTransition({ appId: '${schema.appId}' })${fileContents.slice(
|
)}.withServerTransition({ appId: '${schema.appId}' })${fileContents.slice(
|
||||||
browserModuleNode.getEnd(),
|
browserModuleNode.getEnd(),
|
||||||
-1
|
fileContents.length
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
tree.write(pathToAppModule, newFileContents);
|
tree.write(pathToAppModule, newFileContents);
|
||||||
|
|||||||
@ -113,7 +113,8 @@ describe('setupSSR', () => {
|
|||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }"
|
export class AppModule { }
|
||||||
|
"
|
||||||
`);
|
`);
|
||||||
const packageJson = readJson<PackageJson>(tree, 'package.json');
|
const packageJson = readJson<PackageJson>(tree, 'package.json');
|
||||||
const dependencies = {
|
const dependencies = {
|
||||||
|
|||||||
@ -10,6 +10,8 @@ export const angularJsVersion = '1.7.9';
|
|||||||
export const tsLibVersion = '^2.3.0';
|
export const tsLibVersion = '^2.3.0';
|
||||||
|
|
||||||
export const ngUniversalVersion = '~15.0.0';
|
export const ngUniversalVersion = '~15.0.0';
|
||||||
|
export const corsVersion = '~2.8.5';
|
||||||
|
export const moduleFederationNodeVersion = '~0.9.6';
|
||||||
|
|
||||||
export const angularEslintVersion = '~15.0.0';
|
export const angularEslintVersion = '~15.0.0';
|
||||||
export const tailwindVersion = '^3.0.2';
|
export const tailwindVersion = '^3.0.2';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user