├── .gitignore ├── .travis.yml ├── Gruntfile.js ├── README.md ├── package.json └── tasks └── unused.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ~* 3 | *.bak 4 | *.tmp 5 | .sass-cache 6 | *.log 7 | node_modules/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '0.12' 5 | - '0.10' 6 | before_script: 7 | - npm install grunt-cli -g -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | unused: { 6 | options: { 7 | reference: 'img/', 8 | directory: ['**/*.handlebars', '**/*.html'], 9 | days: 30, 10 | remove: false // set to true to delete unused files from project 11 | }, 12 | }, 13 | jshint: { 14 | all: ['**/*.js'], 15 | options: { 16 | reporter: require('jshint-stylish'), 17 | curly: true, 18 | eqeqeq: true, 19 | eqnull: false, 20 | browser: true, 21 | indent: 2, 22 | quotmark: 'single', 23 | unused: false, 24 | ignores: ['node_modules/**/*.js'], 25 | }, 26 | }, 27 | jsonlint: { 28 | sample: { 29 | src: [ 'package.json' ] 30 | } 31 | }, 32 | watch: { 33 | scripts: { 34 | files: ['**/*.js'], 35 | tasks: ['jshint'], 36 | options: { 37 | livereload: true 38 | } 39 | }, 40 | json: { 41 | files: ['**/*.json'], 42 | tasks: ['newer:jsonlint'], 43 | options: { 44 | spawn: false 45 | } 46 | } 47 | } 48 | }); 49 | grunt.loadNpmTasks('grunt-contrib-watch'); 50 | grunt.loadNpmTasks('grunt-contrib-jshint'); 51 | grunt.loadNpmTasks('grunt-jsonlint'); 52 | grunt.loadNpmTasks('grunt-unused'); 53 | grunt.registerTask('test',['jshint', 'jsonlint']); 54 | grunt.registerTask('default',['unused']); 55 | }; 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Grunt Unused 2 | ============ 3 | 4 | [![npm version](https://badge.fury.io/js/grunt-unused.svg)](http://badge.fury.io/js/grunt-unused) [![Build Status](https://travis-ci.org/ryanburgess/grunt-unused.svg?branch=master)](https://travis-ci.org/ryanburgess/grunt-unused) 5 | 6 | A Grunt task to check for unused files (jpg, png, css, js etc) in a project files and output them to the console. 7 | 8 | 9 | ## Getting Started 10 | 11 | If you haven't used [grunt][] before, be sure to check out the [Getting Started][] guide, as it explains how to create a [gruntfile][Getting Started] as well as install and use grunt plugins. Once you're familiar with that process, install this plugin with this command: 12 | 13 | ```sh 14 | npm install grunt-unused --save-dev 15 | ``` 16 | 17 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 18 | 19 | ```js 20 | grunt.loadNpmTasks('grunt-unused'); 21 | ``` 22 | 23 | *Tip: the [load-grunt-tasks](https://github.com/sindresorhus/load-grunt-tasks) module makes it easier to load multiple grunt tasks.* 24 | 25 | [grunt]: http://gruntjs.com 26 | [Getting Started]: https://github.com/gruntjs/grunt/wiki/Getting-started 27 | 28 | 29 | ## Documentation 30 | 31 | See the [Gruntfile](Gruntfile.js) in this repo for a full example. 32 | 33 | 34 | ### Example config 35 | 36 | ```js 37 | grunt.initConfig({ 38 | unused: { 39 | options: { 40 | reference: 'img/', 41 | directory: ['**/*.handlebars', '**/*.html'], 42 | days: 30, 43 | remove: false, // set to true to delete unused files from project 44 | reportOutput:'report.txt', // set to false to disable file output 45 | fail: false // set to true to make the task fail when unused files are found 46 | } 47 | } 48 | }); 49 | 50 | grunt.loadNpmTasks('grunt-unused'); 51 | grunt.registerTask('default', ['unused']); 52 | ``` 53 | 54 | ### Options 55 | 56 | #### reference 57 | Type: `String` 58 | Default value: `img/` 59 | 60 | A reference to the directory of files that are being checked if they are referenced in other project files. 61 | 62 | #### directory 63 | Type: `String|Array` 64 | Default value: `['**/*.html']` 65 | 66 | An array of directories that contain files that reference files in the reference directory. 67 | 68 | #### remove 69 | Type: `Boolean` 70 | Default value: `false` 71 | 72 | The ability to automatically delete unused file reference from project. 73 | 74 | #### days 75 | Type: `Number` 76 | Default value: `false` 77 | 78 | If remove is set to true and days has a value files will only delete if the file hasn't been modified after the length of days. 79 | 80 | #### reportOutput 81 | Type: `String` 82 | Default value: `false` 83 | 84 | Output unused files to a file. Set to false to disable 85 | 86 | ### fail 87 | Type: `Boolean` 88 | Default value: `false` 89 | 90 | Allows the Grunt task to fail when unused files are found. 91 | 92 | ## Release History 93 | * 0.2.3: Merge pull request [#8](https://github.com/ryanburgess/grunt-unused/pull/8) 94 | * 0.2.2: Merge pull request [#6](https://github.com/ryanburgess/grunt-unused/pull/6) and [#7](https://github.com/ryanburgess/grunt-unused/pull/7) 95 | * 0.2.1: Merge pull request [#5](https://github.com/ryanburgess/grunt-unused/pull/5) 96 | * 0.1.9: Add jshint and jsonlint tests. 97 | * 0.1.8: Add development dependencies. 98 | * 0.1.7: add release history notes to documentation. 99 | * 0.1.6: add the ability to only delete files after modified date. 100 | * 0.1.5 updates to documentation. 101 | * 0.1.4: add the option to automatically delete unused files from project. 102 | * 0.1.3: updates to documentation. 103 | * 0.1.2: clean up code. 104 | * 0.1.1: fix [#1](https://github.com/ryanburgess/grunt-unused/issues/1) by replace `String.prototype.search` with `String.prototype.in..` 105 | * 0.1.0: Initial release. 106 | 107 | ## Contributing 108 | 1. Fork it 109 | 2. Run `npm install` 110 | 3. Run Grunt watch `grunt watch` 111 | 4. Create your feature branch (`git checkout -b my-new-feature`) 112 | 5. Commit your changes (`git commit -am "Add some feature"`) 113 | 6. Push to the branch (`git push origin my-new-feature`) 114 | 7. Create new Pull Request 115 | 116 | 117 | ## License 118 | 119 | MIT © [Ryan Burgess](http://github.com/ryanburgess) 120 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-unused", 3 | "version": "0.2.3", 4 | "description": "Check for unused files in a project", 5 | "keywords": [ 6 | "gruntplugin", 7 | "unused", 8 | "files", 9 | "images", 10 | "css", 11 | "jpg", 12 | "png", 13 | "js", 14 | "javascript", 15 | "delete", 16 | "remove", 17 | "organize", 18 | "optimize", 19 | "clean" 20 | ], 21 | "homepage": "https://github.com/ryanburgess/grunt-unused", 22 | "bugs": { 23 | "url": "https://github.com/ryanburgess/grunt-unused/issues" 24 | }, 25 | "author": { 26 | "name": "Ryan Burgess", 27 | "url": "http://ryanburgess.com" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/ryanburgess/grunt-unused.git" 32 | }, 33 | "scripts": { 34 | "test": "grunt test" 35 | }, 36 | "dependencies": { 37 | "lodash": "^4.2.1", 38 | "moment": "^2.8.4" 39 | }, 40 | "devDependencies": { 41 | "grunt": "^0.4.0", 42 | "grunt-contrib-jshint": "^0.10.0", 43 | "grunt-contrib-watch": "^0.6.1", 44 | "grunt-jsonlint": "^1.0.4", 45 | "grunt-newer": "^1.1.0", 46 | "jshint-stylish": "^1.0.0" 47 | }, 48 | "peerDependencies": { 49 | "grunt": ">=0.4.0" 50 | }, 51 | "engines": { 52 | "node": ">=0.10.0" 53 | }, 54 | "licenses": { 55 | "type": "MIT" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tasks/unused.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-unused 3 | * https://github.com/ryanburgess/grunt-unused 4 | * 5 | * Copyright (c) 2014 Ryan Burgess 6 | * Licensed under the MIT license. 7 | */ 8 | var path = require('path'); 9 | var _ = require('lodash'); 10 | module.exports = function (grunt) { 11 | 'use strict'; 12 | grunt.registerTask('unused', function(){ 13 | var fs = require('fs'), 14 | moment = require('moment'), 15 | options, 16 | reference, 17 | directory, 18 | unused, 19 | content, 20 | datemod, 21 | todayDate, 22 | startDate, 23 | endDate, 24 | dayDiff, 25 | assets = [], 26 | links = []; 27 | 28 | options = this.options({ 29 | reference: 'img/', 30 | directory: ['**/*.html'], 31 | remove: false, 32 | days: null, 33 | reportOutput: false, 34 | fail: false, 35 | exclude: [] 36 | }); 37 | 38 | //get current date and time 39 | function getDateTime() { 40 | 41 | var date = new Date(), 42 | hour, 43 | min, 44 | sec, 45 | year, 46 | month, 47 | day; 48 | 49 | //get hours 50 | hour = date.getHours(); 51 | hour = (hour < 10 ? '0' : '') + hour; 52 | 53 | //get minutes 54 | min = date.getMinutes(); 55 | min = (min < 10 ? '0' : '') + min; 56 | 57 | //get seconds 58 | sec = date.getSeconds(); 59 | sec = (sec < 10 ? '0' : '') + sec; 60 | 61 | //get year 62 | year = date.getFullYear(); 63 | 64 | //get month 65 | month = date.getMonth() + 1; 66 | month = (month < 10 ? '0' : '') + month; 67 | 68 | //get day 69 | day = date.getDate(); 70 | day = (day < 10 ? '0' : '') + day; 71 | 72 | return year + '-' + month + '-' + day; 73 | 74 | } 75 | 76 | todayDate = getDateTime(); 77 | 78 | 79 | function deleteFile(fileRef){ 80 | fs.unlinkSync(fileRef); 81 | console.log('deleted '+ fileRef); 82 | } 83 | 84 | function logFiles(fileRef){ 85 | console.log(fileRef); 86 | } 87 | 88 | // Get list of files depending on the file directory 89 | grunt.file.expand({ 90 | filter: 'isFile', 91 | cwd: options.reference // Change this reference to your directory 92 | }, 93 | ['**/*']).forEach(function(file){ 94 | assets.push(file); 95 | }); 96 | 97 | // Find the references in content file 98 | grunt.file.expand({ 99 | filter: 'isFile', 100 | }, options.directory).forEach(function(file){ // Change this to narrow down the search 101 | content = grunt.file.read(file); 102 | assets.forEach(function(asset){ 103 | if(content.indexOf(asset) !== -1){ 104 | links.push(asset); 105 | } 106 | }); 107 | }); 108 | 109 | // Output unused files list in console 110 | unused = _.difference(assets, links); 111 | 112 | options.exclude.map((ex) => { 113 | unused = _.filter(unused, (_unused) => !_unused.includes(ex)); 114 | }); 115 | 116 | 117 | // output number of unused files 118 | if (unused.length) { 119 | grunt.log.warn(unused.length + ' unused file' + (unused.length === 1 ? '' : 's') + ':'); 120 | } 121 | else { 122 | grunt.log.ok('No unused files found.'); 123 | } 124 | 125 | unused.forEach(function(file){ 126 | 127 | // delete file if remove is set to true 128 | if(options.remove === true && options.days !== null){ 129 | datemod = fs.statSync(options.reference + file).mtime.toISOString(); 130 | datemod = datemod.replace(/\T.+/, ''); 131 | startDate = moment(datemod, 'YYYY-M-DD'); 132 | endDate = moment(todayDate, 'YYYY-M-DD'); 133 | dayDiff = endDate.diff(startDate, 'days'); 134 | 135 | if(dayDiff >= options.days){ 136 | //delete file 137 | deleteFile(options.reference + file); 138 | }else{ 139 | // log file references 140 | logFiles(options.reference + file); 141 | } 142 | }else if(options.remove === true){ 143 | //delete file 144 | deleteFile(options.reference + file); 145 | }else{ 146 | // log file references 147 | logFiles(options.reference + file); 148 | } 149 | }); 150 | 151 | if (unused.length > 0 && options.reportOutput) { 152 | var destDir = path.dirname(options.reportOutput); 153 | if (!grunt.file.exists(destDir)) { 154 | grunt.file.mkdir(destDir); 155 | } 156 | grunt.file.write(options.reportOutput,unused.join('\r\n')); 157 | grunt.log.ok('Report "' + options.reportOutput + '" created.'); 158 | } 159 | 160 | if (unused.length && !options.remove && options.fail) { 161 | grunt.fail.warn('Unused files were found.'); 162 | } 163 | }); 164 | }; 165 | --------------------------------------------------------------------------------