Skip to content

Commit f4cd684

Browse files
committed
refactor(@ngtools/webpack): remove direct angular resource loader
We remove the custom direct resource loader which is used for JIT when `directTemplateLoading` is enabled. Instead, use Webpack's [asset modules](https://webpack.js.org/guides/asset-modules/) which were introduced in version 5. To the resource URL, we also add a query parameter, `ngResource`. This is used to be filter request based on a query. See https://webpack.js.org/guides/asset-modules/#replacing-inline-loader-syntax for more information.
1 parent ee694e4 commit f4cd684

File tree

6 files changed

+58
-121
lines changed

6 files changed

+58
-121
lines changed

packages/angular_devkit/build_angular/src/webpack/configs/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,12 @@ export async function getCommonConfig(wco: WebpackConfigOptions): Promise<Config
360360
: undefined,
361361

362362
rules: [
363+
{
364+
test: /\.?(svg|html)$/,
365+
// Only process HTML and SVG which are known Angular component resources.
366+
resourceQuery: /\?ngResource/,
367+
type: 'asset/source',
368+
},
363369
{
364370
// Mark files inside `rxjs/add` as containing side effects.
365371
// If this is fixed upstream and the fixed version becomes the minimum

packages/ngtools/webpack/src/ivy/transformation.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,14 @@ export function createJitTransformers(
3737
builder: ts.BuilderProgram,
3838
compilerCli: typeof import('@angular/compiler-cli'),
3939
options: {
40-
directTemplateLoading?: boolean;
4140
inlineStyleFileExtension?: string;
4241
},
4342
): ts.CustomTransformers {
4443
const getTypeChecker = () => builder.getProgram().getTypeChecker();
4544

4645
return {
4746
before: [
48-
replaceResources(
49-
() => true,
50-
getTypeChecker,
51-
options.directTemplateLoading,
52-
options.inlineStyleFileExtension,
53-
),
47+
replaceResources(() => true, getTypeChecker, options.inlineStyleFileExtension),
5448
compilerCli.constructorParametersDownlevelTransform(builder.getProgram()),
5549
],
5650
};

packages/ngtools/webpack/src/loaders/direct-resource.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

packages/ngtools/webpack/src/resource_loader.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
InlineAngularResourceLoaderPath,
1717
InlineAngularResourceSymbol,
1818
} from './loaders/inline-resource';
19+
import { NG_COMPONENT_RESOURCE_QUERY } from './transformers/replace_resources';
1920

2021
interface CompilationOutput {
2122
content: string;
@@ -110,17 +111,24 @@ export class WebpackResourceLoader {
110111
throw new Error('WebpackResourceLoader cannot be used without parentCompilation');
111112
}
112113

113-
// Create a special URL for reading the resource from memory
114-
const entry =
115-
filePath ||
116-
(resourceType
117-
? `${containingFile}-${this.outputPathCounter}.${fileExtension}!=!${this.inlineDataLoaderPath}!${containingFile}`
118-
: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
119-
`angular-resource:${resourceType},${createHash('md5').update(data!).digest('hex')}`);
114+
const getEntry = (): string => {
115+
if (filePath) {
116+
return `${filePath}?${NG_COMPONENT_RESOURCE_QUERY}`;
117+
} else if (resourceType) {
118+
return (
119+
// app.component.ts-2.css?ngResource!=!@ngtools/webpack/src/loaders/inline-resource.js!app.component.ts
120+
`${containingFile}-${this.outputPathCounter}.${fileExtension}` +
121+
`?${NG_COMPONENT_RESOURCE_QUERY}!=!${this.inlineDataLoaderPath}!${containingFile}`
122+
);
123+
} else if (data) {
124+
// Create a special URL for reading the resource from memory
125+
return `angular-resource:${resourceType},${createHash('md5').update(data).digest('hex')}`;
126+
}
120127

121-
if (!entry) {
122-
throw new Error(`"filePath" or "data" must be specified.`);
123-
}
128+
throw new Error(`"filePath", "resourceType" or "data" must be specified.`);
129+
};
130+
131+
const entry = getEntry();
124132

125133
// Simple sanity check.
126134
if (filePath?.match(/\.[jt]s$/)) {

packages/ngtools/webpack/src/transformers/replace_resources.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
*/
88

99
import * as ts from 'typescript';
10-
import { DirectAngularResourceLoaderPath } from '../loaders/direct-resource';
1110
import { InlineAngularResourceLoaderPath } from '../loaders/inline-resource';
1211

12+
export const NG_COMPONENT_RESOURCE_QUERY = 'ngResource';
13+
1314
export function replaceResources(
1415
shouldTransform: (fileName: string) => boolean,
1516
getTypeChecker: () => ts.TypeChecker,
16-
directTemplateLoading = false,
1717
inlineStyleFileExtension?: string,
1818
): ts.TransformerFactory<ts.SourceFile> {
1919
return (context: ts.TransformationContext) => {
@@ -30,7 +30,6 @@ export function replaceResources(
3030
nodeFactory,
3131
node,
3232
typeChecker,
33-
directTemplateLoading,
3433
resourceImportDeclarations,
3534
moduleKind,
3635
inlineStyleFileExtension,
@@ -81,7 +80,6 @@ function visitDecorator(
8180
nodeFactory: ts.NodeFactory,
8281
node: ts.Decorator,
8382
typeChecker: ts.TypeChecker,
84-
directTemplateLoading: boolean,
8583
resourceImportDeclarations: ts.ImportDeclaration[],
8684
moduleKind?: ts.ModuleKind,
8785
inlineStyleFileExtension?: string,
@@ -111,7 +109,6 @@ function visitDecorator(
111109
nodeFactory,
112110
node,
113111
styleReplacements,
114-
directTemplateLoading,
115112
resourceImportDeclarations,
116113
moduleKind,
117114
inlineStyleFileExtension,
@@ -144,7 +141,6 @@ function visitComponentMetadata(
144141
nodeFactory: ts.NodeFactory,
145142
node: ts.ObjectLiteralElementLike,
146143
styleReplacements: ts.Expression[],
147-
directTemplateLoading: boolean,
148144
resourceImportDeclarations: ts.ImportDeclaration[],
149145
moduleKind: ts.ModuleKind = ts.ModuleKind.ES2015,
150146
inlineStyleFileExtension?: string,
@@ -159,11 +155,7 @@ function visitComponentMetadata(
159155
return undefined;
160156

161157
case 'templateUrl':
162-
const loaderOptions = moduleKind < ts.ModuleKind.ES2015 ? '?esModule=false' : '';
163-
const url = getResourceUrl(
164-
node.initializer,
165-
directTemplateLoading ? `!${DirectAngularResourceLoaderPath}${loaderOptions}!` : '',
166-
);
158+
const url = getResourceUrl(node.initializer);
167159
if (!url) {
168160
return node;
169161
}
@@ -200,9 +192,12 @@ function visitComponentMetadata(
200192
if (inlineStyleFileExtension) {
201193
const data = Buffer.from(node.text).toString('base64');
202194
const containingFile = node.getSourceFile().fileName;
203-
url = `${containingFile}.${inlineStyleFileExtension}!=!${InlineAngularResourceLoaderPath}?data=${encodeURIComponent(
204-
data,
205-
)}!${containingFile}`;
195+
// app.component.ts.css?ngResource!=!@ngtools/webpack/src/loaders/inline-resource.js?data=...!app.component.ts
196+
url =
197+
`${containingFile}.${inlineStyleFileExtension}?${NG_COMPONENT_RESOURCE_QUERY}` +
198+
`!=!${InlineAngularResourceLoaderPath}?data=${encodeURIComponent(
199+
data,
200+
)}!${containingFile}`;
206201
} else {
207202
return nodeFactory.createStringLiteral(node.text);
208203
}
@@ -230,13 +225,13 @@ function visitComponentMetadata(
230225
}
231226
}
232227

233-
export function getResourceUrl(node: ts.Node, loader = ''): string | null {
228+
export function getResourceUrl(node: ts.Node): string | null {
234229
// only analyze strings
235230
if (!ts.isStringLiteral(node) && !ts.isNoSubstitutionTemplateLiteral(node)) {
236231
return null;
237232
}
238233

239-
return `${loader}${/^\.?\.\//.test(node.text) ? '' : './'}${node.text}`;
234+
return `${/^\.?\.\//.test(node.text) ? '' : './'}${node.text}?${NG_COMPONENT_RESOURCE_QUERY}`;
240235
}
241236

242237
function isComponentDecorator(node: ts.Node, typeChecker: ts.TypeChecker): node is ts.Decorator {

packages/ngtools/webpack/src/transformers/replace_resources_spec.ts

Lines changed: 22 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88

99
import { tags } from '@angular-devkit/core';
1010
import * as ts from 'typescript';
11-
import { DirectAngularResourceLoaderPath } from '../loaders/direct-resource';
1211
import { replaceResources } from './replace_resources';
1312
import { createTypescriptContext, transformTypescript } from './spec_helpers';
1413

1514
function transform(
1615
input: string,
1716
shouldTransform = true,
18-
directTemplateLoading = true,
1917
importHelpers = true,
2018
module: ts.ModuleKind = ts.ModuleKind.ES2020,
2119
) {
@@ -24,11 +22,7 @@ function transform(
2422
module,
2523
});
2624
const getTypeChecker = () => program.getTypeChecker();
27-
const transformer = replaceResources(
28-
() => shouldTransform,
29-
getTypeChecker,
30-
directTemplateLoading,
31-
);
25+
const transformer = replaceResources(() => shouldTransform, getTypeChecker);
3226

3327
return transformTypescript(input, [transformer], program, compilerHost);
3428
}
@@ -51,9 +45,9 @@ describe('@ngtools/webpack transformers', () => {
5145
`;
5246
const output = tags.stripIndent`
5347
import { __decorate } from "tslib";
54-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.html";
55-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
56-
import __NG_CLI_RESOURCE__2 from "./app.component.2.css";
48+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
49+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
50+
import __NG_CLI_RESOURCE__2 from "./app.component.2.css?ngResource";
5751
import { Component } from '@angular/core';
5852
5953
let AppComponent = class AppComponent {
@@ -102,53 +96,12 @@ describe('@ngtools/webpack transformers', () => {
10296
AppComponent = (0, tslib_1.__decorate)([
10397
(0, core_1.Component)({
10498
selector: 'app-root',
105-
template: require("!${DirectAngularResourceLoaderPath}?esModule=false!./app.component.html"),
106-
styles: [require("./app.component.css"), require("./app.component.2.css")] }) ], AppComponent);
99+
template: require("./app.component.html?ngResource"),
100+
styles: [require("./app.component.css?ngResource"), require("./app.component.2.css?ngResource")] }) ], AppComponent);
107101
exports.AppComponent = AppComponent;
108102
`;
109103

110-
const result = transform(input, true, true, true, ts.ModuleKind.CommonJS);
111-
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
112-
});
113-
114-
it('should not replace resources when directTemplateLoading is false', () => {
115-
const input = tags.stripIndent`
116-
import { Component } from '@angular/core';
117-
118-
@Component({
119-
selector: 'app-root',
120-
templateUrl: './app.component.html',
121-
styleUrls: [
122-
'./app.component.css',
123-
'./app.component.2.css'
124-
]
125-
})
126-
export class AppComponent {
127-
title = 'app';
128-
}
129-
`;
130-
const output = tags.stripIndent`
131-
import { __decorate } from "tslib";
132-
import __NG_CLI_RESOURCE__0 from "./app.component.html";
133-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
134-
import __NG_CLI_RESOURCE__2 from "./app.component.2.css";
135-
import { Component } from '@angular/core';
136-
let AppComponent = class AppComponent {
137-
constructor() {
138-
this.title = 'app';
139-
}
140-
};
141-
AppComponent = __decorate([
142-
Component({
143-
selector: 'app-root',
144-
template: __NG_CLI_RESOURCE__0,
145-
styles: [__NG_CLI_RESOURCE__1, __NG_CLI_RESOURCE__2]
146-
})
147-
], AppComponent);
148-
export { AppComponent };
149-
`;
150-
151-
const result = transform(input, true, false);
104+
const result = transform(input, true, true, ts.ModuleKind.CommonJS);
152105
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
153106
});
154107

@@ -166,7 +119,7 @@ describe('@ngtools/webpack transformers', () => {
166119
`;
167120
const output = tags.stripIndent`
168121
import { __decorate } from "tslib";
169-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.svg";
122+
import __NG_CLI_RESOURCE__0 from "./app.component.svg?ngResource";
170123
import { Component } from '@angular/core';
171124
let AppComponent = class AppComponent {
172125
constructor() {
@@ -202,8 +155,8 @@ describe('@ngtools/webpack transformers', () => {
202155
`;
203156
const output = tags.stripIndent`
204157
import { __decorate } from "tslib";
205-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.html";
206-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
158+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
159+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
207160
import { Component } from '@angular/core';
208161
209162
let AppComponent = class AppComponent {
@@ -240,9 +193,9 @@ describe('@ngtools/webpack transformers', () => {
240193
`;
241194
const output = `
242195
import { __decorate } from "tslib";
243-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.html";
244-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
245-
import __NG_CLI_RESOURCE__2 from "./app.component.2.css";
196+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
197+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
198+
import __NG_CLI_RESOURCE__2 from "./app.component.2.css?ngResource";
246199
247200
import { Component } from '@angular/core';
248201
let AppComponent = class AppComponent {
@@ -279,9 +232,9 @@ describe('@ngtools/webpack transformers', () => {
279232
`;
280233
const output = tags.stripIndent`
281234
import { __decorate } from "tslib";
282-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.html";
283-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
284-
import __NG_CLI_RESOURCE__2 from "./app.component.2.css";
235+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
236+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
237+
import __NG_CLI_RESOURCE__2 from "./app.component.2.css?ngResource";
285238
import { Component as NgComponent } from '@angular/core';
286239
287240
let AppComponent = class AppComponent {
@@ -301,7 +254,7 @@ describe('@ngtools/webpack transformers', () => {
301254

302255
const { program } = createTypescriptContext(input);
303256
const getTypeChecker = () => program.getTypeChecker();
304-
const transformer = replaceResources(() => true, getTypeChecker, true);
257+
const transformer = replaceResources(() => true, getTypeChecker);
305258
const result = transformTypescript(input, [transformer]);
306259

307260
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
@@ -322,9 +275,9 @@ describe('@ngtools/webpack transformers', () => {
322275
`;
323276
const output = tags.stripIndent`
324277
import { __decorate } from "tslib";
325-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.html";
326-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
327-
import __NG_CLI_RESOURCE__2 from "./app.component.2.css";
278+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
279+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
280+
import __NG_CLI_RESOURCE__2 from "./app.component.2.css?ngResource";
328281
329282
import * as ng from '@angular/core';
330283
let AppComponent = class AppComponent {
@@ -367,8 +320,8 @@ describe('@ngtools/webpack transformers', () => {
367320

368321
const output = tags.stripIndent`
369322
import { __decorate } from "tslib";
370-
import __NG_CLI_RESOURCE__0 from "!${DirectAngularResourceLoaderPath}!./app.component.html";
371-
import __NG_CLI_RESOURCE__1 from "./app.component.css";
323+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
324+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
372325
373326
import { Component } from '@angular/core';
374327

0 commit comments

Comments
 (0)