Skip to content

Commit fe3e052

Browse files
committed
perf: embeed assets in css using esbuild instead of postcss
Postcss is now only used when using tailwind
1 parent 84cf578 commit fe3e052

File tree

5 files changed

+54
-57
lines changed

5 files changed

+54
-57
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
# Nested package.json are only needed for development.
1+
# Nested package.json's are only needed for development.
22
**/package.json

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
"ora": "^5.1.0",
5353
"piscina": "^4.2.0",
5454
"postcss": "^8.4.31",
55-
"postcss-url": "^10.1.3",
5655
"rxjs": "^7.8.1",
5756
"sass": "^1.69.5"
5857
},

src/lib/esbuild/esbuild-executor.ts

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

9-
import type { FormatMessagesOptions, PartialMessage, TransformOptions, TransformResult } from 'esbuild';
9+
import type {
10+
BuildOptions,
11+
BuildResult,
12+
FormatMessagesOptions,
13+
PartialMessage,
14+
TransformOptions,
15+
TransformResult,
16+
} from 'esbuild';
1017

1118
/**
1219
* Provides the ability to execute esbuild regardless of the current platform's support
@@ -17,6 +24,7 @@ import type { FormatMessagesOptions, PartialMessage, TransformOptions, Transform
1724
*/
1825
export class EsbuildExecutor implements Pick<typeof import('esbuild'), 'transform' | 'formatMessages'> {
1926
private esbuildTransform: this['transform'];
27+
private esbuildBuild: this['build'];
2028
private esbuildFormatMessages: this['formatMessages'];
2129
private initialized = false;
2230

@@ -27,9 +35,12 @@ export class EsbuildExecutor implements Pick<typeof import('esbuild'), 'transfor
2735
* performed; if false (default), the native variant will be preferred.
2836
*/
2937
constructor(private alwaysUseWasm = false) {
30-
this.esbuildTransform = this.esbuildFormatMessages = () => {
31-
throw new Error('esbuild implementation missing');
32-
};
38+
this.esbuildBuild =
39+
this.esbuildTransform =
40+
this.esbuildFormatMessages =
41+
() => {
42+
throw new Error('esbuild implementation missing');
43+
};
3344
}
3445

3546
/**
@@ -68,10 +79,11 @@ export class EsbuildExecutor implements Pick<typeof import('esbuild'), 'transfor
6879

6980
try {
7081
// Use the faster native variant if available.
71-
const { transform, formatMessages } = await import('esbuild');
82+
const { transform, build, formatMessages } = await import('esbuild');
7283

7384
this.esbuildTransform = transform;
7485
this.esbuildFormatMessages = formatMessages;
86+
this.esbuildBuild = build;
7587
} catch {
7688
// If the native variant is not installed then use the WASM-based variant
7789
await this.useWasm();
@@ -84,9 +96,10 @@ export class EsbuildExecutor implements Pick<typeof import('esbuild'), 'transfor
8496
* Transitions an executor instance to use the WASM-variant of esbuild.
8597
*/
8698
private async useWasm(): Promise<void> {
87-
const { transform, formatMessages } = await import('esbuild-wasm');
99+
const { transform, build, formatMessages } = await import('esbuild-wasm');
88100
this.esbuildTransform = transform;
89101
this.esbuildFormatMessages = formatMessages;
102+
this.esbuildBuild = build;
90103

91104
// The ESBUILD_BINARY_PATH environment variable cannot exist when attempting to use the
92105
// WASM variant. If it is then the binary located at the specified path will be used instead
@@ -102,6 +115,12 @@ export class EsbuildExecutor implements Pick<typeof import('esbuild'), 'transfor
102115
return this.esbuildTransform(input, options);
103116
}
104117

118+
async build(options: BuildOptions): Promise<BuildResult> {
119+
await this.ensureEsbuild();
120+
121+
return this.esbuildBuild(options);
122+
}
123+
105124
async formatMessages(messages: PartialMessage[], options: FormatMessagesOptions): Promise<string[]> {
106125
await this.ensureEsbuild();
107126

src/lib/styles/stylesheet-processor-worker.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { extname, relative } from 'node:path';
1+
import { dirname, extname, relative } from 'node:path';
22
import { pathToFileURL } from 'node:url';
33
import { workerData } from 'node:worker_threads';
44
import postcss from 'postcss';
5-
import postcssUrl from 'postcss-url';
65
import { EsbuildExecutor } from '../esbuild/esbuild-executor';
76
import { generateKey, readCacheEntry, saveCacheEntry } from '../utils/cache';
87
import * as log from '../utils/log';
@@ -68,7 +67,7 @@ async function render({ content, filePath }: RenderRequest): Promise<string> {
6867
}
6968

7069
const warnings: string[] = [];
71-
if (shouldProcessWithPostCSS(renderedCss)) {
70+
if (hasTailwindKeywords(renderedCss)) {
7271
const result = await postCssProcessor.process(renderedCss, {
7372
from: filePath,
7473
to: filePath.replace(extname(filePath), '.css'),
@@ -78,13 +77,35 @@ async function render({ content, filePath }: RenderRequest): Promise<string> {
7877
renderedCss = result.css;
7978
}
8079

81-
const { code, warnings: esBuildWarnings } = await esbuild.transform(renderedCss, {
82-
loader: 'css',
80+
const loader = cssUrl === CssUrl.none ? 'empty' : 'dataurl';
81+
82+
const { outputFiles, warnings: esBuildWarnings } = await esbuild.build({
83+
stdin: {
84+
contents: renderedCss,
85+
loader: 'css',
86+
resolveDir: dirname(filePath),
87+
},
88+
loader: {
89+
'.svg': loader,
90+
'.jpg': loader,
91+
'.jpeg': loader,
92+
'.png': loader,
93+
'.apng': loader,
94+
'.webp': loader,
95+
'.avif': loader,
96+
'.gif': loader,
97+
'.otf': loader,
98+
'.ttf': loader,
99+
'.woff': loader,
100+
'.woff2': loader,
101+
},
102+
write: false,
103+
sourcemap: false,
83104
minify: true,
84105
target: targets,
85-
sourcefile: filePath,
86106
});
87107

108+
const code = outputFiles[0].text;
88109
if (esBuildWarnings.length > 0) {
89110
warnings.push(...(await esbuild.formatMessages(esBuildWarnings, { kind: 'warning' })));
90111
}
@@ -169,10 +190,6 @@ async function initialize() {
169190
cacheDirectory = undefined;
170191
}
171192

172-
if (cssUrl !== CssUrl.none) {
173-
postCssPlugins.push(postcssUrl({ url: cssUrl }));
174-
}
175-
176193
postCssProcessor = postcss(postCssPlugins);
177194

178195
esbuild = new EsbuildExecutor();
@@ -194,10 +211,6 @@ function hasTailwindKeywords(contents: string): boolean {
194211
return TAILWIND_KEYWORDS.some(keyword => contents.includes(keyword));
195212
}
196213

197-
function shouldProcessWithPostCSS(contents: string): boolean {
198-
return (cssUrl !== CssUrl.none && contents.includes('url(')) || hasTailwindKeywords(contents);
199-
}
200-
201214
/**
202215
* The default export will be the promise returned by the initialize function.
203216
* This is awaited by piscina prior to using the Worker.

yarn.lock

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,11 +2662,6 @@ cssesc@^3.0.0:
26622662
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
26632663
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
26642664

2665-
cuint@^0.2.2:
2666-
version "0.2.2"
2667-
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
2668-
integrity sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==
2669-
26702665
d@1, d@^1.0.1:
26712666
version "1.0.1"
26722667
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
@@ -4361,7 +4356,7 @@ make-dir@^2.1.0:
43614356
pify "^4.0.1"
43624357
semver "^5.6.0"
43634358

4364-
make-dir@^3.0.2, make-dir@~3.1.0:
4359+
make-dir@^3.0.2:
43654360
version "3.1.0"
43664361
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
43674362
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -4442,11 +4437,6 @@ mime@^1.4.1:
44424437
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
44434438
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
44444439

4445-
mime@~2.5.2:
4446-
version "2.5.2"
4447-
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
4448-
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
4449-
44504440
mimic-fn@^2.1.0:
44514441
version "2.1.0"
44524442
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@@ -4471,13 +4461,6 @@ minimatch@^9.0.0:
44714461
dependencies:
44724462
brace-expansion "^2.0.1"
44734463

4474-
minimatch@~3.0.4:
4475-
version "3.0.8"
4476-
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
4477-
integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
4478-
dependencies:
4479-
brace-expansion "^1.1.7"
4480-
44814464
minimist-options@4.1.0:
44824465
version "4.1.0"
44834466
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@@ -5004,16 +4987,6 @@ postcss-selector-parser@^6.0.11:
50044987
cssesc "^3.0.0"
50054988
util-deprecate "^1.0.2"
50064989

5007-
postcss-url@^10.1.3:
5008-
version "10.1.3"
5009-
resolved "https://registry.yarnpkg.com/postcss-url/-/postcss-url-10.1.3.tgz#54120cc910309e2475ec05c2cfa8f8a2deafdf1e"
5010-
integrity sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==
5011-
dependencies:
5012-
make-dir "~3.1.0"
5013-
mime "~2.5.2"
5014-
minimatch "~3.0.4"
5015-
xxhashjs "~0.2.2"
5016-
50174990
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
50184991
version "4.2.0"
50194992
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
@@ -5985,13 +5958,6 @@ xtend@~4.0.1:
59855958
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
59865959
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
59875960

5988-
xxhashjs@~0.2.2:
5989-
version "0.2.2"
5990-
resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8"
5991-
integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==
5992-
dependencies:
5993-
cuint "^0.2.2"
5994-
59955961
y18n@^5.0.5:
59965962
version "5.0.8"
59975963
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"

0 commit comments

Comments
 (0)