@angular/build 19.1.0-next.0 → 19.1.0-next.2
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 +13 -11
- package/src/builders/application/execute-build.js +37 -4
- 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.d.ts +2 -2
- package/src/builders/dev-server/vite-server.js +31 -47
- package/src/builders/extract-i18n/options.js +1 -1
- package/src/tools/angular/angular-host.d.ts +1 -1
- package/src/tools/angular/angular-host.js +4 -4
- package/src/tools/angular/compilation/aot-compilation.d.ts +2 -0
- package/src/tools/angular/compilation/aot-compilation.js +30 -9
- package/src/tools/angular/compilation/factory.d.ts +2 -1
- package/src/tools/angular/compilation/factory.js +5 -4
- package/src/tools/angular/compilation/jit-compilation.d.ts +2 -0
- package/src/tools/angular/compilation/jit-compilation.js +12 -2
- 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/esbuild/angular/compiler-plugin.d.ts +1 -0
- package/src/tools/esbuild/angular/compiler-plugin.js +1 -1
- package/src/tools/esbuild/application-code-bundle.js +26 -20
- package/src/tools/esbuild/compiler-plugin-options.js +1 -0
- package/src/tools/vite/plugins/angular-memory-plugin.d.ts +1 -0
- package/src/tools/vite/plugins/angular-memory-plugin.js +25 -2
- package/src/tools/vite/utils.d.ts +14 -0
- package/src/tools/vite/utils.js +37 -0
- package/src/utils/environment-options.js +1 -1
- 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 +5 -5
- package/src/utils/normalize-cache.js +1 -1
- package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.js +9 -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/tsconfig-build.json +6 -0
|
@@ -17,6 +17,7 @@ const typescript_1 = __importDefault(require("typescript"));
|
|
|
17
17
|
const profiling_1 = require("../../esbuild/profiling");
|
|
18
18
|
const angular_host_1 = require("../angular-host");
|
|
19
19
|
const jit_bootstrap_transformer_1 = require("../transformers/jit-bootstrap-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
|
const hmr_candidates_1 = require("./hmr-candidates");
|
|
@@ -48,7 +49,12 @@ class AngularCompilationState {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
52
|
+
browserOnlyBuild;
|
|
51
53
|
#state;
|
|
54
|
+
constructor(browserOnlyBuild) {
|
|
55
|
+
super();
|
|
56
|
+
this.browserOnlyBuild = browserOnlyBuild;
|
|
57
|
+
}
|
|
52
58
|
async initialize(tsconfig, hostOptions, compilerOptionsTransformer) {
|
|
53
59
|
// Dynamically load the Angular compiler CLI package
|
|
54
60
|
const { NgtscProgram, OptimizeFor } = await angular_compilation_1.AngularCompilation.loadCompilerCli();
|
|
@@ -58,22 +64,34 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
58
64
|
if (compilerOptions.externalRuntimeStyles) {
|
|
59
65
|
hostOptions.externalStylesheets ??= new Map();
|
|
60
66
|
}
|
|
67
|
+
// Reuse the package.json cache from the previous compilation
|
|
68
|
+
const packageJsonCache = this.#state?.compilerHost
|
|
69
|
+
.getModuleResolutionCache?.()
|
|
70
|
+
?.getPackageJsonInfoCache();
|
|
61
71
|
const useHmr = compilerOptions['_enableHmr'] &&
|
|
62
72
|
hostOptions.modifiedFiles &&
|
|
63
73
|
hostOptions.modifiedFiles.size <= HMR_MODIFIED_FILE_LIMIT;
|
|
64
|
-
// Collect stale source files for HMR analysis of inline component resources
|
|
65
74
|
let staleSourceFiles;
|
|
66
|
-
|
|
75
|
+
let clearPackageJsonCache = false;
|
|
76
|
+
if (hostOptions.modifiedFiles && this.#state) {
|
|
67
77
|
for (const modifiedFile of hostOptions.modifiedFiles) {
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
// Clear package.json cache if a node modules file was modified
|
|
79
|
+
if (!clearPackageJsonCache && modifiedFile.includes('node_modules')) {
|
|
80
|
+
clearPackageJsonCache = true;
|
|
81
|
+
packageJsonCache?.clear();
|
|
82
|
+
}
|
|
83
|
+
// Collect stale source files for HMR analysis of inline component resources
|
|
84
|
+
if (useHmr) {
|
|
85
|
+
const sourceFile = this.#state.typeScriptProgram.getSourceFile(modifiedFile);
|
|
86
|
+
if (sourceFile) {
|
|
87
|
+
staleSourceFiles ??= new Map();
|
|
88
|
+
staleSourceFiles.set(modifiedFile, sourceFile);
|
|
89
|
+
}
|
|
72
90
|
}
|
|
73
91
|
}
|
|
74
92
|
}
|
|
75
93
|
// Create Angular compiler host
|
|
76
|
-
const host = (0, angular_host_1.createAngularCompilerHost)(typescript_1.default, compilerOptions, hostOptions);
|
|
94
|
+
const host = (0, angular_host_1.createAngularCompilerHost)(typescript_1.default, compilerOptions, hostOptions, packageJsonCache);
|
|
77
95
|
// Create the Angular specific program that contains the Angular compiler
|
|
78
96
|
const angularProgram = (0, profiling_1.profileSync)('NG_CREATE_PROGRAM', () => new NgtscProgram(rootNames, compilerOptions, host, this.#state?.angularProgram));
|
|
79
97
|
const angularCompiler = angularProgram.compiler;
|
|
@@ -99,6 +117,7 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
99
117
|
if (relativePath.startsWith('..')) {
|
|
100
118
|
relativePath = componentFilename;
|
|
101
119
|
}
|
|
120
|
+
relativePath = relativePath.replaceAll('\\', '/');
|
|
102
121
|
const updateId = encodeURIComponent(`${host.getCanonicalFileName(relativePath)}@${node.name?.text}`);
|
|
103
122
|
const updateText = angularCompiler.emitHmrUpdateModule(node);
|
|
104
123
|
if (updateText === null) {
|
|
@@ -210,8 +229,10 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
210
229
|
};
|
|
211
230
|
const transformers = angularCompiler.prepareEmit().transformers;
|
|
212
231
|
transformers.before ??= [];
|
|
213
|
-
transformers.before.push((0, jit_bootstrap_transformer_1.replaceBootstrap)(() => typeScriptProgram.getProgram().getTypeChecker()));
|
|
214
|
-
|
|
232
|
+
transformers.before.push((0, jit_bootstrap_transformer_1.replaceBootstrap)(() => typeScriptProgram.getProgram().getTypeChecker()), webWorkerTransform);
|
|
233
|
+
if (!this.browserOnlyBuild) {
|
|
234
|
+
transformers.before.push((0, lazy_routes_transformer_1.lazyRoutesTransformer)(compilerOptions, compilerHost));
|
|
235
|
+
}
|
|
215
236
|
// Emit is handled in write file callback when using TypeScript
|
|
216
237
|
if (useTypeScriptTranspilation) {
|
|
217
238
|
// TypeScript will loop until there are no more affected files in the program
|
|
@@ -11,6 +11,7 @@ import type { AngularCompilation } from './angular-compilation';
|
|
|
11
11
|
* compilation either for AOT or JIT mode. By default a parallel compilation is created
|
|
12
12
|
* that uses a Node.js worker thread.
|
|
13
13
|
* @param jit True, for Angular JIT compilation; False, for Angular AOT compilation.
|
|
14
|
+
* @param browserOnlyBuild True, for browser only builds; False, for browser and server builds.
|
|
14
15
|
* @returns An instance of an Angular compilation object.
|
|
15
16
|
*/
|
|
16
|
-
export declare function createAngularCompilation(jit: boolean): Promise<AngularCompilation>;
|
|
17
|
+
export declare function createAngularCompilation(jit: boolean, browserOnlyBuild: boolean): Promise<AngularCompilation>;
|
|
@@ -47,19 +47,20 @@ const environment_options_1 = require("../../../utils/environment-options");
|
|
|
47
47
|
* compilation either for AOT or JIT mode. By default a parallel compilation is created
|
|
48
48
|
* that uses a Node.js worker thread.
|
|
49
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.
|
|
50
51
|
* @returns An instance of an Angular compilation object.
|
|
51
52
|
*/
|
|
52
|
-
async function createAngularCompilation(jit) {
|
|
53
|
+
async function createAngularCompilation(jit, browserOnlyBuild) {
|
|
53
54
|
if (environment_options_1.useParallelTs) {
|
|
54
55
|
const { ParallelCompilation } = await Promise.resolve().then(() => __importStar(require('./parallel-compilation')));
|
|
55
|
-
return new ParallelCompilation(jit);
|
|
56
|
+
return new ParallelCompilation(jit, browserOnlyBuild);
|
|
56
57
|
}
|
|
57
58
|
if (jit) {
|
|
58
59
|
const { JitCompilation } = await Promise.resolve().then(() => __importStar(require('./jit-compilation')));
|
|
59
|
-
return new JitCompilation();
|
|
60
|
+
return new JitCompilation(browserOnlyBuild);
|
|
60
61
|
}
|
|
61
62
|
else {
|
|
62
63
|
const { AotCompilation } = await Promise.resolve().then(() => __importStar(require('./aot-compilation')));
|
|
63
|
-
return new AotCompilation();
|
|
64
|
+
return new AotCompilation(browserOnlyBuild);
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -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');
|
|
@@ -42,7 +48,7 @@ class JitCompilation extends angular_compilation_1.AngularCompilation {
|
|
|
42
48
|
const { options: originalCompilerOptions, rootNames, errors: configurationDiagnostics, } = await this.loadConfiguration(tsconfig);
|
|
43
49
|
const compilerOptions = compilerOptionsTransformer?.(originalCompilerOptions) ?? originalCompilerOptions;
|
|
44
50
|
// Create Angular compiler host
|
|
45
|
-
const host = (0, angular_host_1.createAngularCompilerHost)(typescript_1.default, compilerOptions, hostOptions);
|
|
51
|
+
const host = (0, angular_host_1.createAngularCompilerHost)(typescript_1.default, compilerOptions, hostOptions, undefined);
|
|
46
52
|
// Create the TypeScript Program
|
|
47
53
|
const typeScriptProgram = (0, profiling_1.profileSync)('TS_CREATE_PROGRAM', () => typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram(rootNames, compilerOptions, host, this.#state?.typeScriptProgram ?? typescript_1.default.readBuilderProgram(compilerOptions, host), configurationDiagnostics));
|
|
48
54
|
const affectedFiles = (0, profiling_1.profileSync)('TS_FIND_AFFECTED', () => findAffectedFiles(typeScriptProgram));
|
|
@@ -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
|
+
}
|
|
@@ -13,6 +13,7 @@ export interface CompilerPluginOptions {
|
|
|
13
13
|
sourcemap: boolean | 'external';
|
|
14
14
|
tsconfig: string;
|
|
15
15
|
jit?: boolean;
|
|
16
|
+
browserOnlyBuild?: boolean;
|
|
16
17
|
/** Skip TypeScript compilation setup. This is useful to re-use the TypeScript compilation from another plugin. */
|
|
17
18
|
noopTypeScriptCompilation?: boolean;
|
|
18
19
|
advancedOptimizations?: boolean;
|
|
@@ -103,7 +103,7 @@ function createCompilerPlugin(pluginOptions, stylesheetBundler) {
|
|
|
103
103
|
// Create new reusable compilation for the appropriate mode based on the `jit` plugin option
|
|
104
104
|
const compilation = pluginOptions.noopTypeScriptCompilation
|
|
105
105
|
? new compilation_1.NoopCompilation()
|
|
106
|
-
: await (0, compilation_1.createAngularCompilation)(!!pluginOptions.jit);
|
|
106
|
+
: await (0, compilation_1.createAngularCompilation)(!!pluginOptions.jit, !!pluginOptions.browserOnlyBuild);
|
|
107
107
|
// Compilation is initially assumed to have errors until emitted
|
|
108
108
|
let hasCompilationErrors = true;
|
|
109
109
|
// Determines if TypeScript should process JavaScript files based on tsconfig `allowJs` option
|
|
@@ -136,6 +136,15 @@ function createServerPolyfillBundleOptions(options, target, loadResultCache) {
|
|
|
136
136
|
if (!polyfillBundleOptions) {
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
|
+
const jsBanner = [];
|
|
140
|
+
if (polyfillBundleOptions.external?.length) {
|
|
141
|
+
jsBanner.push(`globalThis['ngServerMode'] = true;`);
|
|
142
|
+
}
|
|
143
|
+
if (isNodePlatform) {
|
|
144
|
+
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
|
|
145
|
+
// See: https://github.com/evanw/esbuild/issues/1921.
|
|
146
|
+
jsBanner.push(`import { createRequire } from 'node:module';`, `globalThis['require'] ??= createRequire(import.meta.url);`);
|
|
147
|
+
}
|
|
139
148
|
const buildOptions = {
|
|
140
149
|
...polyfillBundleOptions,
|
|
141
150
|
platform: isNodePlatform ? 'node' : 'neutral',
|
|
@@ -146,16 +155,9 @@ function createServerPolyfillBundleOptions(options, target, loadResultCache) {
|
|
|
146
155
|
// More details: https://github.com/angular/angular-cli/issues/25405.
|
|
147
156
|
mainFields: ['es2020', 'es2015', 'module', 'main'],
|
|
148
157
|
entryNames: '[name]',
|
|
149
|
-
banner:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
|
|
153
|
-
// See: https://github.com/evanw/esbuild/issues/1921.
|
|
154
|
-
`import { createRequire } from 'node:module';`,
|
|
155
|
-
`globalThis['require'] ??= createRequire(import.meta.url);`,
|
|
156
|
-
].join('\n'),
|
|
157
|
-
}
|
|
158
|
-
: undefined,
|
|
158
|
+
banner: {
|
|
159
|
+
js: jsBanner.join('\n'),
|
|
160
|
+
},
|
|
159
161
|
target,
|
|
160
162
|
entryPoints: {
|
|
161
163
|
'polyfills.server': namespace,
|
|
@@ -248,6 +250,8 @@ function createServerMainCodeBundleOptions(options, target, sourceFileCache, sty
|
|
|
248
250
|
ɵextractRoutesAndCreateRouteTree,
|
|
249
251
|
ɵgetOrCreateAngularServerApp,
|
|
250
252
|
} from '@angular/ssr';`,
|
|
253
|
+
// Need for HMR
|
|
254
|
+
`export { ɵresetCompiledComponents } from '@angular/core';`,
|
|
251
255
|
// Re-export all symbols including default export from 'main.server.ts'
|
|
252
256
|
`export { default } from '${mainServerEntryPointJsImport}';`,
|
|
253
257
|
`export * from '${mainServerEntryPointJsImport}';`,
|
|
@@ -274,19 +278,21 @@ function createSsrEntryCodeBundleOptions(options, target, sourceFileCache, style
|
|
|
274
278
|
const ssrEntryNamespace = 'angular:ssr-entry';
|
|
275
279
|
const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest';
|
|
276
280
|
const isNodePlatform = options.ssrOptions?.platform !== schema_1.ExperimentalPlatform.Neutral;
|
|
281
|
+
const jsBanner = [];
|
|
282
|
+
if (options.externalDependencies?.length) {
|
|
283
|
+
jsBanner.push(`globalThis['ngServerMode'] = true;`);
|
|
284
|
+
}
|
|
285
|
+
if (isNodePlatform) {
|
|
286
|
+
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
|
|
287
|
+
// See: https://github.com/evanw/esbuild/issues/1921.
|
|
288
|
+
jsBanner.push(`import { createRequire } from 'node:module';`, `globalThis['require'] ??= createRequire(import.meta.url);`);
|
|
289
|
+
}
|
|
277
290
|
const buildOptions = {
|
|
278
291
|
...getEsBuildServerCommonOptions(options),
|
|
279
292
|
target,
|
|
280
|
-
banner:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
|
|
284
|
-
// See: https://github.com/evanw/esbuild/issues/1921.
|
|
285
|
-
`import { createRequire } from 'node:module';`,
|
|
286
|
-
`globalThis['require'] ??= createRequire(import.meta.url);`,
|
|
287
|
-
].join('\n'),
|
|
288
|
-
}
|
|
289
|
-
: undefined,
|
|
293
|
+
banner: {
|
|
294
|
+
js: jsBanner.join('\n'),
|
|
295
|
+
},
|
|
290
296
|
entryPoints: {
|
|
291
297
|
'server': ssrEntryNamespace,
|
|
292
298
|
},
|
|
@@ -12,6 +12,7 @@ function createCompilerPluginOptions(options, sourceFileCache, loadResultCache,
|
|
|
12
12
|
const { sourcemapOptions, tsconfig, fileReplacements, advancedOptimizations, jit, externalRuntimeStyles, instrumentForCoverage, } = options;
|
|
13
13
|
const incremental = !!options.watch;
|
|
14
14
|
return {
|
|
15
|
+
browserOnlyBuild: !options.serverEntryPoint,
|
|
15
16
|
sourcemap: !!sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
|
|
16
17
|
thirdPartySourcemaps: sourcemapOptions.vendor,
|
|
17
18
|
tsconfig,
|
|
@@ -10,6 +10,7 @@ import { AngularMemoryOutputFiles } from '../utils';
|
|
|
10
10
|
interface AngularMemoryPluginOptions {
|
|
11
11
|
virtualProjectRoot: string;
|
|
12
12
|
outputFiles: AngularMemoryOutputFiles;
|
|
13
|
+
templateUpdates?: ReadonlyMap<string, string>;
|
|
13
14
|
external?: string[];
|
|
14
15
|
skipViteClient?: boolean;
|
|
15
16
|
}
|
|
@@ -15,6 +15,7 @@ const node_assert_1 = __importDefault(require("node:assert"));
|
|
|
15
15
|
const promises_1 = require("node:fs/promises");
|
|
16
16
|
const node_path_1 = require("node:path");
|
|
17
17
|
const load_esm_1 = require("../../../utils/load-esm");
|
|
18
|
+
const ANGULAR_PREFIX = '/@ng/';
|
|
18
19
|
async function createAngularMemoryPlugin(options) {
|
|
19
20
|
const { virtualProjectRoot, outputFiles, external } = options;
|
|
20
21
|
const { normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
|
|
@@ -22,7 +23,11 @@ async function createAngularMemoryPlugin(options) {
|
|
|
22
23
|
name: 'vite:angular-memory',
|
|
23
24
|
// Ensures plugin hooks run before built-in Vite hooks
|
|
24
25
|
enforce: 'pre',
|
|
25
|
-
async resolveId(source, importer) {
|
|
26
|
+
async resolveId(source, importer, { ssr }) {
|
|
27
|
+
// For SSR with component HMR, pass through as a virtual module
|
|
28
|
+
if (ssr && source.startsWith(ANGULAR_PREFIX)) {
|
|
29
|
+
return '\0' + source;
|
|
30
|
+
}
|
|
26
31
|
// Prevent vite from resolving an explicit external dependency (`externalDependencies` option)
|
|
27
32
|
if (external?.includes(source)) {
|
|
28
33
|
// This is still not ideal since Vite will still transform the import specifier to
|
|
@@ -35,13 +40,31 @@ async function createAngularMemoryPlugin(options) {
|
|
|
35
40
|
const [importerFile] = importer.split('?', 1);
|
|
36
41
|
source = '/' + (0, node_path_1.join)((0, node_path_1.dirname)((0, node_path_1.relative)(virtualProjectRoot, importerFile)), source);
|
|
37
42
|
}
|
|
43
|
+
else if (!ssr &&
|
|
44
|
+
source[0] === '/' &&
|
|
45
|
+
importer.endsWith('index.html') &&
|
|
46
|
+
normalizePath(importer).startsWith(virtualProjectRoot)) {
|
|
47
|
+
// This is only needed when using SSR and `angularSsrMiddleware` (old style) to correctly resolve
|
|
48
|
+
// .js files when using lazy-loading.
|
|
49
|
+
// Remove query if present
|
|
50
|
+
const [importerFile] = importer.split('?', 1);
|
|
51
|
+
source =
|
|
52
|
+
'/' + (0, node_path_1.join)((0, node_path_1.dirname)((0, node_path_1.relative)(virtualProjectRoot, importerFile)), (0, node_path_1.basename)(source));
|
|
53
|
+
}
|
|
38
54
|
}
|
|
39
55
|
const [file] = source.split('?', 1);
|
|
40
56
|
if (outputFiles.has(file)) {
|
|
41
57
|
return (0, node_path_1.join)(virtualProjectRoot, source);
|
|
42
58
|
}
|
|
43
59
|
},
|
|
44
|
-
load(id) {
|
|
60
|
+
load(id, loadOptions) {
|
|
61
|
+
// For SSR component updates, return the component update module or empty if none
|
|
62
|
+
if (loadOptions?.ssr && id.startsWith(`\0${ANGULAR_PREFIX}`)) {
|
|
63
|
+
// Extract component identifier (first character is rollup virtual module null)
|
|
64
|
+
const requestUrl = new URL(id.slice(1), 'http://localhost');
|
|
65
|
+
const componentId = requestUrl.searchParams.get('c');
|
|
66
|
+
return (componentId && options.templateUpdates?.get(encodeURIComponent(componentId))) ?? '';
|
|
67
|
+
}
|
|
45
68
|
const [file] = id.split('?', 1);
|
|
46
69
|
const relativeFile = '/' + normalizePath((0, node_path_1.relative)(virtualProjectRoot, file));
|
|
47
70
|
const codeContents = outputFiles.get(relativeFile)?.contents;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.dev/license
|
|
7
7
|
*/
|
|
8
|
+
import type { DepOptimizationConfig } from 'vite';
|
|
9
|
+
import { JavaScriptTransformer } from '../esbuild/javascript-transformer';
|
|
8
10
|
export type AngularMemoryOutputFiles = Map<string, {
|
|
9
11
|
contents: Uint8Array;
|
|
10
12
|
hash: string;
|
|
@@ -12,3 +14,15 @@ export type AngularMemoryOutputFiles = Map<string, {
|
|
|
12
14
|
}>;
|
|
13
15
|
export declare function pathnameWithoutBasePath(url: string, basePath: string): string;
|
|
14
16
|
export declare function lookupMimeTypeFromRequest(url: string): string | undefined;
|
|
17
|
+
export type EsbuildLoaderOption = Exclude<DepOptimizationConfig['esbuildOptions'], undefined>['loader'];
|
|
18
|
+
export declare function getDepOptimizationConfig({ disabled, exclude, include, target, zoneless, prebundleTransformer, ssr, loader, thirdPartySourcemaps, }: {
|
|
19
|
+
disabled: boolean;
|
|
20
|
+
exclude: string[];
|
|
21
|
+
include: string[];
|
|
22
|
+
target: string[];
|
|
23
|
+
prebundleTransformer: JavaScriptTransformer;
|
|
24
|
+
ssr: boolean;
|
|
25
|
+
zoneless: boolean;
|
|
26
|
+
loader?: EsbuildLoaderOption;
|
|
27
|
+
thirdPartySourcemaps: boolean;
|
|
28
|
+
}): DepOptimizationConfig;
|