From a810cbd6413c87c95ff38a0dc8fa7d48dfdf4b7f Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Mon, 15 Jan 2018 14:59:20 -0500 Subject: [PATCH 01/19] Move node-sass resolve to runtime --- lib/loader.js | 26 ++++++++++++++++++++++++-- package.json | 3 +-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 0a1fcd7f..67bd4266 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,6 +1,5 @@ "use strict"; -const sass = require("node-sass"); const path = require("path"); const async = require("neo-async"); const formatSassError = require("./formatSassError"); @@ -12,7 +11,21 @@ const pify = require("pify"); // fs tasks when running the custom importer code. // This can be removed as soon as node-sass implements a fix for this. const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; -const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); +let asyncSassJobQueue = null; +let loadingError = null; + +try { + const sass = require("node-sass"); + const sassVersion = require("node-sass/package.json").version; + + if (/^4[.]/.test(sassVersion)) { + asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); + } else { + loadingError = new Error("The installed version of \`node-sass\` is not compatible (expected: 4.x, actual: " + sassVersion + ")."); + } +} catch (e) { + // ignored +} /** * The sass-loader makes node-sass available to webpack modules. @@ -35,6 +48,15 @@ function sassLoader(content) { throw new Error("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333"); } + if (asyncSassJobQueue == null) { + if (loadingError) { + callback(loadingError); + return; + } + callback(new Error("Please install `node-sass@4.x` to use `sass-loader`.")); + return; + } + const options = normalizeOptions(this, content, webpackImporter( resourcePath, pify(this.resolve.bind(this)), diff --git a/package.json b/package.json index 361512d4..cb094f1e 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "node": ">= 4.3 < 5.0.0 || >= 5.10" }, "peerDependencies": { - "node-sass": "^4.0.0", "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" }, "keywords": [ @@ -65,4 +64,4 @@ "repository": "https://github.com/webpack-contrib/sass-loader.git", "bugs": "https://github.com/webpack-contrib/sass-loader/issues", "homepage": "https://github.com/webpack-contrib/sass-loader" -} +} \ No newline at end of file From 2319c4e2690c86793e7127267a905b7c0848c3e6 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Mon, 15 Jan 2018 15:06:57 -0500 Subject: [PATCH 02/19] Adjust coverage to account for new code --- .nycrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.nycrc b/.nycrc index d0117319..f560da81 100644 --- a/.nycrc +++ b/.nycrc @@ -6,9 +6,9 @@ "include": [ "lib/**/*.js" ], - "lines": 97, + "lines": 93, "statements": 91, "functions": 100, - "branches": 89, + "branches": 86, "check-coverage": true } From 349cda47ebadb0cffa143c99a8206b75e332399c Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Tue, 16 Jan 2018 08:21:12 -0500 Subject: [PATCH 03/19] PR review changes --- lib/loader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 67bd4266..26b62ce0 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -18,13 +18,13 @@ try { const sass = require("node-sass"); const sassVersion = require("node-sass/package.json").version; - if (/^4[.]/.test(sassVersion)) { + if (Number(sassVersion[0]) >= 4) { asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); } else { - loadingError = new Error("The installed version of \`node-sass\` is not compatible (expected: 4.x, actual: " + sassVersion + ")."); + throw new Error("The installed version of \`node-sass\` is not compatible (expected: 4.x, actual: " + sassVersion + ")."); } } catch (e) { - // ignored + loadingError = e; } /** From 434f1e84058c3d064eedaf4e37a9f77f32115134 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Tue, 16 Jan 2018 08:39:24 -0500 Subject: [PATCH 04/19] Move var closer to usage --- lib/loader.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 26b62ce0..c07b9bbd 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -7,10 +7,6 @@ const webpackImporter = require("./webpackImporter"); const normalizeOptions = require("./normalizeOptions"); const pify = require("pify"); -// This queue makes sure node-sass leaves one thread available for executing -// fs tasks when running the custom importer code. -// This can be removed as soon as node-sass implements a fix for this. -const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; let asyncSassJobQueue = null; let loadingError = null; @@ -19,6 +15,11 @@ try { const sassVersion = require("node-sass/package.json").version; if (Number(sassVersion[0]) >= 4) { + // This queue makes sure node-sass leaves one thread available for executing + // fs tasks when running the custom importer code. + // This can be removed as soon as node-sass implements a fix for this. + const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; + asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); } else { throw new Error("The installed version of \`node-sass\` is not compatible (expected: 4.x, actual: " + sassVersion + ")."); From 71d76bee3d9ba0cb79c347a06a6c5d3edf204de0 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Tue, 16 Jan 2018 08:41:40 -0500 Subject: [PATCH 05/19] Update expected version strings --- lib/loader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index c07b9bbd..1731878b 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -22,7 +22,7 @@ try { asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); } else { - throw new Error("The installed version of \`node-sass\` is not compatible (expected: 4.x, actual: " + sassVersion + ")."); + throw new Error("The installed version of \`node-sass\` is not compatible (expected: >=4.x, actual: " + sassVersion + ")."); } } catch (e) { loadingError = e; @@ -54,7 +54,7 @@ function sassLoader(content) { callback(loadingError); return; } - callback(new Error("Please install `node-sass@4.x` to use `sass-loader`.")); + callback(new Error("Please install `node-sass >=4.x` to use `sass-loader`.")); return; } From 751fc1334222aaf5fd632bdedcd74007d5b41aba Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Tue, 16 Jan 2018 09:34:20 -0500 Subject: [PATCH 06/19] Reduce line coverage slightly --- .nycrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nycrc b/.nycrc index f560da81..271df082 100644 --- a/.nycrc +++ b/.nycrc @@ -6,7 +6,7 @@ "include": [ "lib/**/*.js" ], - "lines": 93, + "lines": 92, "statements": 91, "functions": 100, "branches": 86, From 6621317b35ecdaaa193a4fc559f69937ab20befe Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 17 Jan 2018 08:43:26 -0500 Subject: [PATCH 07/19] Add compilers folder for future use --- lib/compilers/index.js | 39 ++++++++++++++ lib/compilers/libsass.js | 27 ++++++++++ lib/loader.js | 114 ++++++++++++++++++--------------------- 3 files changed, 117 insertions(+), 63 deletions(-) create mode 100644 lib/compilers/index.js create mode 100644 lib/compilers/libsass.js diff --git a/lib/compilers/index.js b/lib/compilers/index.js new file mode 100644 index 00000000..7c9e4bb3 --- /dev/null +++ b/lib/compilers/index.js @@ -0,0 +1,39 @@ +"use strict"; + +const libsass = require("./libsass"); + +const compilers = [libsass]; + +const SUGGESTED_COMPILER_MESSAGE = "A Sass compiler is required by `sass-loader`, but none were found. Please install `node-sass` >= 4."; + +function getCompilerQueue() { + // Stores the first error encountered while loading a compiler (e.g. invalid version) + // This error will be ignored if one of the compilers succeed (since we will eventually support multiple). + let firstError; + + for (const compiler of compilers) { + try { + const value = compiler.getCompilerQueue(); + + if (value == null) { + continue; + } + return { error: false, reason: null, value }; + } catch (e) { + if (firstError == null) { + firstError = e; + } + } + } + // Return the error encountered while loading a compiler, otherwise say + // there was no compiler found. + return { + error: true, + reason: firstError || new Error(SUGGESTED_COMPILER_MESSAGE), + value: null + }; +} + +module.exports = { + getCompilerQueue +}; diff --git a/lib/compilers/libsass.js b/lib/compilers/libsass.js new file mode 100644 index 00000000..fec8d984 --- /dev/null +++ b/lib/compilers/libsass.js @@ -0,0 +1,27 @@ +"use strict"; +const async = require("async"); + +function getCompilerQueue() { + let sass; + let sassVersion; + + try { + sass = require("node-sass"); + sassVersion = require("node-sass/package.json").version; + } catch (e) { + return null; + } + + if (Number(sassVersion[0]) < 4) { + throw new Error("The installed version of \`node-sass\` is not compatible (expected: >= 4, actual: " + sassVersion + ")."); + } + + // This queue makes sure node-sass leaves one thread available for executing + // fs tasks when running the custom importer code. + // This can be removed as soon as node-sass implements a fix for this. + const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; + + return async.queue(sass.render, threadPoolSize - 1); +} + +module.exports = { getCompilerQueue }; diff --git a/lib/loader.js b/lib/loader.js index 1731878b..5ae963ac 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,32 +1,14 @@ -"use strict"; +'use strict' -const path = require("path"); -const async = require("neo-async"); -const formatSassError = require("./formatSassError"); -const webpackImporter = require("./webpackImporter"); -const normalizeOptions = require("./normalizeOptions"); -const pify = require("pify"); +const path = require('path') +const async = require('neo-async') +const formatSassError = require('./formatSassError') +const webpackImporter = require('./webpackImporter') +const normalizeOptions = require('./normalizeOptions') +const pify = require('pify') +const compilers = require('./compilers') -let asyncSassJobQueue = null; -let loadingError = null; - -try { - const sass = require("node-sass"); - const sassVersion = require("node-sass/package.json").version; - - if (Number(sassVersion[0]) >= 4) { - // This queue makes sure node-sass leaves one thread available for executing - // fs tasks when running the custom importer code. - // This can be removed as soon as node-sass implements a fix for this. - const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; - - asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); - } else { - throw new Error("The installed version of \`node-sass\` is not compatible (expected: >=4.x, actual: " + sassVersion + ")."); - } -} catch (e) { - loadingError = e; -} +const asyncJobQueue = compilers.getCompilerQueue() /** * The sass-loader makes node-sass available to webpack modules. @@ -35,72 +17,78 @@ try { * @param {string} content */ function sassLoader(content) { - const callback = this.async(); - const isSync = typeof callback !== "function"; - const self = this; - const resourcePath = this.resourcePath; + const callback = this.async() + const isSync = typeof callback !== 'function' + const self = this + const resourcePath = this.resourcePath function addNormalizedDependency(file) { // node-sass returns POSIX paths - self.dependency(path.normalize(file)); + self.dependency(path.normalize(file)) } if (isSync) { - throw new Error("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333"); + throw new Error( + 'Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333' + ) } - if (asyncSassJobQueue == null) { - if (loadingError) { - callback(loadingError); - return; - } - callback(new Error("Please install `node-sass >=4.x` to use `sass-loader`.")); - return; + const compilerError = asyncJobQueue.error + + if (compilerError) { + callback(asyncJobQueue.reason) + return } - const options = normalizeOptions(this, content, webpackImporter( - resourcePath, - pify(this.resolve.bind(this)), - addNormalizedDependency - )); + const options = normalizeOptions( + this, + content, + webpackImporter( + resourcePath, + pify(this.resolve.bind(this)), + addNormalizedDependency + ) + ) // Skip empty files, otherwise it will stop webpack, see issue #21 - if (options.data.trim() === "") { - callback(null, ""); - return; + if (options.data.trim() === '') { + callback(null, '') + return } // start the actual rendering + const asyncSassJobQueue = asyncJobQueue.value + asyncSassJobQueue.push(options, (err, result) => { if (err) { - formatSassError(err, this.resourcePath); - err.file && this.dependency(err.file); - callback(err); - return; + formatSassError(err, this.resourcePath) + err.file && this.dependency(err.file) + callback(err) + return } - if (result.map && result.map !== "{}") { - result.map = JSON.parse(result.map); + if (result.map && result.map !== '{}') { + result.map = JSON.parse(result.map) // result.map.file is an optional property that provides the output filename. // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it. - delete result.map.file; + delete result.map.file // The first source is 'stdin' according to node-sass because we've used the data input. // Now let's override that value with the correct relative path. // Since we specified options.sourceMap = path.join(process.cwd(), "/sass.map"); in normalizeOptions, // we know that this path is relative to process.cwd(). This is how node-sass works. - result.map.sources[0] = path.relative(process.cwd(), resourcePath); + result.map.sources[0] = path.relative(process.cwd(), resourcePath) // node-sass returns POSIX paths, that's why we need to transform them back to native paths. // This fixes an error on windows where the source-map module cannot resolve the source maps. // @see https://github.com/webpack-contrib/sass-loader/issues/366#issuecomment-279460722 - result.map.sourceRoot = path.normalize(result.map.sourceRoot); - result.map.sources = result.map.sources.map(path.normalize); + result.map.sourceRoot = path.normalize(result.map.sourceRoot) + result.map.sources = result.map.sources.map(path.normalize) } else { - result.map = null; + result.map = null } - result.stats.includedFiles.forEach(addNormalizedDependency); - callback(null, result.css.toString(), result.map); - }); + result.stats.includedFiles.forEach(addNormalizedDependency) + callback(null, result.css.toString(), result.map) + }) } -module.exports = sassLoader; +module.exports = sassLoader From c874993c32e62ddf45f6744e2efbbd4bf7b04544 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 17 Jan 2018 08:44:29 -0500 Subject: [PATCH 08/19] Adjust coverage minimums --- .nycrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nycrc b/.nycrc index 271df082..c0cca901 100644 --- a/.nycrc +++ b/.nycrc @@ -9,6 +9,6 @@ "lines": 92, "statements": 91, "functions": 100, - "branches": 86, + "branches": 82, "check-coverage": true } From e72287ec337937ee050a50128a4acf313d3e88f8 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 16:07:40 -0400 Subject: [PATCH 09/19] Re-adjust formatting --- lib/compilers/{libsass.js => node-sass.js} | 8 ++- lib/loader.js | 77 +++++++++++----------- package.json | 2 +- 3 files changed, 45 insertions(+), 42 deletions(-) rename lib/compilers/{libsass.js => node-sass.js} (74%) diff --git a/lib/compilers/libsass.js b/lib/compilers/node-sass.js similarity index 74% rename from lib/compilers/libsass.js rename to lib/compilers/node-sass.js index fec8d984..7e6dada8 100644 --- a/lib/compilers/libsass.js +++ b/lib/compilers/node-sass.js @@ -1,5 +1,5 @@ "use strict"; -const async = require("async"); +const async = require("neo-async"); function getCompilerQueue() { let sass; @@ -13,7 +13,11 @@ function getCompilerQueue() { } if (Number(sassVersion[0]) < 4) { - throw new Error("The installed version of \`node-sass\` is not compatible (expected: >= 4, actual: " + sassVersion + ")."); + throw new Error( + "The installed version of `node-sass` is not compatible (expected: >= 4, actual: " + + sassVersion + + ")." + ); } // This queue makes sure node-sass leaves one thread available for executing diff --git a/lib/loader.js b/lib/loader.js index 5ae963ac..12bc6ecf 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,14 +1,13 @@ -'use strict' +"use strict"; -const path = require('path') -const async = require('neo-async') -const formatSassError = require('./formatSassError') -const webpackImporter = require('./webpackImporter') -const normalizeOptions = require('./normalizeOptions') -const pify = require('pify') -const compilers = require('./compilers') +const path = require("path"); +const formatSassError = require("./formatSassError"); +const webpackImporter = require("./webpackImporter"); +const normalizeOptions = require("./normalizeOptions"); +const pify = require("pify"); +const compilers = require("./compilers"); -const asyncJobQueue = compilers.getCompilerQueue() +const asyncJobQueue = compilers.getCompilerQueue(); /** * The sass-loader makes node-sass available to webpack modules. @@ -17,27 +16,27 @@ const asyncJobQueue = compilers.getCompilerQueue() * @param {string} content */ function sassLoader(content) { - const callback = this.async() - const isSync = typeof callback !== 'function' - const self = this - const resourcePath = this.resourcePath + const callback = this.async(); + const isSync = typeof callback !== "function"; + const self = this; + const resourcePath = this.resourcePath; function addNormalizedDependency(file) { // node-sass returns POSIX paths - self.dependency(path.normalize(file)) + self.dependency(path.normalize(file)); } if (isSync) { throw new Error( - 'Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333' - ) + "Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333" + ); } - const compilerError = asyncJobQueue.error + const compilerError = asyncJobQueue.error; if (compilerError) { - callback(asyncJobQueue.reason) - return + callback(asyncJobQueue.reason); + return; } const options = normalizeOptions( @@ -48,47 +47,47 @@ function sassLoader(content) { pify(this.resolve.bind(this)), addNormalizedDependency ) - ) + ); // Skip empty files, otherwise it will stop webpack, see issue #21 - if (options.data.trim() === '') { - callback(null, '') - return + if (options.data.trim() === "") { + callback(null, ""); + return; } // start the actual rendering - const asyncSassJobQueue = asyncJobQueue.value + const asyncSassJobQueue = asyncJobQueue.value; asyncSassJobQueue.push(options, (err, result) => { if (err) { - formatSassError(err, this.resourcePath) - err.file && this.dependency(err.file) - callback(err) - return + formatSassError(err, this.resourcePath); + err.file && this.dependency(err.file); + callback(err); + return; } - if (result.map && result.map !== '{}') { - result.map = JSON.parse(result.map) + if (result.map && result.map !== "{}") { + result.map = JSON.parse(result.map); // result.map.file is an optional property that provides the output filename. // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it. - delete result.map.file + delete result.map.file; // The first source is 'stdin' according to node-sass because we've used the data input. // Now let's override that value with the correct relative path. // Since we specified options.sourceMap = path.join(process.cwd(), "/sass.map"); in normalizeOptions, // we know that this path is relative to process.cwd(). This is how node-sass works. - result.map.sources[0] = path.relative(process.cwd(), resourcePath) + result.map.sources[0] = path.relative(process.cwd(), resourcePath); // node-sass returns POSIX paths, that's why we need to transform them back to native paths. // This fixes an error on windows where the source-map module cannot resolve the source maps. // @see https://github.com/webpack-contrib/sass-loader/issues/366#issuecomment-279460722 - result.map.sourceRoot = path.normalize(result.map.sourceRoot) - result.map.sources = result.map.sources.map(path.normalize) + result.map.sourceRoot = path.normalize(result.map.sourceRoot); + result.map.sources = result.map.sources.map(path.normalize); } else { - result.map = null + result.map = null; } - result.stats.includedFiles.forEach(addNormalizedDependency) - callback(null, result.css.toString(), result.map) - }) + result.stats.includedFiles.forEach(addNormalizedDependency); + callback(null, result.css.toString(), result.map); + }); } -module.exports = sassLoader +module.exports = sassLoader; diff --git a/package.json b/package.json index cb094f1e..7fd8a1d0 100644 --- a/package.json +++ b/package.json @@ -64,4 +64,4 @@ "repository": "https://github.com/webpack-contrib/sass-loader.git", "bugs": "https://github.com/webpack-contrib/sass-loader/issues", "homepage": "https://github.com/webpack-contrib/sass-loader" -} \ No newline at end of file +} From 0618fbd1558033d099b569b493a3cd7efc878ce2 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 16:26:00 -0400 Subject: [PATCH 10/19] Add `dart-sass` as a supported compiler --- lib/compilers/dart-sass.js | 31 +++++++++++++++++++++++++++++++ lib/compilers/index.js | 9 +++++---- 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 lib/compilers/dart-sass.js diff --git a/lib/compilers/dart-sass.js b/lib/compilers/dart-sass.js new file mode 100644 index 00000000..ba20fe1e --- /dev/null +++ b/lib/compilers/dart-sass.js @@ -0,0 +1,31 @@ +"use strict"; +const async = require("neo-async"); + +function getCompilerQueue() { + let sass; + let sassVersion; + + try { + sass = require("sass"); + sassVersion = require("sass/package.json").version; + } catch (e) { + return null; + } + + if (Number(sassVersion[0]) !== 1) { + throw new Error( + "The installed version of `sass` is not compatible (expected: 1.x, actual: " + + sassVersion + + ")." + ); + } + + // This queue makes sure sass leaves one thread available for executing + // fs tasks when running the custom importer code. + // This can be removed as soon as sass implements a fix for this. + const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; + + return async.queue(sass.render, threadPoolSize - 1); +} + +module.exports = { getCompilerQueue }; diff --git a/lib/compilers/index.js b/lib/compilers/index.js index 7c9e4bb3..585d82c5 100644 --- a/lib/compilers/index.js +++ b/lib/compilers/index.js @@ -1,10 +1,11 @@ "use strict"; -const libsass = require("./libsass"); +const nodeSass = require("./node-sass"); +const dartSass = require("./dart-sass"); -const compilers = [libsass]; +const compilers = [nodeSass, dartSass]; -const SUGGESTED_COMPILER_MESSAGE = "A Sass compiler is required by `sass-loader`, but none were found. Please install `node-sass` >= 4."; +const SUGGESTED_COMPILER_MESSAGE = "A Sass compiler is required by `sass-loader`, but none were found. Please install `node-sass>=4` or `sass@^1`."; function getCompilerQueue() { // Stores the first error encountered while loading a compiler (e.g. invalid version) @@ -15,7 +16,7 @@ function getCompilerQueue() { try { const value = compiler.getCompilerQueue(); - if (value == null) { + if (value === null) { continue; } return { error: false, reason: null, value }; From d2dae27210a01b7be9e23ee0386743335d0e75ea Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 16:28:37 -0400 Subject: [PATCH 11/19] Restore old formatting --- lib/loader.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 12bc6ecf..bdc4d712 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -39,15 +39,11 @@ function sassLoader(content) { return; } - const options = normalizeOptions( - this, - content, - webpackImporter( - resourcePath, - pify(this.resolve.bind(this)), - addNormalizedDependency - ) - ); + const options = normalizeOptions(this, content, webpackImporter( + resourcePath, + pify(this.resolve.bind(this)), + addNormalizedDependency + )); // Skip empty files, otherwise it will stop webpack, see issue #21 if (options.data.trim() === "") { From 9efb59182544cc6031a4e983226c645e64ce0c76 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 16:46:24 -0400 Subject: [PATCH 12/19] Rip out changes --- lib/compilers/dart-sass.js | 31 ----------------------------- lib/compilers/index.js | 40 -------------------------------------- lib/compilers/node-sass.js | 31 ----------------------------- lib/loader.js | 4 +--- 4 files changed, 1 insertion(+), 105 deletions(-) delete mode 100644 lib/compilers/dart-sass.js delete mode 100644 lib/compilers/index.js delete mode 100644 lib/compilers/node-sass.js diff --git a/lib/compilers/dart-sass.js b/lib/compilers/dart-sass.js deleted file mode 100644 index ba20fe1e..00000000 --- a/lib/compilers/dart-sass.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -const async = require("neo-async"); - -function getCompilerQueue() { - let sass; - let sassVersion; - - try { - sass = require("sass"); - sassVersion = require("sass/package.json").version; - } catch (e) { - return null; - } - - if (Number(sassVersion[0]) !== 1) { - throw new Error( - "The installed version of `sass` is not compatible (expected: 1.x, actual: " + - sassVersion + - ")." - ); - } - - // This queue makes sure sass leaves one thread available for executing - // fs tasks when running the custom importer code. - // This can be removed as soon as sass implements a fix for this. - const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; - - return async.queue(sass.render, threadPoolSize - 1); -} - -module.exports = { getCompilerQueue }; diff --git a/lib/compilers/index.js b/lib/compilers/index.js deleted file mode 100644 index 585d82c5..00000000 --- a/lib/compilers/index.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -const nodeSass = require("./node-sass"); -const dartSass = require("./dart-sass"); - -const compilers = [nodeSass, dartSass]; - -const SUGGESTED_COMPILER_MESSAGE = "A Sass compiler is required by `sass-loader`, but none were found. Please install `node-sass>=4` or `sass@^1`."; - -function getCompilerQueue() { - // Stores the first error encountered while loading a compiler (e.g. invalid version) - // This error will be ignored if one of the compilers succeed (since we will eventually support multiple). - let firstError; - - for (const compiler of compilers) { - try { - const value = compiler.getCompilerQueue(); - - if (value === null) { - continue; - } - return { error: false, reason: null, value }; - } catch (e) { - if (firstError == null) { - firstError = e; - } - } - } - // Return the error encountered while loading a compiler, otherwise say - // there was no compiler found. - return { - error: true, - reason: firstError || new Error(SUGGESTED_COMPILER_MESSAGE), - value: null - }; -} - -module.exports = { - getCompilerQueue -}; diff --git a/lib/compilers/node-sass.js b/lib/compilers/node-sass.js deleted file mode 100644 index 7e6dada8..00000000 --- a/lib/compilers/node-sass.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -const async = require("neo-async"); - -function getCompilerQueue() { - let sass; - let sassVersion; - - try { - sass = require("node-sass"); - sassVersion = require("node-sass/package.json").version; - } catch (e) { - return null; - } - - if (Number(sassVersion[0]) < 4) { - throw new Error( - "The installed version of `node-sass` is not compatible (expected: >= 4, actual: " + - sassVersion + - ")." - ); - } - - // This queue makes sure node-sass leaves one thread available for executing - // fs tasks when running the custom importer code. - // This can be removed as soon as node-sass implements a fix for this. - const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; - - return async.queue(sass.render, threadPoolSize - 1); -} - -module.exports = { getCompilerQueue }; diff --git a/lib/loader.js b/lib/loader.js index bdc4d712..0bfc4380 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -27,9 +27,7 @@ function sassLoader(content) { } if (isSync) { - throw new Error( - "Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333" - ); + throw new Error("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333"); } const compilerError = asyncJobQueue.error; From 33ee227cc749830f236412dc264b79037f5882cf Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 16:47:55 -0400 Subject: [PATCH 13/19] Remove rest of changes --- lib/loader.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 0bfc4380..0a1fcd7f 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,13 +1,18 @@ "use strict"; +const sass = require("node-sass"); const path = require("path"); +const async = require("neo-async"); const formatSassError = require("./formatSassError"); const webpackImporter = require("./webpackImporter"); const normalizeOptions = require("./normalizeOptions"); const pify = require("pify"); -const compilers = require("./compilers"); -const asyncJobQueue = compilers.getCompilerQueue(); +// This queue makes sure node-sass leaves one thread available for executing +// fs tasks when running the custom importer code. +// This can be removed as soon as node-sass implements a fix for this. +const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; +const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); /** * The sass-loader makes node-sass available to webpack modules. @@ -30,13 +35,6 @@ function sassLoader(content) { throw new Error("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333"); } - const compilerError = asyncJobQueue.error; - - if (compilerError) { - callback(asyncJobQueue.reason); - return; - } - const options = normalizeOptions(this, content, webpackImporter( resourcePath, pify(this.resolve.bind(this)), @@ -50,8 +48,6 @@ function sassLoader(content) { } // start the actual rendering - const asyncSassJobQueue = asyncJobQueue.value; - asyncSassJobQueue.push(options, (err, result) => { if (err) { formatSassError(err, this.resourcePath); From 82acdcaf2fdc7b841ff70c957a2c0f6518a1d922 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 17:05:48 -0400 Subject: [PATCH 14/19] Defer setting of `asyncSassJobQueue` --- lib/loader.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/loader.js b/lib/loader.js index 0a1fcd7f..cc810012 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,6 +1,5 @@ "use strict"; -const sass = require("node-sass"); const path = require("path"); const async = require("neo-async"); const formatSassError = require("./formatSassError"); @@ -12,7 +11,7 @@ const pify = require("pify"); // fs tasks when running the custom importer code. // This can be removed as soon as node-sass implements a fix for this. const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4; -const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); +let asyncSassJobQueue = null; /** * The sass-loader makes node-sass available to webpack modules. @@ -21,6 +20,28 @@ const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); * @param {string} content */ function sassLoader(content) { + if (asyncSassJobQueue === null) { + let sass; + let sassVersion; + + try { + sass = require("node-sass"); + sassVersion = /^(\d+)/.exec(require("node-sass/package.json").version).pop(); + } catch (e) { + throw new Error( + "`sass-loader` requires `node-sass` >=4. Please install a compatible version." + ); + } + + if (Number(sassVersion[0]) < 4) { + throw new Error( + "The installed version of `node-sass` is not compatible (expected: >= 4, actual: " + sassVersion + ")." + ); + } + + asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1); + } + const callback = this.async(); const isSync = typeof callback !== "function"; const self = this; From 96d0730ffa898f3d3a5d3965bb999b48336ad138 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 17:12:23 -0400 Subject: [PATCH 15/19] Oops --- lib/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/loader.js b/lib/loader.js index cc810012..b56668ff 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -33,7 +33,7 @@ function sassLoader(content) { ); } - if (Number(sassVersion[0]) < 4) { + if (Number(sassVersion) < 4) { throw new Error( "The installed version of `node-sass` is not compatible (expected: >= 4, actual: " + sassVersion + ")." ); From 1a6b8cc4ef428f8204158820314caea4ad83dab9 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 17:59:57 -0400 Subject: [PATCH 16/19] Add tests --- .nycrc | 4 ++-- package.json | 1 + test/index.test.js | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.nycrc b/.nycrc index c0cca901..d0117319 100644 --- a/.nycrc +++ b/.nycrc @@ -6,9 +6,9 @@ "include": [ "lib/**/*.js" ], - "lines": 92, + "lines": 97, "statements": 91, "functions": 100, - "branches": 82, + "branches": 89, "check-coverage": true } diff --git a/package.json b/package.json index 7fd8a1d0..1a7a848e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "eslint-plugin-jsdoc": "^2.4.0", "file-loader": "^0.11.2", "mocha": "^3.0.2", + "mock-require": "^3.0.1", "node-sass": "^4.5.0", "nyc": "^11.0.2", "raw-loader": "^0.5.1", diff --git a/test/index.test.js b/test/index.test.js index fe282366..953ef3b8 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -11,6 +11,7 @@ const customFunctions = require("./tools/customFunctions.js"); const pathToSassLoader = require.resolve("../lib/loader.js"); const testLoader = require("./tools/testLoader"); const sassLoader = require(pathToSassLoader); +const mockRequire = require("mock-require"); const CR = /\r/g; const syntaxStyles = ["scss", "sass"]; @@ -256,6 +257,40 @@ describe("sass-loader", () => { done(); }); }); + it("should output a message when `node-sass` is missing", (done) => { + mockRequire.reRequire(pathToSassLoader); + const module = require("module"); + const originalResolve = module._resolveFilename; + + module._resolveFilename = function (filename) { + if (!filename.match(/node-sass/)) { + return originalResolve.apply(this, arguments); + } + const err = new Error(); + + err.code = "MODULE_NOT_FOUND"; + throw err; + }; + runWebpack({ + entry: pathToSassLoader + "!" + pathToErrorFile + }, (err) => { + module._resolveFilename = originalResolve; + mockRequire.reRequire("node-sass"); + err.message.should.match(/Please install a compatible version/); + done(); + }); + }); + it("should output a message when `node-sass` is an incompatible version", (done) => { + mockRequire.reRequire(pathToSassLoader); + mockRequire("node-sass/package.json", { version: "3.0.0" }); + runWebpack({ + entry: pathToSassLoader + "!" + pathToErrorFile + }, (err) => { + mockRequire.stop("node-sass"); + err.message.should.match(/The installed version of `node-sass` is not compatible/); + done(); + }); + }); }); }); From 2df152973ad89738a778cc1e2c54c9b4ed3f7d0f Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 18:01:22 -0400 Subject: [PATCH 17/19] Bump min coverage requirements --- .nycrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.nycrc b/.nycrc index d0117319..666465eb 100644 --- a/.nycrc +++ b/.nycrc @@ -7,8 +7,8 @@ "lib/**/*.js" ], "lines": 97, - "statements": 91, + "statements": 97, "functions": 100, - "branches": 89, + "branches": 91, "check-coverage": true } From dd0d28b903b3697c73d8847875903ce03fd034d7 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 25 Mar 2018 18:04:58 -0400 Subject: [PATCH 18/19] Make tests pass with webpack 4 --- test/index.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.test.js b/test/index.test.js index 953ef3b8..9797f16c 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -300,6 +300,7 @@ function readCss(ext, id) { function runWebpack(baseConfig, done) { const webpackConfig = merge({ + mode: "development", output: { path: path.join(__dirname, "output"), filename: "bundle.js", From 2cb77d92726dbc0da043e6cfa41dd12d2e196e28 Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Thu, 12 Apr 2018 23:48:36 +0200 Subject: [PATCH 19/19] chore: Update package-lock.json - Add mock-require --- package-lock.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7771f987..afb0baff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5290,6 +5290,16 @@ } } }, + "mock-require": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.2.tgz", + "integrity": "sha512-aD/Y1ZFHqw5pHg3HVQ50dLbfaAAcytS6sqLuhP51Dk3TSPdFb2VkSAa3mjrHifLIlGAtwQHJHINafAyqAne7vA==", + "dev": true, + "requires": { + "get-caller-file": "1.0.2", + "normalize-path": "2.1.1" + } + }, "modify-values": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.0.tgz",