@angular/build 19.0.5 → 19.1.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +19 -19
- package/src/builders/application/build-action.js +17 -7
- package/src/builders/application/execute-build.js +2 -2
- package/src/builders/application/execute-post-bundle.d.ts +3 -1
- package/src/builders/application/execute-post-bundle.js +6 -4
- package/src/builders/application/i18n.d.ts +3 -1
- package/src/builders/application/i18n.js +15 -13
- package/src/builders/application/options.js +7 -5
- package/src/builders/dev-server/vite-server.js +22 -26
- package/src/builders/extract-i18n/builder.js +17 -7
- package/src/builders/extract-i18n/options.js +1 -1
- package/src/tools/angular/compilation/angular-compilation.js +17 -7
- package/src/tools/angular/compilation/aot-compilation.d.ts +2 -0
- package/src/tools/angular/compilation/aot-compilation.js +22 -37
- package/src/tools/angular/compilation/factory.d.ts +2 -1
- package/src/tools/angular/compilation/factory.js +22 -11
- package/src/tools/angular/compilation/hmr-candidates.d.ts +22 -0
- package/src/tools/angular/compilation/hmr-candidates.js +238 -0
- package/src/tools/angular/compilation/jit-compilation.d.ts +2 -0
- package/src/tools/angular/compilation/jit-compilation.js +11 -1
- package/src/tools/angular/compilation/parallel-compilation.d.ts +3 -2
- package/src/tools/angular/compilation/parallel-compilation.js +4 -1
- package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
- package/src/tools/angular/compilation/parallel-worker.js +3 -1
- package/src/tools/angular/transformers/lazy-routes-transformer.d.ts +39 -0
- package/src/tools/angular/transformers/lazy-routes-transformer.js +163 -0
- package/src/tools/babel/plugins/pure-toplevel-functions.js +17 -7
- package/src/tools/esbuild/angular/compiler-plugin.d.ts +1 -0
- package/src/tools/esbuild/angular/compiler-plugin.js +18 -8
- package/src/tools/esbuild/angular/source-file-cache.d.ts +1 -1
- package/src/tools/esbuild/angular/source-file-cache.js +17 -7
- package/src/tools/esbuild/compiler-plugin-options.js +1 -0
- package/src/tools/esbuild/global-scripts.js +17 -7
- package/src/tools/esbuild/javascript-transformer-worker.js +17 -7
- package/src/tools/esbuild/stylesheets/less-language.js +17 -7
- package/src/tools/esbuild/stylesheets/sass-language.js +17 -7
- package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +17 -7
- package/src/tools/vite/plugins/ssr-transform-plugin.js +11 -15
- package/src/utils/check-port.js +17 -7
- package/src/utils/i18n-options.d.ts +4 -1
- package/src/utils/i18n-options.js +50 -7
- package/src/utils/index-file/auto-csp.js +17 -7
- package/src/utils/index-file/inline-fonts.js +17 -7
- package/src/utils/load-proxy-config.js +17 -7
- package/src/utils/load-translations.js +17 -7
- package/src/utils/normalize-asset-patterns.js +17 -7
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/manifest.d.ts +5 -1
- package/src/utils/server-rendering/manifest.js +60 -11
- package/src/utils/server-rendering/prerender.js +1 -1
- package/src/utils/service-worker.js +17 -7
|
@@ -22,13 +22,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
22
22
|
}) : function(o, v) {
|
|
23
23
|
o["default"] = v;
|
|
24
24
|
});
|
|
25
|
-
var __importStar = (this && this.__importStar) || function (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
32
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
43
|
exports.createAngularCompilation = createAngularCompilation;
|
|
34
44
|
const environment_options_1 = require("../../../utils/environment-options");
|
|
@@ -37,19 +47,20 @@ const environment_options_1 = require("../../../utils/environment-options");
|
|
|
37
47
|
* compilation either for AOT or JIT mode. By default a parallel compilation is created
|
|
38
48
|
* that uses a Node.js worker thread.
|
|
39
49
|
* @param jit True, for Angular JIT compilation; False, for Angular AOT compilation.
|
|
50
|
+
* @param browserOnlyBuild True, for browser only builds; False, for browser and server builds.
|
|
40
51
|
* @returns An instance of an Angular compilation object.
|
|
41
52
|
*/
|
|
42
|
-
async function createAngularCompilation(jit) {
|
|
53
|
+
async function createAngularCompilation(jit, browserOnlyBuild) {
|
|
43
54
|
if (environment_options_1.useParallelTs) {
|
|
44
55
|
const { ParallelCompilation } = await Promise.resolve().then(() => __importStar(require('./parallel-compilation')));
|
|
45
|
-
return new ParallelCompilation(jit);
|
|
56
|
+
return new ParallelCompilation(jit, browserOnlyBuild);
|
|
46
57
|
}
|
|
47
58
|
if (jit) {
|
|
48
59
|
const { JitCompilation } = await Promise.resolve().then(() => __importStar(require('./jit-compilation')));
|
|
49
|
-
return new JitCompilation();
|
|
60
|
+
return new JitCompilation(browserOnlyBuild);
|
|
50
61
|
}
|
|
51
62
|
else {
|
|
52
63
|
const { AotCompilation } = await Promise.resolve().then(() => __importStar(require('./aot-compilation')));
|
|
53
|
-
return new AotCompilation();
|
|
64
|
+
return new AotCompilation(browserOnlyBuild);
|
|
54
65
|
}
|
|
55
66
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
import type ng from '@angular/compiler-cli';
|
|
9
|
+
import ts from 'typescript';
|
|
10
|
+
/**
|
|
11
|
+
* Analyzes one or more modified files for changes to determine if any
|
|
12
|
+
* class declarations for Angular components are candidates for hot
|
|
13
|
+
* module replacement (HMR). If any source files are also modified but
|
|
14
|
+
* are not candidates then all candidates become invalid. This invalidation
|
|
15
|
+
* ensures that a full rebuild occurs and the running application stays
|
|
16
|
+
* synchronized with the code.
|
|
17
|
+
* @param modifiedFiles A set of modified files to analyze.
|
|
18
|
+
* @param param1 An Angular compiler instance
|
|
19
|
+
* @param staleSourceFiles A map of paths to previous source file instances.
|
|
20
|
+
* @returns A set of HMR candidate component class declarations.
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectHmrCandidates(modifiedFiles: Set<string>, { compiler }: ng.NgtscProgram, staleSourceFiles: Map<string, ts.SourceFile> | undefined): Set<ts.ClassDeclaration>;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.collectHmrCandidates = collectHmrCandidates;
|
|
14
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
16
|
+
/**
|
|
17
|
+
* Analyzes one or more modified files for changes to determine if any
|
|
18
|
+
* class declarations for Angular components are candidates for hot
|
|
19
|
+
* module replacement (HMR). If any source files are also modified but
|
|
20
|
+
* are not candidates then all candidates become invalid. This invalidation
|
|
21
|
+
* ensures that a full rebuild occurs and the running application stays
|
|
22
|
+
* synchronized with the code.
|
|
23
|
+
* @param modifiedFiles A set of modified files to analyze.
|
|
24
|
+
* @param param1 An Angular compiler instance
|
|
25
|
+
* @param staleSourceFiles A map of paths to previous source file instances.
|
|
26
|
+
* @returns A set of HMR candidate component class declarations.
|
|
27
|
+
*/
|
|
28
|
+
function collectHmrCandidates(modifiedFiles, { compiler }, staleSourceFiles) {
|
|
29
|
+
const candidates = new Set();
|
|
30
|
+
for (const file of modifiedFiles) {
|
|
31
|
+
// If the file is a template for component(s), add component classes as candidates
|
|
32
|
+
const templateFileNodes = compiler.getComponentsWithTemplateFile(file);
|
|
33
|
+
if (templateFileNodes.size) {
|
|
34
|
+
templateFileNodes.forEach((node) => candidates.add(node));
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
// If the file is a style for component(s), add component classes as candidates
|
|
38
|
+
const styleFileNodes = compiler.getComponentsWithStyleFile(file);
|
|
39
|
+
if (styleFileNodes.size) {
|
|
40
|
+
styleFileNodes.forEach((node) => candidates.add(node));
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const staleSource = staleSourceFiles?.get(file);
|
|
44
|
+
if (staleSource === undefined) {
|
|
45
|
+
// Unknown file requires a rebuild so clear out the candidates and stop collecting
|
|
46
|
+
candidates.clear();
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
const updatedSource = compiler.getCurrentProgram().getSourceFile(file);
|
|
50
|
+
if (updatedSource === undefined) {
|
|
51
|
+
// No longer existing program file requires a rebuild so clear out the candidates and stop collecting
|
|
52
|
+
candidates.clear();
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
// Analyze the stale and updated file for changes
|
|
56
|
+
const fileCandidates = analyzeFileUpdates(staleSource, updatedSource, compiler);
|
|
57
|
+
if (fileCandidates) {
|
|
58
|
+
fileCandidates.forEach((node) => candidates.add(node));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Unsupported HMR changes present
|
|
62
|
+
// Only template and style literal changes are allowed.
|
|
63
|
+
candidates.clear();
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return candidates;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Analyzes the updates of a source file for potential HMR component class candidates.
|
|
71
|
+
* A source file can contain candidates if only the Angular component metadata of a class
|
|
72
|
+
* has been changed and the metadata changes are only of supported fields.
|
|
73
|
+
* @param stale The stale (previous) source file instance.
|
|
74
|
+
* @param updated The updated source file instance.
|
|
75
|
+
* @param compiler An Angular compiler instance.
|
|
76
|
+
* @returns An array of candidate class declarations; or `null` if unsupported changes are present.
|
|
77
|
+
*/
|
|
78
|
+
function analyzeFileUpdates(stale, updated, compiler) {
|
|
79
|
+
if (stale.statements.length !== updated.statements.length) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const candidates = [];
|
|
83
|
+
for (let i = 0; i < updated.statements.length; ++i) {
|
|
84
|
+
const updatedNode = updated.statements[i];
|
|
85
|
+
const staleNode = stale.statements[i];
|
|
86
|
+
if (typescript_1.default.isClassDeclaration(updatedNode)) {
|
|
87
|
+
if (!typescript_1.default.isClassDeclaration(staleNode)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
// Check class declaration differences (name/heritage/modifiers)
|
|
91
|
+
if (updatedNode.name?.text !== staleNode.name?.text) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (!equalRangeText(updatedNode.heritageClauses, updated, staleNode.heritageClauses, stale)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const updatedModifiers = typescript_1.default.getModifiers(updatedNode);
|
|
98
|
+
const staleModifiers = typescript_1.default.getModifiers(staleNode);
|
|
99
|
+
if (updatedModifiers?.length !== staleModifiers?.length ||
|
|
100
|
+
!updatedModifiers?.every((updatedModifier) => staleModifiers?.some((staleModifier) => updatedModifier.kind === staleModifier.kind))) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// Check for component class nodes
|
|
104
|
+
const meta = compiler.getMeta(updatedNode);
|
|
105
|
+
if (meta?.decorator && meta.isComponent === true) {
|
|
106
|
+
const updatedDecorators = typescript_1.default.getDecorators(updatedNode);
|
|
107
|
+
const staleDecorators = typescript_1.default.getDecorators(staleNode);
|
|
108
|
+
if (!staleDecorators || staleDecorators.length !== updatedDecorators?.length) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// TODO: Check other decorators instead of assuming all multi-decorator components are unsupported
|
|
112
|
+
if (staleDecorators.length > 1) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
// Find index of component metadata decorator
|
|
116
|
+
const metaDecoratorIndex = updatedDecorators?.indexOf(meta.decorator);
|
|
117
|
+
(0, node_assert_1.default)(metaDecoratorIndex !== undefined, 'Component metadata decorator should always be present on component class.');
|
|
118
|
+
const updatedDecoratorExpression = meta.decorator.expression;
|
|
119
|
+
(0, node_assert_1.default)(typescript_1.default.isCallExpression(updatedDecoratorExpression) &&
|
|
120
|
+
updatedDecoratorExpression.arguments.length === 1, 'Component metadata decorator should contain a call expression with a single argument.');
|
|
121
|
+
// Check the matching stale index for the component decorator
|
|
122
|
+
const staleDecoratorExpression = staleDecorators[metaDecoratorIndex]?.expression;
|
|
123
|
+
if (!staleDecoratorExpression ||
|
|
124
|
+
!typescript_1.default.isCallExpression(staleDecoratorExpression) ||
|
|
125
|
+
staleDecoratorExpression.arguments.length !== 1) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
// Check decorator name/expression
|
|
129
|
+
// NOTE: This would typically be `Component` but can also be a property expression or some other alias.
|
|
130
|
+
// To avoid complex checks, this ensures the textual representation does not change. This has a low chance
|
|
131
|
+
// of a false positive if the expression is changed to still reference the `Component` type but has different
|
|
132
|
+
// text. However, it is rare for `Component` to not be used directly and additionally unlikely that it would
|
|
133
|
+
// be changed between edits. A false positive would also only lead to a difference of a full page reload versus
|
|
134
|
+
// an HMR update.
|
|
135
|
+
if (!equalRangeText(updatedDecoratorExpression.expression, updated, staleDecoratorExpression.expression, stale)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
// Compare component meta decorator object literals
|
|
139
|
+
if (hasUnsupportedMetaUpdates(staleDecoratorExpression, stale, updatedDecoratorExpression, updated)) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
// Compare text of the member nodes to determine if any changes have occurred
|
|
143
|
+
if (!equalRangeText(updatedNode.members, updated, staleNode.members, stale)) {
|
|
144
|
+
// A change to a member outside a component's metadata is unsupported
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
// If all previous class checks passed, this class is supported for HMR updates
|
|
148
|
+
candidates.push(updatedNode);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Compare text of the statement nodes to determine if any changes have occurred
|
|
153
|
+
// TODO: Consider expanding this to check semantic updates for each node kind
|
|
154
|
+
if (!equalRangeText(updatedNode, updated, staleNode, stale)) {
|
|
155
|
+
// A change to a statement outside a component's metadata is unsupported
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return candidates;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The set of Angular component metadata fields that are supported by HMR updates.
|
|
163
|
+
*/
|
|
164
|
+
const SUPPORTED_FIELDS = new Set(['template', 'templateUrl', 'styles', 'styleUrl', 'stylesUrl']);
|
|
165
|
+
/**
|
|
166
|
+
* Analyzes the metadata fields of a decorator call expression for unsupported HMR updates.
|
|
167
|
+
* Only updates to supported fields can be present for HMR to be viable.
|
|
168
|
+
* @param staleCall A call expression instance.
|
|
169
|
+
* @param staleSource The source file instance containing the stale call instance.
|
|
170
|
+
* @param updatedCall A call expression instance.
|
|
171
|
+
* @param updatedSource The source file instance containing the updated call instance.
|
|
172
|
+
* @returns true, if unsupported metadata updates are present; false, otherwise.
|
|
173
|
+
*/
|
|
174
|
+
function hasUnsupportedMetaUpdates(staleCall, staleSource, updatedCall, updatedSource) {
|
|
175
|
+
const staleObject = staleCall.arguments[0];
|
|
176
|
+
const updatedObject = updatedCall.arguments[0];
|
|
177
|
+
if (!typescript_1.default.isObjectLiteralExpression(staleObject) || !typescript_1.default.isObjectLiteralExpression(updatedObject)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const unsupportedFields = [];
|
|
181
|
+
for (const property of staleObject.properties) {
|
|
182
|
+
if (!typescript_1.default.isPropertyAssignment(property) || typescript_1.default.isComputedPropertyName(property.name)) {
|
|
183
|
+
// Unsupported object literal property
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
const name = property.name.text;
|
|
187
|
+
if (SUPPORTED_FIELDS.has(name)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
unsupportedFields.push(property.initializer);
|
|
191
|
+
}
|
|
192
|
+
let i = 0;
|
|
193
|
+
for (const property of updatedObject.properties) {
|
|
194
|
+
if (!typescript_1.default.isPropertyAssignment(property) || typescript_1.default.isComputedPropertyName(property.name)) {
|
|
195
|
+
// Unsupported object literal property
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
const name = property.name.text;
|
|
199
|
+
if (SUPPORTED_FIELDS.has(name)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
// Compare in order
|
|
203
|
+
if (!equalRangeText(property.initializer, updatedSource, unsupportedFields[i++], staleSource)) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return i !== unsupportedFields.length;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Compares the text from a provided range in a source file to the text of a range in a second source file.
|
|
211
|
+
* The comparison avoids making any intermediate string copies.
|
|
212
|
+
* @param firstRange A text range within the first source file.
|
|
213
|
+
* @param firstSource A source file instance.
|
|
214
|
+
* @param secondRange A text range within the second source file.
|
|
215
|
+
* @param secondSource A source file instance.
|
|
216
|
+
* @returns true, if the text from both ranges is equal; false, otherwise.
|
|
217
|
+
*/
|
|
218
|
+
function equalRangeText(firstRange, firstSource, secondRange, secondSource) {
|
|
219
|
+
// Check matching undefined values
|
|
220
|
+
if (!firstRange || !secondRange) {
|
|
221
|
+
return firstRange === secondRange;
|
|
222
|
+
}
|
|
223
|
+
// Ensure lengths are equal
|
|
224
|
+
const firstLength = firstRange.end - firstRange.pos;
|
|
225
|
+
const secondLength = secondRange.end - secondRange.pos;
|
|
226
|
+
if (firstLength !== secondLength) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
// Check each character
|
|
230
|
+
for (let i = 0; i < firstLength; ++i) {
|
|
231
|
+
const firstChar = firstSource.text.charCodeAt(i + firstRange.pos);
|
|
232
|
+
const secondChar = secondSource.text.charCodeAt(i + secondRange.pos);
|
|
233
|
+
if (firstChar !== secondChar) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
@@ -11,6 +11,8 @@ import { AngularHostOptions } from '../angular-host';
|
|
|
11
11
|
import { AngularCompilation, DiagnosticModes, EmitFileResult } from './angular-compilation';
|
|
12
12
|
export declare class JitCompilation extends AngularCompilation {
|
|
13
13
|
#private;
|
|
14
|
+
private readonly browserOnlyBuild;
|
|
15
|
+
constructor(browserOnlyBuild: boolean);
|
|
14
16
|
initialize(tsconfig: string, hostOptions: AngularHostOptions, compilerOptionsTransformer?: (compilerOptions: ng.CompilerOptions) => ng.CompilerOptions): Promise<{
|
|
15
17
|
affectedFiles: ReadonlySet<ts.SourceFile>;
|
|
16
18
|
compilerOptions: ng.CompilerOptions;
|
|
@@ -17,6 +17,7 @@ const load_esm_1 = require("../../../utils/load-esm");
|
|
|
17
17
|
const profiling_1 = require("../../esbuild/profiling");
|
|
18
18
|
const angular_host_1 = require("../angular-host");
|
|
19
19
|
const jit_resource_transformer_1 = require("../transformers/jit-resource-transformer");
|
|
20
|
+
const lazy_routes_transformer_1 = require("../transformers/lazy-routes-transformer");
|
|
20
21
|
const web_worker_transformer_1 = require("../transformers/web-worker-transformer");
|
|
21
22
|
const angular_compilation_1 = require("./angular-compilation");
|
|
22
23
|
class JitCompilationState {
|
|
@@ -34,7 +35,12 @@ class JitCompilationState {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
class JitCompilation extends angular_compilation_1.AngularCompilation {
|
|
38
|
+
browserOnlyBuild;
|
|
37
39
|
#state;
|
|
40
|
+
constructor(browserOnlyBuild) {
|
|
41
|
+
super();
|
|
42
|
+
this.browserOnlyBuild = browserOnlyBuild;
|
|
43
|
+
}
|
|
38
44
|
async initialize(tsconfig, hostOptions, compilerOptionsTransformer) {
|
|
39
45
|
// Dynamically load the Angular compiler CLI package
|
|
40
46
|
const { constructorParametersDownlevelTransform } = await (0, load_esm_1.loadEsmModule)('@angular/compiler-cli/private/tooling');
|
|
@@ -71,7 +77,8 @@ class JitCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
71
77
|
emitAffectedFiles() {
|
|
72
78
|
(0, node_assert_1.default)(this.#state, 'Compilation must be initialized prior to emitting files.');
|
|
73
79
|
const { compilerHost, typeScriptProgram, constructorParametersDownlevelTransform, replaceResourcesTransform, webWorkerTransform, } = this.#state;
|
|
74
|
-
const
|
|
80
|
+
const compilerOptions = typeScriptProgram.getCompilerOptions();
|
|
81
|
+
const buildInfoFilename = compilerOptions.tsBuildInfoFile ?? '.tsbuildinfo';
|
|
75
82
|
const emittedFiles = [];
|
|
76
83
|
const writeFileCallback = (filename, contents, _a, _b, sourceFiles) => {
|
|
77
84
|
if (!sourceFiles?.length && filename.endsWith(buildInfoFilename)) {
|
|
@@ -89,6 +96,9 @@ class JitCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
89
96
|
webWorkerTransform,
|
|
90
97
|
],
|
|
91
98
|
};
|
|
99
|
+
if (!this.browserOnlyBuild) {
|
|
100
|
+
transformers.before.push((0, lazy_routes_transformer_1.lazyRoutesTransformer)(compilerOptions, compilerHost));
|
|
101
|
+
}
|
|
92
102
|
// TypeScript will loop until there are no more affected files in the program
|
|
93
103
|
while (typeScriptProgram.emitNextAffectedFile(writeFileCallback, undefined, undefined, transformers)) {
|
|
94
104
|
/* empty */
|
|
@@ -20,8 +20,9 @@ import { AngularCompilation, DiagnosticModes, EmitFileResult } from './angular-c
|
|
|
20
20
|
*/
|
|
21
21
|
export declare class ParallelCompilation extends AngularCompilation {
|
|
22
22
|
#private;
|
|
23
|
-
readonly jit
|
|
24
|
-
|
|
23
|
+
private readonly jit;
|
|
24
|
+
private readonly browserOnlyBuild;
|
|
25
|
+
constructor(jit: boolean, browserOnlyBuild: boolean);
|
|
25
26
|
initialize(tsconfig: string, hostOptions: AngularHostOptions, compilerOptionsTransformer?: (compilerOptions: CompilerOptions) => CompilerOptions): Promise<{
|
|
26
27
|
affectedFiles: ReadonlySet<SourceFile>;
|
|
27
28
|
compilerOptions: CompilerOptions;
|
|
@@ -22,10 +22,12 @@ const angular_compilation_1 = require("./angular-compilation");
|
|
|
22
22
|
*/
|
|
23
23
|
class ParallelCompilation extends angular_compilation_1.AngularCompilation {
|
|
24
24
|
jit;
|
|
25
|
+
browserOnlyBuild;
|
|
25
26
|
#worker;
|
|
26
|
-
constructor(jit) {
|
|
27
|
+
constructor(jit, browserOnlyBuild) {
|
|
27
28
|
super();
|
|
28
29
|
this.jit = jit;
|
|
30
|
+
this.browserOnlyBuild = browserOnlyBuild;
|
|
29
31
|
// TODO: Convert to import.meta usage during ESM transition
|
|
30
32
|
const localRequire = (0, node_module_1.createRequire)(__filename);
|
|
31
33
|
this.#worker = new worker_pool_1.WorkerPool({
|
|
@@ -82,6 +84,7 @@ class ParallelCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
82
84
|
fileReplacements: hostOptions.fileReplacements,
|
|
83
85
|
tsconfig,
|
|
84
86
|
jit: this.jit,
|
|
87
|
+
browserOnlyBuild: this.browserOnlyBuild,
|
|
85
88
|
stylesheetPort: stylesheetChannel.port2,
|
|
86
89
|
optionsPort: optionsChannel.port2,
|
|
87
90
|
optionsSignal,
|
|
@@ -10,6 +10,7 @@ import { type MessagePort } from 'node:worker_threads';
|
|
|
10
10
|
import type { DiagnosticModes } from './angular-compilation';
|
|
11
11
|
export interface InitRequest {
|
|
12
12
|
jit: boolean;
|
|
13
|
+
browserOnlyBuild: boolean;
|
|
13
14
|
tsconfig: string;
|
|
14
15
|
fileReplacements?: Record<string, string>;
|
|
15
16
|
stylesheetPort: MessagePort;
|
|
@@ -23,7 +23,9 @@ const jit_compilation_1 = require("./jit-compilation");
|
|
|
23
23
|
let compilation;
|
|
24
24
|
const sourceFileCache = new source_file_cache_1.SourceFileCache();
|
|
25
25
|
async function initialize(request) {
|
|
26
|
-
compilation ??= request.jit
|
|
26
|
+
compilation ??= request.jit
|
|
27
|
+
? new jit_compilation_1.JitCompilation(request.browserOnlyBuild)
|
|
28
|
+
: new aot_compilation_1.AotCompilation(request.browserOnlyBuild);
|
|
27
29
|
const stylesheetRequests = new Map();
|
|
28
30
|
request.stylesheetPort.on('message', ({ requestId, value, error }) => {
|
|
29
31
|
if (error) {
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
import ts from 'typescript';
|
|
9
|
+
/**
|
|
10
|
+
* A transformer factory that adds a property to the lazy-loaded route object.
|
|
11
|
+
* This property is used to allow for the retrieval of the module path during SSR.
|
|
12
|
+
*
|
|
13
|
+
* @param compilerOptions The compiler options.
|
|
14
|
+
* @param compilerHost The compiler host.
|
|
15
|
+
* @returns A transformer factory.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* **Before:**
|
|
19
|
+
* ```ts
|
|
20
|
+
* const routes: Routes = [
|
|
21
|
+
* {
|
|
22
|
+
* path: 'lazy',
|
|
23
|
+
* loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
|
|
24
|
+
* }
|
|
25
|
+
* ];
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* **After:**
|
|
29
|
+
* ```ts
|
|
30
|
+
* const routes: Routes = [
|
|
31
|
+
* {
|
|
32
|
+
* path: 'lazy',
|
|
33
|
+
* loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule),
|
|
34
|
+
* ...(typeof ngServerMode !== "undefined" && ngServerMode ? { ɵentryName: "./lazy/lazy.module.ts" }: {})
|
|
35
|
+
* }
|
|
36
|
+
* ];
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function lazyRoutesTransformer(compilerOptions: ts.CompilerOptions, compilerHost: ts.CompilerHost): ts.TransformerFactory<ts.SourceFile>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.lazyRoutesTransformer = lazyRoutesTransformer;
|
|
14
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
15
|
+
const posix_1 = require("node:path/posix");
|
|
16
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
17
|
+
/**
|
|
18
|
+
* A transformer factory that adds a property to the lazy-loaded route object.
|
|
19
|
+
* This property is used to allow for the retrieval of the module path during SSR.
|
|
20
|
+
*
|
|
21
|
+
* @param compilerOptions The compiler options.
|
|
22
|
+
* @param compilerHost The compiler host.
|
|
23
|
+
* @returns A transformer factory.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* **Before:**
|
|
27
|
+
* ```ts
|
|
28
|
+
* const routes: Routes = [
|
|
29
|
+
* {
|
|
30
|
+
* path: 'lazy',
|
|
31
|
+
* loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
|
|
32
|
+
* }
|
|
33
|
+
* ];
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* **After:**
|
|
37
|
+
* ```ts
|
|
38
|
+
* const routes: Routes = [
|
|
39
|
+
* {
|
|
40
|
+
* path: 'lazy',
|
|
41
|
+
* loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule),
|
|
42
|
+
* ...(typeof ngServerMode !== "undefined" && ngServerMode ? { ɵentryName: "./lazy/lazy.module.ts" }: {})
|
|
43
|
+
* }
|
|
44
|
+
* ];
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
function lazyRoutesTransformer(compilerOptions, compilerHost) {
|
|
48
|
+
const moduleResolutionCache = compilerHost.getModuleResolutionCache?.();
|
|
49
|
+
(0, node_assert_1.default)(typeof compilerOptions.basePath === 'string', 'compilerOptions.basePath should be a string.');
|
|
50
|
+
const basePath = compilerOptions.basePath;
|
|
51
|
+
return (context) => {
|
|
52
|
+
const factory = context.factory;
|
|
53
|
+
const visitor = (node) => {
|
|
54
|
+
if (!typescript_1.default.isObjectLiteralExpression(node)) {
|
|
55
|
+
// Not an object literal, so skip it.
|
|
56
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
57
|
+
}
|
|
58
|
+
const loadFunction = getLoadComponentOrChildrenProperty(node)?.initializer;
|
|
59
|
+
// Check if the initializer is an arrow function or a function expression
|
|
60
|
+
if (!loadFunction ||
|
|
61
|
+
(!typescript_1.default.isArrowFunction(loadFunction) && !typescript_1.default.isFunctionExpression(loadFunction))) {
|
|
62
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
63
|
+
}
|
|
64
|
+
let callExpression;
|
|
65
|
+
if (typescript_1.default.isArrowFunction(loadFunction)) {
|
|
66
|
+
// Handle arrow functions: body can either be a block or a direct call expression
|
|
67
|
+
const body = loadFunction.body;
|
|
68
|
+
if (typescript_1.default.isBlock(body)) {
|
|
69
|
+
// Arrow function with a block: check the first statement for a return call expression
|
|
70
|
+
const firstStatement = body.statements[0];
|
|
71
|
+
if (firstStatement &&
|
|
72
|
+
typescript_1.default.isReturnStatement(firstStatement) &&
|
|
73
|
+
firstStatement.expression &&
|
|
74
|
+
typescript_1.default.isCallExpression(firstStatement.expression)) {
|
|
75
|
+
callExpression = firstStatement.expression;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (typescript_1.default.isCallExpression(body)) {
|
|
79
|
+
// Arrow function with a direct call expression as its body
|
|
80
|
+
callExpression = body;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (typescript_1.default.isFunctionExpression(loadFunction)) {
|
|
84
|
+
// Handle function expressions: check for a return statement with a call expression
|
|
85
|
+
const returnExpression = loadFunction.body.statements.find(typescript_1.default.isReturnStatement)?.expression;
|
|
86
|
+
if (returnExpression && typescript_1.default.isCallExpression(returnExpression)) {
|
|
87
|
+
callExpression = returnExpression;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!callExpression) {
|
|
91
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
92
|
+
}
|
|
93
|
+
// Optionally check for the 'then' property access expression
|
|
94
|
+
const expression = callExpression.expression;
|
|
95
|
+
if (!typescript_1.default.isCallExpression(expression) &&
|
|
96
|
+
typescript_1.default.isPropertyAccessExpression(expression) &&
|
|
97
|
+
expression.name.text !== 'then') {
|
|
98
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
99
|
+
}
|
|
100
|
+
const importExpression = typescript_1.default.isPropertyAccessExpression(expression)
|
|
101
|
+
? expression.expression // Navigate to the underlying expression for 'then'
|
|
102
|
+
: callExpression;
|
|
103
|
+
// Ensure the underlying expression is an import call
|
|
104
|
+
if (!typescript_1.default.isCallExpression(importExpression) ||
|
|
105
|
+
importExpression.expression.kind !== typescript_1.default.SyntaxKind.ImportKeyword) {
|
|
106
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
107
|
+
}
|
|
108
|
+
// Check if the argument to the import call is a string literal
|
|
109
|
+
const callExpressionArgument = importExpression.arguments[0];
|
|
110
|
+
if (!typescript_1.default.isStringLiteralLike(callExpressionArgument)) {
|
|
111
|
+
// Not a string literal, so skip it.
|
|
112
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
113
|
+
}
|
|
114
|
+
const resolvedPath = typescript_1.default.resolveModuleName(callExpressionArgument.text, node.getSourceFile().fileName, compilerOptions, compilerHost, moduleResolutionCache)?.resolvedModule?.resolvedFileName;
|
|
115
|
+
if (!resolvedPath) {
|
|
116
|
+
// Could not resolve the module, so skip it.
|
|
117
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
118
|
+
}
|
|
119
|
+
const resolvedRelativePath = (0, posix_1.relative)(basePath, resolvedPath);
|
|
120
|
+
// Create the new property
|
|
121
|
+
// Example: `...(typeof ngServerMode !== "undefined" && ngServerMode ? { ɵentryName: "src/home.ts" }: {})`
|
|
122
|
+
const newProperty = factory.createSpreadAssignment(factory.createParenthesizedExpression(factory.createConditionalExpression(factory.createBinaryExpression(factory.createBinaryExpression(factory.createTypeOfExpression(factory.createIdentifier('ngServerMode')), factory.createToken(typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken), factory.createStringLiteral('undefined')), factory.createToken(typescript_1.default.SyntaxKind.AmpersandAmpersandToken), factory.createIdentifier('ngServerMode')), factory.createToken(typescript_1.default.SyntaxKind.QuestionToken), factory.createObjectLiteralExpression([
|
|
123
|
+
factory.createPropertyAssignment(factory.createIdentifier('ɵentryName'), factory.createStringLiteral(resolvedRelativePath)),
|
|
124
|
+
]), factory.createToken(typescript_1.default.SyntaxKind.ColonToken), factory.createObjectLiteralExpression([]))));
|
|
125
|
+
// Add the new property to the object literal.
|
|
126
|
+
return factory.updateObjectLiteralExpression(node, [...node.properties, newProperty]);
|
|
127
|
+
};
|
|
128
|
+
return (sourceFile) => {
|
|
129
|
+
const text = sourceFile.text;
|
|
130
|
+
if (!text.includes('loadC')) {
|
|
131
|
+
// Fast check for 'loadComponent' and 'loadChildren'.
|
|
132
|
+
return sourceFile;
|
|
133
|
+
}
|
|
134
|
+
return typescript_1.default.visitEachChild(sourceFile, visitor, context);
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Retrieves the property assignment for the `loadComponent` or `loadChildren` property of a route object.
|
|
140
|
+
*
|
|
141
|
+
* @param node The object literal expression to search.
|
|
142
|
+
* @returns The property assignment if found, otherwise `undefined`.
|
|
143
|
+
*/
|
|
144
|
+
function getLoadComponentOrChildrenProperty(node) {
|
|
145
|
+
let hasPathProperty = false;
|
|
146
|
+
let loadComponentOrChildrenProperty;
|
|
147
|
+
for (const prop of node.properties) {
|
|
148
|
+
if (!typescript_1.default.isPropertyAssignment(prop) || !typescript_1.default.isIdentifier(prop.name)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const propertyNameText = prop.name.text;
|
|
152
|
+
if (propertyNameText === 'path') {
|
|
153
|
+
hasPathProperty = true;
|
|
154
|
+
}
|
|
155
|
+
else if (propertyNameText === 'loadComponent' || propertyNameText === 'loadChildren') {
|
|
156
|
+
loadComponentOrChildrenProperty = prop;
|
|
157
|
+
}
|
|
158
|
+
if (hasPathProperty && loadComponentOrChildrenProperty) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return loadComponentOrChildrenProperty;
|
|
163
|
+
}
|
|
@@ -22,13 +22,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
22
22
|
}) : function(o, v) {
|
|
23
23
|
o["default"] = v;
|
|
24
24
|
});
|
|
25
|
-
var __importStar = (this && this.__importStar) || function (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
32
42
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
43
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
44
|
};
|