Skip to content

Commit c7ca4ee

Browse files
feat!: use modern Sass JS API by default for sass and sass-embedded (#1220)
1 parent 57eee6b commit c7ca4ee

6 files changed

+75
-22
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ Type:
683683
type api = "legacy" | "modern" | "modern-compiler";
684684
```
685685

686-
Default: `"legacy"`
686+
Default: `"modern"` for `sass` (`dart-sass`) and `sass-embedded` or `"legacy"` for `node-sass`
687687

688688
Allows you to switch between the `legacy` and `modern` APIs. You can find more information [here](https://sass-lang.com/documentation/js-api). The `modern-compiler` option enables the modern API with support for [Shared Resources](https://github.com/sass/sass/blob/main/accepted/shared-resources.d.ts.md).
689689

@@ -709,7 +709,7 @@ module.exports = {
709709
{
710710
loader: "sass-loader",
711711
options: {
712-
api: "modern",
712+
api: "modern-compiler",
713713
sassOptions: {
714714
// Your sass options
715715
},

src/index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,29 @@ async function loader(content) {
3434

3535
const useSourceMap =
3636
typeof options.sourceMap === "boolean" ? options.sourceMap : this.sourceMap;
37+
// Use `legacy` for `node-sass` and `modern` for `dart-sass` and `sass-embedded`
38+
const apiType =
39+
typeof implementation.compileStringAsync === "undefined"
40+
? "legacy"
41+
: typeof options.api === "undefined"
42+
? "modern"
43+
: options.api;
3744
const sassOptions = await getSassOptions(
3845
this,
3946
options,
4047
content,
4148
implementation,
4249
useSourceMap,
50+
apiType,
4351
);
52+
4453
const shouldUseWebpackImporter =
4554
typeof options.webpackImporter === "boolean"
4655
? options.webpackImporter
4756
: true;
4857

4958
if (shouldUseWebpackImporter) {
50-
const isModernAPI =
51-
options.api === "modern" || options.api === "modern-compiler";
59+
const isModernAPI = apiType === "modern" || apiType === "modern-compiler";
5260

5361
if (!isModernAPI) {
5462
const { includePaths } = sassOptions;
@@ -67,7 +75,7 @@ async function loader(content) {
6775
let compile;
6876

6977
try {
70-
compile = getCompileFn(this, implementation, options);
78+
compile = getCompileFn(this, implementation, apiType);
7179
} catch (error) {
7280
callback(error);
7381
return;

src/utils.js

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ function proxyCustomImporters(importers, loaderContext) {
9494
* @param {string} content
9595
* @param {object} implementation
9696
* @param {boolean} useSourceMap
97+
* @param {"legacy" | "modern" | "modern-compiler"} apiType
9798
* @returns {Object}
9899
*/
99100
async function getSassOptions(
@@ -102,6 +103,7 @@ async function getSassOptions(
102103
content,
103104
implementation,
104105
useSourceMap,
106+
apiType,
105107
) {
106108
const options = loaderOptions.sassOptions
107109
? typeof loaderOptions.sassOptions === "function"
@@ -174,8 +176,7 @@ async function getSassOptions(
174176
};
175177
}
176178

177-
const isModernAPI =
178-
loaderOptions.api === "modern" || loaderOptions.api === "modern-compiler";
179+
const isModernAPI = apiType === "modern" || apiType === "modern-compiler";
179180
const { resourcePath } = loaderContext;
180181

181182
if (isModernAPI) {
@@ -477,9 +478,7 @@ function getWebpackResolver(
477478
includePaths = [],
478479
) {
479480
const isModernSass =
480-
implementation &&
481-
(implementation.info.includes("dart-sass") ||
482-
implementation.info.includes("sass-embedded"));
481+
implementation && typeof implementation.compileStringAsync !== "undefined";
483482
// We only have one difference with the built-in sass resolution logic and out resolution logic:
484483
// First, we look at the files starting with `_`, then without `_` (i.e. `_name.sass`, `_name.scss`, `_name.css`, `name.sass`, `name.scss`, `name.css`),
485484
// although `sass` look together by extensions (i.e. `_name.sass`/`name.sass`/`_name.scss`/`name.scss`/`_name.css`/`name.css`).
@@ -735,24 +734,20 @@ const sassModernCompilers = new WeakMap();
735734
*
736735
* @param {Object} loaderContext
737736
* @param {Object} implementation
738-
* @param {Object} options
737+
* @param {"legacy" | "modern" | "modern-compiler"} apiType
739738
* @returns {Function}
740739
*/
741-
function getCompileFn(loaderContext, implementation, options) {
742-
const isNewSass =
743-
implementation.info.includes("dart-sass") ||
744-
implementation.info.includes("sass-embedded");
745-
746-
if (isNewSass) {
747-
if (options.api === "modern") {
740+
function getCompileFn(loaderContext, implementation, apiType) {
741+
if (typeof implementation.compileStringAsync !== "undefined") {
742+
if (apiType === "modern") {
748743
return (sassOptions) => {
749744
const { data, ...rest } = sassOptions;
750745

751746
return implementation.compileStringAsync(data, rest);
752747
};
753748
}
754749

755-
if (options.api === "modern-compiler") {
750+
if (apiType === "modern-compiler") {
756751
return async (sassOptions) => {
757752
// eslint-disable-next-line no-underscore-dangle
758753
const webpackCompiler = loaderContext._compiler;
@@ -799,7 +794,7 @@ function getCompileFn(loaderContext, implementation, options) {
799794
});
800795
}
801796

802-
if (options.api === "modern" || options.api === "modern-compiler") {
797+
if (apiType === "modern" || apiType === "modern-compiler") {
803798
throw new Error("Modern API is not supported for 'node-sass'");
804799
}
805800

test/__snapshots__/implementation-option.test.js.no-node-sass.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ exports[`implementation option not specify with modern-compiler API: errors 1`]
4040

4141
exports[`implementation option not specify with modern-compiler API: warnings 1`] = `[]`;
4242

43+
exports[`implementation option not specify with node-sass: errors 1`] = `[]`;
44+
45+
exports[`implementation option not specify with node-sass: warnings 1`] = `[]`;
46+
4347
exports[`implementation option not specify: errors 1`] = `[]`;
4448

4549
exports[`implementation option not specify: warnings 1`] = `[]`;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ exports[`implementation option not specify with modern-compiler API: errors 1`]
4444

4545
exports[`implementation option not specify with modern-compiler API: warnings 1`] = `[]`;
4646

47+
exports[`implementation option not specify with node-sass: errors 1`] = `[]`;
48+
49+
exports[`implementation option not specify with node-sass: warnings 1`] = `[]`;
50+
4751
exports[`implementation option not specify: errors 1`] = `[]`;
4852

4953
exports[`implementation option not specify: warnings 1`] = `[]`;

test/implementation-option.test.js

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,49 @@ describe("implementation option", () => {
185185
expect(getWarnings(stats)).toMatchSnapshot("warnings");
186186
expect(getErrors(stats)).toMatchSnapshot("errors");
187187

188-
expect(sassEmbeddedSpy).toHaveBeenCalledTimes(1);
188+
expect(sassEmbeddedSpy).toHaveBeenCalledTimes(0);
189+
expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(1);
189190
expect(nodeSassSpy).toHaveBeenCalledTimes(0);
190191
expect(dartSassSpy).toHaveBeenCalledTimes(0);
192+
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0);
191193

192194
sassEmbeddedSpy.mockClear();
195+
sassEmbeddedSpyModernAPI.mockClear();
193196
nodeSassSpy.mockClear();
194197
dartSassSpy.mockClear();
198+
dartSassSpyModernAPI.mockClear();
199+
200+
await close(compiler);
201+
});
202+
203+
it("not specify with node-sass", async () => {
204+
const testId = getTestId("language", "scss");
205+
const options = {
206+
implementation: nodeSass,
207+
};
208+
const compiler = getCompiler(testId, { loader: { options } });
209+
const stats = await compile(compiler);
210+
const { css, sourceMap } = getCodeFromBundle(stats, compiler);
211+
212+
expect(css).toBeDefined();
213+
expect(sourceMap).toBeUndefined();
214+
215+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
216+
expect(getErrors(stats)).toMatchSnapshot("errors");
217+
218+
expect(sassEmbeddedSpy).toHaveBeenCalledTimes(0);
219+
expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(0);
220+
expect(nodeSassSpy).toHaveBeenCalledTimes(isNodeSassSupported() ? 1 : 0);
221+
expect(dartSassSpy).toHaveBeenCalledTimes(0);
222+
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(
223+
isNodeSassSupported() ? 0 : 1,
224+
);
225+
226+
sassEmbeddedSpy.mockClear();
227+
sassEmbeddedSpyModernAPI.mockClear();
228+
nodeSassSpy.mockClear();
229+
dartSassSpy.mockClear();
230+
dartSassSpyModernAPI.mockClear();
195231

196232
await close(compiler);
197233
});
@@ -216,8 +252,10 @@ describe("implementation option", () => {
216252
expect(dartSassSpy).toHaveBeenCalledTimes(0);
217253

218254
sassEmbeddedSpy.mockClear();
255+
sassEmbeddedSpyModernAPI.mockClear();
219256
nodeSassSpy.mockClear();
220257
dartSassSpy.mockClear();
258+
dartSassSpyModernAPI.mockClear();
221259

222260
await close(compiler);
223261
});
@@ -241,8 +279,10 @@ describe("implementation option", () => {
241279
expect(nodeSassSpy).toHaveBeenCalledTimes(0);
242280
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0);
243281

282+
sassEmbeddedSpy.mockClear();
244283
sassEmbeddedSpyModernAPI.mockClear();
245284
nodeSassSpy.mockClear();
285+
dartSassSpy.mockClear();
246286
dartSassSpyModernAPI.mockClear();
247287

248288
await close(compiler);
@@ -269,9 +309,11 @@ describe("implementation option", () => {
269309
expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0);
270310
expect(dartSassCompilerSpies.compileStringSpy).toHaveBeenCalledTimes(0);
271311

312+
sassEmbeddedSpy.mockClear();
313+
sassEmbeddedSpyModernAPI.mockClear();
272314
nodeSassSpy.mockClear();
315+
dartSassSpy.mockClear();
273316
dartSassSpyModernAPI.mockClear();
274-
dartSassCompilerSpies.mockClear();
275317

276318
await close(compiler);
277319
});

0 commit comments

Comments
 (0)