Skip to content

Commit 2397eed

Browse files
clydindgp1130
authored andcommitted
refactor(@angular-devkit/build-angular): move esbuild angular compiler host creation to separate function
The creation of the esbuild Angular plugin's Angular compiler host has now been consolidated in a separate function. This refactor reduces the amount of code within the plugin's main start function as well as centralizing all initialization of the host into one location.
1 parent 22e1b7f commit 2397eed

File tree

2 files changed

+123
-71
lines changed

2 files changed

+123
-71
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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.io/license
7+
*/
8+
9+
import ng from '@angular/compiler-cli';
10+
import ts from 'typescript';
11+
12+
export type AngularCompilerOptions = ng.CompilerOptions;
13+
export type AngularCompilerHost = ng.CompilerHost;
14+
15+
export interface AngularHostOptions {
16+
fileReplacements?: Record<string, string>;
17+
sourceFileCache?: Map<string, ts.SourceFile>;
18+
modifiedFiles?: Set<string>;
19+
transformStylesheet(
20+
data: string,
21+
containingFile: string,
22+
stylesheetFile?: string,
23+
): Promise<string | null>;
24+
}
25+
26+
// Temporary deep import for host augmentation support.
27+
// TODO: Move these to a private exports location or move the implementation into this package.
28+
const {
29+
augmentHostWithCaching,
30+
augmentHostWithReplacements,
31+
augmentProgramWithVersioning,
32+
} = require('@ngtools/webpack/src/ivy/host');
33+
34+
/**
35+
* Patches in-place the `getSourceFiles` function on an instance of a TypeScript
36+
* `Program` to ensure that all returned SourceFile instances have a `version`
37+
* field. The `version` field is required when used with a TypeScript BuilderProgram.
38+
* @param program The TypeScript Program instance to patch.
39+
*/
40+
export function ensureSourceFileVersions(program: ts.Program): void {
41+
augmentProgramWithVersioning(program);
42+
}
43+
44+
export function createAngularCompilerHost(
45+
compilerOptions: AngularCompilerOptions,
46+
hostOptions: AngularHostOptions,
47+
): AngularCompilerHost {
48+
// Create TypeScript compiler host
49+
const host: AngularCompilerHost = ts.createIncrementalCompilerHost(compilerOptions);
50+
51+
// The AOT compiler currently requires this hook to allow for a transformResource hook.
52+
// Once the AOT compiler allows only a transformResource hook, this can be reevaluated.
53+
host.readResource = async function (filename) {
54+
return this.readFile(filename) ?? '';
55+
};
56+
57+
// Add an AOT compiler resource transform hook
58+
host.transformResource = async function (data, context) {
59+
// Only style resources are transformed currently
60+
if (context.type !== 'style') {
61+
return null;
62+
}
63+
64+
const result = await hostOptions.transformStylesheet(
65+
data,
66+
context.containingFile,
67+
context.resourceFile ?? undefined,
68+
);
69+
70+
return result ? { content: result } : null;
71+
};
72+
73+
// Allow the AOT compiler to request the set of changed templates and styles
74+
host.getModifiedResourceFiles = function () {
75+
return hostOptions.modifiedFiles;
76+
};
77+
78+
// Augment TypeScript Host for file replacements option
79+
if (hostOptions.fileReplacements) {
80+
augmentHostWithReplacements(host, hostOptions.fileReplacements);
81+
}
82+
83+
// Augment TypeScript Host with source file caching if provided
84+
if (hostOptions.sourceFileCache) {
85+
augmentHostWithCaching(host, hostOptions.sourceFileCache);
86+
}
87+
88+
return host;
89+
}

packages/angular_devkit/build_angular/src/builders/browser-esbuild/compiler-plugin.ts

