From 81c07ab47213cddf22154e8cf736790b53beb311 Mon Sep 17 00:00:00 2001 From: Izaak Schroeder Date: Mon, 23 Feb 2015 21:20:06 -0800 Subject: [PATCH] Use proper hook for SASS loader. 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. --- index.js | 150 ++++++++++++++++++++++++++++++----------- package.json | 3 +- stringify.loader.js | 8 +++ test/index.test.js | 9 +-- test/scss/imports.scss | 3 +- 5 files changed, 124 insertions(+), 49 deletions(-) create mode 100644 stringify.loader.js diff --git a/index.js b/index.js index 249b0c60..2dcd85f9 100644 --- a/index.js +++ b/index.js @@ -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))); }; diff --git a/package.json b/package.json index d6a04d58..873963ce 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/stringify.loader.js b/stringify.loader.js new file mode 100644 index 00000000..42d49425 --- /dev/null +++ b/stringify.loader.js @@ -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); +}; diff --git a/test/index.test.js b/test/index.test.js index f3c0e006..69172d79 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -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; @@ -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'); }); diff --git a/test/scss/imports.scss b/test/scss/imports.scss index 16cbc125..c4343ca4 100644 --- a/test/scss/imports.scss +++ b/test/scss/imports.scss @@ -1 +1,2 @@ -@import "another/module"; \ No newline at end of file + +@import "another/module";