Skip to content

Commit ec3823b

Browse files
committed
refactor(@angular/ssr): bundle Critters
This commit bundles the Critters library to ensure compatibility with Nodeless environments. Additionally, all licenses for bundled libraries, including Critters, are now included in the package. This helps maintain compliance with open-source license requirements.
1 parent 7ea85e9 commit ec3823b

File tree

15 files changed

+620
-292
lines changed

15 files changed

+620
-292
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@
199199
"tslib": "2.6.3",
200200
"typescript": "5.5.4",
201201
"undici": "6.19.8",
202+
"unenv": "^1.10.0",
202203
"verdaccio": "5.32.1",
203204
"verdaccio-auth-memory": "^10.0.0",
204205
"vite": "5.4.2",

packages/angular/build/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ ts_library(
7979
"@npm//browserslist",
8080
"@npm//critters",
8181
"@npm//esbuild",
82+
"@npm//esbuild-wasm",
8283
"@npm//fast-glob",
8384
"@npm//https-proxy-agent",
8485
"@npm//listr2",
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
// The `bundled_critters` causes issues with module mappings in Bazel,
10+
// leading to unexpected behavior with esbuild. Specifically, the problem occurs
11+
// when esbuild resolves to a different module or version than expected, due to
12+
// how Bazel handles module mappings.
13+
//
14+
// This change aims to resolve esbuild types correctly and maintain consistency
15+
// in the Bazel build process.
16+
17+
declare module 'esbuild' {
18+
export * from 'esbuild-wasm';
19+
}

packages/angular/ssr/BUILD.bazel

+9-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ ts_library(
1818
),
1919
module_name = "@angular/ssr",
2020
deps = [
21+
"//packages/angular/ssr/third_party/critters:bundled_critters_lib",
2122
"@npm//@angular/common",
2223
"@npm//@angular/core",
2324
"@npm//@angular/platform-server",
2425
"@npm//@angular/router",
2526
"@npm//@types/node",
26-
"@npm//critters",
2727
],
2828
)
2929

@@ -32,8 +32,15 @@ ng_package(
3232
package_name = "@angular/ssr",
3333
srcs = [
3434
":package.json",
35+
"//packages/angular/ssr/third_party/critters:bundled_critters_lib",
36+
],
37+
externals = [
38+
"express",
39+
"../../third_party/critters",
40+
],
41+
nested_packages = [
42+
"//packages/angular/ssr/schematics:npm_package",
3543
],
36-
nested_packages = ["//packages/angular/ssr/schematics:npm_package"],
3744
tags = ["release-package"],
3845
deps = [
3946
":ssr",

packages/angular/ssr/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"save": "dependencies"
1414
},
1515
"dependencies": {
16-
"critters": "0.0.24",
1716
"tslib": "^2.3.0"
1817
},
1918
"peerDependencies": {

packages/angular/ssr/src/app.ts

+38-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Hooks } from './hooks';
1414
import { getAngularAppManifest } from './manifest';
1515
import { ServerRouter } from './routes/router';
1616
import { REQUEST, REQUEST_CONTEXT, RESPONSE_INIT } from './tokens';
17+
import { InlineCriticalCssProcessor } from './utils/inline-critical-css';
1718
import { renderAngular } from './utils/ng';
1819

1920
/**
@@ -52,6 +53,11 @@ export class AngularServerApp {
5253
*/
5354
private router: ServerRouter | undefined;
5455

56+
/**
57+
* The `inlineCriticalCssProcessor` is responsible for handling critical CSS inlining.
58+
*/
59+
private inlineCriticalCssProcessor: InlineCriticalCssProcessor | undefined;
60+
5561
/**
5662
* Renders a response for the given HTTP request using the server application.
5763
*
@@ -177,10 +183,38 @@ export class AngularServerApp {
177183
html = await hooks.run('html:transform:pre', { html });
178184
}
179185

180-
return new Response(
181-
await renderAngular(html, manifest.bootstrap(), new URL(request.url), platformProviders),
182-
responseInit,
183-
);
186+
html = await renderAngular(html, manifest.bootstrap(), new URL(request.url), platformProviders);
187+
188+
if (manifest.inlineCriticalCss) {
189+
// Optionally inline critical CSS.
190+
const inlineCriticalCssProcessor = this.getOrCreateInlineCssProcessor();
191+
html = await inlineCriticalCssProcessor.process(html);
192+
}
193+
194+
return new Response(html, responseInit);
195+
}
196+
197+
/**
198+
* Retrieves or creates the inline critical CSS processor.
199+
* If one does not exist, it initializes a new instance.
200+
*
201+
* @returns The inline critical CSS processor instance.
202+
*/
203+
private getOrCreateInlineCssProcessor(): InlineCriticalCssProcessor {
204+
let inlineCriticalCssProcessor = this.inlineCriticalCssProcessor;
205+
206+
if (!inlineCriticalCssProcessor) {
207+
inlineCriticalCssProcessor = new InlineCriticalCssProcessor();
208+
inlineCriticalCssProcessor.readFile = (path: string) => {
209+
const fileName = path.split('/').pop() ?? path;
210+
211+
return this.assets.getServerAsset(fileName);
212+
};
213+
214+
this.inlineCriticalCssProcessor = inlineCriticalCssProcessor;
215+
}
216+
217+
return inlineCriticalCssProcessor;
184218
}
185219
}
186220

