feat(webpack): use sass-embedded and modern-compiler for sass (#29999)

## Current Behavior
Webpack and Rspack currently use `sass` and its Legacy API with
`sass-loader`.
There is also no method to pass stylePreprocessorOptions other than
`includePaths` to the loaders.


## Expected Behavior
Switch to using `modern-compiler` api to remove deprecation warnings and
improve build performance.
Allow users to choose between `sass` and `sass-embedded` for sass
compiler implementation.

Expand the `stylePreprocesserOptions` interface to accept
`includePaths`, `sassOptions` and `lessOptions` that will be passed to
the appropriate loader.
This commit is contained in:
Colum Ferry 2025-02-24 17:44:19 +00:00 committed by GitHub
parent 75a69d93d0
commit 82169ace03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 704 additions and 240 deletions

View File

@ -5154,7 +5154,7 @@
"file": "generated/packages/rspack/migrations/20.5.0-package-updates.json", "file": "generated/packages/rspack/migrations/20.5.0-package-updates.json",
"hidden": false, "hidden": false,
"name": "20.5.0-package-updates", "name": "20.5.0-package-updates",
"version": "20.5.0-beta.3", "version": "20.5.0-beta.4",
"originalFilePath": "/packages/rspack", "originalFilePath": "/packages/rspack",
"path": "/nx-api/rspack/migrations/20.5.0-package-updates", "path": "/nx-api/rspack/migrations/20.5.0-package-updates",
"type": "migration" "type": "migration"
@ -5955,6 +5955,16 @@
} }
}, },
"migrations": { "migrations": {
"/nx-api/webpack/migrations/20.5.0-package-updates": {
"description": "",
"file": "generated/packages/webpack/migrations/20.5.0-package-updates.json",
"hidden": false,
"name": "20.5.0-package-updates",
"version": "20.5.0-beta.3",
"originalFilePath": "/packages/webpack",
"path": "/nx-api/webpack/migrations/20.5.0-package-updates",
"type": "migration"
},
"/nx-api/webpack/migrations/19.7.0-package-updates": { "/nx-api/webpack/migrations/19.7.0-package-updates": {
"description": "", "description": "",
"file": "generated/packages/webpack/migrations/19.7.0-package-updates.json", "file": "generated/packages/webpack/migrations/19.7.0-package-updates.json",

View File

@ -5122,7 +5122,7 @@
"file": "generated/packages/rspack/migrations/20.5.0-package-updates.json", "file": "generated/packages/rspack/migrations/20.5.0-package-updates.json",
"hidden": false, "hidden": false,
"name": "20.5.0-package-updates", "name": "20.5.0-package-updates",
"version": "20.5.0-beta.3", "version": "20.5.0-beta.4",
"originalFilePath": "/packages/rspack", "originalFilePath": "/packages/rspack",
"path": "rspack/migrations/20.5.0-package-updates", "path": "rspack/migrations/20.5.0-package-updates",
"type": "migration" "type": "migration"
@ -5918,6 +5918,16 @@
} }
], ],
"migrations": [ "migrations": [
{
"description": "",
"file": "generated/packages/webpack/migrations/20.5.0-package-updates.json",
"hidden": false,
"name": "20.5.0-package-updates",
"version": "20.5.0-beta.3",
"originalFilePath": "/packages/webpack",
"path": "webpack/migrations/20.5.0-package-updates",
"type": "migration"
},
{ {
"description": "", "description": "",
"file": "generated/packages/webpack/migrations/19.7.0-package-updates.json", "file": "generated/packages/webpack/migrations/19.7.0-package-updates.json",

View File

@ -286,10 +286,24 @@
"description": "Paths to include. Paths will be resolved to project root.", "description": "Paths to include. Paths will be resolved to project root.",
"type": "array", "type": "array",
"items": { "type": "string" } "items": { "type": "string" }
},
"sassOptions": {
"description": "Options to pass to sass-loader.",
"type": "object"
},
"lessOptions": {
"description": "Options to pass to less-loader.",
"type": "object"
} }
}, },
"additionalProperties": false "additionalProperties": false
}, },
"sassImplementation": {
"type": "string",
"description": "The implementation of the SASS compiler to use. Can be either `sass` or `sass-embedded`. Defaults to `sass-embedded`.",
"enum": ["sass", "sass-embedded"],
"default": "sass"
},
"styles": { "styles": {
"type": "array", "type": "array",
"description": "External Styles which will be included with the application", "description": "External Styles which will be included with the application",

View File

@ -1,7 +1,9 @@
{ {
"name": "20.5.0-package-updates", "name": "20.5.0-package-updates",
"version": "20.5.0-beta.3", "version": "20.5.0-beta.4",
"packages": { "packages": {
"sass-embedded": { "version": "^1.83.4", "alwaysAddToPackageJson": true },
"sass-loader": { "version": "^16.0.4", "alwaysAddToPackageJson": true },
"@rspack/core": { "version": "^1.2.2", "alwaysAddToPackageJson": false } "@rspack/core": { "version": "^1.2.2", "alwaysAddToPackageJson": false }
}, },
"aliases": [], "aliases": [],

View File

@ -222,6 +222,12 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"sassImplementation": {
"type": "string",
"description": "The implementation of the SASS compiler to use. Can be either `sass` or `sass-embedded`. Defaults to `sass-embedded`.",
"enum": ["sass", "sass-embedded"],
"default": "sass"
},
"optimization": { "optimization": {
"description": "Enables optimization of the build output.", "description": "Enables optimization of the build output.",
"oneOf": [ "oneOf": [

View File

@ -0,0 +1,15 @@
{
"name": "20.5.0-package-updates",
"version": "20.5.0-beta.3",
"packages": {
"sass-loader": { "version": "^16.0.4", "alwaysAddToPackageJson": false },
"sass-embedded": { "version": "^1.83.4", "alwaysAddToPackageJson": true }
},
"aliases": [],
"description": "",
"hidden": false,
"implementation": "",
"path": "/packages/webpack",
"schema": null,
"type": "migration"
}

View File

@ -421,13 +421,17 @@ describe('React Applications', () => {
}; };
return config; return config;
}); });
updateFile(
`apps/${appName}/src/base.${style}`,
`html { font-family: "Comic Sans MS"; }`
);
updateFile( updateFile(
`apps/${appName}/src/styles.${style}`, `apps/${appName}/src/styles.${style}`,
`@import 'base.${style}';` `@import 'base.${style}';`
); );
updateFile( updateFile(
`apps/${appName}/src/app/app.module.${style}`, `apps/${appName}/src/app/app.module.${style}`,
(s) => `@import 'base.${style}';\n${s}` (s) => `@import '../base.${style}';\n${s}`
); );
updateFile( updateFile(
`libs/shared/lib/base.${style}`, `libs/shared/lib/base.${style}`,

View File

@ -281,7 +281,8 @@
"rollup-plugin-typescript2": "^0.36.0", "rollup-plugin-typescript2": "^0.36.0",
"rxjs": "^7.8.0", "rxjs": "^7.8.0",
"sass": "1.55.0", "sass": "1.55.0",
"sass-loader": "^12.2.0", "sass-embedded": "^1.83.4",
"sass-loader": "^16.0.4",
"semver": "^7.6.3", "semver": "^7.6.3",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"source-map-support": "0.5.19", "source-map-support": "0.5.19",

View File

@ -50,6 +50,8 @@
"@module-federation/enhanced", "@module-federation/enhanced",
"css-loader", "css-loader",
"webpack", "webpack",
"sass-embedded",
"sass",
"ts-checker-rspack-plugin" "ts-checker-rspack-plugin"
] ]
} }

View File

@ -96,8 +96,16 @@
} }
}, },
"20.5.0": { "20.5.0": {
"version": "20.5.0-beta.3", "version": "20.5.0-beta.4",
"packages": { "packages": {
"sass-embedded": {
"version": "^1.83.4",
"alwaysAddToPackageJson": true
},
"sass-loader": {
"version": "^16.0.4",
"alwaysAddToPackageJson": true
},
"@rspack/core": { "@rspack/core": {
"version": "^1.2.2", "version": "^1.2.2",
"alwaysAddToPackageJson": false "alwaysAddToPackageJson": false

View File

@ -42,8 +42,9 @@
"less-loader": "11.1.0", "less-loader": "11.1.0",
"license-webpack-plugin": "^4.0.2", "license-webpack-plugin": "^4.0.2",
"loader-utils": "^2.0.3", "loader-utils": "^2.0.3",
"sass": "^1.42.1", "sass": "^1.85.0",
"sass-loader": "^12.2.0", "sass-embedded": "^1.83.4",
"sass-loader": "^16.0.4",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"style-loader": "^3.3.0", "style-loader": "^3.3.0",
"picocolors": "^1.1.0", "picocolors": "^1.1.0",

View File

@ -33,6 +33,7 @@ export function normalizeOptions(
styles: options.optimization, styles: options.optimization,
} }
: options.optimization, : options.optimization,
sassImplementation: options.sassImplementation ?? 'sass',
}; };
if (options.assets) { if (options.assets) {
normalizedOptions.assets = normalizeAssets( normalizedOptions.assets = normalizeAssets(

View File

@ -35,7 +35,12 @@ export interface RspackExecutorSchema {
sourceMap?: boolean | DevTool; sourceMap?: boolean | DevTool;
standardRspackConfigFunction?: boolean; standardRspackConfigFunction?: boolean;
statsJson?: boolean; statsJson?: boolean;
stylePreprocessorOptions?: any; stylePreprocessorOptions?: {
includePaths?: string[];
sassOptions?: Record<string, any>;
lessOptions?: Record<string, any>;
};
sassImplementation?: 'sass' | 'sass-embedded';
styles?: Array<ExtraEntryPointClass | string>; styles?: Array<ExtraEntryPointClass | string>;
target?: 'web' | 'node'; target?: 'web' | 'node';
transformers?: TransformerEntry[]; transformers?: TransformerEntry[];

View File

@ -245,10 +245,24 @@
"items": { "items": {
"type": "string" "type": "string"
} }
},
"sassOptions": {
"description": "Options to pass to sass-loader.",
"type": "object"
},
"lessOptions": {
"description": "Options to pass to less-loader.",
"type": "object"
} }
}, },
"additionalProperties": false "additionalProperties": false
}, },
"sassImplementation": {
"type": "string",
"description": "The implementation of the SASS compiler to use. Can be either `sass` or `sass-embedded`. Defaults to `sass-embedded`.",
"enum": ["sass", "sass-embedded"],
"default": "sass"
},
"styles": { "styles": {
"type": "array", "type": "array",
"description": "External Styles which will be included with the application", "description": "External Styles which will be included with the application",

View File

@ -17,6 +17,8 @@ import {
rspackDevServerVersion, rspackDevServerVersion,
rspackPluginMinifyVersion, rspackPluginMinifyVersion,
rspackPluginReactRefreshVersion, rspackPluginReactRefreshVersion,
sassEmbeddedVersion,
sassLoaderVersion,
} from '../../utils/versions'; } from '../../utils/versions';
import { InitGeneratorSchema } from './schema'; import { InitGeneratorSchema } from './schema';
@ -101,6 +103,10 @@ export async function rspackInitGenerator(
if (schema.style === 'less') { if (schema.style === 'less') {
devDependencies['less-loader'] = lessLoaderVersion; devDependencies['less-loader'] = lessLoaderVersion;
} }
if (schema.style === 'scss') {
devDependencies['sass-loader'] = sassLoaderVersion;
devDependencies['sass-embedded'] = sassEmbeddedVersion;
}
if (schema.framework !== 'none' || schema.devServer) { if (schema.framework !== 'none' || schema.devServer) {
devDependencies['@rspack/dev-server'] = rspackDevServerVersion; devDependencies['@rspack/dev-server'] = rspackDevServerVersion;

View File

@ -98,6 +98,8 @@ export function applyWebConfig(
// Determine hashing format. // Determine hashing format.
const hashFormat = getOutputHashFormat(options.outputHashing as string); const hashFormat = getOutputHashFormat(options.outputHashing as string);
const sassOptions = options.stylePreprocessorOptions?.sassOptions;
const lessOptions = options.stylePreprocessorOptions?.lessOptions;
const includePaths: string[] = []; const includePaths: string[] = [];
if (options?.stylePreprocessorOptions?.includePaths?.length > 0) { if (options?.stylePreprocessorOptions?.includePaths?.length > 0) {
options.stylePreprocessorOptions.includePaths.forEach( options.stylePreprocessorOptions.includePaths.forEach(
@ -140,11 +142,16 @@ export function applyWebConfig(
{ {
loader: require.resolve('sass-loader'), loader: require.resolve('sass-loader'),
options: { options: {
implementation: require('sass'), implementation:
options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
api: 'modern-compiler',
sassOptions: { sassOptions: {
fiber: false, fiber: false,
precision: 8, precision: 8,
includePaths, includePaths,
...(sassOptions ?? {}),
}, },
}, },
}, },
@ -160,6 +167,7 @@ export function applyWebConfig(
options: { options: {
lessOptions: { lessOptions: {
paths: includePaths, paths: includePaths,
...(lessOptions ?? {}),
}, },
}, },
}, },
@ -199,13 +207,18 @@ export function applyWebConfig(
{ {
loader: require.resolve('sass-loader'), loader: require.resolve('sass-loader'),
options: { options: {
implementation: require('sass'), api: 'modern-compiler',
implementation:
options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sourceMap: !!options.sourceMap, sourceMap: !!options.sourceMap,
sassOptions: { sassOptions: {
fiber: false, fiber: false,
// bootstrap-sass requires a minimum precision of 8 // bootstrap-sass requires a minimum precision of 8
precision: 8, precision: 8,
includePaths, includePaths,
...(sassOptions ?? {}),
}, },
}, },
}, },
@ -223,6 +236,7 @@ export function applyWebConfig(
lessOptions: { lessOptions: {
javascriptEnabled: true, javascriptEnabled: true,
...lessPathOptions, ...lessPathOptions,
...(lessOptions ?? {}),
}, },
}, },
}, },
@ -263,13 +277,18 @@ export function applyWebConfig(
{ {
loader: require.resolve('sass-loader'), loader: require.resolve('sass-loader'),
options: { options: {
implementation: require('sass'), api: 'modern-compiler',
implementation:
options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sourceMap: !!options.sourceMap, sourceMap: !!options.sourceMap,
sassOptions: { sassOptions: {
fiber: false, fiber: false,
// bootstrap-sass requires a minimum precision of 8 // bootstrap-sass requires a minimum precision of 8
precision: 8, precision: 8,
includePaths, includePaths,
...(sassOptions ?? {}),
}, },
}, },
}, },
@ -287,6 +306,7 @@ export function applyWebConfig(
lessOptions: { lessOptions: {
javascriptEnabled: true, javascriptEnabled: true,
...lessPathOptions, ...lessPathOptions,
...(lessOptions ?? {}),
}, },
}, },
}, },

View File

@ -159,6 +159,12 @@ export interface NxAppRspackPluginOptions {
* Add an additional chunk for the rspack runtime. Defaults to `true` when `target === 'web'`. * Add an additional chunk for the rspack runtime. Defaults to `true` when `target === 'web'`.
*/ */
runtimeChunk?: boolean; runtimeChunk?: boolean;
// TODO(v21): Make Sass Embedded the default in version 21.
// TODO(v22): Remove in version 22.
/**
* The implementation of the SASS compiler to use. Can be either `sass` or `sass-embedded`. Defaults to `sass-embedded`.
*/
sassImplementation?: 'sass' | 'sass-embedded';
/** /**
* External scripts that will be included before the main application entry. * External scripts that will be included before the main application entry.
*/ */
@ -194,7 +200,11 @@ export interface NxAppRspackPluginOptions {
/** /**
* Options for the style preprocessor. e.g. `{ "includePaths": [] }` for SASS. * Options for the style preprocessor. e.g. `{ "includePaths": [] }` for SASS.
*/ */
stylePreprocessorOptions?: any; stylePreprocessorOptions?: {
includePaths?: string[];
sassOptions?: Record<string, any>;
lessOptions?: Record<string, any>;
};
/** /**
* External stylesheets that will be included with the application. * External stylesheets that will be included with the application.
*/ */

View File

@ -120,6 +120,8 @@ export function normalizeOptions(
projectRoot: projectNode.data.root, projectRoot: projectNode.data.root,
root: workspaceRoot, root: workspaceRoot,
runtimeChunk: combinedPluginAndMaybeExecutorOptions.runtimeChunk ?? true, runtimeChunk: combinedPluginAndMaybeExecutorOptions.runtimeChunk ?? true,
sassImplementation:
combinedPluginAndMaybeExecutorOptions.sassImplementation ?? 'sass',
scripts: combinedPluginAndMaybeExecutorOptions.scripts ?? [], scripts: combinedPluginAndMaybeExecutorOptions.scripts ?? [],
sourceMap: combinedPluginAndMaybeExecutorOptions.sourceMap ?? !isProd, sourceMap: combinedPluginAndMaybeExecutorOptions.sourceMap ?? !isProd,
sourceRoot, sourceRoot,

View File

@ -5,6 +5,8 @@ export const rspackDevServerVersion = '1.0.9';
export const rspackPluginMinifyVersion = '^0.7.5'; export const rspackPluginMinifyVersion = '^0.7.5';
export const rspackPluginReactRefreshVersion = '^1.0.0'; export const rspackPluginReactRefreshVersion = '^1.0.0';
export const lessLoaderVersion = '~11.1.3'; export const lessLoaderVersion = '~11.1.3';
export const sassLoaderVersion = '^16.0.4';
export const sassEmbeddedVersion = '^1.83.4';
export const reactRefreshVersion = '~0.14.0'; export const reactRefreshVersion = '~0.14.0';
@ -14,7 +16,6 @@ export const nestjsPlatformExpressVersion = '~9.0.0';
export const nestjsMicroservicesVersion = '~9.0.0'; export const nestjsMicroservicesVersion = '~9.0.0';
export const lessVersion = '4.1.3'; export const lessVersion = '4.1.3';
export const sassVersion = '^1.42.1';
export const stylusVersion = '^0.55.0'; export const stylusVersion = '^0.55.0';
export const eslintPluginImportVersion = '2.27.5'; export const eslintPluginImportVersion = '2.27.5';

View File

@ -10,10 +10,13 @@ export interface WithWebOptions {
generateIndexHtml?: boolean; generateIndexHtml?: boolean;
index?: string; index?: string;
postcssConfig?: string; postcssConfig?: string;
sassImplementation?: 'sass' | 'sass-embedded';
scripts?: Array<ExtraEntryPointClass | string>; scripts?: Array<ExtraEntryPointClass | string>;
styles?: Array<ExtraEntryPointClass | string>; styles?: Array<ExtraEntryPointClass | string>;
stylePreprocessorOptions?: { stylePreprocessorOptions?: {
includePaths?: string[]; includePaths?: string[];
sassOptions?: Record<string, any>;
lessOptions?: Record<string, any>;
}; };
cssModules?: boolean; cssModules?: boolean;
ssr?: boolean; ssr?: boolean;

View File

@ -47,6 +47,8 @@
"less", "less",
"less-loader", "less-loader",
"postcss-loader", "postcss-loader",
"sass",
"sass-embedded",
"sass-loader", "sass-loader",
"style-loader", "style-loader",
"stylus", "stylus",

View File

@ -35,6 +35,19 @@
"alwaysAddToPackageJson": false "alwaysAddToPackageJson": false
} }
} }
},
"20.5.0": {
"version": "20.5.0-beta.3",
"packages": {
"sass-loader": {
"version": "^16.0.4",
"alwaysAddToPackageJson": false
},
"sass-embedded": {
"version": "^1.83.4",
"alwaysAddToPackageJson": true
}
}
} }
} }
} }

View File

@ -51,8 +51,9 @@
"postcss-import": "~14.1.0", "postcss-import": "~14.1.0",
"postcss-loader": "^6.1.1", "postcss-loader": "^6.1.1",
"rxjs": "^7.8.0", "rxjs": "^7.8.0",
"sass": "^1.42.1", "sass": "^1.85.0",
"sass-loader": "^12.2.0", "sass-embedded": "^1.83.4",
"sass-loader": "^16.0.4",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"style-loader": "^3.3.0", "style-loader": "^3.3.0",
"stylus": "^0.64.0", "stylus": "^0.64.0",

View File

@ -26,6 +26,7 @@ export function normalizeOptions(
outputFileName: options.outputFileName ?? 'main.js', outputFileName: options.outputFileName ?? 'main.js',
webpackConfig: normalizePluginPath(options.webpackConfig, root), webpackConfig: normalizePluginPath(options.webpackConfig, root),
fileReplacements: normalizeFileReplacements(root, options.fileReplacements), fileReplacements: normalizeFileReplacements(root, options.fileReplacements),
sassImplementation: options.sassImplementation ?? 'sass',
optimization: optimization:
typeof options.optimization !== 'object' typeof options.optimization !== 'object'
? { ? {

View File

@ -81,6 +81,7 @@ export interface WebpackExecutorOptions {
index?: string; index?: string;
postcssConfig?: string; postcssConfig?: string;
scripts?: Array<ExtraEntryPointClass | string>; scripts?: Array<ExtraEntryPointClass | string>;
sassImplementation?: 'sass' | 'sass-embedded';
stylePreprocessorOptions?: any; stylePreprocessorOptions?: any;
styles?: Array<ExtraEntryPointClass | string>; styles?: Array<ExtraEntryPointClass | string>;
subresourceIntegrity?: boolean; subresourceIntegrity?: boolean;

View File

@ -144,6 +144,12 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"sassImplementation": {
"type": "string",
"description": "The implementation of the SASS compiler to use. Can be either `sass` or `sass-embedded`. Defaults to `sass-embedded`.",
"enum": ["sass", "sass-embedded"],
"default": "sass"
},
"optimization": { "optimization": {
"description": "Enables optimization of the build output.", "description": "Enables optimization of the build output.",
"oneOf": [ "oneOf": [

View File

@ -93,6 +93,8 @@ export function applyWebConfig(
// Determine hashing format. // Determine hashing format.
const hashFormat = getOutputHashFormat(options.outputHashing as string); const hashFormat = getOutputHashFormat(options.outputHashing as string);
const sassOptions = options.stylePreprocessorOptions?.sassOptions;
const lessOptions = options.stylePreprocessorOptions?.lessOptions;
const includePaths: string[] = []; const includePaths: string[] = [];
if (options?.stylePreprocessorOptions?.includePaths?.length > 0) { if (options?.stylePreprocessorOptions?.includePaths?.length > 0) {
options.stylePreprocessorOptions.includePaths.forEach( options.stylePreprocessorOptions.includePaths.forEach(
@ -141,11 +143,16 @@ export function applyWebConfig(
{ {
loader: require.resolve('sass-loader'), loader: require.resolve('sass-loader'),
options: { options: {
implementation: require('sass'), api: 'modern-compiler',
implementation:
options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sassOptions: { sassOptions: {
fiber: false, fiber: false,
precision: 8, precision: 8,
includePaths, includePaths,
...(sassOptions ?? {}),
}, },
}, },
}, },
@ -161,6 +168,7 @@ export function applyWebConfig(
options: { options: {
lessOptions: { lessOptions: {
paths: includePaths, paths: includePaths,
...(lessOptions ?? {}),
}, },
}, },
}, },
@ -200,13 +208,18 @@ export function applyWebConfig(
{ {
loader: require.resolve('sass-loader'), loader: require.resolve('sass-loader'),
options: { options: {
implementation: require('sass'), api: 'modern-compiler',
implementation:
options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sourceMap: !!options.sourceMap, sourceMap: !!options.sourceMap,
sassOptions: { sassOptions: {
fiber: false, fiber: false,
// bootstrap-sass requires a minimum precision of 8 // bootstrap-sass requires a minimum precision of 8
precision: 8, precision: 8,
includePaths, includePaths,
...(sassOptions ?? {}),
}, },
}, },
}, },
@ -224,6 +237,7 @@ export function applyWebConfig(
lessOptions: { lessOptions: {
javascriptEnabled: true, javascriptEnabled: true,
...lessPathOptions, ...lessPathOptions,
...(lessOptions ?? {}),
}, },
}, },
}, },
@ -264,13 +278,18 @@ export function applyWebConfig(
{ {
loader: require.resolve('sass-loader'), loader: require.resolve('sass-loader'),
options: { options: {
implementation: require('sass'), api: 'modern-compiler',
implementation:
options.sassImplementation === 'sass-embedded'
? require.resolve('sass-embedded')
: require.resolve('sass'),
sourceMap: !!options.sourceMap, sourceMap: !!options.sourceMap,
sassOptions: { sassOptions: {
fiber: false, fiber: false,
// bootstrap-sass requires a minimum precision of 8 // bootstrap-sass requires a minimum precision of 8
precision: 8, precision: 8,
includePaths, includePaths,
...(sassOptions ?? {}),
}, },
}, },
}, },
@ -288,6 +307,7 @@ export function applyWebConfig(
lessOptions: { lessOptions: {
javascriptEnabled: true, javascriptEnabled: true,
...lessPathOptions, ...lessPathOptions,
...(lessOptions ?? {}),
}, },
}, },
}, },

View File

@ -128,6 +128,8 @@ export function normalizeOptions(
target: combinedPluginAndMaybeExecutorOptions.target, target: combinedPluginAndMaybeExecutorOptions.target,
targetName, targetName,
vendorChunk: combinedPluginAndMaybeExecutorOptions.vendorChunk ?? !isProd, vendorChunk: combinedPluginAndMaybeExecutorOptions.vendorChunk ?? !isProd,
sassImplementation:
combinedPluginAndMaybeExecutorOptions.sassImplementation ?? 'sass',
}; };
} }

View File

@ -194,7 +194,11 @@ export interface NxAppWebpackPluginOptions {
/** /**
* Options for the style preprocessor. e.g. `{ "includePaths": [] }` for SASS. * Options for the style preprocessor. e.g. `{ "includePaths": [] }` for SASS.
*/ */
stylePreprocessorOptions?: any; stylePreprocessorOptions?: {
includePaths?: string[];
sassOptions?: Record<string, any>;
lessOptions?: Record<string, any>;
};
/** /**
* External stylesheets that will be included with the application. * External stylesheets that will be included with the application.
*/ */
@ -215,6 +219,12 @@ export interface NxAppWebpackPluginOptions {
* Use tsconfig-paths-webpack-plugin to resolve modules using paths in the tsconfig file. * Use tsconfig-paths-webpack-plugin to resolve modules using paths in the tsconfig file.
*/ */
useTsconfigPaths?: boolean; useTsconfigPaths?: boolean;
// TODO(v21): Make Sass Embedded the default in version 21.
// TODO(v22): Remove in version 22.
/**
* The implementation of the SASS compiler to use. Can be either `sass` or `sass-embedded`. Defaults to `sass-embedded`.
*/
sassImplementation?: 'sass' | 'sass-embedded';
/** /**
* Generate a separate vendor chunk for 3rd party packages. * Generate a separate vendor chunk for 3rd party packages.
*/ */

View File

@ -17,8 +17,13 @@ export interface WithWebOptions {
generateIndexHtml?: boolean; generateIndexHtml?: boolean;
index?: string; index?: string;
postcssConfig?: string; postcssConfig?: string;
sassImplementation?: 'sass' | 'sass-embedded';
scripts?: Array<ExtraEntryPointClass | string>; scripts?: Array<ExtraEntryPointClass | string>;
stylePreprocessorOptions?: any; stylePreprocessorOptions?: {
includePaths?: string[];
sassOptions?: Record<string, any>;
lessOptions?: Record<string, any>;
};
styles?: Array<ExtraEntryPointClass | string>; styles?: Array<ExtraEntryPointClass | string>;
subresourceIntegrity?: boolean; subresourceIntegrity?: boolean;
ssr?: boolean; ssr?: boolean;

706
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff