Skip to content

Commit b6951f4

Browse files
committed
feat(@angular/build): add sass to stylePreprocessorOptions in application builder
This commit introduces the functionality to configure a limited number of options for Sass processing in the Angular application builder. The following options have been added to enhance the Sass integration: - **futureDeprecations**: Specifies features that are scheduled for deprecation. The compiler will treat these as active and emit warnings as necessary. - **fatalDeprecations**: Identifies Sass features that are already deprecated and will cause build failures if used. - **silenceDeprecations**: This option suppresses deprecation warnings for specified versions. Usage example: ```json "architect": { "build": { "builder": "@angular-devkit/build-angular:application", "options": { "outputHashing": "none", "namedChunks": true, "stylePreprocessorOptions": { "sass": { "futureDeprecations": ["color-functions"], "fatalDeprecations": ["color-functions"], "silenceDeprecations": ["1.77.0"] } } } } } ``` For more information about these options, please refer to the Sass documentation: https://sass-lang.com/documentation/js-api/interfaces/options/ Closes #28587
1 parent 3db1d81 commit b6951f4

File tree

7 files changed

+145
-2
lines changed

7 files changed

+145
-2
lines changed

packages/angular/build/src/builders/application/schema.json

+28
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,34 @@
125125
"type": "string"
126126
},
127127
"default": []
128+
},
129+
"sass": {
130+
"description": "Options to pass to the sass preprocessor.",
131+
"type": "object",
132+
"properties": {
133+
"fatalDeprecations": {
134+
"description": "A set of deprecations to treat as fatal. If a deprecation warning of any provided type is encountered during compilation, the compiler will error instead. If a Version is provided, then all deprecations that were active in that compiler version will be treated as fatal.",
135+
"type": "array",
136+
"items": {
137+
"type": "string"
138+
}
139+
},
140+
"silenceDeprecations": {
141+
"description": " A set of active deprecations to ignore. If a deprecation warning of any provided type is encountered during compilation, the compiler will ignore it instead.",
142+
"type": "array",
143+
"items": {
144+
"type": "string"
145+
}
146+
},
147+
"futureDeprecations": {
148+
"description": "A set of future deprecations to opt into early. Future deprecations passed here will be treated as active by the compiler, emitting warnings as necessary.",
149+
"type": "array",
150+
"items": {
151+
"type": "string"
152+
}
153+
}
154+
},
155+
"additionalProperties": false
128156
}
129157
},
130158
"additionalProperties": false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { buildApplication } from '../../index';
10+
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
11+
import { logging } from '@angular-devkit/core';
12+
13+
describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
14+
describe('Option: "stylePreprocessorOptions.sass"', () => {
15+
it('should cause the build to fail when using `fatalDeprecations` in global styles', async () => {
16+
await harness.writeFile('src/styles.scss', 'p { color: darken(red, 10%) }');
17+
18+
harness.useTarget('build', {
19+
...BASE_OPTIONS,
20+
styles: ['src/styles.scss'],
21+
stylePreprocessorOptions: {
22+
sass: {
23+
fatalDeprecations: ['color-functions'],
24+
},
25+
},
26+
});
27+
28+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
29+
30+
expect(result?.success).toBeFalse();
31+
expect(logs).not.toContain(
32+
jasmine.objectContaining<logging.LogEntry>({
33+
message: jasmine.stringMatching('darken() is deprecated'),
34+
}),
35+
);
36+
});
37+
38+
it('should succeed without `fatalDeprecations` despite using deprecated color functions', async () => {
39+
await harness.writeFiles({
40+
'src/styles.scss': 'p { color: darken(red, 10%) }',
41+
'src/app/app.component.scss': 'p { color: darken(red, 10%) }',
42+
});
43+
44+
await harness.modifyFile('src/app/app.component.ts', (content) => {
45+
return content.replace('./app.component.css', 'app.component.scss');
46+
});
47+
48+
harness.useTarget('build', {
49+
...BASE_OPTIONS,
50+
styles: ['src/styles.scss'],
51+
stylePreprocessorOptions: {
52+
sass: {},
53+
},
54+
});
55+
56+
const { result } = await harness.executeOnce();
57+
58+
expect(result?.success).toBeTrue();
59+
});
60+
61+
it('should cause the build to fail when using `fatalDeprecations` in component styles', async () => {
62+
await harness.modifyFile('src/app/app.component.ts', (content) => {
63+
return content.replace('./app.component.css', 'app.component.scss');
64+
});
65+
66+
await harness.writeFile('src/app/app.component.scss', 'p { color: darken(red, 10%) }');
67+
68+
harness.useTarget('build', {
69+
...BASE_OPTIONS,
70+
stylePreprocessorOptions: {
71+
sass: {
72+
fatalDeprecations: ['color-functions'],
73+
},
74+
},
75+
});
76+
77+
const { result, logs } = await harness.executeOnce({
78+
outputLogsOnFailure: false,
79+
});
80+
81+
expect(result?.success).toBeFalse();
82+
expect(logs).not.toContain(
83+
jasmine.objectContaining<logging.LogEntry>({
84+
message: jasmine.stringMatching('darken() is deprecated'),
85+
}),
86+
);
87+
});
88+
});
89+
});

