├── .gitignore ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webpack-unused 2 | 3 | Check your frontend code for files/assets that are no longer used. 4 | 5 | Uses the output of `webpack --json` to see which files are actually used in your bundle, 6 | and lists files which haven't been required. 7 | 8 | ## Usage: 9 | 10 | ```bash 11 | # install webpack-unused 12 | npm install -g webpack-unused 13 | 14 | # run webpack using your normal webpack config etc 15 | # with the --json switch to output the stats.json, and pipe to webpack-unused 16 | # unused files in the cwd will be listed 17 | webpack --json | webpack-unused 18 | 19 | # if your source code is in a directory, like src/ pass that as a flag: 20 | webpack --json | webpack-unused -s src 21 | ``` 22 | 23 | ## Notes/Caveats: 24 | 25 | * this doesn't check for any unused npm modules etc that you have installed (`node_modules` is ignored) 26 | * webpack-unused will detect non-js files that are required via loaders etc, however any requires that happen outside of webpack's knowledge may be incorrectly reported as unused, for example: 27 | * css-preprocessor imports, for example `less`'s `@import` happens outside the webpack flow, so files which are only required via `@import` will report as unused, even if they are 28 | * it looks like currently files that would appear in your output from using webpack's `NormalModuleReplacementPlugin` don't appear in webpack's `--json` output, and so will be incorrectly reported as unused, while the original may be incorrectly reported as used. [issue #1](https://github.com/latentflip/webpack-unused/issues/1) 29 | * ideally, you'll have all your frontend code in a `src/` directory or similar so that you can use the `-s` flag, if not, any non-frontend code in cwd will be reported as unused 30 | 31 | ## Related 32 | 33 | - [jspm-unused](https://github.com/oligot/jspm-unused) - Find unused files in a Jspm project 34 | 35 | ## Contributing, etc 36 | 37 | This is just a first stab, and I'm publishing it because I constantly look for/rewrite code to achieve this. PRs/Suggestions for improvements very welcome. 38 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | 6 | const Path = require('path'); 7 | const Promisify = require('es6-promisify'); 8 | const Glob2 = Promisify(require('glob')); 9 | 10 | // webpack root? ideally start from package.json location I think 11 | const cwd = process.cwd(); 12 | 13 | const argv = require('yargs') 14 | .usage('Usage: webpack --stats | $0 [options]') 15 | .example('webpack --stats | $0 -s src', 'Check for unused files in the `src/` directory') 16 | .alias('s', 'src') 17 | .describe('s', 'Directory of source code') 18 | .default('s', '.') 19 | .help('h') 20 | .alias('h', 'help') 21 | .argv; 22 | 23 | // specify which directory to look in for source files 24 | const srcDir = Path.resolve(argv.src); 25 | 26 | const isWebpackLocal = (path) => { 27 | return (path.indexOf('./') === 0 && path.indexOf('./~/') === -1); 28 | }; 29 | 30 | const selectLocalModules = (webpack) => { 31 | return webpack.modules.filter((module) => isWebpackLocal(module.name)) 32 | .map((module) => Path.join(cwd, module.name)); 33 | }; 34 | 35 | const findAllLocalFiles = (cwd) => { 36 | return Glob2('!(node_modules)/**/*.*', { cwd: srcDir }) 37 | .then((files) => files.map((f) => Path.join(srcDir, f))); 38 | }; 39 | 40 | const parseStdin = () => { 41 | return new Promise((resolve, reject) => { 42 | let data = ''; 43 | 44 | process.stdin.setEncoding('utf8'); 45 | 46 | process.stdin.on('readable', () => { 47 | const chunk = process.stdin.read(); 48 | 49 | if (chunk === null && data === '') { 50 | console.error('The output of webpack --json must be piped to webpack-unused'); 51 | process.exit(1); 52 | } 53 | 54 | if (chunk !== null) { 55 | data += chunk.toString(); 56 | } 57 | }); 58 | 59 | process.stdin.on('end', () => { 60 | try { 61 | resolve(JSON.parse(data)); 62 | } catch (e) { 63 | console.error('Warning: output does not parse as json'); 64 | console.error('Attempting to trim to json'); 65 | const from = data.indexOf('{'); 66 | const to = data.lastIndexOf('}'); 67 | 68 | try { 69 | if (from === -1 || to === -1) { 70 | throw new Error('NOT_JSON'); 71 | } 72 | resolve(JSON.parse(data.slice(from, to + 1))); 73 | } catch (e) { 74 | console.error('Output does not appear to be json at all'); 75 | process.exit(1); 76 | } 77 | } 78 | }); 79 | }); 80 | }; 81 | 82 | Promise.all([ 83 | parseStdin().then(selectLocalModules), 84 | findAllLocalFiles(cwd) 85 | ]).then((args) => { 86 | const webpackFiles = args[0]; 87 | const localFiles = args[1]; 88 | 89 | const unused = localFiles.filter((file) => webpackFiles.indexOf(file) === -1) 90 | .map((file) => `./${Path.relative(cwd, file)}`); 91 | console.log(unused.join('\n')); 92 | }); 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-unused", 3 | "description": "Check your frontend code for files/assets that are no longer used.", 4 | "version": "0.1.0", 5 | "author": "Philip Roberts", 6 | "bin": { 7 | "webpack-unused": "index.js" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/latentflip/webpack-unused/issues" 11 | }, 12 | "dependencies": { 13 | "es6-promisify": "^3.0.0", 14 | "glob": "^6.0.4", 15 | "yargs": "^5.0.0" 16 | }, 17 | "devDependencies": {}, 18 | "homepage": "https://github.com/latentflip/webpack-unused#readme", 19 | "license": "MIT", 20 | "main": "index.js", 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/latentflip/webpack-unused.git" 24 | } 25 | } 26 | --------------------------------------------------------------------------------