├── .gitignore ├── .npmignore ├── .travis.yml ├── bin └── ranza.js ├── docs ├── changelog.md ├── help.md └── images │ └── status.png ├── index.js ├── package.json ├── readme.md ├── src ├── cli.js ├── core.js ├── core │ ├── adapter.js │ ├── asker.js │ ├── checker.js │ ├── colorizer.js │ ├── comparer.js │ ├── formater.js │ ├── manager.js │ ├── parser.js │ ├── reader.js │ ├── rooter.js │ ├── searcher.js │ └── watcher.js ├── lib │ ├── nodeApis.js │ └── utils.js └── ranza.js └── test ├── check.js ├── fixtures ├── gruntDependencies │ ├── Gruntfile.js │ └── package.json ├── iojs.simpleRequest.md ├── iojs.wrongRequest.md ├── node.simpleRequest.md ├── node.simpleRequestWithModuleDefinition.md ├── node.simpleRequestWithNodeApis.md ├── perfectDependencies │ ├── index.js │ └── package.json └── unusedDependency │ ├── index.js │ └── package.json ├── help.js ├── status.js └── version.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | script: 5 | - "npm test" -------------------------------------------------------------------------------- /bin/ranza.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function() { 3 | var ranza = require('../src/cli.js'); 4 | })(); 5 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Vr. 1.0.0 4 | 5 | - Stable version 6 | - Major rewrite 7 | - Debug option, ask before clean or install 8 | 9 | ## Vr. 0.2.5 10 | 11 | - Gruntfile Support 12 | - Better error handling 13 | 14 | ## Vr. 0.2.1 15 | 16 | - Added clean feature 17 | 18 | ## Vr. 0.2.0 19 | 20 | - Added watch feature 21 | - Improve install feature 22 | - Added Promises in most functions 23 | 24 | ## Vr. 0.1.7 (stable) 25 | 26 | - Npm Install feature 27 | - Better organization 28 | 29 | ## Vr. 0.1.5 (stable) 30 | 31 | - Most Stable Version 32 | - Status command stable 33 | - Colorful log/status 34 | - Major rewrite 35 | -------------------------------------------------------------------------------- /docs/help.md: -------------------------------------------------------------------------------- 1 | 2 | Usage: ranza 3 | 4 | Description: 5 | More info: https://github.com/raphamorim/ranza 6 | 7 | Commands: 8 | ranza status status about all dependencies from project 9 | ranza install install all requires from project 10 | ranza clean clean all unused dependencies 11 | 12 | Additionals: 13 | --save save/delete require in package.json as dependency 14 | --save-dev save/delete require in package.json as devDependency 15 | 16 | Options: 17 | -a, --all output saved commands 18 | -v, --version output version number 19 | -h, --help output usage information 20 | -d, --debug output status command in debug mode 21 | -------------------------------------------------------------------------------- /docs/images/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphamorim/ranza/98753c9b2f442c7add18e650cd5a7df31b71cdf7/docs/images/status.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/ranza.js'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ranza", 3 | "version": "1.1.0", 4 | "description": "The dependency checker", 5 | "main": "./index.js", 6 | "bin": "./bin/ranza.js", 7 | "scripts": { 8 | "test": "mocha --require assert --recursive --growl" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/raphamorim/ranza.git" 13 | }, 14 | "author": "Raphael Amorim ", 15 | "readmeFilename": "readme.md", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/raphamorim/ranza/issues" 19 | }, 20 | "homepage": "https://github.com/raphamorim/ranza", 21 | "preferGlobal": "true", 22 | "dependencies": { 23 | "babel-core": "^5.7.4", 24 | "bluebird": "^2.9.20", 25 | "char-spinner": "^1.0.1", 26 | "glob": "^5.0.3", 27 | "supports-color": "^1.3.1" 28 | }, 29 | "devDependencies": { 30 | "mocha": "^2.2.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Ranza 2 | 3 | > The dependency checker 4 | 5 | [![NPM Version](https://img.shields.io/npm/v/express.svg?style=flat)](https://www.npmjs.org/package/ranza) 6 | [![Build Status](https://travis-ci.org/raphamorim/ranza.svg)](https://travis-ci.org/raphamorim/ranza) 7 | 8 | Quickly spot any dependency required in the project and not listed in `package.json`. And also the other way around: quickly remove listed dependencies that are not being used. 9 | 10 | **Supports ES5 / ES6** 11 | 12 | # Why use ranza? 13 | 14 | Avoid accumulation of dependencies that are not being used. 15 | 16 | ## Getting Started 17 | 18 | With [node](http://nodejs.org/) and [npm](https://www.npmjs.org/) installed, install ranza with a single command. 19 | 20 | ##### As CLI 21 | 22 | ```sh 23 | $ npm install -g ranza 24 | ``` 25 | 26 | ##### As Node Module 27 | 28 | ```sh 29 | $ npm install ranza 30 | ``` 31 | 32 | ## CLI Usage 33 | 34 | #### Status 35 | 36 | Checks all project for required dependencies and confirms if they are listed on `package.json`: 37 | 38 | ```sh 39 | $ ranza status 40 | ``` 41 | 42 | You can use status with debug option as arguments, to best view requires status showing the occurrence files, ex: 43 | 44 | **input:** 45 | 46 | ```sh 47 | $ ranza status --debug 48 | ``` 49 | 50 | **some output example:** 51 | 52 | ```sh 53 | Defined and used: 54 | • babel-core 55 | => lib/new.js 56 | 57 | • bluebird 58 | => core/src/comparer.js 59 | => core/src/manager.js 60 | => core/src/sentinel.js 61 | 62 | Defined, but unused: 63 | • grunt 64 | • babel 65 | ``` 66 | 67 | #### Install 68 | 69 | Installs all dependencies required throughout the project, but do not save them in `package.json`: 70 | 71 | ```sh 72 | $ ranza install 73 | ``` 74 | 75 | Installs all dependencies required throughout the project and add them to `package.json` as `dependencies`: 76 | 77 | ```sh 78 | $ ranza install --save 79 | ``` 80 | 81 | Installs all dependencies required throughout the project and save them in `package.json` as `devDependencies`: 82 | 83 | ```sh 84 | $ ranza install --save-dev 85 | ``` 86 | 87 | #### Clean 88 | 89 | Remove and clean all unused dependencies from `package.json`: 90 | 91 | ```sh 92 | $ ranza clean 93 | ``` 94 | 95 | ## Node Module Usage 96 | 97 | ### Status 98 | 99 | You can check the dependencies status from current project using: 100 | 101 | ```javascript 102 | var ranza = require('ranza'); 103 | 104 | ranza.status(function(status) { 105 | /* 106 | status = { 107 | undefinedUsed: [], 108 | definedUnused: [ 'ejs'], 109 | definedUsed: [ 'express', 'kenobi' ] 110 | } 111 | */ 112 | 113 | console.log(status) 114 | }); 115 | ``` 116 | 117 | ### Check 118 | 119 | Return dependencies (require and import) from current file using: 120 | 121 | ```javascript 122 | var ranza = require('ranza'); 123 | 124 | ranza.check('file.js', function(dependencies, err) { 125 | console.log(dependencies) // ['kenobi', 'bluebird'] 126 | console.log(err) // null 127 | }); 128 | ``` 129 | 130 | 131 | ## History 132 | 133 | See [Changelog](docs/changelog.md) for more details. 134 | 135 | ## Contributing 136 | 137 | Don't be shy, send a Pull Request! Here is how: 138 | 139 | 1. Fork it! 140 | 2. Create your feature branch: `git checkout -b my-new-feature` 141 | 3. Commit your changes: `git commit -m 'Add some feature'` 142 | 4. Push to the branch: `git push origin my-new-feature` 143 | 5. Submit a pull request :D 144 | 145 | ## About 146 | 147 | **License:** MIT ® [Raphael Amorim](https://github.com/raphamorim) 148 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | var ranza = require('./ranza'), 2 | verify = require('./lib/utils').verify, 3 | commands = process.argv; 4 | 5 | ranza.enableLogs(true); 6 | 7 | if (verify(['-v', '--version'])) 8 | console.log(ranza.version()); 9 | 10 | else if (verify(['-h', '--help'])) 11 | console.log(ranza.help()); 12 | 13 | else if (typeof commands[2] != 'undefined') { 14 | if (commands.indexOf('status') > -1) { 15 | if (commands.indexOf('-d') > -1 || commands.indexOf('--debug') > -1) 16 | ranza.setDebug(); 17 | ranza.status(); 18 | } 19 | 20 | else if (commands.indexOf('clean') > -1) { 21 | ranza.clean(); 22 | } 23 | 24 | else if (commands.indexOf('install') > -1) { 25 | if (commands.indexOf('--save-dev') > -1) 26 | ranza.install('--save-dev'); 27 | 28 | else if (commands.indexOf('--save') > -1) 29 | ranza.install('--save'); 30 | 31 | else 32 | ranza.install(); 33 | } 34 | 35 | else { 36 | console.log(ranza.default()); 37 | } 38 | } 39 | 40 | else { 41 | console.log(ranza.default()); 42 | } 43 | -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | var Core = { 2 | 'asker': require('./core/asker'), 3 | 'checker': require('./core/checker'), 4 | 'colorizer': require('./core/colorizer'), 5 | 'compare': require('./core/comparer'), 6 | 'formater': require('./core/formater'), 7 | 'manager': require('./core/manager'), 8 | 'reader': require('./core/reader'), 9 | 'root': require('./core/rooter'), 10 | 'searcher': require('./core/searcher'), 11 | 'watcher': require('./core/watcher'), 12 | }; 13 | 14 | module.exports = Core; 15 | -------------------------------------------------------------------------------- /src/core/adapter.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | exports.gruntfileData = function(path) { 4 | return fs.readFileSync(path, 'utf8'); 5 | } -------------------------------------------------------------------------------- /src/core/asker.js: -------------------------------------------------------------------------------- 1 | var Promises = require('bluebird'), 2 | colorizer = require('./colorizer'); 3 | 4 | var asker = { 5 | options: { 6 | yes: ['yes','y'], 7 | no: ['no','n'] 8 | }, 9 | 10 | ask: function (question, defaultvalue, callback, yesvalues, novalues) { 11 | var self = this; 12 | 13 | if (!self.__invalid) self.resetInvalidHandler(); 14 | 15 | yesvalues = yesvalues ? yesvalues : self.options.yes; 16 | novalues = novalues ? novalues : self.options.no; 17 | 18 | yesvalues = yesvalues.map(function(v) { return v.toLowerCase(); }); 19 | novalues = novalues.map(function(v) { return v.toLowerCase(); }); 20 | 21 | process.stdout.write(question+" "); 22 | process.stdin.setEncoding('utf8'); 23 | process.stdin.once('data', function(val){ 24 | var result; 25 | var cleaned = val.trim().toLowerCase(); 26 | 27 | if (cleaned == "" && defaultvalue != null) { 28 | result = defaultvalue; 29 | } 30 | else if (yesvalues.indexOf(cleaned) >= 0) { 31 | result = true; 32 | } 33 | else if (novalues.indexOf(cleaned) >= 0) { 34 | result = false; 35 | } 36 | else { 37 | self.__invalid(question,defaultvalue,callback,yesvalues,novalues); 38 | return; 39 | } 40 | 41 | callback(result); 42 | }).resume(); 43 | }, 44 | 45 | onInvalidHandler: function(callback) { 46 | this.__invalid = callback; 47 | }, 48 | 49 | _invalidHandler: function(question, defaultvalue, callback, yesvalues, novalues) { 50 | process.stdout.write(colorizer('message', "\nInvalid Response.\n")); 51 | process.stdout.write("Answer either yes : ("+ yesvalues.join(', ')+')'); 52 | process.stdout.write(", or no: ("+ novalues.join(', ')+')\n'); 53 | this.ask(question,defaultvalue,callback,yesvalues,novalues); 54 | }, 55 | 56 | resetInvalidHandler: function() { 57 | this.onInvalidHandler(this._invalidHandler); 58 | } 59 | }; 60 | 61 | var Asker = asker.ask.bind(asker, 'Are you sure you want to continue? (y/n)', true); 62 | module.exports = Asker; 63 | -------------------------------------------------------------------------------- /src/core/checker.js: -------------------------------------------------------------------------------- 1 | var Promises = require('bluebird'), 2 | parser = require('./parser'), 3 | apiNode = require('../lib/nodeApis'); 4 | 5 | function Checker(data) { 6 | return new Promises(function(resolve, reject) { 7 | var dependencies = null; 8 | 9 | try { 10 | dependencies = parser(data); 11 | } catch(err) { 12 | return resolve([null, err]); 13 | } 14 | 15 | for (var i = 0; i < dependencies.length; i++) { 16 | if ((dependencies[i] || '').indexOf('./') !== -1) 17 | dependencies.splice(i, 1); 18 | if (apiNode.indexOf((dependencies[i] || '')) !== -1) 19 | dependencies.splice(i, 1); 20 | if ((dependencies[i] || '').indexOf('/') !== -1) 21 | dependencies[i] = dependencies[i].split('/')[0]; 22 | } 23 | 24 | return resolve([dependencies, null]); 25 | }); 26 | } 27 | 28 | module.exports = Checker; -------------------------------------------------------------------------------- /src/core/colorizer.js: -------------------------------------------------------------------------------- 1 | var suportColors = (process.env ? require('supports-color') : null), 2 | useColors = (suportColors && true); 3 | 4 | var colors = { 5 | 'success': 92, 'error': 91, 'message': 93 6 | }; 7 | 8 | function Colorizer(color, message) { 9 | if (!useColors) return String(str); 10 | return '\u001b[' + colors[color] + 'm' + message + '\u001b[0m'; 11 | } 12 | 13 | module.exports = Colorizer; -------------------------------------------------------------------------------- /src/core/comparer.js: -------------------------------------------------------------------------------- 1 | var Promises = require('bluebird'), 2 | colorizer = require('./colorizer'), 3 | adapter = require('./adapter'), 4 | path = require('path'); 5 | 6 | function diff (arr, diff){ 7 | return arr.filter(function(i) { 8 | return diff.indexOf(i) < 0 9 | }); 10 | } 11 | 12 | function debug(log, options, dependency) { 13 | if (!options.debug) return; 14 | var files = (options.where[dependency] || []).map(path.relative.bind(path, options.root)); 15 | log.push(' => ' + (files).join('\n => ') + '\n'); 16 | } 17 | 18 | function Compare (root, requires, options) { 19 | options = options || {}; 20 | options.debug = options.debug || false; 21 | options.root = root; 22 | return new Promises(function(resolve, reject) { 23 | var packageJson = require(root + '/package.json'); 24 | 25 | var log = [], 26 | successLog = [], 27 | unusedLog = [], 28 | undefinedLog = []; 29 | 30 | var dependencies = [], 31 | usedDependencies = [], 32 | normal = packageJson.dependencies || {}, 33 | dev = packageJson.devDependencies || {}; 34 | 35 | normal = (Object.keys(normal)); 36 | dev = (Object.keys(dev)); 37 | 38 | dependencies = normal.concat(dev); 39 | 40 | if (requires.length <= 0 && dependencies.length <= 0) { 41 | return reject('There is no dependencies or requires!'); 42 | } 43 | 44 | dependencies = dependencies.filter(function(item, pos) { 45 | return dependencies.indexOf(item) == pos; 46 | }); 47 | 48 | /* Dependency Comparator */ 49 | var gruntDependencies = dependencies.filter(function(item){ 50 | return (item.indexOf('grunt') >= 0) 51 | }); 52 | 53 | if (gruntDependencies.length > 0) { 54 | var gruntfilePath = root + '/Gruntfile.js', 55 | gruntfile; 56 | 57 | if (require('fs').existsSync(gruntfilePath)) { 58 | gruntfile = (adapter.gruntfileData(gruntfilePath) || ''); 59 | } 60 | 61 | gruntDependencies.forEach(function(gruntDependency){ 62 | if (gruntfile.indexOf(gruntDependency) < 0){ 63 | // requires.push(gruntDependency) 64 | unusedLog.push(colorizer('error', ' • ') + gruntDependency); 65 | } else { 66 | usedDependencies.push(gruntDependency); 67 | successLog.push(colorizer('success', ' • ') + gruntDependency); 68 | } 69 | }); 70 | } 71 | 72 | var unusedGruntDependencies = diff(gruntDependencies, usedDependencies) 73 | 74 | // Requires 75 | dependencies = dependencies.filter(function(item){ 76 | if (item.indexOf('grunt') === -1) return true }); 77 | 78 | dependencies.forEach(function(dependency) { 79 | if (requires.indexOf(dependency) < 0) { 80 | unusedLog.push(colorizer('error', ' • ') + dependency); 81 | } else { 82 | usedDependencies.push(dependency); 83 | successLog.push(colorizer('success', ' • ') + dependency); 84 | debug(successLog, options, dependency); 85 | } 86 | }); 87 | 88 | dependencies = dependencies.concat(unusedGruntDependencies); 89 | var unusedDependencies = diff(dependencies, requires), 90 | differences = diff(requires, dependencies); 91 | 92 | if (differences.length > 0) { 93 | differences.forEach(function(diff) { 94 | undefinedLog.push(colorizer('error', ' • ') + diff); 95 | debug(undefinedLog, options, diff); 96 | }); 97 | } 98 | 99 | if (successLog.length > 0) { 100 | log.push(colorizer('success','\n Defined and used:')); 101 | log.push(successLog.join('\n')); 102 | } 103 | 104 | if (unusedLog.length > 0) { 105 | log.push(colorizer('error','\n Defined, but unused:')); 106 | log.push(unusedLog.join('\n')); 107 | } 108 | 109 | if (undefinedLog.length > 0) { 110 | log.push(colorizer('error','\n Undefined, but used:')); 111 | log.push(undefinedLog.join('\n')); 112 | } 113 | 114 | resolve([differences, unusedDependencies, usedDependencies, log.join('\n')]); 115 | }); 116 | } 117 | 118 | module.exports = Compare; -------------------------------------------------------------------------------- /src/core/formater.js: -------------------------------------------------------------------------------- 1 | function Formater(arr) { 2 | var newArr = []; 3 | if (typeof arr === 'object') { 4 | arr.forEach(function(item, index) { 5 | if (/^[A-Za-z0-9\s\\._-]+$/.test(item)) 6 | newArr.push(item); 7 | }); 8 | 9 | return newArr.filter(function(item, pos) { 10 | return newArr.indexOf(item) == pos; 11 | }); 12 | } 13 | } 14 | 15 | module.exports = Formater; -------------------------------------------------------------------------------- /src/core/manager.js: -------------------------------------------------------------------------------- 1 | var Promises = require('bluebird'), 2 | shell = Promises.promisifyAll(require('child_process')), 3 | colorizer = require('./colorizer'); 4 | 5 | function generateCommand(path, dev, command) { 6 | var save = ''; 7 | if (dev) save = ' ' + dev; 8 | 9 | var sh = 'cd ' + path + ' && npm ' + command + save + ' '; 10 | return function(req) { 11 | return sh + req; 12 | } 13 | } 14 | 15 | function generateLog(command, print) { 16 | if (!print) return false; 17 | 18 | if (command === 'install') { 19 | console.log(colorizer('success', 'Ranza say: It\'s installing...\n')); 20 | } else if (command === 'remove') { 21 | console.log(colorizer('success', 'Ranza say: It\'s removing...\n')); 22 | } 23 | } 24 | 25 | function Execute(path, requires, command, sh) { 26 | return new Promises(function(resolve, reject) { 27 | return Promises.map(requires, function(require) { 28 | console.log(colorizer('message', command + ': ' + require)); 29 | return shell.execAsync(sh(require)).spread(function(stdout) { 30 | console.log(stdout); 31 | }).catch(function(err) { 32 | return console.log(colorizer('error', 'Ocurred a error: ' + err)); 33 | }) 34 | }, {concurrency: 1}).then(function(){ 35 | resolve(); 36 | }); 37 | }); 38 | } 39 | 40 | function Manager(command, path, requires, dev, print) { 41 | return new Promises(function(resolve, reject) { 42 | if (requires.length <= 0) 43 | return reject('There is no requires to install!'); 44 | 45 | if (typeof print === 'undefined') 46 | print = false; 47 | 48 | if (command === 'remove') 49 | dev = '--save'; 50 | 51 | generateLog(command, print); 52 | return Execute(path, requires, command, generateCommand(path, dev, command)) 53 | }); 54 | } 55 | 56 | module.exports = Manager; -------------------------------------------------------------------------------- /src/core/parser.js: -------------------------------------------------------------------------------- 1 | var BabelCore = require('babel-core'); 2 | 3 | function Parser(data) { 4 | var ast = BabelCore.parse(data); 5 | return getRequires([], ast.body); 6 | } 7 | 8 | function getRequires(requires, body) { 9 | for (var i = 0; i < body.length; i++) { 10 | if (!body[i] || !body[i].type) return; 11 | 12 | if (body[i].type === 'VariableDeclarator') { 13 | getRequires(requires, [body[i].init]) 14 | } 15 | if (body[i].type === 'VariableDeclaration') { 16 | getRequires(requires, body[i].declarations) 17 | } 18 | if (body[i].type === 'ConditionalExpression') { 19 | getRequires(requires, [body[i].test, body[i].consequent]) 20 | } 21 | if (body[i].type === 'AssignmentExpression') { 22 | getRequires(requires, [body[i].left, body[i].right]) 23 | } 24 | if (body[i].type === 'ExpressionStatement') { 25 | if (body[i].expression.type === 'SequenceExpression') 26 | getRequires(requires, body[i].expression.expressions) 27 | 28 | getRequires(requires, [body[i].expression.left, body[i].expression.right]) 29 | } 30 | if (body[i].type === 'FunctionDeclaration') { 31 | getRequires(requires, (body[i].body.body || [])) 32 | } 33 | if (body[i].type === 'ImportDeclaration') { 34 | requires.push(body[i].source.value); 35 | } 36 | if (body[i].type === 'CallExpression') { 37 | if (body[i].callee.type === 'MemberExpression') { 38 | getRequires(requires, [body[i].callee.object]) 39 | } 40 | 41 | if (body[i].callee.name === 'require') { 42 | requires.push(body[i].arguments[0].value) 43 | } 44 | } 45 | } 46 | return requires; 47 | } 48 | 49 | module.exports = Parser; -------------------------------------------------------------------------------- /src/core/reader.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function Reader (filename) { 4 | return fs.readFileSync(__dirname + '/../' + filename, 'utf8'); 5 | }; 6 | 7 | module.exports = Reader; -------------------------------------------------------------------------------- /src/core/rooter.js: -------------------------------------------------------------------------------- 1 | function Rooter(path) { 2 | if (path.indexOf('/') <= 0) return process.cwd(); 3 | 4 | var dir = path.split('/'); 5 | return process.cwd() + path.replace(dir[dir.length -1], ''); 6 | } 7 | 8 | module.exports = Rooter; 9 | -------------------------------------------------------------------------------- /src/core/searcher.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'), 3 | parser = require('./parser'), 4 | apiNode = require('../lib/nodeApis'); 5 | 6 | function Searcher(paths) { 7 | var requires = [], parserErrors = [], allPaths = [], requiresPath = {}; 8 | paths.forEach(function(path){ 9 | if (path.indexOf('node_modules/') !== -1) return; 10 | if (path.indexOf('bower_components/') !== -1) return; 11 | if (path.indexOf('.min.js') !== -1) return; 12 | var data = fs.readFileSync(path, 'utf8'); 13 | try { 14 | var dependencies = parser(data); 15 | } catch(err) { 16 | parserErrors.push({path: path, err: err}); 17 | return; 18 | } 19 | 20 | dependencies.forEach(function(dependency) { 21 | if (dependency.indexOf('./') !== -1) return; 22 | if (apiNode.indexOf(dependency) !== -1) return; 23 | if (dependency.indexOf('/') !== -1) 24 | dependency = dependency.split('/')[0]; 25 | 26 | requires.push(dependency); 27 | allPaths.push(path); 28 | requiresPath[dependency] = (requiresPath[dependency] || []); 29 | requiresPath[dependency].push(path); 30 | }); 31 | }); 32 | 33 | return { 34 | 'requires': requires, 35 | 'paths': { 36 | 'all': allPaths, 37 | 'require': requiresPath 38 | }, 39 | 'parser': { 40 | 'errors': parserErrors 41 | } 42 | }; 43 | } 44 | 45 | module.exports = Searcher; 46 | -------------------------------------------------------------------------------- /src/core/watcher.js: -------------------------------------------------------------------------------- 1 | var Promises = require('bluebird'), 2 | glob = Promises.promisify(require('glob')), 3 | rooter = require('./rooter'); 4 | 5 | function Watcher(path) { 6 | return new Promises(function(resolve, reject) { 7 | var root = rooter(path), 8 | globOptions = {}; 9 | 10 | glob(root + '/**/*.js', globOptions).then(function(files) { 11 | return resolve([files, root]); 12 | }); 13 | }); 14 | } 15 | 16 | module.exports = Watcher; -------------------------------------------------------------------------------- /src/lib/nodeApis.js: -------------------------------------------------------------------------------- 1 | var nodeApis = [ 2 | 'path', 'exec', 'child_process', 'fs', 'crypto', 'os', 3 | 'http', 'https', 'net', 'assert', 'cluster', 'domain' ]; 4 | 5 | module.exports = nodeApis; -------------------------------------------------------------------------------- /src/lib/utils.js: -------------------------------------------------------------------------------- 1 | var input = process.argv[2]; 2 | 3 | exports.verify = function(args) { 4 | if (typeof args === 'object') { 5 | if (args.indexOf(input) == -1) 6 | return false; 7 | 8 | return true; 9 | } else { 10 | if (args != input) 11 | return false; 12 | 13 | return true; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/ranza.js: -------------------------------------------------------------------------------- 1 | var pJson = require('../package.json'), 2 | core = require('./core'), 3 | spinner = require('char-spinner'), 4 | installing = false; 5 | 6 | function throwError(err) { 7 | throw Error(err); 8 | console.log(core.colorizer('error', 'Ranza: ' + err )); 9 | } 10 | 11 | var Ranza = new Function(); 12 | 13 | Ranza.prototype.enableLogs = function(mode) { 14 | this.logs = (typeof(mode) === 'boolean' ? mode : false); 15 | }; 16 | 17 | Ranza.prototype.setDebug = function(debug, done, fn) { 18 | this.debug = (typeof(debug) === 'undefined' ? true : debug); 19 | }; 20 | 21 | Ranza.prototype.default = function() { 22 | return ('Hey young padawan, please type a valid command.\n' + 23 | '>> To see all commands, use: ranza [ -h, --help ]'); 24 | }; 25 | 26 | Ranza.prototype.version = function() { 27 | return ('Ranza version: ' + pJson.version); 28 | }; 29 | 30 | Ranza.prototype.help = function() { 31 | return core.reader('/../docs/help.md'); 32 | }; 33 | 34 | Ranza.prototype.status = function(fn) { 35 | var self = this; 36 | if (self.logs) self.spinner = spinner(); 37 | return core.watcher('/').spread(function(files, root){ 38 | var searcher = core.searcher(files), 39 | requires = core.formater(searcher['requires']); 40 | var debugOptions = {debug: self.debug, where: searcher.paths.require}; 41 | return core.compare(root, requires, debugOptions) 42 | .spread(function(diffs, unused, reqs, log) { 43 | if (self.logs) { 44 | if (searcher.parser.errors.length) 45 | console.log('Parser Errors: ', searcher.parser.errors); 46 | console.log(log + '\n'); 47 | clearInterval(self.spinner); 48 | } 49 | var status = { 50 | undefinedUsed: diffs, 51 | definedUnused: unused, 52 | definedUsed: reqs 53 | }; 54 | 55 | return ((typeof(fn) === 'function')? fn(status) : status); 56 | }); 57 | }).catch(throwError); 58 | }; 59 | 60 | Ranza.prototype.install = function(install) { 61 | var save = install || false; 62 | 63 | core.asker(function(yes) { 64 | if (!yes) process.exit(); 65 | return core.watcher('/').spread(function(files, root){ 66 | var searcher = core.searcher(files), 67 | requires = core.formater(searcher['requires']); 68 | 69 | return core.manager('install', root, requires, save, true); 70 | }).catch(throwError); 71 | }); 72 | }; 73 | 74 | Ranza.prototype.clean = function(removeType) { 75 | var save = removeType || false; 76 | core.asker(function(yes) { 77 | if (!yes) process.exit(); 78 | return core.watcher('/').spread(function(files, root){ 79 | var searcher = core.searcher(files), 80 | requires = core.formater(searcher['requires']); 81 | 82 | return core.compare(root, requires).spread(function(diffs, unused) { 83 | if (unused.length <= 0) 84 | return console.log(core.colorizer('message', '\nRanza says: There is no unused dependencies!\n')); 85 | 86 | return core.manager('remove', root, unused, save, true); 87 | }); 88 | }).catch(throwError); 89 | }); 90 | }; 91 | 92 | Ranza.prototype.check = function(fileSrc, fn) { 93 | var fileData = require('fs').readFileSync(fileSrc, 'utf8'); 94 | return core.checker(fileData).spread(function(dependencies, err) { 95 | return fn(dependencies, err); 96 | }); 97 | } 98 | 99 | module.exports = new Ranza(); 100 | -------------------------------------------------------------------------------- /test/check.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | Promises = require('bluebird'), 3 | Ranza = Promises.promisifyAll(require('../index.js')); 4 | 5 | describe('Check', function() { 6 | context('require [node native]', function() { 7 | context('simple require without node apis', function() { 8 | it('should return dependency list and err equals null', function(done) { 9 | var fileName = __dirname + '/fixtures/node.simpleRequest.md'; 10 | Ranza.check(fileName, function(dependencies, err) { 11 | assert.deepEqual(typeof dependencies, 'object'); 12 | assert.deepEqual(err, null); 13 | 14 | assert.deepEqual(dependencies, ['kenobi', 'bluebird', 'ranza']); 15 | done(); 16 | }); 17 | }) 18 | }) 19 | context('simple require with node apis', function() { 20 | it('should return dependency list without node apis and err equals null', function(done) { 21 | var fileName = __dirname + '/fixtures/node.simpleRequestWithNodeApis.md'; 22 | Ranza.check(fileName, function(dependencies, err) { 23 | assert.deepEqual(typeof dependencies, 'object'); 24 | assert.deepEqual(err, null); 25 | 26 | assert.deepEqual(dependencies, ['kenobi', 'bluebird', 'ranza']); 27 | done(); 28 | }); 29 | }) 30 | }) 31 | context('simple require with module definition without node apis', function() { 32 | it('should return dependency list and err equals null', function(done) { 33 | var fileName = __dirname + '/fixtures/node.simpleRequestWithModuleDefinition.md'; 34 | Ranza.check(fileName, function(dependencies, err) { 35 | assert.deepEqual(typeof dependencies, 'object'); 36 | assert.deepEqual(err, null); 37 | 38 | assert.deepEqual(dependencies, ['mandrill-api', 'ranza']); 39 | done(); 40 | }); 41 | }) 42 | }) 43 | }) 44 | context('import [iojs ES6]', function() { 45 | context('simple import without node apis', function() { 46 | it('should return dependency list and err equals null', function(done) { 47 | var fileName = __dirname + '/fixtures/iojs.simpleRequest.md'; 48 | Ranza.check(fileName, function(dependencies, err) { 49 | assert.deepEqual(typeof dependencies, 'object'); 50 | assert.deepEqual(err, null); 51 | 52 | assert.deepEqual(dependencies, ['kenobi', 'ranza', 'bluebird']); 53 | done(); 54 | }); 55 | }) 56 | }) 57 | context('wrong import without node apis', function() { 58 | it('should return dependency list and err equals null', function(done) { 59 | var fileName = __dirname + '/fixtures/iojs.wrongRequest.md'; 60 | Ranza.check(fileName, function(dependencies, err) { 61 | assert.deepEqual(dependencies, null); 62 | assert.deepEqual(typeof err, 'object'); 63 | done(); 64 | }); 65 | }) 66 | }) 67 | }) 68 | }); 69 | -------------------------------------------------------------------------------- /test/fixtures/gruntDependencies/Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | grunt.initConfig({ 5 | uglify: { 6 | main: { 7 | files: { 8 | "assets/js/app.js": [ 9 | "assets/js/main.js", 10 | "assets/js/analytics.js" 11 | ] 12 | } 13 | } 14 | }, 15 | 16 | cssmin: { 17 | target: { 18 | files: [{ 19 | expand: true, 20 | cwd: 'assets/css', 21 | src: ['*.css', '!*.min.css'], 22 | dest: 'assets/css', 23 | ext: '.min.css' 24 | }] 25 | } 26 | } 27 | }); 28 | 29 | [ 30 | 'grunt-contrib-uglify', 31 | 'grunt-contrib-cssmin' 32 | ].forEach( function( task ) { 33 | grunt.loadNpmTasks( task ); 34 | }); 35 | 36 | grunt.registerTask('default', ['uglify', 'cssmin']); 37 | }; 38 | -------------------------------------------------------------------------------- /test/fixtures/gruntDependencies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gruntDependencies", 3 | "devDependencies": { 4 | "grunt": "^0.4.5", 5 | "grunt-contrib-cssmin": "^0.12.2", 6 | "grunt-contrib-uglify": "^0.9.1", 7 | "grunt-contrib-watch": "^0.6.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/iojs.simpleRequest.md: -------------------------------------------------------------------------------- 1 | 'use-strict'; 2 | 3 | import kenobi from 'kenobi'; 4 | import { rz as rz } from "ranza"; 5 | import 'bluebird'; 6 | 7 | if (kenobi) { 8 | console.log(kenobi) 9 | } -------------------------------------------------------------------------------- /test/fixtures/iojs.wrongRequest.md: -------------------------------------------------------------------------------- 1 | import from 'ranza'; 2 | 3 | if (ranza) { 4 | console.log(ranza) 5 | } -------------------------------------------------------------------------------- /test/fixtures/node.simpleRequest.md: -------------------------------------------------------------------------------- 1 | var kenobi = require("kenobi"), 2 | bluebird = require('bluebird'); 3 | 4 | var ranza = require( 'ranza' ); 5 | 6 | if (ranza) { 7 | console.log(ranza); 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/node.simpleRequestWithModuleDefinition.md: -------------------------------------------------------------------------------- 1 | var mandrill = require('mandrill-api/mandrill'); 2 | var ranza = require( "ranza" ); 3 | 4 | if (mandrill) { 5 | console.log(mandrill); 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/node.simpleRequestWithNodeApis.md: -------------------------------------------------------------------------------- 1 | var kenobi = require('kenobi'), 2 | path = require('path'), 3 | bluebird = require('bluebird'); 4 | 5 | var fs = require('fs'); 6 | var ranza = require( 'ranza' ); 7 | 8 | if (ranza) { 9 | console.log(ranza); 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/perfectDependencies/index.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'), glob = require( 'glob' ); -------------------------------------------------------------------------------- /test/fixtures/perfectDependencies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "perfectProject", 3 | "dependencies": { 4 | "bluebird": "^2.9.20" 5 | }, 6 | "devDependencies": { 7 | "glob": "^5.0.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/unusedDependency/index.js: -------------------------------------------------------------------------------- 1 | var bluebird = require('bluebird'); -------------------------------------------------------------------------------- /test/fixtures/unusedDependency/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "perfectProject", 3 | "dependencies": { 4 | "bluebird": "^2.9.20", 5 | "kenobi": "^1.0.0" 6 | }, 7 | "devDependencies": { 8 | "glob": "^5.0.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/help.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | fs = require('fs'), 3 | Ranza = require('../index.js'); 4 | 5 | describe('Help', function() { 6 | context('help method', function() { 7 | it('should be equal docs/help.md', function(done) { 8 | var help = fs.readFileSync(__dirname + '/../docs/help.md', 'utf8'); 9 | var ranzaHelp = Ranza.help(); 10 | 11 | assert.deepEqual(ranzaHelp, help); 12 | done(); 13 | }) 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/status.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | Promises = require('bluebird'), 3 | Ranza = Promises.promisifyAll(require('../index.js')), 4 | realCwd = process.cwd(); 5 | 6 | function fakeCwd(path) { 7 | process.cwd = function() { 8 | return realCwd + '/test/fixtures/' + path; 9 | }; 10 | } 11 | 12 | describe('Status', function() { 13 | context('getting status from project', function() { 14 | context('without missed requires and undefined dependencies', function() { 15 | it('should return only the used dependencies and a empty unused/undefined list', function(done) { 16 | fakeCwd('perfectDependencies'); 17 | 18 | Ranza.statusAsync(function(status) { 19 | assert.deepEqual(typeof(status), 'object'); 20 | assert.deepEqual(status.undefinedUsed, []); 21 | assert.deepEqual(status.definedUnused, []); 22 | assert.deepEqual(status.definedUsed, ['bluebird', 'glob']); 23 | done(); 24 | }); 25 | }) 26 | }) 27 | context('with unused dependencies', function() { 28 | it('should return the unused dependencies', function(done) { 29 | fakeCwd('unusedDependency'); 30 | 31 | Ranza.statusAsync(function(status) { 32 | assert.deepEqual(typeof(status), 'object'); 33 | assert.deepEqual(status.undefinedUsed, []); 34 | assert.deepEqual(status.definedUnused, ['kenobi', 'glob']); 35 | assert.deepEqual(status.definedUsed, ['bluebird']); 36 | done(); 37 | }); 38 | }) 39 | }) 40 | context('with grunt used and unused dependencies', function() { 41 | it('should return the unused dependencies', function(done) { 42 | fakeCwd('gruntDependencies'); 43 | 44 | Ranza.statusAsync(function(status) { 45 | assert.deepEqual(typeof(status), 'object'); 46 | assert.deepEqual(status.undefinedUsed, []); 47 | assert.deepEqual(status.definedUnused, ['grunt-contrib-watch']); 48 | assert.deepEqual(status.definedUsed, ['grunt', 'grunt-contrib-cssmin', 'grunt-contrib-uglify']); 49 | done(); 50 | }); 51 | }) 52 | }) 53 | 54 | }) 55 | }); 56 | -------------------------------------------------------------------------------- /test/version.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | Ranza = require('../index.js'); 3 | 4 | describe('Version', function() { 5 | context('version method', function() { 6 | it('should be equal package.json version', function(done) { 7 | var packageVersion = require('../package.json').version, 8 | ranzaVersion = Ranza.version(); 9 | 10 | assert.deepEqual(ranzaVersion, 'Ranza version: ' + packageVersion); 11 | done(); 12 | }) 13 | }); 14 | }); 15 | --------------------------------------------------------------------------------