Skip to content

Commit 6439cef

Browse files
Timerjhnns
authored andcommitted
feat: Remove node-sass from peerDependencies (#533)
- Only require("node-sass") on runtime when loader has been called - Based on feedback from #533 and facebook/create-react-app#4195 BREAKING CHANGE: The sass-loader throws an error at runtime now and refuses to compile if the peer dependency is wrong. This could break applications where npm's peer dependency warning was just ignored.
1 parent eb5a555 commit 6439cef

File tree

5 files changed

+71
-5
lines changed

5 files changed

+71
-5
lines changed

.nycrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"lib/**/*.js"
88
],
99
"lines": 97,
10-
"statements": 91,
10+
"statements": 97,
1111
"functions": 100,
12-
"branches": 89,
12+
"branches": 91,
1313
"check-coverage": true
1414
}

lib/loader.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use strict";
22

3-
const sass = require("node-sass");
43
const path = require("path");
54
const async = require("neo-async");
65
const formatSassError = require("./formatSassError");
@@ -12,7 +11,7 @@ const pify = require("pify");
1211
// fs tasks when running the custom importer code.
1312
// This can be removed as soon as node-sass implements a fix for this.
1413
const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4;
15-
const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1);
14+
let asyncSassJobQueue = null;
1615

1716
/**
1817
* The sass-loader makes node-sass available to webpack modules.
@@ -21,6 +20,28 @@ const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1);
2120
* @param {string} content
2221
*/
2322
function sassLoader(content) {
23+
if (asyncSassJobQueue === null) {
24+
let sass;
25+
let sassVersion;
26+
27+
try {
28+
sass = require("node-sass");
29+
sassVersion = /^(\d+)/.exec(require("node-sass/package.json").version).pop();
30+
} catch (e) {
31+
throw new Error(
32+
"`sass-loader` requires `node-sass` >=4. Please install a compatible version."
33+
);
34+
}
35+
36+
if (Number(sassVersion) < 4) {
37+
throw new Error(
38+
"The installed version of `node-sass` is not compatible (expected: >= 4, actual: " + sassVersion + ")."
39+
);
40+
}
41+
42+
asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1);
43+
}
44+
2445
const callback = this.async();
2546
const isSync = typeof callback !== "function";
2647
const self = this;

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"eslint-plugin-jsdoc": "^2.4.0",
4141
"file-loader": "^0.11.2",
4242
"mocha": "^3.0.2",
43+
"mock-require": "^3.0.1",
4344
"node-sass": "^4.5.0",
4445
"nyc": "^11.0.2",
4546
"raw-loader": "^0.5.1",
@@ -54,7 +55,6 @@
5455
"node": ">= 6.9.0 || >= 8.9.0"
5556
},
5657
"peerDependencies": {
57-
"node-sass": "^4.0.0",
5858
"webpack": "^3.0.0 || ^4.0.0"
5959
},
6060
"keywords": [

test/index.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const customFunctions = require("./tools/customFunctions.js");
1111
const pathToSassLoader = require.resolve("../lib/loader.js");
1212
const testLoader = require("./tools/testLoader");
1313
const sassLoader = require(pathToSassLoader);
14+
const mockRequire = require("mock-require");
1415

1516
const CR = /\r/g;
1617
const syntaxStyles = ["scss", "sass"];
@@ -256,6 +257,40 @@ describe("sass-loader", () => {
256257
done();
257258
});
258259
});
260+
it("should output a message when `node-sass` is missing", (done) => {
261+
mockRequire.reRequire(pathToSassLoader);
262+
const module = require("module");
263+
const originalResolve = module._resolveFilename;
264+
265+
module._resolveFilename = function (filename) {
266+
if (!filename.match(/node-sass/)) {
267+
return originalResolve.apply(this, arguments);
268+
}
269+
const err = new Error();
270+
271+
err.code = "MODULE_NOT_FOUND";
272+
throw err;
273+
};
274+
runWebpack({
275+
entry: pathToSassLoader + "!" + pathToErrorFile
276+
}, (err) => {
277+
module._resolveFilename = originalResolve;
278+
mockRequire.reRequire("node-sass");
279+
err.message.should.match(/Please install a compatible version/);
280+
done();
281+
});
282+
});
283+
it("should output a message when `node-sass` is an incompatible version", (done) => {
284+
mockRequire.reRequire(pathToSassLoader);
285+
mockRequire("node-sass/package.json", { version: "3.0.0" });
286+
runWebpack({
287+
entry: pathToSassLoader + "!" + pathToErrorFile
288+
}, (err) => {
289+
mockRequire.stop("node-sass");
290+
err.message.should.match(/The installed version of `node-sass` is not compatible/);
291+
done();
292+
});
293+
});
259294
});
260295
});
261296

0 commit comments

Comments
 (0)