From 10be1ba161557638fd3b80f4a5467159179ef9b1 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 25 Jul 2024 21:02:50 +0300 Subject: [PATCH 1/5] feat!: use modern Sass JS API by default --- README.md | 2 +- src/index.js | 8 +++++--- src/utils.js | 14 ++++++++------ test/implementation-option.test.js | 14 ++++++++++++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1227e456..6d84e26c 100644 --- a/README.md +++ b/README.md @@ -683,7 +683,7 @@ Type: type api = "legacy" | "modern" | "modern-compiler"; ``` -Default: `"legacy"` +Default: `"modern"` 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). diff --git a/src/index.js b/src/index.js index 9d270560..84442bcc 100644 --- a/src/index.js +++ b/src/index.js @@ -34,21 +34,23 @@ async function loader(content) { const useSourceMap = typeof options.sourceMap === "boolean" ? options.sourceMap : this.sourceMap; + const apiType = typeof options.api === "undefined" ? "modern" : options.api; const sassOptions = await getSassOptions( this, options, content, implementation, useSourceMap, + apiType, ); + const shouldUseWebpackImporter = typeof options.webpackImporter === "boolean" ? options.webpackImporter : true; if (shouldUseWebpackImporter) { - const isModernAPI = - options.api === "modern" || options.api === "modern-compiler"; + const isModernAPI = apiType === "modern" || apiType === "modern-compiler"; if (!isModernAPI) { const { includePaths } = sassOptions; @@ -67,7 +69,7 @@ async function loader(content) { let compile; try { - compile = getCompileFn(this, implementation, options); + compile = getCompileFn(this, implementation, apiType); } catch (error) { callback(error); return; diff --git a/src/utils.js b/src/utils.js index 41968374..d10c3533 100644 --- a/src/utils.js +++ b/src/utils.js @@ -94,6 +94,7 @@ function proxyCustomImporters(importers, loaderContext) { * @param {string} content * @param {object} implementation * @param {boolean} useSourceMap + * @param {"legacy" | "modern" | "modern-compiler"} apiType * @returns {Object} */ async function getSassOptions( @@ -102,6 +103,7 @@ async function getSassOptions( content, implementation, useSourceMap, + apiType, ) { const options = loaderOptions.sassOptions ? typeof loaderOptions.sassOptions === "function" @@ -175,7 +177,7 @@ async function getSassOptions( } const isModernAPI = - loaderOptions.api === "modern" || loaderOptions.api === "modern-compiler"; + apiType === "modern" || apiType === "modern-compiler"; const { resourcePath } = loaderContext; if (isModernAPI) { @@ -735,16 +737,16 @@ const sassModernCompilers = new WeakMap(); * * @param {Object} loaderContext * @param {Object} implementation - * @param {Object} options + * @param {"legacy" | "modern" | "modern-compiler"} apiType * @returns {Function} */ -function getCompileFn(loaderContext, implementation, options) { +function getCompileFn(loaderContext, implementation, apiType) { const isNewSass = implementation.info.includes("dart-sass") || implementation.info.includes("sass-embedded"); if (isNewSass) { - if (options.api === "modern") { + if (apiType === "modern") { return (sassOptions) => { const { data, ...rest } = sassOptions; @@ -752,7 +754,7 @@ function getCompileFn(loaderContext, implementation, options) { }; } - if (options.api === "modern-compiler") { + if (apiType === "modern-compiler") { return async (sassOptions) => { // eslint-disable-next-line no-underscore-dangle const webpackCompiler = loaderContext._compiler; @@ -799,7 +801,7 @@ function getCompileFn(loaderContext, implementation, options) { }); } - if (options.api === "modern" || options.api === "modern-compiler") { + if (apiType === "modern" || apiType === "modern-compiler") { throw new Error("Modern API is not supported for 'node-sass'"); } diff --git a/test/implementation-option.test.js b/test/implementation-option.test.js index 37fd9446..a18bd29e 100644 --- a/test/implementation-option.test.js +++ b/test/implementation-option.test.js @@ -185,13 +185,17 @@ describe("implementation option", () => { expect(getWarnings(stats)).toMatchSnapshot("warnings"); expect(getErrors(stats)).toMatchSnapshot("errors"); - expect(sassEmbeddedSpy).toHaveBeenCalledTimes(1); + expect(sassEmbeddedSpy).toHaveBeenCalledTimes(0); + expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(1); expect(nodeSassSpy).toHaveBeenCalledTimes(0); expect(dartSassSpy).toHaveBeenCalledTimes(0); + expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0); sassEmbeddedSpy.mockClear(); + sassEmbeddedSpyModernAPI.mockClear(); nodeSassSpy.mockClear(); dartSassSpy.mockClear(); + dartSassSpyModernAPI.mockClear(); await close(compiler); }); @@ -216,8 +220,10 @@ describe("implementation option", () => { expect(dartSassSpy).toHaveBeenCalledTimes(0); sassEmbeddedSpy.mockClear(); + sassEmbeddedSpyModernAPI.mockClear(); nodeSassSpy.mockClear(); dartSassSpy.mockClear(); + dartSassSpyModernAPI.mockClear(); await close(compiler); }); @@ -241,8 +247,10 @@ describe("implementation option", () => { expect(nodeSassSpy).toHaveBeenCalledTimes(0); expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0); + sassEmbeddedSpy.mockClear(); sassEmbeddedSpyModernAPI.mockClear(); nodeSassSpy.mockClear(); + dartSassSpy.mockClear(); dartSassSpyModernAPI.mockClear(); await close(compiler); @@ -269,9 +277,11 @@ describe("implementation option", () => { expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0); expect(dartSassCompilerSpies.compileStringSpy).toHaveBeenCalledTimes(0); + sassEmbeddedSpy.mockClear(); + sassEmbeddedSpyModernAPI.mockClear(); nodeSassSpy.mockClear(); + dartSassSpy.mockClear(); dartSassSpyModernAPI.mockClear(); - dartSassCompilerSpies.mockClear(); await close(compiler); }); From 7c215817d965fbf7c59e8a17d81be137af7f1c4a Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 25 Jul 2024 21:13:22 +0300 Subject: [PATCH 2/5] fix: use `legacy` --- README.md | 4 +- src/index.js | 7 +- ...mentation-option.test.js.no-node-sass.snap | 106 +----------------- .../implementation-option.test.js.snap | 4 + test/implementation-option.test.js | 30 +++++ 5 files changed, 44 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 6d84e26c..78b2459a 100644 --- a/README.md +++ b/README.md @@ -683,7 +683,7 @@ Type: type api = "legacy" | "modern" | "modern-compiler"; ``` -Default: `"modern"` +Default: `"modern"` for `sass` (`dart-sass`) and `sass-embedded` or `"legacy"` for `node-sass` 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). @@ -709,7 +709,7 @@ module.exports = { { loader: "sass-loader", options: { - api: "modern", + api: "modern-compiler", sassOptions: { // Your sass options }, diff --git a/src/index.js b/src/index.js index 84442bcc..ef93668d 100644 --- a/src/index.js +++ b/src/index.js @@ -34,7 +34,12 @@ async function loader(content) { const useSourceMap = typeof options.sourceMap === "boolean" ? options.sourceMap : this.sourceMap; - const apiType = typeof options.api === "undefined" ? "modern" : options.api; + // Use `legacy` for `node-sass` and `modern` for `dart-sass` and `sass-embedded` + const apiType = !implementation.compileStringAsync + ? "legacy" + : typeof options.api === "undefined" + ? "modern" + : options.api; const sassOptions = await getSassOptions( this, options, diff --git a/test/__snapshots__/implementation-option.test.js.no-node-sass.snap b/test/__snapshots__/implementation-option.test.js.no-node-sass.snap index 96cfe4c3..04b7e2d0 100644 --- a/test/__snapshots__/implementation-option.test.js.no-node-sass.snap +++ b/test/__snapshots__/implementation-option.test.js.no-node-sass.snap @@ -1,107 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`implementation option 'dart-sass', 'legacy' API: errors 1`] = `[]`; +exports[`implementation option not specify with node-sass: errors 1`] = `[]`; -exports[`implementation option 'dart-sass', 'legacy' API: warnings 1`] = `[]`; - -exports[`implementation option 'dart-sass', 'modern' API: errors 1`] = `[]`; - -exports[`implementation option 'dart-sass', 'modern' API: warnings 1`] = `[]`; - -exports[`implementation option 'dart-sass', 'modern-compiler' API: errors 1`] = `[]`; - -exports[`implementation option 'dart-sass', 'modern-compiler' API: warnings 1`] = `[]`; - -exports[`implementation option 'sass_string', 'legacy' API: errors 1`] = `[]`; - -exports[`implementation option 'sass_string', 'legacy' API: warnings 1`] = `[]`; - -exports[`implementation option 'sass-embedded', 'legacy' API: errors 1`] = `[]`; - -exports[`implementation option 'sass-embedded', 'legacy' API: warnings 1`] = `[]`; - -exports[`implementation option 'sass-embedded', 'modern' API: errors 1`] = `[]`; - -exports[`implementation option 'sass-embedded', 'modern' API: warnings 1`] = `[]`; - -exports[`implementation option 'sass-embedded', 'modern-compiler' API: errors 1`] = `[]`; - -exports[`implementation option 'sass-embedded', 'modern-compiler' API: warnings 1`] = `[]`; - -exports[`implementation option not specify with legacy API: errors 1`] = `[]`; - -exports[`implementation option not specify with legacy API: warnings 1`] = `[]`; - -exports[`implementation option not specify with modern API: errors 1`] = `[]`; - -exports[`implementation option not specify with modern API: warnings 1`] = `[]`; - -exports[`implementation option not specify with modern-compiler API: errors 1`] = `[]`; - -exports[`implementation option not specify with modern-compiler API: warnings 1`] = `[]`; - -exports[`implementation option not specify: errors 1`] = `[]`; - -exports[`implementation option not specify: warnings 1`] = `[]`; - -exports[`implementation option should not swallow an error when trying to load a sass implementation: errors 1`] = ` -[ - "ModuleBuildError: Module build failed (from ../src/cjs.js): -Some error sass-embedded", -] -`; - -exports[`implementation option should not swallow an error when trying to load a sass implementation: warnings 1`] = `[]`; - -exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: errors 1`] = `[]`; - -exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: errors 2`] = `[]`; - -exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: warnings 1`] = `[]`; - -exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: warnings 2`] = `[]`; - -exports[`implementation option should throw an error on an unknown sass implementation: errors 1`] = ` -[ - "ModuleBuildError: Module build failed (from ../src/cjs.js): -Error: Unknown Sass implementation "strange-sass".", -] -`; - -exports[`implementation option should throw an error on an unknown sass implementation: warnings 1`] = `[]`; - -exports[`implementation option should throw an error when the "info" is unparseable: errors 1`] = ` -[ - "ModuleBuildError: Module build failed (from ../src/cjs.js): -Error: Unknown Sass implementation "asdfj".", -] -`; - -exports[`implementation option should throw an error when the "info" is unparseable: warnings 1`] = `[]`; - -exports[`implementation option should throw error when the "info" does not exist: errors 1`] = ` -[ - "ModuleBuildError: Module build failed (from ../src/cjs.js): -Error: Unknown Sass implementation.", -] -`; - -exports[`implementation option should throw error when the "info" does not exist: warnings 1`] = `[]`; - -exports[`implementation option should throw error when unresolved package: errors 1`] = ` -[ - "ModuleBuildError: Module build failed (from ../src/cjs.js): -Error: Cannot find module 'unresolved' from 'src/utils.js'", -] -`; - -exports[`implementation option should throw error when unresolved package: warnings 1`] = `[]`; - -exports[`implementation option should try to load using valid order: errors 1`] = ` -[ - "ModuleBuildError: Module build failed (from ../src/cjs.js): -Some error sass-embedded", -] -`; - -exports[`implementation option should try to load using valid order: warnings 1`] = `[]`; +exports[`implementation option not specify with node-sass: warnings 1`] = `[]`; diff --git a/test/__snapshots__/implementation-option.test.js.snap b/test/__snapshots__/implementation-option.test.js.snap index 2b458aec..21d95e88 100644 --- a/test/__snapshots__/implementation-option.test.js.snap +++ b/test/__snapshots__/implementation-option.test.js.snap @@ -44,6 +44,10 @@ exports[`implementation option not specify with modern-compiler API: errors 1`] exports[`implementation option not specify with modern-compiler API: warnings 1`] = `[]`; +exports[`implementation option not specify with node-sass: errors 1`] = `[]`; + +exports[`implementation option not specify with node-sass: warnings 1`] = `[]`; + exports[`implementation option not specify: errors 1`] = `[]`; exports[`implementation option not specify: warnings 1`] = `[]`; diff --git a/test/implementation-option.test.js b/test/implementation-option.test.js index a18bd29e..74526976 100644 --- a/test/implementation-option.test.js +++ b/test/implementation-option.test.js @@ -200,6 +200,36 @@ describe("implementation option", () => { await close(compiler); }); + it("not specify with node-sass", async () => { + const testId = getTestId("language", "scss"); + const options = { + implementation: nodeSass + }; + const compiler = getCompiler(testId, { loader: { options } }); + const stats = await compile(compiler); + const { css, sourceMap } = getCodeFromBundle(stats, compiler); + + expect(css).toBeDefined(); + expect(sourceMap).toBeUndefined(); + + expect(getWarnings(stats)).toMatchSnapshot("warnings"); + expect(getErrors(stats)).toMatchSnapshot("errors"); + + expect(sassEmbeddedSpy).toHaveBeenCalledTimes(0); + expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(0); + expect(nodeSassSpy).toHaveBeenCalledTimes(1); + expect(dartSassSpy).toHaveBeenCalledTimes(0); + expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0); + + sassEmbeddedSpy.mockClear(); + sassEmbeddedSpyModernAPI.mockClear(); + nodeSassSpy.mockClear(); + dartSassSpy.mockClear(); + dartSassSpyModernAPI.mockClear(); + + await close(compiler); + }); + it("not specify with legacy API", async () => { const testId = getTestId("language", "scss"); const options = { From 8e9eaeeb2e48ea49a9105098b26e6d6974f30736 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 25 Jul 2024 21:16:53 +0300 Subject: [PATCH 3/5] style: fix --- src/utils.js | 3 +-- test/implementation-option.test.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils.js b/src/utils.js index d10c3533..112fb19d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -176,8 +176,7 @@ async function getSassOptions( }; } - const isModernAPI = - apiType === "modern" || apiType === "modern-compiler"; + const isModernAPI = apiType === "modern" || apiType === "modern-compiler"; const { resourcePath } = loaderContext; if (isModernAPI) { diff --git a/test/implementation-option.test.js b/test/implementation-option.test.js index 74526976..c98f3825 100644 --- a/test/implementation-option.test.js +++ b/test/implementation-option.test.js @@ -203,7 +203,7 @@ describe("implementation option", () => { it("not specify with node-sass", async () => { const testId = getTestId("language", "scss"); const options = { - implementation: nodeSass + implementation: nodeSass, }; const compiler = getCompiler(testId, { loader: { options } }); const stats = await compile(compiler); From 901e6b4942a053b0da7a3c392714f6c982df35a3 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 25 Jul 2024 21:22:36 +0300 Subject: [PATCH 4/5] refactor: code --- src/index.js | 11 ++++++----- src/utils.js | 10 ++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index ef93668d..7d1c16c4 100644 --- a/src/index.js +++ b/src/index.js @@ -35,11 +35,12 @@ async function loader(content) { const useSourceMap = typeof options.sourceMap === "boolean" ? options.sourceMap : this.sourceMap; // Use `legacy` for `node-sass` and `modern` for `dart-sass` and `sass-embedded` - const apiType = !implementation.compileStringAsync - ? "legacy" - : typeof options.api === "undefined" - ? "modern" - : options.api; + const apiType = + typeof implementation.compileStringAsync === "undefined" + ? "legacy" + : typeof options.api === "undefined" + ? "modern" + : options.api; const sassOptions = await getSassOptions( this, options, diff --git a/src/utils.js b/src/utils.js index 112fb19d..9912b9a9 100644 --- a/src/utils.js +++ b/src/utils.js @@ -478,9 +478,7 @@ function getWebpackResolver( includePaths = [], ) { const isModernSass = - implementation && - (implementation.info.includes("dart-sass") || - implementation.info.includes("sass-embedded")); + implementation && typeof implementation.compileStringAsync !== "undefined"; // We only have one difference with the built-in sass resolution logic and out resolution logic: // First, we look at the files starting with `_`, then without `_` (i.e. `_name.sass`, `_name.scss`, `_name.css`, `name.sass`, `name.scss`, `name.css`), // although `sass` look together by extensions (i.e. `_name.sass`/`name.sass`/`_name.scss`/`name.scss`/`_name.css`/`name.css`). @@ -740,11 +738,7 @@ const sassModernCompilers = new WeakMap(); * @returns {Function} */ function getCompileFn(loaderContext, implementation, apiType) { - const isNewSass = - implementation.info.includes("dart-sass") || - implementation.info.includes("sass-embedded"); - - if (isNewSass) { + if (typeof implementation.compileStringAsync !== "undefined") { if (apiType === "modern") { return (sassOptions) => { const { data, ...rest } = sassOptions; From 3a6bf13f912c757fa2ca16bf74dd9e681e0aeb62 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Thu, 25 Jul 2024 21:29:45 +0300 Subject: [PATCH 5/5] test: fix --- ...mentation-option.test.js.no-node-sass.snap | 106 ++++++++++++++++++ test/implementation-option.test.js | 6 +- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/test/__snapshots__/implementation-option.test.js.no-node-sass.snap b/test/__snapshots__/implementation-option.test.js.no-node-sass.snap index 04b7e2d0..68f8787b 100644 --- a/test/__snapshots__/implementation-option.test.js.no-node-sass.snap +++ b/test/__snapshots__/implementation-option.test.js.no-node-sass.snap @@ -1,5 +1,111 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`implementation option 'dart-sass', 'legacy' API: errors 1`] = `[]`; + +exports[`implementation option 'dart-sass', 'legacy' API: warnings 1`] = `[]`; + +exports[`implementation option 'dart-sass', 'modern' API: errors 1`] = `[]`; + +exports[`implementation option 'dart-sass', 'modern' API: warnings 1`] = `[]`; + +exports[`implementation option 'dart-sass', 'modern-compiler' API: errors 1`] = `[]`; + +exports[`implementation option 'dart-sass', 'modern-compiler' API: warnings 1`] = `[]`; + +exports[`implementation option 'sass_string', 'legacy' API: errors 1`] = `[]`; + +exports[`implementation option 'sass_string', 'legacy' API: warnings 1`] = `[]`; + +exports[`implementation option 'sass-embedded', 'legacy' API: errors 1`] = `[]`; + +exports[`implementation option 'sass-embedded', 'legacy' API: warnings 1`] = `[]`; + +exports[`implementation option 'sass-embedded', 'modern' API: errors 1`] = `[]`; + +exports[`implementation option 'sass-embedded', 'modern' API: warnings 1`] = `[]`; + +exports[`implementation option 'sass-embedded', 'modern-compiler' API: errors 1`] = `[]`; + +exports[`implementation option 'sass-embedded', 'modern-compiler' API: warnings 1`] = `[]`; + +exports[`implementation option not specify with legacy API: errors 1`] = `[]`; + +exports[`implementation option not specify with legacy API: warnings 1`] = `[]`; + +exports[`implementation option not specify with modern API: errors 1`] = `[]`; + +exports[`implementation option not specify with modern API: warnings 1`] = `[]`; + +exports[`implementation option not specify with modern-compiler API: errors 1`] = `[]`; + +exports[`implementation option not specify with modern-compiler API: warnings 1`] = `[]`; + exports[`implementation option not specify with node-sass: errors 1`] = `[]`; exports[`implementation option not specify with node-sass: warnings 1`] = `[]`; + +exports[`implementation option not specify: errors 1`] = `[]`; + +exports[`implementation option not specify: warnings 1`] = `[]`; + +exports[`implementation option should not swallow an error when trying to load a sass implementation: errors 1`] = ` +[ + "ModuleBuildError: Module build failed (from ../src/cjs.js): +Some error sass-embedded", +] +`; + +exports[`implementation option should not swallow an error when trying to load a sass implementation: warnings 1`] = `[]`; + +exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: errors 1`] = `[]`; + +exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: errors 2`] = `[]`; + +exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: warnings 1`] = `[]`; + +exports[`implementation option should support switching the implementation within the same process when using the modern-compiler API: warnings 2`] = `[]`; + +exports[`implementation option should throw an error on an unknown sass implementation: errors 1`] = ` +[ + "ModuleBuildError: Module build failed (from ../src/cjs.js): +Error: Unknown Sass implementation "strange-sass".", +] +`; + +exports[`implementation option should throw an error on an unknown sass implementation: warnings 1`] = `[]`; + +exports[`implementation option should throw an error when the "info" is unparseable: errors 1`] = ` +[ + "ModuleBuildError: Module build failed (from ../src/cjs.js): +Error: Unknown Sass implementation "asdfj".", +] +`; + +exports[`implementation option should throw an error when the "info" is unparseable: warnings 1`] = `[]`; + +exports[`implementation option should throw error when the "info" does not exist: errors 1`] = ` +[ + "ModuleBuildError: Module build failed (from ../src/cjs.js): +Error: Unknown Sass implementation.", +] +`; + +exports[`implementation option should throw error when the "info" does not exist: warnings 1`] = `[]`; + +exports[`implementation option should throw error when unresolved package: errors 1`] = ` +[ + "ModuleBuildError: Module build failed (from ../src/cjs.js): +Error: Cannot find module 'unresolved' from 'src/utils.js'", +] +`; + +exports[`implementation option should throw error when unresolved package: warnings 1`] = `[]`; + +exports[`implementation option should try to load using valid order: errors 1`] = ` +[ + "ModuleBuildError: Module build failed (from ../src/cjs.js): +Some error sass-embedded", +] +`; + +exports[`implementation option should try to load using valid order: warnings 1`] = `[]`; diff --git a/test/implementation-option.test.js b/test/implementation-option.test.js index c98f3825..ba11835c 100644 --- a/test/implementation-option.test.js +++ b/test/implementation-option.test.js @@ -217,9 +217,11 @@ describe("implementation option", () => { expect(sassEmbeddedSpy).toHaveBeenCalledTimes(0); expect(sassEmbeddedSpyModernAPI).toHaveBeenCalledTimes(0); - expect(nodeSassSpy).toHaveBeenCalledTimes(1); + expect(nodeSassSpy).toHaveBeenCalledTimes(isNodeSassSupported() ? 1 : 0); expect(dartSassSpy).toHaveBeenCalledTimes(0); - expect(dartSassSpyModernAPI).toHaveBeenCalledTimes(0); + expect(dartSassSpyModernAPI).toHaveBeenCalledTimes( + isNodeSassSupported() ? 0 : 1, + ); sassEmbeddedSpy.mockClear(); sassEmbeddedSpyModernAPI.mockClear();