Comparing version 0.1.2 to 0.2.0
@@ -47,2 +47,3 @@ /** | ||
var _extend = UTILS.extend; | ||
var _error = UTILS.makeError; | ||
@@ -53,9 +54,5 @@ function _reset(asset) { | ||
asset._sourcePackages = {}; | ||
asset._watching = {}; | ||
} | ||
function _error(context, done, err) { | ||
context.error(err); | ||
done(err); | ||
} | ||
//............................... | ||
@@ -71,2 +68,4 @@ // PACKAGED ASSET | ||
this.basedir = PATH.resolve(this.basedir); | ||
this.invalidate = this.invalidate.bind(this); | ||
if (!this.path) throw new Error('AssetPackager requires a path'); | ||
_reset(this); | ||
@@ -139,2 +138,6 @@ } | ||
], function(err) { | ||
if (self.watch && !self._watching[assetPath]) { | ||
self._watching[assetPath] = | ||
FS.watch(assetPath, { persistant: false }, self.invalidate); | ||
} | ||
return done(err, asset); | ||
@@ -174,2 +177,24 @@ }); | ||
/** | ||
* Returns true if the packager can generate the named path. For AssetPackager | ||
* this just patches against the path. | ||
* | ||
* @param {String} path search path | ||
* @return {void} | ||
*/ | ||
AssetPackager.prototype.exists = function(path, done) { | ||
done(path === this.path); | ||
}; | ||
/** | ||
* Returns an array of relative paths this asset can generate. The default | ||
* implementation can only generate one path. | ||
* | ||
* @param {Function} done invoked on complete | ||
* @return {void} | ||
*/ | ||
AssetPackager.prototype.findPaths = function(done) { | ||
return done(null, [this.path]); | ||
}; | ||
/** | ||
* Attempts to map a moduleId to a physical path, relative to the named | ||
@@ -268,4 +293,5 @@ * rootPath. This will respect the installed compiler extensions. | ||
var self = this; | ||
done = _error(this, done); | ||
_expandDependencies(this, [], [], assets, function(err, expanded) { | ||
if (err) return _error(self, done, err); | ||
if (err) return done(err); | ||
@@ -316,3 +342,8 @@ var seen = {}, output = [], hasConflicts = false, warned = []; | ||
*/ | ||
AssetPackager.prototype.build = function(done) { | ||
AssetPackager.prototype.build = function(sourcePath, done) { | ||
done = _error(this, done); | ||
if (sourcePath !== this.path) { | ||
return done(new Error('path not found ' + sourcePath)); | ||
} | ||
if (!this._build) { | ||
@@ -336,3 +367,3 @@ var self = this; | ||
if (err) return _error(self, done, err); | ||
if (err) return done(err); | ||
@@ -356,3 +387,4 @@ // normalize some params | ||
path: self.path, | ||
assets: assets | ||
assets: assets, | ||
type: self.type | ||
}; | ||
@@ -391,5 +423,4 @@ | ||
], function(err, asset) { | ||
if (err) return _error(self, done, err); | ||
self.info('built', self.main); | ||
done(null, asset); | ||
if (!err) self.info('built', self.main); | ||
done(err, asset); | ||
}); | ||
@@ -406,23 +437,20 @@ }); | ||
* | ||
* @param {String} outputPath Optional. output path to write to. | ||
* @param {String} dstPath Output path to write to. | ||
* @param {String} srcPath Asset path to write. Must equal path. | ||
* @param {Function} done Optional. Invoked when complete. | ||
* @return {void} | ||
*/ | ||
AssetPackager.prototype.writeFile = function(outputPath, done) { | ||
AssetPackager.prototype.writeFile = function(dstPath, srcPath, done) { | ||
var self = this; | ||
if (!done && 'function' === typeof outputPath) { | ||
done = outputPath; | ||
outputPath = null; | ||
} | ||
if (!outputPath) outputPath = this.path; | ||
outputPath = PATH.resolve(outputPath); | ||
this.build(function(err, asset) { | ||
if (err) return _error(self, done, err); | ||
FS.writeFile(outputPath, asset.body, asset.encoding || 'utf8', function(e){ | ||
if (e) return _error(self, done, e); | ||
self.info('wrote', outputPath); | ||
done(); | ||
done = _error(this, done); | ||
dstPath = PATH.resolve(dstPath); | ||
this.build(srcPath, function(err, asset) { | ||
if (err) return done(err); | ||
UTILS.mkdir_p(PATH.dirname(dstPath), function(err) { | ||
if (err) return done(err); | ||
var encoding = asset.encoding || 'utf8'; | ||
FS.writeFile(dstPath, asset.body, encoding, function(err) { | ||
if (!err) self.info('wrote', dstPath); | ||
done(err); | ||
}); | ||
}); | ||
@@ -433,2 +461,14 @@ }); | ||
/** | ||
* Cleans up any watchers. Normally just for testing. | ||
* @return {void} | ||
*/ | ||
AssetPackager.prototype.unwatch = function() { | ||
// stop watching to restart later... | ||
var self = this; | ||
Object.keys(self._watching).forEach(function(path) { | ||
self._watching[path].close(); | ||
}); | ||
}; | ||
/** | ||
* Invalidates any caches so that the next call to the packager will rebuild | ||
@@ -439,4 +479,5 @@ * from scratch. | ||
*/ | ||
AssetPackager.prototype.invalidate = function() { | ||
AssetPackager.prototype.invalidate = function(event, filename) { | ||
this.emit('invalidate'); | ||
this.unwatch(); | ||
_reset(this); | ||
@@ -443,0 +484,0 @@ }; |
@@ -10,2 +10,3 @@ /** | ||
var AssetPackager = require('./asset_packager').AssetPackager; | ||
var AssetCopier = require('./asset_copier').AssetCopier; | ||
@@ -24,4 +25,5 @@ /** | ||
exports.AssetPackager = AssetPackager; | ||
exports.AssetCopier = AssetCopier; | ||
exports.Pipeline = Pipeline; | ||
exports.plugins = require('../pipeline_plugins'); | ||
exports.packager = require('./packagers').packager; |
@@ -44,4 +44,6 @@ /** | ||
if (logger) logger.error.apply(logger, args); | ||
args.unshift('error'); | ||
this.emit.apply(this, args); | ||
if (this.listeners('error').length>0) { | ||
args.unshift('error'); | ||
this.emit.apply(this, args); | ||
} | ||
}; | ||
@@ -48,0 +50,0 @@ |
@@ -60,4 +60,2 @@ /** | ||
var LoggedEventEmitter = require('./logged_events').LoggedEventEmitter; | ||
var UTILS = require('./utils'); | ||
var PATH = require('path'); | ||
@@ -69,72 +67,13 @@ var ASYNC = require('async'); | ||
var AssetPackager = require('./asset_packager').AssetPackager; | ||
var plugins = require('../pipeline_plugins'); | ||
var LoggedEventEmitter = require('./logged_events').LoggedEventEmitter; | ||
var UTILS = require('./utils'); | ||
var packagers = require('./packagers'); | ||
// wraps done so that it will emit an error | ||
function _error(pipeline, done) { | ||
return function(err) { | ||
if (err) pipeline.emit('error', err); | ||
if (done) done.apply(this, arguments); | ||
}; | ||
} | ||
var _extend = UTILS.extend; | ||
var _values = UTILS.values; | ||
var _error = UTILS.makeError; | ||
//................................... | ||
// FILE UTILITIES | ||
// | ||
PATH.sep = PATH.sep || (process.platform == 'win32' ? '\\' : '/'); | ||
function _mkdir_p(path, done) { | ||
PATH.exists(path, function(exists) { | ||
if (exists) { | ||
FS.stat(path, function(err, stat) { | ||
if (!err && !stat.isDirectory()) { | ||
err = new Error(path + ' is not a directory'); | ||
} | ||
done(err); | ||
}); | ||
} else { | ||
_mkdir_p(PATH.dirname(path), function(err) { | ||
if (err) return done(err); | ||
FS.mkdir(path, done); | ||
}); | ||
} | ||
}); | ||
} | ||
function mkdir_p(path, done) { | ||
_mkdir_p(PATH.normalize(path), done); | ||
} | ||
function cp(src, dst, logger, done) { | ||
var is = FS.createReadStream(src); | ||
var os = FS.createWriteStream(dst); | ||
UTIL.pump(is, os, function(err) { | ||
if (!err) logger.info('copied', src, dst); | ||
done(err); | ||
}); | ||
} | ||
function cp_r(src, dst, logger, done) { | ||
FS.stat(src, function(err, stat) { | ||
if (stat.isDirectory()) { | ||
mkdir_p(dst, function(err) { | ||
if (err) return done(err); | ||
FS.readdir(src, function(err, files) { | ||
ASYNC.forEach(files, function(file, next) { | ||
cp_r(PATH.join(src, file), PATH.join(dst, file), logger, next); | ||
}, done); | ||
}); | ||
}); | ||
} else { | ||
cp(src, dst, logger, done); | ||
} | ||
}); | ||
} | ||
function Pipeline() { | ||
LoggedEventEmitter.call(this); | ||
this.packagers = {}; | ||
this.copiers = {}; | ||
this.invalidate = this.invalidate.bind(); // so we can use as a listener | ||
@@ -146,38 +85,2 @@ this._config(arguments); | ||
var DEFAULT_CONFIGS = { | ||
javascript: { | ||
compilers: { | ||
'.js': plugins.GenericCompiler, | ||
'.coffee': plugins.CoffeeScriptCompiler | ||
}, | ||
analyzer: plugins.CommonJSAnalyzer, | ||
linker: plugins.CommonJSLinker, | ||
minifier: plugins.UglifyMinifier | ||
}, | ||
css: { | ||
compilers: { | ||
'.css': plugins.GenericCompiler | ||
}, | ||
analyzer: plugins.GenericAnalyzer, | ||
linker: plugins.SimpleMergeLinker, | ||
minifier: null // none yet defined for CSS | ||
}, | ||
// for JS that doesn't know how to CommonJS | ||
legacy_javascript: { | ||
compilers: { | ||
'.js': plugins.GenericCompiler, | ||
'.coffee': plugins.CoffeeScriptCompiler | ||
}, | ||
analyzer: plugins.GenericAnalyzer, | ||
linker: plugins.SimpleMergeLinker, | ||
minifier: plugins.UglifyMinifier | ||
} | ||
}; | ||
var CONFIG_KEYS = ['watch']; | ||
@@ -220,15 +123,26 @@ | ||
Pipeline.prototype.add = function(path, config) { | ||
var ret, locals; | ||
var ret, Packager; | ||
if (config.type === 'copy') { | ||
this.copiers[path] = config; | ||
} else { | ||
if (this.packagers[path]) this.remove(path); | ||
locals = { watch: this.watch }; | ||
ret = new AssetPackager(DEFAULT_CONFIGS[config.type] || {}, locals, config); | ||
this.pipeLogging(ret, path); | ||
ret.on('invalidate', this.invalidate); | ||
this.packagers[path] = ret; | ||
if (this.packagers[path]) this.remove(path); | ||
Packager = config.packager; | ||
if ('string' === typeof Packager) { | ||
Packager = packagers[Packager]; | ||
if (!Packager) { | ||
throw new Error('Unknown packager ' + config.packager); | ||
} | ||
} else if (!Packager) { | ||
throw new Error('Must define a packager for '+path); | ||
} | ||
config = _extend({ | ||
watch: this.watch, | ||
pipeline: this, | ||
path: path | ||
}, config); | ||
ret = new Packager(config); | ||
this.pipeLogging(ret, path); | ||
ret.on('invalidate', this.invalidate); | ||
this.packagers[path] = ret; | ||
return ret; | ||
@@ -247,41 +161,27 @@ }; | ||
packager.removeListener('invalidate', this.invalidate); | ||
delete this.packagers[path]; | ||
} | ||
delete this.packagers[path]; | ||
}; | ||
function _copyFile(pipeline, path, buildir, done) { | ||
var copiers = pipeline.copiers, | ||
copyPath, working, dst, src, config; | ||
function _selectPackager(pipeline, path, done) { | ||
ASYNC.filter(_values(pipeline.packagers), function(packager, next) { | ||
packager.exists(path, next); | ||
}, function(packagers) { | ||
done(packagers[0]); | ||
}); | ||
} | ||
for(working in copiers) { | ||
if (copiers.hasOwnProperty(working) && path.indexOf(working) === 0) { | ||
if (!copyPath || working.length>copyPath.length) copyPath = working; | ||
} | ||
} | ||
if (copyPath) { | ||
config = copiers[copyPath]; | ||
dst = PATH.resolve(buildir, path); | ||
src = PATH.resolve(config.root, PATH.relative(copyPath, path)); | ||
mkdir_p(PATH.dirname(dst), function(err) { | ||
if (err) return done(err); | ||
cp_r(src, dst, pipeline, done); | ||
// builds a map of path -> packager | ||
function _mapPaths(pipeline, done) { | ||
ASYNC.reduce(_values(pipeline.packagers), {}, function(memo, packager, next){ | ||
packager.findPaths(function(err, paths) { | ||
if (err) return next(err); | ||
paths.forEach(function(path) { | ||
if (!memo[path]) memo[path] = packager; | ||
}); | ||
next(null, memo); | ||
}); | ||
} else { | ||
done(new Error('asset not found for ' + path)); | ||
} | ||
}, done); | ||
} | ||
function _writeFile(pipeline, path, buildir, done) { | ||
var packager = pipeline.packagers[path]; | ||
if (!packager) { | ||
_copyFile(pipeline, path, buildir, done); | ||
} else { | ||
path = PATH.resolve(buildir, path); | ||
packager.writeFile(path, done); | ||
} | ||
} | ||
/** | ||
@@ -296,3 +196,8 @@ * Writes a single asset to the build directory. Invokes callback when done. | ||
Pipeline.prototype.writeFile = function(path, buildir, done) { | ||
_writeFile(this, path, buildir, _error(this, done)); | ||
var self = this; | ||
done = _error(this, done); | ||
_selectPackager(this, path, function(packager) { | ||
if (!packager) return done(new Error('path not found '+path)); | ||
packager.writeFile(PATH.resolve(buildir, path), path, done); | ||
}); | ||
}; | ||
@@ -310,15 +215,64 @@ | ||
var self = this; | ||
var paths = Object.keys(this.packagers).concat(Object.keys(this.copiers)); | ||
ASYNC.forEach(paths, function(path, next) { | ||
_writeFile(self, path, buildir, next); | ||
}, _error(this, done)); | ||
_mapPaths(this, function(err, packagers) { | ||
if (err) return _error(self, err, done); | ||
ASYNC.forEach(Object.keys(packagers), function(path, next) { | ||
packagers[path].writeFile(PATH.resolve(buildir, path), path, next); | ||
}, done); | ||
}); | ||
}; | ||
/** | ||
* Builds a single asset. The callback will be invoked with an asset object. | ||
* The asset contains either a `body` property or a `bodyStream` that can | ||
* be used to return the asset contents. | ||
* | ||
* @param {String} path Path within pipeline | ||
* @param {Function} done Calling to invoke when asset is ready. | ||
* @return {void} | ||
*/ | ||
Pipeline.prototype.build = function(path, done) { | ||
_selectPackager(this, path, function(packager) { | ||
if (!packager) { | ||
_error(this, done, new Error('path not found '+path)); | ||
} else { | ||
packager.build(path, done); | ||
} | ||
}); | ||
}; | ||
/** | ||
* Invokes the done callback with `true` if the pipeline knows how to handle | ||
* the path. You will often use this when implementing middleware in a server. | ||
* | ||
* @param {String} path path to evaluate | ||
* @param {Function} callback Invoked in response. | ||
* @return {void} | ||
*/ | ||
Pipeline.prototype.exists = function(path, callback) { | ||
_selectPackager(this, path, function(packager) { | ||
callback(!!packager); | ||
}); | ||
}; | ||
/** | ||
* Discovers all the valid paths handled by this pipeline and returns them in | ||
* the done callback. This can involve crawling the disk for asset | ||
* directories. | ||
* | ||
* @param {Function} done Callback | ||
* @return {void} | ||
*/ | ||
Pipeline.prototype.findPaths = function(done) { | ||
_mapPaths(this, function(err, packagers) { | ||
done(err, !err && packagers ? Object.keys(packagers) : null); | ||
}); | ||
}; | ||
Pipeline.prototype.invalidate = function() { | ||
var key; | ||
var path; | ||
if (!this._invalidating) { | ||
this._invalidating = true; | ||
for(key in packagers) { | ||
if (packagers.hasOwnProperty(key)) packagers[key].invalidate(); | ||
for(path in packagers) { | ||
if (packagers.hasOwnProperty(path)) packagers[path].invalidate(); | ||
} | ||
@@ -325,0 +279,0 @@ this._invalidating = false; |
105
lib/utils.js
@@ -6,2 +6,7 @@ /** | ||
var PATH = require('path'); | ||
var FS = require('fs'); | ||
var ASYNC = require('async'); | ||
var UTIL = require('util'); | ||
/** | ||
@@ -32,5 +37,24 @@ * Runs the passed function once, caching the return value for future calls. | ||
exports.extend = function(dst, src) { | ||
function _extend(dst, src) { | ||
Object.keys(src).forEach(function(key) { dst[key] = src[key]; }); | ||
return dst; | ||
} | ||
exports.extend = _extend; | ||
exports.merge = function(ret) { | ||
function _merge(props) { _extend(ret, props); } | ||
var len = arguments.length, idx, props; | ||
for(idx=1;idx<len;idx++) { | ||
props = arguments[idx]; | ||
if (props instanceof Array) { | ||
props.forEach(_merge); | ||
} else { | ||
_extend(ret, props); | ||
} | ||
} | ||
return ret; | ||
}; | ||
@@ -42,1 +66,80 @@ | ||
// wraps done so that it will emit an error | ||
exports.makeError = function(pipeline, done) { | ||
return function(err) { | ||
if (err) pipeline.emit('error', err); | ||
if (done) done.apply(this, arguments); | ||
}; | ||
}; | ||
//................................... | ||
// FILE UTILITIES | ||
// | ||
PATH.sep = PATH.sep || (process.platform == 'win32' ? '\\' : '/'); | ||
var making = {}; | ||
function _mkdir_p(path, done) { | ||
PATH.exists(path, function(exists) { | ||
if (exists) { | ||
FS.stat(path, function(err, stat) { | ||
if (!err && !stat.isDirectory()) { | ||
err = new Error(path + ' is not a directory'); | ||
} | ||
done(err); | ||
}); | ||
} else { | ||
// sometimes multiple actions might try to make the same directory, | ||
// this prevents them from both trying to mkdir at the same time. | ||
if (!making[path]) { | ||
making[path] = ASYNC.memoize(function(done) { | ||
_mkdir_p(PATH.dirname(path), function(err) { | ||
if (err) return done(err); | ||
FS.mkdir(path, done); | ||
delete making[path]; | ||
}); | ||
}); | ||
} | ||
making[path](done); | ||
} | ||
}); | ||
} | ||
function mkdir_p(path, done) { | ||
_mkdir_p(PATH.normalize(path), done); | ||
} | ||
function cp(src, dst, logger, done) { | ||
var is = FS.createReadStream(src); | ||
var os = FS.createWriteStream(dst); | ||
UTIL.pump(is, os, function(err) { | ||
if (!err && logger) logger.info('copied', src, dst); | ||
done(err); | ||
}); | ||
} | ||
function cp_r(src, dst, logger, done) { | ||
FS.stat(src, function(err, stat) { | ||
if (stat.isDirectory()) { | ||
mkdir_p(dst, function(err) { | ||
if (err) return done(err); | ||
FS.readdir(src, function(err, files) { | ||
ASYNC.forEach(files, function(file, next) { | ||
cp_r(PATH.join(src, file), PATH.join(dst, file), logger, next); | ||
}, done); | ||
}); | ||
}); | ||
} else { | ||
cp(src, dst, logger, done); | ||
} | ||
}); | ||
} | ||
exports.mkdir_p = mkdir_p; | ||
exports.cp = cp; | ||
exports.cp_r = cp_r; | ||
{ | ||
"name": "convoy", | ||
"version": "v0.1.2", | ||
"version": "v0.2.0", | ||
"author": "Charles Jolley <charles@sproutcore.com>", | ||
@@ -23,3 +23,7 @@ "description": "Pluggable, package-aware asset pipeline for node", | ||
"resolve": "0.2.x", | ||
"uglify-js": "~1.2" | ||
"uglify-js": "~1.2", | ||
"glob": "~3.1", | ||
"mime": "~1.2", | ||
"watch": "0.5.x", | ||
"coffee-script": "~1.3" | ||
}, | ||
@@ -26,0 +30,0 @@ |
@@ -6,6 +6,13 @@ /** | ||
var FS = require('fs'); | ||
var CoffeeScript = require('coffee-script'); | ||
function CoffeeScriptCompiler(asset, context, done) { | ||
done(new Error('CoffeeScriptCompiler not yet implemented.')); | ||
FS.readFile(asset.path, 'utf8', function(err, data) { | ||
if (err) return done(err); | ||
asset.body = CoffeeScript.compile(data); | ||
done(); | ||
}); | ||
} | ||
module.exports = CoffeeScriptCompiler; |
@@ -10,3 +10,3 @@ /** | ||
var DEFAULT_UGLIFY_OPTIONS = { | ||
toplevel: true | ||
// TODO: determine default options | ||
}; | ||
@@ -13,0 +13,0 @@ |
@@ -44,3 +44,3 @@ # Convoy | ||
'app.js': { | ||
type: 'javascript', // selects some default plugins | ||
packager: 'javascript', // selects some default plugins | ||
main: path.resolve('app/main.js'), // starting module to include | ||
@@ -51,3 +51,3 @@ minify: true // must be set to minify output | ||
'app.css': { | ||
type: 'css', | ||
packager: 'css', | ||
main: path.resolve('app/styles') // includes app/styles/index.css | ||
@@ -57,3 +57,3 @@ }, | ||
'assets': { | ||
type: 'copy', | ||
packager: 'copy', | ||
root: 'app/assets' | ||
@@ -132,3 +132,3 @@ } | ||
Legacy JavaScript files work the same way. To build an asset with legacy | ||
javascript instead of modules, use the `legacy_javascript` type when | ||
javascript instead of modules, use the `legacy_javascript` packager when | ||
configuring your pipeline: | ||
@@ -138,3 +138,3 @@ | ||
'legacy.js': { | ||
type: 'legacy_javascript', | ||
packager: 'legacy_javascript', | ||
main: 'vendor/index.js' | ||
@@ -224,3 +224,3 @@ } | ||
'app.js': { | ||
type: 'javascript', | ||
packager: 'javascript', | ||
main: path.resolve('app/main.js'), | ||
@@ -232,3 +232,3 @@ | ||
'app.css': { | ||
type: 'css', | ||
packager: 'css', | ||
main: path.resolve('app/assets/main.less'), | ||
@@ -235,0 +235,0 @@ |
@@ -5,3 +5,4 @@ // loader for tests | ||
require('./unit/commonjs_packager_test'); | ||
require('./unit/asset_copier_test'); | ||
require('./unit/loader_test'); | ||
require('./unit/pipeline_write_test'); | ||
require('./unit/pipeline_test'); |
@@ -9,5 +9,10 @@ /** | ||
var lib = h.lib; | ||
var UTILS = require('../../lib/utils'); | ||
var PATH = require('path'); | ||
var ASYNC = require('async'); | ||
function packager(config) { | ||
return new lib.AssetPackager({ | ||
path: 'app.js', | ||
compilers: { | ||
@@ -34,2 +39,3 @@ '.js': lib.plugins.GenericCompiler | ||
inst = packager({ | ||
path: 'app.js', | ||
main: [h.fixture('test_module.js'), h.fixture('test_module_2.js')] | ||
@@ -41,3 +47,3 @@ }); | ||
function(done) { | ||
inst.build(function(err, asset) { | ||
inst.build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -50,5 +56,5 @@ asset.body.should.equal(expected); | ||
it('should memoize build result', function(done) { | ||
inst.build(function(err, asset1) { | ||
inst.build('app.js', function(err, asset1) { | ||
if (err) done(err); | ||
inst.build(function(err, asset2) { | ||
inst.build('app.js', function(err, asset2) { | ||
if (err) done(err); | ||
@@ -62,6 +68,6 @@ asset2.should.equal(asset1); | ||
it("should rebuild after invalidate", function(done) { | ||
inst.build(function(err, asset1) { | ||
inst.build('app.js', function(err, asset1) { | ||
if (err) return done(err); | ||
inst.invalidate(); | ||
inst.build(function(err, asset2) { | ||
inst.build('app.js', function(err, asset2) { | ||
if (err) return done(err); | ||
@@ -76,3 +82,3 @@ asset2.should.not.equal(asset1); | ||
var tmpfile = h.tmpfile(); | ||
inst.writeFile(tmpfile, function(err) { | ||
inst.writeFile(tmpfile, 'app.js', function(err) { | ||
if (err) return done(err); | ||
@@ -86,7 +92,7 @@ FS.readFileSync(tmpfile, 'utf8').should.equal(expected); | ||
var tmpfile = h.tmpfile(); | ||
inst.writeFile(tmpfile, function(err) { | ||
inst.writeFile(tmpfile, 'app.js', function(err) { | ||
if (err) return done(err); | ||
var expected = FS.readFileSync(tmpfile, 'utf8'); | ||
FS.writeFileSync(tmpfile, "DUMMY"); // make sure write happens again | ||
inst.writeFile(tmpfile, function(err) { | ||
inst.writeFile(tmpfile, 'app.js', function(err) { | ||
if (err) return done(err); | ||
@@ -110,6 +116,7 @@ FS.readFileSync(tmpfile, 'utf8').should.equal(expected); | ||
inst = packager({ | ||
path: 'app.js', | ||
main: h.fixture('local_dependencies.js') | ||
}); | ||
inst.build(function(err, asset) { | ||
inst.build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -126,6 +133,7 @@ asset.body.should.equal(expected); | ||
inst = packager({ | ||
path: 'app.js', | ||
main: h.fixture('circular', 'second.js') | ||
}); | ||
inst.build(function(err, asset) { | ||
inst.build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -142,4 +150,5 @@ asset.body.should.equal(expected); | ||
packager({ | ||
path: 'app.js', | ||
main: h.fixture('demo_package') | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -158,4 +167,5 @@ asset.body.should.equal(expected); | ||
packager({ | ||
path: 'app.js', | ||
main: h.fixture('demo_package/lib/requires_package.js') | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -177,5 +187,6 @@ asset.body.should.equal(expected); | ||
packager({ | ||
path: 'app.js', | ||
main: [h.fixture('test_module.js'), h.fixture('test_module_2.js')], | ||
minify: true | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -199,5 +210,6 @@ asset.body.should.equal(expected); | ||
packager({ | ||
path: 'app.js', | ||
main: [h.fixture('test_module.js'), h.fixture('test_module_2.js')], | ||
minify: options | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -238,2 +250,3 @@ asset.body.should.equal(expected); | ||
var inst = packager({ | ||
path: 'app.js', | ||
main: h.fixture('demo_package/conflict_test.js') | ||
@@ -244,3 +257,3 @@ }); | ||
inst.build(function(err, asset) { | ||
inst.build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -254,4 +267,196 @@ should.exist(log.warnings[0]); | ||
describe('[watching]', function() { | ||
var tmp = h.tmpfile('tmp_fixtures'), inst; | ||
beforeEach(function(done) { | ||
ASYNC.series([ | ||
function(next) { | ||
UTILS.mkdir_p(tmp, next); | ||
}, function(next) { | ||
var paths = ['local_dependencies.js', 'test_module.js', | ||
'test_module_2.js', 'required_module.js']; | ||
ASYNC.forEach(paths, function(path, next) { | ||
UTILS.cp(h.fixture(path), PATH.resolve(tmp, path), null, next); | ||
}, next); | ||
}, function(next) { | ||
inst = packager({ | ||
main: PATH.resolve(tmp, 'local_dependencies.js'), | ||
watch: true | ||
}); | ||
next(); | ||
} | ||
], done); | ||
}); | ||
afterEach(function() { | ||
inst.unwatch(); | ||
}); | ||
it('should invalidate when main file changes', function(done) { | ||
inst.build('app.js', function(err) { | ||
if (err) return done(err); | ||
// if not called then this will timeout eventually. | ||
inst.on('invalidate', function() { | ||
should.exist(true, 'invalidate called'); | ||
done(); | ||
}); | ||
var corePath = PATH.resolve(tmp, 'local_dependencies.js'); | ||
FS.writeFile(corePath, 'FOO', function(err) { | ||
if (err) done(err); | ||
}); | ||
}); | ||
}); | ||
it('should invalidate when a dependent changes', function(done) { | ||
inst.build('app.js', function(err) { | ||
if (err) return done(err); | ||
// if not called then this will timeout eventually. | ||
inst.on('invalidate', function() { | ||
should.exist(true, 'invalidate called'); | ||
done(); | ||
}); | ||
var corePath = PATH.resolve(tmp, 'test_module.js'); | ||
FS.writeFile(corePath, 'FOO', function(err) { | ||
if (err) done(err); | ||
}); | ||
}); | ||
}); | ||
it('should not invalidate again until rebuilt', function(done) { | ||
var shouldInvalidate = true, next; | ||
var corePath = PATH.resolve(tmp, 'test_module.js'); | ||
var timer = null; | ||
var cnt = 1; | ||
function writeFile() { | ||
FS.writeFile(corePath, 'FOO ' + cnt++, function(err) { | ||
if (err) { | ||
if (timer) clearTimeout(timer); | ||
done(err); | ||
} | ||
}); | ||
} | ||
var states ; | ||
function gotoState(key) { | ||
states[key](); | ||
} | ||
// this test progresses through the states below. the basic idea is: | ||
// 1. build, modify file -> should invalidate | ||
// 2. modify file again -> should not invalidate again | ||
// 3. build again, modify file -> should invalidate | ||
states = { | ||
first_build: function() { | ||
inst.build('app.js', function(err) { | ||
if (err) return done(err); | ||
// if not called then this will timeout eventually. | ||
inst.on('invalidate', function() { | ||
shouldInvalidate.should.equal(true, 'invalidate called'); | ||
next(); | ||
}); | ||
gotoState('first_write'); | ||
}); | ||
}, | ||
first_write: function() { | ||
shouldInvalidate = true; | ||
next = function() { gotoState('second_write'); }; | ||
writeFile(); | ||
}, | ||
second_write: function() { | ||
shouldInvalidate = false; | ||
timer = setTimeout(function() { | ||
gotoState('second_build'); | ||
}, 100); | ||
next = function() { | ||
clearTimeout(timer); | ||
done(); | ||
}; | ||
writeFile(); | ||
}, | ||
second_build: function() { | ||
inst.build('app.js', function(err) { | ||
if (err) return done(err); | ||
gotoState('third_write'); | ||
}); | ||
}, | ||
third_write: function() { | ||
shouldInvalidate = true; | ||
next = done; | ||
writeFile(); | ||
} | ||
}; | ||
gotoState('first_build'); | ||
}); | ||
it('should not invalidate when an unrelated file changes', function(done){ | ||
inst.build('app.js', function(err) { | ||
if (err) return done(err); | ||
inst.on('invalidate', function() { | ||
should.exist(false, 'invalidate called!'); | ||
clearTimeout(timer); | ||
done(new Error('invalidate called')); | ||
}); | ||
// give FS watcher some time. | ||
var timer = setTimeout(function() { | ||
should.exist(true, 'invalidate not called'); | ||
done(); | ||
}, 100); | ||
var corePath = PATH.resolve(tmp, 'unrelated.js'); | ||
FS.writeFile(corePath, 'FOO', function(err) { | ||
if (err) { | ||
clearTimeout(timer); | ||
done(err); | ||
} | ||
}); | ||
}); | ||
}); | ||
it ('should not invalidate until the asset has been built', function(done){ | ||
inst.on('invalidate', function() { | ||
should.exist(false, 'invalidate called!'); | ||
clearTimeout(timer); | ||
done(new Error('invalidate called')); | ||
}); | ||
// give FS watcher some time. | ||
var timer = setTimeout(function() { | ||
should.exist(true, 'invalidate not called'); | ||
done(); | ||
}, 100); | ||
var corePath = PATH.resolve(tmp, 'local_dependencies.js'); | ||
FS.writeFile(corePath, 'FOO', function(err) { | ||
if (err) { | ||
clearTimeout(timer); | ||
done(err); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -9,7 +9,10 @@ /** | ||
var UGLIFY = require('uglify-js'); | ||
var CS = require('coffee-script'); | ||
function packager(config) { | ||
return new lib.AssetPackager({ | ||
path: 'app.js', | ||
compilers: { | ||
'.js': lib.plugins.JavaScriptCompiler | ||
'.js': lib.plugins.JavaScriptCompiler, | ||
'.coffee': lib.plugins.CoffeeScriptCompiler | ||
}, | ||
@@ -60,3 +63,3 @@ | ||
main: h.fixture('commonjs_package/single.js') | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -78,3 +81,3 @@ asset.body.should.equal(expected); | ||
minify: true | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -101,3 +104,3 @@ console.log('SIZE: '+ asset.body.length); | ||
main: h.fixture('commonjs_package') | ||
}).build(function(err, asset) { | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -151,3 +154,3 @@ asset.body.should.equal(expected); | ||
inst.build(function(err, asset) { | ||
inst.build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
@@ -161,3 +164,35 @@ should.exist(log.warnings[0]); | ||
describe("CoffeeScript support", function() { | ||
function compileCoffee(body) { | ||
return CS.compile(body); | ||
} | ||
it('should parse CoffeeScript files', function(done) { | ||
var expected = join([ | ||
{ id: 'cs_package/coffee_include', | ||
body: compileCoffee(h.loadEach('cs_package/coffee_include.coffee')[0]) | ||
}, | ||
{ id: 'cs_package/javascript_include', | ||
body: h.loadEach('cs_package/javascript_include.js') | ||
}, | ||
{ id: ['cs_package/main', 'cs_package'], | ||
sourceURL: 'cs_package/main', | ||
body: compileCoffee(h.loadEach('cs_package/main.coffee')[0]) | ||
} | ||
]); | ||
packager({ | ||
main: h.fixture('cs_package/main.coffee') | ||
}).build('app.js', function(err, asset) { | ||
if (err) return done(err); | ||
asset.body.should.equal(expected); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
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
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
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
408871
11433
7
74
12
2
+ Addedcoffee-script@~1.3
+ Addedglob@~3.1
+ Addedmime@~1.2
+ Addedwatch@0.5.x
+ Addedcoffee-script@1.3.3(transitive)
+ Addedglob@3.1.21(transitive)
+ Addedgraceful-fs@1.2.3(transitive)
+ Addedinherits@1.0.2(transitive)
+ Addedlru-cache@2.7.3(transitive)
+ Addedmime@1.2.11(transitive)
+ Addedminimatch@0.2.14(transitive)
+ Addedsigmund@1.0.1(transitive)
+ Addedwatch@0.5.1(transitive)