sass-loader 12.4.0 → 12.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,19 @@ To begin, you'll need to install `sass-loader`:
27
27
  npm install sass-loader sass webpack --save-dev
28
28
  ```
29
29
 
30
- `sass-loader` requires you to install either [Dart Sass](https://github.com/sass/dart-sass) or [Node Sass](https://github.com/sass/node-sass) on your own (more documentation can be found below).
30
+ or
31
+
32
+ ```console
33
+ yarn add -D sass-loader sass webpack
34
+ ```
35
+
36
+ or
37
+
38
+ ```console
39
+ pnpm add -D sass-loader sass webpack
40
+ ```
41
+
42
+ `sass-loader` requires you to install either [Dart Sass](https://github.com/sass/dart-sass), [Node Sass](https://github.com/sass/node-sass) on your own (more documentation can be found below) or [Sass Embedded](https://github.com/sass/embedded-host-node).
31
43
 
32
44
  This allows you to control the versions of all your dependencies, and to choose which Sass implementation to use.
33
45
 
@@ -35,6 +47,8 @@ This allows you to control the versions of all your dependencies, and to choose
35
47
 
36
48
  > ⚠ [Node Sass](https://github.com/sass/node-sass) does not work with [Yarn PnP](https://classic.yarnpkg.com/en/docs/pnp/) feature and doesn't support [@use rule](https://sass-lang.com/documentation/at-rules/use).
37
49
 
50
+ > ⚠ [Sass Embedded](https://github.com/sass/embedded-host-node) is experimental and in `beta`, therefore some features may not work
51
+
38
52
  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.
39
53
 
40
54
  Then add the loader to your Webpack configuration. For example:
@@ -119,18 +133,21 @@ Thankfully there are a two solutions to this problem:
119
133
 
120
134
  ## Options
121
135
 
122
- | Name | Type | Default | Description |
123
- | :-------------------------------------------: | :------------------: | :-------------------------------------: | :---------------------------------------------------------------- |
124
- | **[`implementation`](#implementation)** | `{Object\|String}` | `sass` | Setup Sass implementation to use. |
125
- | **[`sassOptions`](#sassoptions)** | `{Object\|Function}` | defaults values for Sass implementation | Options for Sass. |
126
- | **[`sourceMap`](#sourcemap)** | `{Boolean}` | `compiler.devtool` | Enables/Disables generation of source maps. |
127
- | **[`additionalData`](#additionaldata)** | `{String\|Function}` | `undefined` | Prepends/Appends `Sass`/`SCSS` code before the actual entry file. |
128
- | **[`webpackImporter`](#webpackimporter)** | `{Boolean}` | `true` | Enables/Disables the default Webpack importer. |
129
- | **[`warnRuleAsWarning`](#warnruleaswarning)** | `{Boolean}` | `false` | Treats the `@warn` rule as a webpack warning. |
136
+ - **[`implementation`](#implementation)**
137
+ - **[`sassOptions`](#sassoptions)**
138
+ - **[`sourceMap`](#sourcemap)**
139
+ - **[`additionalData`](#additionaldata)**
140
+ - **[`webpackImporter`](#webpackimporter)**
141
+ - **[`warnRuleAsWarning`](#warnruleaswarning)**
130
142
 
131
143
  ### `implementation`
132
144
 
133
- Type: `Object | String`
145
+ Type:
146
+
147
+ ```ts
148
+ type implementation = object | string;
149
+ ```
150
+
134
151
  Default: `sass`
135
152
 
136
153
  The special `implementation` option determines which implementation of Sass to use.
@@ -169,7 +186,7 @@ In order to avoid this situation you can use the `implementation` option.
169
186
 
170
187
  The `implementation` options either accepts `sass` (`Dart Sass`) or `node-sass` as a module.
171
188
 
172
- #### Object
189
+ #### `object`
173
190
 
174
191
  For example, to use Dart Sass, you'd pass:
175
192
 
@@ -196,7 +213,7 @@ module.exports = {
196
213
  };
197
214
  ```
198
215
 
199
- #### String
216
+ #### `string`
200
217
 
201
218
  For example, to use Dart Sass, you'd pass:
202
219
 