packages/angular/build/src/tools/esbuild/compiler-plugin-options.ts

+3
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ export function createCompilerPluginOptions(
6868
sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
6969
outputNames,
7070
includePaths: stylePreprocessorOptions?.includePaths,
71+
// string[] | undefined' is not assignable to type '(Version | DeprecationOrId)[] | undefined'.
72+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
73+
sass: stylePreprocessorOptions?.sass as any,
7174
externalDependencies,
7275
target,
7376
inlineStyleLanguage,

packages/angular/build/src/tools/esbuild/global-styles.ts

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ export function createGlobalStylesBundleOptions(
6363
bundles: '[name]',
6464
},
6565
includePaths: stylePreprocessorOptions?.includePaths,
66+
// string[] | undefined' is not assignable to type '(Version | DeprecationOrId)[] | undefined'.
67+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
68+
sass: stylePreprocessorOptions?.sass as any,
6669
tailwindConfiguration,
6770
postcssConfiguration,
6871
cacheOptions,

packages/angular/build/src/tools/esbuild/stylesheets/bundle-options.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { CssStylesheetLanguage } from './css-language';
1616
import { createCssResourcePlugin } from './css-resource-plugin';
1717
import { LessStylesheetLanguage } from './less-language';
1818
import { SassStylesheetLanguage } from './sass-language';
19-
import { StylesheetPluginFactory } from './stylesheet-plugin-factory';
19+
import { StylesheetPluginFactory, StylesheetPluginsass } from './stylesheet-plugin-factory';
2020

2121
export interface BundleStylesheetOptions {
2222
workspaceRoot: string;
@@ -26,6 +26,7 @@ export interface BundleStylesheetOptions {
2626
sourcemap: boolean | 'external' | 'inline' | 'linked';
2727
outputNames: { bundles: string; media: string };
2828
includePaths?: string[];
29+
sass?: StylesheetPluginsass;
2930
externalDependencies?: string[];
3031
target: string[];
3132
tailwindConfiguration?: { file: string; package: string };
@@ -51,6 +52,7 @@ export function createStylesheetBundleOptions(
5152
inlineComponentData,
5253
tailwindConfiguration: options.tailwindConfiguration,
5354
postcssConfiguration: options.postcssConfiguration,
55+
sass: options.sass,
5456
},
5557
cache,
5658
);

packages/angular/build/src/tools/esbuild/stylesheets/sass-language.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ async function compileString(
9494
// failing resolution attempts.
9595
const resolutionCache = new MemoryCache<URL | null>();
9696
const packageRootCache = new MemoryCache<string | null>();
97-
9897
const warnings: PartialMessage[] = [];
98+
const { silenceDeprecations, futureDeprecations, fatalDeprecations } = options.sass ?? {};
99+
99100
try {
100101
const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
101102
url: pathToFileURL(filePath),
@@ -104,6 +105,9 @@ async function compileString(
104105
loadPaths: options.includePaths,
105106
sourceMap: options.sourcemap,
106107
sourceMapIncludeSources: options.sourcemap,
108+
silenceDeprecations,
109+
fatalDeprecations,
110+
futureDeprecations,
107111
quietDeps: true,
108112
importers: [
109113
{

packages/angular/build/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.ts

+14
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@ import glob from 'fast-glob';
1111
import assert from 'node:assert';
1212
import { readFile } from 'node:fs/promises';
1313
import { extname } from 'node:path';
14+
import type { Options } from 'sass';
1415
import type { PostcssConfiguration } from '../../../utils/postcss-configuration';
1516
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
1617

18+
/**
19+
* Configuration options for handling Sass-specific deprecations in a stylesheet plugin.
20+
*/
21+
export type StylesheetPluginsass = Pick<
22+
Options<'async'>,
23+
'futureDeprecations' | 'fatalDeprecations' | 'silenceDeprecations'
24+
>;
25+
1726
/**
1827
* Convenience type for a postcss processor.
1928
*/
@@ -60,6 +69,11 @@ export interface StylesheetPluginOptions {
6069
* and any tailwind usage must be manually configured in the custom postcss usage.
6170
*/
6271
postcssConfiguration?: PostcssConfiguration;
72+
73+
/**
74+
* Optional Options for configuring Sass behavior.
75+
*/
76+
sass?: StylesheetPluginsass;
6377
}
6478

6579
/**

0 commit comments

Comments
 (0)