sass-loader 7.1.0 → 7.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ <a name="7.2.0"></a>
6
+ # [7.2.0](https://github.com/webpack-contrib/sass-loader/compare/v7.1.0...v7.2.0) (2019-08-08)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * better handle stdin in sources ([#681](https://github.com/webpack-contrib/sass-loader/issues/681)) ([e279f2a](https://github.com/webpack-contrib/sass-loader/commit/e279f2a))
12
+ * prefer `sass`/`scss`/`css` extensions ([#711](https://github.com/webpack-contrib/sass-loader/issues/711)) ([6fc9d4e](https://github.com/webpack-contrib/sass-loader/commit/6fc9d4e))
13
+ * relax node engine ([#708](https://github.com/webpack-contrib/sass-loader/issues/708)) ([2a51502](https://github.com/webpack-contrib/sass-loader/commit/2a51502))
14
+
15
+
16
+ ### Features
17
+
18
+ * allow passing `functions` option as function ([#651](https://github.com/webpack-contrib/sass-loader/issues/651)) ([6c9654d](https://github.com/webpack-contrib/sass-loader/commit/6c9654d))
19
+ * support `data` as `Function` ([#648](https://github.com/webpack-contrib/sass-loader/issues/648)) ([aa64e1b](https://github.com/webpack-contrib/sass-loader/commit/aa64e1b))
20
+ * support `sass` and `style` fields in `package.json` ([#647](https://github.com/webpack-contrib/sass-loader/issues/647)) ([a8709c9](https://github.com/webpack-contrib/sass-loader/commit/a8709c9))
21
+ * support auto resolving `dart-sass` ([ff90dd6](https://github.com/webpack-contrib/sass-loader/commit/ff90dd6))
22
+
23
+
24
+
5
25
  <a name="7.1.0"></a>
6
26
  # [7.1.0](https://github.com/webpack-contrib/sass-loader/compare/v7.0.3...v7.1.0) (2018-08-01)
7
27
 
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  <p>Loads a Sass/SCSS file and compiles it to CSS.</p>
19
19
  </div>
20
20
 
21
- Use the [css-loader](https://github.com/webpack-contrib/css-loader) or the [raw-loader](https://github.com/webpack-contrib/raw-loader) to turn it into a JS module and the [MiniCssExtractPlugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to extract it into a separate file.
21
+ Use the [css-loader](https://github.com/webpack-contrib/css-loader) or the [raw-loader](https://github.com/webpack-contrib/raw-loader) to turn it into a JS module and the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) to extract it into a separate file.
22
22
  Looking for the webpack 1 loader? Check out the [archive/webpack-1 branch](https://github.com/webpack-contrib/sass-loader/tree/archive/webpack-1).
23
23
 
24
24
  <h2 align="center">Install</h2>
@@ -29,7 +29,7 @@ npm install sass-loader node-sass webpack --save-dev
29
29
 
30
30
  The sass-loader requires [webpack](https://github.com/webpack) as a
31
31
  [`peerDependency`](https://docs.npmjs.com/files/package.json#peerdependencies)
32
- and it requires you to install either [Node Sass][] or [Dart Sass][] on your
32
+ and it requires you to install either [Node Sass](https://github.com/sass/node-sass) or [Dart Sass](https://github.com/sass/dart-sass) on your
33
33
  own. This allows you to control the versions of all your dependencies, and to
34
34
  choose which Sass implementation to use.
35
35
 
@@ -87,6 +87,40 @@ module.exports = {
87
87
 
88
88
  See [the Node Sass documentation](https://github.com/sass/node-sass/blob/master/README.md#options) for all available Sass options.
89
89
 
90
+ By default the loader resolve the implementation based on your dependencies.
91
+ Just add required implementation to `package.json`
92
+ (`node-sass` or `sass` package) and install dependencies.
93
+
94
+ Example where the `sass-loader` loader uses the `sass` (`dart-sass`) implementation:
95
+
96
+ **package.json**
97
+
98
+ ```json
99
+ {
100
+ "devDependencies": {
101
+ "sass-loader": "*",
102
+ "sass": "*"
103
+ }
104
+ }
105
+ ```
106
+
107
+ Example where the `sass-loader` loader uses the `node-sass` implementation:
108
+
109
+ **package.json**
110
+
111
+ ```json
112
+ {
113
+ "devDependencies": {
114
+ "sass-loader": "*",
115
+ "node-sass": "*"
116
+ }
117
+ }
118
+ ```
119
+
120
+ Beware the situation
121
+ when `node-sass` and `sass` was installed, by default the `sass-loader`
122
+ prefers `node-sass`, to avoid this situation use the `implementation` option.
123
+
90
124
  The special `implementation` option determines which implementation of Sass to
91
125
  use. It takes either a [Node Sass][] or a [Dart Sass][] module. For example, to
92
126
  use Dart Sass, you'd pass:
@@ -136,7 +170,7 @@ module.exports = {
136
170
 
137
171
  ### In production
138
172
 
139
- Usually, it's recommended to extract the style sheets into a dedicated file in production using the [MiniCssExtractPlugin](https://github.com/webpack-contrib/mini-css-extract-plugin). This way your styles are not dependent on JavaScript:
173
+ Usually, it's recommended to extract the style sheets into a dedicated file in production using the [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin). This way your styles are not dependent on JavaScript:
140
174
 
141
175
  ```js
142
176
  const MiniCssExtractPlugin = require("mini-css-extract-plugin");
@@ -196,7 +230,7 @@ Bundling CSS with webpack has some nice advantages like referencing images and f
196
230
  There are two possibilities to extract a style sheet from the bundle:
197
231
 
198
232
  - [extract-loader](https://github.com/peerigon/extract-loader) (simpler, but specialized on the css-loader's output)
199
- - [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) (more complex, but works in all use-cases)
233
+ - [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) (use this, when using webpack 4 configuration. Works in all use-cases)
200
234
 
201
235
  ### Source maps
202
236
 
@@ -210,7 +244,9 @@ module.exports = {
210
244
  rules: [{
211
245
  test: /\.scss$/,
212
246
  use: [{
213
- loader: "style-loader"
247
+ loader: "style-loader", options: {
248
+ sourceMap: true
249
+ }
214
250
  }, {
215
251
  loader: "css-loader", options: {
216
252
  sourceMap: true
@@ -240,6 +276,27 @@ If you want to prepend Sass code before the actual entry file, you can set the `
240
276
  }
241
277
  ```
242
278
 
279
+ The `data` option supports `Function` notation:
280
+
281
+ ```javascript
282
+ {
283
+ loader: "sass-loader",
284
+ options: {
285
+ data: (loaderContext) => {
286
+ // More information about avalaible options https://webpack.js.org/api/loaders/
287
+ const { resourcePath, rootContext } = loaderContext;
288
+ const relativePath = path.relative(rootContext,resourcePath);
289
+
290
+ if (relativePath === "styles/foo.scss") {
291
+ return "$value: 100px;"
292
+ }
293
+
294
+ return "$value: 200px;"
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
243
300
  **Please note:** Since you're injecting code, this will break the source mappings in your entry file. Often there's a simpler solution than this, like multiple Sass entry files.
244
301
 
245
302
  <h2 align="center">Maintainers</h2>
@@ -1,17 +1,17 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
- const path = require("path");
4
- const os = require("os");
5
- const fs = require("fs");
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const fs = require('fs');
6
6
 
7
7
  // A typical sass error looks like this
8
- const SassError = { // eslint-disable-line no-unused-vars
9
- message: "invalid property name",
10
- column: 14,
11
- line: 1,
12
- file: "stdin",
13
- status: 1
14
- };
8
+ // const SassError = {
9
+ // message: "invalid property name",
10
+ // column: 14,
11
+ // line: 1,
12
+ // file: "stdin",
13
+ // status: 1
14
+ // };
15
15
 
16
16
  /**
17
17
  * Enhances the sass error with additional information about what actually went wrong.
@@ -20,31 +20,37 @@ const SassError = { // eslint-disable-line no-unused-vars
20
20
  * @param {string} resourcePath
21
21
  */
22
22
  function formatSassError(err, resourcePath) {
23
- // Instruct webpack to hide the JS stack from the console
24
- // Usually you're only interested in the SASS stack in this case.
25
- err.hideStack = true;
23
+ // Instruct webpack to hide the JS stack from the console
24
+ // Usually you're only interested in the SASS stack in this case.
25
+ // eslint-disable-next-line no-param-reassign
26
+ err.hideStack = true;
26
27
 
27
- // The file property is missing in rare cases.
28
- // No improvement in the error is possible.
29
- if (!err.file) {
30
- return;
31
- }
28
+ // The file property is missing in rare cases.
29
+ // No improvement in the error is possible.
30
+ if (!err.file) {
31
+ return;
32
+ }
32
33
 
33
- let msg = err.message;
34
+ let msg = err.message;
34
35
 
35
- if (err.file === "stdin") {
36
- err.file = resourcePath;
37
- }
38
- // node-sass returns UNIX-style paths
39
- err.file = path.normalize(err.file);
36
+ if (err.file === 'stdin') {
37
+ // eslint-disable-next-line no-param-reassign
38
+ err.file = resourcePath;
39
+ }
40
40
 
41
- // The 'Current dir' hint of node-sass does not help us, we're providing
42
- // additional information by reading the err.file property
43
- msg = msg.replace(/\s*Current dir:\s*/, "");
41
+ // node-sass returns UNIX-style paths
42
+ // eslint-disable-next-line no-param-reassign
43
+ err.file = path.normalize(err.file);
44
44
 
45
- err.message = getFileExcerptIfPossible(err) +
46
- msg.charAt(0).toUpperCase() + msg.slice(1) + os.EOL +
47
- " in " + err.file + " (line " + err.line + ", column " + err.column + ")";
45
+ // The 'Current dir' hint of node-sass does not help us, we're providing
46
+ // additional information by reading the err.file property
47
+ msg = msg.replace(/\s*Current dir:\s*/, '');
48
+
49
+ // eslint-disable-next-line no-param-reassign
50
+ err.message = `${getFileExcerptIfPossible(err) +
51
+ msg.charAt(0).toUpperCase() +
52
+ msg.slice(1) +
53
+ os.EOL} in ${err.file} (line ${err.line}, column ${err.column})`;
48
54
  }
49
55
 
50
56
  /**
@@ -57,17 +63,17 @@ function formatSassError(err, resourcePath) {
57
63
  * @returns {string}
58
64
  */
59
65
  function getFileExcerptIfPossible(err) {
60
- try {
61
- const content = fs.readFileSync(err.file, "utf8");
66
+ try {
67
+ const content = fs.readFileSync(err.file, 'utf8');
62
68
 
63
- return os.EOL +
64
- content.split(os.EOL)[err.line - 1] + os.EOL +
65
- new Array(err.column - 1).join(" ") + "^" + os.EOL +
66
- " ";
67
- } catch (err) {
68
- // If anything goes wrong here, we don't want any errors to be reported to the user
69
- return "";
70
- }
69
+ return `${os.EOL +
70
+ content.split(os.EOL)[err.line - 1] +
71
+ os.EOL +
72
+ new Array(err.column - 1).join(' ')}^${os.EOL} `;
73
+ } catch (ignoreErr) {
74
+ // If anything goes wrong here, we don't want any errors to be reported to the user
75
+ return '';
76
+ }
71
77
  }
72
78
 
73
79
  module.exports = formatSassError;
@@ -1,9 +1,10 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
- const path = require("path");
4
- const utils = require("loader-utils");
3
+ const path = require('path');
5
4
 
6
- const matchModuleImport = /^~([^\/]+|@[^\/]+[\/][^\/]+)$/;
5
+ const utils = require('loader-utils');
6
+
7
+ const matchModuleImport = /^~([^/]+|@[^/]+[/][^/]+)$/;
7
8
 
8
9
  /**
9
10
  * When libsass tries to resolve an import, it uses a special algorithm.
@@ -15,46 +16,47 @@ const matchModuleImport = /^~([^\/]+|@[^\/]+[\/][^\/]+)$/;
15
16
  * @returns {Array<string>}
16
17
  */
17
18
  function importsToResolve(url) {
18
- const request = utils.urlToRequest(url);
19
- // Keep in mind: ext can also be something like '.datepicker' when the true extension is omitted and the filename contains a dot.
20
- // @see https://github.com/webpack-contrib/sass-loader/issues/167
21
- const ext = path.extname(request);
22
-
23
- if (matchModuleImport.test(url)) {
24
- return [request, url];
25
- }
26
-
27
- // libsass' import algorithm works like this:
28
-
29
- // In case there is a file extension...
30
- // - If the file is a CSS-file, do not include it all, but just link it via @import url().
31
- // - The exact file name must match (no auto-resolving of '_'-modules).
32
- if (ext === ".css") {
33
- return [];
34
- }
35
- if (ext === ".scss" || ext === ".sass") {
36
- return [request, url];
37
- }
38
-
39
- // In case there is no file extension...
40
- // - Prefer modules starting with '_'.
41
- // - File extension precedence: .scss, .sass, .css.
42
- const basename = path.basename(request);
43
-
44
- if (basename.charAt(0) === "_") {
45
- return [
46
- `${ request }.scss`, `${ request }.sass`, `${ request }.css`,
47
- url
48
- ];
49
- }
50
-
51
- const dirname = path.dirname(request);
52
-
53
- return [
54
- `${ dirname }/_${ basename }.scss`, `${ dirname }/_${ basename }.sass`, `${ dirname }/_${ basename }.css`,
55
- `${ request }.scss`, `${ request }.sass`, `${ request }.css`,
56
- url
57
- ];
19
+ const request = utils.urlToRequest(url);
20
+ // Keep in mind: ext can also be something like '.datepicker' when the true extension is omitted and the filename contains a dot.
21
+ // @see https://github.com/webpack-contrib/sass-loader/issues/167
22
+ const ext = path.extname(request);
23
+
24
+ if (matchModuleImport.test(url)) {
25
+ return [request, url];
26
+ }
27
+
28
+ // libsass' import algorithm works like this:
29
+
30
+ // In case there is a file extension...
31
+ // - If the file is a CSS-file, do not include it all, but just link it via @import url().
32
+ // - The exact file name must match (no auto-resolving of '_'-modules).
33
+ if (ext === '.css') {
34
+ return [];
35
+ }
36
+ if (ext === '.scss' || ext === '.sass') {
37
+ return [request, url];
38
+ }
39
+
40
+ // In case there is no file extension...
41
+ // - Prefer modules starting with '_'.
42
+ // - File extension precedence: .scss, .sass, .css.
43
+ const basename = path.basename(request);
44
+
45
+ if (basename.charAt(0) === '_') {
46
+ return [`${request}.scss`, `${request}.sass`, `${request}.css`, url];
47
+ }
48
+
49
+ const dirname = path.dirname(request);
50
+
51
+ return [
52
+ `${dirname}/_${basename}.scss`,
53
+ `${dirname}/_${basename}.sass`,
54
+ `${dirname}/_${basename}.css`,
55
+ `${request}.scss`,
56
+ `${request}.sass`,
57
+ `${request}.css`,
58
+ url,
59
+ ];
58
60
  }
59
61
 
60
62
  module.exports = importsToResolve;
package/lib/loader.js CHANGED
@@ -1,80 +1,135 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
- const path = require("path");
4
- const async = require("neo-async");
5
- const formatSassError = require("./formatSassError");
6
- const webpackImporter = require("./webpackImporter");
7
- const normalizeOptions = require("./normalizeOptions");
8
- const pify = require("pify");
9
- const semver = require("semver");
3
+ const path = require('path');
4
+
5
+ const async = require('neo-async');
6
+ const pify = require('pify');
7
+ const semver = require('semver');
8
+
9
+ const formatSassError = require('./formatSassError');
10
+ const webpackImporter = require('./webpackImporter');
11
+ const normalizeOptions = require('./normalizeOptions');
10
12
 
11
13
  let nodeSassJobQueue = null;
12
14
 
15
+ // Very hacky check
16
+ function hasGetResolve(loaderContext) {
17
+ return (
18
+ loaderContext.getResolve &&
19
+ // eslint-disable-next-line no-underscore-dangle
20
+ loaderContext._compiler &&
21
+ // eslint-disable-next-line no-underscore-dangle
22
+ loaderContext._compiler.resolverFactory &&
23
+ // eslint-disable-next-line no-underscore-dangle
24
+ loaderContext._compiler.resolverFactory._create &&
25
+ /cachedCleverMerge/.test(
26
+ // eslint-disable-next-line no-underscore-dangle
27
+ loaderContext._compiler.resolverFactory._create.toString()
28
+ )
29
+ );
30
+ }
31
+
13
32
  /**
14
- * The sass-loader makes node-sass available to webpack modules.
33
+ * The sass-loader makes node-sass and dart-sass available to webpack modules.
15
34
  *
16
35
  * @this {LoaderContext}
17
36
  * @param {string} content
18
37
  */
19
38
  function sassLoader(content) {
20
- const callback = this.async();
21
- const isSync = typeof callback !== "function";
22
- const self = this;
23
- const resourcePath = this.resourcePath;
24
-
25
- function addNormalizedDependency(file) {
26
- // node-sass returns POSIX paths
27
- self.dependency(path.normalize(file));
39
+ const callback = this.async();
40
+ const isSync = typeof callback !== 'function';
41
+ const self = this;
42
+ const { resourcePath } = this;
43
+
44
+ function addNormalizedDependency(file) {
45
+ // node-sass returns POSIX paths
46
+ self.dependency(path.normalize(file));
47
+ }
48
+
49
+ if (isSync) {
50
+ throw new Error(
51
+ 'Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333'
52
+ );
53
+ }
54
+
55
+ let resolve = pify(this.resolve);
56
+
57
+ // Supported since v4.36.0
58
+ if (hasGetResolve(self)) {
59
+ resolve = this.getResolve({
60
+ mainFields: ['sass', 'style', '...'],
61
+ extensions: ['.scss', '.sass', '.css', '...'],
62
+ });
63
+ }
64
+
65
+ const options = normalizeOptions(
66
+ this,
67
+ content,
68
+ webpackImporter(resourcePath, resolve, addNormalizedDependency)
69
+ );
70
+
71
+ // Skip empty files, otherwise it will stop webpack, see issue #21
72
+ if (options.data.trim() === '') {
73
+ callback(null, '');
74
+ return;
75
+ }
76
+
77
+ const render = getRenderFuncFromSassImpl(
78
+ // eslint-disable-next-line import/no-extraneous-dependencies, global-require
79
+ options.implementation || getDefaultSassImpl()
80
+ );
81
+
82
+ render(options, (err, result) => {
83
+ if (err) {
84
+ formatSassError(err, this.resourcePath);
85
+
86
+ if (err.file) {
87
+ this.dependency(err.file);
88
+ }
89
+
90
+ callback(err);
91
+ return;
28
92
  }
29
93
 
30
- if (isSync) {
31
- throw new Error("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333");
94
+ if (result.map && result.map !== '{}') {
95
+ // eslint-disable-next-line no-param-reassign
96
+ result.map = JSON.parse(result.map);
97
+ // result.map.file is an optional property that provides the output filename.
98
+ // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
99
+ // eslint-disable-next-line no-param-reassign
100
+ delete result.map.file;
101
+ // One of the sources is 'stdin' according to dart-sass/node-sass because we've used the data input.
102
+ // Now let's override that value with the correct relative path.
103
+ // Since we specified options.sourceMap = path.join(process.cwd(), "/sass.map"); in normalizeOptions,
104
+ // we know that this path is relative to process.cwd(). This is how node-sass works.
105
+ // eslint-disable-next-line no-param-reassign
106
+ const stdinIndex = result.map.sources.findIndex(
107
+ (source) => source.indexOf('stdin') !== -1
108
+ );
109
+
110
+ if (stdinIndex !== -1) {
111
+ // eslint-disable-next-line no-param-reassign
112
+ result.map.sources[stdinIndex] = path.relative(
113
+ process.cwd(),
114
+ resourcePath
115
+ );
116
+ }
117
+ // node-sass returns POSIX paths, that's why we need to transform them back to native paths.
118
+ // This fixes an error on windows where the source-map module cannot resolve the source maps.
119
+ // @see https://github.com/webpack-contrib/sass-loader/issues/366#issuecomment-279460722
120
+ // eslint-disable-next-line no-param-reassign
121
+ result.map.sourceRoot = path.normalize(result.map.sourceRoot);
122
+ // eslint-disable-next-line no-param-reassign
123
+ result.map.sources = result.map.sources.map(path.normalize);
124
+ } else {
125
+ // eslint-disable-next-line no-param-reassign
126
+ result.map = null;
32
127
  }
33
128
 
34
- const options = normalizeOptions(this, content, webpackImporter(
35
- resourcePath,
36
- pify(this.resolve.bind(this)),
37
- addNormalizedDependency
38
- ));
129
+ result.stats.includedFiles.forEach(addNormalizedDependency);
39
130
 
40
- // Skip empty files, otherwise it will stop webpack, see issue #21
41
- if (options.data.trim() === "") {
42
- callback(null, "");
43
- return;
44
- }
45
-
46
- const render = getRenderFuncFromSassImpl(options.implementation || require("node-sass"));
47
-
48
- render(options, (err, result) => {
49
- if (err) {
50
- formatSassError(err, this.resourcePath);
51
- err.file && this.dependency(err.file);
52
- callback(err);
53
- return;
54
- }
55
-
56
- if (result.map && result.map !== "{}") {
57
- result.map = JSON.parse(result.map);
58
- // result.map.file is an optional property that provides the output filename.
59
- // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
60
- delete result.map.file;
61
- // The first source is 'stdin' according to node-sass because we've used the data input.
62
- // Now let's override that value with the correct relative path.
63
- // Since we specified options.sourceMap = path.join(process.cwd(), "/sass.map"); in normalizeOptions,
64
- // we know that this path is relative to process.cwd(). This is how node-sass works.
65
- result.map.sources[0] = path.relative(process.cwd(), resourcePath);
66
- // node-sass returns POSIX paths, that's why we need to transform them back to native paths.
67
- // This fixes an error on windows where the source-map module cannot resolve the source maps.
68
- // @see https://github.com/webpack-contrib/sass-loader/issues/366#issuecomment-279460722
69
- result.map.sourceRoot = path.normalize(result.map.sourceRoot);
70
- result.map.sources = result.map.sources.map(path.normalize);
71
- } else {
72
- result.map = null;
73
- }
74
-
75
- result.stats.includedFiles.forEach(addNormalizedDependency);
76
- callback(null, result.css.toString(), result.map);
77
- });
131
+ callback(null, result.css.toString(), result.map);
132
+ });
78
133
  }
79
134
 
80
135
  /**
@@ -84,41 +139,68 @@ function sassLoader(content) {
84
139
  * @returns {Function}
85
140
  */
86
141
  function getRenderFuncFromSassImpl(module) {
87
- const info = module.info;
88
- const components = info.split("\t");
142
+ const { info } = module;
143
+ const components = info.split('\t');
144
+
145
+ if (components.length < 2) {
146
+ throw new Error(`Unknown Sass implementation "${info}".`);
147
+ }
89
148
 
90
- if (components.length < 2) {
91
- throw new Error("Unknown Sass implementation \"" + info + "\".");
149
+ const [implementation, version] = components;
150
+
151
+ if (!semver.valid(version)) {
152
+ throw new Error(`Invalid Sass version "${version}".`);
153
+ }
154
+
155
+ if (implementation === 'dart-sass') {
156
+ if (!semver.satisfies(version, '^1.3.0')) {
157
+ throw new Error(
158
+ `Dart Sass version ${version} is incompatible with ^1.3.0.`
159
+ );
160
+ }
161
+
162
+ return module.render.bind(module);
163
+ } else if (implementation === 'node-sass') {
164
+ if (!semver.satisfies(version, '^4.0.0')) {
165
+ throw new Error(
166
+ `Node Sass version ${version} is incompatible with ^4.0.0.`
167
+ );
92
168
  }
93
169
 
94
- const implementation = components[0];
95
- const version = components[1];
170
+ // There is an issue with node-sass when async custom importers are used
171
+ // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
172
+ // We need to use a job queue to make sure that one thread is always available to the UV lib
173
+ if (nodeSassJobQueue === null) {
174
+ const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4);
96
175
 
97
- if (!semver.valid(version)) {
98
- throw new Error("Invalid Sass version \"" + version + "\".");
176
+ nodeSassJobQueue = async.queue(
177
+ module.render.bind(module),
178
+ threadPoolSize - 1
179
+ );
99
180
  }
100
181
 
101
- if (implementation === "dart-sass") {
102
- if (!semver.satisfies(version, "^1.3.0")) {
103
- throw new Error("Dart Sass version " + version + " is incompatible with ^1.3.0.");
104
- }
105
- return module.render.bind(module);
106
- } else if (implementation === "node-sass") {
107
- if (!semver.satisfies(version, "^4.0.0")) {
108
- throw new Error("Node Sass version " + version + " is incompatible with ^4.0.0.");
109
- }
110
- // There is an issue with node-sass when async custom importers are used
111
- // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
112
- // We need to use a job queue to make sure that one thread is always available to the UV lib
113
- if (nodeSassJobQueue === null) {
114
- const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4);
115
-
116
- nodeSassJobQueue = async.queue(module.render.bind(module), threadPoolSize - 1);
117
- }
118
-
119
- return nodeSassJobQueue.push.bind(nodeSassJobQueue);
182
+ return nodeSassJobQueue.push.bind(nodeSassJobQueue);
183
+ }
184
+
185
+ throw new Error(`Unknown Sass implementation "${implementation}".`);
186
+ }
187
+
188
+ function getDefaultSassImpl() {
189
+ let sassImplPkg = 'node-sass';
190
+
191
+ try {
192
+ require.resolve('node-sass');
193
+ } catch (error) {
194
+ try {
195
+ require.resolve('sass');
196
+ sassImplPkg = 'sass';
197
+ } catch (ignoreError) {
198
+ sassImplPkg = 'node-sass';
120
199
  }
121
- throw new Error("Unknown Sass implementation \"" + implementation + "\".");
200
+ }
201
+
202
+ // eslint-disable-next-line import/no-dynamic-require, global-require
203
+ return require(sassImplPkg);
122
204
  }
123
205
 
124
206
  module.exports = sassLoader;
@@ -1,10 +1,12 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
- const os = require("os");
4
- const utils = require("loader-utils");
5
- const cloneDeep = require("clone-deep");
6
- const path = require("path");
7
- const proxyCustomImporters = require("./proxyCustomImporters");
3
+ const os = require('os');
4
+ const path = require('path');
5
+
6
+ const utils = require('loader-utils');
7
+ const cloneDeep = require('clone-deep');
8
+
9
+ const proxyCustomImporters = require('./proxyCustomImporters');
8
10
 
9
11
  /**
10
12
  * Derives the sass options from the loader context and normalizes its values with sane defaults.
@@ -18,61 +20,78 @@ const proxyCustomImporters = require("./proxyCustomImporters");
18
20
  * @returns {Object}
19
21
  */
20
22
  function normalizeOptions(loaderContext, content, webpackImporter) {
21
- const options = cloneDeep(utils.getOptions(loaderContext)) || {};
22
- const resourcePath = loaderContext.resourcePath;
23
+ const options = cloneDeep(utils.getOptions(loaderContext)) || {};
24
+ const { resourcePath } = loaderContext;
23
25
 
24
- options.data = options.data ? (options.data + os.EOL + content) : content;
26
+ // allow opt.functions to be configured WRT loaderContext
27
+ if (typeof options.functions === 'function') {
28
+ options.functions = options.functions(loaderContext);
29
+ }
25
30
 
26
- // opt.outputStyle
27
- if (!options.outputStyle && loaderContext.minimize) {
28
- options.outputStyle = "compressed";
29
- }
31
+ let { data } = options;
30
32
 
31
- // opt.sourceMap
32
- // Not using the `this.sourceMap` flag because css source maps are different
33
- // @see https://github.com/webpack/css-loader/pull/40
34
- if (options.sourceMap) {
35
- // Deliberately overriding the sourceMap option here.
36
- // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string.
37
- // In case it is a string, options.sourceMap should be a path where the source map is written.
38
- // But since we're using the data option, the source map will not actually be written, but
39
- // all paths in sourceMap.sources will be relative to that path.
40
- // Pretty complicated... :(
41
- options.sourceMap = path.join(process.cwd(), "/sass.map");
42
- if ("sourceMapRoot" in options === false) {
43
- options.sourceMapRoot = process.cwd();
44
- }
45
- if ("omitSourceMapUrl" in options === false) {
46
- // The source map url doesn't make sense because we don't know the output path
47
- // The css-loader will handle that for us
48
- options.omitSourceMapUrl = true;
49
- }
50
- if ("sourceMapContents" in options === false) {
51
- // If sourceMapContents option is not set, set it to true otherwise maps will be empty/null
52
- // when exported by webpack-extract-text-plugin.
53
- options.sourceMapContents = true;
54
- }
55
- }
33
+ if (typeof options.data === 'function') {
34
+ data = options.data(loaderContext);
35
+ }
36
+
37
+ options.data = data ? data + os.EOL + content : content;
56
38
 
57
- // indentedSyntax is a boolean flag.
58
- const ext = path.extname(resourcePath);
39
+ // opt.outputStyle
40
+ if (!options.outputStyle && loaderContext.minimize) {
41
+ options.outputStyle = 'compressed';
42
+ }
59
43
 
60
- // If we are compiling sass and indentedSyntax isn't set, automatically set it.
61
- if (ext && ext.toLowerCase() === ".sass" && "indentedSyntax" in options === false) {
62
- options.indentedSyntax = true;
63
- } else {
64
- options.indentedSyntax = Boolean(options.indentedSyntax);
44
+ // opt.sourceMap
45
+ // Not using the `this.sourceMap` flag because css source maps are different
46
+ // @see https://github.com/webpack/css-loader/pull/40
47
+ if (options.sourceMap) {
48
+ // Deliberately overriding the sourceMap option here.
49
+ // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string.
50
+ // In case it is a string, options.sourceMap should be a path where the source map is written.
51
+ // But since we're using the data option, the source map will not actually be written, but
52
+ // all paths in sourceMap.sources will be relative to that path.
53
+ // Pretty complicated... :(
54
+ options.sourceMap = path.join(process.cwd(), '/sass.map');
55
+ if ('sourceMapRoot' in options === false) {
56
+ options.sourceMapRoot = process.cwd();
65
57
  }
58
+ if ('omitSourceMapUrl' in options === false) {
59
+ // The source map url doesn't make sense because we don't know the output path
60
+ // The css-loader will handle that for us
61
+ options.omitSourceMapUrl = true;
62
+ }
63
+ if ('sourceMapContents' in options === false) {
64
+ // If sourceMapContents option is not set, set it to true otherwise maps will be empty/null
65
+ // when exported by webpack-extract-text-plugin.
66
+ options.sourceMapContents = true;
67
+ }
68
+ }
69
+
70
+ // indentedSyntax is a boolean flag.
71
+ const ext = path.extname(resourcePath);
72
+
73
+ // If we are compiling sass and indentedSyntax isn't set, automatically set it.
74
+ if (
75
+ ext &&
76
+ ext.toLowerCase() === '.sass' &&
77
+ 'indentedSyntax' in options === false
78
+ ) {
79
+ options.indentedSyntax = true;
80
+ } else {
81
+ options.indentedSyntax = Boolean(options.indentedSyntax);
82
+ }
66
83
 
67
- // Allow passing custom importers to `node-sass`. Accepts `Function` or an array of `Function`s.
68
- options.importer = options.importer ? proxyCustomImporters(options.importer, resourcePath) : [];
69
- options.importer.push(webpackImporter);
84
+ // Allow passing custom importers to `node-sass`. Accepts `Function` or an array of `Function`s.
85
+ options.importer = options.importer
86
+ ? proxyCustomImporters(options.importer, resourcePath)
87
+ : [];
88
+ options.importer.push(webpackImporter);
70
89
 
71
- // `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file.
72
- options.includePaths = options.includePaths || [];
73
- options.includePaths.push(path.dirname(resourcePath));
90
+ // `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file.
91
+ options.includePaths = options.includePaths || [];
92
+ options.includePaths.push(path.dirname(resourcePath));
74
93
 
75
- return options;
94
+ return options;
76
95
  }
77
96
 
78
97
  module.exports = normalizeOptions;
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  /**
4
4
  * Creates new custom importers that use the given `resourcePath` if libsass calls the custom importer with `prev`
@@ -10,20 +10,24 @@
10
10
  *
11
11
  * We have to fix this behavior in order to provide a consistent experience to the webpack user.
12
12
  *
13
- * @param {function|Array<function>} importer
13
+ * @param {Function|Array<Function>} importer
14
14
  * @param {string} resourcePath
15
- * @returns {Array<function>}
15
+ * @returns {Array<Function>}
16
16
  */
17
17
  function proxyCustomImporters(importer, resourcePath) {
18
- return [].concat(importer).map((importer) => {
19
- return function (url, prev, done) {
20
- return importer.apply(
21
- this, // eslint-disable-line no-invalid-this
22
- Array.from(arguments)
23
- .map((arg, i) => i === 1 && arg === "stdin" ? resourcePath : arg)
24
- );
25
- };
26
- });
18
+ return [].concat(importer).map(
19
+ // eslint-disable-next-line no-shadow
20
+ (importer) =>
21
+ function customImporter() {
22
+ return importer.apply(
23
+ this,
24
+ // eslint-disable-next-line prefer-rest-params
25
+ Array.from(arguments).map((arg, i) =>
26
+ i === 1 && arg === 'stdin' ? resourcePath : arg
27
+ )
28
+ );
29
+ }
30
+ );
27
31
  }
28
32
 
29
33
  module.exports = proxyCustomImporters;
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  /**
4
4
  * @name PromisedResolve
@@ -16,9 +16,9 @@
16
16
  * @param {Function<Error, string>} done
17
17
  */
18
18
 
19
- const path = require("path");
20
- const tail = require("lodash.tail");
21
- const importsToResolve = require("./importsToResolve");
19
+ const path = require('path');
20
+
21
+ const importsToResolve = require('./importsToResolve');
22
22
 
23
23
  const matchCss = /\.css$/;
24
24
 
@@ -34,39 +34,45 @@ const matchCss = /\.css$/;
34
34
  * @returns {Importer}
35
35
  */
36
36
  function webpackImporter(resourcePath, resolve, addNormalizedDependency) {
37
- function dirContextFrom(fileContext) {
38
- return path.dirname(
39
- // The first file is 'stdin' when we're using the data option
40
- fileContext === "stdin" ? resourcePath : fileContext
41
- );
42
- }
37
+ function dirContextFrom(fileContext) {
38
+ return path.dirname(
39
+ // The first file is 'stdin' when we're using the data option
40
+ fileContext === 'stdin' ? resourcePath : fileContext
41
+ );
42
+ }
43
43
 
44
- function startResolving(dir, importsToResolve) {
45
- return importsToResolve.length === 0 ?
46
- Promise.reject() :
47
- resolve(dir, importsToResolve[0])
48
- .then(resolvedFile => {
49
- // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
50
- // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
51
- addNormalizedDependency(resolvedFile);
52
- return {
53
- // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
54
- file: resolvedFile.replace(matchCss, "")
55
- };
56
- }, () => startResolving(
57
- dir,
58
- tail(importsToResolve)
59
- ));
60
- }
44
+ // eslint-disable-next-line no-shadow
45
+ function startResolving(dir, importsToResolve) {
46
+ return importsToResolve.length === 0
47
+ ? Promise.reject()
48
+ : resolve(dir, importsToResolve[0]).then(
49
+ (resolvedFile) => {
50
+ // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
51
+ // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
52
+ addNormalizedDependency(resolvedFile);
53
+ return {
54
+ // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
55
+ file: resolvedFile.replace(matchCss, ''),
56
+ };
57
+ },
58
+ () => {
59
+ const [, ...tailResult] = importsToResolve;
60
+
61
+ return startResolving(dir, tailResult);
62
+ }
63
+ );
64
+ }
61
65
 
62
- return (url, prev, done) => {
63
- startResolving(
64
- dirContextFrom(prev),
65
- importsToResolve(url)
66
- ) // Catch all resolving errors, return the original file and pass responsibility back to other custom importers
67
- .catch(() => ({ file: url }))
68
- .then(done);
69
- };
66
+ return (url, prev, done) => {
67
+ startResolving(dirContextFrom(prev), importsToResolve(url))
68
+ // Catch all resolving errors, return the original file and pass responsibility back to other custom importers
69
+ .catch(() => {
70
+ return {
71
+ file: url,
72
+ };
73
+ })
74
+ .then(done);
75
+ };
70
76
  }
71
77
 
72
78
  module.exports = webpackImporter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sass-loader",
3
- "version": "7.1.0",
3
+ "version": "7.2.0",
4
4
  "description": "Sass loader for webpack",
5
5
  "author": "J. Tangelder",
6
6
  "license": "MIT",
@@ -12,13 +12,14 @@
12
12
  "appveyor:test": "npm test",
13
13
  "create-spec": "node test/tools/runCreateSpec.js",
14
14
  "lint": "eslint lib test",
15
- "test": "nyc --all mocha -R spec -t 10000",
15
+ "test": "nyc mocha -R spec -t 10000",
16
16
  "test-bootstrap-sass": "webpack-dev-server --config test/bootstrapSass/webpack.config.js --content-base ./test/bootstrapSass",
17
17
  "test-source-map": "webpack-dev-server --config test/sourceMap/webpack.config.js --content-base ./test/sourceMap --inline",
18
18
  "test-watch": "webpack --config test/watch/webpack.config.js",
19
19
  "test-extract-text": "webpack --config test/extractText/webpack.config.js",
20
20
  "test-hmr": "webpack-dev-server --config test/hmr/webpack.config.js --content-base ./test/hmr --hot --inline",
21
- "travis:lint": "npm run lint",
21
+ "travis:lint:commits": "commitlint --from=${TRAVIS_BRANCH} --to=${TRAVIS_COMMIT}",
22
+ "travis:lint": "npm run lint && npm run travis:lint:commits",
22
23
  "travis:test": "npm run test",
23
24
  "travis:coverage": "npm run test",
24
25
  "pretest": "npm run create-spec",
@@ -26,35 +27,43 @@
26
27
  "release": "standard-version"
27
28
  },
28
29
  "dependencies": {
29
- "clone-deep": "^2.0.1",
30
+ "clone-deep": "^4.0.1",
30
31
  "loader-utils": "^1.0.1",
31
- "lodash.tail": "^4.1.1",
32
32
  "neo-async": "^2.5.0",
33
- "pify": "^3.0.0",
33
+ "pify": "^4.0.1",
34
34
  "semver": "^5.5.0"
35
35
  },
36
36
  "devDependencies": {
37
+ "@commitlint/cli": "^7.2.1",
38
+ "@commitlint/config-conventional": "^7.1.2",
39
+ "@webpack-contrib/eslint-config-webpack": "^3.0.0",
37
40
  "bootstrap-sass": "^3.3.5",
38
- "css-loader": "^0.28.4",
39
- "eslint": "^3.16.0",
40
- "eslint-config-peerigon": "^9.0.0",
41
- "eslint-plugin-jsdoc": "^2.4.0",
42
- "file-loader": "^0.11.2",
43
- "mocha": "^3.0.2",
41
+ "css-loader": "^2.0.0",
42
+ "eslint": "^5.10.0",
43
+ "eslint-plugin-import": "^2.14.0",
44
+ "eslint-plugin-jsdoc": "^4.4.3",
45
+ "eslint-plugin-prettier": "^3.0.0",
46
+ "file-loader": "^3.0.1",
47
+ "husky": "^1.2.0",
48
+ "lint-staged": "^8.1.0",
49
+ "mocha": "^6.0.2",
44
50
  "mock-require": "^3.0.1",
45
51
  "node-sass": "^4.5.0",
46
- "nyc": "^11.0.2",
47
- "raw-loader": "^0.5.1",
52
+ "nyc": "^13.1.0",
53
+ "raw-loader": "^1.0.0",
54
+ "prettier": "^1.15.2",
48
55
  "sass": "^1.3.0",
49
- "should": "^11.2.0",
50
- "standard-version": "^4.2.0",
51
- "style-loader": "^0.18.2",
56
+ "should": "^13.2.3",
57
+ "standard-version": "^5.0.2",
58
+ "style-loader": "^0.23.1",
52
59
  "webpack": "^4.5.0",
53
- "webpack-dev-server": "^2.4.1",
54
- "webpack-merge": "^4.0.0"
60
+ "webpack-cli": "^3.1.0",
61
+ "webpack-dev-server": "^3.1.4",
62
+ "webpack-merge": "^4.0.0",
63
+ "chokidar": "^2.1.6"
55
64
  },
56
65
  "engines": {
57
- "node": ">= 6.9.0 || >= 8.9.0"
66
+ "node": ">= 6.9.0"
58
67
  },
59
68
  "peerDependencies": {
60
69
  "webpack": "^3.0.0 || ^4.0.0"
@@ -67,5 +76,21 @@
67
76
  ],
68
77
  "repository": "https://github.com/webpack-contrib/sass-loader.git",
69
78
  "bugs": "https://github.com/webpack-contrib/sass-loader/issues",
70
- "homepage": "https://github.com/webpack-contrib/sass-loader"
79
+ "homepage": "https://github.com/webpack-contrib/sass-loader",
80
+ "husky": {
81
+ "hooks": {
82
+ "pre-commit": "lint-staged"
83
+ }
84
+ },
85
+ "lint-staged": {
86
+ "*.js": [
87
+ "eslint --fix",
88
+ "git add"
89
+ ]
90
+ },
91
+ "commitlint": {
92
+ "extends": [
93
+ "@commitlint/config-conventional"
94
+ ]
95
+ }
71
96
  }