Skip to content

Commit 2ae5328

Browse files
committed
feat(@angular/build): add dataurl, base64 loaders
This passes through the existing esbuild functionality to support dataurl and base64 loaders, in addition to the pre-existing text, binary, and file loaders. Both the dataurl and base64 loaders return strings with the relevant content. There is no way to control whether the dataurl method uses base64 or plain text encoding, and no way to control the mime type it detects. Fixes #30391
1 parent 0574076 commit 2ae5328

File tree

6 files changed

+91
-7
lines changed

6 files changed

+91
-7
lines changed

packages/angular/build/src/builders/application/options.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,13 @@ export async function normalizeOptions(
206206
}
207207
}
208208

209-
let loaderExtensions: Record<string, 'text' | 'binary' | 'file'> | undefined;
209+
let loaderExtensions: Record<string, 'text' | 'binary' | 'file' | 'dataurl' | 'base64'> | undefined;
210210
if (options.loader) {
211211
for (const [extension, value] of Object.entries(options.loader)) {
212212
if (extension[0] !== '.' || /\.[cm]?[jt]sx?$/.test(extension)) {
213213
continue;
214214
}
215-
if (value !== 'text' && value !== 'binary' && value !== 'file' && value !== 'empty') {
215+
if (value !== 'text' && value !== 'binary' && value !== 'file' && value !== 'dataurl' && value !== 'base64' && value !== 'empty') {
216216
continue;
217217
}
218218
loaderExtensions ??= {};

packages/angular/build/src/builders/application/schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,10 @@
279279
]
280280
},
281281
"loader": {
282-
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles.",
282+
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `dataurl` inlines the content as a data URL with best guess of MIME type; `base64` inlines the content as a Base64-encoded string; `empty` considers the content to be empty and not include it in bundles.",
283283
"type": "object",
284284
"patternProperties": {
285-
"^\\.\\S+$": { "enum": ["text", "binary", "file", "empty"] }
285+
"^\\.\\S+$": { "enum": ["text", "binary", "file", "dataurl", "base64", "empty"] }
286286
}
287287
},
288288
"define": {

packages/angular/build/src/builders/application/tests/behavior/loader-import-attribute_spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,41 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
5151
harness.expectFile('dist/browser/main.js').content.not.toContain('ABC');
5252
});
5353

54+
it('should inline base64 content for file extension set to "base64"', async () => {
55+
harness.useTarget('build', {
56+
...BASE_OPTIONS,
57+
});
58+
59+
await harness.writeFile('./src/a.unknown', 'ABC');
60+
await harness.writeFile(
61+
'src/main.ts',
62+
'// @ts-expect-error\nimport contents from "./a.unknown" with { loader: "base64" };\n console.log(contents);',
63+
);
64+
65+
const { result } = await harness.executeOnce();
66+
expect(result?.success).toBe(true);
67+
// Should contain the binary encoding used esbuild and not the text content
68+
harness.expectFile('dist/browser/main.js').content.toContain('QUJD');
69+
harness.expectFile('dist/browser/main.js').content.not.toContain('ABC');
70+
});
71+
72+
it('should inline dataurl content for file extension set to "dataurl"', async () => {
73+
harness.useTarget('build', {
74+
...BASE_OPTIONS,
75+
});
76+
77+
await harness.writeFile('./src/a.svg', 'ABC');
78+
await harness.writeFile(
79+
'src/main.ts',
80+
'// @ts-expect-error\nimport contents from "./a.svg" with { loader: "dataurl" };\n console.log(contents);',
81+
);
82+
83+
const { result } = await harness.executeOnce();
84+
expect(result?.success).toBe(true);
85+
// Should contain the binary encoding used esbuild and not the text content
86+
harness.expectFile('dist/browser/main.js').content.toContain('data:image/svg+xml,ABC');
87+
});
88+
5489
it('should emit an output file for loader attribute set to "file"', async () => {
5590
harness.useTarget('build', {
5691
...BASE_OPTIONS,

packages/angular/build/src/builders/application/tests/options/loader_spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,55 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
108108
harness.expectFile('dist/browser/main.js').content.not.toContain('ABC');
109109
});
110110

111+
it('should inline binary content for file extension set to "base64"', async () => {
112+
harness.useTarget('build', {
113+
...BASE_OPTIONS,
114+
loader: {
115+
'.unknown': 'base64',
116+
},
117+
});
118+
119+
await harness.writeFile(
120+
'./src/types.d.ts',
121+
'declare module "*.unknown" { const content: string; export default content; }',
122+
);
123+
await harness.writeFile('./src/a.unknown', 'ABC');
124+
await harness.writeFile(
125+
'src/main.ts',
126+
'import contents from "./a.unknown";\n console.log(contents);',
127+
);
128+
129+
const { result } = await harness.executeOnce();
130+
expect(result?.success).toBe(true);
131+
// Should contain the binary encoding used esbuild and not the text content
132+
harness.expectFile('dist/browser/main.js').content.toContain('QUJD');
133+
harness.expectFile('dist/browser/main.js').content.not.toContain('ABC');
134+
});
135+
136+
it('should inline binary content for file extension set to "dataurl"', async () => {
137+
harness.useTarget('build', {
138+
...BASE_OPTIONS,
139+
loader: {
140+
'.svg': 'dataurl',
141+
},
142+
});
143+
144+
await harness.writeFile(
145+
'./src/types.d.ts',
146+
'declare module "*.svg" { const content: string; export default content; }',
147+
);
148+
await harness.writeFile('./src/a.svg', 'ABC');
149+
await harness.writeFile(
150+
'src/main.ts',
151+
'import contents from "./a.svg";\n console.log(contents);',
152+
);
153+
154+
const { result } = await harness.executeOnce();
155+
expect(result?.success).toBe(true);
156+
// Should contain the binary encoding used esbuild and not the text content
157+
harness.expectFile('dist/browser/main.js').content.toContain('data:image/svg+xml,ABC');
158+
});
159+
111160
it('should emit an output file for file extension set to "file"', async () => {
112161
harness.useTarget('build', {
113162
...BASE_OPTIONS,

packages/angular/build/src/builders/karma/schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@
163163
"default": []
164164
},
165165
"loader": {
166-
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `empty` considers the content to be empty and not include it in bundles.",
166+
"description": "Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `dataurl` inlines the content as a data URL with best guess of MIME type; `base64` inlines the content as a Base64-encoded string; `empty` considers the content to be empty and not include it in bundles.",
167167
"type": "object",
168168
"patternProperties": {
169-
"^\\.\\S+$": { "enum": ["text", "binary", "file", "empty"] }
169+
"^\\.\\S+$": { "enum": ["text", "binary", "file", "dataurl", "base64", "empty"] }
170170
}
171171
},
172172
"define": {

packages/angular/build/src/tools/esbuild/loader-import-attribute-plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import type { Loader, Plugin } from 'esbuild';
1010
import { readFile } from 'node:fs/promises';
1111

12-
const SUPPORTED_LOADERS: Loader[] = ['binary', 'file', 'text'];
12+
const SUPPORTED_LOADERS: Loader[] = ['base64', 'binary', 'dataurl', 'file', 'text'];
1313

1414
export function createLoaderImportAttributePlugin(): Plugin {
1515
return {

0 commit comments

Comments
 (0)