Skip to content

Commit

Permalink
Use proper hook for SASS loader.
Browse files Browse the repository at this point in the history
This changes  a lot of stuff and the tests need to be updated to match. This builds a small project I have going right now but I haven't tested it majorly on anything else. Someone should look into it.
  • Loading branch information
izaakschroeder committed Feb 27, 2015
1 parent 4d16d49 commit 81c07ab
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 49 deletions.
150 changes: 110 additions & 40 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,122 @@

'use strict';

var utils = require('loader-utils');
var sass = require('node-sass');
var path = require('path');
var sassGraph = require('sass-graph');
var _ = require('lodash');

function split(str) {
var idx = str.lastIndexOf("!");
return idx < 0 ? ['', str] : [str.substr(0, idx+1), str.substr(idx+1)];
}

var UnsafeCachePlugin = require("enhanced-resolve/lib/UnsafeCachePlugin");

var ModulesInRootPlugin = require("enhanced-resolve/lib/ModulesInRootPlugin");
var ModuleAsFilePlugin = require("enhanced-resolve/lib/ModuleAsFilePlugin");
var ModuleAsDirectoryPlugin = require("enhanced-resolve/lib/ModuleAsDirectoryPlugin");
var ModuleAliasPlugin = require("enhanced-resolve/lib/ModuleAliasPlugin");
var DirectoryDefaultFilePlugin = require("enhanced-resolve/lib/DirectoryDefaultFilePlugin");
var DirectoryDescriptionFilePlugin = require("enhanced-resolve/lib/DirectoryDescriptionFilePlugin");
var DirectoryDescriptionFileFieldAliasPlugin = require("enhanced-resolve/lib/DirectoryDescriptionFileFieldAliasPlugin");
var FileAppendPlugin = require("enhanced-resolve/lib/FileAppendPlugin");
var DirectoryResultPlugin = require("enhanced-resolve/lib/DirectoryResultPlugin");
var ResultSymlinkPlugin = require("enhanced-resolve/lib/ResultSymlinkPlugin");

var Resolver = require("enhanced-resolve/lib/Resolver");

function makeRootPlugin(name, root) {
if(typeof root === "string") {
return new ModulesInRootPlugin(name, root);
} else if(Array.isArray(root)) {
return function() {
root.forEach(function(root) {
this.apply(new ModulesInRootPlugin(name, root));
}, this);
}
}
return function() {};
}

module.exports = function (content) {

this.cacheable();
var callback = this.async();
var self = this;

var opt = utils.parseQuery(this.query);
opt.data = content;
function importer(url, prev, callback) {

// skip empty files, otherwise it will stop webpack, see issue #21
if (opt.data.trim() === '') {
return callback(null, content);
}
var parts = split(url),
loaders = parts[0],
filename = parts[1],
mod;

// set include path to fix imports
opt.includePaths = opt.includePaths || [];
opt.includePaths.push(path.dirname(this.resourcePath));
if (this.options.resolve && this.options.resolve.root) {
var root = [].concat(this.options.resolve.root);
opt.includePaths = opt.includePaths.concat(root);
}
var resolver = new Resolver(null);

// lol this.
// Waiting on https://github.com/webpack/webpack/pull/732
resolver.fileSystem = require('fs');

resolver.apply(
makeRootPlugin("module", path.dirname(prev)),
makeRootPlugin("module", self.context),
makeRootPlugin("module", self.options.resolve.root),
new ModuleAsFilePlugin("module"),
new ModuleAsDirectoryPlugin("module"),
new DirectoryDefaultFilePlugin(["index"]),
new FileAppendPlugin([ '.scss', '.sass' ]),
new ResultSymlinkPlugin()
);

function resolveFinish(err, resolved) {
if (err) {
callback(err);
return;
}

var url = loaders + resolved;
var k = '!!' +__dirname+'/stringify.loader.js!' + url;

self.loadModule(k, function(err, data, map, mod) {
if (err) {
callback(err);
return;
}

self.dependency(resolved);

// Why this doesn't take an explicit err argument is beyond me.
callback({
contents: data && JSON.parse(data),
file: resolved
});
});
}

mod = path.join(path.dirname(filename), '_' + path.basename(filename));

// Try for normal version first, then _ version
resolver.resolve(self.context, filename, function(err, resolved) {
if (err) {
resolver.resolve(self.context, mod, resolveFinish);
} else {
resolveFinish(err, resolved);
}

});
};

// output compressed by default
opt.outputStyle = opt.outputStyle || 'compressed';

var loadPaths = opt.includePaths;
var markDependencies = function () {
try {
var graph = sassGraph.parseFile(this.resourcePath, {loadPaths: loadPaths});
graph.visitDescendents(this.resourcePath, function (imp) {
this.addDependency(imp);
}.bind(this));
} catch (err) {
this.emitError(err);
}
}.bind(this);

opt.success = function (result) {
markDependencies();
callback(null, result.css, result.map);
}.bind(this);

opt.error = function (err) {
markDependencies();
callback({message: err.message + ' (' + err.line + ':' + err.column + ')'});
}.bind(this);

sass.render(opt);
sass.render(_.assign({
file: this.resourcePath,
data: content,
importer: importer,
outputStyle: 'compressed',
error: function (err) {
callback(err);
},
success: function (result) {
callback(null, result.css, result.map);
}
}, utils.parseQuery(this.query)));
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"dependencies": {
"loader-utils": "^0.2.5",
"node-sass": "^2.0.1",
"sass-graph": "^1.0.3"
"lodash": "^3.2.0",
"enhanced-resolve": "^0.8.4"
},
"devDependencies": {
"mocha": "^2.0.1",
Expand Down
8 changes: 8 additions & 0 deletions stringify.loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
module.exports = function(source) {
if(this.cacheable) this.cacheable();
return JSON.stringify(source);
};
9 changes: 2 additions & 7 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ function test(name, id, query) {
exts.forEach(function (ext) {
var expectedCss = readCss(ext, id);
var sassFile = 'raw!' +
path.resolve(__dirname, '../index.js') + '?' +
query +
(ext === 'sass'? '&indentedSyntax=sass' : '') + '!' +
path.resolve(__dirname, '../index.js') +
(ext === 'sass'? '?indentedSyntax=sass' : '') + '!' +
path.join(__dirname, ext, id + '.' + ext);
var actualCss;

Expand Down Expand Up @@ -64,10 +63,6 @@ function test(name, id, query) {
describe('sass-loader', function () {
test('should compile simple sass without errors', 'language');
test('should resolve imports correctly', 'imports');
test('should pass the include paths to node-sass', 'include-paths',
'includePaths[]=' + path.resolve(__dirname, './sass/another') + '&' +
'includePaths[]=' + path.resolve(__dirname, './scss/another'));

// Test for issue: https://github.com/jtangelder/sass-loader/issues/32
test('should pass with multiple imports', 'multiple-imports');
});
3 changes: 2 additions & 1 deletion test/scss/imports.scss
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@import "another/module";

@import "another/module";

0 comments on commit 81c07ab

Please sign in to comment.