gulp-buster
Advanced tools
Comparing version 0.1.0 to 0.2.0
58
index.js
@@ -1,2 +0,5 @@ | ||
var crypto = require('crypto'), | ||
'use strict'; | ||
var PLUGIN_NAME = 'gulp-buster', | ||
crypto = require('crypto'), | ||
path = require('path'), | ||
@@ -7,9 +10,23 @@ es = require('event-stream'), | ||
PluginError = gutil.PluginError, | ||
defaultConfig = { | ||
fileName: 'busters.json', | ||
algo: 'md5', | ||
length: 0 | ||
}, | ||
config = extend({}, defaultConfig), | ||
hashes = {}; | ||
const PLUGIN_NAME = 'gulp-buster'; | ||
// a simple, shallow object extend function | ||
function extend(dest, src) { | ||
Object.keys(src).forEach(function(key) { | ||
dest[key] = src[key]; | ||
}); | ||
return dest; | ||
} | ||
function md5(str) { | ||
return crypto.createHash('md5').update(str).digest('hex'); | ||
function hash(str) { | ||
var ret = crypto.createHash(config.algo).update(str).digest('hex'); | ||
return config.length ? ret.substr(0, config.length) : ret; | ||
} | ||
function path_relative_to_project(projectPath, filePath) { | ||
@@ -20,3 +37,4 @@ return path.relative(projectPath, filePath).replace(/\\/g, '/'); | ||
module.exports = function(fileName) { | ||
if (!fileName) throw new PluginError(PLUGIN_NAME, 'Missing fileName option for ' + PLUGIN_NAME); | ||
fileName = fileName || config.fileName; | ||
hashes[fileName] = hashes[fileName] || {}; | ||
@@ -27,7 +45,7 @@ var firstFile; | ||
if (file.isNull()) return; // ignore | ||
if (file.isStream()) return this.emit('error', new PluginError(PLUGIN_NAME, 'Streaming not supported')); | ||
if (file.isStream()) return this.emit('error', new PluginError(PLUGIN_NAME, 'Streaming not supported')); | ||
if (!firstFile) firstFile = file; | ||
hashes[path_relative_to_project(file.cwd, file.path)] = md5(file.contents.toString('utf8')); | ||
hashes[fileName][path_relative_to_project(file.cwd, file.path)] = hash(file.contents.toString('utf8')); | ||
} | ||
@@ -40,3 +58,3 @@ | ||
path: path.join(firstFile.base, fileName), | ||
contents: new Buffer(JSON.stringify(hashes)) | ||
contents: new Buffer(JSON.stringify(hashes[fileName])) | ||
}); | ||
@@ -51,4 +69,24 @@ | ||
// for testing | ||
module.exports._md5 = md5; | ||
module.exports.config = function(key, value) { | ||
if (!arguments.length) return config; | ||
if ({}.toString.call(key) === '[object Object]') { | ||
extend(config, key); | ||
} else if (typeof key === 'string') { | ||
if (arguments.length > 1) { | ||
config[key] = value; | ||
} else { | ||
return config[key]; | ||
} | ||
} else { | ||
throw new PluginError(PLUGIN_NAME, PLUGIN_NAME + ': Invalid first argument for .config(), must be object or string'); | ||
} | ||
}; | ||
// for testing. Don't use, may be removed or changed at anytime | ||
module.exports._hash = hash; | ||
module.exports._path_relative_to_project = path_relative_to_project; | ||
module.exports._reset = function() { | ||
config = extend({}, defaultConfig); | ||
hashes = {}; | ||
}; |
{ | ||
"name": "gulp-buster", | ||
"description": "Cache buster hashes generator for gulp", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Ult Combo", |
# [gulp](https://github.com/gulpjs/gulp/)-buster | ||
[](https://npmjs.org/package/gulp-buster) | ||
[](https://travis-ci.org/UltCombo/gulp-buster) | ||
[](https://david-dm.org/UltCombo/gulp-buster) | ||
[](https://david-dm.org/UltCombo/gulp-buster#info=devDependencies) | ||
@@ -15,3 +19,3 @@ Cache buster hashes generator for gulp | ||
## Using | ||
## How to use | ||
@@ -41,4 +45,39 @@ gulp-buster can be used standalone as part of a build task, or in conjunction with [`gulp-watch`](https://npmjs.org/package/gulp-watch) to update the cache buster hashes as the files are modified. | ||
`fileName` (first parameter): the output JSON file's name (with extension). | ||
- `fileName` (first parameter, string or `undefined`, optional): the output JSON file's name (with extension). The default is `busters.json`, which can also be changed through the `.config()` method (see below). | ||
## Configs | ||
You can set global configurations such as the hash algorithm and length (as well as the output `fileName`, which is useful when using a custom output filename and multiple entry points, more on that later) by calling the `.config()` method. It is very jQuery-y: | ||
```js | ||
var bust = require('gulp-buster'); | ||
// accepts an object as setter | ||
bust.config({ | ||
hash: 'sha1', | ||
length: 6 | ||
}); | ||
// pass no arguments to retrieve the current configs object | ||
var configs = bust.config(); // { fileName: 'busters.json', hash: 'sha1', length: 6 [, ...] } | ||
// NOTE: this returns a reference to the actual config object, so it is possible (but not advisable) | ||
// to edit the plugin's configs by assigning to this object's properties. | ||
// pass two arguments to set the value for a single config | ||
bust.config('length', 8); | ||
// and of course, pass a single string to retrieve the given config's value | ||
var lengthLimit = bust.config('length'); // 8 | ||
``` | ||
### Available configurations | ||
- `fileName` (string): the filename to be used when no `fileName` argument is specified in a `bust()` call. Defaults to `busters.json`. | ||
- `algo` (string): the hashing algorithm to be used. Defaults to `md5`. Accepts the same algorithms as [`crypto.createHash`](http://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm). | ||
- `length` (number): the maximum length of the hash. If specified, only the leading characters of the hash (up to `length`) will be returned. Defaults to `0`, which means no limit (actual length will then depend on the hashing algorithm used). Specifying a length larger than the hash will have no effect. | ||
## Multiple streams - single and multiple output files | ||
gulp-buster groups hashes by the output `fileName`. That means, piping two different streams into `bust('foo.json')` will merge both of those streams' files' hashes into the same output file (obviously, you should then set both streams' `.dest()` to the same path to don't create duplicated output files). Likewise, in case you'd like two streams' files to have their hashes outputted to different files, simply pass different filenames (and set their `.dest()` to your liking). | ||
## Integrating with Web applications | ||
@@ -78,3 +117,3 @@ | ||
As you can see, the gulp-buster `busters.json`'s paths are relative to project root without the leading slash, so we manually prepend a `/` to the cache-busted URL. This works nicely if your project root corresponds to the web server root. Otherwise, you will have to prepend the correct base URL - the optimal way would be to dynamically retrieve your app's base URL, specially if your local development server and production server's directory path differs (e.g. it is a common scenario to have production server run at `/` while local development server at `/myProject/`). | ||
As you can see, the gulp-buster `busters.json`'s paths are relative to project root without the leading slash, so we manually prepend a `/` to the cache-busted URL. This works nicely if your project root corresponds to the web server root. Otherwise, you will have to prepend the correct base URL. The optimal way would be to dynamically retrieve your app's base URL, specially as the project path relative to the web server root may differ between production and local development environments (e.g. it is a common scenario to have the project run at `/` in the production server and at `/myProject/` in local development). | ||
@@ -95,2 +134,6 @@ There are many ways to implement this in the front-end as well. If using an AMD loader such as Require.js, you can map modules to cache-busted URLs in the config. Even without any loader, it is possible to `document.write` the scripts as follows: | ||
## Changelog | ||
[Available here.](https://github.com/UltCombo/gulp-buster/blob/master/CHANGELOG.md) | ||
## FAQ | ||
@@ -97,0 +140,0 @@ |
@@ -9,2 +9,6 @@ var bust = require('../'); | ||
beforeEach(function() { | ||
bust._reset(); | ||
}); | ||
describe('gulp-buster', function() { | ||
@@ -14,3 +18,3 @@ describe('buster()', function() { | ||
it('should hash a string', function() { | ||
var hash = bust._md5('foo'); | ||
var hash = bust._hash('foo'); | ||
hash.should.be.a.String; | ||
@@ -20,2 +24,11 @@ hash.length.should.be.greaterThan(0); | ||
it('should return a hash with fixed length', function() { | ||
var expectedLength = 6; | ||
bust.config('length', expectedLength); | ||
var hash = bust._hash('foo'); | ||
hash.should.be.a.String; | ||
hash.length.should.equal(expectedLength); | ||
}); | ||
it('should return a path relative to project root', function() { | ||
@@ -26,3 +39,3 @@ bust._path_relative_to_project('/projectRoot/', '/projectRoot/folder/file.ext') | ||
it('should bust two files', function(done) { | ||
it('should bust two files into the same output', function(done) { | ||
var fileContentStr = "foo", | ||
@@ -58,4 +71,4 @@ fileContentStr2 = "bar"; | ||
var expectedObj = {}; | ||
expectedObj[bust._path_relative_to_project(fakeFile.cwd, fakeFile.path)] = bust._md5(fileContentStr); | ||
expectedObj[bust._path_relative_to_project(fakeFile2.cwd, fakeFile2.path)] = bust._md5(fileContentStr2); | ||
expectedObj[bust._path_relative_to_project(fakeFile.cwd, fakeFile.path)] = bust._hash(fileContentStr); | ||
expectedObj[bust._path_relative_to_project(fakeFile2.cwd, fakeFile2.path)] = bust._hash(fileContentStr2); | ||
@@ -71,3 +84,58 @@ JSON.parse(newFile.contents.toString()).should.eql(expectedObj); | ||
it('should bust two files into different outputs', function(done) { | ||
var fileContentStr = "foo", | ||
fileContentStr2 = "bar"; | ||
var stream = bust("output1.json"); | ||
var fakeFile = new File({ | ||
cwd: "/home/contra/", | ||
base: "/home/contra/test", | ||
path: "/home/contra/test/file.js", | ||
contents: new Buffer(fileContentStr) | ||
}); | ||
var stream2 = bust("output2.json"); | ||
var fakeFile2 = new File({ | ||
cwd: "/home/contra/", | ||
base: "/home/contra/test", | ||
path: "/home/contra/test/file2.js", | ||
contents: new Buffer(fileContentStr2) | ||
}); | ||
var testedOutputs = 0; | ||
stream.on('data', function(newFile) { | ||
var obj = JSON.parse(newFile.contents.toString()); | ||
should.exist(obj[bust._path_relative_to_project(fakeFile.cwd, fakeFile.path)]); | ||
should.not.exist(obj[bust._path_relative_to_project(fakeFile2.cwd, fakeFile2.path)]); | ||
if (++testedOutputs === 2) done(); | ||
}); | ||
stream2.on('data', function(newFile) { | ||
var obj = JSON.parse(newFile.contents.toString()); | ||
should.not.exist(obj[bust._path_relative_to_project(fakeFile.cwd, fakeFile.path)]); | ||
should.exist(obj[bust._path_relative_to_project(fakeFile2.cwd, fakeFile2.path)]); | ||
if (++testedOutputs === 2) done(); | ||
}); | ||
stream.write(fakeFile); | ||
stream.end(); | ||
stream2.write(fakeFile2); | ||
stream2.end(); | ||
}); | ||
}); | ||
describe('config method', function() { | ||
it('should return the configs object', function() { | ||
bust.config().should.be.an.Object; | ||
}); | ||
it('should accept an object as setter; string as getter', function() { | ||
bust.config({ foo: 0, bar: 1 }); | ||
bust.config('foo').should.equal(0); | ||
bust.config('bar').should.equal(1); | ||
}); | ||
it('should accept two arguments as setter', function() { | ||
bust.config('foo', 'bar'); | ||
bust.config('foo').should.equal('bar'); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
15915
8
187
142