Skip to content

Commit

Permalink
feat(@angular/build): add sassOptions to stylePreprocessorOptions
Browse files Browse the repository at this point in the history
… 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": {
        "sassOptions": {
          "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 angular#28587
  • Loading branch information
alan-agius4 committed Oct 14, 2024
1 parent 44f8081 commit 46387b2
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 2 deletions.
28 changes: 28 additions & 0 deletions packages/angular/build/src/builders/application/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,34 @@
"type": "string"
},
"default": []
},
"sassOptions": {
"description": "Options to pass to the sass preprocessor.",
"type": "object",
"properties": {
"fatalDeprecations": {
"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.",
"type": "array",
"items": {
"type": "string"
}
},
"silenceDeprecations": {
"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.",
"type": "array",
"items": {
"type": "string"
}
},
"futureDeprecations": {
"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.",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { buildApplication } from '../../index';
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
import { logging } from '@angular-devkit/core';

describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
describe('Option: "sassOptions"', () => {
it('should cause the build to fail when using `fatalDeprecations` in global styles', async () => {
await harness.writeFile('src/styles.scss', 'p { color: darken(red, 10%) }');

harness.useTarget('build', {
...BASE_OPTIONS,
styles: ['src/styles.scss'],
stylePreprocessorOptions: {
sassOptions: {
fatalDeprecations: ['color-functions'],
},
},
});

const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });

expect(result?.success).toBeFalse();
expect(logs).not.toContain(
jasmine.objectContaining<logging.LogEntry>({
message: jasmine.stringMatching('darken() is deprecated'),
}),
);
});

it('should succeed without `fatalDeprecations` despite using deprecated color functions', async () => {
await harness.writeFiles({
'src/styles.scss': 'p { color: darken(red, 10%) }',
'src/app/app.component.scss': 'p { color: darken(red, 10%) }',
});

await harness.modifyFile('src/app/app.component.ts', (content) => {
return content.replace('./app.component.css', 'app.component.scss');
});

harness.useTarget('build', {
...BASE_OPTIONS,
styles: ['src/styles.scss'],
stylePreprocessorOptions: {
sassOptions: {},
},
});

const { result } = await harness.executeOnce();

expect(result?.success).toBeTrue();
});

it('should cause the build to fail when using `fatalDeprecations` in component styles', async () => {
await harness.modifyFile('src/app/app.component.ts', (content) => {
return content.replace('./app.component.css', 'app.component.scss');
});

await harness.writeFile('src/app/app.component.scss', 'p { color: darken(red, 10%) }');

harness.useTarget('build', {
...BASE_OPTIONS,
stylePreprocessorOptions: {
sassOptions: {
fatalDeprecations: ['color-functions'],
},
},
});

const { result, logs } = await harness.executeOnce({
outputLogsOnFailure: false,
});

expect(result?.success).toBeFalse();
expect(logs).not.toContain(
jasmine.objectContaining<logging.LogEntry>({
message: jasmine.stringMatching('darken() is deprecated'),
}),
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export function createCompilerPluginOptions(
sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
outputNames,
includePaths: stylePreprocessorOptions?.includePaths,
// string[] | undefined' is not assignable to type '(Version | DeprecationOrId)[] | undefined'.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sassOptions: stylePreprocessorOptions?.sassOptions as any,
externalDependencies,
target,
inlineStyleLanguage,
Expand Down
3 changes: 3 additions & 0 deletions packages/angular/build/src/tools/esbuild/global-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export function createGlobalStylesBundleOptions(
bundles: '[name]',
},
includePaths: stylePreprocessorOptions?.includePaths,
// string[] | undefined' is not assignable to type '(Version | DeprecationOrId)[] | undefined'.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sassOptions: stylePreprocessorOptions?.sassOptions as any,
tailwindConfiguration,
postcssConfiguration,
cacheOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { CssStylesheetLanguage } from './css-language';
import { createCssResourcePlugin } from './css-resource-plugin';
import { LessStylesheetLanguage } from './less-language';
import { SassStylesheetLanguage } from './sass-language';
import { StylesheetPluginFactory } from './stylesheet-plugin-factory';
import { StylesheetPluginFactory, StylesheetPluginSassOptions } from './stylesheet-plugin-factory';

export interface BundleStylesheetOptions {
workspaceRoot: string;
Expand All @@ -26,6 +26,7 @@ export interface BundleStylesheetOptions {
sourcemap: boolean | 'external' | 'inline' | 'linked';
outputNames: { bundles: string; media: string };
includePaths?: string[];
sassOptions?: StylesheetPluginSassOptions;
externalDependencies?: string[];
target: string[];
tailwindConfiguration?: { file: string; package: string };
Expand All @@ -51,6 +52,7 @@ export function createStylesheetBundleOptions(
inlineComponentData,
tailwindConfiguration: options.tailwindConfiguration,
postcssConfiguration: options.postcssConfiguration,
sassOptions: options.sassOptions,
},
cache,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ async function compileString(
// failing resolution attempts.
const resolutionCache = new MemoryCache<URL | null>();
const packageRootCache = new MemoryCache<string | null>();

const warnings: PartialMessage[] = [];
const { silenceDeprecations, futureDeprecations, fatalDeprecations } = options.sassOptions ?? {};

try {
const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
url: pathToFileURL(filePath),
Expand All @@ -104,6 +105,9 @@ async function compileString(
loadPaths: options.includePaths,
sourceMap: options.sourcemap,
sourceMapIncludeSources: options.sourcemap,
silenceDeprecations,
fatalDeprecations,
futureDeprecations,
quietDeps: true,
importers: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@ import glob from 'fast-glob';
import assert from 'node:assert';
import { readFile } from 'node:fs/promises';
import { extname } from 'node:path';
import type { Options } from 'sass';
import type { PostcssConfiguration } from '../../../utils/postcss-configuration';
import { LoadResultCache, createCachedLoad } from '../load-result-cache';

/**
* Configuration options for handling Sass-specific deprecations in a stylesheet plugin.
*/
export type StylesheetPluginSassOptions = Pick<
Options<'async'>,
'futureDeprecations' | 'fatalDeprecations' | 'silenceDeprecations'
>;

/**
* Convenience type for a postcss processor.
*/
Expand Down Expand Up @@ -60,6 +69,11 @@ export interface StylesheetPluginOptions {
* and any tailwind usage must be manually configured in the custom postcss usage.
*/
postcssConfiguration?: PostcssConfiguration;

/**
* Optional Options for configuring Sass behavior.
*/
sassOptions?: StylesheetPluginSassOptions;
}

/**
Expand Down

0 comments on commit 46387b2

Please sign in to comment.