Skip to content

Commit 8f305ef

Browse files
imirkindgp1130
authored andcommitted
feat(@angular/build): add dataurl, base64 loaders
Pass 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 7ca250a commit 8f305ef

File tree

6 files changed

+100
-7
lines changed

6 files changed

+100
-7
lines changed

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

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

209-
let loaderExtensions: Record<string, 'text' | 'binary' | 'file'> | undefined;
209+
let loaderExtensions:
210+
| Record<string, 'text' | 'binary' | 'file' | 'dataurl' | 'base64'>
211+
| undefined;
210212
if (options.loader) {
211213
for (const [extension, value] of Object.entries(options.loader)) {
212214
if (extension[0] !== '.' || /\.[cm]?[jt]sx?$/.test(extension)) {
213215
continue;
214216
}
215-
if (value !== 'text' && value !== 'binary' && value !== 'file' && value !== 'empty') {
217+
if (
218+
value !== 'text' &&
219+
value !== 'binary' &&
220+
value !== 'file' &&
221+
value !== 'dataurl' &&
222+
value !== 'base64' &&
223+
value !== 'empty'
224+
) {
216225
continue;
217226
}
218227
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 base64 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 dataurl 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 base64 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 base64 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 dataurl 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 dataurl 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)