Skip to content

Commit

Permalink
CXSPA-5516 Upgrade to Angular 17 (#18272)
Browse files Browse the repository at this point in the history
migration:
- Nx migrate to 17.1.3
- Nx updated "@angular/" and "@angular-devkit/" packages to 17.0.5
- Nx updated "@angular-eslint/builder" to 17.1.0
- Nx updated "@angular-eslint/eslint-plugin" to 17.0.1
- Nx updated "@angular-eslint/eslint-plugin-template" to 17.0.1
- Nx updated "@angular-eslint/schematics" to 17.1.0
- Nx updated "@angular-eslint/template-parser" to 17.0.1
- Nx updated "@angular-eslint/utils" to 17.1.0
- Nx updated "ng-packagr" to 17.0.2
- Nx updated "zone.js" to 0.14.2
- Nx update "@ngrx/" packages to 17.0.1 together with {connectInZone: true} in app.module.ts
- manual bump "@angular/pwa" to 17.0.5
- manual bump "ngx-infinite-scroll" to v17.0.0
- manual bump "@ng-select/ng-select" to v12.0.4
- manual bump "@angular-builders/custom-webpack" to v17.0.0
- manual bump "angular-ouauth2-oidc" to v17.0.1
- bump versions in librariers
- migration change - makeStateKey, StateKey, TransferState are now imported from "@angular/core"
- storefrontapp - use "buildTarget" instead of deprecated "browserTarget" for dev-server

parse5:
- bumped version to the one that Angular uses to prevent errors during Spartacus schematics run (our old version overwrote the one installed by Angular)
- adjust typing in 'html-utils.ts'
- bump version in schematics lib and 'dependencies.json'

SSR adjustments:
- added ngExpressEngine with unit tests
- added REQUEST and RESPONSE injection tokens
- removed 'jest-preset-angular/setup-jest' from 'setup-jest.ts' for 'setup' library to prevent NG0400 error in express engine tests. See: https://github.com/angular/angular/blob/d4b423690210872b5c32a322a6090beda30b05a3/packages/core/src/application_ref.ts#L353
- added 'TestBed.initTestEnvironment' to test files where needed
- bumped packages in 'setup' library
- adjusted schematics for ssr
- adjusted server.ts
- adjusted unit tests for 'add-ssr' and 'ng-add'
- removed a reference to "@nguniversal/express-engine" from 'constants.ts'

docs:
- migration docs
- installation docs (including workarounds for issue with the new application builder and SSR)

additional adjustments:
- installation script in scripts/install/functions.sh has been adjusted to work with v17 and previous Angular versions
- LinkComponent refactor - changes around host bindings in OnPush components created dynamically caused an issue when setting the CSS class received from the backend. The class set as a side effect in the tap operator when subscribing data$ property doesn't reflect in DOM. To solve this, such a logic has been moved to ngOnInit. For more about OnPush changes, see: angular/angular@40bb45f

Co-authored-by: Krzysztof Platis <[email protected]>
  • Loading branch information
pawelfras and Platonn authored Dec 20, 2023
1 parent b0682ab commit 19bc9f2
Show file tree
Hide file tree
Showing 122 changed files with 7,371 additions and 7,111 deletions.
2 changes: 1 addition & 1 deletion .github/api-extractor-action/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:16
FROM node:18

COPY package.json /
COPY package-lock.json /
Expand Down
2 changes: 1 addition & 1 deletion .github/cache-builded-libs/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:16
FROM node:18

COPY package.json /
COPY package-lock.json /
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/config-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@master
- uses: actions/setup-node@v4
with:
node-version: '16'
node-version: '18'
- name: NPM
run: npm i
- name: Check configurations
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '16'
node-version: '18'
- name: NPM
run: npm ci
env:
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ feature-libs/smartedit/assets/webApplicationInjector.js
**/*.md
**/*.yml
coverage

/.nx/cache
6 changes: 3 additions & 3 deletions core-libs/setup/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
"tslib": "^2.6.2"
},
"peerDependencies": {
"@angular/core": "^16.2.10",
"@angular/core": "^17.0.5",
"@angular/ssr": "^17.0.5",
"@spartacus/cart": "2211.19.0",
"@spartacus/core": "2211.19.0",
"@spartacus/order": "2211.19.0",
"@spartacus/user": "2211.19.0"
},
"optionalDependencies": {
"@angular/platform-server": "^16.2.10",
"@nguniversal/express-engine": "^16.2.0",
"@angular/platform-server": "^17.0.5",
"express": "^4.18.2"
},
"publishConfig": {
Expand Down
13 changes: 12 additions & 1 deletion core-libs/setup/setup-jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/

import 'jest-preset-angular/setup-jest';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
platformServerTesting,
ServerTestingModule,
} from '@angular/platform-server/testing';

