├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json ├── test.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | - '0.12' 5 | - '0.10' 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 James K Nelson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [gulp](https://github.com/wearefractal/gulp)-rev-replace [![Build Status](https://travis-ci.org/jamesknelson/gulp-rev-replace.svg?branch=master)](https://travis-ci.org/jamesknelson/gulp-rev-replace) 2 | ================ 3 | 4 | Rewrite occurrences of filenames which have been renamed by gulp-rev 5 | 6 | ### Note: this package is no longer maintained. Development continues in [TheDancingCode/gulp-rev-rewrite](https://github.com/TheDancingCode/gulp-rev-rewrite). 7 | 8 | ## Install 9 | 10 | ```bash 11 | $ npm install --save-dev gulp-rev-replace 12 | ``` 13 | 14 | 15 | ## Usage 16 | 17 | Pipe through a stream which has both the files you want to be updated, as well as the files which have been renamed. 18 | 19 | For example, we can use [gulp-useref](https://github.com/jonkemp/gulp-useref) to concatenate assets in an index.html, 20 | and then use [gulp-rev](https://github.com/sindresorhus/gulp-rev) and gulp-rev-replace to cache-bust them. 21 | 22 | ```js 23 | var gulp = require('gulp'); 24 | var rev = require('gulp-rev'); 25 | var revReplace = require('gulp-rev-replace'); 26 | var useref = require('gulp-useref'); 27 | var filter = require('gulp-filter'); 28 | var uglify = require('gulp-uglify'); 29 | var csso = require('gulp-csso'); 30 | 31 | gulp.task("index", function() { 32 | var jsFilter = filter("**/*.js", { restore: true }); 33 | var cssFilter = filter("**/*.css", { restore: true }); 34 | var indexHtmlFilter = filter(['**/*', '!**/index.html'], { restore: true }); 35 | 36 | return gulp.src("src/index.html") 37 | .pipe(useref()) // Concatenate with gulp-useref 38 | .pipe(jsFilter) 39 | .pipe(uglify()) // Minify any javascript sources 40 | .pipe(jsFilter.restore) 41 | .pipe(cssFilter) 42 | .pipe(csso()) // Minify any CSS sources 43 | .pipe(cssFilter.restore) 44 | .pipe(indexHtmlFilter) 45 | .pipe(rev()) // Rename the concatenated files (but not index.html) 46 | .pipe(indexHtmlFilter.restore) 47 | .pipe(revReplace()) // Substitute in new filenames 48 | .pipe(gulp.dest('public')); 49 | }); 50 | ``` 51 | 52 | It is also possible to use gulp-rev-replace without gulp-useref: 53 | 54 | ```js 55 | var rev = require("gulp-rev"); 56 | var revReplace = require("gulp-rev-replace"); 57 | gulp.task("revision", ["dist:css", "dist:js"], function(){ 58 | return gulp.src(["dist/**/*.css", "dist/**/*.js"]) 59 | .pipe(rev()) 60 | .pipe(gulp.dest(opt.distFolder)) 61 | .pipe(rev.manifest()) 62 | .pipe(gulp.dest(opt.distFolder)) 63 | }) 64 | 65 | gulp.task("revreplace", ["revision"], function(){ 66 | var manifest = gulp.src("./" + opt.distFolder + "/rev-manifest.json"); 67 | 68 | return gulp.src(opt.srcFolder + "/index.html") 69 | .pipe(revReplace({manifest: manifest})) 70 | .pipe(gulp.dest(opt.distFolder)); 71 | }); 72 | ``` 73 | 74 | 75 | ## API 76 | 77 | ### revReplace(options) 78 | 79 | #### options.canonicalUris 80 | Type: `boolean` 81 | 82 | Default: `true` 83 | 84 | Use canonical Uris when replacing filePaths, i.e. when working with filepaths 85 | with non forward slash (`/`) path separators we replace them with forward slash. 86 | 87 | #### options.replaceInExtensions 88 | Type: `Array` 89 | 90 | Default: `['.js', '.css', '.html', '.hbs']` 91 | 92 | Only substitute in new filenames in files of these types. 93 | 94 | #### options.prefix 95 | Type: `string` 96 | 97 | Default: `` 98 | 99 | Add the prefix string to each replacement. 100 | 101 | #### options.manifest 102 | Type: `Stream` (e.g., `gulp.src()`) 103 | 104 | Read JSON manifests written out by `rev`. Allows replacing filenames that were 105 | `rev`ed prior to the current task. 106 | 107 | #### options.modifyUnreved, options.modifyReved 108 | Type: `Function` 109 | 110 | Modify the name of the unreved/reved files before using them. The filename is 111 | passed to the function as the first argument. 112 | 113 | For example, if in your manifest you have: 114 | 115 | ```js 116 | {"js/app.js.map": "js/app-98adc164.js.map"} 117 | ``` 118 | 119 | If you wanted to get rid of the `js/` path just for `.map` files (because they 120 | are sourcemaps and the references to them are relative, not absolute) you could 121 | do the following: 122 | 123 | ```js 124 | function replaceJsIfMap(filename) { 125 | if (filename.indexOf('.map') > -1) { 126 | return filename.replace('js/', ''); 127 | } 128 | return filename; 129 | } 130 | 131 | return gulp.src(opt.distFolder + '**/*.js') 132 | .pipe(revReplace({ 133 | manifest: manifest, 134 | modifyUnreved: replaceJsIfMap, 135 | modifyReved: replaceJsIfMap 136 | })) 137 | .pipe(gulp.dest(opt.distFolder)); 138 | ``` 139 | 140 | ## Contributors 141 | 142 | - Chad Jablonski 143 | - Denis Parchenko 144 | - Evgeniy Vasilev 145 | - George Song 146 | - Håkon K. Eide 147 | - Juan Lasheras 148 | - Majid Burney 149 | - Simon Ihmig 150 | - Vincent Voyer 151 | - Bradley Abrahams 152 | 153 | 154 | ## License 155 | 156 | [MIT](http://opensource.org/licenses/MIT) © [James K Nelson](http://jamesknelson.com) 157 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = plugin; 4 | 5 | var path = require('path'); 6 | var PluginError = require('plugin-error'); 7 | var through = require('through2'); 8 | 9 | var utils = require('./utils'); 10 | 11 | function plugin(options) { 12 | var renames = []; 13 | var cache = []; 14 | 15 | options = options || {}; 16 | 17 | if (typeof options.canonicalUris === 'undefined') { 18 | options.canonicalUris = true; 19 | } 20 | 21 | options.prefix = options.prefix || ''; 22 | 23 | options.replaceInExtensions = options.replaceInExtensions || ['.js', '.css', '.html', '.hbs']; 24 | 25 | return through.obj(function collectRevs(file, enc, cb) { 26 | if (file.isNull()) { 27 | this.push(file); 28 | return cb(); 29 | } 30 | 31 | if (file.isStream()) { 32 | this.emit('error', new PluginError('gulp-rev-replace', 'Streaming not supported')); 33 | return cb(); 34 | } 35 | 36 | // Collect renames from reved files. 37 | if (file.revOrigPath) { 38 | renames.push({ 39 | unreved: fmtPath(file.revOrigBase, file.revOrigPath), 40 | reved: options.prefix + fmtPath(file.base, file.path) 41 | }); 42 | } 43 | 44 | if (options.replaceInExtensions.indexOf(path.extname(file.path)) > -1) { 45 | // file should be searched for replaces 46 | cache.push(file); 47 | } else { 48 | // nothing to do with this file 49 | this.push(file); 50 | } 51 | 52 | cb(); 53 | }, function replaceInFiles(cb) { 54 | var stream = this; 55 | 56 | if (options.manifest) { 57 | // Read manifest file for the list of renames. 58 | options.manifest.on('data', function (file) { 59 | var manifest = JSON.parse(file.contents.toString()); 60 | Object.keys(manifest).forEach(function (srcFile) { 61 | renames.push({ 62 | unreved: canonicalizeUri(srcFile), 63 | reved: options.prefix + canonicalizeUri(manifest[srcFile]) 64 | }); 65 | }); 66 | }); 67 | options.manifest.on('end', replaceContents); 68 | } 69 | else { 70 | replaceContents(); 71 | } 72 | 73 | function replaceContents() { 74 | renames = renames.sort(utils.byLongestUnreved); 75 | 76 | // Once we have a full list of renames, search/replace in the cached 77 | // files and push them through. 78 | cache.forEach(function replaceInFile(file) { 79 | var contents = file.contents.toString(); 80 | 81 | renames.forEach(function replaceOnce(rename) { 82 | var unreved = options.modifyUnreved ? options.modifyUnreved(rename.unreved) : rename.unreved; 83 | var reved = options.modifyReved ? options.modifyReved(rename.reved) : rename.reved; 84 | contents = contents.split(unreved).join(reved); 85 | if (options.prefix) { 86 | contents = contents.split('/' + options.prefix).join(options.prefix + '/'); 87 | } 88 | }); 89 | 90 | file.contents = new Buffer(contents); 91 | stream.push(file); 92 | }); 93 | 94 | cb(); 95 | } 96 | }); 97 | 98 | function fmtPath(base, filePath) { 99 | var newPath = path.relative(base, filePath); 100 | 101 | return canonicalizeUri(newPath); 102 | } 103 | 104 | function canonicalizeUri(filePath) { 105 | if (path.sep !== '/' && options.canonicalUris) { 106 | filePath = filePath.split(path.sep).join('/'); 107 | } 108 | 109 | return filePath; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-rev-replace", 3 | "version": "0.4.4", 4 | "description": "Rewrite occurences of filenames which have been renamed by gulp-rev", 5 | "main": "index.js", 6 | "repository": "jamesknelson/gulp-rev-replace", 7 | "scripts": { 8 | "test": "mocha" 9 | }, 10 | "keywords": [ 11 | "gulpplugin", 12 | "rev", 13 | "revision", 14 | "version", 15 | "replace", 16 | "asset" 17 | ], 18 | "author": { 19 | "name": "James K Nelson", 20 | "email": "james@numbattech.com", 21 | "url": "http://jamesknelson.com" 22 | }, 23 | "engines": { 24 | "node": ">=0.10.0" 25 | }, 26 | "license": "MIT", 27 | "dependencies": { 28 | "plugin-error": "^0.1.2", 29 | "through2": "^2.0.0" 30 | }, 31 | "devDependencies": { 32 | "event-stream": "^3.2.2", 33 | "gulp-filter": "^3.0.1", 34 | "gulp-rev": "^6.0.1", 35 | "mocha": "^2.3.4", 36 | "vinyl": "^2.1.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* global it describe */ 2 | 3 | 'use strict'; 4 | 5 | var assert = require('assert'); 6 | var filter = require('gulp-filter'); 7 | var Vinyl = require('vinyl'); 8 | var path = require('path'); 9 | var rev = require('gulp-rev'); 10 | var es = require('event-stream'); 11 | 12 | var revReplace = require('./index'); 13 | var utils = require('./utils'); 14 | 15 | var svgFileBody = ''; 16 | var cssFileBody = '@font-face { font-family: \'test\'; src: url(\'/fonts/font.svg\'); }\nbody { color: red; }'; 17 | var jsFileBody = 'console.log("Hello world"); //# sourceMappingURL=app.js.map'; 18 | var htmlFileBody = ''; 19 | 20 | it('should by default replace filenames in .css and .html files', function (cb) { 21 | var filesToRevFilter = filter(['**/*.css', '**/*.svg', '**/*.png'], {restore: true}); 22 | 23 | var stream = filesToRevFilter 24 | .pipe(rev()) 25 | .pipe(filesToRevFilter.restore) 26 | .pipe(revReplace()); 27 | 28 | var fileCount = 0; 29 | var unreplacedCSSFilePattern = /style\.css/; 30 | var unreplacedSVGFilePattern = /font\.svg/; 31 | var unreplacedPNGFilePattern = /image\.png/; 32 | stream.on('data', function(file) { 33 | var contents = file.contents.toString(); 34 | var extension = path.extname(file.path); 35 | 36 | if (extension === '.html') { 37 | assert( 38 | !unreplacedCSSFilePattern.test(contents), 39 | 'The renamed CSS file\'s name should be replaced' 40 | ); 41 | assert( 42 | !unreplacedPNGFilePattern.test(contents), 43 | 'The renamed PNG file\'s name should be globally replaced' 44 | ); 45 | } else if (extension === '.css') { 46 | assert( 47 | !unreplacedSVGFilePattern.test(contents), 48 | 'The renamed SVG file\'s name should be replaced' 49 | ); 50 | } else if (extension === '.svg') { 51 | assert( 52 | contents === svgFileBody, 53 | 'The SVG file should not be modified' 54 | ); 55 | } 56 | 57 | fileCount++; 58 | }); 59 | stream.on('end', function() { 60 | assert.equal(fileCount, 4, 'Only four files should pass through the stream'); 61 | cb(); 62 | }); 63 | 64 | filesToRevFilter.write(new Vinyl({ 65 | path: path.join('css', 'style.css'), 66 | contents: new Buffer(cssFileBody) 67 | })); 68 | filesToRevFilter.write(new Vinyl({ 69 | path: path.join('fonts', 'font.svg'), 70 | contents: new Buffer(svgFileBody) 71 | })); 72 | filesToRevFilter.write(new Vinyl({ 73 | path: 'images/image.png', 74 | contents: new Buffer('PNG') 75 | })); 76 | filesToRevFilter.write(new Vinyl({ 77 | path: 'index.html', 78 | contents: new Buffer(htmlFileBody) 79 | })); 80 | 81 | filesToRevFilter.end(); 82 | }); 83 | 84 | it('should not replace filenames in extensions not in replaceInExtensions', function (cb) { 85 | var filesToRevFilter = filter(['**/*.css'], {restore: true}); 86 | 87 | var stream = filesToRevFilter 88 | .pipe(rev()) 89 | .pipe(filesToRevFilter.restore) 90 | .pipe(revReplace({replaceInExtensions: ['.svg']})); 91 | 92 | var unreplacedCSSFilePattern = /style\.css/; 93 | stream.on('data', function(file) { 94 | var contents = file.contents.toString(); 95 | var extension = path.extname(file.path); 96 | 97 | if (extension === '.html') { 98 | assert( 99 | unreplacedCSSFilePattern.test(contents), 100 | 'The renamed CSS file\'s name should not be replaced' 101 | ); 102 | } 103 | }); 104 | stream.on('end', function() { 105 | cb(); 106 | }); 107 | 108 | filesToRevFilter.write(new Vinyl({ 109 | path: 'css\\style.css', 110 | contents: new Buffer(cssFileBody) 111 | })); 112 | filesToRevFilter.write(new Vinyl({ 113 | path: 'index.html', 114 | contents: new Buffer(htmlFileBody) 115 | })); 116 | 117 | filesToRevFilter.end(); 118 | }); 119 | 120 | it('should not canonicalize URIs when option is off', function (cb) { 121 | var filesToRevFilter = filter(['**/*.css'], {restore: true}); 122 | 123 | var stream = filesToRevFilter 124 | .pipe(rev()) 125 | .pipe(filesToRevFilter.restore) 126 | .pipe(revReplace({canonicalUris: false})); 127 | 128 | var unreplacedCSSFilePattern = /style\.css/; 129 | stream.on('data', function(file) { 130 | var contents = file.contents.toString(); 131 | var extension = path.extname(file.path); 132 | 133 | if (extension === '.html') { 134 | assert( 135 | unreplacedCSSFilePattern.test(contents), 136 | 'The renamed CSS file\'s name should not be replaced' 137 | ); 138 | } 139 | }); 140 | stream.on('end', function() { 141 | cb(); 142 | }); 143 | 144 | filesToRevFilter.write(new Vinyl({ 145 | path: 'css\\style.css', 146 | contents: new Buffer(cssFileBody) 147 | })); 148 | filesToRevFilter.write(new Vinyl({ 149 | path: 'index.html', 150 | contents: new Buffer(htmlFileBody) 151 | })); 152 | 153 | filesToRevFilter.end(); 154 | }); 155 | 156 | 157 | it('should add prefix to path', function (cb) { 158 | var filesToRevFilter = filter(['**/*.css'], {restore: true}); 159 | 160 | var stream = filesToRevFilter 161 | .pipe(rev()) 162 | .pipe(filesToRevFilter.restore) 163 | .pipe(revReplace({prefix: 'http://example.com'})); 164 | 165 | var replacedCSSFilePattern = /"http:\/\/example\.com\/css\/style-[^\.]+\.css"/; 166 | stream.on('data', function(file) { 167 | var contents = file.contents.toString(); 168 | var extension = path.extname(file.path); 169 | if (extension === '.html') { 170 | assert( 171 | replacedCSSFilePattern.test(contents), 172 | 'The prefix should be added in to the file url' 173 | ); 174 | } 175 | }); 176 | stream.on('end', function() { 177 | cb(); 178 | }); 179 | 180 | filesToRevFilter.write(new Vinyl({ 181 | path: 'css/style.css', 182 | contents: new Buffer(cssFileBody) 183 | })); 184 | filesToRevFilter.write(new Vinyl({ 185 | path: 'index.html', 186 | contents: new Buffer(htmlFileBody) 187 | })); 188 | 189 | filesToRevFilter.end(); 190 | }); 191 | 192 | it('should stop at first longest replace', function(cb) { 193 | var jsFileBody = 'var loadFile = "nopestyle.css"'; 194 | var replacedJsFileBody = 'var loadFile = "nopestyle-19269897ba.css"'; 195 | 196 | var filesToRevFilter = filter(['**/*.css'], {restore: true}); 197 | 198 | var stream = filesToRevFilter 199 | .pipe(rev()) 200 | .pipe(filesToRevFilter.restore) 201 | .pipe(revReplace({canonicalUris: false})); 202 | 203 | stream.on('data', function(file) { 204 | if (file.path === 'script.js') { 205 | assert.equal( 206 | file.contents.toString(), 207 | replacedJsFileBody, 208 | 'It should have replaced using the longest string match (nopestyle)' 209 | ); 210 | } 211 | }); 212 | stream.on('end', function() { 213 | cb(); 214 | }); 215 | 216 | filesToRevFilter.write(new Vinyl({ 217 | path: 'style.css', 218 | contents: new Buffer(cssFileBody) 219 | })); 220 | filesToRevFilter.write(new Vinyl({ 221 | path: 'nopestyle.css', 222 | contents: new Buffer('boooooo') 223 | })); 224 | filesToRevFilter.write(new Vinyl({ 225 | path: 'script.js', 226 | contents: new Buffer(jsFileBody) 227 | })); 228 | 229 | filesToRevFilter.end(); 230 | }); 231 | 232 | describe('manifest option', function () { 233 | it('should replace filenames from manifest files', function (cb) { 234 | var manifest = es.readArray([ 235 | new Vinyl({ 236 | path: '/project/rev-manifest.json', 237 | contents: new Buffer(JSON.stringify({ 238 | '/css/style.css': '/css/style-12345.css' 239 | })) 240 | }), 241 | new Vinyl({ 242 | path: '/project/rev-image-manifest.json', 243 | contents: new Buffer(JSON.stringify({ 244 | 'images/image.png': 'images/image-12345.png', 245 | '/fonts/font.svg': '/fonts/font-12345.svg' 246 | })) 247 | }) 248 | ]); 249 | 250 | var stream = revReplace({manifest: manifest}); 251 | 252 | var replacedCSSFilePattern = /style-12345\.css/; 253 | var replacedSVGFilePattern = /font-12345\.svg/; 254 | var replacedPNGFilePattern = /image-12345\.png/; 255 | stream.on('data', function(file) { 256 | var contents = file.contents.toString(); 257 | var extension = path.extname(file.path); 258 | 259 | if (extension === '.html') { 260 | assert( 261 | replacedCSSFilePattern.test(contents), 262 | 'The renamed CSS file\'s name should be replaced' 263 | ); 264 | assert( 265 | replacedPNGFilePattern.test(contents), 266 | 'The renamed PNG file\'s name should be globally replaced' 267 | ); 268 | } else if (extension === '.css') { 269 | assert( 270 | replacedSVGFilePattern.test(contents), 271 | 'The renamed SVG file\'s name should be replaced' 272 | ); 273 | } else if (extension === '.svg') { 274 | assert( 275 | contents === svgFileBody, 276 | 'The SVG file should not be modified' 277 | ); 278 | } 279 | }); 280 | stream.on('end', function() { 281 | cb(); 282 | }); 283 | 284 | stream.write(new Vinyl({ 285 | path: path.join('css', 'style.css'), 286 | contents: new Buffer(cssFileBody) 287 | })); 288 | stream.write(new Vinyl({ 289 | path: path.join('fonts', 'font.svg'), 290 | contents: new Buffer(svgFileBody) 291 | })); 292 | stream.write(new Vinyl({ 293 | path: 'index.html', 294 | contents: new Buffer(htmlFileBody) 295 | })); 296 | 297 | stream.end(); 298 | }); 299 | 300 | it('should add prefix to path', function (cb) { 301 | var manifest = es.readArray([ 302 | new Vinyl({ 303 | path: '/project/rev-manifest.json', 304 | contents: new Buffer(JSON.stringify({ 305 | '/css/style.css': '/css/style-12345.css' 306 | })) 307 | }) 308 | ]); 309 | 310 | var stream = revReplace({prefix: 'http://example.com', manifest: manifest}); 311 | 312 | var replacedCSSFilePattern = /"http:\/\/example\.com\/css\/style-12345\.css"/; 313 | stream.on('data', function(file) { 314 | var contents = file.contents.toString(); 315 | var extension = path.extname(file.path); 316 | if (extension === '.html') { 317 | assert( 318 | replacedCSSFilePattern.test(contents), 319 | 'The prefix should be added in to the file url' 320 | ); 321 | } 322 | }); 323 | stream.on('end', function() { 324 | cb(); 325 | }); 326 | 327 | stream.write(new Vinyl({ 328 | path: 'index.html', 329 | contents: new Buffer(htmlFileBody) 330 | })); 331 | 332 | stream.end(); 333 | }); 334 | }); 335 | 336 | describe('utils.byLongestUnreved', function() { 337 | it('should arrange renames from longest to shortest', function() { 338 | var renames = [{ 339 | unreved: 'data/favicon.ico', 340 | reved: 'data/favicon-15d0f308.ico' 341 | }, { 342 | unreved: 'fonts/FontAwesome.otf', 343 | reved: 'fonts/FontAwesome-0b462f5c.otf' 344 | }, { 345 | unreved: 'fonts/fontawesome-webfont.eot', 346 | reved: 'fonts/fontawesome-webfont-f7c2b4b7.eot' 347 | }, { 348 | unreved: 'fonts/fontawesome-webfont.svg', 349 | reved: 'fonts/fontawesome-webfont-29800836.svg' 350 | }, { 351 | unreved: 'fonts/fontawesome-webfont.ttf', 352 | reved: 'fonts/fontawesome-webfont-706450d7.ttf' 353 | }, { 354 | unreved: 'fonts/fontawesome-webfont.woff', 355 | reved: 'fonts/fontawesome-webfont-d9ee23d5.woff' 356 | }, { 357 | unreved: 'fonts/fontawesome-webfont.woff2', 358 | reved: 'fonts/fontawesome-webfont-97493d3f.woff2' 359 | }, { 360 | unreved: 'fonts/glyphicons-halflings-regular.eot', 361 | reved: 'fonts/glyphicons-halflings-regular-f4769f9b.eot' 362 | }, { 363 | unreved: 'fonts/glyphicons-halflings-regular.svg', 364 | reved: 'fonts/glyphicons-halflings-regular-89889688.svg' 365 | }, { 366 | unreved: 'fonts/glyphicons-halflings-regular.ttf', 367 | reved: 'fonts/glyphicons-halflings-regular-e18bbf61.ttf' 368 | }, { 369 | unreved: 'fonts/glyphicons-halflings-regular.woff', 370 | reved: 'fonts/glyphicons-halflings-regular-fa277232.woff' 371 | }, { 372 | unreved: 'fonts/glyphicons-halflings-regular.woff2', 373 | reved: 'fonts/glyphicons-halflings-regular-448c34a5.woff2' 374 | }, { 375 | unreved: 'images/busy-indicator-lg-dark.gif', 376 | reved: 'images/busy-indicator-lg-dark-8f372b90.gif' 377 | }, { 378 | unreved: 'images/busy-indicator-lg-light.gif', 379 | reved: 'images/busy-indicator-lg-light-25050875.gif' 380 | }, { 381 | unreved: 'images/busy-indicator-sm-light.gif', 382 | reved: 'images/busy-indicator-sm-light-b464283c.gif' 383 | }, { 384 | unreved: 'images/footer-logo.png', 385 | reved: 'images/footer-logo-df3d73ed.png' 386 | }, { 387 | unreved: 'images/icon-progress-indicator.png', 388 | reved: 'images/icon-progress-indicator-b342c570.png' 389 | }, { 390 | unreved: 'images/scripps-swirl.png', 391 | reved: 'images/scripps-swirl-65b0319e.png' 392 | }, { 393 | unreved: 'images/sprite.png', 394 | reved: 'images/sprite-9e275087.png' 395 | }, { 396 | unreved: 'images/sprite_2x.png', 397 | reved: 'images/sprite_2x-c7af344b.png' 398 | }, { 399 | unreved: 'scripts/app.js', reved: 'scripts/app-137924b0.js' 400 | }, { 401 | unreved: 'styles/app.css', reved: 'styles/app-4858235a.css' 402 | }, { 403 | unreved: 'env/deploy/features.json', 404 | reved: 'env/deploy/features-2a501331.json' 405 | }]; 406 | 407 | var expected = [{ 408 | unreved: 'fonts/glyphicons-halflings-regular.woff2', 409 | reved: 'fonts/glyphicons-halflings-regular-448c34a5.woff2' 410 | }, { 411 | unreved: 'fonts/glyphicons-halflings-regular.woff', 412 | reved: 'fonts/glyphicons-halflings-regular-fa277232.woff' 413 | }, { 414 | unreved: 'fonts/glyphicons-halflings-regular.svg', 415 | reved: 'fonts/glyphicons-halflings-regular-89889688.svg' 416 | }, { 417 | unreved: 'fonts/glyphicons-halflings-regular.ttf', 418 | reved: 'fonts/glyphicons-halflings-regular-e18bbf61.ttf' 419 | }, { 420 | unreved: 'fonts/glyphicons-halflings-regular.eot', 421 | reved: 'fonts/glyphicons-halflings-regular-f4769f9b.eot' 422 | }, { 423 | unreved: 'images/busy-indicator-lg-light.gif', 424 | reved: 'images/busy-indicator-lg-light-25050875.gif' 425 | }, { 426 | unreved: 'images/busy-indicator-sm-light.gif', 427 | reved: 'images/busy-indicator-sm-light-b464283c.gif' 428 | }, { 429 | unreved: 'images/icon-progress-indicator.png', 430 | reved: 'images/icon-progress-indicator-b342c570.png' 431 | }, { 432 | unreved: 'images/busy-indicator-lg-dark.gif', 433 | reved: 'images/busy-indicator-lg-dark-8f372b90.gif' 434 | }, { 435 | unreved: 'fonts/fontawesome-webfont.woff2', 436 | reved: 'fonts/fontawesome-webfont-97493d3f.woff2' 437 | }, { 438 | unreved: 'fonts/fontawesome-webfont.woff', 439 | reved: 'fonts/fontawesome-webfont-d9ee23d5.woff' 440 | }, { 441 | unreved: 'fonts/fontawesome-webfont.eot', 442 | reved: 'fonts/fontawesome-webfont-f7c2b4b7.eot' 443 | }, { 444 | unreved: 'fonts/fontawesome-webfont.ttf', 445 | reved: 'fonts/fontawesome-webfont-706450d7.ttf' 446 | }, { 447 | unreved: 'fonts/fontawesome-webfont.svg', 448 | reved: 'fonts/fontawesome-webfont-29800836.svg' 449 | }, { 450 | unreved: 'env/deploy/features.json', 451 | reved: 'env/deploy/features-2a501331.json' 452 | }, { 453 | unreved: 'images/scripps-swirl.png', 454 | reved: 'images/scripps-swirl-65b0319e.png' 455 | }, { 456 | unreved: 'images/footer-logo.png', 457 | reved: 'images/footer-logo-df3d73ed.png' 458 | }, { 459 | unreved: 'fonts/FontAwesome.otf', 460 | reved: 'fonts/FontAwesome-0b462f5c.otf' 461 | }, { 462 | unreved: 'images/sprite_2x.png', 463 | reved: 'images/sprite_2x-c7af344b.png' 464 | }, { 465 | unreved: 'images/sprite.png', 466 | reved: 'images/sprite-9e275087.png' 467 | }, { 468 | unreved: 'data/favicon.ico', 469 | reved: 'data/favicon-15d0f308.ico' 470 | }, { 471 | unreved: 'scripts/app.js', reved: 'scripts/app-137924b0.js' 472 | }, { 473 | unreved: 'styles/app.css', reved: 'styles/app-4858235a.css' 474 | }]; 475 | 476 | assert.deepEqual(renames.sort(utils.byLongestUnreved), expected); 477 | }); 478 | }); 479 | 480 | describe('modifyUnreved and modifyReved options', function() { 481 | it('should modify the names of reved and un-reved files', function(cb) { 482 | var manifest = es.readArray([ 483 | new Vinyl({ 484 | path: '/project/rev-manifest.json', 485 | contents: new Buffer(JSON.stringify({ 486 | 'js/app.js.map': 'js/app-12345.js.map', 487 | 'css/style.css': 'css/style-12345.css' 488 | })) 489 | }) 490 | ]); 491 | 492 | function replaceJsIfMap(filename) { 493 | if (filename.indexOf('.map') > -1) { 494 | return filename.replace('js/', ''); 495 | } 496 | return filename; 497 | } 498 | 499 | var stream = revReplace({ 500 | manifest: manifest, 501 | modifyUnreved: replaceJsIfMap, 502 | modifyReved: replaceJsIfMap 503 | }); 504 | 505 | var replacedJSMapFilePattern = /sourceMappingURL\=app-12345\.js\.map/; 506 | var replacedCSSFilePattern = /css\/style-12345\.css/; 507 | 508 | stream.on('data', function(file) { 509 | var contents = file.contents.toString(); 510 | var extension = path.extname(file.path); 511 | 512 | if (extension === '.js') { 513 | assert( 514 | replacedJSMapFilePattern.test(contents), 515 | 'The source map has been correctly replaced using modifyReved and modifyUnreved' 516 | ); 517 | } else if (extension === '.html') { 518 | assert( 519 | replacedCSSFilePattern.test(contents), 520 | 'The renamed CSS file\'s name should be replaced and not affected by modifyReved or modifyUnreved' 521 | ); 522 | } 523 | }); 524 | 525 | stream.on('end', function() { 526 | cb(); 527 | }); 528 | 529 | stream.write(new Vinyl({ 530 | path: path.join('js', 'app.js'), 531 | contents: new Buffer(jsFileBody) 532 | })); 533 | stream.write(new Vinyl({ 534 | path: 'index.html', 535 | contents: new Buffer(htmlFileBody) 536 | })); 537 | 538 | stream.end(); 539 | }); 540 | }); 541 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function byLongestUnreved(a, b) { 4 | return b.unreved.length - a.unreved.length; 5 | } 6 | 7 | module.exports = { 8 | byLongestUnreved: byLongestUnreved 9 | }; 10 | --------------------------------------------------------------------------------