Skip to content
This repository has been archived by the owner on Oct 25, 2022. It is now read-only.

Commit

Permalink
Merge pull request #30 from Luismahou/override-hashed-files
Browse files Browse the repository at this point in the history
Override hashed files
  • Loading branch information
Luismahou committed Jan 23, 2014
2 parents 4bf8a53 + 04e7291 commit 87ddf3d
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 33 deletions.
19 changes: 10 additions & 9 deletions README.md
@@ -1,7 +1,9 @@
## grunt-hashres has been updated to Grunt 0.4. if you're still using Grunt 0.3.x go to [old version documentation](https://github.com/Luismahou/grunt-hashres/blob/master/grunt-0.3-README.md)
#### grunt-hashres has been updated to Grunt 0.4. if you're still using Grunt 0.3.x go to [old version documentation](https://github.com/Luismahou/grunt-hashres/blob/master/grunt-0.3-README.md)

# grunt-hashres

[![Build Status](https://api.travis-ci.org/Luismahou/grunt-hashres.png)](https://travis-ci.org/Luismahou/grunt-hashres)

Hashes your js and css files and rename the ```<script>``` and ```<link>``` declarations that refer to them in your html/php/etc files.

## Getting Started
Expand Down Expand Up @@ -50,12 +52,13 @@ hashres: {
}
```

The way this task works follows my workflow: I only hash the .js and .css files of my production release files,
which are first both uglified and minified.
If you want to hash a different set of files for a different environment,
simply add another subtask under ```hashres```.
### Recommended workflow
```grunt-hashres```, as a general rule, should be run when you're going to release your code. Ideally, you should create a ```stage``` folder where you'll copy your ```html```, minified ```js``` and ```css``` and all your resources. And then, on this clean copy, hash the resource names.

### Alternative workflow
Due to popular demand, the task support to update references that where already hashed. This means, that you won't need to create a stage folder before running ```grunt-hashres```. See [#26](https://github.com/Luismahou/grunt-hashres/issues/26) and [#29](https://github.com/Luismahou/grunt-hashres/issues/29) for more info.

### Heads up:
### Heads up
If you have upgraded from Grunt 0.3 version: 'files' and 'out' config properties have been replaced by 'src' and 'dest'

### Properties
Expand All @@ -71,14 +74,12 @@ according to the pattern specified in this property. The following variables are
* ```${ext}```: the original extension of the file.
* ```renameFiles```: Rename the files or leave them in place and only alter the references to them in ```out```. Defaults to ```true```

## Status
[![Build Status](https://api.travis-ci.org/Luismahou/grunt-hashres.png)](https://travis-ci.org/Luismahou/grunt-hashres)

## Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style.
Add unit tests for any new or changed functionality. Lint and test your code using [grunt][grunt].

## Release History
* 24/01/14 - 0.4.0: Due to popular demand (see [#26](https://github.com/Luismahou/grunt-hashres/issues/26), [#29](https://github.com/Luismahou/grunt-hashres/issues/29), and more) ```grunt-hashres``` now support to run multiple times the hashing without creating a clean copy of the files to hash. Thanks to [jrduncans](https://github.com/jrduncans) and [DaWyz](https://github.com/DaWyz) for their effort.
* 17/11/14 - 0.3.4: Bugfix [#18](https://github.com/Luismahou/grunt-hashres/pull/18): Fixed special character test that doesn't work on windows.
* 07/11/13 - 0.3.3: Bugfix [#16](https://github.com/Luismahou/grunt-hashres/pull/16): Renaming files with special characters. Thanks to [crodas](https://github.com/crodas).
* 14/05/13 - 0.3.2: Bugfix [#8](https://github.com/Luismahou/grunt-hashres/pull/8): Replace all ocurrences. Thanks to [kleinsch](https://github.com/kleinsch).
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,7 +1,7 @@
{
"name": "grunt-hashres",
"description": "Hashes your js and css files and rename the <script> and <link> declarations that refer to them in your html/php/etc files.",
"version": "0.3.4",
"version": "0.4.0",
"homepage": "https://github.com/luismahou/grunt-hashres",
"author": {
"name": "luismahou"
Expand Down
43 changes: 20 additions & 23 deletions tasks/hashresHelper.js
Expand Up @@ -12,22 +12,6 @@ var fs = require('fs'),
path = require('path'),
utils = require('./hashresUtils');

function preg_quote (str, delimiter) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// + improved by: Brett Zamir (http://brett-zamir.me)
// * example 1: preg_quote("$40");
// * returns 1: '\$40'
// * example 2: preg_quote("*RRRING* Hello?");
// * returns 2: '\*RRRING\* Hello\?'
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
}

exports.hashAndSub = function(grunt, options) {

var src = options.src,
Expand All @@ -36,28 +20,38 @@ exports.hashAndSub = function(grunt, options) {
fileNameFormat = options.fileNameFormat,
renameFiles = options.renameFiles,
nameToHashedName = {},
formatter = null;
nameToNameSearch = {},
formatter = null,
searchFormatter = null;

grunt.log.debug('files: ' + options.files);
grunt.log.debug('Using encoding ' + encoding);
grunt.log.debug('Using fileNameFormat ' + fileNameFormat);
grunt.log.debug(renameFiles ? 'Renaming files' : 'Not renaming files');

formatter = utils.compileFormat(fileNameFormat);
searchFormatter = utils.compileSearchFormat(fileNameFormat);

if (options.files) {
options.files.forEach(function(f) {
f.src.forEach(function(src) {
var md5 = utils.md5(src).slice(0, 8),
fileName = path.basename(src),
lastIndex = fileName.lastIndexOf('.'),
renamed = formatter({
var md5 = utils.md5(src).slice(0, 8),
fileName = path.basename(src),
lastIndex = fileName.lastIndexOf('.'),
renamed = formatter({
hash: md5,
name: fileName.slice(0, lastIndex),
ext : fileName.slice(lastIndex + 1, fileName.length) });
ext : fileName.slice(lastIndex + 1, fileName.length)
}),
nameSearch = searchFormatter({
hash: /[0-9a-f]{8}/,
name: fileName.slice(0, lastIndex),
ext: fileName.slice(lastIndex + 1, fileName.length)
});

// Mapping the original name with hashed one for later use.
nameToHashedName[fileName] = renamed;
nameToNameSearch[fileName] = nameSearch;

// Renaming the file
if (renameFiles) {
Expand All @@ -71,7 +65,10 @@ exports.hashAndSub = function(grunt, options) {
var destContents = fs.readFileSync(f, encoding);
for (var name in nameToHashedName) {
grunt.log.debug('Substituting ' + name + ' by ' + nameToHashedName[name]);
destContents = destContents.replace(new RegExp(preg_quote(name)+"(\\?[0-9a-z]+)?", "g"), nameToHashedName[name]);
destContents = destContents.replace(new RegExp(utils.preg_quote(name)+"(\\?[0-9a-z]+)?", "g"), nameToHashedName[name]);

grunt.log.debug('Substituting ' + nameToNameSearch[name] + ' by ' + nameToHashedName[name]);
destContents = destContents.replace(new RegExp(nameToNameSearch[name], "g"), nameToHashedName[name]);
}
grunt.log.debug('Saving the updated contents of the outination file');
fs.writeFileSync(f, destContents, encoding);
Expand Down
34 changes: 34 additions & 0 deletions tasks/hashresUtils.js
Expand Up @@ -11,6 +11,28 @@
var crypto = require('crypto'),
fs = require('fs');

function preg_quote (str, delimiter) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// + improved by: Brett Zamir (http://brett-zamir.me)
// * example 1: preg_quote("$40");
// * returns 1: '\$40'
// * example 2: preg_quote("*RRRING* Hello?");
// * returns 2: '\*RRRING\* Hello\?'
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
}

exports.preg_quote = preg_quote;

function escapeNonRegex(input) {
return (input instanceof RegExp) ? input.source : preg_quote(input);
}

// Generates a function for the given format
// Valid format variables: ${hash}, ${name} and ${ext}
exports.compileFormat = function(format) {
Expand All @@ -22,6 +44,18 @@ exports.compileFormat = function(format) {
};
};

// Generates a function for the given format as a regex string
// Valid format variables: ${hash}, ${name} and ${ext}
exports.compileSearchFormat = function(format) {
format = preg_quote(format);
return function(options) {
var output = format.split('\\$\\{hash\\}').join(escapeNonRegex(options.hash));
output = output.split('\\$\\{name\\}').join(escapeNonRegex(options.name));
output = output.split('\\$\\{ext\\}').join(escapeNonRegex(options.ext));
return output;
};
};

// Generates the md5 for the given file
exports.md5 = function(filepath) {
var hash = crypto.createHash('md5');
Expand Down
28 changes: 28 additions & 0 deletions test/fixtures/overriding/Gruntfile.js
@@ -0,0 +1,28 @@
/*
* grunt-hashres
* https://github.com/luismahou/grunt-hashres
*
* Copyright (c) 2013 luismahou
* Licensed under the MIT license.
*/

'use strict';

module.exports = function(grunt) {

grunt.initConfig({
hashres: {
firstVersion: {
src : ['js-v1/*.js'],
dest: ['*.html']
},
secondVersion: {
src : ['js-v2/*.js'],
dest: ['*.html']
}
}
});

grunt.loadTasks('../../../tasks');

};
5 changes: 5 additions & 0 deletions test/fixtures/overriding/index.html
@@ -0,0 +1,5 @@
<html>
<head>
<script type="text/javascript" src="/js/script.js"></script>
</head>
</html>
3 changes: 3 additions & 0 deletions test/fixtures/overriding/js-v1/script.js
@@ -0,0 +1,3 @@
(function() {
return 'v1';
});
3 changes: 3 additions & 0 deletions test/fixtures/overriding/js-v2/script.js
@@ -0,0 +1,3 @@
(function() {
return 'v2';
});
17 changes: 17 additions & 0 deletions test/hashres.spec.js
Expand Up @@ -40,6 +40,7 @@ var runCommand = function(command, options, callback) {
var pathWithCustomOptions = 'temp/hashres/options/with-custom-options';
var pathWithDefaultOptions = 'temp/hashres/options/with-default-options';
var pathWithSpecialCharacters = 'temp/hashres/options/with-special-characters';
var pathOverridingHashedFiles = 'temp/hashres/overriding';

vows.describe('hashres').addBatch({
'with custom options': {
Expand Down Expand Up @@ -95,5 +96,21 @@ vows.describe('hashres').addBatch({
assert(html.indexOf('scripts/+3$3.js?5a7a5b61') !== -1);
assert(html.indexOf('styles/+1.css?3b97b071') !== -1);
}
},
'overriding hashed files': {
topic: function() {
runCommand(
'../../../node_modules/grunt-cli/bin/./grunt hashres:firstVersion hashres:secondVersion',
{ cwd: pathOverridingHashedFiles },
this.callback);
},
'hashes resources overriding references in second hashres execution': function() {
// Both files have been renamed
assert(grunt.file.exists(pathOverridingHashedFiles + '/js-v1/688d441c.script.cache.js'));
assert(grunt.file.exists(pathOverridingHashedFiles + '/js-v2/cab5c571.script.cache.js'));
// index.html has been updated with the second version
var html = grunt.file.read(pathOverridingHashedFiles + '/index.html');
assert(html.indexOf('js/cab5c571.script.cache.js') !== -1);
}
}
}).export(module);

0 comments on commit 87ddf3d

Please sign in to comment.