Skip to content

Commit 8336ad8

Browse files
clydinalan-agius4
authored andcommitted
perf(@angular-devkit/build-angular): enable in-memory load result caching for stylesheets in esbuild builder
The stylesheet related plugins for the esbuild-based browser application builder will now cache intermediate load results when in watch mode. This reduces the potential amount of processing needed during a rebuild for both `ng build --watch` and `ng serve`.
1 parent ffea33f commit 8336ad8

File tree

4 files changed

+78
-49
lines changed

4 files changed

+78
-49
lines changed

packages/angular_devkit/build_angular/src/builders/browser-esbuild/stylesheets/bundle-options.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,24 @@ export function createStylesheetBundleOptions(
7070
},
7171
cache,
7272
),
73-
createLessPlugin({
74-
sourcemap: !!options.sourcemap,
75-
includePaths,
76-
inlineComponentData,
77-
}),
78-
createCssPlugin({
79-
sourcemap: !!options.sourcemap,
80-
inlineComponentData,
81-
browsers: options.browsers,
82-
tailwindConfiguration: options.tailwindConfiguration,
83-
}),
84-
createCssResourcePlugin(),
73+
createLessPlugin(
74+
{
75+
sourcemap: !!options.sourcemap,
76+
includePaths,
77+
inlineComponentData,
78+
},
79+
cache,
80+
),
81+
createCssPlugin(
82+
{
83+
sourcemap: !!options.sourcemap,
84+
inlineComponentData,
85+
browsers: options.browsers,
86+
tailwindConfiguration: options.tailwindConfiguration,
87+
},
88+
cache,
89+
),
90+
createCssResourcePlugin(cache),
8591
],
8692
};
8793
}

packages/angular_devkit/build_angular/src/builders/browser-esbuild/stylesheets/css-plugin.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import createAutoPrefixerPlugin from 'autoprefixer';
1010
import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
1111
import assert from 'node:assert';
1212
import { readFile } from 'node:fs/promises';
13+
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
1314

1415
/**
1516
* The lazy-loaded instance of the postcss stylesheet postprocessor.
@@ -46,7 +47,7 @@ export interface CssPluginOptions {
4647
* @param options An object containing the plugin options.
4748
* @returns An esbuild Plugin instance.
4849
*/
49-
export function createCssPlugin(options: CssPluginOptions): Plugin {
50+
export function createCssPlugin(options: CssPluginOptions, cache?: LoadResultCache): Plugin {
5051
return {
5152
name: 'angular-css',
5253
async setup(build: PluginBuild): Promise<void> {
@@ -78,24 +79,30 @@ export function createCssPlugin(options: CssPluginOptions): Plugin {
7879
}
7980

8081
// Add a load callback to support inline Component styles
81-
build.onLoad({ filter: /^css;/, namespace: 'angular:styles/component' }, async (args) => {
82-
const data = options.inlineComponentData?.[args.path];
83-
assert(
84-
typeof data === 'string',
85-
`component style name should always be found [${args.path}]`,
86-
);
82+
build.onLoad(
83+
{ filter: /^css;/, namespace: 'angular:styles/component' },
84+
createCachedLoad(cache, async (args) => {
85+
const data = options.inlineComponentData?.[args.path];
86+
assert(
87+
typeof data === 'string',
88+
`component style name should always be found [${args.path}]`,
89+
);
8790

88-
const [, , filePath] = args.path.split(';', 3);
91+
const [, , filePath] = args.path.split(';', 3);
8992

90-
return compileString(data, filePath, postcssProcessor, options);
91-
});
93+
return compileString(data, filePath, postcssProcessor, options);
94+
}),
95+
);
9296

9397
// Add a load callback to support files from disk
94-
build.onLoad({ filter: /\.css$/ }, async (args) => {
95-
const data = await readFile(args.path, 'utf-8');
96-
97-
return compileString(data, args.path, postcssProcessor, options);
98-
});
98+
build.onLoad(
99+
{ filter: /\.css$/ },
100+
createCachedLoad(cache, async (args) => {
101+
const data = await readFile(args.path, 'utf-8');
102+
103+
return compileString(data, args.path, postcssProcessor, options);
104+
}),
105+
);
99106
},
100107
};
101108
}
@@ -157,6 +164,7 @@ async function compileString(
157164
contents: result.css,
158165
loader: 'css',
159166
warnings,
167+
watchFiles: [filename],
160168
};
161169
} catch (error) {
162170
postcss ??= (await import('postcss')).default;

packages/angular_devkit/build_angular/src/builders/browser-esbuild/stylesheets/css-resource-plugin.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import type { Plugin, PluginBuild } from 'esbuild';
1010
import { readFile } from 'node:fs/promises';
1111
import { join, relative } from 'node:path';
12+
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
1213

1314
/**
1415
* Symbol marker used to indicate CSS resource resolution is being attempted.
@@ -24,7 +25,7 @@ const CSS_RESOURCE_RESOLUTION = Symbol('CSS_RESOURCE_RESOLUTION');
2425
*
2526
* @returns An esbuild {@link Plugin} instance.
2627
*/
27-
export function createCssResourcePlugin(): Plugin {
28+
export function createCssResourcePlugin(cache?: LoadResultCache): Plugin {
2829
return {
2930
name: 'angular-css-resource',
3031
setup(build: PluginBuild): void {
@@ -80,12 +81,18 @@ export function createCssResourcePlugin(): Plugin {
8081
};
8182
});
8283

83-
build.onLoad({ filter: /.*/, namespace: 'css-resource' }, async (args) => {
84-
return {
85-
contents: await readFile(join(build.initialOptions.absWorkingDir ?? '', args.path)),
86-
loader: 'file',
87-
};
88-
});
84+
build.onLoad(
85+
{ filter: /./, namespace: 'css-resource' },
86+
createCachedLoad(cache, async (args) => {
87+
const resourcePath = join(build.initialOptions.absWorkingDir ?? '', args.path);
88+
89+
return {
90+
contents: await readFile(resourcePath),
91+
loader: 'file',
92+
watchFiles: [resourcePath],
93+
};
94+
}),
95+
);
8996
},
9097
};
9198
}

packages/angular_devkit/build_angular/src/builders/browser-esbuild/stylesheets/less-plugin.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
1010
import assert from 'node:assert';
1111
import { readFile } from 'node:fs/promises';
12+
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
1213

1314
/**
1415
* The lazy-loaded instance of the less stylesheet preprocessor.
@@ -33,29 +34,35 @@ function isLessException(error: unknown): error is LessException {
3334
return !!error && typeof error === 'object' && 'column' in error;
3435
}
3536

36-
export function createLessPlugin(options: LessPluginOptions): Plugin {
37+
export function createLessPlugin(options: LessPluginOptions, cache?: LoadResultCache): Plugin {
3738
return {
3839
name: 'angular-less',
3940
setup(build: PluginBuild): void {
4041
// Add a load callback to support inline Component styles
41-
build.onLoad({ filter: /^less;/, namespace: 'angular:styles/component' }, async (args) => {
42-
const data = options.inlineComponentData?.[args.path];
43-
assert(
44-
typeof data === 'string',
45-
`component style name should always be found [${args.path}]`,
46-
);
42+
build.onLoad(
43+
{ filter: /^less;/, namespace: 'angular:styles/component' },
44+
createCachedLoad(cache, async (args) => {
45+
const data = options.inlineComponentData?.[args.path];
46+
assert(
47+
typeof data === 'string',
48+
`component style name should always be found [${args.path}]`,
49+
);
4750

48-
const [, , filePath] = args.path.split(';', 3);
51+
const [, , filePath] = args.path.split(';', 3);
4952

50-
return compileString(data, filePath, options, build.resolve.bind(build));
51-
});
53+
return compileString(data, filePath, options, build.resolve.bind(build));
54+
}),
55+
);
5256

5357
// Add a load callback to support files from disk
54-
build.onLoad({ filter: /\.less$/ }, async (args) => {
55-
const data = await readFile(args.path, 'utf-8');
56-
57-
return compileString(data, args.path, options, build.resolve.bind(build));
58-
});
58+
build.onLoad(
59+
{ filter: /\.less$/ },
60+
createCachedLoad(cache, async (args) => {
61+
const data = await readFile(args.path, 'utf-8');
62+
63+
return compileString(data, args.path, options, build.resolve.bind(build));
64+
}),
65+
);
5966
},
6067
};
6168
}
@@ -127,6 +134,7 @@ async function compileString(
127134
return {
128135
contents: result.css,
129136
loader: 'css',
137+
watchFiles: [filename, ...result.imports],
130138
};
131139
} catch (error) {
132140
if (isLessException(error)) {

0 commit comments

Comments
 (0)