less-cluster
Advanced tools
Comparing version 0.4.6 to 0.4.7
@@ -151,3 +151,3 @@ /** | ||
// resolve onto directory | ||
parsed._files = remain.map(function (file) { | ||
parsed.files = remain.map(function (file) { | ||
return path.resolve(parsed.directory, file); | ||
@@ -154,0 +154,0 @@ }); |
@@ -6,3 +6,3 @@ /** | ||
var fs = require('fs'); | ||
var fs = require('graceful-fs'); | ||
var glob = require('glob'); | ||
@@ -45,3 +45,3 @@ var path = require('path'); | ||
this._children = {}; | ||
this._fileData = {}; | ||
this._fileCache = {}; | ||
} | ||
@@ -76,6 +76,9 @@ | ||
@method run | ||
@chainable | ||
**/ | ||
LessCluster.prototype.run = function () { | ||
this.worker = require('./less-worker')(this.options); | ||
this.worker = require('./less-worker')(this.options, this._fileCache); | ||
this._attachEvents(); | ||
return this; | ||
}; | ||
@@ -153,6 +156,3 @@ | ||
if (worker && fileName) { | ||
worker.build({ | ||
dest: this._getDestinationPath(fileName), | ||
file: fileName | ||
}); | ||
worker.build(fileName, this._getDestinationPath(fileName)); | ||
} | ||
@@ -167,14 +167,2 @@ }; | ||
/** | ||
Convert absolute dirpath into relative fragment | ||
@method _getRelativePath | ||
@param {String} dir | ||
@return {String} | ||
@private | ||
**/ | ||
LessCluster.prototype._getRelativePath = function (dir) { | ||
return path.relative(process.cwd(), dir); | ||
}; | ||
/** | ||
Create pattern for glob | ||
@@ -188,3 +176,2 @@ | ||
LessCluster.prototype._getGlobPattern = function (dir) { | ||
// return path.join(this._getRelativePath(dir), this.options.match); | ||
return path.join(dir, this.options.match); | ||
@@ -239,10 +226,18 @@ }; | ||
fileList.on('error', function (err) { | ||
fileList.on("abort", handleAbort); | ||
fileList.on("error", handleError); | ||
fileList.on("end", handleEnd); | ||
function handleError(err) { | ||
fileList.abort(); | ||
throw err; | ||
}); | ||
cb(err); | ||
} | ||
fileList.on('end', function (matches) { | ||
function handleEnd(matches) { | ||
readFiles(matches, cb); | ||
}); | ||
} | ||
function handleAbort() { | ||
fileList.removeListener("end", handleEnd); | ||
} | ||
}; | ||
@@ -332,6 +327,6 @@ | ||
var filesToRead = Object.keys(data); | ||
var filesToProcess = this.options._files; | ||
var filesToProcess = this.options.files; | ||
// add data to cache | ||
utils.mix(this._fileData, data); | ||
utils.mix(this._fileCache, data); | ||
@@ -377,3 +372,3 @@ // but only operate on added data, not entire cache | ||
var imports = []; | ||
var fileContents = this._fileData[fileName]; | ||
var fileContents = this._fileCache[fileName]; | ||
var rx = LessCluster.RX_IMPORT; | ||
@@ -380,0 +375,0 @@ |
@@ -5,3 +5,3 @@ /** | ||
var fs = require('fs'); | ||
var fs = require('graceful-fs'); | ||
var path = require('path'); | ||
@@ -15,3 +15,2 @@ var less = require('less'); | ||
var utils = require('./utils'); | ||
var readFiles = require('./read-files'); | ||
var Logger = require('./logger'); | ||
@@ -31,4 +30,5 @@ | ||
@param {Object} [options] | ||
@param {Object} [fileCache] Hash of file contents, stored by reference, keyed on filename. | ||
**/ | ||
function LessWorker(options) { | ||
function LessWorker(options, fileCache) { | ||
// factory constructor | ||
@@ -43,3 +43,3 @@ if (!(this instanceof LessWorker)) { | ||
this._fileData = {}; | ||
this._fileCache = fileCache || {}; | ||
this._pathCache = {}; | ||
@@ -60,4 +60,5 @@ this._pathRebase = {}; | ||
LessWorker.prototype.destroy = function () { | ||
this._fileData = this._pathCache = this._pathRebase = null; | ||
this._fileCache = this._pathCache = this._pathRebase = null; | ||
less.Parser.importer = LessWorker._originalLessImporter; | ||
this.emit('cleanup'); | ||
this.removeAllListeners(); | ||
@@ -71,3 +72,3 @@ }; | ||
LessWorker.prototype._bindImporter = function () { | ||
// override static importer method to utilize _fileData cache | ||
// override static importer method to utilize _fileCache | ||
less.Parser.importer = this._importer.bind(this); | ||
@@ -129,6 +130,4 @@ }; | ||
LessWorker.prototype.build = function (msg) { | ||
var file = msg.file, | ||
dest = msg.dest, | ||
data = this._fileData[file], | ||
LessWorker.prototype._build = function (file, dest) { | ||
var data = this._fileCache[file], | ||
parser = this._createParser(file, dest); | ||
@@ -142,2 +141,6 @@ | ||
LessWorker.prototype.build = function (file, dest) { | ||
this._build(file, dest); | ||
}; | ||
LessWorker.prototype._createParseEnv = function (fileName, destFileName) { | ||
@@ -160,3 +163,3 @@ // copy options to avoid munging between executions | ||
// copying over cached data avoids repetitious disk seeks | ||
options.contents = this._fileData; | ||
options.contents = this._fileCache; | ||
@@ -173,3 +176,3 @@ return new less.tree.parseEnv(options); | ||
// Actually support --include-path args... | ||
LessWorker.prototype._getChildPath = function (file, parentFilePath, envPaths) { | ||
LessWorker.prototype.resolveChildPath = function (file, parentFilePath, envPaths) { | ||
var i, len, importPaths, | ||
@@ -237,3 +240,3 @@ childPath = this._pathCache[file]; | ||
// resolve child filename against parent filepath | ||
childPath = this._getChildPath(file, parentFileInfo.filename, parentEnv.paths), | ||
childPath = this.resolveChildPath(file, parentFileInfo.filename, parentEnv.paths), | ||
@@ -244,3 +247,3 @@ // derive child dirname for currentDirectory | ||
// retrieve cached file contents from instance hash | ||
childData = this._fileData[childPath]; | ||
childData = this._fileCache[childPath]; | ||
@@ -279,22 +282,1 @@ // only the top importing parser visits imports | ||
}; | ||
LessWorker.prototype._readFinished = function (err, data) { | ||
if (err) { | ||
this.emit('error', err); | ||
return; | ||
} | ||
this.debug(this._logPrefix, 'finished reading'); | ||
// add data to cache | ||
utils.mix(this._fileData, data); | ||
// tell our master that we are ready | ||
this.emit('ready'); | ||
}; | ||
LessWorker.prototype.start = function (msg) { | ||
this.debug(this._logPrefix, 'started'); | ||
readFiles(msg.data, this._readFinished.bind(this)); | ||
}; |
@@ -5,3 +5,3 @@ /** | ||
var fs = require('fs'); | ||
var fs = require('graceful-fs'); | ||
@@ -8,0 +8,0 @@ module.exports = readFiles; |
@@ -7,2 +7,3 @@ /** | ||
var LessWorker = require('./less-worker'); | ||
var utils = require('./utils'); | ||
@@ -36,4 +37,7 @@ module.exports = Worker; | ||
Worker.readFiles = require('./read-files'); | ||
Worker.prototype._attachEvents = function () { | ||
process.on('message', this.dispatchMessage.bind(this)); | ||
var boundDispatchMessage = this.dispatchMessage.bind(this); | ||
process.on('message', boundDispatchMessage); | ||
@@ -43,2 +47,6 @@ this.on('drain', function () { this.sendMaster('drain'); }); | ||
this.on('ready', function () { this.sendMaster('ready'); }); | ||
this.once('cleanup', function () { | ||
process.removeListener('message', boundDispatchMessage); | ||
}); | ||
}; | ||
@@ -68,2 +76,27 @@ | ||
Worker.prototype.build = function (msg) { | ||
this._build(msg.file, msg.dest); | ||
}; | ||
Worker.prototype._readFinished = function (err, data) { | ||
if (err) { | ||
this.emit('error', err); | ||
return; | ||
} | ||
this.debug(this._logPrefix, 'finished reading'); | ||
// add data to cache | ||
utils.mix(this._fileCache, data); | ||
// tell our master that we are ready | ||
this.emit('ready'); | ||
}; | ||
Worker.prototype.start = function (msg) { | ||
this.debug(this._logPrefix, 'started'); | ||
Worker.readFiles(msg.data, this._readFinished.bind(this)); | ||
}; | ||
if (cluster.isWorker) { | ||
@@ -70,0 +103,0 @@ /*jshint newcap: false */ |
{ | ||
"name": "less-cluster", | ||
"version": "0.4.6", | ||
"version": "0.4.7", | ||
"description": "Parallel, directory-based execution of lessc.", | ||
@@ -22,4 +22,4 @@ "main": "index.js", | ||
"pretest": "jshint bin lib test", | ||
"test": "istanbul test --print both ./node_modules/mocha/bin/_mocha", | ||
"posttest": "istanbul check-coverage --statements 60 --functions 50 --branches 50 --lines 60" | ||
"test": "istanbul cover --print both ./node_modules/mocha/bin/_mocha", | ||
"posttest": "istanbul check-coverage --statements 90 --functions 90 --branches 90 --lines 90" | ||
}, | ||
@@ -40,6 +40,7 @@ "repository": { | ||
"devDependencies": { | ||
"chai": "~1.8.0", | ||
"istanbul": "~0.1.44", | ||
"chai": "~1.8.1", | ||
"istanbul": "~0.1.46", | ||
"jshint": "~2.3.0", | ||
"mocha": "~1.14.0", | ||
"mocha": "~1.15.1", | ||
"rimraf": "~2.2.4", | ||
"sinon": "~1.7.3", | ||
@@ -49,4 +50,5 @@ "sinon-chai": "~2.4.0" | ||
"dependencies": { | ||
"nopt": "~2.1.1", | ||
"glob": "~3.2.1", | ||
"nopt": "~2.1.2", | ||
"glob": "~3.2.7", | ||
"graceful-fs": "~2.0.1", | ||
"less": "git://github.com/evocateur/less.js.git#normalize-urls", | ||
@@ -53,0 +55,0 @@ "mkdirp": "~0.3.5" |
# less-cluster | ||
A utility to make lessc's CLI more performant for large directories of LESS files. | ||
A utility to make [lessc](https://github.com/less/less.js)'s CLI more performant for large directories of LESS files. | ||
[![Build Status](https://travis-ci.org/zillow/less-cluster.png)](https://travis-ci.org/zillow/less-cluster) | ||
## Install | ||
@@ -6,0 +8,0 @@ |
@@ -497,10 +497,10 @@ /*global describe, it, before, beforeEach, after, afterEach, chai, should, sinon */ | ||
it("should populate files array", function () { | ||
topic._files.should.be.an('array'); | ||
topic.files.should.be.an('array'); | ||
}); | ||
it("with two members", function () { | ||
topic._files.should.have.length(files.length); | ||
topic.files.should.have.length(files.length); | ||
}); | ||
it("resolved to directory", function () { | ||
files.forEach(function (file, i) { | ||
topic._files[i].should.equal(path.resolve(topic.directory, file)); | ||
topic.files[i].should.equal(path.resolve(topic.directory, file)); | ||
}); | ||
@@ -507,0 +507,0 @@ }); |
@@ -5,2 +5,3 @@ /*global describe, it, before, beforeEach, after, afterEach, chai, should, sinon */ | ||
**/ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
@@ -97,6 +98,45 @@ | ||
instance.should.have.ownProperty('_children', {}); | ||
instance.should.have.ownProperty('_fileData', {}); | ||
instance.should.have.ownProperty('_fileCache', {}); | ||
}); | ||
}); | ||
describe("run()", function () { | ||
beforeEach(function () { | ||
this.instance = new LessCluster().run(); | ||
}); | ||
afterEach(function () { | ||
this.instance = null; | ||
}); | ||
it("should instantiate a worker", function () { | ||
this.instance.should.have.ownProperty("worker"); | ||
}); | ||
it("should hook worker events", function () { | ||
this.instance.worker.listeners("drain").should.have.length(1); | ||
}); | ||
}); | ||
describe("destroy()", function () { | ||
beforeEach(function () { | ||
this.instance = new LessCluster().run(); | ||
sinon.stub(this.instance, "removeAllListeners"); | ||
sinon.stub(this.instance.worker, "destroy"); | ||
this.instance.destroy(); | ||
}); | ||
afterEach(function () { | ||
this.instance = null; | ||
}); | ||
it("should unhook all events", function () { | ||
this.instance.removeAllListeners.should.have.been.calledOnce; | ||
}); | ||
it("should destroy worker", function () { | ||
this.instance.worker.destroy.should.have.been.calledOnce; | ||
}); | ||
}); | ||
describe("getNextFile()", function () { | ||
@@ -129,2 +169,59 @@ beforeEach(function () { | ||
describe("onDrain()", function () { | ||
beforeEach(function () { | ||
this.instance = new LessCluster(); | ||
sinon.stub(this.instance, "getNextFile"); | ||
}); | ||
afterEach(function () { | ||
this.instance = null; | ||
}); | ||
it("should pass next available file to buildFile()", function () { | ||
sinon.stub(this.instance, "buildFile"); | ||
this.instance.getNextFile.returns("foo.less"); | ||
this.instance.onDrain(); | ||
this.instance.buildFile.should.have.been.calledWith("foo.less"); | ||
}); | ||
it("should emit 'finished' event when queue empty", function () { | ||
sinon.stub(this.instance, "emit"); | ||
this.instance.getNextFile.returns(); | ||
this.instance.onDrain(); | ||
this.instance.emit.should.have.been.calledWith("finished"); | ||
}); | ||
}); | ||
describe("buildFile()", function () { | ||
beforeEach(function () { | ||
this.instance = new LessCluster().run(); | ||
sinon.stub(this.instance.worker, "build"); | ||
}); | ||
afterEach(function () { | ||
this.instance.destroy(); | ||
this.instance = null; | ||
}); | ||
it("should not error when worker missing", function () { | ||
should.not.Throw(function () { | ||
new LessCluster().buildFile("foo.less"); | ||
}); | ||
}); | ||
it("should not call worker.build() when fileName missing", function () { | ||
this.instance.buildFile(); | ||
this.instance.worker.build.should.not.have.been.called; | ||
}); | ||
it("should call worker.build() with correct options", function () { | ||
var fileName = addImportsDir("base.less"); | ||
this.instance.buildFile(fileName); | ||
this.instance.worker.build.should.have.been.calledWith(fileName, addImportsDir("base.css")); | ||
}); | ||
}); | ||
describe("collect()", function () { | ||
@@ -138,13 +235,2 @@ var instance; | ||
it("_getDestinationPath()", function () { | ||
var actually = instance._getDestinationPath(addImportsDir('base.less')); | ||
var expected = addImportsDir('base.css'); | ||
actually.should.equal(expected); | ||
}); | ||
it("_getRelativePath()", function () { | ||
var actually = instance._getRelativePath(__dirname + '/fixtures'); | ||
var expected = 'test/fixtures'; | ||
actually.should.equal(expected); | ||
}); | ||
it("_getGlobPattern()", function () { | ||
@@ -184,3 +270,3 @@ var actually = instance._getGlobPattern('foo'); | ||
it("finds all files successfully", function () { | ||
cb.data.should.have.keys([ | ||
cb.data.should.include.keys([ | ||
addImportsDir("_variables.less"), | ||
@@ -197,2 +283,43 @@ addImportsDir("base.less"), | ||
}); | ||
describe("variations", function () { | ||
beforeEach(function () { | ||
this.instance = new LessCluster({ | ||
"directory": importsDir | ||
}); | ||
}); | ||
afterEach(function () { | ||
this.instance = null; | ||
}); | ||
it("should allow callback only", function (done) { | ||
this.instance.collect(done); | ||
}); | ||
it("should allow accept directory override", function (done) { | ||
this.instance.collect(includeDir, function (err, data) { | ||
should.exist(data); | ||
data.should.have.property(path.join(includeDir, "a.less")); | ||
data.should.have.property(path.join(includeDir, "b.less")); | ||
done(); | ||
}); | ||
}); | ||
it("should allow call with no arguments", function (done) { | ||
sinon.stub(this.instance, "_finishCollect", done); | ||
this.instance.collect(); | ||
}); | ||
it("should abort glob if error emitted", function (done) { | ||
sinon.stub(fs, "readdir"); | ||
// yieldsAsync() necessary to pass through internal process.nextTick | ||
fs.readdir.yieldsAsync({ code: "WTF" }); | ||
this.instance.collect(function (err) { | ||
fs.readdir.restore(); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -206,2 +333,3 @@ | ||
"included/a.less", | ||
"included/b.less", | ||
"modules/child.less", | ||
@@ -224,2 +352,3 @@ "modules/parent.less", | ||
"included/a.less", | ||
"included/b.less", | ||
"modules/child.less", | ||
@@ -238,2 +367,3 @@ "modules/parent.less", | ||
"included/a.less", | ||
"included/b.less", | ||
"modules/child.less", | ||
@@ -257,3 +387,2 @@ "modules/parent.less", | ||
// "base.less", | ||
// "included/a.less", | ||
"modules/child.less", | ||
@@ -316,10 +445,9 @@ "modules/parent.less", | ||
var absolutized = expected.map(addImportsDir); | ||
actually.should.eql(absolutized); | ||
actually.should.include.members(absolutized); | ||
}); | ||
expected.forEach(function (relativePath, i) { | ||
expected.forEach(function (relativePath) { | ||
it(relativePath, function () { | ||
var actually = results[subtitle]; | ||
should.exist(actually[i]); | ||
actually[i].should.equal(addImportsDir(relativePath)); | ||
actually.should.include(addImportsDir(relativePath)); | ||
}); | ||
@@ -344,3 +472,3 @@ }); | ||
if (relativePaths && relativePaths.length) { | ||
instanceConfig._files = relativePaths.map(function (p) { | ||
instanceConfig.files = relativePaths.map(function (p) { | ||
return path.join(importsDir, p); | ||
@@ -347,0 +475,0 @@ }); |
@@ -5,2 +5,6 @@ /*global describe, it, before, beforeEach, after, afterEach, chai, should, sinon */ | ||
**/ | ||
var fs = require('graceful-fs'); | ||
var path = require('path'); | ||
var less = require('less'); | ||
var rimraf = require('rimraf'); | ||
var LessWorker = require('../lib/less-worker'); | ||
@@ -19,2 +23,3 @@ | ||
}); | ||
describe("instance", function () { | ||
@@ -26,29 +31,376 @@ var instance = new LessWorker(); | ||
}); | ||
it("should set _fileData property to an empty object", function () { | ||
instance.should.have.property('_fileData') | ||
.that.is.an('object') | ||
.that.deep.equals({}); | ||
it("should create private caches as empty objects", function () { | ||
instance.should.have.property('_fileCache').that.deep.equals({}); | ||
instance.should.have.property('_pathCache').that.deep.equals({}); | ||
instance.should.have.property('_pathRebase').that.deep.equals({}); | ||
}); | ||
it("should default options when missing", function () { | ||
instance.options.should.deep.equal(LessWorker.defaults); | ||
}); | ||
describe("destroy()", function () { | ||
before(function () { | ||
sinon.spy(instance, "emit"); | ||
sinon.spy(instance, "removeAllListeners"); | ||
instance.destroy(); | ||
}); | ||
it("should nullify private caches", function () { | ||
instance.should.have.property('_fileCache').that.is.null; | ||
instance.should.have.property('_pathCache').that.is.null; | ||
instance.should.have.property('_pathRebase').that.is.null; | ||
}); | ||
it("should emit 'cleanup' event", function () { | ||
instance.emit.should.have.been.calledWith("cleanup"); | ||
}); | ||
it("should remove all listeners", function () { | ||
instance.removeAllListeners.should.have.been.calledOnce; | ||
}); | ||
it("should restore original less.Parser.importer"); | ||
// this is hard to assert that it actually happened... | ||
}); | ||
describe("with options", function () { | ||
var fileDataCache = { | ||
"foo.less": "foo" | ||
}; | ||
before(function () { | ||
this.instance = new LessWorker({ | ||
lint: true | ||
}, fileDataCache); | ||
}); | ||
after(function () { | ||
this.instance.destroy(); | ||
}); | ||
it("should override defaults when passed", function () { | ||
this.instance.options.should.have.property('lint').that.is.true; | ||
}); | ||
it("should update cache when fileData passed", function () { | ||
this.instance.should.have.property("_fileCache").that.equals(fileDataCache); | ||
// the object stored in _fileCache is literally the same object passed in | ||
fileDataCache.bar = "bar"; | ||
this.instance._fileCache.should.equal(fileDataCache); | ||
}); | ||
}); | ||
}); | ||
describe("methods", function () { | ||
describe("_applyConfig()", function () { | ||
var defaults = LessWorker.defaults; | ||
var instance = new LessWorker(); | ||
it("defaults values when missing", function () { | ||
instance._applyConfig(); | ||
instance.options.should.deep.equal(defaults); | ||
describe("method", function () { | ||
var fileName = "a.less"; | ||
var filePath = path.resolve(__dirname, "fixtures/imports/included", fileName); | ||
var fileData = "foo"; // file is actually empty, but we never actually read it | ||
var childImportName = "modules/parent.less"; | ||
var childImportPath = path.resolve(__dirname, "fixtures/imports", childImportName); | ||
var parentFilePath = path.resolve(__dirname, "fixtures/imports/base.less"); | ||
var ohNoesError = "oh noes!"; | ||
var outputdir = path.resolve(__dirname, "output"); | ||
// simulating --include-path arguments | ||
var envPaths = [path.dirname(filePath)]; | ||
beforeEach(function () { | ||
this.instance = new LessWorker({ | ||
outputdir: outputdir | ||
}); | ||
}); | ||
afterEach(function () { | ||
this.instance.destroy(); | ||
this.instance = null; | ||
}); | ||
it("overrides defaults when passed", function () { | ||
instance._applyConfig({ | ||
'lint': true | ||
describe("doneWrote()", function () { | ||
beforeEach(function () { | ||
sinon.stub(this.instance, "emit"); | ||
sinon.stub(this.instance, "log"); | ||
}); | ||
afterEach(function () { | ||
this.instance.emit.restore(); | ||
this.instance.log.restore(); | ||
}); | ||
it("should emit an error if present", function () { | ||
this.instance.doneWrote(filePath, ohNoesError); | ||
this.instance.emit.should.have.been.calledWith("error", ohNoesError); | ||
}); | ||
it("should emit drain when successful", function () { | ||
this.instance.doneWrote(filePath); | ||
this.instance.log.should.have.been.calledOnce; | ||
this.instance.emit.should.have.been.calledWith("drain", filePath); | ||
}); | ||
}); | ||
describe("inDir()", function () { | ||
beforeEach(function () { | ||
sinon.stub(this.instance, "emit"); | ||
sinon.stub(this.instance, "doneWrote"); | ||
sinon.stub(fs, "writeFile"); | ||
}); | ||
afterEach(function () { | ||
this.instance.emit.restore(); | ||
this.instance.doneWrote.restore(); | ||
fs.writeFile.restore(); | ||
}); | ||
it("should emit an error if present", function () { | ||
this.instance.inDir(filePath, fileData, ohNoesError); | ||
this.instance.emit.should.have.been.calledWith("error", ohNoesError); | ||
fs.writeFile.should.not.have.been.called; | ||
}); | ||
it("should write data into filename", function () { | ||
fs.writeFile.yields(); | ||
this.instance.inDir(filePath, fileData, null); | ||
this.instance.doneWrote.should.have.been.calledOnce; | ||
fs.writeFile.should.have.been.calledWith(filePath, fileData, "utf8", sinon.match.func); | ||
}); | ||
}); | ||
describe("writeOutput()", function () { | ||
beforeEach(function () { | ||
sinon.stub(this.instance, "emit"); | ||
sinon.stub(this.instance, "warn"); | ||
sinon.stub(this.instance, "inDir"); | ||
// mkdirp calls fs.mkdir under the covers | ||
sinon.stub(fs, "mkdir"); | ||
}); | ||
afterEach(function () { | ||
this.instance.emit.restore(); | ||
this.instance.warn.restore(); | ||
this.instance.inDir.restore(); | ||
fs.mkdir.restore(); | ||
}); | ||
it("should skip writing if data empty, draining immediately", function () { | ||
this.instance.writeOutput(filePath, ""); | ||
this.instance.warn.should.have.been.calledOnce; | ||
this.instance.emit.should.have.been.calledWith("drain", filePath); | ||
this.instance.inDir.should.not.have.been.called; | ||
fs.mkdir.should.not.have.been.called; | ||
}); | ||
it("should ensure directory exists before writing", function () { | ||
fs.mkdir.yields(null); // success | ||
this.instance.writeOutput(filePath, fileData); | ||
fs.mkdir.should.have.been.calledWith(path.dirname(filePath)); | ||
this.instance.inDir.should.have.been.calledWith(filePath, fileData, null); | ||
}); | ||
}); | ||
describe("resolveChildPath()", function () { | ||
beforeEach(function () { | ||
sinon.stub(fs, "existsSync"); | ||
}); | ||
afterEach(function () { | ||
fs.existsSync.restore(); | ||
}); | ||
describe("when cache is hot", function () { | ||
beforeEach(function () { | ||
this.instance._pathCache[fileName] = filePath; | ||
}); | ||
instance.options.should.have.property('lint').that.is.true; | ||
it("should early return cached childPath", function () { | ||
var result = this.instance.resolveChildPath(fileName, parentFilePath, []); | ||
fs.existsSync.should.not.have.been.called; | ||
result.should.equal(filePath); | ||
}); | ||
}); | ||
describe("when cache is cold", function () { | ||
beforeEach(function () { | ||
fs.existsSync.withArgs(filePath).returns(true); | ||
fs.existsSync.withArgs(childImportPath).returns(true); | ||
}); | ||
it("should cache a successful lookup", function () { | ||
this.instance | ||
.resolveChildPath(childImportName, parentFilePath, envPaths) | ||
.should.equal(childImportPath); | ||
fs.existsSync.should.have.been.calledOnce; | ||
this.instance._pathCache.should.have.property(childImportName, childImportPath); | ||
}); | ||
it("should loop over all include paths when locating child", function () { | ||
this.instance | ||
.resolveChildPath(fileName, parentFilePath, envPaths) | ||
.should.equal(filePath); | ||
fs.existsSync.should.have.been.calledTwice; | ||
}); | ||
it("should indicate if the resolved path has been rebased", function () { | ||
this.instance.resolveChildPath(childImportName, parentFilePath, envPaths); | ||
this.instance.resolveChildPath(fileName, parentFilePath, envPaths); | ||
fs.existsSync.should.have.been.calledThrice; | ||
this.instance._pathRebase.should.have.property(childImportName, false); | ||
this.instance._pathRebase.should.have.property(fileName, true); | ||
}); | ||
it("should throw error when lookup fails?"); | ||
}); | ||
}); | ||
describe("rebaseRootPath()", function () { | ||
describe("when staying inside parent directory", function () { | ||
it("should not rebase sibling rootpath", function () { | ||
var childFileInfo = { | ||
"currentDirectory" : __dirname + "/fixtures/imports/", | ||
"entryPath" : __dirname + "/fixtures/imports/", | ||
"filename" : __dirname + "/fixtures/imports/external.css", | ||
"rootFilename" : __dirname + "/fixtures/imports/base.less", | ||
"rootpath" : "" | ||
}; | ||
this.instance.rebaseRootPath("external.css", parentFilePath, childFileInfo); | ||
childFileInfo.rootpath.should.equal(""); | ||
}); | ||
it("should rebase relative from root file source path", function () { | ||
var childFileInfo = { | ||
"currentDirectory" : __dirname + "/fixtures/imports/modules/", | ||
"entryPath" : __dirname + "/fixtures/imports/", | ||
"filename" : __dirname + "/fixtures/imports/modules/parent.less", | ||
"rootFilename" : __dirname + "/fixtures/imports/base.less", | ||
"rootpath" : "" | ||
}; | ||
this.instance.rebaseRootPath(childImportName, parentFilePath, childFileInfo); | ||
childFileInfo.rootpath.should.equal("modules/"); | ||
}); | ||
}); | ||
describe("when traversing outside parent directory", function () { | ||
beforeEach(function () { | ||
this.instance._pathRebase[fileName] = true; | ||
}); | ||
it("should rebase relative from destination path", function () { | ||
var childFileInfo = { | ||
"currentDirectory" : __dirname + "/fixtures/imports/included/", | ||
"filename" : __dirname + "/fixtures/imports/included/a.less", | ||
"entryPath" : __dirname + "/fixtures/imports/", | ||
"rootFilename" : __dirname + "/fixtures/imports/base.less", | ||
"destPath" : __dirname + "/fixtures/output", | ||
"rootpath" : "" | ||
}; | ||
this.instance.rebaseRootPath(fileName, parentFilePath, childFileInfo); | ||
childFileInfo.rootpath.should.equal("../imports/included/"); | ||
}); | ||
}); | ||
}); | ||
describe("build()", function () { | ||
it("TODO"); | ||
var expectedFilePath = path.resolve(__dirname, "fixtures", "output", "base.css"); | ||
var compiledFilePath = path.resolve(outputdir, "base.css"); | ||
before(function (done) { | ||
// read file contents into context variables | ||
var test = this; | ||
rimraf(outputdir, function () { | ||
fs.readFile(expectedFilePath, "utf8", function (err, data) { | ||
test.expectedContent = data; | ||
fs.readFile(parentFilePath, "utf8", function (err, data) { | ||
test.parentContent = data; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
beforeEach(function () { | ||
// populate instance file cache | ||
this.instance._fileCache = {}; | ||
this.instance._fileCache[parentFilePath] = this.parentContent; | ||
}); | ||
it("should pass arguments directly to utility method", function () { | ||
sinon.stub(this.instance, "_build"); | ||
this.instance.build("foo.less", "foo.css"); | ||
this.instance._build.should.be.calledWith("foo.less", "foo.css"); | ||
}); | ||
describe("failure", function () { | ||
beforeEach(function () { | ||
sinon.stub(less, "writeError"); | ||
sinon.stub(console, "error"); | ||
}); | ||
afterEach(function () { | ||
less.writeError.restore(); | ||
console.error.restore(); | ||
}); | ||
it("should emit error when imported file not found", function (done) { | ||
this.instance.once("error", function (err) { | ||
should.exist(err); | ||
err.should.have.property("type", "Syntax"); | ||
err.should.have.property("message", "File not found: undefined"); | ||
less.writeError.should.have.been.calledWith(err, this.options); | ||
done(); | ||
}); | ||
this.instance.build(parentFilePath, compiledFilePath); | ||
}); | ||
it("should emit error when tree.toCSS fails", function (done) { | ||
// provide correct import paths | ||
this.instance.options.paths = envPaths; | ||
// we can't effectively mock tree.toCSS, so let's pretend | ||
sinon.stub(this.instance, "writeOutput").throws({ type: "Fake" }); | ||
this.instance.once("error", function (err) { | ||
should.exist(err); | ||
err.should.have.property("type", "Fake"); | ||
done(); | ||
}); | ||
this.instance.build(parentFilePath, compiledFilePath); | ||
}); | ||
}); | ||
describe("success", function () { | ||
beforeEach(function () { | ||
// provide correct import paths | ||
this.instance.options.paths = envPaths; | ||
}); | ||
it("should match compiled output", function (done) { | ||
var test = this; | ||
sinon.stub(console, "log"); | ||
this.instance.once("error", done); | ||
this.instance.once("drain", function (fileName) { | ||
should.exist(fileName); | ||
fileName.should.equal(compiledFilePath); | ||
fs.readFile(fileName, "utf8", function (err, data) { | ||
should.exist(data); | ||
data.should.equal(test.expectedContent); | ||
console.log.should.have.been.calledWith("compiled test/output/base.css"); | ||
console.log.restore(); | ||
done(); | ||
}); | ||
}); | ||
this.instance.build(parentFilePath, compiledFilePath); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -149,3 +149,3 @@ /*global describe, it, before, beforeEach, after, afterEach, chai, should, sinon */ | ||
afterEach(function () { | ||
cluster.workers = null; | ||
cluster.workers = {}; | ||
}); | ||
@@ -197,3 +197,3 @@ | ||
cluster.workers = null; | ||
cluster.workers = {}; | ||
}); | ||
@@ -261,3 +261,3 @@ }); | ||
afterEach(function () { | ||
cluster.workers = null; | ||
cluster.workers = {}; | ||
}); | ||
@@ -264,0 +264,0 @@ |
@@ -57,2 +57,3 @@ /*global describe, it, before, beforeEach, after, afterEach, chai, should, sinon */ | ||
}); | ||
it("should pass lifecycle events through to sendMaster", function () { | ||
@@ -73,2 +74,16 @@ var consoleError = sinon.stub(console, "error"); | ||
}); | ||
describe("when destroyed", function () { | ||
before(function () { | ||
sinon.spy(process, "removeListener"); | ||
instance.destroy(); | ||
}); | ||
after(function () { | ||
process.removeListener.restore(); | ||
}); | ||
it("should remove process.message listener", function () { | ||
process.removeListener.should.have.been.calledWith("message", sinon.match.func); | ||
}); | ||
}); | ||
}); | ||
@@ -100,5 +115,9 @@ | ||
it("should execute build command", function () { | ||
var build = sinon.stub(instance, "build"); | ||
var build = sinon.spy(instance, "build"); | ||
var _build = sinon.stub(instance, "_build"); | ||
var msg = { | ||
cmd: "build" | ||
cmd: "build", | ||
dest: "one.css", | ||
file: "one.less" | ||
}; | ||
@@ -110,2 +129,5 @@ | ||
build.restore(); | ||
_build.should.have.been.calledWith(msg.file, msg.dest); | ||
_build.restore(); | ||
}); | ||
@@ -165,3 +187,39 @@ it("should execute start command", function () { | ||
}); | ||
describe("start()", function () { | ||
var message = { "cmd": "start", "data": ["foo.less"] }; | ||
beforeEach(function () { | ||
this.instance = new ClusterWorker(); | ||
sinon.stub(this.instance, "emit"); | ||
sinon.stub(ClusterWorker, "readFiles"); | ||
}); | ||
afterEach(function () { | ||
ClusterWorker.readFiles.restore(); | ||
this.instance.destroy(); | ||
this.instance = null; | ||
}); | ||
it("should read all the files specified in message data", function () { | ||
this.instance.start(message); | ||
ClusterWorker.readFiles.should.have.been.calledWith(message.data, sinon.match.func); | ||
}); | ||
it("should emit error event when encountered", function () { | ||
var ohNoesError = "oh noes!"; | ||
ClusterWorker.readFiles.yields(ohNoesError); | ||
this.instance.start(message); | ||
this.instance.emit.should.have.been.calledWith("error", ohNoesError); | ||
}); | ||
it("should emit ready event when successful", function () { | ||
var data = {}; | ||
data["foo.less"] = "foo"; | ||
ClusterWorker.readFiles.yields(null, data); | ||
this.instance.start(message); | ||
this.instance.emit.should.have.been.calledWith("ready"); | ||
this.instance._fileCache.should.eql(data); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
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
141986
38
3516
71
3
5
7
2
+ Addedgraceful-fs@~2.0.1
+ Addedgraceful-fs@2.0.3(transitive)
Updatedglob@~3.2.7
Updatednopt@~2.1.2