Lines changed: 34 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import type { CompilerHost, NgtscProgram } from '@angular/compiler-cli';
9+
import type { NgtscProgram } from '@angular/compiler-cli';
1010
import { transformAsync } from '@babel/core';
1111
import type {
1212
OnStartResult,
@@ -25,6 +25,7 @@ import ts from 'typescript';
2525
import angularApplicationPreset from '../../babel/presets/application';
2626
import { requiresLinking } from '../../babel/webpack-loader';
2727
import { loadEsmModule } from '../../utils/load-esm';
28+
import { createAngularCompilerHost, ensureSourceFileVersions } from './angular-host';
2829
import {
2930
logCumulativeDurations,
3031
profileAsync,
@@ -268,77 +269,39 @@ export function createCompilerPlugin(
268269
// Reset stylesheet resource output files
269270
stylesheetResourceFiles = [];
270271

271-
// Create TypeScript compiler host
272-
const host = ts.createIncrementalCompilerHost(compilerOptions);
273-
274-
// Temporarily process external resources via readResource.
275-
// The AOT compiler currently requires this hook to allow for a transformResource hook.
276-
// Once the AOT compiler allows only a transformResource hook, this can be reevaluated.
277-
(host as CompilerHost).readResource = async function (fileName) {
278-
// Template resources (.html/.svg) files are not bundled or transformed
279-
if (fileName.endsWith('.html') || fileName.endsWith('.svg')) {
280-
return this.readFile(fileName) ?? '';
281-
}
282-
283-
const { contents, resourceFiles, errors, warnings } = await bundleStylesheetFile(
284-
fileName,
285-
styleOptions,
286-
);
287-
288-
(result.errors ??= []).push(...errors);
289-
(result.warnings ??= []).push(...warnings);
290-
stylesheetResourceFiles.push(...resourceFiles);
291-
292-
return contents;
293-
};
294-
295-
// Add an AOT compiler resource transform hook
296-
(host as CompilerHost).transformResource = async function (data, context) {
297-
// Only inline style resources are transformed separately currently
298-
if (context.resourceFile || context.type !== 'style') {
299-
return null;
300-
}
301-
302-
// The file with the resource content will either be an actual file (resourceFile)
303-
// or the file containing the inline component style text (containingFile).
304-
const file = context.resourceFile ?? context.containingFile;
305-
306-
const { contents, resourceFiles, errors, warnings } = await bundleStylesheetText(
307-
data,
308-
{
309-
resolvePath: path.dirname(file),
310-
virtualName: file,
311-
},
312-
styleOptions,
313-
);
314-
315-
(result.errors ??= []).push(...errors);
316-
(result.warnings ??= []).push(...warnings);
317-
stylesheetResourceFiles.push(...resourceFiles);
318-
319-
return { content: contents };
320-
};
321-
322-
// Temporary deep import for host augmentation support
323-
const {
324-
augmentHostWithCaching,
325-
augmentHostWithReplacements,
326-
augmentProgramWithVersioning,
327-
} = require('@ngtools/webpack/src/ivy/host');
272+
// Create Angular compiler host
273+
const host = createAngularCompilerHost(compilerOptions, {
274+
fileReplacements: pluginOptions.fileReplacements,
275+
modifiedFiles: pluginOptions.sourceFileCache?.modifiedFiles,
276+
sourceFileCache: pluginOptions.sourceFileCache,
277+
async transformStylesheet(data, containingFile, stylesheetFile) {
278+
// Stylesheet file only exists for external stylesheets
279+
const filename = stylesheetFile ?? containingFile;
280+
281+
// Temporary workaround for lack of virtual file support in the Sass plugin.
282+
// External Sass stylesheets are transformed using the file instead of the already read content.
283+
let stylesheetResult;
284+
if (filename.endsWith('.scss') || filename.endsWith('.sass')) {
285+
stylesheetResult = await bundleStylesheetFile(filename, styleOptions);
286+
} else {
287+
stylesheetResult = await bundleStylesheetText(
288+
data,
289+
{
290+
resolvePath: path.dirname(filename),
291+
virtualName: filename,
292+
},
293+
styleOptions,
294+
);
295+
}
328296

329-
// Augment TypeScript Host for file replacements option
330-
if (pluginOptions.fileReplacements) {
331-
augmentHostWithReplacements(host, pluginOptions.fileReplacements);
332-
}
297+
const { contents, resourceFiles, errors, warnings } = stylesheetResult;
298+
(result.errors ??= []).push(...errors);
299+
(result.warnings ??= []).push(...warnings);
300+
stylesheetResourceFiles.push(...resourceFiles);
333301

334-
// Augment TypeScript Host with source file caching if provided
335-
if (pluginOptions.sourceFileCache) {
336-
augmentHostWithCaching(host, pluginOptions.sourceFileCache);
337-
// Allow the AOT compiler to request the set of changed templates and styles
338-
(host as CompilerHost).getModifiedResourceFiles = function () {
339-
return pluginOptions.sourceFileCache?.modifiedFiles;
340-
};
341-
}
302+
return contents;
303+
},
304+
});
342305

343306
// Create the Angular specific program that contains the Angular compiler
344307
const angularProgram = profileSync(
@@ -348,7 +311,7 @@ export function createCompilerPlugin(
348311
previousAngularProgram = angularProgram;
349312
const angularCompiler = angularProgram.compiler;
350313
const typeScriptProgram = angularProgram.getTsProgram();
351-
augmentProgramWithVersioning(typeScriptProgram);
314+
ensureSourceFileVersions(typeScriptProgram);
352315

353316
const builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
354317
typeScriptProgram,

0 commit comments

Comments
 (0)