sass-loader 12.4.0 → 13.0.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.
@@ -321,12 +349,12 @@ There is a slight difference between the `sass` (`dart-sass`) and `node-sass` op
321
349
 
322
350
  Please consult documentation before using them:
323
351
 
324
- - [Dart Sass documentation](https://github.com/sass/dart-sass#javascript-api) for all available `sass` options.
352
+ - [Dart Sass documentation](https://sass-lang.com/documentation/js-api/interfaces/Options) 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
@@ -8,18 +8,20 @@ exports.default = void 0;
8
8
  class SassError extends Error {
9
9
  constructor(sassError) {
10
10
  super();
11
- this.name = "SassError"; // TODO remove me in the next major release
11
+ this.name = "SassError";
12
12
 
13
- this.originalSassError = sassError;
14
- this.loc = {
15
- line: sassError.line,
16
- column: sassError.column
17
- }; // Keep original error if `sassError.formatted` is unavailable
13
+ if (typeof sassError.line !== "undefined" || typeof sassError.column !== "undefined") {
14
+ this.loc = {
15
+ line: sassError.line,
16
+ column: sassError.column
17
+ };
18
+ } // Keep original error if `sassError.formatted` is unavailable
18
19
 
19
- this.message = `${this.name}: ${this.originalSassError.message}`;
20
20
 
21
- if (this.originalSassError.formatted) {
22
- this.message = `${this.name}: ${this.originalSassError.formatted.replace(/^Error: /, "")}`; // Instruct webpack to hide the JS stack from the console.
21
+ this.message = `${this.name}: ${typeof sassError.message !== "undefined" ? sassError.message : sassError}`;
22
+
23
+ if (sassError.formatted) {
24
+ this.message = `${this.name}: ${sassError.formatted.replace(/^Error: /, "")}`; // Instruct webpack to hide the JS stack from the console.
23
25
  // Usually you're only interested in the SASS stack in this case.
24
26
 
25
27
  this.hideStack = true;
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;
@@ -28,13 +29,19 @@ function getDefaultSassImplementation() {
28
29
 
29
30
  try {
30
31
  require.resolve("sass");
31
- } catch (error) {
32
+ } catch (ignoreError) {
32
33
  try {
33
34
  require.resolve("node-sass");
34
35
 
35
36
  sassImplPkg = "node-sass";
36
- } catch (ignoreError) {
37
- sassImplPkg = "sass";
37
+ } catch (_ignoreError) {
38
+ try {
39
+ require.resolve("sass-embedded");
40
+
41
+ sassImplPkg = "sass-embedded";
42
+ } catch (__ignoreError) {
43
+ sassImplPkg = "sass";
44
+ }
38
45
  }
39
46
  } // eslint-disable-next-line import/no-dynamic-require, global-require
40
47
 
@@ -93,6 +100,9 @@ function getSassImplementation(loaderContext, implementation) {
93
100
  } else if (implementationName === "node-sass") {
94
101
  // eslint-disable-next-line consistent-return
95
102
  return resolvedImplementation;
103
+ } else if (implementationName === "sass-embedded") {
104
+ // eslint-disable-next-line consistent-return
105
+ return resolvedImplementation;
96
106
  }
97
107
 
98
108
  loaderContext.emitError(new Error(`Unknown Sass implementation "${implementationName}".`));
@@ -135,77 +145,11 @@ function isSupportedFibers() {
135
145
  async function getSassOptions(loaderContext, loaderOptions, content, implementation, useSourceMap) {
136
146
  const options = (0, _full.klona)(loaderOptions.sassOptions ? typeof loaderOptions.sassOptions === "function" ? loaderOptions.sassOptions(loaderContext) || {} : loaderOptions.sassOptions : {});
137
147
  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
- }
148
+ const isModernAPI = loaderOptions.api === "modern";
149
+ options.data = loaderOptions.additionalData ? typeof loaderOptions.additionalData === "function" ? await loaderOptions.additionalData(content, loaderContext) : `${loaderOptions.additionalData}\n${content}` : content;
205
150
 
206
151
  if (!options.logger) {
207
- // TODO set me to `true` by default in the next major release
208
- const needEmitWarning = loaderOptions.warnRuleAsWarning === true;
152
+ const needEmitWarning = loaderOptions.warnRuleAsWarning !== false;
209
153
  const logger = loaderContext.getLogger("sass-loader");
210
154
 
211
155
  const formatSpan = span => `${span.url || "-"}:${span.start.line}:${span.start.column}: `;
@@ -249,6 +193,100 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
249
193
  };
250
194
  }
251
195
 
196
+ const {
197
+ resourcePath
198
+ } = loaderContext;
199
+
200
+ if (isModernAPI) {
201
+ options.url = _url.default.pathToFileURL(resourcePath); // opt.outputStyle
202
+
203
+ if (!options.style && isProductionLikeMode(loaderContext)) {
204
+ options.style = "compressed";
205
+ }
206
+
207
+ if (useSourceMap) {
208
+ options.sourceMap = true;
209
+ } // If we are compiling sass and indentedSyntax isn't set, automatically set it.
210
+
211
+
212
+ if (typeof options.syntax === "undefined") {
213
+ const ext = _path.default.extname(resourcePath);
214
+
215
+ if (ext && ext.toLowerCase() === ".scss") {
216
+ options.syntax = "scss";
217
+ } else if (ext && ext.toLowerCase() === ".sass") {
218
+ options.syntax = "indented";
219
+ } else if (ext && ext.toLowerCase() === ".css") {
220
+ options.syntax = "css";
221
+ }
222
+ }
223
+
224
+ options.importers = options.importers ? Array.isArray(options.importers) ? options.importers : [options.importers] : [];
225
+ } else {
226
+ options.file = resourcePath;
227
+
228
+ if (isDartSass && isSupportedFibers()) {
229
+ const shouldTryToResolveFibers = !options.fiber && options.fiber !== false;
230
+
231
+ if (shouldTryToResolveFibers) {
232
+ let fibers;
233
+
234
+ try {
235
+ fibers = require.resolve("fibers");
236
+ } catch (_error) {// Nothing
237
+ }
238
+
239
+ if (fibers) {
240
+ // eslint-disable-next-line global-require, import/no-dynamic-require
241
+ options.fiber = require(fibers);
242
+ }
243
+ } else if (options.fiber === false) {
244
+ // Don't pass the `fiber` option for `sass` (`Dart Sass`)
245
+ delete options.fiber;
246
+ }
247
+ } else {
248
+ // Don't pass the `fiber` option for `node-sass`
249
+ delete options.fiber;
250
+ } // opt.outputStyle
251
+
252
+
253
+ if (!options.outputStyle && isProductionLikeMode(loaderContext)) {
254
+ options.outputStyle = "compressed";
255
+ }
256
+
257
+ if (useSourceMap) {
258
+ // Deliberately overriding the sourceMap option here.
259
+ // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string.
260
+ // In case it is a string, options.sourceMap should be a path where the source map is written.
261
+ // But since we're using the data option, the source map will not actually be written, but
262
+ // all paths in sourceMap.sources will be relative to that path.
263
+ // Pretty complicated... :(
264
+ options.sourceMap = true;
265
+ options.outFile = _path.default.join(loaderContext.rootContext, "style.css.map");
266
+ options.sourceMapContents = true;
267
+ options.omitSourceMapUrl = true;
268
+ options.sourceMapEmbed = false;
269
+ }
270
+
271
+ const ext = _path.default.extname(resourcePath); // If we are compiling sass and indentedSyntax isn't set, automatically set it.
272
+
273
+
274
+ if (ext && ext.toLowerCase() === ".sass" && typeof options.indentedSyntax === "undefined") {
275
+ options.indentedSyntax = true;
276
+ } else {
277
+ options.indentedSyntax = Boolean(options.indentedSyntax);
278
+ } // Allow passing custom importers to `sass`/`node-sass`. Accepts `Function` or an array of `Function`s.
279
+
280
+
281
+ options.importer = options.importer ? proxyCustomImporters(Array.isArray(options.importer) ? options.importer : [options.importer], loaderContext) : [];
282
+ options.includePaths = [].concat(process.cwd()).concat( // We use `includePaths` in context for resolver, so it should be always absolute
283
+ (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" ? ";" : ":") : []);
284
+
285
+ if (typeof options.charset === "undefined") {
286
+ options.charset = true;
287
+ }
288
+ }
289
+
252
290
  return options;
253
291
  }
254
292
 
@@ -330,6 +368,40 @@ function promiseResolve(callbackResolve) {
330
368
  });
331
369
  }
332
370
 
371
+ async function startResolving(resolutionMap) {
372
+ if (resolutionMap.length === 0) {
373
+ return Promise.reject();
374
+ }
375
+
376
+ const [{
377
+ possibleRequests
378
+ }] = resolutionMap;
379
+
380
+ if (possibleRequests.length === 0) {
381
+ return Promise.reject();
382
+ }
383
+
384
+ const [{
385
+ resolve,
386
+ context
387
+ }] = resolutionMap;
388
+
389
+ try {
390
+ return await resolve(context, possibleRequests[0]);
391
+ } catch (_ignoreError) {
392
+ const [, ...tailResult] = possibleRequests;
393
+
394
+ if (tailResult.length === 0) {
395
+ const [, ...tailResolutionMap] = resolutionMap;
396
+ return startResolving(tailResolutionMap);
397
+ } // eslint-disable-next-line no-param-reassign
398
+
399
+
400
+ resolutionMap[0].possibleRequests = tailResult;
401
+ return startResolving(resolutionMap);
402
+ }
403
+ }
404
+
333
405
  const IS_SPECIAL_MODULE_IMPORT = /^~[^/]+$/; // `[drive_letter]:\` + `\\[server]\[sharename]\`
334
406
 
335
407
  const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
@@ -352,41 +424,7 @@ const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
352
424
  */
353
425
 
354
426
  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:
427
+ 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
428
  // 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
429
  // although `sass` look together by extensions (i.e. `_name.sass`/`name.sass`/`_name.scss`/`name.scss`/`_name.css`/`name.css`).
392
430
  // It shouldn't be a problem because `sass` throw errors:
@@ -510,6 +548,18 @@ function getWebpackResolver(resolverFactory, implementation, includePaths = [])
510
548
 
511
549
  const MATCH_CSS = /\.css$/i;
512
550
 
551
+ function getModernWebpackImporter() {
552
+ return {
553
+ async canonicalize() {
554
+ return null;
555
+ },
556
+
557
+ load() {// TODO implement
558
+ }
559
+
560
+ };
561
+ }
562
+
513
563
  function getWebpackImporter(loaderContext, implementation, includePaths) {
514
564
  const resolve = getWebpackResolver(loaderContext.getResolve, implementation, includePaths);
515
565
  return function importer(originalUrl, prev, done) {
@@ -539,14 +589,38 @@ let nodeSassJobQueue = null;
539
589
  * Verifies that the implementation and version of Sass is supported by this loader.
540
590
  *
541
591
  * @param {Object} implementation
592
+ * @param {Object} options
542
593
  * @returns {Function}
543
594
  */
544
595
 
545
- function getRenderFunctionFromSassImplementation(implementation) {
546
- const isDartSass = implementation.info.includes("dart-sass");
596
+ function getCompileFn(implementation, options) {
597
+ const isNewSass = implementation.info.includes("dart-sass") || implementation.info.includes("sass-embedded");
598
+
599
+ if (isNewSass) {
600
+ if (options.api === "modern") {
601
+ return sassOptions => {
602
+ const {
603
+ data,
604
+ ...rest
605
+ } = sassOptions;
606
+ return implementation.compileStringAsync(data, rest);
607
+ };
608
+ }
609
+
610
+ return sassOptions => new Promise((resolve, reject) => {
611
+ implementation.render(sassOptions, (error, result) => {
612
+ if (error) {
613
+ reject(error);
614
+ return;
615
+ }
616
+
617
+ resolve(result);
618
+ });
619
+ });
620
+ }
547
621
 
548
- if (isDartSass) {
549
- return implementation.render.bind(implementation);
622
+ if (options.api === "modern") {
623
+ throw new Error("Modern API is not supported for 'node-sass'");
550
624
  } // There is an issue with node-sass when async custom importers are used
551
625
  // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
552
626
  // We need to use a job queue to make sure that one thread is always available to the UV lib
@@ -557,7 +631,16 @@ function getRenderFunctionFromSassImplementation(implementation) {
557
631
  nodeSassJobQueue = _neoAsync.default.queue(implementation.render.bind(implementation), threadPoolSize - 1);
558
632
  }
559
633
 
560
- return nodeSassJobQueue.push.bind(nodeSassJobQueue);
634
+ return sassOptions => new Promise((resolve, reject) => {
635
+ nodeSassJobQueue.push.bind(nodeSassJobQueue)(sassOptions, (error, result) => {
636
+ if (error) {
637
+ reject(error);
638
+ return;
639
+ }
640
+
641
+ resolve(result);
642
+ });
643
+ });
561
644
  }
562
645
 
563
646
  const ABSOLUTE_SCHEME = /^[A-Za-z0-9+\-.]+:/;
@@ -587,7 +670,10 @@ function normalizeSourceMap(map, rootContext) {
587
670
  // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
588
671
  // eslint-disable-next-line no-param-reassign
589
672
 
590
- delete newMap.file; // eslint-disable-next-line no-param-reassign
673
+ if (typeof newMap.file !== "undefined") {
674
+ delete newMap.file;
675
+ } // eslint-disable-next-line no-param-reassign
676
+
591
677
 
592
678
  newMap.sourceRoot = ""; // node-sass returns POSIX paths, that's why we need to transform them back to native paths.
593
679
  // This fixes an error on windows where the source-map module cannot resolve the source maps.
@@ -595,9 +681,11 @@ function normalizeSourceMap(map, rootContext) {
595
681
  // eslint-disable-next-line no-param-reassign
596
682
 
597
683
  newMap.sources = newMap.sources.map(source => {
598
- const sourceType = getURLType(source); // Do no touch `scheme-relative`, `path-absolute` and `absolute` types
684
+ const sourceType = getURLType(source); // Do no touch `scheme-relative`, `path-absolute` and `absolute` types (except `file:`)
599
685
 
600
- if (sourceType === "path-relative") {
686
+ if (sourceType === "absolute" && /^file:/i.test(source)) {
687
+ return _url.default.fileURLToPath(source);
688
+ } else if (sourceType === "path-relative") {
601
689
  return _path.default.resolve(rootContext, _path.default.normalize(source));
602
690
  }
603
691
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sass-loader",
3
- "version": "12.4.0",
3
+ "version": "13.0.0",
4
4
  "description": "Sass loader for webpack",
5
5
  "license": "MIT",
6
6
  "repository": "webpack-contrib/sass-loader",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "main": "dist/cjs.js",
15
15
  "engines": {
16
- "node": ">= 12.13.0"
16
+ "node": ">= 14.15.0"
17
17
  },
18
18
  "scripts": {
19
19
  "start": "npm run build -- -w",
@@ -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.10",
67
+ "@babel/core": "^7.17.10",
68
+ "@babel/preset-env": "^7.17.10",
69
+ "@commitlint/cli": "^17.0.0",
70
+ "@commitlint/config-conventional": "^17.0.0",
67
71
  "@webpack-contrib/eslint-config-webpack": "^3.0.0",
68
- "babel-jest": "^27.0.6",
72
+ "babel-jest": "^28.1.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.15.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
- "husky": "^7.0.1",
84
- "jest": "^27.0.6",
85
- "lint-staged": "^12.1.2",
87
+ "husky": "^8.0.1",
88
+ "jest": "^28.1.0",
89
+ "jest-environment-node-single-context": "^28.0.0",
90
+ "lint-staged": "^12.4.1",
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.51.0",
98
+ "sass-embedded": "^1.51.0",
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.72.1"
97
103
  },
98
104
  "keywords": [
99
105
  "sass",