Skip to content

Commit

Permalink
Refactor webpackImporter
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnns committed Dec 27, 2016
1 parent 7dc9731 commit 43cec39
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 55 deletions.
2 changes: 1 addition & 1 deletion lib/importsToResolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const extPrecedence = [".scss", ".sass", ".css"];
* returns an array of import paths to try.
*
* @param {string} request
* @returns {Array}
* @returns {Array<string>}
*/
function importsToResolve(request) {
// libsass' import algorithm works like this:
Expand Down
3 changes: 2 additions & 1 deletion lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const assign = require("object-assign");
const formatSassError = require("./formatSassError");
const proxyCustomImporters = require("./proxyCustomImporters");
const webpackImporter = require("./webpackImporter");
const pify = require("pify");

// This queue makes sure node-sass leaves one thread available for executing
// fs tasks when running the custom importer code.
Expand Down Expand Up @@ -92,7 +93,7 @@ function sassLoader(content) {
sassOptions.importer = sassOptions.importer ? proxyCustomImporters(sassOptions.importer, resourcePath) : [];
sassOptions.importer.push(webpackImporter(
this.resourcePath,
this.resolve.bind(this),
pify(this.resolve.bind(this)),
addNormalizedDependency
));

Expand Down
96 changes: 44 additions & 52 deletions lib/webpackImporter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
"use strict";

/**
* @name PromisedResolve
* @type {Function}
* @param {string} dir
* @param {string} request
* @returns Promise
*/

/**
* @name Importer
* @type {Function}
* @param {string} url
* @param {string} prev
* @param {Function<Error, string>} done
*/

const path = require("path");
const utils = require("loader-utils");
const tail = require("lodash.tail");
const importsToResolve = require("./importsToResolve");

const matchCss = /\.css$/;
Expand All @@ -13,68 +30,43 @@ const matchCss = /\.css$/;
* (based on whether the call is sync or async) because otherwise node-sass doesn't exit.
*
* @param {string} resourcePath
* @param {Function<string, string, Function<Error, string>>} loaderResolve
* @param {PromisedResolve} resolve
* @param {Function<string>} addNormalizedDependency
* @returns {Function<string, string, Function>}
* @returns {Importer}
*/
function webpackImporter(resourcePath, loaderResolve, addNormalizedDependency) {
/**
* Tries to resolve the first url of importsToResolve. If that resolve fails, the next url is tried.
* If all imports fail, the import is passed to libsass which also take includePaths into account.
*
* @param {string} dirContext
* @param {string} originalImport
* @param {Array} importsToResolve
* @param {Function} done
*/
function resolve(dirContext, originalImport, importsToResolve, done) {
const importToResolve = importsToResolve.shift();

if (!importToResolve) {
// No import possibilities left. Let's pass that one back to libsass...
done({
file: originalImport
});
return;
}

loaderResolve(dirContext, importToResolve, (err, resolvedFilename) => {
if (err) {
resolve(dirContext, originalImport, importsToResolve, done);
return;
}
// Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
// in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
addNormalizedDependency(resolvedFilename);
// By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
resolvedFilename = resolvedFilename.replace(matchCss, "");

// Use self.loadModule() before calling done() to make imported files available to
// other webpack tools like postLoaders etc.?

done({
file: resolvedFilename.replace(matchCss, "")
});
});
}

function webpackImporter(resourcePath, resolve, addNormalizedDependency) {
function dirContextFrom(fileContext) {
return path.dirname(
// The first file is 'stdin' when we're using the data option
fileContext === "stdin" ? resourcePath : fileContext
);
}

function startResolving(dir, importsToResolve) {
return importsToResolve.length === 0 ?
Promise.reject() :
resolve(dir, importsToResolve[0])
.then(resolvedFile => {
// Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
// in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
addNormalizedDependency(resolvedFile);
return {
// By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
file: resolvedFile.replace(matchCss, "")
};
}, () => startResolving(
dir,
tail(importsToResolve)
));
}

return (url, prev, done) => {
resolve(
dirContextFrom(
// node-sass returns UNIX-style paths
path.normalize(prev)
),
url,
importsToResolve(utils.urlToRequest(url)),
done
);
startResolving(
dirContextFrom(prev),
importsToResolve(utils.urlToRequest(url))
) // Catch all resolving errors, return the original file and pass responsibility back to other custom importers
.catch(() => ({ file: url }))
.then(done);
};
}

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"dependencies": {
"async": "^2.0.1",
"loader-utils": "^0.2.15",
"object-assign": "^4.1.0"
"lodash.tail": "^4.1.1",
"object-assign": "^4.1.0",
"pify": "^2.3.0"
},
"devDependencies": {
"bootstrap-sass": "^3.3.5",
Expand Down

0 comments on commit 43cec39

Please sign in to comment.