sass-loader 16.0.4 → 16.0.6
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 +55 -43
- package/dist/index.js +16 -16
- package/dist/options.json +7 -7
- package/dist/utils.js +143 -117
- package/package.json +75 -66
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
[![coverage][cover]][cover-url]
|
|
14
14
|
[![discussion][discussion]][discussion-url]
|
|
15
15
|
[![size][size]][size-url]
|
|
16
|
+
[![discord-invite][discord-invite]][discord-url]
|
|
16
17
|
|
|
17
18
|
# sass-loader
|
|
18
19
|
|
|
@@ -44,7 +45,7 @@ pnpm add -D sass-loader sass webpack
|
|
|
44
45
|
|
|
45
46
|
`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).
|
|
46
47
|
|
|
47
|
-
This allows you to control the versions of all your dependencies
|
|
48
|
+
This allows you to control the versions of all your dependencies and to choose which Sass implementation to use.
|
|
48
49
|
|
|
49
50
|
> [!NOTE]
|
|
50
51
|
>
|
|
@@ -54,7 +55,7 @@ This allows you to control the versions of all your dependencies, and to choose
|
|
|
54
55
|
>
|
|
55
56
|
> [Node Sass](https://github.com/sass/node-sass) does not work with [Yarn PnP](https://classic.yarnpkg.com/en/docs/pnp/) and doesn't support [@use rule](https://sass-lang.com/documentation/at-rules/use).
|
|
56
57
|
|
|
57
|
-
Chain the `sass-loader` with the [css-loader](https://github.com/webpack
|
|
58
|
+
Chain the `sass-loader` with the [css-loader](https://github.com/webpack/css-loader) and the [style-loader](https://github.com/webpack/style-loader) to immediately apply all styles to the DOM, or with the [mini-css-extract-plugin](https://github.com/webpack/mini-css-extract-plugin) to extract it into a separate file.
|
|
58
59
|
|
|
59
60
|
Then add the loader to your webpack configuration. For example:
|
|
60
61
|
|
|
@@ -96,7 +97,7 @@ module.exports = {
|
|
|
96
97
|
};
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
Finally run `webpack` via your preferred method.
|
|
100
|
+
Finally run `webpack` via your preferred method (e.g., via CLI or an npm script).
|
|
100
101
|
|
|
101
102
|
### The `style` (new API, by default since 16 version) and `outputStyle` (old API) options in `production` mode
|
|
102
103
|
|
|
@@ -106,13 +107,14 @@ For `production` mode, the `style` (new API, by default since 16 version) and `o
|
|
|
106
107
|
|
|
107
108
|
Webpack provides an [advanced mechanism to resolve files](https://webpack.js.org/concepts/module-resolution/).
|
|
108
109
|
|
|
109
|
-
The `sass-loader` uses Sass's custom importer feature to pass all queries to the webpack resolving engine enabling you to import your Sass modules from `node_modules`.
|
|
110
|
+
The `sass-loader` uses Sass's custom importer feature to pass all queries to the webpack resolving engine, enabling you to import your Sass modules from `node_modules`.
|
|
110
111
|
|
|
111
112
|
```scss
|
|
112
113
|
@import "bootstrap";
|
|
113
114
|
```
|
|
114
115
|
|
|
115
116
|
Using `~` is deprecated and should be removed from your code, but we still support it for historical reasons.
|
|
117
|
+
|
|
116
118
|
Why can you remove it? The loader will first try to resolve `@import` as a relative path. If it cannot be resolved, then the loader will try to resolve `@import` inside [`node_modules`](https://webpack.js.org/configuration/resolve/#resolvemodules).
|
|
117
119
|
|
|
118
120
|
Prepending module paths with a `~` tells webpack to search through [`node_modules`](https://webpack.js.org/configuration/resolve/#resolvemodules).
|
|
@@ -122,22 +124,25 @@ Prepending module paths with a `~` tells webpack to search through [`node_module
|
|
|
122
124
|
```
|
|
123
125
|
|
|
124
126
|
It's important to prepend the path with only `~`, because `~/` resolves to the home directory.
|
|
127
|
+
|
|
125
128
|
Webpack needs to distinguish between `bootstrap` and `~bootstrap` because CSS and Sass files have no special syntax for importing relative files.
|
|
129
|
+
|
|
126
130
|
Writing `@import "style.scss"` is the same as `@import "./style.scss";`
|
|
127
131
|
|
|
128
132
|
### Problems with `url(...)`
|
|
129
133
|
|
|
130
134
|
Since Sass implementations don't provide [url rewriting](https://github.com/sass/libsass/issues/532), all linked assets must be relative to the output.
|
|
131
135
|
|
|
132
|
-
- If you pass the generated CSS on to the `css-loader`, all
|
|
133
|
-
- If you're just generating CSS without passing it to the `css-loader`,
|
|
136
|
+
- If you pass the generated CSS on to the `css-loader`, all URLs must be relative to the entry-file (e.g. `main.scss`).
|
|
137
|
+
- If you're just generating CSS without passing it to the `css-loader`, URLs must be relative to your web root.
|
|
134
138
|
|
|
135
139
|
You might be surprised by this first issue, as it is natural to expect relative references to be resolved against the `.sass`/`.scss` file in which they are specified (like in regular `.css` files).
|
|
136
140
|
|
|
137
141
|
Thankfully there are two solutions to this problem:
|
|
138
142
|
|
|
139
|
-
- Add the missing
|
|
140
|
-
|
|
143
|
+
- Add the missing URL rewriting using the [resolve-url-loader](https://github.com/bholloway/resolve-url-loader). Place it before `sass-loader` in the loader chain.
|
|
144
|
+
|
|
145
|
+
- Library authors usually provide a variable to modify the asset path. [bootstrap-sass](https://github.com/twbs/bootstrap-sass) for example, has an `$icon-font-path`.
|
|
141
146
|
|
|
142
147
|
## Options
|
|
143
148
|
|
|
@@ -164,7 +169,7 @@ The special `implementation` option determines which implementation of Sass to u
|
|
|
164
169
|
By default, the loader resolves the implementation based on your dependencies.
|
|
165
170
|
Just add the desired implementation to your `package.json` (`sass`, `sass-embedded`, or `node-sass` package) and install dependencies.
|
|
166
171
|
|
|
167
|
-
Example where the `sass-loader`
|
|
172
|
+
Example where the `sass-loader` uses the `sass` (`dart-sass`) implementation:
|
|
168
173
|
|
|
169
174
|
**package.json**
|
|
170
175
|
|
|
@@ -177,7 +182,7 @@ Example where the `sass-loader` loader uses the `sass` (`dart-sass`) implementat
|
|
|
177
182
|
}
|
|
178
183
|
```
|
|
179
184
|
|
|
180
|
-
Example where the `sass-loader`
|
|
185
|
+
Example where the `sass-loader` uses the `node-sass` implementation:
|
|
181
186
|
|
|
182
187
|
**package.json**
|
|
183
188
|
|
|
@@ -190,7 +195,7 @@ Example where the `sass-loader` loader uses the `node-sass` implementation:
|
|
|
190
195
|
}
|
|
191
196
|
```
|
|
192
197
|
|
|
193
|
-
Example where the `sass-loader`
|
|
198
|
+
Example where the `sass-loader` uses the `sass-embedded` implementation:
|
|
194
199
|
|
|
195
200
|
**package.json**
|
|
196
201
|
|
|
@@ -208,8 +213,7 @@ Example where the `sass-loader` loader uses the `sass-embedded` implementation:
|
|
|
208
213
|
|
|
209
214
|
> [!NOTE]
|
|
210
215
|
>
|
|
211
|
-
> Using `optionalDependencies` means that `sass-loader` can fallback to `sass`
|
|
212
|
-
> when running on an operating system not supported by `sass-embedded`
|
|
216
|
+
> Using `optionalDependencies` means that `sass-loader` can fallback to `sass` when running on an operating system not supported by `sass-embedded`
|
|
213
217
|
|
|
214
218
|
Be aware of the order that `sass-loader` will resolve the implementation:
|
|
215
219
|
|
|
@@ -221,7 +225,7 @@ You can specify a specific implementation by using the `implementation` option,
|
|
|
221
225
|
|
|
222
226
|
#### `object`
|
|
223
227
|
|
|
224
|
-
For example, to always use Dart Sass
|
|
228
|
+
For example, to always use `Dart Sass`, you'd pass:
|
|
225
229
|
|
|
226
230
|
```js
|
|
227
231
|
module.exports = {
|
|
@@ -293,11 +297,11 @@ Options for [Dart Sass](http://sass-lang.com/dart-sass) or [Node Sass](https://g
|
|
|
293
297
|
|
|
294
298
|
> [!NOTE]
|
|
295
299
|
>
|
|
296
|
-
> The `charset` option is `true` by default for `dart-sass
|
|
300
|
+
> The `charset` option is `true` by default for `dart-sass`. We strongly discourage setting this to `false` because webpack doesn't support files other than `utf-8`.
|
|
297
301
|
|
|
298
302
|
> [!NOTE]
|
|
299
303
|
>
|
|
300
|
-
> The `syntax` (new API, by default since 16 version)
|
|
304
|
+
> The `syntax` (new API, by default since 16 version) and `indentedSyntax` (old API) option is `scss` for the `scss` extension, `indented` for the `sass` extension, and `css` for the `css` extension.
|
|
301
305
|
|
|
302
306
|
> [!NOTE]
|
|
303
307
|
>
|
|
@@ -336,7 +340,7 @@ module.exports = {
|
|
|
336
340
|
loader: "sass-loader",
|
|
337
341
|
options: {
|
|
338
342
|
sassOptions: {
|
|
339
|
-
style:
|
|
343
|
+
style: "compressed",
|
|
340
344
|
loadPaths: ["absolute/path/a", "absolute/path/b"],
|
|
341
345
|
},
|
|
342
346
|
},
|
|
@@ -398,7 +402,7 @@ type sourceMap = boolean;
|
|
|
398
402
|
|
|
399
403
|
Default: depends on the `compiler.devtool` value
|
|
400
404
|
|
|
401
|
-
Enables/
|
|
405
|
+
Enables/disables generation of source maps.
|
|
402
406
|
|
|
403
407
|
By default generation of source maps depends on the [`devtool`](https://webpack.js.org/configuration/devtool/) option.
|
|
404
408
|
All values enable source map generation except `eval` and `false`.
|
|
@@ -434,9 +438,9 @@ module.exports = {
|
|
|
434
438
|
};
|
|
435
439
|
```
|
|
436
440
|
|
|
437
|
-
> ℹ In some rare cases `node-sass` can output invalid source maps (
|
|
441
|
+
> ℹ In some rare cases `node-sass` can output invalid source maps (this is a `node-sass` bug).
|
|
438
442
|
>
|
|
439
|
-
> In order to avoid this, you can try to update `node-sass` to latest version, or you can try to set within `sassOptions` the `outputStyle` option to `compressed`.
|
|
443
|
+
> In order to avoid this, you can try to update `node-sass` to the latest version, or you can try to set within `sassOptions` the `outputStyle` option to `compressed`.
|
|
440
444
|
|
|
441
445
|
**webpack.config.js**
|
|
442
446
|
|
|
@@ -496,7 +500,7 @@ module.exports = {
|
|
|
496
500
|
{
|
|
497
501
|
loader: "sass-loader",
|
|
498
502
|
options: {
|
|
499
|
-
additionalData:
|
|
503
|
+
additionalData: `$env: ${process.env.NODE_ENV};`,
|
|
500
504
|
},
|
|
501
505
|
},
|
|
502
506
|
],
|
|
@@ -528,10 +532,10 @@ module.exports = {
|
|
|
528
532
|
const relativePath = path.relative(rootContext, resourcePath);
|
|
529
533
|
|
|
530
534
|
if (relativePath === "styles/foo.scss") {
|
|
531
|
-
return
|
|
535
|
+
return `$value: 100px;${content}`;
|
|
532
536
|
}
|
|
533
537
|
|
|
534
|
-
return
|
|
538
|
+
return `$value: 200px;${content}`;
|
|
535
539
|
},
|
|
536
540
|
},
|
|
537
541
|
},
|
|
@@ -562,10 +566,10 @@ module.exports = {
|
|
|
562
566
|
const relativePath = path.relative(rootContext, resourcePath);
|
|
563
567
|
|
|
564
568
|
if (relativePath === "styles/foo.scss") {
|
|
565
|
-
return
|
|
569
|
+
return `$value: 100px;${content}`;
|
|
566
570
|
}
|
|
567
571
|
|
|
568
|
-
return
|
|
572
|
+
return `$value: 200px;${content}`;
|
|
569
573
|
},
|
|
570
574
|
},
|
|
571
575
|
},
|
|
@@ -586,10 +590,10 @@ type webpackImporter = boolean;
|
|
|
586
590
|
|
|
587
591
|
Default: `true`
|
|
588
592
|
|
|
589
|
-
Enables/
|
|
593
|
+
Enables/disables the default webpack importer.
|
|
590
594
|
|
|
591
595
|
This can improve performance in some cases, though use it with caution because aliases and `@import` at-rules starting with `~` will not work.
|
|
592
|
-
You can pass your own `importer` to solve this (see [`importer docs
|
|
596
|
+
You can pass your own `importer` to solve this (see [`importer` docs](https://github.com/sass/node-sass#importer--v200---experimental)).
|
|
593
597
|
|
|
594
598
|
**webpack.config.js**
|
|
595
599
|
|
|
@@ -649,7 +653,7 @@ $known-prefixes: webkit, moz, ms, o;
|
|
|
649
653
|
}
|
|
650
654
|
```
|
|
651
655
|
|
|
652
|
-
The presented code will throw a webpack warning instead logging.
|
|
656
|
+
The presented code will throw a webpack warning instead of logging.
|
|
653
657
|
|
|
654
658
|
To ignore unnecessary warnings you can use the [ignoreWarnings](https://webpack.js.org/configuration/other-options/#ignorewarnings) option.
|
|
655
659
|
|
|
@@ -691,11 +695,11 @@ Allows you to switch between the `legacy` and `modern` APIs. You can find more i
|
|
|
691
695
|
|
|
692
696
|
> [!NOTE]
|
|
693
697
|
>
|
|
694
|
-
> Using `modern-compiler` and `sass-embedded` together significantly
|
|
698
|
+
> Using `modern-compiler` and `sass-embedded` together significantly improves performance and decreases build time. We strongly recommend their use. We will enable them by default in a future major release.
|
|
695
699
|
|
|
696
700
|
> [!WARNING]
|
|
697
701
|
>
|
|
698
|
-
> The
|
|
702
|
+
> The Sass options are different for the `legacy` and `modern` APIs. Please look at [docs](https://sass-lang.com/documentation/js-api) to learn how to migrate to the modern options.
|
|
699
703
|
|
|
700
704
|
**webpack.config.js**
|
|
701
705
|
|
|
@@ -726,7 +730,7 @@ module.exports = {
|
|
|
726
730
|
|
|
727
731
|
## How to enable `@debug` output
|
|
728
732
|
|
|
729
|
-
By default, the output of `@debug` messages
|
|
733
|
+
By default, the output of `@debug` messages is disabled.
|
|
730
734
|
Add the following to **webpack.config.js** to enable them:
|
|
731
735
|
|
|
732
736
|
```js
|
|
@@ -742,11 +746,11 @@ module.exports = {
|
|
|
742
746
|
|
|
743
747
|
### Extracts CSS into separate files
|
|
744
748
|
|
|
745
|
-
For production builds it's recommended to extract the CSS from your bundle to
|
|
749
|
+
For production builds, it's recommended to extract the CSS from your bundle to enable parallel loading of CSS/JS resources.
|
|
746
750
|
|
|
747
751
|
There are four recommended ways to extract a stylesheet from a bundle:
|
|
748
752
|
|
|
749
|
-
#### 1. [mini-css-extract-plugin](https://github.com/webpack
|
|
753
|
+
#### 1. [mini-css-extract-plugin](https://github.com/webpack/mini-css-extract-plugin)
|
|
750
754
|
|
|
751
755
|
**webpack.config.js**
|
|
752
756
|
|
|
@@ -785,8 +789,10 @@ module.exports = {
|
|
|
785
789
|
**webpack.config.js**
|
|
786
790
|
|
|
787
791
|
```js
|
|
792
|
+
const path = require("node:path");
|
|
793
|
+
|
|
788
794
|
module.exports = {
|
|
789
|
-
entry: [__dirname
|
|
795
|
+
entry: [path.resolve(__dirname, "./src/scss/app.scss")],
|
|
790
796
|
module: {
|
|
791
797
|
rules: [
|
|
792
798
|
{
|
|
@@ -815,8 +821,10 @@ module.exports = {
|
|
|
815
821
|
**webpack.config.js**
|
|
816
822
|
|
|
817
823
|
```js
|
|
824
|
+
const path = require("node:path");
|
|
825
|
+
|
|
818
826
|
module.exports = {
|
|
819
|
-
entry: [__dirname
|
|
827
|
+
entry: [path.resolve(__dirname, "./src/scss/app.scss")],
|
|
820
828
|
module: {
|
|
821
829
|
rules: [
|
|
822
830
|
{
|
|
@@ -844,9 +852,9 @@ module.exports = {
|
|
|
844
852
|
|
|
845
853
|
### Source maps
|
|
846
854
|
|
|
847
|
-
Enables/
|
|
855
|
+
Enables/disables generation of source maps.
|
|
848
856
|
|
|
849
|
-
To enable CSS source maps, you'll need to pass the `sourceMap` option to the `sass-loader` _and_ the `css-loader`.
|
|
857
|
+
To enable CSS source maps, you'll need to pass the `sourceMap` option to both the `sass-loader` _and_ the `css-loader`.
|
|
850
858
|
|
|
851
859
|
**webpack.config.js**
|
|
852
860
|
|
|
@@ -878,11 +886,13 @@ module.exports = {
|
|
|
878
886
|
};
|
|
879
887
|
```
|
|
880
888
|
|
|
881
|
-
If you want to edit the original Sass files inside Chrome, [there's a good blog post](https://medium.com/@toolmantim/getting-started-with-css-sourcemaps-and-in-browser-sass-editing-b4daab987fb0).
|
|
889
|
+
If you want to edit the original Sass files inside Chrome, [there's a good blog post](https://medium.com/@toolmantim/getting-started-with-css-sourcemaps-and-in-browser-sass-editing-b4daab987fb0).
|
|
890
|
+
Checkout [test/sourceMap](https://github.com/webpack/sass-loader/tree/main/test) for a working example.
|
|
882
891
|
|
|
883
892
|
## Contributing
|
|
884
893
|
|
|
885
|
-
|
|
894
|
+
We welcome all contributions!
|
|
895
|
+
If you're new here, please take a moment to review our contributing guidelines before submitting issues or pull requests.
|
|
886
896
|
|
|
887
897
|
[CONTRIBUTING](./.github/CONTRIBUTING.md)
|
|
888
898
|
|
|
@@ -894,11 +904,13 @@ Please take a moment to read our contributing guidelines if you haven't yet done
|
|
|
894
904
|
[npm-url]: https://npmjs.com/package/sass-loader
|
|
895
905
|
[node]: https://img.shields.io/node/v/sass-loader.svg
|
|
896
906
|
[node-url]: https://nodejs.org
|
|
897
|
-
[tests]: https://github.com/webpack
|
|
898
|
-
[tests-url]: https://github.com/webpack
|
|
899
|
-
[cover]: https://codecov.io/gh/webpack
|
|
900
|
-
[cover-url]: https://codecov.io/gh/webpack
|
|
907
|
+
[tests]: https://github.com/webpack/sass-loader/workflows/sass-loader/badge.svg
|
|
908
|
+
[tests-url]: https://github.com/webpack/sass-loader/actions
|
|
909
|
+
[cover]: https://codecov.io/gh/webpack/sass-loader/branch/main/graph/badge.svg
|
|
910
|
+
[cover-url]: https://codecov.io/gh/webpack/sass-loader
|
|
901
911
|
[discussion]: https://img.shields.io/github/discussions/webpack/webpack
|
|
902
912
|
[discussion-url]: https://github.com/webpack/webpack/discussions
|
|
903
913
|
[size]: https://packagephobia.now.sh/badge?p=sass-loader
|
|
904
914
|
[size-url]: https://packagephobia.now.sh/result?p=sass-loader
|
|
915
|
+
[discord-invite]: https://img.shields.io/discord/1180618526436888586?style=flat&logo=discord&logoColor=white&label=discord
|
|
916
|
+
[discord-url]: https://discord.gg/ARKBCXBu
|
package/dist/index.js
CHANGED
|
@@ -4,16 +4,16 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var
|
|
8
|
-
var
|
|
7
|
+
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
8
|
+
var _nodeUrl = _interopRequireDefault(require("node:url"));
|
|
9
9
|
var _options = _interopRequireDefault(require("./options.json"));
|
|
10
10
|
var _utils = require("./utils");
|
|
11
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
// eslint-disable-next-line jsdoc/no-restricted-syntax
|
|
12
13
|
/**
|
|
13
14
|
* The sass-loader makes node-sass and dart-sass available to webpack modules.
|
|
14
|
-
*
|
|
15
|
-
* @
|
|
16
|
-
* @param {string} content
|
|
15
|
+
* @this {LoaderContext<{ string: any }>}
|
|
16
|
+
* @param {string} content content
|
|
17
17
|
*/
|
|
18
18
|
async function loader(content) {
|
|
19
19
|
const options = this.getOptions(_options.default);
|
|
@@ -57,19 +57,19 @@ async function loader(content) {
|
|
|
57
57
|
// There are situations when the `file`/`span.url` property do not exist
|
|
58
58
|
// Modern API
|
|
59
59
|
if (error.span && typeof error.span.url !== "undefined") {
|
|
60
|
-
this.addDependency(
|
|
60
|
+
this.addDependency(_nodeUrl.default.fileURLToPath(error.span.url));
|
|
61
61
|
}
|
|
62
62
|
// Legacy API
|
|
63
63
|
else if (typeof error.file !== "undefined") {
|
|
64
64
|
// `node-sass` returns POSIX paths
|
|
65
|
-
this.addDependency(
|
|
65
|
+
this.addDependency(_nodePath.default.normalize(error.file));
|
|
66
66
|
}
|
|
67
67
|
callback((0, _utils.errorFactory)(error));
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
let map =
|
|
71
71
|
// Modern API, then legacy API
|
|
72
|
-
result.sourceMap
|
|
72
|
+
result.sourceMap || (result.map ? JSON.parse(result.map) : null);
|
|
73
73
|
|
|
74
74
|
// Modify source paths only for webpack, otherwise we do nothing
|
|
75
75
|
if (map && useSourceMap) {
|
|
@@ -78,25 +78,25 @@ async function loader(content) {
|
|
|
78
78
|
|
|
79
79
|
// Modern API
|
|
80
80
|
if (typeof result.loadedUrls !== "undefined") {
|
|
81
|
-
result.loadedUrls.filter(loadedUrl => loadedUrl.protocol === "file:")
|
|
82
|
-
const normalizedIncludedFile =
|
|
81
|
+
for (const includedFile of result.loadedUrls.filter(loadedUrl => loadedUrl.protocol === "file:")) {
|
|
82
|
+
const normalizedIncludedFile = _nodeUrl.default.fileURLToPath(includedFile);
|
|
83
83
|
|
|
84
84
|
// Custom `importer` can return only `contents` so includedFile will be relative
|
|
85
|
-
if (
|
|
85
|
+
if (_nodePath.default.isAbsolute(normalizedIncludedFile)) {
|
|
86
86
|
this.addDependency(normalizedIncludedFile);
|
|
87
87
|
}
|
|
88
|
-
}
|
|
88
|
+
}
|
|
89
89
|
}
|
|
90
90
|
// Legacy API
|
|
91
91
|
else if (typeof result.stats !== "undefined" && typeof result.stats.includedFiles !== "undefined") {
|
|
92
|
-
result.stats.includedFiles
|
|
93
|
-
const normalizedIncludedFile =
|
|
92
|
+
for (const includedFile of result.stats.includedFiles) {
|
|
93
|
+
const normalizedIncludedFile = _nodePath.default.normalize(includedFile);
|
|
94
94
|
|
|
95
95
|
// Custom `importer` can return only `contents` so includedFile will be relative
|
|
96
|
-
if (
|
|
96
|
+
if (_nodePath.default.isAbsolute(normalizedIncludedFile)) {
|
|
97
97
|
this.addDependency(normalizedIncludedFile);
|
|
98
98
|
}
|
|
99
|
-
}
|
|
99
|
+
}
|
|
100
100
|
}
|
|
101
101
|
callback(null, result.css.toString(), map);
|
|
102
102
|
}
|
package/dist/options.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"properties": {
|
|
5
5
|
"implementation": {
|
|
6
6
|
"description": "The implementation of the sass to be used.",
|
|
7
|
-
"link": "https://github.com/webpack
|
|
7
|
+
"link": "https://github.com/webpack/sass-loader#implementation",
|
|
8
8
|
"anyOf": [
|
|
9
9
|
{
|
|
10
10
|
"type": "string"
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
"api": {
|
|
18
18
|
"description": "Switch between old and modern API for `sass` (`Dart Sass`) and `Sass Embedded` implementations.",
|
|
19
|
-
"link": "https://github.com/webpack
|
|
19
|
+
"link": "https://github.com/webpack/sass-loader#sassoptions",
|
|
20
20
|
"enum": ["legacy", "modern", "modern-compiler"]
|
|
21
21
|
},
|
|
22
22
|
"sassOptions": {
|
|
23
23
|
"description": "Options for `node-sass` or `sass` (`Dart Sass`) implementation.",
|
|
24
|
-
"link": "https://github.com/webpack
|
|
24
|
+
"link": "https://github.com/webpack/sass-loader#sassoptions",
|
|
25
25
|
"anyOf": [
|
|
26
26
|
{
|
|
27
27
|
"type": "object",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"additionalData": {
|
|
36
36
|
"description": "Prepends/Appends `Sass`/`SCSS` code before the actual entry file.",
|
|
37
|
-
"link": "https://github.com/webpack
|
|
37
|
+
"link": "https://github.com/webpack/sass-loader#additionaldata",
|
|
38
38
|
"anyOf": [
|
|
39
39
|
{
|
|
40
40
|
"type": "string"
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
},
|
|
47
47
|
"sourceMap": {
|
|
48
48
|
"description": "Enables/Disables generation of source maps.",
|
|
49
|
-
"link": "https://github.com/webpack
|
|
49
|
+
"link": "https://github.com/webpack/sass-loader#sourcemap",
|
|
50
50
|
"type": "boolean"
|
|
51
51
|
},
|
|
52
52
|
"webpackImporter": {
|
|
53
53
|
"description": "Enables/Disables default `webpack` importer.",
|
|
54
|
-
"link": "https://github.com/webpack
|
|
54
|
+
"link": "https://github.com/webpack/sass-loader#webpackimporter",
|
|
55
55
|
"type": "boolean"
|
|
56
56
|
},
|
|
57
57
|
"warnRuleAsWarning": {
|
|
58
58
|
"description": "Treats the '@warn' rule as a webpack warning.",
|
|
59
|
-
"link": "https://github.com/webpack
|
|
59
|
+
"link": "https://github.com/webpack/sass-loader#warnruleaswarning",
|
|
60
60
|
"type": "boolean"
|
|
61
61
|
}
|
|
62
62
|
},
|
package/dist/utils.js
CHANGED
|
@@ -11,33 +11,47 @@ exports.getSassOptions = getSassOptions;
|
|
|
11
11
|
exports.getWebpackImporter = getWebpackImporter;
|
|
12
12
|
exports.getWebpackResolver = getWebpackResolver;
|
|
13
13
|
exports.normalizeSourceMap = normalizeSourceMap;
|
|
14
|
-
var
|
|
15
|
-
var
|
|
14
|
+
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
15
|
+
var _nodeUrl = _interopRequireDefault(require("node:url"));
|
|
16
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
/** @typedef {import("sass")} Sass */
|
|
18
|
+
/** @typedef {import("sass").StringOptionsWithImporter} SassSassOptions */
|
|
19
|
+
/** @typedef {import("sass-embedded")} SassEmbedded */
|
|
20
|
+
/** @typedef {import("sass-embedded").StringOptionsWithImporter} SassEmbeddedOptions */
|
|
21
|
+
|
|
22
|
+
/** @typedef {Sass | SassEmbedded} SassImplementation */
|
|
23
|
+
/** @typedef {SassSassOptions | SassEmbeddedOptions} SassOptions */
|
|
24
|
+
// eslint-disable-next-line jsdoc/no-restricted-syntax
|
|
25
|
+
/** @typedef {Record<string, any>} LoaderOptions */
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @returns {Sass | SassEmbedded} sass implementation
|
|
29
|
+
*/
|
|
17
30
|
function getDefaultSassImplementation() {
|
|
18
31
|
let sassImplPkg = "sass";
|
|
19
32
|
try {
|
|
20
33
|
require.resolve("sass-embedded");
|
|
21
34
|
sassImplPkg = "sass-embedded";
|
|
22
|
-
} catch
|
|
35
|
+
} catch {
|
|
23
36
|
try {
|
|
24
37
|
require.resolve("sass");
|
|
25
|
-
} catch
|
|
38
|
+
} catch {
|
|
26
39
|
try {
|
|
27
40
|
require.resolve("node-sass");
|
|
28
41
|
sassImplPkg = "node-sass";
|
|
29
|
-
} catch
|
|
42
|
+
} catch {
|
|
30
43
|
sassImplPkg = "sass";
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
}
|
|
34
|
-
|
|
35
|
-
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
36
47
|
return require(sassImplPkg);
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* This function is not Webpack-specific and can be used by tools wishing to mimic `sass-loader`'s behaviour, so its signature should not be changed.
|
|
52
|
+
* @param {LoaderContext} loaderContext loader context
|
|
53
|
+
* @param {SassImplementation} implementation sass implementation
|
|
54
|
+
* @returns {SassImplementation} resolved sass implementation
|
|
41
55
|
*/
|
|
42
56
|
function getSassImplementation(loaderContext, implementation) {
|
|
43
57
|
let resolvedImplementation = implementation;
|
|
@@ -45,7 +59,6 @@ function getSassImplementation(loaderContext, implementation) {
|
|
|
45
59
|
resolvedImplementation = getDefaultSassImplementation();
|
|
46
60
|
}
|
|
47
61
|
if (typeof resolvedImplementation === "string") {
|
|
48
|
-
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
49
62
|
resolvedImplementation = require(resolvedImplementation);
|
|
50
63
|
}
|
|
51
64
|
const {
|
|
@@ -60,27 +73,30 @@ function getSassImplementation(loaderContext, implementation) {
|
|
|
60
73
|
}
|
|
61
74
|
const [implementationName] = infoParts;
|
|
62
75
|
if (implementationName === "dart-sass") {
|
|
63
|
-
// eslint-disable-next-line consistent-return
|
|
64
76
|
return resolvedImplementation;
|
|
65
77
|
} else if (implementationName === "node-sass") {
|
|
66
|
-
// eslint-disable-next-line consistent-return
|
|
67
78
|
return resolvedImplementation;
|
|
68
79
|
} else if (implementationName === "sass-embedded") {
|
|
69
|
-
// eslint-disable-next-line consistent-return
|
|
70
80
|
return resolvedImplementation;
|
|
71
81
|
}
|
|
72
82
|
throw new Error(`Unknown Sass implementation "${implementationName}".`);
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/**
|
|
76
|
-
* @param {
|
|
77
|
-
* @returns {boolean}
|
|
86
|
+
* @param {LoaderContext} loaderContext loader context
|
|
87
|
+
* @returns {boolean} true when mode is production, otherwise false
|
|
78
88
|
*/
|
|
79
89
|
function isProductionLikeMode(loaderContext) {
|
|
80
90
|
return loaderContext.mode === "production" || !loaderContext.mode;
|
|
81
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {Importer[]} importers importers
|
|
95
|
+
* @param {LoaderContext} loaderContext loader context
|
|
96
|
+
* @returns {Importer[]} proxied importers
|
|
97
|
+
*/
|
|
82
98
|
function proxyCustomImporters(importers, loaderContext) {
|
|
83
|
-
return [].
|
|
99
|
+
return [importers].flat().map(importer => function proxyImporter(...args) {
|
|
84
100
|
const self = {
|
|
85
101
|
...this,
|
|
86
102
|
webpackLoaderContext: loaderContext
|
|
@@ -91,14 +107,13 @@ function proxyCustomImporters(importers, loaderContext) {
|
|
|
91
107
|
|
|
92
108
|
/**
|
|
93
109
|
* Derives the sass options from the loader context and normalizes its values with sane defaults.
|
|
94
|
-
*
|
|
95
|
-
* @param {
|
|
96
|
-
* @param {
|
|
97
|
-
* @param {
|
|
98
|
-
* @param {
|
|
99
|
-
* @param {
|
|
100
|
-
* @
|
|
101
|
-
* @returns {Object}
|
|
110
|
+
* @param {LoaderContext} loaderContext loader context
|
|
111
|
+
* @param {LoaderOptions} loaderOptions loader options
|
|
112
|
+
* @param {string} content content
|
|
113
|
+
* @param {SassImplementation} implementation sass implementation
|
|
114
|
+
* @param {boolean} useSourceMap true when need to generate source maps, otherwise false
|
|
115
|
+
* @param {"legacy" | "modern" | "modern-compiler"} apiType api type
|
|
116
|
+
* @returns {SassOptions} sass options
|
|
102
117
|
*/
|
|
103
118
|
async function getSassOptions(loaderContext, loaderOptions, content, implementation, useSourceMap, apiType) {
|
|
104
119
|
const options = loaderOptions.sassOptions ? typeof loaderOptions.sassOptions === "function" ? loaderOptions.sassOptions(loaderContext) || {} : loaderOptions.sassOptions : {};
|
|
@@ -151,7 +166,7 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
|
|
|
151
166
|
resourcePath
|
|
152
167
|
} = loaderContext;
|
|
153
168
|
if (isModernAPI) {
|
|
154
|
-
sassOptions.url =
|
|
169
|
+
sassOptions.url = _nodeUrl.default.pathToFileURL(resourcePath);
|
|
155
170
|
|
|
156
171
|
// opt.outputStyle
|
|
157
172
|
if (!sassOptions.style && isProductionLikeMode(loaderContext)) {
|
|
@@ -164,7 +179,7 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
|
|
|
164
179
|
|
|
165
180
|
// If we are compiling sass and indentedSyntax isn't set, automatically set it.
|
|
166
181
|
if (typeof sassOptions.syntax === "undefined") {
|
|
167
|
-
const ext =
|
|
182
|
+
const ext = _nodePath.default.extname(resourcePath);
|
|
168
183
|
if (ext && ext.toLowerCase() === ".scss") {
|
|
169
184
|
sassOptions.syntax = "scss";
|
|
170
185
|
} else if (ext && ext.toLowerCase() === ".sass") {
|
|
@@ -173,10 +188,10 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
|
|
|
173
188
|
sassOptions.syntax = "css";
|
|
174
189
|
}
|
|
175
190
|
}
|
|
176
|
-
sassOptions.loadPaths = [
|
|
191
|
+
sassOptions.loadPaths = [
|
|
177
192
|
// We use `loadPaths` in context for resolver, so it should be always absolute
|
|
178
|
-
(sassOptions.loadPaths ? sassOptions.loadPaths
|
|
179
|
-
sassOptions.importers = sassOptions.importers ? Array.isArray(sassOptions.importers) ? sassOptions.importers
|
|
193
|
+
...(sassOptions.loadPaths ? [...sassOptions.loadPaths] : []).map(includePath => _nodePath.default.isAbsolute(includePath) ? includePath : _nodePath.default.join(process.cwd(), includePath)), ...(process.env.SASS_PATH ? process.env.SASS_PATH.split(process.platform === "win32" ? ";" : ":") : [])];
|
|
194
|
+
sassOptions.importers = sassOptions.importers ? Array.isArray(sassOptions.importers) ? [...sassOptions.importers] : [sassOptions.importers] : [];
|
|
180
195
|
} else {
|
|
181
196
|
sassOptions.file = resourcePath;
|
|
182
197
|
|
|
@@ -192,12 +207,12 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
|
|
|
192
207
|
// all paths in sourceMap.sources will be relative to that path.
|
|
193
208
|
// Pretty complicated... :(
|
|
194
209
|
sassOptions.sourceMap = true;
|
|
195
|
-
sassOptions.outFile =
|
|
210
|
+
sassOptions.outFile = _nodePath.default.join(loaderContext.rootContext, "style.css.map");
|
|
196
211
|
sassOptions.sourceMapContents = true;
|
|
197
212
|
sassOptions.omitSourceMapUrl = true;
|
|
198
213
|
sassOptions.sourceMapEmbed = false;
|
|
199
214
|
}
|
|
200
|
-
const ext =
|
|
215
|
+
const ext = _nodePath.default.extname(resourcePath);
|
|
201
216
|
|
|
202
217
|
// If we are compiling sass and indentedSyntax isn't set, automatically set it.
|
|
203
218
|
if (ext && ext.toLowerCase() === ".sass" && typeof sassOptions.indentedSyntax === "undefined") {
|
|
@@ -207,16 +222,15 @@ async function getSassOptions(loaderContext, loaderOptions, content, implementat
|
|
|
207
222
|
}
|
|
208
223
|
|
|
209
224
|
// Allow passing custom importers to `sass`/`node-sass`. Accepts `Function` or an array of `Function`s.
|
|
210
|
-
sassOptions.importer = sassOptions.importer ? proxyCustomImporters(Array.isArray(sassOptions.importer) ? sassOptions.importer
|
|
225
|
+
sassOptions.importer = sassOptions.importer ? proxyCustomImporters(Array.isArray(sassOptions.importer) ? [...sassOptions.importer] : [sassOptions.importer], loaderContext) : [];
|
|
211
226
|
|
|
212
227
|
// Regression on the `sass-embedded` side
|
|
213
228
|
if (loaderOptions.webpackImporter === false && sassOptions.importer.length === 0) {
|
|
214
|
-
// eslint-disable-next-line no-undefined
|
|
215
229
|
sassOptions.importer = undefined;
|
|
216
230
|
}
|
|
217
|
-
sassOptions.includePaths = [
|
|
231
|
+
sassOptions.includePaths = [process.cwd(), ...
|
|
218
232
|
// We use `includePaths` in context for resolver, so it should be always absolute
|
|
219
|
-
(sassOptions.includePaths ? sassOptions.includePaths
|
|
233
|
+
(sassOptions.includePaths ? [...sassOptions.includePaths] : []).map(includePath => _nodePath.default.isAbsolute(includePath) ? includePath : _nodePath.default.join(process.cwd(), includePath)), ...(process.env.SASS_PATH ? process.env.SASS_PATH.split(process.platform === "win32" ? ";" : ":") : [])];
|
|
220
234
|
if (typeof sassOptions.charset === "undefined") {
|
|
221
235
|
sassOptions.charset = true;
|
|
222
236
|
}
|
|
@@ -243,15 +257,12 @@ const IS_PKG_SCHEME = /^pkg:/i;
|
|
|
243
257
|
*
|
|
244
258
|
* We don't need emulate `dart-sass` "It's not clear which file to import." errors (when "file.ext" and "_file.ext" files are present simultaneously in the same directory).
|
|
245
259
|
* This reduces performance and `dart-sass` always do it on own side.
|
|
246
|
-
*
|
|
247
|
-
* @param {
|
|
248
|
-
* @param {boolean}
|
|
249
|
-
* @
|
|
250
|
-
* @returns {Array<string>}
|
|
260
|
+
* @param {string} url url
|
|
261
|
+
* @param {boolean} forWebpackResolver true when for webpack resolver, otherwise false
|
|
262
|
+
* @param {boolean} fromImport true when from `@import`, otherwise false
|
|
263
|
+
* @returns {string[]} possible requests
|
|
251
264
|
*/
|
|
252
|
-
function getPossibleRequests(
|
|
253
|
-
// eslint-disable-next-line no-shadow
|
|
254
|
-
url, forWebpackResolver = false, fromImport = false) {
|
|
265
|
+
function getPossibleRequests(url, forWebpackResolver = false, fromImport = false) {
|
|
255
266
|
let request = url;
|
|
256
267
|
|
|
257
268
|
// In case there is module request, send this to webpack resolver
|
|
@@ -270,8 +281,8 @@ url, forWebpackResolver = false, fromImport = false) {
|
|
|
270
281
|
}
|
|
271
282
|
|
|
272
283
|
// Keep in mind: ext can also be something like '.datepicker' when the true extension is omitted and the filename contains a dot.
|
|
273
|
-
// @see https://github.com/webpack
|
|
274
|
-
const extension =
|
|
284
|
+
// @see https://github.com/webpack/sass/issues/167
|
|
285
|
+
const extension = _nodePath.default.extname(request).toLowerCase();
|
|
275
286
|
|
|
276
287
|
// Because @import is also defined in CSS, Sass needs a way of compiling plain CSS @imports without trying to import the files at compile time.
|
|
277
288
|
// To accomplish this, and to ensure SCSS is as much of a superset of CSS as possible, Sass will compile any @imports with the following characteristics to plain CSS imports:
|
|
@@ -281,15 +292,21 @@ url, forWebpackResolver = false, fromImport = false) {
|
|
|
281
292
|
// - imports that have media queries.
|
|
282
293
|
//
|
|
283
294
|
// The `node-sass` package sends `@import` ending on `.css` to importer, it is bug, so we skip resolve
|
|
295
|
+
// Also sass outputs as is `@import "style.css"`, but `@use "style.css"` should include CSS content
|
|
284
296
|
if (extension === ".css") {
|
|
285
|
-
return [];
|
|
297
|
+
return fromImport ? [] : [url];
|
|
286
298
|
}
|
|
287
|
-
const dirname =
|
|
299
|
+
const dirname = _nodePath.default.dirname(request);
|
|
288
300
|
const normalizedDirname = dirname === "." ? "" : `${dirname}/`;
|
|
289
|
-
const basename =
|
|
290
|
-
const basenameWithoutExtension =
|
|
291
|
-
return [...new Set([
|
|
301
|
+
const basename = _nodePath.default.basename(request);
|
|
302
|
+
const basenameWithoutExtension = _nodePath.default.basename(request, extension);
|
|
303
|
+
return [...new Set([...[fromImport ? [`${normalizedDirname}_${basenameWithoutExtension}.import${extension}`, `${normalizedDirname}${basenameWithoutExtension}.import${extension}`] : []].flat(), `${normalizedDirname}_${basename}`, `${normalizedDirname}${basename}`, ...(forWebpackResolver ? [url] : [])])];
|
|
292
304
|
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @param {(context: string, request: string, callback: (error: Error | null, result: string) => void) => void} callbackResolve callback resolve
|
|
308
|
+
* @returns {(context: string, request: string) => Promise<string>} promise resolve
|
|
309
|
+
*/
|
|
293
310
|
function promiseResolve(callbackResolve) {
|
|
294
311
|
return (context, request) => new Promise((resolve, reject) => {
|
|
295
312
|
callbackResolve(context, request, (error, result) => {
|
|
@@ -301,15 +318,20 @@ function promiseResolve(callbackResolve) {
|
|
|
301
318
|
});
|
|
302
319
|
});
|
|
303
320
|
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* @param {ResolutionMap} resolutionMap resolution map
|
|
324
|
+
* @returns {Promise<string>} resolved value
|
|
325
|
+
*/
|
|
304
326
|
async function startResolving(resolutionMap) {
|
|
305
327
|
if (resolutionMap.length === 0) {
|
|
306
|
-
|
|
328
|
+
throw new Error("Next");
|
|
307
329
|
}
|
|
308
330
|
const [{
|
|
309
331
|
possibleRequests
|
|
310
332
|
}] = resolutionMap;
|
|
311
333
|
if (possibleRequests.length === 0) {
|
|
312
|
-
|
|
334
|
+
throw new Error("Next");
|
|
313
335
|
}
|
|
314
336
|
const [{
|
|
315
337
|
resolve,
|
|
@@ -317,14 +339,12 @@ async function startResolving(resolutionMap) {
|
|
|
317
339
|
}] = resolutionMap;
|
|
318
340
|
try {
|
|
319
341
|
return await resolve(context, possibleRequests[0]);
|
|
320
|
-
} catch
|
|
342
|
+
} catch {
|
|
321
343
|
const [, ...tailResult] = possibleRequests;
|
|
322
344
|
if (tailResult.length === 0) {
|
|
323
345
|
const [, ...tailResolutionMap] = resolutionMap;
|
|
324
346
|
return startResolving(tailResolutionMap);
|
|
325
347
|
}
|
|
326
|
-
|
|
327
|
-
// eslint-disable-next-line no-param-reassign
|
|
328
348
|
resolutionMap[0].possibleRequests = tailResult;
|
|
329
349
|
return startResolving(resolutionMap);
|
|
330
350
|
}
|
|
@@ -334,20 +354,15 @@ const IS_SPECIAL_MODULE_IMPORT = /^~[^/]+$/;
|
|
|
334
354
|
const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
|
|
335
355
|
|
|
336
356
|
/**
|
|
337
|
-
* @public
|
|
338
357
|
* Create the resolve function used in the custom Sass importer.
|
|
339
|
-
*
|
|
340
358
|
* Can be used by external tools to mimic how `sass-loader` works, for example
|
|
341
359
|
* in a Jest transform. Such usages will want to wrap `resolve.create` from
|
|
342
360
|
* [`enhanced-resolve`]{@link https://github.com/webpack/enhanced-resolve} to
|
|
343
361
|
* pass as the `resolverFactory` argument.
|
|
344
|
-
*
|
|
345
|
-
* @param {
|
|
346
|
-
*
|
|
347
|
-
* @
|
|
348
|
-
* `sass` (Dart Sass) and `node-sass` are supported.
|
|
349
|
-
* @param {string[]} [includePaths] - The list of include paths passed to Sass.
|
|
350
|
-
*
|
|
362
|
+
* @param {ResolveFactory} resolverFactory a factory function for creating a Webpack resolver.
|
|
363
|
+
* @param {Sass} implementation the imported Sass implementation, both `sass` (Dart Sass) and `node-sass` are supported.
|
|
364
|
+
* @param {string[]=} includePaths the list of include paths passed to Sass.
|
|
365
|
+
* @returns {Resolver} webpack resolver
|
|
351
366
|
* @throws If a compatible Sass implementation cannot be found.
|
|
352
367
|
*/
|
|
353
368
|
function getWebpackResolver(resolverFactory, implementation, includePaths = []) {
|
|
@@ -408,17 +423,15 @@ function getWebpackResolver(resolverFactory, implementation, includePaths = [])
|
|
|
408
423
|
// See https://github.com/webpack/webpack/issues/12340
|
|
409
424
|
// Because `node-sass` calls our importer before `1. Filesystem imports relative to the base file.`
|
|
410
425
|
// custom importer may not return `{ file: '/path/to/name.ext' }` and therefore our `context` will be relative
|
|
411
|
-
if (!isModernSass && !
|
|
426
|
+
if (!isModernSass && !_nodePath.default.isAbsolute(context)) {
|
|
412
427
|
return Promise.reject();
|
|
413
428
|
}
|
|
414
429
|
const originalRequest = request;
|
|
415
430
|
const isFileScheme = originalRequest.slice(0, 5).toLowerCase() === "file:";
|
|
416
431
|
if (isFileScheme) {
|
|
417
432
|
try {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
} catch (ignoreError) {
|
|
421
|
-
// eslint-disable-next-line no-param-reassign
|
|
433
|
+
request = _nodeUrl.default.fileURLToPath(originalRequest);
|
|
434
|
+
} catch {
|
|
422
435
|
request = request.slice(7);
|
|
423
436
|
}
|
|
424
437
|
}
|
|
@@ -447,32 +460,35 @@ function getWebpackResolver(resolverFactory, implementation, includePaths = [])
|
|
|
447
460
|
|
|
448
461
|
// `node-sass` calls our importer before `1. Filesystem imports relative to the base file.`, so we need emulate this too
|
|
449
462
|
if (!isModernSass) {
|
|
450
|
-
resolutionMap = resolutionMap
|
|
463
|
+
resolutionMap = [...resolutionMap, {
|
|
451
464
|
resolve: fromImport ? sassImportResolve : sassModuleResolve,
|
|
452
|
-
context:
|
|
465
|
+
context: _nodePath.default.dirname(context),
|
|
453
466
|
possibleRequests: sassPossibleRequests
|
|
454
|
-
}
|
|
467
|
+
}];
|
|
455
468
|
}
|
|
456
|
-
resolutionMap = resolutionMap.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
context,
|
|
462
|
-
possibleRequests: sassPossibleRequests
|
|
463
|
-
};
|
|
464
|
-
}));
|
|
469
|
+
resolutionMap = [...resolutionMap, ...includePaths.map(context => ({
|
|
470
|
+
resolve: fromImport ? sassImportResolve : sassModuleResolve,
|
|
471
|
+
context,
|
|
472
|
+
possibleRequests: sassPossibleRequests
|
|
473
|
+
}))];
|
|
465
474
|
}
|
|
466
475
|
const webpackPossibleRequests = getPossibleRequests(request, true, fromImport);
|
|
467
|
-
resolutionMap = resolutionMap
|
|
476
|
+
resolutionMap = [...resolutionMap, {
|
|
468
477
|
resolve: fromImport ? webpackImportResolve : webpackModuleResolve,
|
|
469
|
-
context:
|
|
478
|
+
context: _nodePath.default.dirname(context),
|
|
470
479
|
possibleRequests: webpackPossibleRequests
|
|
471
|
-
}
|
|
480
|
+
}];
|
|
472
481
|
return startResolving(resolutionMap);
|
|
473
482
|
};
|
|
474
483
|
}
|
|
475
484
|
const MATCH_CSS = /\.css$/i;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @param {LoaderContext} loaderContext loader context
|
|
488
|
+
* @param {SassImplementation} implementation sass implementation
|
|
489
|
+
* @param {string[]} loadPaths load paths
|
|
490
|
+
* @returns {Importer} the modern webpack importer
|
|
491
|
+
*/
|
|
476
492
|
function getModernWebpackImporter(loaderContext, implementation, loadPaths) {
|
|
477
493
|
const resolve = getWebpackResolver(loaderContext.getResolve, implementation, loadPaths);
|
|
478
494
|
return {
|
|
@@ -480,19 +496,19 @@ function getModernWebpackImporter(loaderContext, implementation, loadPaths) {
|
|
|
480
496
|
const {
|
|
481
497
|
fromImport
|
|
482
498
|
} = context;
|
|
483
|
-
const prev = context.containingUrl ?
|
|
499
|
+
const prev = context.containingUrl ? _nodeUrl.default.fileURLToPath(context.containingUrl.toString()) : loaderContext.resourcePath;
|
|
484
500
|
let result;
|
|
485
501
|
try {
|
|
486
502
|
result = await resolve(prev, originalUrl, fromImport);
|
|
487
|
-
} catch
|
|
503
|
+
} catch {
|
|
488
504
|
// If no stylesheets are found, the importer should return null.
|
|
489
505
|
return null;
|
|
490
506
|
}
|
|
491
|
-
loaderContext.addDependency(
|
|
492
|
-
return
|
|
507
|
+
loaderContext.addDependency(_nodePath.default.normalize(result));
|
|
508
|
+
return _nodeUrl.default.pathToFileURL(result);
|
|
493
509
|
},
|
|
494
510
|
async load(canonicalUrl) {
|
|
495
|
-
const ext =
|
|
511
|
+
const ext = _nodePath.default.extname(canonicalUrl.pathname);
|
|
496
512
|
let syntax;
|
|
497
513
|
if (ext && ext.toLowerCase() === ".scss") {
|
|
498
514
|
syntax = "scss";
|
|
@@ -505,17 +521,16 @@ function getModernWebpackImporter(loaderContext, implementation, loadPaths) {
|
|
|
505
521
|
syntax = "scss";
|
|
506
522
|
}
|
|
507
523
|
try {
|
|
508
|
-
// eslint-disable-next-line no-shadow
|
|
509
524
|
const contents = await new Promise((resolve, reject) => {
|
|
510
525
|
// Old version of `enhanced-resolve` supports only path as a string
|
|
511
526
|
// TODO simplify in the next major release and pass URL
|
|
512
|
-
const canonicalPath =
|
|
513
|
-
loaderContext.fs.readFile(canonicalPath,
|
|
527
|
+
const canonicalPath = _nodeUrl.default.fileURLToPath(canonicalUrl);
|
|
528
|
+
loaderContext.fs.readFile(canonicalPath, (err, content) => {
|
|
514
529
|
if (err) {
|
|
515
530
|
reject(err);
|
|
516
531
|
return;
|
|
517
532
|
}
|
|
518
|
-
resolve(content);
|
|
533
|
+
resolve(content.toString("utf8"));
|
|
519
534
|
});
|
|
520
535
|
});
|
|
521
536
|
return {
|
|
@@ -523,23 +538,32 @@ function getModernWebpackImporter(loaderContext, implementation, loadPaths) {
|
|
|
523
538
|
syntax,
|
|
524
539
|
sourceMapUrl: canonicalUrl
|
|
525
540
|
};
|
|
526
|
-
} catch
|
|
541
|
+
} catch {
|
|
527
542
|
return null;
|
|
528
543
|
}
|
|
529
544
|
}
|
|
530
545
|
};
|
|
531
546
|
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @param {LoaderContext} loaderContext loader context
|
|
550
|
+
* @param {SassImplementation} implementation sass implementation
|
|
551
|
+
* @param {string[]} includePaths include paths
|
|
552
|
+
* @returns {Importer} the webpack importer
|
|
553
|
+
*/
|
|
532
554
|
function getWebpackImporter(loaderContext, implementation, includePaths) {
|
|
533
555
|
const resolve = getWebpackResolver(loaderContext.getResolve, implementation, includePaths);
|
|
534
556
|
return function importer(originalUrl, prev, done) {
|
|
535
557
|
const {
|
|
536
558
|
fromImport
|
|
537
559
|
} = this;
|
|
538
|
-
resolve(prev, originalUrl,
|
|
560
|
+
resolve(prev, originalUrl,
|
|
561
|
+
// For `node-sass`
|
|
562
|
+
typeof fromImport === "undefined" ? true : fromImport).then(result => {
|
|
539
563
|
// Add the result as dependency.
|
|
540
564
|
// Although we're also using stats.includedFiles, this might come in handy when an error occurs.
|
|
541
565
|
// In this case, we don't get stats.includedFiles from node-sass/sass.
|
|
542
|
-
loaderContext.addDependency(
|
|
566
|
+
loaderContext.addDependency(_nodePath.default.normalize(result));
|
|
543
567
|
|
|
544
568
|
// By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
|
|
545
569
|
done({
|
|
@@ -559,11 +583,10 @@ const sassModernCompilers = new WeakMap();
|
|
|
559
583
|
|
|
560
584
|
/**
|
|
561
585
|
* Verifies that the implementation and version of Sass is supported by this loader.
|
|
562
|
-
*
|
|
563
|
-
* @param {
|
|
564
|
-
* @param {
|
|
565
|
-
* @
|
|
566
|
-
* @returns {Function}
|
|
586
|
+
* @param {LoaderContext} loaderContext loader context
|
|
587
|
+
* @param {SassImplementation} implementation sass implementation
|
|
588
|
+
* @param {"legacy" | "modern" | "modern-compiler"} apiType api type
|
|
589
|
+
* @returns {SassCompileFunction} compile function
|
|
567
590
|
*/
|
|
568
591
|
function getCompileFn(loaderContext, implementation, apiType) {
|
|
569
592
|
if (typeof implementation.compileStringAsync !== "undefined") {
|
|
@@ -578,7 +601,6 @@ function getCompileFn(loaderContext, implementation, apiType) {
|
|
|
578
601
|
}
|
|
579
602
|
if (apiType === "modern-compiler") {
|
|
580
603
|
return async sassOptions => {
|
|
581
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
582
604
|
const webpackCompiler = loaderContext._compiler;
|
|
583
605
|
const {
|
|
584
606
|
data,
|
|
@@ -628,8 +650,9 @@ function getCompileFn(loaderContext, implementation, apiType) {
|
|
|
628
650
|
// We need to use a job queue to make sure that one thread is always available to the UV lib
|
|
629
651
|
if (nodeSassJobQueue === null) {
|
|
630
652
|
const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4);
|
|
653
|
+
|
|
631
654
|
// Only used for `node-sass`, so let's load it lazily
|
|
632
|
-
|
|
655
|
+
|
|
633
656
|
const async = require("neo-async");
|
|
634
657
|
nodeSassJobQueue = async.queue(implementation.render.bind(implementation), threadPoolSize - 1);
|
|
635
658
|
}
|
|
@@ -646,8 +669,8 @@ function getCompileFn(loaderContext, implementation, apiType) {
|
|
|
646
669
|
const ABSOLUTE_SCHEME = /^[A-Za-z0-9+\-.]+:/;
|
|
647
670
|
|
|
648
671
|
/**
|
|
649
|
-
* @param {string} source
|
|
650
|
-
* @returns {"absolute" | "scheme-relative" | "path-absolute" | "path-absolute"}
|
|
672
|
+
* @param {string} source source
|
|
673
|
+
* @returns {"absolute" | "scheme-relative" | "path-absolute" | "path-absolute"} a type of URL
|
|
651
674
|
*/
|
|
652
675
|
function getURLType(source) {
|
|
653
676
|
if (source[0] === "/") {
|
|
@@ -661,44 +684,47 @@ function getURLType(source) {
|
|
|
661
684
|
}
|
|
662
685
|
return ABSOLUTE_SCHEME.test(source) ? "absolute" : "path-relative";
|
|
663
686
|
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* @param {RawSourceMap} map source map
|
|
690
|
+
* @param {string} rootContext root context
|
|
691
|
+
* @returns {RawSourceMap} normalized source map
|
|
692
|
+
*/
|
|
664
693
|
function normalizeSourceMap(map, rootContext) {
|
|
665
694
|
const newMap = map;
|
|
666
695
|
|
|
667
696
|
// result.map.file is an optional property that provides the output filename.
|
|
668
697
|
// Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
|
|
669
|
-
|
|
698
|
+
|
|
670
699
|
if (typeof newMap.file !== "undefined") {
|
|
671
700
|
delete newMap.file;
|
|
672
701
|
}
|
|
673
|
-
|
|
674
|
-
// eslint-disable-next-line no-param-reassign
|
|
675
702
|
newMap.sourceRoot = "";
|
|
676
703
|
|
|
677
704
|
// node-sass returns POSIX paths, that's why we need to transform them back to native paths.
|
|
678
705
|
// This fixes an error on windows where the source-map module cannot resolve the source maps.
|
|
679
|
-
// @see https://github.com/webpack
|
|
680
|
-
|
|
706
|
+
// @see https://github.com/webpack/sass-loader/issues/366#issuecomment-279460722
|
|
707
|
+
|
|
681
708
|
newMap.sources = newMap.sources.map(source => {
|
|
682
709
|
const sourceType = getURLType(source);
|
|
683
710
|
|
|
684
711
|
// Do no touch `scheme-relative`, `path-absolute` and `absolute` types (except `file:`)
|
|
685
712
|
if (sourceType === "absolute" && /^file:/i.test(source)) {
|
|
686
|
-
return
|
|
713
|
+
return _nodeUrl.default.fileURLToPath(source);
|
|
687
714
|
} else if (sourceType === "path-relative") {
|
|
688
|
-
return
|
|
715
|
+
return _nodePath.default.resolve(rootContext, _nodePath.default.normalize(source));
|
|
689
716
|
}
|
|
690
717
|
return source;
|
|
691
718
|
});
|
|
692
719
|
return newMap;
|
|
693
720
|
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* @param {Error | SassError} error the original sass error
|
|
724
|
+
* @returns {Error} a new error
|
|
725
|
+
*/
|
|
694
726
|
function errorFactory(error) {
|
|
695
|
-
|
|
696
|
-
if (error.formatted) {
|
|
697
|
-
message = error.formatted.replace(/^(.+)?Error: /, "");
|
|
698
|
-
} else {
|
|
699
|
-
// Keep original error if `sassError.formatted` is unavailable
|
|
700
|
-
message = (error.message || error.toString()).replace(/^(.+)?Error: /, "");
|
|
701
|
-
}
|
|
727
|
+
const message = error.formatted ? error.formatted.replace(/^(.+)?Error: /, "") : (error.message || error.toString()).replace(/^(.+)?Error: /, "");
|
|
702
728
|
const obj = new Error(message, {
|
|
703
729
|
cause: error
|
|
704
730
|
});
|
package/package.json
CHANGED
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sass-loader",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.6",
|
|
4
4
|
"description": "Sass loader for webpack",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"sass",
|
|
7
|
+
"libsass",
|
|
8
|
+
"webpack",
|
|
9
|
+
"loader"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/webpack/sass-loader",
|
|
12
|
+
"bugs": "https://github.com/webpack/sass-loader/issues",
|
|
13
|
+
"repository": "webpack/sass-loader",
|
|
10
14
|
"funding": {
|
|
11
15
|
"type": "opencollective",
|
|
12
16
|
"url": "https://opencollective.com/webpack"
|
|
13
17
|
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "J. Tangelder",
|
|
14
20
|
"main": "dist/cjs.js",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
18
24
|
"scripts": {
|
|
19
25
|
"start": "npm run build -- -w",
|
|
20
26
|
"clean": "del-cli dist",
|
|
21
27
|
"prebuild": "npm run clean",
|
|
22
28
|
"build": "cross-env NODE_ENV=production babel src -d dist --copy-files",
|
|
23
|
-
"commitlint": "commitlint --from=
|
|
29
|
+
"commitlint": "commitlint --from=main",
|
|
24
30
|
"security": "npm audit --production",
|
|
25
31
|
"lint:prettier": "prettier --cache --list-different .",
|
|
26
|
-
"lint:
|
|
32
|
+
"lint:code": "eslint --cache .",
|
|
27
33
|
"lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"",
|
|
28
34
|
"lint": "npm-run-all -l -p \"lint:**\"",
|
|
29
35
|
"test:only": "cross-env NODE_ENV=test jest",
|
|
30
|
-
"fix:
|
|
36
|
+
"fix:code": "npm run lint:code -- --fix",
|
|
31
37
|
"fix:prettier": "npm run lint:prettier -- --write",
|
|
32
|
-
"fix": "npm-run-all -l fix:
|
|
38
|
+
"fix": "npm-run-all -l fix:code fix:prettier",
|
|
33
39
|
"test:watch": "npm run test:only -- --watch",
|
|
34
40
|
"test:manual": "npm run build && webpack-dev-server test/manual/src/index.js --open --config test/manual/webpack.config.js",
|
|
35
41
|
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
|
|
@@ -38,9 +44,59 @@
|
|
|
38
44
|
"prepare": "husky && npm run build",
|
|
39
45
|
"release": "standard-version"
|
|
40
46
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"neo-async": "^2.6.2"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@babel/cli": "^7.28.0",
|
|
52
|
+
"@babel/core": "^7.28.0",
|
|
53
|
+
"@babel/preset-env": "^7.28.0",
|
|
54
|
+
"@commitlint/cli": "^19.8.1",
|
|
55
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
56
|
+
"@eslint/js": "^9.32.0",
|
|
57
|
+
"@eslint/markdown": "^7.0.0",
|
|
58
|
+
"@stylistic/eslint-plugin": "^5.2.2",
|
|
59
|
+
"bootstrap-sass": "^3.4.1",
|
|
60
|
+
"bootstrap-v4": "npm:bootstrap@^4.5.3",
|
|
61
|
+
"bootstrap-v5": "npm:bootstrap@^5.3.7",
|
|
62
|
+
"cross-env": "^7.0.3",
|
|
63
|
+
"cspell": "^8.19.4",
|
|
64
|
+
"css-loader": "^6.9.0",
|
|
65
|
+
"del": "^6.1.1",
|
|
66
|
+
"del-cli": "^5.1.0",
|
|
67
|
+
"enhanced-resolve": "^5.18.2",
|
|
68
|
+
"eslint": "^9.32.0",
|
|
69
|
+
"eslint-config-prettier": "^10.1.8",
|
|
70
|
+
"eslint-config-webpack": "^4.5.1",
|
|
71
|
+
"eslint-plugin-import": "^2.32.0",
|
|
72
|
+
"eslint-plugin-jest": "^29.0.1",
|
|
73
|
+
"eslint-plugin-jsdoc": "^52.0.0",
|
|
74
|
+
"eslint-plugin-n": "^17.21.0",
|
|
75
|
+
"eslint-plugin-prettier": "^5.5.3",
|
|
76
|
+
"eslint-plugin-unicorn": "^60.0.0",
|
|
77
|
+
"file-loader": "^6.2.0",
|
|
78
|
+
"foundation-sites": "^6.7.5",
|
|
79
|
+
"globals": "^16.3.0",
|
|
80
|
+
"husky": "^9.1.3",
|
|
81
|
+
"jest": "^30.0.5",
|
|
82
|
+
"lint-staged": "^15.5.2",
|
|
83
|
+
"material-components-web": "^9.0.0",
|
|
84
|
+
"memfs": "^4.23.0",
|
|
85
|
+
"node-sass": "^9.0.0",
|
|
86
|
+
"node-sass-glob-importer": "^5.3.2",
|
|
87
|
+
"npm-run-all": "^4.1.5",
|
|
88
|
+
"prettier": "^3.6.2",
|
|
89
|
+
"sass": "^1.89.2",
|
|
90
|
+
"sass-embedded": "^1.89.2",
|
|
91
|
+
"semver": "^7.7.2",
|
|
92
|
+
"standard-version": "^9.3.1",
|
|
93
|
+
"style-loader": "^3.3.4",
|
|
94
|
+
"typescript": "^5.9.2",
|
|
95
|
+
"typescript-eslint": "^8.39.0",
|
|
96
|
+
"webpack": "^5.101.0",
|
|
97
|
+
"webpack-cli": "^6.0.1",
|
|
98
|
+
"webpack-dev-server": "^5.2.2"
|
|
99
|
+
},
|
|
44
100
|
"peerDependencies": {
|
|
45
101
|
"@rspack/core": "0.x || 1.x",
|
|
46
102
|
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
|
|
@@ -65,54 +121,7 @@
|
|
|
65
121
|
"optional": true
|
|
66
122
|
}
|
|
67
123
|
},
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
}
|
|
71
|
-
"devDependencies": {
|
|
72
|
-
"@babel/cli": "^7.23.4",
|
|
73
|
-
"@babel/core": "^7.24.0",
|
|
74
|
-
"@babel/preset-env": "^7.24.0",
|
|
75
|
-
"@commitlint/cli": "^19.3.0",
|
|
76
|
-
"@commitlint/config-conventional": "^19.2.2",
|
|
77
|
-
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
|
|
78
|
-
"babel-jest": "^29.6.2",
|
|
79
|
-
"bootstrap-sass": "^3.4.1",
|
|
80
|
-
"bootstrap-v4": "npm:bootstrap@^4.5.3",
|
|
81
|
-
"bootstrap-v5": "npm:bootstrap@^5.0.1",
|
|
82
|
-
"cross-env": "^7.0.3",
|
|
83
|
-
"cspell": "^8.6.0",
|
|
84
|
-
"css-loader": "^6.9.0",
|
|
85
|
-
"del": "^6.1.1",
|
|
86
|
-
"del-cli": "^5.1.0",
|
|
87
|
-
"enhanced-resolve": "^5.15.1",
|
|
88
|
-
"eslint": "^8.57.0",
|
|
89
|
-
"eslint-config-prettier": "^9.1.0",
|
|
90
|
-
"eslint-plugin-import": "^2.28.0",
|
|
91
|
-
"file-loader": "^6.2.0",
|
|
92
|
-
"foundation-sites": "^6.7.5",
|
|
93
|
-
"husky": "^9.1.3",
|
|
94
|
-
"jest": "^29.6.2",
|
|
95
|
-
"jest-environment-node-single-context": "^29.1.0",
|
|
96
|
-
"lint-staged": "^15.2.0",
|
|
97
|
-
"material-components-web": "^9.0.0",
|
|
98
|
-
"memfs": "^4.7.7",
|
|
99
|
-
"node-sass": "^9.0.0",
|
|
100
|
-
"node-sass-glob-importer": "^5.3.2",
|
|
101
|
-
"npm-run-all": "^4.1.5",
|
|
102
|
-
"prettier": "^3.2.2",
|
|
103
|
-
"sass": "^1.71.1",
|
|
104
|
-
"sass-embedded": "^1.71.1",
|
|
105
|
-
"semver": "^7.5.4",
|
|
106
|
-
"standard-version": "^9.3.1",
|
|
107
|
-
"style-loader": "^3.3.4",
|
|
108
|
-
"webpack": "^5.93.0",
|
|
109
|
-
"webpack-cli": "^5.1.4",
|
|
110
|
-
"webpack-dev-server": "^5.0.4"
|
|
111
|
-
},
|
|
112
|
-
"keywords": [
|
|
113
|
-
"sass",
|
|
114
|
-
"libsass",
|
|
115
|
-
"webpack",
|
|
116
|
-
"loader"
|
|
117
|
-
]
|
|
124
|
+
"engines": {
|
|
125
|
+
"node": ">= 18.12.0"
|
|
126
|
+
}
|
|
118
127
|
}
|