metalsmith-less
Advanced tools
Comparing version
120
lib/index.js
var _ = require('lodash') | ||
var async = require('async') | ||
var BPromise = require('bluebird') | ||
var Joi = require('joi') | ||
var less = require('less') | ||
var multimatch = require('multimatch') | ||
var Parser = require('less').Parser | ||
var path = require('path') | ||
var DEFAULT_PATTERN = '**/*.less' | ||
var MAP_INPUT_KEY = 'input' | ||
var MAP_RE = /less/g | ||
var INPUT_SOURCE = 'input' | ||
var OPTIONS_SCHEMA = Joi.object().keys({ | ||
pattern: Joi.any([ | ||
Joi.string(), | ||
Joi.array().items(Joi.string()), | ||
]).default('**/*.less'), | ||
render: Joi.object(), | ||
useDynamicSourceMap: Joi.boolean().default(false), | ||
}) | ||
@@ -14,66 +22,46 @@ module.exports = plugin | ||
function plugin(options) { | ||
options = options || {} | ||
var pattern = options.pattern || DEFAULT_PATTERN | ||
var useDefaultSourceMap = options.useDefaultSourceMap || false | ||
var parseOptions = options.parse || {} | ||
var renderOptions = options.render || {} | ||
var parser = new Parser(parseOptions) | ||
var validation = Joi.validate((options || {}), OPTIONS_SCHEMA) | ||
if (validation.error) throw validation.error | ||
var useDynamicSourceMap = validation.value.useDynamicSourceMap | ||
var renderOptions = validation.value.render | ||
return function (files, metalsmith, done) { | ||
var paths = Object.keys(files).filter(function (path) { | ||
return multimatch(path, pattern).length > 0 | ||
}) | ||
var iterator = convert.bind(null, { | ||
files: files, | ||
parser: parser, | ||
renderOptions: renderOptions, | ||
useDefaultSourceMap: useDefaultSourceMap | ||
}) | ||
async.each(paths, iterator, done) | ||
return BPromise | ||
.filter(Object.keys(files), function (_path) { | ||
return multimatch(_path, validation.value.pattern).length > 0 | ||
}) | ||
.map(function (_path) { | ||
var destination = _path.replace(MAP_RE, 'css') | ||
var mapDestination | ||
var map | ||
if (useDynamicSourceMap) { | ||
mapDestination = destination + '.map' | ||
renderOptions = _.chain(renderOptions) | ||
.cloneDeep() | ||
.omit('useDynamicSourceMap') | ||
.extend({ | ||
sourceMap: { | ||
outputSourceFiles: true, | ||
sourceMapURL: path.basename(mapDestination), | ||
}, | ||
}) | ||
.value() | ||
} | ||
return less.render(files[_path].contents.toString(), renderOptions) | ||
.then(function (output) { | ||
var mapInputIndex | ||
files[destination] = { | ||
contents: new Buffer(output.css), | ||
} | ||
if (useDynamicSourceMap) { | ||
map = JSON.parse(output.map) | ||
mapInputIndex = map.sources.indexOf(MAP_INPUT_KEY) | ||
if (mapInputIndex) map.sources[mapInputIndex] = path.join(metalsmith._source, _path) | ||
files[mapDestination] = { | ||
contents: new Buffer(JSON.stringify(map)), | ||
} | ||
} | ||
}) | ||
}) | ||
.nodeify(done) | ||
} | ||
} | ||
function convert(options, filePath, done) { | ||
var files = options.files | ||
var parser = options.parser | ||
var renderOptions = options.renderOptions | ||
var useDefaultSourceMap = options.useDefaultSourceMap | ||
var data = files[filePath] | ||
parser.parse(data.contents.toString(), function (err, tree) { | ||
if (err) return done(err) | ||
var destination = filePath.replace(MAP_RE, 'css') | ||
var sourceMapDestination | ||
var sourceMap | ||
var contents | ||
if (useDefaultSourceMap) { | ||
sourceMapDestination = destination + '.map' | ||
renderOptions = _.chain(renderOptions) | ||
.clone() | ||
.extend({ | ||
outputSourceFiles: true, | ||
sourceMap: true, | ||
sourceMapBasepath: undefined, | ||
sourceMapFilename: undefined, | ||
sourceMapOutputFilename: undefined, | ||
sourceMapURL: path.basename(sourceMapDestination), | ||
writeSourceMap: function (res) { | ||
// This method is invoked synchronously during `tree.toCSS()`. | ||
var sm = JSON.parse(res) | ||
var inputIndex = sm.sources.indexOf(INPUT_SOURCE) | ||
if (inputIndex) sm.sources[inputIndex] = 'src/' + filePath | ||
sourceMap = JSON.stringify(sm) | ||
} | ||
}) | ||
.value() | ||
} | ||
contents = tree.toCSS(renderOptions) | ||
if (useDefaultSourceMap) { | ||
files[sourceMapDestination] = { | ||
contents: new Buffer(sourceMap) | ||
} | ||
} | ||
files[destination] = { | ||
contents: new Buffer(contents) | ||
} | ||
return done(null) | ||
}) | ||
} |
{ | ||
"name": "metalsmith-less", | ||
"version": "1.0.3", | ||
"version": "2.0.0", | ||
"description": "A LESS plugin for Metalsmith", | ||
@@ -22,13 +22,14 @@ "main": "./lib/", | ||
"dependencies": { | ||
"async": "^0.9.0", | ||
"less": "^1.7.5", | ||
"lodash": "^2.4.1", | ||
"multimatch": "^1.0.0" | ||
"bluebird": "^2.9.14", | ||
"joi": "^6.0.8", | ||
"less": "^2.4.0", | ||
"lodash": "^3.5.0", | ||
"multimatch": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"assert-dir-equal": "^1.0.1", | ||
"crispy": "^2.0.0", | ||
"metalsmith": "^0.11.0", | ||
"mocha": "^1.21.4" | ||
"crispy": "^3.1.2", | ||
"metalsmith": "^1.3.0", | ||
"mocha": "^2.2.1" | ||
} | ||
} |
@@ -5,3 +5,3 @@ # metalsmith-less | ||
A [LESS][less] plugin for [Metalsmith][metalsmith]. | ||
A [LESS](http://lesscss.org/) plugin for [Metalsmith](http://www.metalsmith.io/). | ||
@@ -19,25 +19,21 @@ ## Installation | ||
Metalsmith(__dirname) | ||
.use(less(options)) | ||
.build() | ||
new Metalsmith(__dirname) | ||
.use(less(options)) | ||
.build() | ||
``` | ||
### Options | ||
### **`options`** `Object` | ||
- **`pattern`** `String pattern|Array<String> pattern` | ||
- **`pattern`** `String|Array<String>` | ||
A [pattern][multimatch] to filter source files. Default `**/*.less`. | ||
The [pattern](https://github.com/sindresorhus/multimatch) to filter source files. Default `**/*.less`. | ||
- **`parse`** `Object parseOptions` | ||
- **`render`** `Object` | ||
An object that gets passed along to [`new less.Parser(parseOptions)`][less config]. Default `null`. | ||
The options passed to [`less.render(String[, Object])`](http://lesscss.org/usage/#programmatic-usage). Unfortunately, this method is *undocumented*. See https://github.com/less/less-docs/issues/212 for more information. Default `undefined`. | ||
- **`render`** `Object renderOptions` | ||
- **`useDynamicSourceMap`** `Boolean` | ||
An object that gets passed along to [`tree.toCSS(renderOptions)`][less config]. Default `null`. | ||
Overrides the supplied source map configuration with a dynamic file-level configuration. This is the easiest way to enable source maps in your Metalsmith build. Default `false`. | ||
- **`useDefaultSourceMap`** `Boolean useDefaultSourceMap` | ||
A switch to enable the default source map configuration. LESS's source map API doesn't play nice with Metalsmith, so this is a heavy-handed approach to sensible source maps. Default `false`. | ||
## Tests | ||
@@ -52,6 +48,1 @@ | ||
MIT License, see [LICENSE](https://github.com/christophercliff/metalsmith-less/blob/master/LICENSE.md) for details. | ||
[less]: http://lesscss.org/ | ||
[less config]: http://lesscss.org/#using-less-configuration | ||
[metalsmith]: http://www.metalsmith.io/ | ||
[multimatch]: https://github.com/sindresorhus/multimatch |
@@ -5,3 +5,3 @@ var assertDir = require('assert-dir-equal') | ||
describe('metalsmith-less', function () { | ||
describe('the plugin', function () { | ||
@@ -22,5 +22,7 @@ it('should convert less to css', function (done) { | ||
pattern: 'less/index.less', | ||
parse: { | ||
paths: ['test/fixtures/import/src/less'] | ||
} | ||
render: { | ||
paths: [ | ||
'test/fixtures/import/src/less', | ||
], | ||
}, | ||
})) | ||
@@ -37,7 +39,12 @@ .build(function (err) { | ||
.use(less({ | ||
pattern: 'less/index.less', | ||
useDefaultSourceMap: true, | ||
parse: { | ||
paths: ['test/fixtures/source-map/src/less'] | ||
} | ||
pattern: [ | ||
'less/entry-1.less', | ||
'less/entry-2.less', | ||
], | ||
render: { | ||
paths: [ | ||
'test/fixtures/source-map/src/less', | ||
], | ||
}, | ||
useDynamicSourceMap: true, | ||
})) | ||
@@ -44,0 +51,0 @@ .build(function (err) { |
9516
8.46%34
36%143
5.93%5
25%46
-16.36%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated