Skip to content

Commit

Permalink
perf(@angular-devkit/build-angular): enable in-memory load result cac…
Browse files Browse the repository at this point in the history
…hing 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`.
  • Loading branch information
clydin authored and alan-agius4 committed May 19, 2023
1 parent ffea33f commit 8336ad8
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,24 @@ export function createStylesheetBundleOptions(
},
cache,
),
createLessPlugin({
sourcemap: !!options.sourcemap,
includePaths,
inlineComponentData,
}),
createCssPlugin({
sourcemap: !!options.sourcemap,
inlineComponentData,
browsers: options.browsers,
tailwindConfiguration: options.tailwindConfiguration,
}),
createCssResourcePlugin(),
createLessPlugin(
{
sourcemap: !!options.sourcemap,
includePaths,
inlineComponentData,
},
cache,
),
createCssPlugin(
{
sourcemap: !!options.sourcemap,
inlineComponentData,
browsers: options.browsers,
tailwindConfiguration: options.tailwindConfiguration,
},
cache,
),
createCssResourcePlugin(cache),
],
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import createAutoPrefixerPlugin from 'autoprefixer';
import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
import assert from 'node:assert';
import { readFile } from 'node:fs/promises';
import { LoadResultCache, createCachedLoad } from '../load-result-cache';

/**
* The lazy-loaded instance of the postcss stylesheet postprocessor.
Expand Down Expand Up @@ -46,7 +47,7 @@ export interface CssPluginOptions {
* @param options An object containing the plugin options.
* @returns An esbuild Plugin instance.
*/
export function createCssPlugin(options: CssPluginOptions): Plugin {
export function createCssPlugin(options: CssPluginOptions, cache?: LoadResultCache): Plugin {
return {
name: 'angular-css',
async setup(build: PluginBuild): Promise<void> {
Expand Down Expand Up @@ -78,24 +79,30 @@ export function createCssPlugin(options: CssPluginOptions): Plugin {
}

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

const [, , filePath] = args.path.split(';', 3);
const [, , filePath] = args.path.split(';', 3);

return compileString(data, filePath, postcssProcessor, options);
});
return compileString(data, filePath, postcssProcessor, options);
}),
);

// Add a load callback to support files from disk
build.onLoad({ filter: /\.css$/ }, async (args) => {
const data = await readFile(args.path, 'utf-8');

return compileString(data, args.path, postcssProcessor, options);
});
build.onLoad(
{ filter: /\.css$/ },
createCachedLoad(cache, async (args) => {
const data = await readFile(args.path, 'utf-8');

return compileString(data, args.path, postcssProcessor, options);
}),
);
},
};
}
Expand Down Expand Up @@ -157,6 +164,7 @@ async function compileString(
contents: result.css,
loader: 'css',
warnings,
watchFiles: [filename],
};
} catch (error) {
postcss ??= (await import('postcss')).default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { Plugin, PluginBuild } from 'esbuild';
import { readFile } from 'node:fs/promises';
import { join, relative } from 'node:path';
import { LoadResultCache, createCachedLoad } from '../load-result-cache';

/**
* Symbol marker used to indicate CSS resource resolution is being attempted.
Expand All @@ -24,7 +25,7 @@ const CSS_RESOURCE_RESOLUTION = Symbol('CSS_RESOURCE_RESOLUTION');
*
* @returns An esbuild {@link Plugin} instance.
*/
export function createCssResourcePlugin(): Plugin {
export function createCssResourcePlugin(cache?: LoadResultCache): Plugin {
return {
name: 'angular-css-resource',
setup(build: PluginBuild): void {
Expand Down Expand Up @@ -80,12 +81,18 @@ export function createCssResourcePlugin(): Plugin {
};
});

build.onLoad({ filter: /.*/, namespace: 'css-resource' }, async (args) => {
return {
contents: await readFile(join(build.initialOptions.absWorkingDir ?? '', args.path)),
loader: 'file',
};
});
build.onLoad(
{ filter: /./, namespace: 'css-resource' },
createCachedLoad(cache, async (args) => {
const resourcePath = join(build.initialOptions.absWorkingDir ?? '', args.path);

return {
contents: await readFile(resourcePath),
loader: 'file',
watchFiles: [resourcePath],
};
}),
);
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
import assert from 'node:assert';
import { readFile } from 'node:fs/promises';
import { LoadResultCache, createCachedLoad } from '../load-result-cache';

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

export function createLessPlugin(options: LessPluginOptions): Plugin {
export function createLessPlugin(options: LessPluginOptions, cache?: LoadResultCache): Plugin {
return {
name: 'angular-less',
setup(build: PluginBuild): void {
// Add a load callback to support inline Component styles
build.onLoad({ filter: /^less;/, namespace: 'angular:styles/component' }, async (args) => {
const data = options.inlineComponentData?.[args.path];
assert(
typeof data === 'string',
`component style name should always be found [${args.path}]`,
);
build.onLoad(
{ filter: /^less;/, namespace: 'angular:styles/component' },
createCachedLoad(cache, async (args) => {
const data = options.inlineComponentData?.[args.path];
assert(
typeof data === 'string',
`component style name should always be found [${args.path}]`,
);

const [, , filePath] = args.path.split(';', 3);
const [, , filePath] = args.path.split(';', 3);

return compileString(data, filePath, options, build.resolve.bind(build));
});
return compileString(data, filePath, options, build.resolve.bind(build));
}),
);

// Add a load callback to support files from disk
build.onLoad({ filter: /\.less$/ }, async (args) => {
const data = await readFile(args.path, 'utf-8');

return compileString(data, args.path, options, build.resolve.bind(build));
});
build.onLoad(
{ filter: /\.less$/ },
createCachedLoad(cache, async (args) => {
const data = await readFile(args.path, 'utf-8');

return compileString(data, args.path, options, build.resolve.bind(build));
}),
);
},
};
}
Expand Down Expand Up @@ -127,6 +134,7 @@ async function compileString(
return {
contents: result.css,
loader: 'css',
watchFiles: [filename, ...result.imports],
};
} catch (error) {
if (isLessException(error)) {
Expand Down

0 comments on commit 8336ad8

Please sign in to comment.