packages/angular/ssr/src/common-engine/common-engine.ts

+9-20
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/plat
1111
import * as fs from 'node:fs';
1212
import { dirname, join, normalize, resolve } from 'node:path';
1313
import { URL } from 'node:url';
14-
import { InlineCriticalCssProcessor, InlineCriticalCssResult } from './inline-css-processor';
14+
import { CommonEngineInlineCriticalCssProcessor } from './inline-css-processor';
1515
import {
1616
noopRunMethodAndMeasurePerf,
1717
printPerformanceLogs,
@@ -55,14 +55,10 @@ export interface CommonEngineRenderOptions {
5555

5656
export class CommonEngine {
5757
private readonly templateCache = new Map<string, string>();
58-
private readonly inlineCriticalCssProcessor: InlineCriticalCssProcessor;
58+
private readonly inlineCriticalCssProcessor = new CommonEngineInlineCriticalCssProcessor();
5959
private readonly pageIsSSG = new Map<string, boolean>();
6060

61-
constructor(private options?: CommonEngineOptions) {
62-
this.inlineCriticalCssProcessor = new InlineCriticalCssProcessor({
63-
minify: false,
64-
});
65-
}
61+
constructor(private options?: CommonEngineOptions) {}
6662

6763
/**
6864
* Render an HTML document for a specific URL with specified
@@ -81,17 +77,12 @@ export class CommonEngine {
8177
html = await runMethod('Render Page', () => this.renderApplication(opts));
8278

8379
if (opts.inlineCriticalCss !== false) {
84-
const { content, errors, warnings } = await runMethod('Inline Critical CSS', () =>
80+
const content = await runMethod('Inline Critical CSS', () =>
8581
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
8682
this.inlineCriticalCss(html!, opts),
8783
);
8884

8985
html = content;
90-
91-
// eslint-disable-next-line no-console
92-
warnings?.forEach((m) => console.warn(m));
93-
// eslint-disable-next-line no-console
94-
errors?.forEach((m) => console.error(m));
9586
}
9687
}
9788

@@ -102,13 +93,11 @@ export class CommonEngine {
10293
return html;
10394
}
10495

105-
private inlineCriticalCss(
106-
html: string,
107-
opts: CommonEngineRenderOptions,
108-
): Promise<InlineCriticalCssResult> {
109-
return this.inlineCriticalCssProcessor.process(html, {
110-
outputPath: opts.publicPath ?? (opts.documentFilePath ? dirname(opts.documentFilePath) : ''),
111-
});
96+
private inlineCriticalCss(html: string, opts: CommonEngineRenderOptions): Promise<string> {
97+
const outputPath =
98+
opts.publicPath ?? (opts.documentFilePath ? dirname(opts.documentFilePath) : '');
99+
100+
return this.inlineCriticalCssProcessor.process(html, outputPath);
112101
}
113102

114103
private async retrieveSSGPage(opts: CommonEngineRenderOptions): Promise<string | undefined> {

0 commit comments

Comments
 (0)