@@ -302,7 +319,18 @@ module.exports = {
302
319
 
303
320
  ### `sassOptions`
304
321
 
305
- Type: `Object|Function`
322
+ Type:
323
+
324
+ ```ts
325
+ type sassOptions =
326
+ | import("sass").LegacyOptions<"async">
327
+ | ((
328
+ content: string | Buffer,
329
+ loaderContext: LoaderContext,
330
+ meta: any
331
+ ) => import("sass").LegacyOptions<"async">);
332
+ ```
333
+
306
334
  Default: defaults values for Sass implementation
307
335
 
308
336
  Options for [Dart Sass](http://sass-lang.com/dart-sass) or [Node Sass](https://github.com/sass/node-sass) implementation.
@@ -324,9 +352,9 @@ Please consult documentation before using them:
324
352
  - [Dart Sass documentation](https://github.com/sass/dart-sass#javascript-api) for all available `sass` options.
325
353
  - [Node Sass documentation](https://github.com/sass/node-sass/#options) for all available `node-sass` options.
326
354
 
327
- #### `Object`
355
+ #### `object`
328
356
 
329
- Use and object for the Sass implementation setup.
357
+ Use an object for the Sass implementation setup.
330
358
 
331
359
  **webpack.config.js**
332
360
 
@@ -355,7 +383,7 @@ module.exports = {
355
383
  };
356
384
  ```
357
385
 
358
- #### `Function`
386
+ #### `function`
359
387
 
360
388
  Allows to setup the Sass implementation by setting different options based on the loader context.
361
389
 
@@ -397,7 +425,12 @@ module.exports = {
397
425
 
398
426
  ### `sourceMap`
399
427
 
400
- Type: `Boolean`
428
+ Type:
429
+
430
+ ```ts
431
+ type sourceMap = boolean;
432
+ ```
433
+
401
434
  Default: depends on the `compiler.devtool` value
402
435
 
403
436
  Enables/Disables generation of source maps.
@@ -469,7 +502,14 @@ module.exports = {
469
502
 
470
503
  ### `additionalData`
471
504
 
472
- Type: `String|Function`
505
+ Type:
506
+
507
+ ```ts
508
+ type additionalData =
509
+ | string
510
+ | ((content: string | Buffer, loaderContext: LoaderContext) => string);
511
+ ```
512
+
473
513
  Default: `undefined`
474
514
 
475
515
  Prepends `Sass`/`SCSS` code before the actual entry file.
@@ -477,7 +517,7 @@ In this case, the `sass-loader` will not override the `data` option but just **p
477
517
 
478
518
  This is especially useful when some of your Sass variables depend on the environment:
479
519
 
480
- #### `String`
520
+ #### `string`
481
521
 
482
522
  ```js
483
523
  module.exports = {
@@ -501,7 +541,7 @@ module.exports = {
501
541
  };
502
542
  ```
503
543
 
504
- #### `Function`
544
+ #### `function`
505
545
 
506
546
  ##### Sync
507
547
 
@@ -573,7 +613,12 @@ module.exports = {
573
613
 
574
614
  ### `webpackImporter`
575
615
 
576
- Type: `Boolean`
616
+ Type:
617
+
618
+ ```ts
619
+ type webpackImporter = boolean;
620
+ ```
621
+
577
622
  Default: `true`
578
623
 
579
624
  Enables/Disables the default Webpack importer.
@@ -607,7 +652,12 @@ module.exports = {
607
652
 
608
653
  ### `warnRuleAsWarning`
609
654
 
610
- Type: `Boolean`
655
+ Type:
656
+
657
+ ```ts
658
+ type warnRuleAsWarning = boolean;
659
+ ```
660
+
611
661
  Default: `false`
612
662
 
613
663
  Treats the `@warn` rule as a webpack warning.
@@ -664,6 +714,49 @@ module.exports = {
664
714
  };
665
715
  ```
666
716
 
717
+ ### `api`
718
+
719
+ Type:
720
+
721
+ ```ts
722
+ type api = "legacy" | "modern";
723
+ ```
724
+
725
+ Default: `"legacy"`
726
+
727
+ Allows you to switch between `legacy` and `modern` API. You can find more information [here](https://sass-lang.com/documentation/js-api).
728
+
729
+ > ⚠ "modern" API is experimental, so some features may not work (known: built-in `importer` is not working and files with errors is not watching on initial run), you can follow this [here](https://github.com/webpack-contrib/sass-loader/issues/774).
730
+
731
+ > ⚠ The sass options are different for `modern` and `old` APIs. Please look at [docs](https://sass-lang.com/documentation/js-api) how to migrate on new options.
732
+
733
+ **webpack.config.js**
734
+
735
+ ```js
736
+ module.exports = {
737
+ module: {
738
+ rules: [
739
+ {
740
+ test: /\.s[ac]ss$/i,
741
+ use: [
742
+ "style-loader",
743
+ "css-loader",
744
+ {
745
+ loader: "sass-loader",
746
+ options: {
747
+ api: "modern",
748
+ sassOptions: {
749
+ // Your sass options
750
+ },
751
+ },
752
+ },
753
+ ],
754
+ },
755
+ ],
756
+ },
757
+ };
758
+ ```
759
+
667
760
  ## Examples
668
761
 
669
762
  ### Extracts CSS into separate files
package/dist/SassError.js CHANGED
@@ -11,12 +11,16 @@ class SassError extends Error {
11
11
  this.name = "SassError"; // TODO remove me in the next major release
12
12
 
13
13
  this.originalSassError = sassError;
14
- this.loc = {
15
- line: sassError.line,
16
- column: sassError.column
17
- }; // Keep original error if `sassError.formatted` is unavailable
18
14
 
19
- this.message = `${this.name}: ${this.originalSassError.message}`;
15
+ if (typeof sassError.line !== "undefined" || typeof sassError.column !== "undefined") {
16
+ this.loc = {
17
+ line: sassError.line,
18
+ column: sassError.column
19
+ };
20
+ } // Keep original error if `sassError.formatted` is unavailable
21
+
22
+
23
+ this.message = `${this.name}: ${typeof this.originalSassError.message !== "undefined" ? this.originalSassError.message : this.originalSassError}`;
20
24
 
21
25
  if (this.originalSassError.formatted) {
22
26
  this.message = `${this.name}: ${this.originalSassError.formatted.replace(/^Error: /, "")}`; // Instruct webpack to hide the JS stack from the console.
package/dist/index.js CHANGED
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
+ var _url = _interopRequireDefault(require("url"));
9
+
8
10
  var _path = _interopRequireDefault(require("path"));
9
11
 
10
12
  var _options = _interopRequireDefault(require("./options.json"));
@@ -36,31 +38,57 @@ async function loader(content) {
36
38
  const shouldUseWebpackImporter = typeof options.webpackImporter === "boolean" ? options.webpackImporter : true;
37
39
 
38
40
  if (shouldUseWebpackImporter) {
39
- const {
40
- includePaths
41
- } = sassOptions;
42
- sassOptions.importer.push((0, _utils.getWebpackImporter)(this, implementation, includePaths));
41
+ const isModernAPI = options.api === "modern";
42
+
43
+ if (!isModernAPI) {
44
+ const {
45
+ includePaths
46
+ } = sassOptions;
47
+ sassOptions.importer.push((0, _utils.getWebpackImporter)(this, implementation, includePaths));
48
+ } else {
49
+ sassOptions.importers.push((0, _utils.getModernWebpackImporter)(this, implementation));
50
+ }
43
51
  }
44
52
 
45
- const render = (0, _utils.getRenderFunctionFromSassImplementation)(implementation);
46
- render(sassOptions, (error, result) => {
47
- if (error) {
48
- // There are situations when the `file` property do not exist
49
- if (error.file) {
50
- // `node-sass` returns POSIX paths
51
- this.addDependency(_path.default.normalize(error.file));
52
- }
53
-
54
- callback(new _SassError.default(error));
55
- return;
53
+ const compile = (0, _utils.getCompileFn)(implementation, options);
54
+ let result;
55
+
56
+ try {
57
+ result = await compile(sassOptions, options);
58
+ } catch (error) {
59
+ // There are situations when the `file`/`span.url` property do not exist
60
+ // Modern API
61
+ if (error.span && typeof error.span.url !== "undefined") {
62
+ this.addDependency(_url.default.fileURLToPath(error.span.url));
63
+ } // Legacy API
64
+ else if (typeof error.file !== "undefined") {
65
+ // `node-sass` returns POSIX paths
66
+ this.addDependency(_path.default.normalize(error.file));
56
67
  }
57
68
 
58
- let map = result.map ? JSON.parse(result.map) : null; // Modify source paths only for webpack, otherwise we do nothing
69
+ callback(new _SassError.default(error));
70
+ return;
71
+ }
72
+
73
+ let map = // Modern API, then legacy API
74
+ result.sourceMap ? result.sourceMap : result.map ? JSON.parse(result.map) : null; // Modify source paths only for webpack, otherwise we do nothing
75
+
76
+ if (map && useSourceMap) {
77
+ map = (0, _utils.normalizeSourceMap)(map, this.rootContext);
78
+ } // Modern API
59
79
 
60
- if (map && useSourceMap) {
61
- map = (0, _utils.normalizeSourceMap)(map, this.rootContext);
62
- }
63
80
 
81
+ if (typeof result.loadedUrls !== "undefined") {
82
+ result.loadedUrls.forEach(includedFile => {
83
+ const normalizedIncludedFile = _url.default.fileURLToPath(includedFile); // Custom `importer` can return only `contents` so includedFile will be relative
84
+
85
+
86
+ if (_path.default.isAbsolute(normalizedIncludedFile)) {
87
+ this.addDependency(normalizedIncludedFile);
88
+ }
89
+ });
90
+ } // Legacy API
91
+ else if (typeof result.stats !== "undefined" && typeof result.stats.includedFiles !== "undefined") {
64
92
  result.stats.includedFiles.forEach(includedFile => {
65
93
  const normalizedIncludedFile = _path.default.normalize(includedFile); // Custom `importer` can return only `contents` so includedFile will be relative
66
94
 
@@ -69,8 +97,9 @@ async function loader(content) {
69
97
  this.addDependency(normalizedIncludedFile);
70
98
  }
71
99
  });
72
- callback(null, result.css.toString(), map);
73
- });
100
+ }
101
+
102
+ callback(null, result.css.toString(), map);
74
103
  }
75
104
 
76
105
  var _default = loader;
package/dist/options.json CHANGED
@@ -14,6 +14,11 @@
14
14
  }
15
15
  ]
16
16
  },
17
+ "api": {
18
+ "description": "Switch between old and modern API for `sass` (`Dart Sass`) and `Sass Embedded` implementations.",
19
+ "link": "https://github.com/webpack-contrib/sass-loader#sassoptions",
20
+ "enum": ["legacy", "modern"]
21
+ },
17
22
  "sassOptions": {
18
23
  "description": "Options for `node-sass` or `sass` (`Dart Sass`) implementation.",
19
24
  "link": "https://github.com/webpack-contrib/sass-loader#sassoptions",
package/dist/utils.js CHANGED
@@ -3,7 +3,8 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getRenderFunctionFromSassImplementation = getRenderFunctionFromSassImplementation;
6
+ exports.getCompileFn = getCompileFn;
7
+ exports.getModernWebpackImporter = getModernWebpackImporter;
7
8
  exports.getSassImplementation = getSassImplementation;
8
9
  exports.getSassOptions = getSassOptions;
9
10
  exports.getWebpackImporter = getWebpackImporter;
@@ -93,6 +94,9 @@ function getSassImplementation(loaderContext, implementation) {
93
94
  } else if (implementationName === "node-sass") {
94
95
  // eslint-disable-next-line consistent-return
95
96
  return resolvedImplementation;
97
+ } else if (implementationName === "sass-embedded") {
98
+ // eslint-disable-next-line consistent-return
99
+ return resolvedImplementation;
96
100
  }
97
101
 
98
102
  loaderContext.emitError(new Error(`Unknown Sass implementation "${implementationName}".`));
@@ -135,73 +139,8 @@ function isSupportedFibers() {
135
139
  async function getSassOptions(loaderContext, loaderOptions, content, implementation, useSourceMap) {
136
140
  const options = (0, _full.klona)(loaderOptions.sassOptions ? typeof loaderOptions.sassOptions === "function" ? loaderOptions.sassOptions(loaderContext) || {} : loaderOptions.sassOptions : {});
137
141
  const isDartSass = implementation.info.includes("dart-sass");
138
-
139
- if (isDartSass && isSupportedFibers()) {
140
- const shouldTryToResolveFibers = !options.fiber && options.fiber !== false;
141
-
142
- if (shouldTryToResolveFibers) {
143
- let fibers;
144
-
145
- try {
146
- fibers = require.resolve("fibers");
147
- } catch (_error) {// Nothing
148
- }
149
-
150
- if (fibers) {
151
- // eslint-disable-next-line global-require, import/no-dynamic-require
152
- options.fiber = require(fibers);
153
- }
154
- } else if (options.fiber === false) {
155
- // Don't pass the `fiber` option for `sass` (`Dart Sass`)
156
- delete options.fiber;
157
- }
158
- } else {
159
- // Don't pass the `fiber` option for `node-sass`
160
- delete options.fiber;
161
- }
162
-
163
- options.file = loaderContext.resourcePath;
164
- options.data = loaderOptions.additionalData ? typeof loaderOptions.additionalData === "function" ? await loaderOptions.additionalData(content, loaderContext) : `${loaderOptions.additionalData}\n${content}` : content; // opt.outputStyle
165
-
166
- if (!options.outputStyle && isProductionLikeMode(loaderContext)) {
167
- options.outputStyle = "compressed";
168
- }
169
-
170
- if (useSourceMap) {
171
- // Deliberately overriding the sourceMap option here.
172
- // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string.
173
- // In case it is a string, options.sourceMap should be a path where the source map is written.
174
- // But since we're using the data option, the source map will not actually be written, but
175
- // all paths in sourceMap.sources will be relative to that path.
176
- // Pretty complicated... :(
177
- options.sourceMap = true;
178
- options.outFile = _path.default.join(loaderContext.rootContext, "style.css.map");
179
- options.sourceMapContents = true;
180
- options.omitSourceMapUrl = true;
181
- options.sourceMapEmbed = false;
182
- }
183
-
184
- const {
185
- resourcePath
186
- } = loaderContext;
187
-
188
- const ext = _path.default.extname(resourcePath); // If we are compiling sass and indentedSyntax isn't set, automatically set it.
189
-
190
-
191
- if (ext && ext.toLowerCase() === ".sass" && typeof options.indentedSyntax === "undefined") {
192
- options.indentedSyntax = true;
193
- } else {
194
- options.indentedSyntax = Boolean(options.indentedSyntax);
195
- } // Allow passing custom importers to `sass`/`node-sass`. Accepts `Function` or an array of `Function`s.
196
-
197
-
198
- options.importer = options.importer ? proxyCustomImporters(Array.isArray(options.importer) ? options.importer : [options.importer], loaderContext) : [];
199
- options.includePaths = [].concat(process.cwd()).concat( // We use `includePaths` in context for resolver, so it should be always absolute
200
- (options.includePaths || []).map(includePath => _path.default.isAbsolute(includePath) ? includePath : _path.default.join(process.cwd(), includePath))).concat(process.env.SASS_PATH ? process.env.SASS_PATH.split(process.platform === "win32" ? ";" : ":") : []);
201
-
202
- if (typeof options.charset === "undefined") {
203
- options.charset = true;
204
- }
142
+ const isModernAPI = loaderOptions.api === "modern";
143
+ options.data = loaderOptions.additionalData ? typeof loaderOptions.additionalData === "function" ? await loaderOptions.additionalData(content, loaderContext) : `${loaderOptions.additionalData}\n${content}` : content;
205
144
 
206
145
  if (!options.logger) {
207
146
  // TODO set me to `true` by default in the next major release
@@ -249,6 +188,100 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
249
188
  };
250
189
  }
251
190
 
191
+ const {
192
+ resourcePath
193
+ } = loaderContext;
194
+
195
+ if (isModernAPI) {
196
+ options.url = _url.default.pathToFileURL(resourcePath); // opt.outputStyle
197
+
198
+ if (!options.style && isProductionLikeMode(loaderContext)) {
199
+ options.style = "compressed";
200
+ }
201
+
202
+ if (useSourceMap) {
203
+ options.sourceMap = true;
204
+ } // If we are compiling sass and indentedSyntax isn't set, automatically set it.
205
+
206
+
207
+ if (typeof options.syntax === "undefined") {
208
+ const ext = _path.default.extname(resourcePath);
209
+
210
+ if (ext && ext.toLowerCase() === ".scss") {
211
+ options.syntax = "scss";
212
+ } else if (ext && ext.toLowerCase() === ".sass") {
213
+ options.syntax = "indented";
214
+ } else if (ext && ext.toLowerCase() === ".css") {
215
+ options.syntax = "css";
216
+ }
217
+ }
218
+
219
+ options.importers = options.importers ? proxyCustomImporters(Array.isArray(options.importers) ? options.importers : [options.importers], loaderContext) : [];
220
+ } else {
221
+ options.file = resourcePath;
222
+
223
+ if (isDartSass && isSupportedFibers()) {
224
+ const shouldTryToResolveFibers = !options.fiber && options.fiber !== false;
225
+
226
+ if (shouldTryToResolveFibers) {
227
+ let fibers;
228
+
229
+ try {
230
+ fibers = require.resolve("fibers");
231
+ } catch (_error) {// Nothing
232
+ }
233
+
234
+ if (fibers) {
235
+ // eslint-disable-next-line global-require, import/no-dynamic-require
236
+ options.fiber = require(fibers);
237
+ }
238
+ } else if (options.fiber === false) {
239
+ // Don't pass the `fiber` option for `sass` (`Dart Sass`)
240
+ delete options.fiber;
241
+ }
242
+ } else {
243
+ // Don't pass the `fiber` option for `node-sass`
244
+ delete options.fiber;
245
+ } // opt.outputStyle
246
+
247
+
248
+ if (!options.outputStyle && isProductionLikeMode(loaderContext)) {
249
+ options.outputStyle = "compressed";
250
+ }
251
+
252
+ if (useSourceMap) {
253
+ // Deliberately overriding the sourceMap option here.
254
+ // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string.
255
+ // In case it is a string, options.sourceMap should be a path where the source map is written.
256
+ // But since we're using the data option, the source map will not actually be written, but
257
+ // all paths in sourceMap.sources will be relative to that path.
258
+ // Pretty complicated... :(
259
+ options.sourceMap = true;
260
+ options.outFile = _path.default.join(loaderContext.rootContext, "style.css.map");
261
+ options.sourceMapContents = true;
262
+ options.omitSourceMapUrl = true;
263
+ options.sourceMapEmbed = false;
264
+ }
265
+
266
+ const ext = _path.default.extname(resourcePath); // If we are compiling sass and indentedSyntax isn't set, automatically set it.
267
+
268
+
269
+ if (ext && ext.toLowerCase() === ".sass" && typeof options.indentedSyntax === "undefined") {
270
+ options.indentedSyntax = true;
271
+ } else {
272
+ options.indentedSyntax = Boolean(options.indentedSyntax);
273
+ } // Allow passing custom importers to `sass`/`node-sass`. Accepts `Function` or an array of `Function`s.
274
+
275
+
276
+ options.importer = options.importer ? proxyCustomImporters(Array.isArray(options.importer) ? options.importer : [options.importer], loaderContext) : [];
277
+ options.includePaths = [].concat(process.cwd()).concat( // We use `includePaths` in context for resolver, so it should be always absolute
278
+ (options.includePaths || []).map(includePath => _path.default.isAbsolute(includePath) ? includePath : _path.default.join(process.cwd(), includePath))).concat(process.env.SASS_PATH ? process.env.SASS_PATH.split(process.platform === "win32" ? ";" : ":") : []);
279
+
280
+ if (typeof options.charset === "undefined") {
281
+ options.charset = true;
282
+ }
283
+ }
284
+
252
285
  return options;
253
286
  }
254
287
 
@@ -330,6 +363,40 @@ function promiseResolve(callbackResolve) {
330
363
  });
331
364
  }
332
365
 
366
+ async function startResolving(resolutionMap) {
367
+ if (resolutionMap.length === 0) {
368
+ return Promise.reject();
369
+ }
370
+
371
+ const [{
372
+ possibleRequests
373
+ }] = resolutionMap;
374
+
375
+ if (possibleRequests.length === 0) {
376
+ return Promise.reject();
377
+ }
378
+
379
+ const [{
380
+ resolve,
381
+ context
382
+ }] = resolutionMap;
383
+
384
+ try {
385
+ return await resolve(context, possibleRequests[0]);
386
+ } catch (_ignoreError) {
387
+ const [, ...tailResult] = possibleRequests;
388
+
389
+ if (tailResult.length === 0) {
390
+ const [, ...tailResolutionMap] = resolutionMap;
391
+ return startResolving(tailResolutionMap);
392
+ } // eslint-disable-next-line no-param-reassign
393
+
394
+
395
+ resolutionMap[0].possibleRequests = tailResult;
396
+ return startResolving(resolutionMap);
397
+ }
398
+ }
399
+
333
400
  const IS_SPECIAL_MODULE_IMPORT = /^~[^/]+$/; // `[drive_letter]:\` + `\\[server]\[sharename]\`
334
401
 
335
402
  const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
@@ -352,41 +419,7 @@ const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
352
419
  */
353
420
 
354
421
  function getWebpackResolver(resolverFactory, implementation, includePaths = []) {
355
- async function startResolving(resolutionMap) {
356
- if (resolutionMap.length === 0) {
357
- return Promise.reject();
358
- }
359
-
360
- const [{
361
- possibleRequests
362
- }] = resolutionMap;
363
-
364
- if (possibleRequests.length === 0) {
365
- return Promise.reject();
366
- }
367
-
368
- const [{
369
- resolve,
370
- context
371
- }] = resolutionMap;
372
-
373
- try {
374
- return await resolve(context, possibleRequests[0]);
375
- } catch (_ignoreError) {
376
- const [, ...tailResult] = possibleRequests;
377
-
378
- if (tailResult.length === 0) {
379
- const [, ...tailResolutionMap] = resolutionMap;
380
- return startResolving(tailResolutionMap);
381
- } // eslint-disable-next-line no-param-reassign
382
-
383
-
384
- resolutionMap[0].possibleRequests = tailResult;
385
- return startResolving(resolutionMap);
386
- }
387
- }
388
-
389
- const isDartSass = implementation.info.includes("dart-sass"); // We only have one difference with the built-in sass resolution logic and out resolution logic:
422
+ const isDartSass = implementation && implementation.info.includes("dart-sass"); // We only have one difference with the built-in sass resolution logic and out resolution logic:
390
423
  // First, we look at the files starting with `_`, then without `_` (i.e. `_name.sass`, `_name.scss`, `_name.css`, `name.sass`, `name.scss`, `name.css`),
391
424
  // although `sass` look together by extensions (i.e. `_name.sass`/`name.sass`/`_name.scss`/`name.scss`/`_name.css`/`name.css`).
392
425
  // It shouldn't be a problem because `sass` throw errors:
@@ -510,6 +543,18 @@ function getWebpackResolver(resolverFactory, implementation, includePaths = [])
510
543
 
511
544
  const MATCH_CSS = /\.css$/i;
512
545
 
546
+ function getModernWebpackImporter() {
547
+ return {
548
+ async canonicalize() {
549
+ return null;
550
+ },
551
+
552
+ load() {// TODO implement
553
+ }
554
+
555
+ };
556
+ }
557
+
513
558
  function getWebpackImporter(loaderContext, implementation, includePaths) {
514
559
  const resolve = getWebpackResolver(loaderContext.getResolve, implementation, includePaths);
515
560
  return function importer(originalUrl, prev, done) {
@@ -539,14 +584,38 @@ let nodeSassJobQueue = null;
539
584
  * Verifies that the implementation and version of Sass is supported by this loader.
540
585
  *
541
586
  * @param {Object} implementation
587
+ * @param {Object} options
542
588
  * @returns {Function}
543
589
  */
544
590
 
545
- function getRenderFunctionFromSassImplementation(implementation) {
546
- const isDartSass = implementation.info.includes("dart-sass");
591
+ function getCompileFn(implementation, options) {
592
+ const isNewSass = implementation.info.includes("dart-sass") || implementation.info.includes("sass-embedded");
593
+
594
+ if (isNewSass) {
595
+ if (options.api === "modern") {
596
+ return sassOptions => {
597
+ const {
598
+ data,
599
+ ...rest
600
+ } = sassOptions;
601
+ return implementation.compileStringAsync(data, rest);
602
+ };
603
+ }
547
604
 
548
- if (isDartSass) {
549
- return implementation.render.bind(implementation);
605
+ return sassOptions => new Promise((resolve, reject) => {
606
+ implementation.render(sassOptions, (error, result) => {
607
+ if (error) {
608
+ reject(error);
609
+ return;
610
+ }
611
+
612
+ resolve(result);
613
+ });
614
+ });
615
+ }
616
+
617
+ if (options.api === "modern") {
618
+ throw new Error("Modern API is not supported for 'node-sass'");
550
619
  } // There is an issue with node-sass when async custom importers are used
551
620
  // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
552
621
  // We need to use a job queue to make sure that one thread is always available to the UV lib
@@ -557,7 +626,16 @@ function getRenderFunctionFromSassImplementation(implementation) {
557
626
  nodeSassJobQueue = _neoAsync.default.queue(implementation.render.bind(implementation), threadPoolSize - 1);
558
627
  }
559
628
 
560
- return nodeSassJobQueue.push.bind(nodeSassJobQueue);
629
+ return sassOptions => new Promise((resolve, reject) => {
630
+ nodeSassJobQueue.push.bind(nodeSassJobQueue)(sassOptions, (error, result) => {
631
+ if (error) {
632
+ reject(error);
633
+ return;
634
+ }
635
+
636
+ resolve(result);
637
+ });
638
+ });
561
639
  }
562
640
 
563
641
  const ABSOLUTE_SCHEME = /^[A-Za-z0-9+\-.]+:/;
@@ -587,7 +665,10 @@ function normalizeSourceMap(map, rootContext) {
587
665
  // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
588
666
  // eslint-disable-next-line no-param-reassign
589
667
 
590
- delete newMap.file; // eslint-disable-next-line no-param-reassign
668
+ if (typeof newMap.file !== "undefined") {
669
+ delete newMap.file;
670
+ } // eslint-disable-next-line no-param-reassign
671
+
591
672
 
592
673
  newMap.sourceRoot = ""; // node-sass returns POSIX paths, that's why we need to transform them back to native paths.
593
674
  // This fixes an error on windows where the source-map module cannot resolve the source maps.
@@ -595,9 +676,11 @@ function normalizeSourceMap(map, rootContext) {
595
676
  // eslint-disable-next-line no-param-reassign
596
677
 
597
678
  newMap.sources = newMap.sources.map(source => {
598
- const sourceType = getURLType(source); // Do no touch `scheme-relative`, `path-absolute` and `absolute` types
679
+ const sourceType = getURLType(source); // Do no touch `scheme-relative`, `path-absolute` and `absolute` types (except `file:`)
599
680
 
600
- if (sourceType === "path-relative") {
681
+ if (sourceType === "absolute" && /^file:/i.test(source)) {
682
+ return _url.default.fileURLToPath(source);
683
+ } else if (sourceType === "path-relative") {
601
684
  return _path.default.resolve(rootContext, _path.default.normalize(source));
602
685
  }
603
686
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sass-loader",
3
- "version": "12.4.0",
3
+ "version": "12.5.0",
4
4
  "description": "Sass loader for webpack",
5
5
  "license": "MIT",
6
6
  "repository": "webpack-contrib/sass-loader",
@@ -41,6 +41,7 @@
41
41
  "fibers": ">= 3.1.0",
42
42
  "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
43
43
  "sass": "^1.3.0",
44
+ "sass-embedded": "*",
44
45
  "webpack": "^5.0.0"
45
46
  },
46
47
  "peerDependenciesMeta": {
@@ -50,6 +51,9 @@
50
51
  "sass": {
51
52
  "optional": true
52
53
  },
54
+ "sass-embedded": {
55
+ "optional": true
56
+ },
53
57
  "fibers": {
54
58
  "optional": true
55
59
  }
@@ -59,41 +63,43 @@
59
63
  "neo-async": "^2.6.2"
60
64
  },
61
65
  "devDependencies": {
62
- "@babel/cli": "^7.14.5",
63
- "@babel/core": "^7.14.6",
64
- "@babel/preset-env": "^7.14.7",
65
- "@commitlint/cli": "^15.0.0",
66
- "@commitlint/config-conventional": "^15.0.0",
66
+ "@babel/cli": "^7.17.0",
67
+ "@babel/core": "^7.17.0",
68
+ "@babel/preset-env": "^7.16.11",
69
+ "@commitlint/cli": "^16.2.1",
70
+ "@commitlint/config-conventional": "^16.2.1",
67
71
  "@webpack-contrib/eslint-config-webpack": "^3.0.0",
68
- "babel-jest": "^27.0.6",
72
+ "babel-jest": "^27.5.0",
69
73
  "bootstrap-sass": "^3.4.1",
70
74
  "bootstrap-v4": "npm:bootstrap@^4.5.3",
71
75
  "bootstrap-v5": "npm:bootstrap@^5.0.1",
72
76
  "cross-env": "^7.0.3",
73
- "css-loader": "^6.2.0",
77
+ "css-loader": "^6.6.0",
74
78
  "del": "^6.0.0",
75
79
  "del-cli": "^4.0.1",
76
80
  "enhanced-resolve": "^5.8.2",
77
- "eslint": "^8.1.0",
81
+ "eslint": "^8.8.0",
78
82
  "eslint-config-prettier": "^8.3.0",
79
- "eslint-plugin-import": "^2.23.3",
80
- "fibers": "^5.0.0",
83
+ "eslint-plugin-import": "^2.25.4",
84
+ "fibers": "^5.0.1",
81
85
  "file-loader": "^6.2.0",
82
86
  "foundation-sites": "^6.6.3",
83
87
  "husky": "^7.0.1",
84
- "jest": "^27.0.6",
85
- "lint-staged": "^12.1.2",
88
+ "jest": "^27.5.0",
89
+ "jest-environment-node-single-context": "^27.2.0",
90
+ "lint-staged": "^12.3.3",
86
91
  "material-components-web": "^8.0.0",
87
- "memfs": "^3.2.2",
88
- "node-sass": "^7.0.0",
92
+ "memfs": "^3.4.1",
93
+ "node-sass": "^7.0.1",
89
94
  "node-sass-glob-importer": "^5.3.2",
90
95
  "npm-run-all": "^4.1.5",
91
96
  "prettier": "^2.3.2",
92
- "sass": "^1.35.2",
97
+ "sass": "^1.49.7",
98
+ "sass-embedded": "^1.49.7",
93
99
  "semver": "^7.3.5",
94
100
  "standard-version": "^9.3.1",
95
101
  "style-loader": "^3.2.1",
96
- "webpack": "^5.45.1"
102
+ "webpack": "^5.68.0"
97
103
  },
98
104
  "keywords": [
99
105
  "sass",