Skip to content

Commit

Permalink
feat(@angular-devkit/build-angular): switch to Sass modern API in esb…
Browse files Browse the repository at this point in the history
…uild builder

With this change we replace Sass legacy with the modern API in the experimental esbuilder. The goal is that in the next major version this change is propagated to the Webpack builder.

Based on the benchmarks that we did Sass modern API is faster compared to the legacy version.
  • Loading branch information
alan-agius4 authored and dgp1130 committed Aug 5, 2022
1 parent 4d2f2bd commit 3fb569b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 42 deletions.
Expand Up @@ -6,54 +6,74 @@
* found in the LICENSE file at https://angular.io/license
*/

import type { Plugin, PluginBuild } from 'esbuild';
import type { LegacyResult } from 'sass';
import { SassWorkerImplementation } from '../../sass/sass-service';
import type { PartialMessage, Plugin, PluginBuild } from 'esbuild';
import type { CompileResult } from 'sass';
import { fileURLToPath } from 'url';

export function createSassPlugin(options: { sourcemap: boolean; includePaths?: string[] }): Plugin {
export function createSassPlugin(options: { sourcemap: boolean; loadPaths?: string[] }): Plugin {
return {
name: 'angular-sass',
setup(build: PluginBuild): void {
let sass: SassWorkerImplementation;
let sass: typeof import('sass');

build.onStart(() => {
sass = new SassWorkerImplementation();
build.onStart(async () => {
// Lazily load Sass
sass = await import('sass');
});

build.onEnd(() => {
sass?.close();
});

build.onLoad({ filter: /\.s[ac]ss$/ }, async (args) => {
const result = await new Promise<LegacyResult>((resolve, reject) => {
sass.render(
{
file: args.path,
includePaths: options.includePaths,
indentedSyntax: args.path.endsWith('.sass'),
outputStyle: 'expanded',
sourceMap: options.sourcemap,
sourceMapContents: options.sourcemap,
sourceMapEmbed: options.sourcemap,
quietDeps: true,
},
(error, result) => {
if (error) {
reject(error);
}
if (result) {
resolve(result);
}
build.onLoad({ filter: /\.s[ac]ss$/ }, (args) => {
try {
const warnings: PartialMessage[] = [];
// Use sync version as async version is slower.
const { css, sourceMap, loadedUrls } = sass.compile(args.path, {
style: 'expanded',
loadPaths: options.loadPaths,
sourceMap: options.sourcemap,
sourceMapIncludeSources: options.sourcemap,
quietDeps: true,
logger: {
warn: (text, _options) => {
warnings.push({
text,
});
},
},
);
});

return {
contents: result.css,
loader: 'css',
watchFiles: result.stats.includedFiles,
};
});

return {
loader: 'css',
contents: css + sourceMapToUrlComment(sourceMap),
watchFiles: loadedUrls.map((url) => fileURLToPath(url)),
warnings,
};
} catch (error) {
if (error instanceof sass.Exception) {
const file = error.span.url ? fileURLToPath(error.span.url) : undefined;

return {
loader: 'css',
errors: [
{
text: error.toString(),
},
],
watchFiles: file ? [file] : undefined,
};
}

throw error;
}
});
},
};
}

function sourceMapToUrlComment(sourceMap: CompileResult['sourceMap']): string {
if (!sourceMap) {
return '';
}

const urlSourceMap = Buffer.from(JSON.stringify(sourceMap), 'utf-8').toString('base64');

return `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${urlSourceMap}`;
}
Expand Up @@ -24,6 +24,10 @@ async function bundleStylesheet(
entry: Required<Pick<BuildOptions, 'stdin'> | Pick<BuildOptions, 'entryPoints'>>,
options: BundleStylesheetOptions,
) {
const loadPaths = options.includePaths ?? [];
// Needed to resolve node packages.
loadPaths.push(path.join(options.workspaceRoot, 'node_modules'));

// Execute esbuild
const result = await bundle({
...entry,
Expand All @@ -40,9 +44,7 @@ async function bundleStylesheet(
preserveSymlinks: options.preserveSymlinks,
conditions: ['style', 'sass'],
mainFields: ['style', 'sass'],
plugins: [
createSassPlugin({ sourcemap: !!options.sourcemap, includePaths: options.includePaths }),
],
plugins: [createSassPlugin({ sourcemap: !!options.sourcemap, loadPaths })],
});

// Extract the result of the bundling from the output files
Expand Down

0 comments on commit 3fb569b

Please sign in to comment.