781 lines
26 KiB
TypeScript
781 lines
26 KiB
TypeScript
import 'nx/src/internal-testing-utils/mock-project-graph';
|
|
|
|
import {
|
|
NxJsonConfiguration,
|
|
readJson,
|
|
readProjectConfiguration,
|
|
updateJson,
|
|
updateProjectConfiguration,
|
|
} from '@nx/devkit';
|
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
|
import { PackageJson } from 'nx/src/utils/package-json';
|
|
import { backwardCompatibleVersions } from '../../utils/backward-compatible-versions';
|
|
import {
|
|
angularDevkitVersion,
|
|
angularVersion,
|
|
expressVersion,
|
|
typesExpressVersion,
|
|
} from '../../utils/versions';
|
|
import { generateTestApplication } from '../utils/testing';
|
|
import { setupSsr } from './setup-ssr';
|
|
|
|
describe('setupSSR', () => {
|
|
describe('with application builder', () => {
|
|
it('should create the files correctly for ssr', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1' });
|
|
|
|
// ASSERT
|
|
expect(
|
|
readProjectConfiguration(tree, 'app1').targets.build
|
|
).toMatchSnapshot();
|
|
expect(tree.read('app1/server.ts', 'utf-8')).toMatchSnapshot();
|
|
expect(tree.read('app1/src/main.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"export { AppServerModule as default } from './app/app.server.module';
|
|
"
|
|
`);
|
|
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
|
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
import { AppModule } from './app/app.module';
|
|
|
|
platformBrowserDynamic()
|
|
.bootstrapModule(AppModule)
|
|
.catch((err) => console.error(err));
|
|
"
|
|
`);
|
|
expect(tree.exists('app1/tsconfig.server.json')).toBe(false);
|
|
expect(readJson(tree, 'app1/tsconfig.app.json').files).toStrictEqual([
|
|
'src/main.ts',
|
|
'src/main.server.ts',
|
|
'server.ts',
|
|
]);
|
|
expect(tree.read('app1/src/app/app.server.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { ServerModule } from '@angular/platform-server';
|
|
|
|
import { AppModule } from './app.module';
|
|
import { AppComponent } from './app.component';
|
|
|
|
@NgModule({
|
|
imports: [AppModule, ServerModule],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppServerModule {}
|
|
"
|
|
`);
|
|
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import {
|
|
BrowserModule,
|
|
provideClientHydration,
|
|
} from '@angular/platform-browser';
|
|
import { RouterModule } from '@angular/router';
|
|
import { AppComponent } from './app.component';
|
|
import { appRoutes } from './app.routes';
|
|
import { NxWelcomeComponent } from './nx-welcome.component';
|
|
|
|
@NgModule({
|
|
declarations: [AppComponent, NxWelcomeComponent],
|
|
imports: [BrowserModule, RouterModule.forRoot(appRoutes)],
|
|
providers: [provideClientHydration()],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppModule {}
|
|
"
|
|
`);
|
|
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
|
expect(nxJson.targetDefaults.server).toBeUndefined();
|
|
});
|
|
|
|
it('should create the files correctly for ssr when app is standalone', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1' });
|
|
|
|
// ASSERT
|
|
expect(
|
|
readProjectConfiguration(tree, 'app1').targets.build
|
|
).toMatchSnapshot();
|
|
expect(tree.read('app1/server.ts', 'utf-8')).toMatchSnapshot();
|
|
expect(tree.read('app1/src/main.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { bootstrapApplication } from '@angular/platform-browser';
|
|
import { AppComponent } from './app/app.component';
|
|
import { config } from './app/app.config.server';
|
|
|
|
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
|
|
|
export default bootstrap;
|
|
"
|
|
`);
|
|
expect(tree.exists('app1/tsconfig.server.json')).toBe(false);
|
|
expect(readJson(tree, 'app1/tsconfig.app.json').files).toStrictEqual([
|
|
'src/main.ts',
|
|
'src/main.server.ts',
|
|
'server.ts',
|
|
]);
|
|
expect(tree.read('app1/src/app/app.config.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
|
import { provideServerRendering } from '@angular/platform-server';
|
|
import { appConfig } from './app.config';
|
|
|
|
const serverConfig: ApplicationConfig = {
|
|
providers: [provideServerRendering()],
|
|
};
|
|
|
|
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
|
"
|
|
`);
|
|
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
|
expect(nxJson.targetDefaults.server).toBeUndefined();
|
|
});
|
|
|
|
it('should support object output option using a custom "outputPath.browser" and "outputPath.server" values', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
const project = readProjectConfiguration(tree, 'app1');
|
|
project.targets.build.options.outputPath = {
|
|
base: project.targets.build.options.outputPath,
|
|
browser: 'public',
|
|
server: 'node-server',
|
|
};
|
|
updateProjectConfiguration(tree, 'app1', project);
|
|
|
|
await setupSsr(tree, { project: 'app1' });
|
|
|
|
const serverFileContent = tree.read('app1/server.ts', 'utf-8');
|
|
expect(serverFileContent).toContain(
|
|
`resolve(serverDistFolder, '../public')`
|
|
);
|
|
});
|
|
|
|
it('should remove "outputPath.browser" when it is an empty string', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
const project = readProjectConfiguration(tree, 'app1');
|
|
project.targets.build.options.outputPath = {
|
|
base: project.targets.build.options.outputPath,
|
|
browser: '',
|
|
server: 'node-server',
|
|
};
|
|
updateProjectConfiguration(tree, 'app1', project);
|
|
|
|
await setupSsr(tree, { project: 'app1' });
|
|
|
|
const updatedProject = readProjectConfiguration(tree, 'app1');
|
|
expect(updatedProject.targets.build.options.outputPath).toStrictEqual({
|
|
base: 'dist/app1',
|
|
server: 'node-server',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('with browser builder', () => {
|
|
it('should create the files correctly for ssr', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
bundler: 'webpack',
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
expect(
|
|
readProjectConfiguration(tree, 'app1').targets.server
|
|
).toMatchSnapshot();
|
|
expect(tree.read('app1/server.ts', 'utf-8')).toMatchSnapshot();
|
|
expect(tree.read('app1/src/main.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"export { AppServerModule as default } from './app/app.server.module';
|
|
"
|
|
`);
|
|
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
|
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
import { AppModule } from './app/app.module';
|
|
|
|
platformBrowserDynamic()
|
|
.bootstrapModule(AppModule)
|
|
.catch((err) => console.error(err));
|
|
"
|
|
`);
|
|
expect(tree.read('app1/tsconfig.server.json', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
{
|
|
"extends": "./tsconfig.app.json",
|
|
"compilerOptions": {
|
|
"outDir": "../../out-tsc/server",
|
|
"target": "es2019",
|
|
"types": [
|
|
"node"
|
|
]
|
|
},
|
|
"files": [
|
|
"src/main.server.ts",
|
|
"server.ts",
|
|
]
|
|
}"
|
|
`);
|
|
expect(tree.read('app1/src/app/app.server.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { ServerModule } from '@angular/platform-server';
|
|
|
|
import { AppModule } from './app.module';
|
|
import { AppComponent } from './app.component';
|
|
|
|
@NgModule({
|
|
imports: [
|
|
AppModule,
|
|
ServerModule,
|
|
],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppServerModule {}"
|
|
`);
|
|
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { BrowserModule, provideClientHydration } from '@angular/platform-browser';
|
|
import { RouterModule } from '@angular/router';
|
|
import { AppComponent } from './app.component';
|
|
import { appRoutes } from './app.routes';
|
|
import { NxWelcomeComponent } from './nx-welcome.component';
|
|
|
|
@NgModule({
|
|
declarations: [AppComponent, NxWelcomeComponent],
|
|
imports: [
|
|
BrowserModule,
|
|
RouterModule.forRoot(appRoutes),
|
|
],
|
|
providers: [provideClientHydration()],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppModule {}
|
|
"
|
|
`);
|
|
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
|
expect(nxJson.targetDefaults.server.cache).toBe(true);
|
|
});
|
|
|
|
it('should create the files correctly for ssr when app is standalone', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
bundler: 'webpack',
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
expect(
|
|
readProjectConfiguration(tree, 'app1').targets.server
|
|
).toMatchSnapshot();
|
|
expect(tree.read('app1/server.ts', 'utf-8')).toMatchSnapshot();
|
|
expect(tree.read('app1/src/main.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { bootstrapApplication } from '@angular/platform-browser';
|
|
import { AppComponent } from './app/app.component';
|
|
import { config } from './app/app.config.server';
|
|
|
|
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
|
|
|
export default bootstrap;
|
|
"
|
|
`);
|
|
expect(tree.read('app1/tsconfig.server.json', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
{
|
|
"extends": "./tsconfig.app.json",
|
|
"compilerOptions": {
|
|
"outDir": "../../out-tsc/server",
|
|
"target": "es2019",
|
|
"types": [
|
|
"node"
|
|
]
|
|
},
|
|
"files": [
|
|
"src/main.server.ts",
|
|
"server.ts",
|
|
]
|
|
}"
|
|
`);
|
|
expect(tree.read('app1/src/app/app.config.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
|
import { provideServerRendering } from '@angular/platform-server';
|
|
import { appConfig } from './app.config';
|
|
|
|
const serverConfig: ApplicationConfig = {
|
|
providers: [
|
|
provideServerRendering()
|
|
]
|
|
};
|
|
|
|
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
|
"
|
|
`);
|
|
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
|
expect(nxJson.targetDefaults.server.cache).toEqual(true);
|
|
});
|
|
|
|
it('should update build target output path', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
bundler: 'webpack',
|
|
skipFormat: true,
|
|
});
|
|
// verify default output path
|
|
expect(
|
|
readProjectConfiguration(tree, 'app1').targets.build.options.outputPath
|
|
).toBe('dist/app1');
|
|
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
expect(
|
|
readProjectConfiguration(tree, 'app1').targets.build.options.outputPath
|
|
).toBe('dist/app1/browser');
|
|
});
|
|
});
|
|
|
|
it('should install the correct dependencies', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
const { dependencies, devDependencies } = readJson<PackageJson>(
|
|
tree,
|
|
'package.json'
|
|
);
|
|
expect(dependencies['@angular/platform-server']).toEqual(angularVersion);
|
|
expect(dependencies['@angular/ssr']).toEqual(angularDevkitVersion);
|
|
expect(dependencies['express']).toEqual(expressVersion);
|
|
expect(dependencies['@nguniversal/express-engine']).toBeUndefined();
|
|
expect(devDependencies['@types/express']).toBe(typesExpressVersion);
|
|
expect(devDependencies['@nguniversal/builders']).toBeUndefined();
|
|
});
|
|
|
|
it('should not touch the package.json when run with `--skipPackageJson`', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
let initialPackageJson;
|
|
updateJson(tree, 'package.json', (json) => {
|
|
json.dependencies = {};
|
|
json.devDependencies = {};
|
|
initialPackageJson = json;
|
|
|
|
return json;
|
|
});
|
|
|
|
await setupSsr(tree, {
|
|
project: 'app1',
|
|
skipFormat: true,
|
|
skipPackageJson: true,
|
|
});
|
|
|
|
const packageJson = readJson(tree, 'package.json');
|
|
expect(packageJson).toEqual(initialPackageJson);
|
|
});
|
|
|
|
it('should add hydration correctly for NgModule apps', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, {
|
|
project: 'app1',
|
|
hydration: true,
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ASSERT
|
|
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { BrowserModule, provideClientHydration } from '@angular/platform-browser';
|
|
import { RouterModule } from '@angular/router';
|
|
import { AppComponent } from './app.component';
|
|
import { appRoutes } from './app.routes';
|
|
import { NxWelcomeComponent } from './nx-welcome.component';
|
|
|
|
@NgModule({
|
|
declarations: [AppComponent, NxWelcomeComponent],
|
|
imports: [
|
|
BrowserModule,
|
|
RouterModule.forRoot(appRoutes),
|
|
],
|
|
providers: [provideClientHydration()],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppModule {}
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should add hydration correctly to standalone', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, {
|
|
project: 'app1',
|
|
hydration: true,
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ASSERT
|
|
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { ApplicationConfig } from '@angular/core';
|
|
import { provideRouter } from '@angular/router';
|
|
import { appRoutes } from './app.routes';
|
|
import { provideClientHydration } from '@angular/platform-browser';
|
|
|
|
export const appConfig: ApplicationConfig = {
|
|
providers: [provideClientHydration(),provideRouter(appRoutes) ]
|
|
};
|
|
"
|
|
`);
|
|
|
|
expect(tree.read('app1/src/app/app.config.server.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
|
import { provideServerRendering } from '@angular/platform-server';
|
|
import { appConfig } from './app.config';
|
|
|
|
const serverConfig: ApplicationConfig = {
|
|
providers: [
|
|
provideServerRendering()
|
|
]
|
|
};
|
|
|
|
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should set "initialNavigation: enabledBlocking" in "RouterModule.forRoot" options when hydration=false', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
await setupSsr(tree, {
|
|
project: 'app1',
|
|
hydration: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { BrowserModule } from '@angular/platform-browser';
|
|
import { RouterModule } from '@angular/router';
|
|
import { AppComponent } from './app.component';
|
|
import { appRoutes } from './app.routes';
|
|
import { NxWelcomeComponent } from './nx-welcome.component';
|
|
|
|
@NgModule({
|
|
declarations: [AppComponent, NxWelcomeComponent],
|
|
imports: [
|
|
BrowserModule,
|
|
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
|
|
],
|
|
providers: [],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppModule {}
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should set "withEnabledBlockingInitialNavigation()" in "provideRouter" features when hydration=false', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
|
|
await setupSsr(tree, {
|
|
project: 'app1',
|
|
hydration: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { ApplicationConfig } from '@angular/core';
|
|
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
|
import { appRoutes } from './app.routes';
|
|
|
|
export const appConfig: ApplicationConfig = {
|
|
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) ]
|
|
};
|
|
"
|
|
`);
|
|
});
|
|
|
|
describe('compat', () => {
|
|
it('should install the correct versions when using older versions of Angular', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: {
|
|
'@angular/core': '15.2.0',
|
|
},
|
|
}));
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
const pkgJson = readJson(tree, 'package.json');
|
|
expect(pkgJson.dependencies['@angular/ssr']).toBeUndefined();
|
|
expect(pkgJson.dependencies['@angular/platform-server']).toEqual(
|
|
backwardCompatibleVersions.angularV15.angularVersion
|
|
);
|
|
expect(pkgJson.dependencies['@nguniversal/express-engine']).toEqual(
|
|
backwardCompatibleVersions.angularV15.ngUniversalVersion
|
|
);
|
|
expect(pkgJson.devDependencies['@nguniversal/builders']).toEqual(
|
|
backwardCompatibleVersions.angularV15.ngUniversalVersion
|
|
);
|
|
});
|
|
|
|
it('should add "withServerTransition" call to app module for angular versions lower than 16', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: { ...json.dependencies, '@angular/core': '^15.2.0' },
|
|
}));
|
|
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { BrowserModule } from '@angular/platform-browser';
|
|
import { RouterModule } from '@angular/router';
|
|
import { AppComponent } from './app.component';
|
|
import { appRoutes } from './app.routes';
|
|
import { NxWelcomeComponent } from './nx-welcome.component';
|
|
|
|
@NgModule({
|
|
declarations: [AppComponent, NxWelcomeComponent],
|
|
imports: [
|
|
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
|
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
|
|
],
|
|
providers: [],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppModule {}
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should set "initialNavigation: enabledBlocking" in "RouterModule.forRoot" options', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: { ...json.dependencies, '@angular/core': '^15.2.0' },
|
|
}));
|
|
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
expect(tree.read('app1/src/app/app.module.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { NgModule } from '@angular/core';
|
|
import { BrowserModule } from '@angular/platform-browser';
|
|
import { RouterModule } from '@angular/router';
|
|
import { AppComponent } from './app.component';
|
|
import { appRoutes } from './app.routes';
|
|
import { NxWelcomeComponent } from './nx-welcome.component';
|
|
|
|
@NgModule({
|
|
declarations: [AppComponent, NxWelcomeComponent],
|
|
imports: [
|
|
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
|
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabledBlocking' }),
|
|
],
|
|
providers: [],
|
|
bootstrap: [AppComponent],
|
|
})
|
|
export class AppModule {}
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should set "withEnabledBlockingInitialNavigation()" in "provideRouter" features', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: { ...json.dependencies, '@angular/core': '^15.2.0' },
|
|
}));
|
|
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { ApplicationConfig } from '@angular/platform-browser';
|
|
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
|
import { appRoutes } from './app.routes';
|
|
|
|
export const appConfig: ApplicationConfig = {
|
|
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) ]
|
|
};
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should wrap bootstrap call for Angular versions lower than 15.2', async () => {
|
|
// ARRANGE
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: { '@angular/core': '15.1.0' },
|
|
}));
|
|
|
|
// ACT
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
// ASSERT
|
|
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchInlineSnapshot(`
|
|
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
import { AppModule } from './app/app.module';
|
|
|
|
function bootstrap() {
|
|
platformBrowserDynamic()
|
|
.bootstrapModule(AppModule)
|
|
.catch((err) => console.error(err));
|
|
}
|
|
|
|
if (document.readyState !== 'loading') {
|
|
bootstrap();
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', bootstrap);
|
|
}
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should generate a correct server.ts', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: {
|
|
'@angular/core': '15.2.0',
|
|
},
|
|
}));
|
|
await generateTestApplication(tree, {
|
|
name: 'app1',
|
|
standalone: false,
|
|
skipFormat: true,
|
|
});
|
|
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
expect(tree.read('app1/server.ts', 'utf-8')).toMatchSnapshot();
|
|
});
|
|
|
|
it('should not set up hydration by default', async () => {
|
|
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
updateJson(tree, 'package.json', (json) => ({
|
|
...json,
|
|
dependencies: {
|
|
'@angular/core': '15.2.0',
|
|
},
|
|
}));
|
|
await generateTestApplication(tree, { name: 'app1', skipFormat: true });
|
|
|
|
await setupSsr(tree, { project: 'app1', skipFormat: true });
|
|
|
|
expect(tree.read('app1/src/app/app.config.ts', 'utf-8'))
|
|
.toMatchInlineSnapshot(`
|
|
"import { ApplicationConfig } from '@angular/platform-browser';
|
|
import { provideRouter, withEnabledBlockingInitialNavigation } from '@angular/router';
|
|
import { appRoutes } from './app.routes';
|
|
|
|
export const appConfig: ApplicationConfig = {
|
|
providers: [provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) ]
|
|
};
|
|
"
|
|
`);
|
|
});
|
|
});
|
|
});
|