getTestBed().initTestEnvironment(
ServerTestingModule,
platformServerTesting(),
{}
);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NgSetupOptions, RenderOptions } from '@nguniversal/express-engine';
import { SERVER_REQUEST_URL } from '@spartacus/core';
import { NgSetupOptions, RenderOptions } from '../engine/ng-express-engine';
import { EXPRESS_SERVER_LOGGER, LegacyExpressServerLogger } from '../logger';
import {
NgExpressEngine,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { NgSetupOptions } from '@nguniversal/express-engine';
import { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr';
import { NgSetupOptions } from '../engine/ng-express-engine';
import {
OptimizedSsrEngine,
SsrCallbackFn,
Expand All @@ -22,7 +23,7 @@ export type NgExpressEngineInstance = (
) => void;

export type NgExpressEngine = (
setupOptions: Readonly<NgSetupOptions>
setupOptions: Readonly<CommonEngineRenderOptions & CommonEngineOptions>
) => NgExpressEngineInstance;

/**
Expand Down
283 changes: 283 additions & 0 deletions core-libs/setup/ssr/engine/ng-express-engine.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
/*
* SPDX-FileCopyrightText: 2023 SAP Spartacus team <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

// eslint-disable-next-line import/no-unassigned-import
import '@angular/compiler';

import { Component, Inject, InjectionToken, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ServerModule } from '@angular/platform-server';
import { REQUEST, RESPONSE } from '../public_api';
import { ngExpressEngine } from './ng-express-engine';
//@ts-ignore

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@Component({ selector: 'cx-mock', template: 'some template' })
export class MockComponent {}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@NgModule({
imports: [BrowserModule, ServerModule],
declarations: [MockComponent],
bootstrap: [MockComponent],
})
export class MockServerModule {}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@Component({ selector: 'cx-request', template: `url:{{ _req.url }}` })
export class RequestComponent {
constructor(@Inject(REQUEST) public readonly _req: any) {}
}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@NgModule({
imports: [BrowserModule, ServerModule],
declarations: [RequestComponent],
bootstrap: [RequestComponent],
})
export class RequestServerModule {}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@Component({
selector: 'cx-response',
template: `statusCode:{{ _res.statusCode }}`,
})
export class ResponseComponent {
constructor(@Inject(RESPONSE) public readonly _res: any) {}
}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@NgModule({
imports: [BrowserModule, ServerModule],
declarations: [ResponseComponent],
bootstrap: [ResponseComponent],
})
export class ResponseServerModule {}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
export const SOME_TOKEN = new InjectionToken<string>('SOME_TOKEN');

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@Component({
selector: 'cx-token',
template: `message:{{ _someToken.message }}`,
})
export class TokenComponent {
constructor(@Inject(SOME_TOKEN) public readonly _someToken: any) {}
}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts
*
*/
@NgModule({
imports: [BrowserModule, ServerModule],
declarations: [TokenComponent],
bootstrap: [TokenComponent],
})
export class TokenServerModule {}

/**
* @license
* The MIT License
* Copyright (c) 2010-2023 Google LLC. http://angular.io/license
*
* Inspired by tests for ngExpressEngine.
*
* See:
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/index.spec.ts
*
*/
describe('ngExpressEngine', () => {
beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation();
});

it('should render a basic template', (done) => {
ngExpressEngine({ bootstrap: MockServerModule })(
null as any as string,
{
req: { get: () => 'localhost' } as any,
document: '<cx-mock></cx-mock>',
},
(err, html) => {
if (err) {
throw err;
}
expect(html).toContain('some template');
done();
}
);
});

it('Should throw when no module is passed', () => {
ngExpressEngine({ bootstrap: null as any })(
null as any as string,
{
req: {} as any,
bootstrap: null as any,
document: '<cx-mock></cx-mock>',
},
(_err, _html) => {
expect(_err).toBeTruthy();
}
);
});

it('should be able to inject REQUEST token', (done) => {
ngExpressEngine({ bootstrap: RequestServerModule })(
null as any as string,
{
req: {
get: () => 'localhost',
url: 'http://localhost:4200',
} as any,
document: '<cx-request></cx-request>',
},
(err, html) => {
if (err) {
throw err;
}
expect(html).toContain('url:http://localhost:4200');
done();
}
);
});

it('should be able to inject RESPONSE token', (done) => {
const someStatusCode = 400;
ngExpressEngine({ bootstrap: ResponseServerModule })(
null as any as string,
{
req: {
get: () => 'localhost',
res: {
statusCode: someStatusCode,
},
} as any,
document: '<cx-response></cx-response>',
},
(err, html) => {
if (err) {
throw err;
}
expect(html).toContain(`statusCode:${someStatusCode}`);
done();
}
);
});

it('should be able to inject some token', (done) => {
const someValue = { message: 'value' + new Date() };
ngExpressEngine({
bootstrap: TokenServerModule,
providers: [{ provide: SOME_TOKEN, useValue: someValue }],
})(
null as any as string,
{
req: {
get: () => 'localhost',
url: 'http://localhost:4200',
} as any,
document: '<cx-token></cx-token>',
},
(err, html) => {
if (err) {
throw err;
}
expect(html).toContain(someValue.message);
done();
}
);
});
});
Loading

0 comments on commit 19bc9f2

Please sign in to comment.