sass-loader
Advanced tools
Comparing version 0.5.0 to 1.0.0
206
index.js
@@ -6,12 +6,128 @@ 'use strict'; | ||
var path = require('path'); | ||
var sassGraph = require('sass-graph'); | ||
var os = require('os'); | ||
var fs = require('fs'); | ||
// A typical sass error looks like this | ||
var SassError = { | ||
message: 'invalid property name', | ||
column: 14, | ||
line: 1, | ||
file: 'stdin', | ||
status: 1 | ||
}; | ||
/** | ||
* The sass-loader makes node-sass available to webpack modules. | ||
* | ||
* @param {String} content | ||
* @returns {*} | ||
*/ | ||
module.exports = function (content) { | ||
this.cacheable(); | ||
var callback = this.async(); | ||
var isSync = typeof callback !== 'function'; | ||
var self = this; | ||
var resourcePath = this.resourcePath; | ||
var fileExt; | ||
var opt; | ||
var opt = utils.parseQuery(this.query); | ||
/** | ||
* Enhances the sass error with additional information about what actually went wrong. | ||
* | ||
* @param {SassError} err | ||
*/ | ||
function formatSassError(err) { | ||
var msg = err.message; | ||
if (err.file === 'stdin') { | ||
err.file = resourcePath; | ||
} | ||
// The 'Current dir' hint of node-sass does not help us, we're providing | ||
// additional information by reading the err.file property | ||
msg = msg.replace(/\s*Current dir:\s*/, ''); | ||
err.message = getFileExcerptIfPossible(err) + | ||
msg.charAt(0).toUpperCase() + msg.slice(1) + os.EOL + | ||
' in ' + err.file + ' (line ' + err.line + ', column ' + err.column + ')'; | ||
// Instruct webpack to hide the JS stack from the console | ||
// Usually you're only interested in the SASS stack in this case. | ||
err.hideStack = true; | ||
} | ||
/** | ||
* 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() { | ||
if (isSync) { | ||
return function syncWebpackImporter(url, context) { | ||
var filename; | ||
url = urlToRequest(url); | ||
context = normalizeContext(context); | ||
try { | ||
filename = self.resolveSync(context, url); | ||
self.dependency && self.dependency(filename); | ||
} catch (err) { | ||
// Unfortunately we can't return an error inside a custom importer yet | ||
// @see https://github.com/sass/node-sass/issues/651#issuecomment-73317319 | ||
filename = url; | ||
} | ||
return { | ||
file: filename | ||
}; | ||
}; | ||
} | ||
return function asyncWebpackImporter(url, context, done) { | ||
url = urlToRequest(url); | ||
context = normalizeContext(context); | ||
self.resolve(context, url, function onWebpackResolve(err, filename) { | ||
if (err) { | ||
// Unfortunately we can't return an error inside a custom importer yet | ||
// @see https://github.com/sass/node-sass/issues/651#issuecomment-73317319 | ||
filename = url; | ||
} else { | ||
self.dependency && self.dependency(filename); | ||
} | ||
// Use self.loadModule() before calling done() to make imported files available to | ||
// other webpack tools like postLoaders etc.? | ||
done({ | ||
file: filename | ||
}); | ||
}); | ||
}; | ||
} | ||
function urlToRequest(url) { | ||
// Add file extension if it's not present already | ||
if (url.slice(-fileExt.length) !== fileExt) { | ||
url = url + fileExt; | ||
} | ||
return utils.urlToRequest(url, opt.root); | ||
} | ||
function normalizeContext(context) { | ||
// The first file is 'stdin' when we're using the data option | ||
if (context === 'stdin') { | ||
context = resourcePath; | ||
} | ||
return path.dirname(context); | ||
} | ||
this.cacheable(); | ||
opt = utils.parseQuery(this.query); | ||
opt.data = content; | ||
// skip empty files, otherwise it will stop webpack, see issue #21 | ||
// Skip empty files, otherwise it will stop webpack, see issue #21 | ||
if (opt.data.trim() === '') { | ||
@@ -21,10 +137,3 @@ return callback(null, content); | ||
// 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); | ||
} | ||
// opt.outputStyle | ||
if (!opt.outputStyle && this.minimize) { | ||
@@ -34,3 +143,4 @@ opt.outputStyle = 'compressed'; | ||
// not using the `this.sourceMap` flag because css source maps are different | ||
// opt.sourceMap | ||
// Not using the `this.sourceMap` flag because css source maps are different | ||
// @see https://github.com/webpack/css-loader/pull/40 | ||
@@ -44,29 +154,32 @@ if (opt.sourceMap) { | ||
var loadPaths = opt.includePaths; | ||
var markDependencies = function () { | ||
// indentedSyntax is a boolean flag | ||
opt.indentedSyntax = Boolean(opt.indentedSyntax); | ||
fileExt = '.' + (opt.indentedSyntax? 'sass' : 'scss'); | ||
// opt.importer | ||
opt.importer = getWebpackImporter(); | ||
// start the actual rendering | ||
if (isSync) { | ||
try { | ||
var graph = sassGraph.parseFile(this.resourcePath, {loadPaths: loadPaths}); | ||
graph.visitDescendents(this.resourcePath, function (imp) { | ||
this.addDependency(imp); | ||
}.bind(this)); | ||
return sass.renderSync(opt).css.toString(); | ||
} catch (err) { | ||
this.emitError(err); | ||
formatSassError(err); | ||
throw err; | ||
} | ||
}.bind(this); | ||
sass.render(opt, function(err, result) { | ||
if(err) { | ||
markDependencies(); | ||
callback({message: err.message + ' (' + err.line + ':' + err.column + ')'}); | ||
} | ||
sass.render(opt, function onRender(err, result) { | ||
if (err) { | ||
formatSassError(err); | ||
callback(err); | ||
return; | ||
} | ||
markDependencies(); | ||
if (result.map && result.map !== '{}') { | ||
result.map = JSON.parse(result.map); | ||
result.map.file = utils.getCurrentRequest(this); | ||
// the first source is 'stdin' according to libsass because we've used the data input | ||
// now let's override that value with the correct relative path | ||
result.map.sources[0] = path.relative(this.options.output.path, utils.getRemainingRequest(this)); | ||
result.map.file = resourcePath; | ||
// The first source is 'stdin' according to libsass because we've used the data input | ||
// Now let's override that value with the correct relative path | ||
result.map.sources[0] = path.relative(self.options.output.path, resourcePath); | ||
} else { | ||
@@ -76,4 +189,29 @@ result.map = null; | ||
callback(null, result.css, result.map); | ||
}.bind(this)); | ||
callback(null, result.css.toString(), result.map); | ||
}); | ||
}; | ||
/** | ||
* Tries to get an excerpt of the file where the error happened. | ||
* Uses err.line and err.column. | ||
* | ||
* Returns an empty string if the excerpt could not be retrieved. | ||
* | ||
* @param {SassError} err | ||
* @returns {string} | ||
*/ | ||
function getFileExcerptIfPossible(err) { | ||
var content; | ||
try { | ||
content = fs.readFileSync(err.file, 'utf8'); | ||
return os.EOL + | ||
content.split(os.EOL)[err.line - 1] + os.EOL + | ||
new Array(err.column - 1).join(' ') + '^' + os.EOL + | ||
' '; | ||
} catch (err) { | ||
// If anything goes wrong here, we don't want any errors to be reported to the user | ||
return ''; | ||
} | ||
} |
{ | ||
"name": "sass-loader", | ||
"version": "0.5.0", | ||
"description": "SASS loader for Webpack", | ||
"version": "1.0.0", | ||
"description": "Sass loader for webpack", | ||
"main": "index.js", | ||
@@ -9,6 +9,9 @@ "scripts": { | ||
"test": "mocha -R spec", | ||
"test-source-map": "webpack --config test/sourceMap/webpack.config.js && open ./test/sourceMap/index.html" | ||
"test-source-map": "webpack-dev-server --config test/sourceMap/webpack.config.js --content-base ./test/sourceMap", | ||
"test-watch": "webpack --config test/watch/webpack.config.js", | ||
"test-hmr": "webpack-dev-server --config test/hmr/webpack.config.js --content-base ./test/hmr --hot --inline" | ||
}, | ||
"keywords": [ | ||
"sass", | ||
"libsass", | ||
"webpack", | ||
@@ -23,15 +26,20 @@ "loader" | ||
"license": "MIT", | ||
"peerDependencies": { | ||
"node-sass": "^3.0.0-alpha.0" | ||
}, | ||
"dependencies": { | ||
"loader-utils": "^0.2.5", | ||
"node-sass": "^3.0.0-pre", | ||
"sass-graph": "^1.0.3" | ||
"loader-utils": "^0.2.5" | ||
}, | ||
"devDependencies": { | ||
"css-loader": "^0.9.1", | ||
"enhanced-require": "^0.5.0-beta6", | ||
"extract-text-webpack-plugin": "^0.3.8", | ||
"mocha": "^2.0.1", | ||
"node-sass": "^3.0.0-alpha.0", | ||
"raw-loader": "^0.5.1", | ||
"should": "^5.0.1", | ||
"webpack": "^1.4.0" | ||
"style-loader": "^0.8.3", | ||
"webpack": "^1.4.0", | ||
"webpack-dev-server": "^1.7.0" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
# sass loader for [webpack](http://webpack.github.io/) | ||
# Sass loader for [webpack](http://webpack.github.io/) | ||
@@ -7,2 +7,4 @@ ## Install | ||
Starting with `1.0.0`, the sass-loader requires [node-sass](https://github.com/sass/node-sass) as [`peerDependency`](https://docs.npmjs.com/files/package.json#peerdependencies). Thus you are able to specify the required version accurately. | ||
## Usage | ||
@@ -44,5 +46,5 @@ | ||
### SASS options | ||
### Sass options | ||
You can pass any SASS specific configuration options through to the render function via [query parameters](http://webpack.github.io/docs/using-loaders.html#query-parameters). | ||
You can pass any Sass specific configuration options through to the render function via [query parameters](http://webpack.github.io/docs/using-loaders.html#query-parameters). | ||
@@ -68,5 +70,15 @@ ``` javascript | ||
### Imports | ||
webpack provides an [advanced mechanism to resolve files](http://webpack.github.io/docs/resolving.html). The sass-loader uses node-sass' custom importer feature to pass all queries to the webpack resolving engine. Thus you can import your sass-modules from `node_modules`. Just prepend them with a `~` which tells webpack to look-up the [`modulesDirectories`](http://webpack.github.io/docs/configuration.html#resolve-modulesdirectories) | ||
```css | ||
@import "~bootstrap/less/bootstrap"; | ||
``` | ||
It's important to only prepend it with `~`, because `~/` resolves to the home-directory. webpack needs to distinguish between `bootstrap` and `~bootstrap` because CSS- and Sass-files have no special syntax for importing relative files. Writing `@import "file"` is the same as `@import "./file";` | ||
### .sass files | ||
For requiring `.sass` files, add `indentedSyntax=sass` as a loader option: | ||
For requiring `.sass` files, add `indentedSyntax` as a loader option: | ||
@@ -80,3 +92,3 @@ ``` javascript | ||
// Passing indentedSyntax query param to node-sass | ||
loader: "style!css!sass?indentedSyntax=sass" | ||
loader: "style!css!sass?indentedSyntax" | ||
} | ||
@@ -90,3 +102,3 @@ ] | ||
Because of browser limitations, source maps are only available in conjunction with the [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin). Use that plugin to extract the CSS code from the generated JS bundle into a separate file (which even improves the perceived performance because JS and CSS are loaded in parallel). | ||
Because of browser limitations, source maps are only available in conjunction with the [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin). Use that plugin to extract the CSS code from the generated JS bundle into a separate file (which even improves the perceived performance because JS and CSS are downloaded in parallel). | ||
@@ -121,11 +133,6 @@ Then your `webpack.config.js` should look like this: | ||
If you want to view the original SASS files inside Chrome and even edit it, [there's a good blog post](https://medium.com/@toolmantim/getting-started-with-css-sourcemaps-and-in-browser-sass-editing-b4daab987fb0). Checkout [test/sourceMap](https://github.com/jtangelder/sass-loader/tree/master/test) for a running example. Make sure to serve the content with an HTTP server. | ||
If you want to view the original Sass files inside Chrome and even edit it, [there's a good blog post](https://medium.com/@toolmantim/getting-started-with-css-sourcemaps-and-in-browser-sass-editing-b4daab987fb0). Checkout [test/sourceMap](https://github.com/jtangelder/sass-loader/tree/master/test) for a running example. Make sure to serve the content with an HTTP server. | ||
## Caveats | ||
Currently the sass-loader does not follow all of the webpack loader guidelines. The general problem is that the entry scss-file is passed to [node-sass](https://github.com/sass/node-sass) which does pretty much the rest. Thus `@import` statements inside your scss-files cannot be resolved by webpack's resolver. However, there is an [issue for that missing feature in libsass](https://github.com/sass/libsass/issues/21). | ||
## License | ||
MIT (http://www.opensource.org/licenses/mit-license.php) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
17565
2
9
181
1
133
10
1
1
- Removednode-sass@^3.0.0-pre
- Removedsass-graph@^1.0.3
- Removedcommander@2.20.3(transitive)
- Removedglob@4.5.3(transitive)
- Removedlodash@2.4.2(transitive)
- Removedminimatch@2.0.10(transitive)
- Removedsass-graph@1.3.0(transitive)