Skip to content

Commit cef40a8

Browse files
authored
feat: added the modern-compiler value for API to reuse compiler process (#1195)
1 parent 13f0dc8 commit cef40a8

21 files changed

+36650
-2034
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -657,12 +657,12 @@ module.exports = {
657657
Type:
658658

659659
```ts
660-
type api = "legacy" | "modern";
660+
type api = "legacy" | "modern" | "modern-compiler";
661661
```
662662

663663
Default: `"legacy"`
664664

665-
Allows you to switch between `legacy` and `modern` API. You can find more information [here](https://sass-lang.com/documentation/js-api).
665+
Allows you to switch between `legacy` and `modern` API. 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).
666666

667667
> **Warning**
668668
>

src/index.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ async function loader(content) {
4747
: true;
4848

4949
if (shouldUseWebpackImporter) {
50-
const isModernAPI = options.api === "modern";
50+
const isModernAPI =
51+
options.api === "modern" || options.api === "modern-compiler";
5152

5253
if (!isModernAPI) {
5354
const { includePaths } = sassOptions;
@@ -65,7 +66,7 @@ async function loader(content) {
6566
let compile;
6667

6768
try {
68-
compile = getCompileFn(implementation, options);
69+
compile = getCompileFn(this, implementation, options);
6970
} catch (error) {
7071
callback(error);
7172
return;
@@ -74,7 +75,7 @@ async function loader(content) {
7475
let result;
7576

7677
try {
77-
result = await compile(sassOptions, options);
78+
result = await compile(sassOptions);
7879
} catch (error) {
7980
// There are situations when the `file`/`span.url` property do not exist
8081
// Modern API

src/options.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"api": {
1818
"description": "Switch between old and modern API for `sass` (`Dart Sass`) and `Sass Embedded` implementations.",
1919
"link": "https://github.com/webpack-contrib/sass-loader#sassoptions",
20-
"enum": ["legacy", "modern"]
20+
"enum": ["legacy", "modern", "modern-compiler"]
2121
},
2222
"sassOptions": {
2323
"description": "Options for `node-sass` or `sass` (`Dart Sass`) implementation.",

src/utils.js

+37-3
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ async function getSassOptions(
174174
};
175175
}
176176

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

180181
if (isModernAPI) {
@@ -650,15 +651,17 @@ function getWebpackImporter(loaderContext, implementation, includePaths) {
650651
}
651652

652653
let nodeSassJobQueue = null;
654+
const sassModernCompilers = new WeakMap();
653655

654656
/**
655657
* Verifies that the implementation and version of Sass is supported by this loader.
656658
*
659+
* @param {Object} loaderContext
657660
* @param {Object} implementation
658661
* @param {Object} options
659662
* @returns {Function}
660663
*/
661-
function getCompileFn(implementation, options) {
664+
function getCompileFn(loaderContext, implementation, options) {
662665
const isNewSass =
663666
implementation.info.includes("dart-sass") ||
664667
implementation.info.includes("sass-embedded");
@@ -672,6 +675,37 @@ function getCompileFn(implementation, options) {
672675
};
673676
}
674677

678+
if (options.api === "modern-compiler") {
679+
return async (sassOptions) => {
680+
// eslint-disable-next-line no-underscore-dangle
681+
const webpackCompiler = loaderContext._compiler;
682+
const { data, ...rest } = sassOptions;
683+
684+
// Some people can run the loader in a multi-threading way;
685+
// there is no webpack compiler object in such case.
686+
if (webpackCompiler) {
687+
if (!sassModernCompilers.has(implementation)) {
688+
// Create a long-running compiler process that can be reused
689+
// for compiling individual files.
690+
const compiler = await implementation.initAsyncCompiler();
691+
// Check again because awaiting the initialization function
692+
// introduces a race condition.
693+
if (!sassModernCompilers.has(implementation)) {
694+
sassModernCompilers.set(implementation, compiler);
695+
webpackCompiler.hooks.shutdown.tap("sass-loader", () => {
696+
compiler.dispose();
697+
});
698+
}
699+
}
700+
return sassModernCompilers
701+
.get(implementation)
702+
.compileStringAsync(data, rest);
703+
}
704+
705+
return implementation.compileStringAsync(data, rest);
706+
};
707+
}
708+
675709
return (sassOptions) =>
676710
new Promise((resolve, reject) => {
677711
implementation.render(sassOptions, (error, result) => {
@@ -686,7 +720,7 @@ function getCompileFn(implementation, options) {
686720
});
687721
}
688722

689-
if (options.api === "modern") {
723+
if (options.api === "modern" || options.api === "modern-compiler") {
690724
throw new Error("Modern API is not supported for 'node-sass'");
691725
}
692726

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

+176
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,34 @@ exports[`additionalData option should use same EOL on all os ('dart-sass', 'mode
5656

5757
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
5858

59+
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern-compiler' API, 'sass' syntax): css 1`] = `
60+
"a {
61+
color: hotpink;
62+
}
63+
64+
body {
65+
color: hotpink;
66+
}"
67+
`;
68+
69+
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
70+
71+
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
72+
73+
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern-compiler' API, 'scss' syntax): css 1`] = `
74+
"a {
75+
color: red;
76+
}
77+
78+
body {
79+
color: hotpink;
80+
}"
81+
`;
82+
83+
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
84+
85+
exports[`additionalData option should use same EOL on all os ('dart-sass', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
86+
5987
exports[`additionalData option should use same EOL on all os ('node-sass', 'legacy' API, 'sass' syntax): css 1`] = `
6088
"a {
6189
color: hotpink; }
@@ -138,6 +166,34 @@ exports[`additionalData option should use same EOL on all os ('sass-embedded', '
138166

139167
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
140168

169+
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern-compiler' API, 'sass' syntax): css 1`] = `
170+
"a {
171+
color: hotpink;
172+
}
173+
174+
body {
175+
color: hotpink;
176+
}"
177+
`;
178+
179+
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
180+
181+
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
182+
183+
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern-compiler' API, 'scss' syntax): css 1`] = `
184+
"a {
185+
color: red;
186+
}
187+
188+
body {
189+
color: hotpink;
190+
}"
191+
`;
192+
193+
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
194+
195+
exports[`additionalData option should use same EOL on all os ('sass-embedded', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
196+
141197
exports[`additionalData option should work as a function ('dart-sass', 'legacy' API, 'sass' syntax): css 1`] = `
142198
"body {
143199
color: hotpink;
@@ -178,6 +234,26 @@ exports[`additionalData option should work as a function ('dart-sass', 'modern'
178234

179235
exports[`additionalData option should work as a function ('dart-sass', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
180236

237+
exports[`additionalData option should work as a function ('dart-sass', 'modern-compiler' API, 'sass' syntax): css 1`] = `
238+
"body {
239+
color: hotpink;
240+
}"
241+
`;
242+
243+
exports[`additionalData option should work as a function ('dart-sass', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
244+
245+
exports[`additionalData option should work as a function ('dart-sass', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
246+
247+
exports[`additionalData option should work as a function ('dart-sass', 'modern-compiler' API, 'scss' syntax): css 1`] = `
248+
"body {
249+
color: hotpink;
250+
}"
251+
`;
252+
253+
exports[`additionalData option should work as a function ('dart-sass', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
254+
255+
exports[`additionalData option should work as a function ('dart-sass', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
256+
181257
exports[`additionalData option should work as a function ('node-sass', 'legacy' API, 'sass' syntax): css 1`] = `
182258
"body {
183259
color: hotpink; }
@@ -238,6 +314,26 @@ exports[`additionalData option should work as a function ('sass-embedded', 'mode
238314

239315
exports[`additionalData option should work as a function ('sass-embedded', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
240316

317+
exports[`additionalData option should work as a function ('sass-embedded', 'modern-compiler' API, 'sass' syntax): css 1`] = `
318+
"body {
319+
color: hotpink;
320+
}"
321+
`;
322+
323+
exports[`additionalData option should work as a function ('sass-embedded', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
324+
325+
exports[`additionalData option should work as a function ('sass-embedded', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
326+
327+
exports[`additionalData option should work as a function ('sass-embedded', 'modern-compiler' API, 'scss' syntax): css 1`] = `
328+
"body {
329+
color: hotpink;
330+
}"
331+
`;
332+
333+
exports[`additionalData option should work as a function ('sass-embedded', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
334+
335+
exports[`additionalData option should work as a function ('sass-embedded', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
336+
241337
exports[`additionalData option should work as a string ('dart-sass', 'legacy' API, 'sass' syntax): css 1`] = `
242338
"body {
243339
color: hotpink;
@@ -278,6 +374,26 @@ exports[`additionalData option should work as a string ('dart-sass', 'modern' AP
278374

279375
exports[`additionalData option should work as a string ('dart-sass', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
280376

377+
exports[`additionalData option should work as a string ('dart-sass', 'modern-compiler' API, 'sass' syntax): css 1`] = `
378+
"body {
379+
color: hotpink;
380+
}"
381+
`;
382+
383+
exports[`additionalData option should work as a string ('dart-sass', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
384+
385+
exports[`additionalData option should work as a string ('dart-sass', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
386+
387+
exports[`additionalData option should work as a string ('dart-sass', 'modern-compiler' API, 'scss' syntax): css 1`] = `
388+
"body {
389+
color: hotpink;
390+
}"
391+
`;
392+
393+
exports[`additionalData option should work as a string ('dart-sass', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
394+
395+
exports[`additionalData option should work as a string ('dart-sass', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
396+
281397
exports[`additionalData option should work as a string ('node-sass', 'legacy' API, 'sass' syntax): css 1`] = `
282398
"body {
283399
color: hotpink; }
@@ -338,6 +454,26 @@ exports[`additionalData option should work as a string ('sass-embedded', 'modern
338454

339455
exports[`additionalData option should work as a string ('sass-embedded', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
340456

457+
exports[`additionalData option should work as a string ('sass-embedded', 'modern-compiler' API, 'sass' syntax): css 1`] = `
458+
"body {
459+
color: hotpink;
460+
}"
461+
`;
462+
463+
exports[`additionalData option should work as a string ('sass-embedded', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
464+
465+
exports[`additionalData option should work as a string ('sass-embedded', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
466+
467+
exports[`additionalData option should work as a string ('sass-embedded', 'modern-compiler' API, 'scss' syntax): css 1`] = `
468+
"body {
469+
color: hotpink;
470+
}"
471+
`;
472+
473+
exports[`additionalData option should work as a string ('sass-embedded', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
474+
475+
exports[`additionalData option should work as a string ('sass-embedded', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
476+
341477
exports[`additionalData option should work as an async function ('dart-sass', 'legacy' API, 'sass' syntax): css 1`] = `
342478
"body {
343479
color: hotpink;
@@ -378,6 +514,26 @@ exports[`additionalData option should work as an async function ('dart-sass', 'm
378514

379515
exports[`additionalData option should work as an async function ('dart-sass', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
380516

517+
exports[`additionalData option should work as an async function ('dart-sass', 'modern-compiler' API, 'sass' syntax): css 1`] = `
518+
"body {
519+
color: hotpink;
520+
}"
521+
`;
522+
523+
exports[`additionalData option should work as an async function ('dart-sass', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
524+
525+
exports[`additionalData option should work as an async function ('dart-sass', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
526+
527+
exports[`additionalData option should work as an async function ('dart-sass', 'modern-compiler' API, 'scss' syntax): css 1`] = `
528+
"body {
529+
color: hotpink;
530+
}"
531+
`;
532+
533+
exports[`additionalData option should work as an async function ('dart-sass', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
534+
535+
exports[`additionalData option should work as an async function ('dart-sass', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;
536+
381537
exports[`additionalData option should work as an async function ('node-sass', 'legacy' API, 'sass' syntax): css 1`] = `
382538
"body {
383539
color: hotpink; }
@@ -437,3 +593,23 @@ exports[`additionalData option should work as an async function ('sass-embedded'
437593
exports[`additionalData option should work as an async function ('sass-embedded', 'modern' API, 'scss' syntax): errors 1`] = `[]`;
438594

439595
exports[`additionalData option should work as an async function ('sass-embedded', 'modern' API, 'scss' syntax): warnings 1`] = `[]`;
596+
597+
exports[`additionalData option should work as an async function ('sass-embedded', 'modern-compiler' API, 'sass' syntax): css 1`] = `
598+
"body {
599+
color: hotpink;
600+
}"
601+
`;
602+
603+
exports[`additionalData option should work as an async function ('sass-embedded', 'modern-compiler' API, 'sass' syntax): errors 1`] = `[]`;
604+
605+
exports[`additionalData option should work as an async function ('sass-embedded', 'modern-compiler' API, 'sass' syntax): warnings 1`] = `[]`;
606+
607+
exports[`additionalData option should work as an async function ('sass-embedded', 'modern-compiler' API, 'scss' syntax): css 1`] = `
608+
"body {
609+
color: hotpink;
610+
}"
611+
`;
612+
613+
exports[`additionalData option should work as an async function ('sass-embedded', 'modern-compiler' API, 'scss' syntax): errors 1`] = `[]`;
614+
615+
exports[`additionalData option should work as an async function ('sass-embedded', 'modern-compiler' API, 'scss' syntax): warnings 1`] = `[]`;

0 commit comments

Comments
 (0)