gulp-manifest
Advanced tools
Comparing version 0.0.10 to 0.1.0
50
index.js
@@ -8,11 +8,17 @@ "use strict"; | ||
minimatch = require('minimatch'), | ||
slash = require('slash'), | ||
lineBreak = '\n'; | ||
function manifest(options) { | ||
var filename, exclude, hasher, cwd, contents; | ||
var filename, exclude, include, hasher, cwd, contents; | ||
options = options || {}; | ||
if(options.basePath) { | ||
gutil.log('basePath option is deprecated. Consider using gulp.src base instead: https://github.com/gulpjs/gulp/blob/master/docs/API.md#optionsbase'); | ||
} | ||
filename = options.filename || 'app.manifest'; | ||
exclude = Array.prototype.concat(options.exclude || []); | ||
include = Array.prototype.concat(options.include || []); | ||
exclude = Array.prototype.concat(options.exclude || []).concat(include); | ||
hasher = crypto.createHash('sha256'); | ||
@@ -32,5 +38,7 @@ cwd = process.cwd(); | ||
contents.push(lineBreak); | ||
contents.push(''); | ||
contents.push('CACHE:'); | ||
contents = contents.concat(include); | ||
if (options.cache) { | ||
@@ -42,4 +50,8 @@ options.cache.forEach(function (file) { | ||
function shouldExcludeFile(filePath) { | ||
return exclude.some(minimatch.bind(null, filePath)); | ||
} | ||
function writeToManifest(file) { | ||
var prefix, filepath; | ||
var prefix, suffix, filepath; | ||
@@ -49,11 +61,17 @@ if (file.isNull()) return; | ||
for (var i = 0; i < exclude.length; i++) { | ||
if(minimatch(file.relative, exclude[i])){ | ||
return; | ||
} | ||
prefix = slash(options.prefix || ''); | ||
suffix = slash(options.suffix || ''); | ||
filepath = slash(file.relative); | ||
if (shouldExcludeFile(filepath)) { | ||
return; | ||
} | ||
prefix = options.prefix || ''; | ||
filepath = prefix + file.relative; | ||
if(options.basePath) { // deprecated | ||
var relative = path.relative(file.base, __dirname); | ||
filepath = filepath.replace(new RegExp('^' + path.join(relative, options.basePath)), ''); | ||
} | ||
filepath = [prefix, filepath, suffix].join(''); | ||
contents.push(encodeURI(filepath)); | ||
@@ -69,3 +87,3 @@ | ||
options.network = options.network || ['*']; | ||
contents.push(lineBreak); | ||
contents.push(''); | ||
contents.push('NETWORK:'); | ||
@@ -78,4 +96,7 @@ options.network.forEach(function (file) { | ||
if (options.fallback) { | ||
contents.push(lineBreak); | ||
contents.push(''); | ||
contents.push('FALLBACK:'); | ||
if (typeof options.fallback === 'string') { | ||
options.fallback = [options.fallback]; | ||
} | ||
options.fallback.forEach(function (file) { | ||
@@ -98,3 +119,3 @@ var firstSpace = file.indexOf(' '); | ||
if (options.preferOnline) { | ||
contents.push(lineBreak); | ||
contents.push(''); | ||
contents.push('SETTINGS:'); | ||
@@ -106,3 +127,4 @@ contents.push('prefer-online'); | ||
if (options.hash) { | ||
contents.push('\n# hash: ' + hasher.digest("hex")); | ||
contents.push(''); | ||
contents.push('# hash: ' + hasher.digest("hex")); | ||
} | ||
@@ -109,0 +131,0 @@ |
{ | ||
"name": "gulp-manifest", | ||
"version": "0.0.10", | ||
"version": "0.1.0", | ||
"description": "Generate HTML5 Cache Manifest files", | ||
@@ -28,8 +28,10 @@ "main": "index.js", | ||
"mocha": "~1.16.2", | ||
"should": "~2.1.1" | ||
"should": "~2.1.1", | ||
"gulp": "^3.9.0" | ||
}, | ||
"dependencies": { | ||
"through": "~2.3.4", | ||
"gulp-util": "~2.2.6", | ||
"minimatch": "~2.0.1" | ||
"minimatch": "~2.0.1", | ||
"slash": "^1.0.0", | ||
"through": "~2.3.4" | ||
}, | ||
@@ -36,0 +38,0 @@ "bugs": { |
152
README.md
@@ -1,5 +0,5 @@ | ||
# gulp-manifest | ||
# gulp-manifest | ||
> Generate HTML5 Cache Manifest files. Submitted by [Scott Hillman](https://github.com/hillmanov/). | ||
Big thanks to [Gunther Brunner](https://github.com/gunta/) for writing the [grunt-manifest](https://github.com/gunta/grunt-manifest) plugin. This plugin was heavily influenced by his great work. | ||
Big thanks to [Gunther Brunner](https://github.com/gunta/) for writing the [grunt-manifest](https://github.com/gunta/grunt-manifest) plugin. This plugin was heavily influenced by his great work. | ||
@@ -25,16 +25,16 @@ Visit the [HTML 5 Guide to AppCache](http://www.html5rocks.com/en/tutorials/appcache/beginner/) for more information on Cache Manifest files. | ||
#### options.prefix | ||
Type: `String` | ||
Default: `undefined` | ||
Type: `String` | ||
Default: `undefined` | ||
Add a prefix to the file paths. Useful when your files are in a different URL than the page. | ||
#### options.basePath | ||
#### options.suffix | ||
Type: `String` | ||
Default: `undefined` | ||
Set a basepath for the files. Useful when the files are not served from the toplevel directory. | ||
Add a suffix to the file paths. Useful when your files have query string. | ||
#### options.filename | ||
Type: `String` | ||
Default: `"app.manifest"` | ||
Type: `String` | ||
Default: `'app.manifest'` | ||
@@ -44,4 +44,4 @@ Set name of the Cache Manifest file. | ||
#### options.cache | ||
Type: `String` | ||
Default: `undefined` | ||
Type: `String` | ||
Default: `undefined` | ||
@@ -51,4 +51,4 @@ Adds manually a string to the **CACHE** section. Needed when you have cache buster for example. | ||
#### options.exclude | ||
Type: `String` `Array` | ||
Default: `undefined` | ||
Type: `String` `Array` | ||
Default: `undefined` | ||
@@ -58,4 +58,4 @@ Exclude specific files from the Cache Manifest file. | ||
#### options.network | ||
Type: `String` `Array` | ||
Default: `"*"` (By default, an online whitelist wildcard flag is added) | ||
Type: `String` `Array` | ||
Default: `'*'` (By default, an online whitelist wildcard flag is added) | ||
@@ -67,4 +67,4 @@ Adds a string to the **NETWORK** section. | ||
#### options.fallback | ||
Type: `String` `Array` | ||
Default: `undefined` | ||
Type: `String` `Array` | ||
Default: `undefined` | ||
@@ -76,4 +76,4 @@ Adds a string to the **FALLBACK** section. | ||
#### options.preferOnline | ||
Type: `Boolean` | ||
Default: `undefined` | ||
Type: `Boolean` | ||
Default: `undefined` | ||
@@ -85,4 +85,4 @@ Adds a string to the **SETTINGS** section, specifically the cache mode flag of the ```prefer-online``` state. | ||
#### options.timestamp | ||
Type: `Boolean` | ||
Default: `true` | ||
Type: `Boolean` | ||
Default: `true` | ||
@@ -94,4 +94,4 @@ Adds a timestamp as a comment for easy versioning. | ||
#### options.hash | ||
Type: `Boolean` | ||
Default: `false` | ||
Type: `Boolean` | ||
Default: `false` | ||
@@ -106,3 +106,3 @@ Adds a sha256 hash of all `src` files (actual contents) as a comment. | ||
gulp.task('manifest', function(){ | ||
gulp.src(['build/*']) | ||
gulp.src(['build/*'], { base: './' }) | ||
.pipe(manifest({ | ||
@@ -121,19 +121,20 @@ hash: true, | ||
CACHE MANIFEST | ||
``` | ||
CACHE MANIFEST | ||
CACHE: | ||
js/app.js | ||
css/style | ||
css/style.css | ||
js/zepto.min.js | ||
js/script.js | ||
some_files/index.html | ||
some_files/about.html | ||
CACHE: | ||
js/app.js | ||
css/style | ||
css/style.css | ||
js/zepto.min.js | ||
js/script.js | ||
some_files/index.html | ||
some_files/about.html | ||
NETWORK: | ||
* | ||
NETWORK: | ||
* | ||
# hash: 76f0ef591f999871e1dbdf6d5064d1276d80846feeef6b556f74ad87b44ca16a | ||
# hash: 76f0ef591f999871e1dbdf6d5064d1276d80846feeef6b556f74ad87b44ca16a | ||
``` | ||
You do need to be fully aware of standard browser caching. | ||
@@ -143,1 +144,80 @@ If the files in **CACHE** are in the network cache, they won't actually update, | ||
Therefore, it's recommended to add a hash to the filenames's, akin to rails or yeoman. See [here](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/) why query strings are not recommended. | ||
### Composition of paths | ||
Sometimes your assets are served from different source directories. To route these correctly the `gulp.src.base` option can be used to [define a glob's base path](https://github.com/gulpjs/gulp/blob/master/docs/API.md#optionsbase). Later multiple streams can be composed with [merge-stream](https://github.com/grncdr/merge-stream), so that one single manifest file is created from them | ||
```javascript | ||
var path = require('path'); | ||
var mergeStream = require('merge-stream'); | ||
var config = { | ||
app: './app', | ||
tmp: './tmp' | ||
}; | ||
mergeStream( | ||
gulp.src([ | ||
path.join(config.app + '*.html'), | ||
path.join(config.app + 'assets/*.{png,svg,jpg}'), | ||
path.join(config.app + 'js/*.js') | ||
], { | ||
base: config.app | ||
}), | ||
gulp.src([ | ||
path.join(config.tmp + 'css/*.css') | ||
], { | ||
base: config.tmp | ||
}) | ||
); | ||
.pipe(plugins.manifest({ | ||
hash: true, | ||
preferOnline: false, | ||
network: ['*'], | ||
filename: 'appcache.manifest' | ||
})) | ||
.pipe(gulp.dest(config.tmp)); | ||
``` | ||
for the given file tree | ||
``` | ||
├── app | ||
│ ├── assets | ||
│ │ ├── cover.png | ||
│ │ └── logo.svg | ||
│ ├── index.html | ||
│ ├── js | ||
│ │ └── script.js | ||
│ └── scss | ||
│ └── style.scss | ||
└── tmp | ||
└── css | ||
└── style.css | ||
``` | ||
will result in | ||
``` | ||
index.html | ||
assets/cover.png | ||
assets/logo.svg | ||
js/script.js | ||
css/style.css | ||
``` | ||
Sometimes you might want to alter the way paths are passed to the plugin. The correct way will be to provide options to `gulp.src` so that it generates correct paths. | ||
Say, you have a single folder named `public`, which is the top-level directory that's served to the browser. In the same directory, you have the `css`, `js` and `asset` files under different directories, along with the `html` files. | ||
``` | ||
public/ | ||
├── assets | ||
│ ├── cover.png | ||
│ └── logo.png | ||
├── css | ||
│ └── style.css | ||
├── js | ||
│ └── app.js | ||
└── index.html | ||
``` |
314
test/main.js
@@ -7,2 +7,3 @@ var fs = require('fs'), | ||
gutil = require('gulp-util'), | ||
gulp = require('gulp'), | ||
mocha = require('mocha'), | ||
@@ -62,7 +63,10 @@ manifestPlugin = require('../'); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
var contents = data.contents.toString(); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain('fixture/hello.js'); | ||
done(); | ||
}); | ||
stream.once('end', done); | ||
@@ -84,4 +88,7 @@ stream.write(new gutil.File({ | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
var contents = data.contents.toString(); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain('file1.js'); | ||
@@ -91,4 +98,4 @@ contents.should.not.contain('file2.js'); | ||
contents.should.not.contain('file4.js'); | ||
done(); | ||
}); | ||
stream.once('end', done); | ||
@@ -99,17 +106,44 @@ fakeFiles.forEach(stream.write.bind(stream)); | ||
it('Should allow options.exclude to be a string', function(done) { | ||
var stream = manifestPlugin({ | ||
exclude: 'file2.js' | ||
}); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain('file1.js'); | ||
contents.should.not.contain('file2.js'); | ||
contents.should.contain('file3.js'); | ||
done(); | ||
}); | ||
fakeFiles.forEach(stream.write.bind(stream)); | ||
stream.end(); | ||
}); | ||
it('Should work with hash multiple times', function (done) { | ||
var pending = 2; | ||
function generateWithHash() { | ||
var stream = manifestPlugin({ hash: true }); | ||
stream.on('data', function (data) { | ||
data.contents.toString().should.contain('# hash: '); | ||
return new Promise(function(resolve, reject) { | ||
var stream = manifestPlugin({ hash: true }); | ||
var contents = ''; | ||
stream.on('data', function (data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function () { | ||
contents.should.contain('# hash: '); | ||
resolve(); | ||
}); | ||
fakeFiles.forEach(stream.write.bind(stream)); | ||
stream.end(); | ||
}); | ||
stream.once('end', function () { | ||
if (--pending <= 0) done(); | ||
}); | ||
fakeFiles.forEach(stream.write.bind(stream)); | ||
stream.end(); | ||
} | ||
generateWithHash(); | ||
generateWithHash(); | ||
Promise.all([ | ||
generateWithHash(), | ||
generateWithHash() | ||
]).then(function() { | ||
done(); | ||
}); | ||
}); | ||
@@ -124,10 +158,14 @@ | ||
var contents = '', | ||
relatives = []; | ||
stream.on('data', function(data) { | ||
data.should.be.an.instanceOf(gutil.File); | ||
data.relative.should.eql('cache.manifest'); | ||
var contents = data.contents.toString(); | ||
relatives.push(data.relative); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain('FALLBACK:\n/ /offline.html'); | ||
relatives.indexOf('cache.manifest').should.not.be.equal(-1); | ||
done(); | ||
}); | ||
stream.once('end', done); | ||
stream.end(); | ||
@@ -142,6 +180,31 @@ }); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
var contents = data.contents.toString(); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain(prefix); | ||
done(); | ||
}); | ||
stream.write(new gutil.File({ | ||
path: path.resolve('test\\fixture\\hello.js'), | ||
cwd: path.resolve('test/'), | ||
base: path.resolve('test/'), | ||
contents: new Buffer('notimportant') | ||
})); | ||
stream.end(); | ||
}); | ||
it('Should add a suffix', function(done) { | ||
var suffix = '?query', | ||
stream = manifestPlugin({ | ||
suffix: suffix | ||
}); | ||
stream.on('data', function(data) { | ||
var contents = data.contents.toString(); | ||
contents.should.contain(suffix); | ||
}); | ||
stream.once('end', done); | ||
@@ -159,2 +222,61 @@ | ||
it('Should lookup fileglobs in directories', function(done) { | ||
var stream = manifestPlugin(); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.match(/^subdir\/somefile.txt$/gm); | ||
contents.should.match(/^subdir\/other\/file.txt$/gm); | ||
done(); | ||
}); | ||
gulp.src(path.join(__dirname, 'fixtures/**')) | ||
.pipe(stream); | ||
}); | ||
it('Should lookup fileglobs relative to working directory when gulp.src.base is ./', function(done) { | ||
var stream = manifestPlugin(); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.match(/^test\/fixtures\/subdir\/somefile.txt$/gm); | ||
contents.should.match(/^test\/fixtures\/subdir\/other\/file.txt$/gm); | ||
done(); | ||
}); | ||
gulp.src(path.join(__dirname, 'fixtures/**'), { | ||
base: './' | ||
}) | ||
.pipe(stream); | ||
}); | ||
it('Should lookup fileglobs relative to gulp.src.base', function(done) { | ||
var stream = manifestPlugin(); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.match(/^fixtures\/subdir\/somefile.txt$/gm); | ||
contents.should.match(/^fixtures\/subdir\/other\/file.txt$/gm); | ||
done(); | ||
}); | ||
gulp.src(path.join(__dirname, 'fixtures/**'), { | ||
base: 'test' | ||
}) | ||
.pipe(stream); | ||
}); | ||
it('Should add correct path', function(done) { | ||
@@ -164,13 +286,16 @@ var filepath = 'test\\fixture\\hello.js', | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
var contents = data.contents.toString(); | ||
contents.should.contain(slash(filepath)); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', done); | ||
stream.once('end', function() { | ||
contents.should.match(new RegExp('^' + slash(filepath) + '$', 'gm')); | ||
done(); | ||
}); | ||
stream.write(new gutil.File({ | ||
path: path.resolve(filepath), | ||
path: slash(filepath), | ||
cwd: path.resolve('test/'), | ||
base: path.resolve('test/'), | ||
base: './', | ||
contents: new Buffer('notimportant') | ||
@@ -188,13 +313,16 @@ })); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
var contents = data.contents.toString(); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.not.contain(basePath); | ||
done(); | ||
}); | ||
stream.once('end', done); | ||
stream.write(new gutil.File({ | ||
path: path.resolve(basePath + '\\test\\fixture\\hello.js'), | ||
path: slash(basePath + '\\test\\fixture\\hello.js'), | ||
cwd: path.resolve('test/'), | ||
base: path.resolve('test/'), | ||
base: path.resolve('./'), | ||
contents: new Buffer('notimportant') | ||
@@ -209,3 +337,3 @@ })); | ||
filename: 'cache.manifest', | ||
exclude: ['file2.js','exclude/**',"children2/**/exclude*.js"], | ||
exclude: ['file2.js','exclude/**','children2/**/exclude*.js'], | ||
hash: true, | ||
@@ -216,7 +344,11 @@ network: ['http://*', 'https://*', '*'], | ||
var contents = '', relatives = []; | ||
stream.on('data', function(data) { | ||
data.should.be.an.instanceOf(gutil.File); | ||
data.relative.should.eql('cache.manifest'); | ||
var contents = data.contents.toString(); | ||
relatives.push(data.relative); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.startWith('CACHE MANIFEST'); | ||
@@ -233,6 +365,7 @@ contents.should.contain('CACHE:'); | ||
contents.should.contain('include2.js'); | ||
relatives.indexOf('cache.manifest').should.not.be.equal(-1); | ||
done(); | ||
}); | ||
stream.once('end', done); | ||
fakeFiles.forEach(stream.write.bind(stream)); | ||
@@ -242,2 +375,113 @@ | ||
}); | ||
it('Should exclude backslash excaped exclusions', function(done) { | ||
var stream = manifestPlugin({ | ||
filename: 'cache.manifest', | ||
exclude: ['some\\file.txt'] | ||
}); | ||
var contents = '', relatives = []; | ||
stream.on('data', function(data) { | ||
data.should.be.an.instanceOf(gutil.File); | ||
relatives.push(data.relative); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.startWith('CACHE MANIFEST'); | ||
contents.should.contain('CACHE:'); | ||
contents.should.contain('somefile.txt'); | ||
contents.should.not.contain('some/file.js'); | ||
relatives.indexOf('cache.manifest').should.not.be.equal(-1); | ||
done(); | ||
}); | ||
gulp.src(path.join(__dirname, 'fixtures/**'), { | ||
base: 'test' | ||
}) | ||
.pipe(stream); | ||
}); | ||
it('Should add includes provided by option.include', function(done) { | ||
var basePath = 'basedir', | ||
stream = manifestPlugin({ | ||
include: [ | ||
'foo/bar' | ||
] | ||
}); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain('baz'); | ||
contents.should.contain('foo/bar'); | ||
done(); | ||
}); | ||
stream.write(new gutil.File({ | ||
path: 'baz', | ||
base: path.resolve('./'), | ||
contents: new Buffer('notimportant') | ||
})); | ||
stream.end(); | ||
}); | ||
it('Should allow option.include to be a string', function(done) { | ||
var basePath = 'basedir', | ||
stream = manifestPlugin({ | ||
include: 'foo/bar' | ||
}); | ||
var contents = ''; | ||
stream.on('data', function(data) { | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.should.contain('baz'); | ||
contents.should.contain('foo/bar'); | ||
done(); | ||
}); | ||
stream.write(new gutil.File({ | ||
path: 'baz', | ||
base: path.resolve('./'), | ||
contents: new Buffer('notimportant') | ||
})); | ||
stream.end(); | ||
}); | ||
it('Should not duplicate includes', function(done) { | ||
var basePath = 'basedir', | ||
stream = manifestPlugin({ | ||
include: [ | ||
'foobar' | ||
] | ||
}); | ||
var contents = '', count = 0; | ||
stream.on('data', function(data) { | ||
data.should.be.an.instanceOf(gutil.File); | ||
contents += data.contents.toString(); | ||
}); | ||
stream.once('end', function() { | ||
contents.match(/foobar/gm).length.should.equal(1); | ||
done(); | ||
}); | ||
stream.write(new gutil.File({ | ||
path: 'foobar', | ||
base: path.resolve('./'), | ||
contents: new Buffer('notimportant') | ||
})); | ||
stream.end(); | ||
}); | ||
}); |
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
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
23850
8
503
213
4
4
+ Addedslash@^1.0.0
+ Addedslash@1.0.0(transitive)