Skip to content

Commit 34e66ff

Browse files
committed
refactor(@angular-devkit/build-angular): use Webpack provided loader types
Webpack now provides loader function type definitions. These type definitions are now used in custom loaders within the package. This improves type safety and behavior correctness of the loaders when used with Webpack.
1 parent dbbcf5c commit 34e66ff

File tree

3 files changed

+25
-24
lines changed

3 files changed

+25
-24
lines changed

packages/angular_devkit/build_angular/src/extract-i18n/ivy-extract-loader.ts

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

99
import { MessageExtractor } from '@angular/localize/src/tools/src/extract/extraction';
10-
import { getOptions } from 'loader-utils';
1110
import * as nodePath from 'path';
1211

12+
// Extract loader source map parameter type since it is not exported directly
13+
type LoaderSourceMap = Parameters<import('webpack').LoaderDefinitionFunction>[1];
14+
1315
interface LocalizeExtractLoaderOptions {
1416
messageHandler: (messages: import('@angular/localize').ɵParsedMessage[]) => void;
1517
}
1618

1719
export default function localizeExtractLoader(
18-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
19-
this: any,
20+
this: import('webpack').LoaderContext<LocalizeExtractLoaderOptions>,
2021
content: string,
21-
// Source map types are broken in the webpack type definitions
22-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
23-
map: any,
22+
map: LoaderSourceMap,
2423
) {
2524
// eslint-disable-next-line @typescript-eslint/no-this-alias
2625
const loaderContext = this;
27-
28-
// Casts are needed to workaround the loader-utils typings limited support for option values
29-
const options = (getOptions(this) as unknown) as LocalizeExtractLoaderOptions | undefined;
26+
const options = this.getOptions();
3027

3128
// Setup a Webpack-based logger instance
3229
const logger = {
@@ -37,20 +34,22 @@ export default function localizeExtractLoader(
3734
console.debug(...args);
3835
},
3936
info(...args: string[]): void {
40-
loaderContext.emitWarning(args.join(''));
37+
loaderContext.emitWarning(new Error(args.join('')));
4138
},
4239
warn(...args: string[]): void {
43-
loaderContext.emitWarning(args.join(''));
40+
loaderContext.emitWarning(new Error(args.join('')));
4441
},
4542
error(...args: string[]): void {
46-
loaderContext.emitError(args.join(''));
43+
loaderContext.emitError(new Error(args.join('')));
4744
},
4845
};
4946

5047
let filename = loaderContext.resourcePath;
51-
if (map?.file) {
48+
const mapObject =
49+
typeof map === 'string' ? (JSON.parse(map) as Exclude<LoaderSourceMap, string>) : map;
50+
if (mapObject?.file) {
5251
// The extractor's internal sourcemap handling expects the filenames to match
53-
filename = nodePath.join(loaderContext.context, map.file);
52+
filename = nodePath.join(loaderContext.context, mapObject.file);
5453
}
5554

5655
// Setup a virtual file system instance for the extractor

packages/angular_devkit/build_angular/src/webpack/plugins/postcss-cli-resources.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ export interface PostcssCliResourcesOptions {
3232
/** CSS is extracted to a `.css` or is embedded in a `.js` file. */
3333
extracted?: boolean;
3434
filename: (resourcePath: string) => string;
35-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
36-
loader: any;
35+
loader: import('webpack').LoaderContext<unknown>;
3736
emitFile: boolean;
3837
}
3938

@@ -94,20 +93,20 @@ export default function (options?: PostcssCliResourcesOptions): Plugin {
9493
const { pathname, hash, search } = url.parse(inputUrl.replace(/\\/g, '/'));
9594
const resolver = (file: string, base: string) =>
9695
new Promise<string>((resolve, reject) => {
97-
loader.resolve(base, decodeURI(file), (err: Error, result: string) => {
96+
loader.resolve(base, decodeURI(file), (err, result) => {
9897
if (err) {
9998
reject(err);
10099

101100
return;
102101
}
103-
resolve(result);
102+
resolve(result as string);
104103
});
105104
});
106105

107106
const result = await resolve(pathname as string, context, resolver);
108107

109108
return new Promise<string>((resolve, reject) => {
110-
loader.fs.readFile(result, (err: Error, content: Buffer) => {
109+
loader.fs.readFile(result, (err, content) => {
111110
if (err) {
112111
reject(err);
113112

@@ -125,7 +124,8 @@ export default function (options?: PostcssCliResourcesOptions): Plugin {
125124

126125
loader.addDependency(result);
127126
if (emitFile) {
128-
loader.emitFile(outputPath, content, undefined, { sourceFilename: result });
127+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
128+
loader.emitFile(outputPath, content!, undefined, { sourceFilename: result });
129129
}
130130

131131
let outputUrl = outputPath.replace(/\\/g, '/');
@@ -172,7 +172,7 @@ export default function (options?: PostcssCliResourcesOptions): Plugin {
172172
try {
173173
processedUrl = await process(originalUrl, context, resourceCache);
174174
} catch (err) {
175-
loader.emitError(decl.error(err.message, { word: originalUrl }).toString());
175+
loader.emitError(decl.error(err.message, { word: originalUrl }));
176176
continue;
177177
}
178178

packages/angular_devkit/build_angular/src/webpack/plugins/single-test-transform.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import { logging, tags } from '@angular-devkit/core';
10-
import { getOptions } from 'loader-utils';
1110
import { extname } from 'path';
1211

1312
export interface SingleTestTransformLoaderOptions {
@@ -32,8 +31,11 @@ export const SingleTestTransformLoader = __filename;
3231
* array to import them directly, and thus run the tests there.
3332
*/
3433
// eslint-disable-next-line @typescript-eslint/no-explicit-any
35-
export default function loader(this: any, source: string): string {
36-
const { files = [], logger = console } = getOptions(this) as SingleTestTransformLoaderOptions;
34+
export default function loader(
35+
this: import('webpack').LoaderContext<SingleTestTransformLoaderOptions>,
36+
source: string,
37+
): string {
38+
const { files = [], logger = console } = this.getOptions();
3739
// signal the user that expected content is not present.
3840
if (!source.includes('require.context(')) {
3941
logger.error(tags.stripIndent`The 'include' option requires that the 'main' file for tests includes the below line:

0 commit comments

Comments
 (0)