Skip to content

Commit

Permalink
Move option normalization code into dedicated module
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnns committed Dec 27, 2016
1 parent d8e1fa9 commit bd0e549
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 80 deletions.
86 changes: 6 additions & 80 deletions lib/loader.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
"use strict";

const utils = require("loader-utils");
const sass = require("node-sass");
const path = require("path");
const os = require("os");
const async = require("async");
const assign = require("object-assign");
const formatSassError = require("./formatSassError");
const proxyCustomImporters = require("./proxyCustomImporters");
const webpackImporter = require("./webpackImporter");
const normalizeOptions = require("./normalizeOptions");
const pify = require("pify");

// This queue makes sure node-sass leaves one thread available for executing
Expand All @@ -29,12 +26,6 @@ function sassLoader(content) {
const self = this;
const resourcePath = this.resourcePath;

// 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) {
includedFiles.forEach(addNormalizedDependency);
}

function addNormalizedDependency(file) {
// node-sass returns UNIX-style paths
self.dependency(path.normalize(file));
Expand All @@ -46,65 +37,17 @@ function sassLoader(content) {

this.cacheable();

const sassOptions = getLoaderConfig(this);

sassOptions.data = sassOptions.data ? (sassOptions.data + os.EOL + content) : content;

// Skip empty files, otherwise it will stop webpack, see issue #21
if (sassOptions.data.trim() === "") {
callback(null, content);
return;
}

// opt.outputStyle
if (!sassOptions.outputStyle && this.minimize) {
sassOptions.outputStyle = "compressed";
}

// opt.sourceMap
// Not using the `this.sourceMap` flag because css source maps are different
// @see https://github.com/webpack/css-loader/pull/40
if (sassOptions.sourceMap) {
// deliberately overriding the sourceMap option
// this value is (currently) ignored by libsass when using the data input instead of file input
// however, it is still necessary for correct relative paths in result.map.sources.
sassOptions.sourceMap = this.options.context + "/sass.map";
sassOptions.omitSourceMapUrl = true;

// If sourceMapContents option is not set, set it to true otherwise maps will be empty/null
// when exported by webpack-extract-text-plugin.
if ("sourceMapContents" in sassOptions === false) {
sassOptions.sourceMapContents = true;
}
}

// indentedSyntax is a boolean flag.
const ext = path.extname(resourcePath);

// If we are compiling sass and indentedSyntax isn't set, automatically set it.
if (ext && ext.toLowerCase() === ".sass" && "indentedSyntax" in sassOptions === false) {
sassOptions.indentedSyntax = true;
} else {
sassOptions.indentedSyntax = Boolean(sassOptions.indentedSyntax);
}

// 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(webpackImporter(
this.resourcePath,
const options = normalizeOptions(this, content, webpackImporter(
resourcePath,
pify(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) : [];
sassOptions.includePaths.push(path.dirname(resourcePath));

// start the actual rendering
asyncSassJobQueue.push(sassOptions, (err, result) => {
asyncSassJobQueue.push(options, (err, result) => {
if (err) {
formatSassError(err, this.resourcePath);
err.file && self.dependency(err.file);
err.file && this.dependency(err.file);
callback(err);
return;
}
Expand All @@ -120,26 +63,9 @@ function sassLoader(content) {
result.map = null;
}

addIncludedFilesToWebpack(result.stats.includedFiles);
result.stats.includedFiles.forEach(addNormalizedDependency);
callback(null, result.css.toString(), result.map);
});
}

/**
* Check the loader query and webpack config for loader options. If an option is defined in both places,
* the loader query takes precedence.
*
* @param {LoaderContext} loaderContext
* @returns {Object}
*/
function getLoaderConfig(loaderContext) {
const query = utils.parseQuery(loaderContext.query);
const configKey = query.config || "sassLoader";
const config = loaderContext.options[configKey] || {};

delete query.config;

return assign({}, config, query);
}

module.exports = sassLoader;
83 changes: 83 additions & 0 deletions lib/normalizeOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use strict";

const os = require("os");
const utils = require("loader-utils");
const assign = require("object-assign");
const path = require("path");
const proxyCustomImporters = require("./proxyCustomImporters");

/**
* Derives the sass options from the loader context and normalizes its values with sane defaults.
*
* @param {LoaderContext} loaderContext
* @param {string} content
* @param {Function} webpackImporter
* @returns {Object}
*/
function normalizeOptions(loaderContext, content, webpackImporter) {
const options = getLoaderOptions(loaderContext);
const resourcePath = loaderContext.resourcePath;

options.data = options.data ? (options.data + os.EOL + content) : content;

// opt.outputStyle
if (!options.outputStyle && loaderContext.minimize) {
options.outputStyle = "compressed";
}

// opt.sourceMap
// Not using the `this.sourceMap` flag because css source maps are different
// @see https://github.com/webpack/css-loader/pull/40
if (options.sourceMap) {
// deliberately overriding the sourceMap option
// this value is (currently) ignored by libsass when using the data input instead of file input
// however, it is still necessary for correct relative paths in result.map.sources.
options.sourceMap = loaderContext.options.context + "/sass.map";
options.omitSourceMapUrl = true;

// If sourceMapContents option is not set, set it to true otherwise maps will be empty/null
// when exported by webpack-extract-text-plugin.
if ("sourceMapContents" in options === false) {
options.sourceMapContents = true;
}
}

// indentedSyntax is a boolean flag.
const ext = path.extname(resourcePath);

// If we are compiling sass and indentedSyntax isn't set, automatically set it.
if (ext && ext.toLowerCase() === ".sass" && "indentedSyntax" in options === false) {
options.indentedSyntax = true;
} else {
options.indentedSyntax = Boolean(options.indentedSyntax);
}

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

// `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file.
options.includePaths = options.includePaths ? [].concat(options.includePaths) : [];
options.includePaths.push(path.dirname(resourcePath));

return options;
}

/**
* Check the loader query and webpack config for loader options. If an option is defined in both places,
* the loader query takes precedence.
*
* @param {LoaderContext} loaderContext
* @returns {Object}
*/
function getLoaderOptions(loaderContext) {
const query = utils.parseQuery(loaderContext.query);
const configKey = query.config || "sassLoader";
const config = loaderContext.options[configKey] || {};

delete query.config;

return assign({}, config, query);
}

module.exports = normalizeOptions;

0 comments on commit bd0e549

Please sign in to comment.