Skip to content

Commit

Permalink
Move webpackImporter to dedicated module
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnns committed Dec 26, 2016
1 parent f8ea88a commit 7dc9731
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 71 deletions.
77 changes: 6 additions & 71 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ const os = require("os");
const async = require("async");
const assign = require("object-assign");
const formatSassError = require("./formatSassError");
const importsToResolve = require("./importsToResolve");
const proxyCustomImporters = require("./proxyCustomImporters");

const matchCss = /\.css$/;
const webpackImporter = require("./webpackImporter");

// This queue makes sure node-sass leaves one thread available for executing
// fs tasks when running the custom importer code.
Expand All @@ -31,73 +29,6 @@ function sassLoader(content) {
const self = this;
const resourcePath = this.resourcePath;

/**
* Returns an importer that uses webpack's resolving algorithm.
*
* It's important that the returned function has the correct number of arguments
* (based on whether the call is sync or async) because otherwise node-sass doesn't exit.
*
* @returns {Function}
*/
function getWebpackImporter() {
return function asyncWebpackImporter(url, fileContext, done) {
// node-sass returns UNIX-style paths
fileContext = path.normalize(fileContext);
const request = utils.urlToRequest(url, sassOptions.root);
const dirContext = fileToDirContext(fileContext);

resolve(dirContext, url, importsToResolve(request), done);
};
}

/**
* 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;
}

self.resolve(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 fileToDirContext(fileContext) {
// The first file is 'stdin' when we're using the data option
if (fileContext === "stdin") {
fileContext = resourcePath;
}
return path.dirname(fileContext);
}

// When files have been imported via the includePaths-option, these files need to be
// introduced to webpack in order to make them watchable.
function addIncludedFilesToWebpack(includedFiles) {
Expand Down Expand Up @@ -159,7 +90,11 @@ function sassLoader(content) {

// Allow passing custom importers to `node-sass`. Accepts `Function` or an array of `Function`s.
sassOptions.importer = sassOptions.importer ? proxyCustomImporters(sassOptions.importer, resourcePath) : [];
sassOptions.importer.push(getWebpackImporter());
sassOptions.importer.push(webpackImporter(
this.resourcePath,
this.resolve.bind(this),
addNormalizedDependency
));

// `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file.
sassOptions.includePaths = sassOptions.includePaths ? [].concat(sassOptions.includePaths) : [];
Expand Down
81 changes: 81 additions & 0 deletions lib/webpackImporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use strict";

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

const matchCss = /\.css$/;

/**
* Returns an importer that uses webpack's resolving algorithm.
*
* It's important that the returned function has the correct number of arguments
* (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 {Function<string>} addNormalizedDependency
* @returns {Function<string, string, Function>}
*/
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 dirContextFrom(fileContext) {
return path.dirname(
// The first file is 'stdin' when we're using the data option
fileContext === "stdin" ? resourcePath : fileContext
);
}

return (url, prev, done) => {
resolve(
dirContextFrom(
// node-sass returns UNIX-style paths
path.normalize(prev)
),
url,
importsToResolve(utils.urlToRequest(url)),
done
);
};
}

module.exports = webpackImporter;

0 comments on commit 7dc9731

Please sign in to comment.