├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE-MIT ├── README.md ├── demo ├── css │ └── hello.css ├── index.html └── js │ └── hello.js ├── package.json └── tasks └── cache.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | tmp 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "node": true 13 | } 14 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * asset-cache-control 3 | * https://github.com/jessiehan/asset-cache-control 4 | * 5 | * Copyright (c) 2013 hpp 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | 13 | // Project configuration. 14 | grunt.initConfig({ 15 | 16 | // Before generating any new files, remove any previously-created files. 17 | clean: { 18 | tests: ['tmp'], 19 | }, 20 | 21 | // Configuration to be run (and then tested). 22 | cache: { 23 | js: { 24 | options: { 25 | }, 26 | assetUrl:'demo/js/hello.js', 27 | files: { 28 | 'tmp': ['demo/index.html'], 29 | }, 30 | }, 31 | css: { 32 | options: { 33 | }, 34 | assetUrl:'demo/css/hello.css', 35 | files: { 36 | 'tmp': ['demo/index.html'], 37 | }, 38 | }, 39 | }, 40 | 41 | // Unit tests. 42 | nodeunit: { 43 | tests: ['test/*_test.js'], 44 | }, 45 | 46 | }); 47 | 48 | // Actually load this plugin's task(s). 49 | grunt.loadTasks('tasks'); 50 | 51 | // These plugins provide necessary tasks. 52 | grunt.loadNpmTasks('grunt-contrib-clean'); 53 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 54 | 55 | // Whenever the "test" task is run, first clean the "tmp" dir, then run this 56 | // plugin's task(s), then test the result. 57 | //grunt.registerTask('test', ['clean', 'cache', 'nodeunit']); 58 | 59 | // By default, lint and run all tests. 60 | grunt.registerTask('default', ['cache']); 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 hpp 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asset-cache-control 2 | 3 | control the cache of assets by appending md5 hash to asset url 4 | 5 | For example 6 | 7 | in index.html we have 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | 14 | after running the task 15 | 16 | ```html 17 | 18 | ``` 19 | ## Getting Started 20 | This plugin requires Grunt `~0.4.1` 21 | 22 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 23 | 24 | ```shell 25 | npm install asset-cache-control --save-dev 26 | ``` 27 | 28 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 29 | 30 | ```js 31 | grunt.loadNpmTasks('asset-cache-control'); 32 | ``` 33 | 34 | ## The "cache" task 35 | 36 | ### Overview 37 | In your project's Gruntfile, add a section named `cache` to the data object passed into `grunt.initConfig()`. 38 | 39 | ```js 40 | grunt.initConfig({ 41 | cache: { 42 | js: { 43 | options: { 44 | }, 45 | assetUrl:'demo/js/hello.js', 46 | files: { 47 | 'tmp': ['demo/index.html'], 48 | }, 49 | }, 50 | }, 51 | }); 52 | ``` 53 | 54 | 55 | 56 | ### Usage Examples 57 | 58 | #### Default Options 59 | In this example, we have index.html which contains hello.js and hello.css. 60 | In Gruntfile.js, write as below, then `grunt`, we can get the index.html which has assets url with md5. 61 | 62 | `assetUrl` is the css or js file path 63 | `files` is the file which contains the assets(usually is html file) 64 | 65 | Notice to write the correct path. 66 | 67 | ```js 68 | grunt.initConfig({ 69 | cache: { 70 | js: { 71 | options: { 72 | }, 73 | assetUrl:'demo/js/hello.js', 74 | files: { 75 | 'tmp': ['demo/index.html'], 76 | }, 77 | }, 78 | css: { 79 | options: { 80 | }, 81 | assetUrl:'demo/css/hello.css', 82 | files: { 83 | 'tmp': ['demo/index.html'], 84 | }, 85 | }, 86 | }, 87 | }); 88 | ``` 89 | 90 | 91 | ## Contributing 92 | 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](http://gruntjs.com/). 93 | 94 | ## Release History 95 | _(Nothing yet)_ 96 | -------------------------------------------------------------------------------- /demo/css/hello.css: -------------------------------------------------------------------------------- 1 | .hello{ 2 | color:#fff; 3 | } -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/js/hello.js: -------------------------------------------------------------------------------- 1 | var hello='hello world'; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asset-cache-control", 3 | "description": "control the cache of assets by appending md5 hash to asset url", 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/jessiehan/asset-cache-control", 6 | "author": { 7 | "name": "Jessie Han", 8 | "email": "hpp.zju@gmail.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/jessiehan/asset-cache-control.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/jessiehan/asset-cache-control/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "https://github.com/jessiehan/asset-cache-control/blob/master/LICENSE-MIT" 21 | } 22 | ], 23 | "main": "Gruntfile.js", 24 | "engines": { 25 | "node": ">= 0.8.0" 26 | }, 27 | "scripts": { 28 | "test": "grunt test" 29 | }, 30 | "devDependencies": { 31 | "grunt-contrib-clean": "~0.4.0", 32 | "grunt-contrib-nodeunit": "~0.2.0", 33 | "grunt": "~0.4.1" 34 | }, 35 | "peerDependencies": { 36 | "grunt": "~0.4.1" 37 | }, 38 | "keywords": [ 39 | "gruntplugin,cache control,asset,hash" 40 | ] 41 | } -------------------------------------------------------------------------------- /tasks/cache.js: -------------------------------------------------------------------------------- 1 | /* 2 | * asset-cache-control 3 | * https://github.com/jessiehan/asset-cache-control 4 | * 5 | * Copyright (c) 2013 hpp 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | var crypto = require('crypto'); 13 | 14 | 15 | function getNewAssetsUrl(assetName, md5) { 16 | md5=md5.substring(0,8); 17 | var newurl=''; 18 | //already has ts, just update md5 19 | if(assetName.indexOf('?t=')>=0){ 20 | newurl = assetName.substring(0,assetName.length-8) + md5; 21 | 22 | }else{ 23 | 24 | newurl = assetName + '?t=' + md5; 25 | } 26 | 27 | return newurl; 28 | 29 | 30 | 31 | } 32 | 33 | function replaceAssets(fileSrc, assetUrl) { 34 | if (grunt.file.exists(fileSrc)) { 35 | //read page file data 36 | var data = grunt.file.read(fileSrc); 37 | //read asset file data 38 | var assetData=grunt.file.read(assetUrl); 39 | //remove the directory 40 | assetUrl=assetUrl.substring(assetUrl.lastIndexOf('/'),assetUrl.length); 41 | 42 | //if the page file has the asset 43 | if (data.indexOf(assetUrl) >= 0) { 44 | 45 | var md5sum = crypto.createHash('md5'); 46 | md5sum.update(assetData, 'utf-8'); 47 | 48 | //get the full asset text, like "text/javascript" src="js/hello.js?t=cefe2283" 49 | var reg=new RegExp('".*'+assetUrl+'.*"','g'); 50 | var fullAssetUrl=reg.exec(data).toString(); 51 | //only leave hello.js?t=cefe2283 52 | var start=fullAssetUrl.indexOf(assetUrl); 53 | var assetName=fullAssetUrl.substring(start,fullAssetUrl.indexOf('"',start)); 54 | 55 | var newurl = getNewAssetsUrl(assetName, md5sum.digest('hex')); 56 | var newdata = data.replace(assetName, newurl); 57 | 58 | if (grunt.file.write(fileSrc, newdata)) { 59 | grunt.log.success(fileSrc + ' add ts successfully'); 60 | } else { 61 | grunt.log.error(fileSrc + ' add ts failed'); 62 | } 63 | 64 | 65 | 66 | 67 | 68 | } else { 69 | grunt.log.error('asset not found in file ' + fileSrc); 70 | 71 | 72 | } 73 | 74 | 75 | 76 | } 77 | 78 | 79 | 80 | 81 | } 82 | 83 | 84 | // Please see the Grunt documentation for more information regarding task 85 | // creation: http://gruntjs.com/creating-tasks 86 | 87 | grunt.registerMultiTask('cache', 'The best Grunt plugin ever.', function() { 88 | // Merge task-specific and/or target-specific options with these defaults. 89 | var options = this.options({ 90 | 91 | }); 92 | var assetUrl = this.data.assetUrl; 93 | 94 | // Iterate over all specified file groups. 95 | this.files.forEach(function(f) { 96 | // Concat specified files. 97 | 98 | var src = f.src.filter(function(filepath) { 99 | // Warn on and remove invalid source files (if nonull was set). 100 | if (!grunt.file.exists(filepath)) { 101 | grunt.log.warn('Source file "' + filepath + '" not found.'); 102 | return false; 103 | } else { 104 | grunt.log.success('Source file "' + filepath + '" found.'); 105 | replaceAssets(filepath, assetUrl); 106 | 107 | 108 | return true; 109 | } 110 | }); 111 | 112 | }); 113 | }); 114 | 115 | }; 116 | --------------------------------------------------------------------------------