Skip to content

Commit 83423ff

Browse files
authored
feat!: prefer sass-embedded over sass by default (#1211)
1 parent 0f3a056 commit 83423ff

File tree

5 files changed

+57
-27
lines changed

5 files changed

+57
-27
lines changed

README.md

+33-13
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,12 @@ This allows you to control the versions of all your dependencies, and to choose
4848

4949
> [!NOTE]
5050
>
51-
> We highly recommend using [Dart Sass](https://github.com/sass/dart-sass).
51+
> We highly recommend using [Sass Embedded](https://github.com/sass/embedded-host-node) or [Dart Sass](https://github.com/sass/dart-sass).
5252
5353
> [!WARNING]
5454
>
5555
> [Node Sass](https://github.com/sass/node-sass) does not work with [Yarn PnP](https://classic.yarnpkg.com/en/docs/pnp/) and doesn't support [@use rule](https://sass-lang.com/documentation/at-rules/use).
5656
57-
> [!WARNING]
58-
>
59-
> [Sass Embedded](https://github.com/sass/embedded-host-node) is experimental and in `beta`, therefore some features may not work
60-
6157
Chain the `sass-loader` with the [css-loader](https://github.com/webpack-contrib/css-loader) and the [style-loader](https://github.com/webpack-contrib/style-loader) to immediately apply all styles to the DOM or the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to extract it into a separate file.
6258

6359
Then add the loader to your webpack configuration. For example:
@@ -164,8 +160,8 @@ Default: `sass`
164160

165161
The special `implementation` option determines which implementation of Sass to use.
166162

167-
By default, the loader resolve the implementation based on your dependencies.
168-
Just add the desired implementation to your `package.json` (`sass` or `node-sass` package) and install dependencies.
163+
By default, the loader resolves the implementation based on your dependencies.
164+
Just add the desired implementation to your `package.json` (`sass`, `sass-embedded`, or `node-sass` package) and install dependencies.
169165

170166
Example where the `sass-loader` loader uses the `sass` (`dart-sass`) implementation:
171167

@@ -193,14 +189,38 @@ Example where the `sass-loader` loader uses the `node-sass` implementation:
193189
}
194190
```
195191

196-
Beware the situation where both `node-sass` and `sass` are installed! By default, the `sass-loader` prefers `sass`.
197-
In order to avoid this situation you can use the `implementation` option.
192+
Example where the `sass-loader` loader uses the `sass-embedded` implementation:
198193

199-
The `implementation` options either accepts `sass` (`Dart Sass`) or `node-sass` as a module.
194+
**package.json**
195+
196+
```json
197+
{
198+
"devDependencies": {
199+
"sass-loader": "^7.2.0",
200+
"sass": "^1.22.10"
201+
},
202+
"optionalDependencies": {
203+
"sass-embedded": "^1.70.0"
204+
}
205+
}
206+
```
207+
208+
> [!NOTE]
209+
>
210+
> Using `optionalDependencies` means that `sass-loader` can fallback to `sass`
211+
> when running on an operating system not supported by `sass-embedded`
212+
213+
Be aware of the order that `sass-loader` will resolve the implementation:
214+
215+
1. `sass-embedded`
216+
2. `sass`
217+
3. `node-sass`
218+
219+
You can specify a specific implementation by using the `implementation` option, which accepts one of the above values.
200220

201221
#### `object`
202222

203-
For example, to use Dart Sass, you'd pass:
223+
For example, to always use Dart Sass, you'd pass:
204224

205225
```js
206226
module.exports = {
@@ -214,7 +234,7 @@ module.exports = {
214234
{
215235
loader: "sass-loader",
216236
options: {
217-
// Prefer `dart-sass`
237+
// Prefer `dart-sass`, even if `sass-embedded` is available
218238
implementation: require("sass"),
219239
},
220240
},
@@ -241,7 +261,7 @@ module.exports = {
241261
{
242262
loader: "sass-loader",
243263
options: {
244-
// Prefer `dart-sass`
264+
// Prefer `dart-sass`, even if `sass-embedded` is available
245265
implementation: require.resolve("sass"),
246266
},
247267
},

src/utils.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ function getDefaultSassImplementation() {
55
let sassImplPkg = "sass";
66

77
try {
8-
require.resolve("sass");
8+
require.resolve("sass-embedded");
9+
sassImplPkg = "sass-embedded";
910
} catch (ignoreError) {
1011
try {
11-
require.resolve("node-sass");
12-
sassImplPkg = "node-sass";
12+
require.resolve("sass");
1313
} catch (_ignoreError) {
1414
try {
15-
require.resolve("sass-embedded");
16-
sassImplPkg = "sass-embedded";
15+
require.resolve("node-sass");
16+
sassImplPkg = "node-sass";
1717
} catch (__ignoreError) {
1818
sassImplPkg = "sass";
1919
}
@@ -676,6 +676,7 @@ function getModernWebpackImporter(loaderContext, implementation, loadPaths) {
676676
}
677677

678678
try {
679+
// eslint-disable-next-line no-shadow
679680
const contents = await new Promise((resolve, reject) => {
680681
// Old version of `enhanced-resolve` supports only path as a string
681682
// TODO simplify in the next major release and pass URL

test/__snapshots__/implementation-option.test.js.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ exports[`implementation option not specify: warnings 1`] = `[]`;
5151
exports[`implementation option should not swallow an error when trying to load a sass implementation: errors 1`] = `
5252
[
5353
"ModuleBuildError: Module build failed (from ../src/cjs.js):
54-
Some error",
54+
Some error sass-embedded",
5555
]
5656
`;
5757

@@ -104,7 +104,7 @@ exports[`implementation option should throw error when unresolved package: warni
104104
exports[`implementation option should try to load using valid order: errors 1`] = `
105105
[
106106
"ModuleBuildError: Module build failed (from ../src/cjs.js):
107-
Some error sass",
107+
Some error sass-embedded",
108108
]
109109
`;
110110

test/implementation-option.test.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,11 @@ describe("implementation option", () => {
182182
expect(getWarnings(stats)).toMatchSnapshot("warnings");
183183
expect(getErrors(stats)).toMatchSnapshot("errors");
184184

185+
expect(sassEmbeddedSpy).toHaveBeenCalledTimes(1);
185186
expect(nodeSassSpy).toHaveBeenCalledTimes(0);
186-
expect(dartSassSpy).toHaveBeenCalledTimes(1);
187+
expect(dartSassSpy).toHaveBeenCalledTimes(0);
187188

189+
sassEmbeddedSpy.mockClear();
188190
nodeSassSpy.mockClear();
189191
dartSassSpy.mockClear();
190192

@@ -206,9 +208,11 @@ describe("implementation option", () => {
206208
expect(getWarnings(stats)).toMatchSnapshot("warnings");
207209
expect(getErrors(stats)).toMatchSnapshot("errors");
208210

211+
expect(sassEmbeddedSpy).toHaveBeenCalledTimes(1);
209212
expect(nodeSassSpy).toHaveBeenCalledTimes(0);
210-
expect(dartSassSpy).toHaveBeenCalledTimes(1);
213+
expect(dartSassSpy).toHaveBeenCalledTimes(0);
211214

215+
sassEmbeddedSpy.mockClear();
212216
nodeSassSpy.mockClear();
213217
dartSassSpy.mockClear();
214218

@@ -230,9 +234,11 @@ describe("implementation option", () => {
230234
expect(getWarnings(stats)).toMatchSnapshot("warnings");
231235
expect(getErrors(stats)).toMatchSnapshot("errors");
232236

237+
expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(1);
233238
expect(nodeSassSpy).toHaveBeenCalledTimes(0);
234-
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(1);
239+
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0);
235240

241+
sassEmbeddedSpyModernAPI.mockClear();
236242
nodeSassSpy.mockClear();
237243
dartSassSpyModernAPI.mockClear();
238244

@@ -254,9 +260,11 @@ describe("implementation option", () => {
254260
expect(getWarnings(stats)).toMatchSnapshot("warnings");
255261
expect(getErrors(stats)).toMatchSnapshot("errors");
256262

263+
expect(sassEmbeddedCompilerSpies.compileStringSpy).toHaveBeenCalledTimes(1);
264+
expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(0);
257265
expect(nodeSassSpy).toHaveBeenCalledTimes(0);
258266
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0);
259-
expect(dartSassCompilerSpies.compileStringSpy).toHaveBeenCalledTimes(1);
267+
expect(dartSassCompilerSpies.compileStringSpy).toHaveBeenCalledTimes(0);
260268

261269
nodeSassSpy.mockClear();
262270
dartSassSpyModernAPI.mockClear();
@@ -265,7 +273,7 @@ describe("implementation option", () => {
265273
await close(compiler);
266274
});
267275

268-
it.each(["dart-sass", "sass-embedded"])(
276+
it.each(["sass-embedded", "dart-sass"])(
269277
"should support switching the implementation within the same process when using the modern-compiler API",
270278
async (implementationName) => {
271279
const testId = getTestId("language", "scss");

test/validate-options.test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ describe("validate options", () => {
99
const tests = {
1010
implementation: {
1111
success: [
12+
// eslint-disable-next-line global-require
13+
require("sass-embedded"),
1214
// eslint-disable-next-line global-require
1315
require("sass"),
1416
// eslint-disable-next-line global-require
1517
require("node-sass"),
16-
// eslint-disable-next-line global-require
17-
require("sass-embedded"),
18+
"sass-embedded",
1819
"sass",
1920
"node-sass",
2021
],

0 commit comments

Comments
 (0)