sass-loader 12.1.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,17 +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. |
136
+ - **[`implementation`](#implementation)**
137
+ - **[`sassOptions`](#sassoptions)**
138
+ - **[`sourceMap`](#sourcemap)**
139
+ - **[`additionalData`](#additionaldata)**
140
+ - **[`webpackImporter`](#webpackimporter)**
141
+ - **[`warnRuleAsWarning`](#warnruleaswarning)**
129
142
 
130
143
  ### `implementation`
131
144
 
132
- Type: `Object | String`
145
+ Type:
146
+
147
+ ```ts
148
+ type implementation = object | string;
149
+ ```
150
+
133
151
  Default: `sass`
134
152
 
135
153
  The special `implementation` option determines which implementation of Sass to use.
@@ -168,7 +186,7 @@ In order to avoid this situation you can use the `implementation` option.
168
186
 
169
187
  The `implementation` options either accepts `sass` (`Dart Sass`) or `node-sass` as a module.
170
188
 
171
- #### Object
189
+ #### `object`
172
190
 
173
191
  For example, to use Dart Sass, you'd pass:
174
192
 
@@ -195,7 +213,7 @@ module.exports = {
195
213
  };
196
214
  ```
197
215
 
198
- #### String
216
+ #### `string`
199
217
 
200
218
  For example, to use Dart Sass, you'd pass:
201
219
 
@@ -301,16 +319,29 @@ module.exports = {
301
319
 
302
320
  ### `sassOptions`
303
321
 
304
- 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
+
305
334
  Default: defaults values for Sass implementation
306
335
 
307
336
  Options for [Dart Sass](http://sass-lang.com/dart-sass) or [Node Sass](https://github.com/sass/node-sass) implementation.
308
337
 
338
+ > ℹ️ The `charset` option has `true` value by default for `dart-sass`, we strongly discourage change value to `false`, because webpack doesn't support files other than `utf-8`.
339
+
309
340
  > ℹ️ The `indentedSyntax` option has `true` value for the `sass` extension.
310
341
 
311
342
  > ℹ️ Options such as `data` and `file` are unavailable and will be ignored.
312
343
 
313
- > ℹ We recommend not to set the `outFile`, `sourceMapContents`, `sourceMapEmbed`, `sourceMapRoot` options because `sass-loader` automatically sets these options when the `sourceMap` option is `true`.
344
+ > ℹ We strongly discourage change `outFile`, `sourceMapContents`, `sourceMapEmbed`, `sourceMapRoot` options because `sass-loader` automatically sets these options when the `sourceMap` option is `true`.
314
345
 
315
346
  > ℹ️ Access to the [loader context](https://webpack.js.org/api/loaders/#the-loader-context) inside the custom importer can be done using the `this.webpackLoaderContext` property.
316
347
 
@@ -321,9 +352,9 @@ Please consult documentation before using them:
321
352
  - [Dart Sass documentation](https://github.com/sass/dart-sass#javascript-api) for all available `sass` options.
322
353
  - [Node Sass documentation](https://github.com/sass/node-sass/#options) for all available `node-sass` options.
323
354
 
324
- #### `Object`
355
+ #### `object`
325
356
 
326
- Use and object for the Sass implementation setup.
357
+ Use an object for the Sass implementation setup.
327
358
 
328
359
  **webpack.config.js**
329
360
 
@@ -352,7 +383,7 @@ module.exports = {
352
383
  };
353
384
  ```
354
385
 
355
- #### `Function`
386
+ #### `function`
356
387
 
357
388
  Allows to setup the Sass implementation by setting different options based on the loader context.
358
389
 
@@ -394,7 +425,12 @@ module.exports = {
394
425
 
395
426
  ### `sourceMap`
396
427
 
397
- Type: `Boolean`
428
+ Type:
429
+
430
+ ```ts
431
+ type sourceMap = boolean;
432
+ ```
433
+
398
434
  Default: depends on the `compiler.devtool` value
399
435
 
400
436
  Enables/Disables generation of source maps.
@@ -466,7 +502,14 @@ module.exports = {
466
502
 
467
503
  ### `additionalData`
468
504
 
469
- Type: `String|Function`
505
+ Type:
506
+
507
+ ```ts
508
+ type additionalData =
509
+ | string
510
+ | ((content: string | Buffer, loaderContext: LoaderContext) => string);
511
+ ```
512
+
470
513
  Default: `undefined`
471
514
 
472
515
  Prepends `Sass`/`SCSS` code before the actual entry file.
@@ -474,7 +517,7 @@ In this case, the `sass-loader` will not override the `data` option but just **p
474
517
 
475
518
  This is especially useful when some of your Sass variables depend on the environment:
476
519
 
477
- #### `String`
520
+ #### `string`
478
521
 
479
522
  ```js
480
523
  module.exports = {
@@ -498,7 +541,7 @@ module.exports = {
498
541
  };
499
542
  ```
500
543
 
501
- #### `Function`
544
+ #### `function`
502
545
 
503
546
  ##### Sync
504
547
 
@@ -570,7 +613,12 @@ module.exports = {
570
613
 
571
614
  ### `webpackImporter`
572
615
 
573
- Type: `Boolean`
616
+ Type:
617
+
618
+ ```ts
619
+ type webpackImporter = boolean;
620
+ ```
621
+
574
622
  Default: `true`
575
623
 
576
624
  Enables/Disables the default Webpack importer.
@@ -602,6 +650,113 @@ module.exports = {
602
650
  };
603
651
  ```
604
652
 
653
+ ### `warnRuleAsWarning`
654
+
655
+ Type:
656
+
657
+ ```ts
658
+ type warnRuleAsWarning = boolean;
659
+ ```
660
+
661
+ Default: `false`
662
+
663
+ Treats the `@warn` rule as a webpack warning.
664
+
665
+ > ℹ️ It will be `true` by default in the next major release.
666
+
667
+ **style.scss**
668
+
669
+ ```scss
670
+ $known-prefixes: webkit, moz, ms, o;
671
+
672
+ @mixin prefix($property, $value, $prefixes) {
673
+ @each $prefix in $prefixes {
674
+ @if not index($known-prefixes, $prefix) {
675
+ @warn "Unknown prefix #{$prefix}.";
676
+ }
677
+
678
+ -#{$prefix}-#{$property}: $value;
679
+ }
680
+ #{$property}: $value;
681
+ }
682
+
683
+ .tilt {
684
+ // Oops, we typo'd "webkit" as "wekbit"!
685
+ @include prefix(transform, rotate(15deg), wekbit ms);
686
+ }
687
+ ```
688
+
689
+ The presented code will throw webpack warning instead logging.
690
+
691
+ To ignore unnecessary warnings you can use the [ignoreWarnings](https://webpack.js.org/configuration/other-options/#ignorewarnings) option.
692
+
693
+ **webpack.config.js**
694
+
695
+ ```js
696
+ module.exports = {
697
+ module: {
698
+ rules: [
699
+ {
700
+ test: /\.s[ac]ss$/i,
701
+ use: [
702
+ "style-loader",
703
+ "css-loader",
704
+ {
705
+ loader: "sass-loader",
706
+ options: {
707
+ warnRuleAsWarning: true,
708
+ },
709
+ },
710
+ ],
711
+ },
712
+ ],
713
+ },
714
+ };
715
+ ```
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
+
605
760
  ## Examples
606
761
 
607
762
  ### Extracts CSS into separate files
package/dist/SassError.js CHANGED
@@ -8,14 +8,19 @@ exports.default = void 0;
8
8
  class SassError extends Error {
9
9
  constructor(sassError) {
10
10
  super();
11
- this.name = "SassError";
11
+ this.name = "SassError"; // TODO remove me in the next major release
12
+
12
13
  this.originalSassError = sassError;
13
- this.loc = {
14
- line: sassError.line,
15
- column: sassError.column
16
- }; // Keep original error if `sassError.formatted` is unavailable
17
14
 
18
- 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}`;
19
24
 
20
25
  if (this.originalSassError.formatted) {
21
26
  this.message = `${this.name}: ${this.originalSassError.formatted.replace(/^Error: /, "")}`; // Instruct webpack to hide the JS stack from the console.
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ class SassWarning extends Error {
9
+ constructor(warning, options) {
10
+ super(warning);
11
+ this.name = "SassWarning";
12
+ this.hideStack = true;
13
+
14
+ if (options.span) {
15
+ this.loc = {
16
+ line: options.span.start.line,
17
+ column: options.span.start.column
18
+ };
19
+ }
20
+ }
21
+
22
+ }
23
+
24
+ var _default = SassWarning;
25
+ exports.default = _default;
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
@@ -3,7 +3,8 @@
3
3
  "type": "object",
4
4
  "properties": {
5
5
  "implementation": {
6
- "description": "The implementation of the sass to be used (https://github.com/webpack-contrib/sass-loader#implementation).",
6
+ "description": "The implementation of the sass to be used.",
7
+ "link": "https://github.com/webpack-contrib/sass-loader#implementation",
7
8
  "anyOf": [
8
9
  {
9
10
  "type": "string"
@@ -13,8 +14,14 @@
13
14
  }
14
15
  ]
15
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
+ },
16
22
  "sassOptions": {
17
- "description": "Options for `node-sass` or `sass` (`Dart Sass`) implementation. (https://github.com/webpack-contrib/sass-loader#implementation).",
23
+ "description": "Options for `node-sass` or `sass` (`Dart Sass`) implementation.",
24
+ "link": "https://github.com/webpack-contrib/sass-loader#sassoptions",
18
25
  "anyOf": [
19
26
  {
20
27
  "type": "object",
@@ -26,7 +33,8 @@
26
33
  ]
27
34
  },
28
35
  "additionalData": {
29
- "description": "Prepends/Appends `Sass`/`SCSS` code before the actual entry file (https://github.com/webpack-contrib/sass-loader#additionaldata).",
36
+ "description": "Prepends/Appends `Sass`/`SCSS` code before the actual entry file.",
37
+ "link": "https://github.com/webpack-contrib/sass-loader#additionaldata",
30
38
  "anyOf": [
31
39
  {
32
40
  "type": "string"
@@ -37,11 +45,18 @@
37
45
  ]
38
46
  },
39
47
  "sourceMap": {
40
- "description": "Enables/Disables generation of source maps (https://github.com/webpack-contrib/sass-loader#sourcemap).",
48
+ "description": "Enables/Disables generation of source maps.",
49
+ "link": "https://github.com/webpack-contrib/sass-loader#sourcemap",
41
50
  "type": "boolean"
42
51
  },
43
52
  "webpackImporter": {
44
- "description": "Enables/Disables default `webpack` importer (https://github.com/webpack-contrib/sass-loader#webpackimporter).",
53
+ "description": "Enables/Disables default `webpack` importer.",
54
+ "link": "https://github.com/webpack-contrib/sass-loader#webpackimporter",
55
+ "type": "boolean"
56
+ },
57
+ "warnRuleAsWarning": {
58
+ "description": "Treats the '@warn' rule as a webpack warning.",
59
+ "link": "https://github.com/webpack-contrib/sass-loader#warnruleaswarning",
45
60
  "type": "boolean"
46
61
  }
47
62
  },
package/dist/utils.js CHANGED
@@ -3,13 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.getCompileFn = getCompileFn;
7
+ exports.getModernWebpackImporter = getModernWebpackImporter;
6
8
  exports.getSassImplementation = getSassImplementation;
7
9
  exports.getSassOptions = getSassOptions;
8
- exports.getWebpackResolver = getWebpackResolver;
9
10
  exports.getWebpackImporter = getWebpackImporter;
10
- exports.getRenderFunctionFromSassImplementation = getRenderFunctionFromSassImplementation;
11
- exports.normalizeSourceMap = normalizeSourceMap;
11
+ exports.getWebpackResolver = getWebpackResolver;
12
12
  exports.isSupportedFibers = isSupportedFibers;
13
+ exports.normalizeSourceMap = normalizeSourceMap;
13
14
 
14
15
  var _url = _interopRequireDefault(require("url"));
15
16
 
@@ -19,6 +20,8 @@ var _full = require("klona/full");
19
20
 
20
21
  var _neoAsync = _interopRequireDefault(require("neo-async"));
21
22
 
23
+ var _SassWarning = _interopRequireDefault(require("./SassWarning"));
24
+
22
25
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
26
 
24
27
  function getDefaultSassImplementation() {
@@ -91,6 +94,9 @@ function getSassImplementation(loaderContext, implementation) {
91
94
  } else if (implementationName === "node-sass") {
92
95
  // eslint-disable-next-line consistent-return
93
96
  return resolvedImplementation;
97
+ } else if (implementationName === "sass-embedded") {
98
+ // eslint-disable-next-line consistent-return
99
+ return resolvedImplementation;
94
100
  }
95
101
 
96
102
  loaderContext.emitError(new Error(`Unknown Sass implementation "${implementationName}".`));
@@ -133,69 +139,149 @@ function isSupportedFibers() {
133
139
  async function getSassOptions(loaderContext, loaderOptions, content, implementation, useSourceMap) {
134
140
  const options = (0, _full.klona)(loaderOptions.sassOptions ? typeof loaderOptions.sassOptions === "function" ? loaderOptions.sassOptions(loaderContext) || {} : loaderOptions.sassOptions : {});
135
141
  const isDartSass = implementation.info.includes("dart-sass");
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;
136
144
 
137
- if (isDartSass && isSupportedFibers()) {
138
- const shouldTryToResolveFibers = !options.fiber && options.fiber !== false;
145
+ if (!options.logger) {
146
+ // TODO set me to `true` by default in the next major release
147
+ const needEmitWarning = loaderOptions.warnRuleAsWarning === true;
148
+ const logger = loaderContext.getLogger("sass-loader");
139
149
 
140
- if (shouldTryToResolveFibers) {
141
- let fibers;
150
+ const formatSpan = span => `${span.url || "-"}:${span.start.line}:${span.start.column}: `;
142
151
 
143
- try {
144
- fibers = require.resolve("fibers");
145
- } catch (_error) {// Nothing
146
- }
152
+ options.logger = {
153
+ debug(message, loggerOptions) {
154
+ let builtMessage = "";
147
155
 
148
- if (fibers) {
149
- // eslint-disable-next-line global-require, import/no-dynamic-require
150
- options.fiber = require(fibers);
151
- }
152
- } else if (options.fiber === false) {
153
- // Don't pass the `fiber` option for `sass` (`Dart Sass`)
154
- delete options.fiber;
155
- }
156
- } else {
157
- // Don't pass the `fiber` option for `node-sass`
158
- delete options.fiber;
159
- }
156
+ if (loggerOptions.span) {
157
+ builtMessage = formatSpan(loggerOptions.span);
158
+ }
160
159
 
161
- options.file = loaderContext.resourcePath;
162
- options.data = loaderOptions.additionalData ? typeof loaderOptions.additionalData === "function" ? await loaderOptions.additionalData(content, loaderContext) : `${loaderOptions.additionalData}\n${content}` : content; // opt.outputStyle
160
+ builtMessage += message;
161
+ logger.debug(builtMessage);
162
+ },
163
163
 
164
- if (!options.outputStyle && isProductionLikeMode(loaderContext)) {
165
- options.outputStyle = "compressed";
166
- }
164
+ warn(message, loggerOptions) {
165
+ let builtMessage = "";
166
+
167
+ if (loggerOptions.deprecation) {
168
+ builtMessage += "Deprecation ";
169
+ }
170
+
171
+ if (loggerOptions.span && !loggerOptions.stack) {
172
+ builtMessage = formatSpan(loggerOptions.span);
173
+ }
174
+
175
+ builtMessage += message;
167
176
 
168
- if (useSourceMap) {
169
- // Deliberately overriding the sourceMap option here.
170
- // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string.
171
- // In case it is a string, options.sourceMap should be a path where the source map is written.
172
- // But since we're using the data option, the source map will not actually be written, but
173
- // all paths in sourceMap.sources will be relative to that path.
174
- // Pretty complicated... :(
175
- options.sourceMap = true;
176
- options.outFile = _path.default.join(loaderContext.rootContext, "style.css.map");
177
- options.sourceMapContents = true;
178
- options.omitSourceMapUrl = true;
179
- options.sourceMapEmbed = false;
177
+ if (loggerOptions.stack) {
178
+ builtMessage += `\n\n${loggerOptions.stack}`;
179
+ }
180
+
181
+ if (needEmitWarning) {
182
+ loaderContext.emitWarning(new _SassWarning.default(builtMessage, loggerOptions));
183
+ } else {
184
+ logger.warn(builtMessage);
185
+ }
186
+ }
187
+
188
+ };
180
189
  }
181
190
 
182
191
  const {
183
192
  resourcePath
184
193
  } = loaderContext;
185
194
 
186
- const ext = _path.default.extname(resourcePath); // If we are compiling sass and indentedSyntax isn't set, automatically set it.
195
+ if (isModernAPI) {
196
+ options.url = _url.default.pathToFileURL(resourcePath); // opt.outputStyle
197
+
198
+ if (!options.style && isProductionLikeMode(loaderContext)) {
199
+ options.style = "compressed";
200
+ }
187
201
 
202
+ if (useSourceMap) {
203
+ options.sourceMap = true;
204
+ } // If we are compiling sass and indentedSyntax isn't set, automatically set it.
188
205
 
189
- if (ext && ext.toLowerCase() === ".sass" && typeof options.indentedSyntax === "undefined") {
190
- options.indentedSyntax = true;
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) : [];
191
220
  } else {
192
- options.indentedSyntax = Boolean(options.indentedSyntax);
193
- } // Allow passing custom importers to `sass`/`node-sass`. Accepts `Function` or an array of `Function`s.
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
+ }
194
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
+ }
195
284
 
196
- options.importer = options.importer ? proxyCustomImporters(Array.isArray(options.importer) ? options.importer : [options.importer], loaderContext) : [];
197
- options.includePaths = [].concat(process.cwd()).concat( // We use `includePaths` in context for resolver, so it should be always absolute
198
- (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" ? ";" : ":") : []);
199
285
  return options;
200
286
  }
201
287
 
@@ -277,6 +363,40 @@ function promiseResolve(callbackResolve) {
277
363
  });
278
364
  }
279
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
+
280
400
  const IS_SPECIAL_MODULE_IMPORT = /^~[^/]+$/; // `[drive_letter]:\` + `\\[server]\[sharename]\`
281
401
 
282
402
  const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
@@ -299,41 +419,7 @@ const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
299
419
  */
300
420
 
301
421
  function getWebpackResolver(resolverFactory, implementation, includePaths = []) {
302
- async function startResolving(resolutionMap) {
303
- if (resolutionMap.length === 0) {
304
- return Promise.reject();
305
- }
306
-
307
- const [{
308
- possibleRequests
309
- }] = resolutionMap;
310
-
311
- if (possibleRequests.length === 0) {
312
- return Promise.reject();
313
- }
314
-
315
- const [{
316
- resolve,
317
- context
318
- }] = resolutionMap;
319
-
320
- try {
321
- return await resolve(context, possibleRequests[0]);
322
- } catch (_ignoreError) {
323
- const [, ...tailResult] = possibleRequests;
324
-
325
- if (tailResult.length === 0) {
326
- const [, ...tailResolutionMap] = resolutionMap;
327
- return startResolving(tailResolutionMap);
328
- } // eslint-disable-next-line no-param-reassign
329
-
330
-
331
- resolutionMap[0].possibleRequests = tailResult;
332
- return startResolving(resolutionMap);
333
- }
334
- }
335
-
336
- 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:
337
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`),
338
424
  // although `sass` look together by extensions (i.e. `_name.sass`/`name.sass`/`_name.scss`/`name.scss`/`_name.css`/`name.css`).
339
425
  // It shouldn't be a problem because `sass` throw errors:
@@ -457,6 +543,18 @@ function getWebpackResolver(resolverFactory, implementation, includePaths = [])
457
543
 
458
544
  const MATCH_CSS = /\.css$/i;
459
545
 
546
+ function getModernWebpackImporter() {
547
+ return {
548
+ async canonicalize() {
549
+ return null;
550
+ },
551
+
552
+ load() {// TODO implement
553
+ }
554
+
555
+ };
556
+ }
557
+
460
558
  function getWebpackImporter(loaderContext, implementation, includePaths) {
461
559
  const resolve = getWebpackResolver(loaderContext.getResolve, implementation, includePaths);
462
560
  return function importer(originalUrl, prev, done) {
@@ -486,14 +584,38 @@ let nodeSassJobQueue = null;
486
584
  * Verifies that the implementation and version of Sass is supported by this loader.
487
585
  *
488
586
  * @param {Object} implementation
587
+ * @param {Object} options
489
588
  * @returns {Function}
490
589
  */
491
590
 
492
- function getRenderFunctionFromSassImplementation(implementation) {
493
- 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
+ }
604
+
605
+ return sassOptions => new Promise((resolve, reject) => {
606
+ implementation.render(sassOptions, (error, result) => {
607
+ if (error) {
608
+ reject(error);
609
+ return;
610
+ }
494
611
 
495
- if (isDartSass) {
496
- return implementation.render.bind(implementation);
612
+ resolve(result);
613
+ });
614
+ });
615
+ }
616
+
617
+ if (options.api === "modern") {
618
+ throw new Error("Modern API is not supported for 'node-sass'");
497
619
  } // There is an issue with node-sass when async custom importers are used
498
620
  // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
499
621
  // We need to use a job queue to make sure that one thread is always available to the UV lib
@@ -504,7 +626,16 @@ function getRenderFunctionFromSassImplementation(implementation) {
504
626
  nodeSassJobQueue = _neoAsync.default.queue(implementation.render.bind(implementation), threadPoolSize - 1);
505
627
  }
506
628
 
507
- 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
+ });
508
639
  }
509
640
 
510
641
  const ABSOLUTE_SCHEME = /^[A-Za-z0-9+\-.]+:/;
@@ -534,7 +665,10 @@ function normalizeSourceMap(map, rootContext) {
534
665
  // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
535
666
  // eslint-disable-next-line no-param-reassign
536
667
 
537
- 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
+
538
672
 
539
673
  newMap.sourceRoot = ""; // node-sass returns POSIX paths, that's why we need to transform them back to native paths.
540
674
  // This fixes an error on windows where the source-map module cannot resolve the source maps.
@@ -542,9 +676,11 @@ function normalizeSourceMap(map, rootContext) {
542
676
  // eslint-disable-next-line no-param-reassign
543
677
 
544
678
  newMap.sources = newMap.sources.map(source => {
545
- 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:`)
546
680
 
547
- if (sourceType === "path-relative") {
681
+ if (sourceType === "absolute" && /^file:/i.test(source)) {
682
+ return _url.default.fileURLToPath(source);
683
+ } else if (sourceType === "path-relative") {
548
684
  return _path.default.resolve(rootContext, _path.default.normalize(source));
549
685
  }
550
686
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sass-loader",
3
- "version": "12.1.0",
3
+ "version": "12.5.0",
4
4
  "description": "Sass loader for webpack",
5
5
  "license": "MIT",
6
6
  "repository": "webpack-contrib/sass-loader",
@@ -39,8 +39,9 @@
39
39
  ],
40
40
  "peerDependencies": {
41
41
  "fibers": ">= 3.1.0",
42
- "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0",
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.5",
64
- "@babel/preset-env": "^7.14.5",
65
- "@commitlint/cli": "^12.1.4",
66
- "@commitlint/config-conventional": "^12.1.4",
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.2",
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": "^5.2.6",
77
+ "css-loader": "^6.6.0",
74
78
  "del": "^6.0.0",
75
- "del-cli": "^3.0.1",
79
+ "del-cli": "^4.0.1",
76
80
  "enhanced-resolve": "^5.8.2",
77
- "eslint": "^7.28.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
- "husky": "^6.0.0",
84
- "jest": "^27.0.4",
85
- "lint-staged": "^11.0.0",
87
+ "husky": "^7.0.1",
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": "^6.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
- "prettier": "^2.3.1",
92
- "sass": "^1.34.1",
96
+ "prettier": "^2.3.2",
97
+ "sass": "^1.49.7",
98
+ "sass-embedded": "^1.49.7",
93
99
  "semver": "^7.3.5",
94
- "standard-version": "^9.3.0",
95
- "style-loader": "^2.0.0",
96
- "webpack": "^5.38.1"
100
+ "standard-version": "^9.3.1",
101
+ "style-loader": "^3.2.1",
102
+ "webpack": "^5.68.0"
97
103
  },
98
104
  "keywords": [
99
105
  "sass",