From c29ea232637da0adbda36f9be966d50595521211 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 12:40:51 -0500 Subject: [PATCH 01/51] Moving functionality to lib/modclean.js --- index.js | 367 +------------------------------------------------------ 1 file changed, 2 insertions(+), 365 deletions(-) diff --git a/index.js b/index.js index 7915ef6..a62afd1 100644 --- a/index.js +++ b/index.js @@ -1,366 +1,3 @@ -/** - * modclean - * Remove unwanted files and directories from your node_modules folder - * @version 1.2.1 (4/29/2015) - * @author Kyle Ross - */ +"use strict"; -var glob = require('glob'), - rimraf = require('rimraf'), - util = require("util"), - fs = require('fs'), - EventEmitter = require("events").EventEmitter, - path = require('path'), - dir = require('node-dir'), - patterns = require('./patterns.json'); - -var defaults = { - /** - * The directory to search in, default is `process.cwd()` - * @type {String} - */ - cwd: process.cwd(), - /** - * Patterns to search for, default is an array loaded from `patterns.json` - * @type {Array} - */ - patterns: patterns.safe, - /** - * Exclude directories from being removed - * @type {Boolean} - */ - noDirs: false, - /** - * Ignore the provided glob patterns - * @type {Array|null} - */ - ignore: null, - /** - * Ignore the case of the file names when searching by patterns (default `true`) - * @type {Boolean} - */ - ignoreCase: true, - /** - * Function (async or sync) to call before each file is deleted to give ability to prevent deletion. (Optional, default `null`) - * If called with 3 arguments (file, files, cb) it's async and `cb` must be called with a result (`false` skips file). - * If called with 1 or 2 arguments (file, files) it's sync and `return false` will skip the file. - * @type {Function|null} - */ - process: null, - /** - * The folder name used for storing modules. Used to append to `options.cwd` if running in parent directory (default `"node_modules"`) - * @type {String} - */ - modulesDir: 'node_modules', - /** - * Remove empty directories as part of the cleanup process (default `true`) - * @type {Boolean} - */ - removeEmptyDirs: true, - /** - * Whether file deletion errors should halt the module from running and return the error to the callback (default `false`) - * @type {Boolean} - */ - errorHalt: false, - /** - * Use test mode which will get the list of files and run the process without actually deleting the files (default `false`) - * @type {Boolean} - */ - test: false -}; - -// export modclean -module.exports = modclean; - -/** - * Shortcut for calling `new ModClean(options, cb).clean()` - * @param {Object} options Options to set for ModClean (Optional) - * @param {Function} cb Callback function to call once completed or if error - * @return {Object} New ModClean instance - */ -function modclean(options, cb) { - return new ModClean(options, cb).clean(); -} - -/** - * The default options for ModClean that can be overridden. - * @property {Object} options Default options for ModClean. - */ -modclean.defaults = defaults; - -/** - * The full list of patterns from patterns.json - * @property {Object} patterns `safe`, `caution` and `danger` patterns from patterns.json - */ -modclean.patterns = patterns; - -// Export ModClean class -modclean.ModClean = ModClean; - -/** - * Main ModClean class - * @class ModClean - * @param {Object} options ModClean options (optional) - * @param {Function} cb Callback function to call if once completed or if error - `function(error, files)` - */ -function ModClean(options, cb) { - if(!(this instanceof ModClean)) return new ModClean(options); - EventEmitter.call(this); - - if(typeof options === 'function') cb = options; - if(!options || typeof options !== 'object') options = {}; - - this.options = options = extend(Object.create(modclean.defaults), options || {}); - if(typeof this.options.patterns === 'string') this.options.patterns = [this.options.patterns]; - - if(Array.isArray(this.options.patterns)) { - var _patterns = []; - for(var i = 0; i < this.options.patterns.length; i++) { - if(Array.isArray(this.options.patterns[i])) { - _patterns = _patterns.concat(this.options.patterns[i]); - } else { - _patterns.push(this.options.patterns[i]); - } - } - - this.options.patterns = _patterns; - } - - if(this.options.modulesDir !== false && path.basename(this.options.cwd) !== this.options.modulesDir) - this.options.cwd = path.join(this.options.cwd, this.options.modulesDir); - - if(cb) this.clean(cb); -} - -// Inherit EventEmitter.prototype -util.inherits(ModClean, EventEmitter); - -/** - * Start the clean process - * @public - * @param {Function} cb Callback function to call if once completed or if error - `function(error, files)` - */ -ModClean.prototype.clean = function(cb) { - var self = this; - - this.finalCallback = function(err, results) { - /** - * @event complete - * @property {Mixed} err An error string/object if error was thrown - * @property {Array} results List of files successfully deleted - */ - this.emit('complete', err, results); - if(typeof cb === 'function') cb(err, results); - }; - - /** - * @event start - * @property {Object} this ModClean instance - */ - self.emit('start', this); - - self._find(null, function(err, files) { - if(err) return self.finalCallback(err); - - self._process(files, function(err, results) { - if(err) return self.finalCallback(err); - - if(self.options.removeEmptyDirs) { - self._removeEmpty(function(e, res) { - self.finalCallback(e, results.concat(res)); - }); - } else self.finalCallback(err, results); - }); - }); -}; - -/** - * Find patterns using glob and return file list - * @protected - * @param {Array} patterns Array of patterns to find (defaults to `options.patterns`) - * @param {Function} cb Callback to call with `error` and `files` when complete - */ -ModClean.prototype._find = function(patterns, cb) { - var self = this, - opts = self.options, - globOpts = { - cwd: opts.cwd, - dot: true, - nocase: opts.ignoreCase, - ignore: opts.ignore, - nodir: opts.noDirs - }; - - glob('**/@('+ (patterns || opts.patterns).join('|') +')', globOpts, function(err, files) { - if(err) self.emit('error', err); - /** - * @event files - * @property {Array} files List of files found based on `options.patterns` in `options.cwd` - */ - else self.emit('files', files); - cb(err, files); - }); -}; - -/** - * Processes a list of files - * @protected - * @param {Array} files List of files to process - * @param {Function} cb Callback function to call once completed or if error - */ -ModClean.prototype._process = function(files, cb) { - var self = this, - opts = this.options, - processFn = typeof opts.process === 'function'? opts.process : function() { return true; }; - - if(!files.length) return cb(null, []); - - eachSeries(files, function(file, callback) { - if(processFn.length === 3) { - // If processFn has 3 arguments (file, files, cb), then assume async - processFn(file, files, function(result) { - if(result !== false) self._deleteFile(file, callback); - else callback(); - }); - } else { - // If processFn has any other number of arguments (default: file, files), then assume sync - if(processFn(file, files) !== false) self._deleteFile(file, callback); - else callback(); - } - }, function(err, results, errFile) { - /** - * @event finish - * @property {Array} results List of files successfully deleted - */ - self.emit('finish', results); - cb(err, results); - }); -}; - -/** - * Deletes file/folder at given path - * @protected - * @param {String} file The file/folder path to delete - * @param {Function} cb Callback function to call when complete or error - */ -ModClean.prototype._deleteFile = function(file, cb) { - var self = this, - opts = self.options; - // If test mode is enabled, just return the file. - if(opts.test) { - self.emit('deleted', file); - return cb(null, file); - } - - rimraf(path.join(opts.cwd, file), function(err) { - if(err) { - /** - * @event fileError - * @property {Mixed} err The error object/string - * @property {String} file The file that caused the error - */ - self.emit('fileError', err, file); - return opts.errorHalt? cb(err, file) : cb(); - } - - /** - * @event deleted - * @property {String} file The deleted file name - */ - self.emit('deleted', file); - cb(null, file); - }); -}; - -/** - * Remove empty directories from `options.cwd`. - * @protected - * @param {Function} cb Callback function to call when completed `function(err)`. - */ -ModClean.prototype._removeEmpty = function(cb) { - var self = this, - opts = self.options; - // If test mode is enabled or removeEmptyDirs is disabled, just return. - if(opts.test || !opts.removeEmptyDirs) return cb(null); - - dir.subdirs(opts.cwd, function(err, dirs) { - if(err) return cb(err); - if(!Array.isArray(dirs)) return cb(); - - eachSeries(dirs, function(dp, icb) { - isEmptyDir(dp, function(err, empty) { - if(err || !empty) return icb(); - - rimraf(dp, function(err) { - if(err) return icb(); - icb(null, dp); - }); - }); - }, function(err, results) { - cb(err, results); - }); - }); -}; - - -/** - * Determines if provided path is empty. - * @private - * @param {String} path The path to check if is empty - * @param {Function} cb Callback function to call once completed - */ -function isEmptyDir(path, cb) { - fs.readdir(path, function (err, files) { - if(err === null) return cb(null, !!!files.length); - cb(err); - }); -} - -/** - * Extends object with properties from another object - * @private - * @param {Object} obj The destination object - * @param {Object} ... The object in which to copy the properties from - * @return {Object} Destination object with properties copied - */ -function extend(obj) { - var arr = []; - - arr.forEach.call(arr.slice.call(arguments, 1), function(source) { - if(source) { - for(var prop in source) { - obj[prop] = source[prop]; - } - } - }); - return obj; -} - -/** - * Iterate through array asynchronously one item at a given time. - * Modified from `node-async` - https://github.com/caolan/async/blob/master/lib/async.js - * @private - * @param {Array} arr The array to iterate through - * @param {Function} iterator The function to run for each iteration - * @param {Function} callback Final callback function to call once all iterations have completed or if error - */ -function eachSeries(arr, iterator, callback) { - var results = []; - callback = callback || function() {}; - if(!arr.length) return callback(); - var completed = 0; - var iterate = function() { - iterator(arr[completed], function(err, result) { - if(err) { - callback(err, results, result); - callback = function() {}; - } else { - if(result) results.push(result); - completed += 1; - if (completed >= arr.length) callback(null, results); - else iterate(); - } - }); - }; - iterate(); -} +module.exports = require('./lib/modclean'); From 2c9f0de3288875fea003b7595d2aa3b15978d848 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 12:41:22 -0500 Subject: [PATCH 02/51] Rewrite for version 2.0.0 --- lib/modclean.js | 365 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/utils.js | 96 +++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 lib/modclean.js create mode 100644 lib/utils.js diff --git a/lib/modclean.js b/lib/modclean.js new file mode 100644 index 0000000..e37be62 --- /dev/null +++ b/lib/modclean.js @@ -0,0 +1,365 @@ +/*! + * modclean + * Remove unwanted files and directories from your node_modules folder + * @author Kyle Ross + */ +"use strict"; +const glob = require('glob'); +const rimraf = require('rimraf'); +const extend = require('extend'); +const path = require('path'); +const subdirs = require('subdirs'); +const emptyDir = require('empty-dir'); +const each = require('async-each-series'); + +const Utils = require('./utils.js'); +const EventEmitter = require("events").EventEmitter; + +let defaults = { + /** + * The directory to search in, default is `process.cwd()` + * @type {String} + */ + cwd: process.cwd(), + /** + * Array of patterns plugins to use. Default is `['default:safe']`. + * @type {Array} + */ + patterns: ['default:safe'], + /** + * Array of additional patterns to use. + * @type {Array} + */ + additionalPatterns: [], + /** + * Ignore the provided glob patterns + * @type {Array|null} + */ + ignorePatterns: null, + /** + * Exclude directories from being removed + * @type {Boolean} + */ + noDirs: false, + /** + * Ignore the case of the file names when searching by patterns (default `true`) + * @type {Boolean} + */ + ignoreCase: true, + /** + * Include dot files in the search (default `true`) + * @type {Boolean} + */ + dotFiles: true, + /** + * Function (async or sync) to call before each file is deleted to give ability to prevent deletion. (Optional, default `null`) + * If called with 3 arguments (file, files, cb) it's async and `cb` must be called with a result (`false` skips file). + * If called with 1 or 2 arguments (file, files) it's sync and `return false` will skip the file. + * @type {Function|null} + */ + process: null, + /** + * The folder name used for storing modules. Used to append to `options.cwd` if running in parent directory (default `"node_modules"`) + * @type {String} + */ + modulesDir: 'node_modules', + /** + * Remove empty directories as part of the cleanup process (default `true`) + * @type {Boolean} + */ + removeEmptyDirs: true, + /** + * Whether file deletion errors should halt the module from running and return the error to the callback (default `false`) + * @type {Boolean} + */ + errorHalt: false, + /** + * Use test mode which will get the list of files and run the process without actually deleting the files (default `false`) + * @type {Boolean} + */ + test: false +}; + +/** + * @class ModClean + * @extends {EventEmitter} + */ +class ModClean extends EventEmitter { + /** + * Initalizes ModClean class with provided options. If `cb` is provided, it will start `clean()`. + * @param {Object} options Options to configure ModClean + * @param {Function} cb Optional callback function, if provided, `clean()` is automatically called. + */ + constructor(options, cb) { + super(); + + this.utils = new Utils(); + + if(typeof options === 'function') cb = options; + if(!options || typeof options !== 'object') options = {}; + + this.options = options = extend({}, modclean.defaults, options); + + this._patterns = this.utils.initPatterns(this.options); + + if(this.options.modulesDir !== false && path.basename(this.options.cwd) !== this.options.modulesDir) + this.options.cwd = path.join(this.options.cwd, this.options.modulesDir); + + if(cb) this.clean(cb); + } + + /** + * Automated clean process that finds and deletes items based on the ModClean options. + * @param {Function} cb Callback to call once process is complete with `err` and `results`. + * @return {ModClean} Instance of ModClean + */ + clean(cb) { + let opts = this.options; + + this.emit('start', this); + + let done = (err, results) => { + /** + * @event complete + * @property {Mixed} err An error string/object if error was thrown + * @property {Array} results List of files successfully deleted + */ + this.emit('complete', err, results); + if(typeof cb === 'function') cb(err, results); + }; + + this._find((err, files) => { + if(err) return done(err); + + this._process(files, (err, results) => { + if(err || !opts.removeEmptyDirs) return done(err, results); + + this.cleanEmptyDirs((err, dirs) => { + return done(err, Array.isArray(dirs)? results.concat(dirs) : results); + }); + }); + }); + + return this; + } + + /** + * Finds files/folders based on the ModClean options. + * @private + * @param {Function} cb Callback function to call once complete. + */ + _find(cb) { + let opts = this.options, + globOpts = { + cwd: opts.cwd, + dot: opts.dotFiles, + nocase: opts.ignoreCase, + ignore: this._patterns.ignore, + nodir: opts.noDirs + }; + + this.emit('beforeFind', this._patterns.allow, globOpts); + + glob(`**/@(${this._patterns.allow.join('|')})`, globOpts, (err, files) => { + if(err) this.emit('error', err); + /** + * @event files + * @property {Array} files List of files found to be removed + */ + else this.emit('files', files); + cb(err, files); + }); + } + + /** + * Processes the found files and deletes the ones that pass `options.process`. + * @private + * @param {Array} files List of file paths to process. + * @param {Function} cb Callback function to call once complete. + */ + _process(files, cb) { + let self = this, + opts = this.options, + processFn = typeof opts.process === 'function'? opts.process : function() { return true; }, + results = []; + + if(!files.length) return cb(null, []); + + this.emit('process', files); + + function deleteFile() { + + } + + each(files, function(file, callback) { + if(processFn.length === 1) { + // If processFn has 1 argument (file), then assume sync + if(processFn(file) !== false) self._deleteFile(file, (err, result) => { + if(!err) results.push(file); + callback(err); + }); + else callback(); + } else { + // If processFn more than 1 argument (file, cb), then assume async + processFn(file, function(result) { + if(result !== false) self._deleteFile(file, (err, result) => { + if(!err) results.push(file); + callback(err); + }); + else callback(); + }); + } + }, function(err) { + /** + * @event finish + * @property {Array} results List of files successfully deleted + */ + self.emit('finish', results); + cb(err, results); + }); + } + + /** + * Deletes a single file/folder from the filesystem. + * @private + * @param {String} file File path to delete. + * @param {Function} cb Callback function to call once complete. + */ + _deleteFile(file, cb) { + let self = this, + opts = this.options; + + function done() { + /** + * @event deleted + * @property {String} file The deleted file name + */ + self.emit('deleted', file); + return cb(null, file); + } + + // If test mode is enabled, just return the file. + if(opts.test) return done(); + + rimraf(path.join(opts.cwd, file), (err) => { + if(err) { + /** + * @event fileError + * @property {Mixed} err The error object/string + * @property {String} file The file that caused the error + */ + this.emit('fileError', err, file); + return opts.errorHalt? cb(err, file) : cb(); + } + + return done(); + }); + } + + /** + * Finds and removes all empty directories. + * @param {Function} cb Callback to call once complete. + */ + cleanEmptyDirs(cb) { + let self = this, + opts = this.options, + results = []; + // If test mode is enabled or removeEmptyDirs is disabled, just return. + if(opts.test || !opts.removeEmptyDirs) return cb(); + + this.emit('beforeEmptyDirs'); + + function done(err, res) { + self.emit('afterEmptyDirs', results); + return cb(err, res); + } + + this._findEmptyDirs((err, dirs) => { + if(err) return done(err); + + this._removeEmptyDirs(dirs, (err, res) => { + return done(err, res); + }); + }); + } + + /** + * Finds all empty directories within `options.cwd`. + * @private + * @param {Function} cb Callback to call once complete. + */ + _findEmptyDirs(cb) { + let self = this, + results = []; + + function emptyFilter(fp) { + if(/Thumbs\.db$/i.test(fp)) return false; + if(/\.DS_Store$/i.test(fp)) return false; + + return true; + } + + subdirs(this.options.cwd, function(err, dirs) { + if(err || !Array.isArray(dirs)) return cb(err); + + each(dirs, (dir, dCb) => { + emptyDir(dir, emptyFilter, function(err, isEmpty) { + if(err || !isEmpty) return dCb(); + results.push(dir); + dCb(); + }); + }, (err) => { + self.emit('emptyDirs', results); + cb(err, results); + }); + }); + } + + /** + * Removes all empty directories provided in `dirs`. + * @private + * @param {Array} dirs List of empty directories to remove. + * @param {Function} cb Callback function to call once complete. + */ + _removeEmptyDirs(dirs, cb) { + let self = this, + results = []; + + each(dirs, (dir, dCb) => { + rimraf(dir, (err) => { + if(!err) { + results.push(dir); + self.emit('deletedEmptyDir', dir); + } else { + self.emit('emptyDirError', dir, err); + } + + dCb(); + }); + }, (err) => { + cb(err, results); + }); + } +} + +// export modclean +module.exports = modclean; + +/** + * Shortcut for calling `new ModClean(options, cb).clean()` + * @param {Object} options Options to set for ModClean (Optional) + * @param {Function} cb Callback function to call once completed or if error + * @return {Object} New ModClean instance + */ +function modclean(options, cb) { + return new ModClean(options, cb).clean(); +} + +/** + * The default options for ModClean that can be overridden. + * @property {Object} options Default options for ModClean. + */ +modclean.defaults = defaults; + +// Export ModClean class +modclean.ModClean = ModClean; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..e0b4787 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,96 @@ +"use strict"; +const uniq = require('lodash.uniq'); +const path = require('path'); + + +class ModClean_Utils { + initPatterns(opts) { + let patDefs = opts.patterns, + patterns = [], + ignore = []; + + if(!Array.isArray(patDefs)) patDefs = [patDefs]; + + patDefs.forEach((def) => { + def = def.split(':'); + let mod = def[0], + name = def[1], + loader = this._loadPatterns(mod), + results; + + mod = loader.module; + results = loader.patterns; + + let all = Object.keys(results).filter(function(val) { + return val[0] !== '$'; + }); + + if(!name) { + if(results.$default) name = results.$default; + else name = all[0]; + } + + if(name === '*') name = all; + + let rules = Array.isArray(name)? name : name.split(','); + + rules.forEach(function(rule) { + if(!results.hasOwnProperty(rule)) throw new Error(`Module "${mod}" does not contain rule "${rule}"`); + let obj = results[rule]; + + if(Array.isArray(obj)) return patterns = patterns.concat(obj); + + if(typeof obj === 'object') { + if(obj.hasOwnProperty('patterns')) patterns = patterns.concat(obj.patterns); + if(obj.hasOwnProperty('ignore')) ignore = ignore.concat(obj.ignore); + } + }); + }); + + let addlPats = opts.additionalPatterns, + addlIgnore = opts.ignorePatterns; + + if(Array.isArray(addlPats) && addlPats.length) patterns = patterns.concat(addlPats); + if(Array.isArray(addlIgnore) && addlIgnore.length) ignore = ignore.concat(addlIgnore); + + patterns = uniq(patterns); + ignore = uniq(ignore); + + if(!patterns.length) throw new Error('No patterns have been loaded, nothing to check against'); + + return { + allow: patterns, + ignore + }; + } + + _loadPatterns(module, def) { + let patterns; + + if(module.indexOf('/') !== -1) { + let ext = path.extname(module); + if(!path.isAbsolute(module)) module = path.resolve(process.cwd(), module); + + if(ext === '.js' || ext === '.json') patterns = require(module); + else throw new Error(`Invalid pattern module "${def}" provided`); + } else { + if(module.match(/modclean\-patterns\-/) === null) module = 'modclean-patterns-' + module; + + try { + patterns = require(module); + } catch(e) { + throw new Error(`Unable to find patterns plugin "${module}", is it installed?`); + } + } + + if(patterns === null || typeof patterns !== 'object') + throw new Error(`Patterns "${module}" did not return an object`); + + return { + module, + patterns + }; + } +} + +module.exports = ModClean_Utils; From 20d822db633e7212284b2788d47e954991114154 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 12:41:39 -0500 Subject: [PATCH 03/51] Removing patterns.json in favor for plugins --- patterns.json | 135 -------------------------------------------------- 1 file changed, 135 deletions(-) delete mode 100644 patterns.json diff --git a/patterns.json b/patterns.json deleted file mode 100644 index 767db97..0000000 --- a/patterns.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "safe": [ - "readme*", - ".npmignore", - "*license*", - "*licence*", - "history.md", - "history.markdown", - "history", - ".gitattributes", - ".gitmodules", - ".travis.yml", - "binding.gyp", - "contributing*", - "component.json", - "composer.json", - "makefile*", - "gemfile*", - "rakefile*", - ".coveralls.yml", - "example*", - "changelog*", - "changes", - ".jshintrc", - "bower.json", - "*appveyor.yml", - "*.log", - "*.tlog", - "*.patch", - "*.sln", - "*.pdb", - "*.vcxproj*", - ".gitignore", - ".sauce-labs*", - ".vimrc*", - ".idea", - "examples", - "samples", - "test", - "tests", - "draft-00", - "draft-01", - "draft-02", - "draft-03", - "draft-04", - ".eslintrc", - ".jamignore", - ".jscsrc", - "*.todo", - "*.md", - "*.markdown", - "*.js.map", - "contributors", - "*.orig", - "*.rej", - ".zuul.yml", - ".editorconfig", - ".npmrc", - ".jshintignore", - ".eslintignore", - ".lint", - ".lintignore", - "cakefile", - ".istanbul.yml", - "authors", - "hyper-schema", - "mocha.opts", - ".gradle", - ".tern-port", - ".gitkeep", - ".dntrc", - "*.watchr", - ".jsbeautifyrc", - "cname", - "screenshots", - ".dir-locals.el", - "jsl.conf", - "jsstyle", - "benchmark", - "dockerfile", - "*.nuspec", - "*.csproj", - "thumbs.db", - ".ds_store", - "desktop.ini", - "npm-debug.log", - "wercker.yml" - ], - - "caution": [ - "*.png", - "*.gif", - "*.jpg", - "*.html", - "*.css", - "*.eot", - "*.ttf", - "*.woff", - "*.ico", - "*.js.gz", - "*.json~", - "*.txt", - "*.sh", - "*.gnu", - "*.jar", - "*.bat", - "*.gyp", - "*.gypi", - "*.h", - "*.c", - "*.cc", - "*.cpp" - ], - - "danger": [ - "*.coffee", - "*.ts", - ".bin", - "*.min.js", - "test.js", - "*.test.js", - "gruntfile.js", - "gulpfile.js", - "*.sh", - "*.bat", - "*.cmd", - "*.ps1", - "*.map", - "images", - "semver", - "link", - "links", - "uglifyjs" - ] -} From ddf2ab1ff8b130c25f73b4bef38d7a2ec47111d5 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 12:41:57 -0500 Subject: [PATCH 04/51] Rewrite CLI utility --- bin/modclean.js | 453 +++++++++++++++++++++++------------------------- bin/utils.js | 53 ++++++ 2 files changed, 271 insertions(+), 235 deletions(-) create mode 100644 bin/utils.js diff --git a/bin/modclean.js b/bin/modclean.js index cac387e..8387797 100644 --- a/bin/modclean.js +++ b/bin/modclean.js @@ -1,302 +1,285 @@ #!/usr/bin/env node -require('colors'); +"use strict"; -var program = require('commander'), - inquirer = require('inquirer'), - pkg = require('../package.json'), - notifier = require('update-notifier')({ pkg: pkg }), - path = require('path'), - os = require('os'), - modclean = require('../index.js'), - ModClean = modclean.ModClean, - pkg = require('../package.json'); +const chalk = require('chalk'); +const program = require('commander'); +const notifier = require('update-notifier'); +const clui = require('clui'); +const pad = require('pad'); +const readline = require('readline'); +const path = require('path'); +const os = require('os'); +const pkg = require('../package.json'); -if(notifier.update) notifier.notify(); +const utils = require('./utils'); +const modclean = require('../lib/modclean'); +const ModClean = modclean.ModClean; + +notifier({ pkg }).notify(); function list(val) { return val.split(','); } +process.on('SIGINT', function() { + process.stdin.destroy(); + process.exit(); +}); + program .version(pkg.version) .description('Remove unwanted files and directories from your node_modules folder') .usage('modclean [options]') .option('-t, --test', 'Run modclean and return results without deleting files') .option('-p, --path ', 'Path to run modclean on (defaults to current directory)') + .option('-D, --modules-dir ', 'Modules directory name (defaults to "node_modules")') .option('-s, --case-sensitive', 'Matches are case sensitive') .option('-i, --interactive', 'Each deleted file/folder requires confirmation') .option('-P, --no-progress', 'Hide progress bar') .option('-e, --error-halt', 'Halt script on error') .option('-v, --verbose', 'Run in verbose mode') .option('-r, --run', 'Run immediately without warning') - .option('-n, --patterns [patterns]', 'Patterns type(s) to remove (safe, caution, or danger)') + .option('-n, --patterns ', 'Patterns plugins/rules to use (defaults to "default:safe")', list) + .option('-a, --additional-patterns ', 'Additional glob patterns to search for', list) .option('-I, --ignore ', 'Comma separated list of patterns to ignore', list) .option('--no-dirs', 'Exclude directories from being removed') + .option('--no-dotfiles', 'Exclude dot files from being removed') .option('-d, --empty', 'Remove empty directories') .parse(process.argv); -var ui = new inquirer.ui.BottomBar(); - -newLine(); -ui.log.write('MODCLEAN'.yellow.bold + (' v' + pkg.version).grey); - -if(program.test) { - newLine(); - ui.log.write('RUNNING IN TEST MODE'.red.bold); - ui.log.write('When running in test mode, files will not be deleted from the file system.'. grey); -} - -if(!program.run && !program.test) { - ui.log.write('\n' + (' WARNING:'.red.bold)); - ui.log.write([ - ' This module deletes files from the filesystem that cannot be retreived. Although what', - ' is being deleted are not required by the modules and/or your application, there is no', - ' guarantee that the modules will work after this script runs. The patterns.json file', - ' included with this module shows a listing of files and folders that will be searched', - ' for and removed from the file system. If you have any concerns with deleting files,', - ' you should run this utility using the "--test" flag first or with the "-i" flag to', - ' make the deletion process interactive. The author or contributors shall not be held', - ' responsible for and damages this module may cause. Please see the README.\n\n' - ].join(' \n')); - - confirm('Are you sure you want to continue?', function(result) { - if(!result) return process.exit(0); - new ModCleanCLI(program); - }); -} else { - newLine(); - process.nextTick(function() { - new ModCleanCLI(program); - }); -} - -function ModCleanCLI(opts) { - if(!(this instanceof ModCleanCLI)) return new ModCleanCLI(opts); - - var self = this, - _patterns = []; - - this.current = 0; - this.skipped = []; - this.total = 0; - this.argv = opts; - this.os = os.platform(); - - if(opts.patterns) { - var patts = opts.patterns.split(','); +class ModClean_CLI { + constructor() { + this.log = utils.initLog(program.verbose); + + // Display CLI header + console.log( + "\n" + + chalk.yellow.bold('MODCLEAN ') + + chalk.gray(' Version ' + pkg.version) + "\n" + ); - for(var i = 0; i < patts.length; i++) { - if(modclean.patterns.hasOwnProperty(patts[i].toLowerCase())) - _patterns.push(modclean.patterns[patts[i].toLowerCase()]); + // Display "running in test mode" message + if(program.test) { + console.log( + chalk.cyan.bold('RUNNING IN TEST MODE') + "\n" + + chalk.gray('When running in test mode, files will not be deleted from the file system.') + "\n" + ); } - } - - this.options = { - cwd: opts.path || process.cwd(), - patterns: _patterns.length? _patterns : modclean.patterns.safe, - noDirs: !!opts.dirs, - errorHalt: !!opts.errorHalt, - removeEmptyDirs: !!opts.empty, - test: !!opts.test, - process: function(file, files, cb) { - self.current += 1; - if(!opts.interactive) return cb(true); - - var fn = path.relative(self.options.cwd, file); - if(self.os === 'win32') fn = fn.replace(/\\+/g, '/'); + + // Display warning message and confirmation prompt + if(!program.run && !program.test) { + console.log( + chalk.red.bold('WARNING:') + "\n" + + chalk.gray(utils.warningMsg) + "\n" + ); - confirm(('(%/%) '.fmt(self.current, files.length)).grey + fn + ' - ' + 'Delete file?'.yellow.bold, function(res) { - if(!res) self.skipped.push(file); - cb(res); + return utils.confirm('Are you sure you want to continue?', (res) => { + if(!res) return process.exit(0); + this.start(); }); } - }; - - if(opts.ignore && opts.ignore.length) this.options.ignore = opts.ignore; - - if(program.caseSensitive) this.options.ignoreCase = false; + + this.start(); + } - this.inst = new ModClean(this.options); - this.initEvents(); - this.inst.clean(this.done.bind(this)); -} - -ModCleanCLI.prototype = { - done: function(err, results) { - var pb = ui.bottomBar; - // DISPLAY RESULTS - newLine(); - ui.updateBottomBar(''); - console.log(pb); - newLine(); - ui.log.write('FILES/FOLDERS DELETED'.green.bold); - ui.log.write(' Total: '.yellow.bold + results.length); - ui.log.write(' Skipped: '.yellow.bold + this.skipped.length); - newLine(); + start() { + let self = this, + platform = os.platform(); - log('verbose', '\n' + results.join('\n')); + this.stats = { + current: 0, + total: 0, + skipped: [], + currentEmpty: 0, + totalEmpty: 0 + }; - if(err) { - newLine(); - log('error', err); - return process.exit(1); - } + // Disable progress bar in interactive mode + if(program.interactive) program.progress = false; - setTimeout(function() { - process.exit(0); - }, 500); - }, + let options = { + cwd: program.path || process.cwd(), + modulesDir: program.modulesDir || 'node_modules', + patterns: program.patterns && program.patterns.length? program.patterns : ['default:safe'], + additionalPatterns: program.additionalPatterns || [], + ignorePatterns: program.ignore || [], + noDirs: !program.dirs, + dotFiles: !!program.dotfiles, + errorHalt: !!program.errorHalt, + removeEmptyDirs: !!program.empty, + ignoreCase: !program.caseSensitive, + test: !!program.test, + process: function(file, cb) { + self.stats.current += 1; + if(!program.interactive) return cb(true); + + let name = path.relative(options.cwd, file); + if(platform === 'win32') name = name.replace(/\\+/g, '/'); + + utils.confirm( + chalk.gray(`(${self.stats.current}/${self.stats.total})`) + + `${name} ${chalk.gray(' - ')} ${chalk.yellow.bold('Delete File?')}`, + function(res) { + if(!res) self.stats.skipped.push(file); + cb(res); + }); + } + }; + + this.modclean = new ModClean(options); + + this.initEvents(); + this.modclean.clean(this.done.bind(this)); + } - initEvents: function() { - var self = this, - inst = self.inst; + initEvents() { + var inst = this.modclean; + + let progressBar = new clui.Progress(40), + spinner = new clui.Spinner('Loading...'), + showProgress = true; - this.progressBar = new Progress(40); + if(!process.stdout.isTTY || program.interactive || !program.progress || program.verbose) showProgress = false; + + function updateProgress(current, total) { + if(showProgress) { + process.stdout.cursorTo(0); + process.stdout.write(progressBar.update(current, total)); + process.stdout.clearLine(1); + } + } + + function showSpinner(msg) { + if(!process.stdout.isTTY || program.verbose || !program.progress) { + console.log(msg); + } else { + spinner.message(msg); + spinner.start(); + } + } // Start Event (searching for files) - inst.on('start', function() { - log('event', 'start'); - log('verbose', '\n' + JSON.stringify(inst.options, null, 4)); - self.spin('Searching for files in '+ self.options.cwd); + inst.on('start', () => { + this.log('event', 'start'); + this.log('verbose', '\n' + JSON.stringify(inst.options, null, 4)); + + showSpinner(`Searching for files in ${inst.options.cwd}...`); }); // Files Event (file list after searching complete) - inst.on('files', function(files) { - log('event', 'files'); - self.spinStop(); + inst.on('files', (files) => { + this.log('event', 'files'); + if(process.stdout.isTTY) spinner.stop(); + + this.stats.total = files.length; + + console.log(`Found ${chalk.green.bold(files.length)} files/folders to remove\n`); + }); + + inst.on('process', (files) => { + this.log('event', 'process'); - self.total = files.length; - log(null, 'Found', (files.length.toString()).green.bold, 'files/folders to remove'); - newLine(); + if(!showProgress && !program.interactive && !program.verbose) + console.log('Deleting files, please wait...'); + + updateProgress(0, files.length); }); // Deleted Event (called for each file deleted) - inst.on('deleted', function(file) { - log('event', 'deleted'); + inst.on('deleted', (file) => { + updateProgress(this.stats.current, this.stats.total); + + this.log( + 'verbose', + `${chalk.yellow.bold('DELETED')} (${this.stats.current}/${this.stats.total}) ${chalk.gray(file)}` + ); + }); + + inst.on('beforeEmptyDirs', () => { + this.log('event', 'beforeEmptyDirs'); + + showSpinner(`Searching for empty directories in ${inst.options.cwd}...`); + }); + + inst.on('emptyDirs', (dirs) => { + this.log('event', 'emptyDirs'); + + if(process.stdout.isTTY) spinner.stop(); + + this.stats.totalEmpty = dirs.length; + console.log(`\nFound ${chalk.green.bold(dirs.length)} empty directories to remove\n`); + + if(!dirs.length) return; - if(!self.argv.interactive && self.argv.progress) ui.updateBottomBar(self.getProgress()); + if(!showProgress && !program.interactive && !program.verbose) + console.log('Deleting empty directories, please wait...'); - log('verbose', 'DELETED'.yellow.bold, '(%/%) %'.fmt(self.current, self.total, file.grey)); + updateProgress(0, dirs.length); + }); + + inst.on('deletedEmptyDir', (dir) => { + this.stats.currentEmpty += 1; + updateProgress(this.stats.currentEmpty, this.stats.totalEmpty); + + this.log( + 'verbose', + `${chalk.yellow.bold('DELETED EMPTY DIR')} (${this.stats.currentEmpty}/${this.stats.totalEmpty}) ${chalk.gray(dir)}` + ); + }); + + inst.on('afterEmptyDirs', (dirs) => { + if(showProgress) process.stdout.write('\n'); + this.log('event', 'afterEmptyDirs'); }); // Error Event (called as soon as an error is encountered) - inst.on('error', function(err) { - log('event', 'error'); - if(self.argv.verbose) log('error', err); + inst.on('error', (err) => { + this.log('event', 'error'); + this.log('error', err); }); // FileError Event (called when there was an error deleting a file) - inst.on('fileError', function(err, file) { - log('event', 'fileError'); - if(self.argv.verbose) log('error', 'FILE ERROR:'.red, err, file.grey); + inst.on('fileError', (err, file) => { + this.log('event', 'fileError'); + this.log('error', `${chalk.red.bold('FILE ERROR:')} ${err}\n${chalk.gray(file)}`); }); // Finish Event (once processing/deleting all files is complete) - inst.on('finish', function(results) { - log('event', 'finish'); - log('verbose', 'FINISH'.green, 'Deleted '+ (results.length.toString()).yellow.bold, 'files/folders of '+ (self.total.toString()).yellow.bold); + inst.on('finish', (results) => { + if(showProgress) process.stdout.write('\n'); + this.log('event', 'finish'); + + this.log( + 'verbose', + `${chalk.green('FINISH')} Deleted ${chalk.yellow.bold(results.length)} files/folders of ${chalk.yellow.bold(this.stats.total)}` + ); }); // Complete Event (once everything has completed) - inst.on('complete', function() { - log('event', 'complete'); + inst.on('complete', () => { + this.log('event', 'complete'); }); - }, - - getProgress: function() { - if(!this.argv.progress) return ''; - return 'Cleanup Progress '.grey + this.progressBar.update(this.current, this.total) + ' (%/%)'.fmt(this.current, this.total); - }, - - spin: function(str) { - str = str.grey; - - var loader = [ - '/ '.cyan.bold + str, - '| '.cyan.bold + str, - '\\ '.cyan.bold + str, - '- '.cyan.bold + str - ]; - - var i = 4; - this._spin = setInterval(function() { - ui.updateBottomBar(loader[i++ % 4]); - }, 300); - }, - - spinStop: function() { - clearInterval(this._spin); - ui.updateBottomBar(''); } -}; - - -function log(type) { - var prefix; - if((type === 'verbose' || type === 'event') && !program.verbose) return; - switch(type) { - case 'event': - prefix = 'EVENT'.magenta.bold; break; - - case 'error': - prefix = 'ERROR'.red.bold; break; + done(err, results) { + console.log( + "\n" + chalk.green.bold('FILES/FOLDERS DELETED') + "\n" + + ` ${chalk.yellow.bold('Total: ')} ${results.length}\n` + + ` ${chalk.yellow.bold('Skipped: ')} ${this.stats.skipped.length}\n` + + ` ${chalk.yellow.bold('Empty: ')} ${this.stats.totalEmpty}\n` + ); + + setTimeout(() => { + process.stdin.destroy(); - case 'verbose': - prefix = 'VERBOSE'.green.bold; break; + if(err) { + this.log('error', err); + return process.exit(1); + } - default: prefix = ''; + process.exit(0); + }, 500); } - - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(prefix + '>'.grey); - - ui.log.write(args.join(' ')); } -function newLine() { - ui.log.write('\n'); -} - -function confirm(str, fn) { - ui.updateBottomBar(str + (' [y|N] ').grey); - process.stdin.setEncoding('utf8'); - process.stdin.once('data', function(val) { - fn(/^y|yes|ok|true$/i.test(val.trim())); - }).resume(); -} - -// Modified from clui (https://github.com/nathanpeck/clui) -function Progress(width) { - var currentValue = 0, - maxValue = 0; - - this.update = function (currentValue, maxValue) { - if(maxValue === 0) return '[]'; - - var barLength = Math.ceil(currentValue / maxValue * width); - if(barLength > width) barLength = width; - - return '[' + - (Array(barLength).join("|")).green + - Array(width + 1 - barLength).join("-") + - '] '+ (Math.ceil(currentValue / maxValue * 100) + '%').grey; - }; -} - -// String Interpolation since I don't care for the built in console one -// Adding this since messing with the String prototype isn't a big deal in CLI apps -String.prototype.fmt = function stringProtoFmtFn() { - var args = (arguments.length === 1 && Array.isArray(arguments[0])) ? arguments[0] : arguments, - index = 0; - return this.replace(/(%)/g, function stringProtoFmtReplaceCB(m) { - var val = args[index]; - - if(Array.isArray(val)) val = val.join(', '); - if(typeof val === 'object') val = JSON.stringify(val); - if(typeof val === 'function') val = val.toString(); - - index++; - return val || m; - }); -}; +new ModClean_CLI(); diff --git a/bin/utils.js b/bin/utils.js new file mode 100644 index 0000000..1c0f787 --- /dev/null +++ b/bin/utils.js @@ -0,0 +1,53 @@ +/* + Misc. utils for the CLI program + */ +"use strict"; +const readline = require('readline'); +const chalk = require('chalk'); + +exports.warningMsg = +` This module deletes files from the filesystem and cannot be recovered. + Depending on the patterns plugins, the files being deleted typically are not + required by modules, although there is no guarantee that the modules will + work correctly after running this script. You can easily restore your modules + by deleting your "node_modules" folder and running "npm install" again. If you + have any concerns with deleting files, you should run this utility with the + "--test" flag first or with the "-i" flag to make the process interactive. The + author or contributors shall not be held responsible for damages this module + might cause. Please see the README for more information.\n\n`; + +exports.confirm = function(msg, cb) { + process.stdin.setEncoding('utf8'); + + let rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + rl.question(msg + chalk.gray(' [y/N] '), function(answer) { + rl.close(); + cb(/^y|yes|ok|true$/i.test(answer.trim())); + }); +}; + + +exports.initLog = function(verbose) { + let types = { + error: chalk.red.bold('ERROR'), + info: '' + }; + + if(verbose) { + types.event = chalk.magenta.bold('EVENT'); + types.verbose = chalk.green.bold('VERBOSE'); + } + + return function log(type, ...args) { + if(!type) type = 'info'; + if(!types.hasOwnProperty(type)) return; + let method = type === 'error'? 'error' : 'log'; + + args.unshift(types[type] + chalk.gray('>')); + console[method](args.join(' ')); + }; +}; From 9a2ceff1e0693c0873bd745e933dac1c06f7620c Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 12:42:49 -0500 Subject: [PATCH 05/51] Changing Node version to test on --- .travis.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 01d7c04..dbac2f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,6 @@ language: node_js node_js: - - "4.1" - - "4.0" - - "1.0" - - "1.6" - - "0.12" - - "0.11" - - "0.10" - - "0.8" + - "6.1" before_install: - npm install -g npm before_script: From 6a0deddc7d4f4bfd5ba128610e8b753970987b74 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 12:43:15 -0500 Subject: [PATCH 06/51] Version 2.0.0 --- HISTORY.md | 41 +++++++++++++++++++++++++++++++++++++++++ README.md | 40 ++++++++++++++++++++++------------------ package.json | 36 +++++++++++++++++++++++++----------- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 70571b3..c307e71 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,46 @@ # ModClean History +## 2.0.0 (1/11/2016) +### ModClean API Changes +* Complete rewrite using ES6 and some breaking changes (now requires Node v6.9+) +* No longer includes `patterns.json` file, instead uses plugins to allow further customization. +* **Breaking Change:** `patterns` option now takes array of pattern plugins instead of patterns. See README for more information. +* **Breaking Change:** `ignore` option has been renamed to `ignorePatterns`. +* **Breaking Change:** `process` option now is sync with only 1 argument and async with 2. +* **New!** Added `additionalPatterns` option that takes array of glob patterns to use with any provided pattern plugins. +* **New!** Added `dotFiles` option that allows disabling of removing dot files. +* **New!** Added `process` event which will be fired before the files start processing. +* **New!** When finding empty directories, it will now ignore `.DS_Store` and `Thumbs.db` files. +* **New!** Added `beforeFind` event. +* **New!** Added `beforeEmptyDirs` event. +* **New!** Added `emptyDirs` event. +* **New!** Added `emptyDirError` event. +* **New!** Added `afterEmptyDirs` event. +* **New!** Added `deletedEmptyDir` event. +* Removed utility functions in favor for micro-packages. + * Removed unused dependencies. + * Replaced `node-dir` dependency with `subdirs`. + * Replaced `colors` dependency with `chalk`. + * Added `empty-dir` dependency. + * Added `async-each-series` dependency. + * Added `extend` dependency. + * Added `lodash.uniq` dependency. +* Updated all dependcies to their latest versions. +* Benchmarks updated for 2017. + +### ModClean CLI Changes +* Rewrite using ES6 with new features and bug fixes. +* **Breaking Change:** `-n, --patterns` option now takes list of plugins instead of a pattern rule name. +* **New!** Added `-D, --modules-dir` option. +* **New!** Added `-a, --additional-patterns` option. +* **New!** Added `--no-dotfiles` option. +* Removed `inquirer` dependency in favor of `clui` and utility functions. +* Reduced memory usage of the CLI. +* Logging has been rewritten to handle stack overflow errors. +* Updated some text. + +_If you still need to support Node < v6.9, use ModClean 1.3.0 instead. For more information about these changes, refer to the README._ + ## 1.3.0 (1/6/2017) * Added `noDirs` option to exclude directories from being removed (#8) - Added `--no-dirs` option to CLI (#8) diff --git a/README.md b/README.md index b9d61c2..8ce7d91 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,54 @@ # ModClean *Remove unwanted files and directories from your node_modules folder* -[![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) [![Build Status](https://img.shields.io/travis/KyleRoss/modclean.svg)](https://travis-ci.org/KyleRoss/modclean) ![NPM Dependencies](https://david-dm.org/KyleRoss/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/KyleRoss/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/KyleRoss/modclean.svg)](https://github.com/KyleRoss/modclean/issues) +[![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) [![Build Status](https://img.shields.io/travis/KyleRoss/modclean.svg)](https://travis-ci.org/KyleRoss/modclean) ![NPM Dependencies](https://david-dm.org/KyleRoss/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/KyleRoss/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/KyleRoss/modclean.svg)](https://github.com/KyleRoss/modclean/issues) [![Package Quality](http://npm.packagequality.com/shield/modclean.svg)](http://packagequality.com/#?package=modclean) -In some environments (especially Enterprise), it's required to commit the `node_modules` folder into version control due to compatibility and vetting open source code. One of the major issues with this is the sheer amount of useless files that are littered through the node_modules folder; taking up space, causing long commit/checkout times, increasing latency on the network, causing additional stress on a CI server, etc. If you think about it, do you really need to deploy tests, examples, build files, attribute files, etc? ModClean is a simple utility that provides a full API and CLI utility to reduce the number of useless files. Even if you do not commit your node_modules folder, this utility is still useful when the application is deployed since you do not need these useless files wasting precious disk space on your server. +### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use ModClean 1.3.0 instead. + +In some environments (especially Enterprise), it's required to commit the `node_modules` folder into version control due to compatibility and vetting open source code. One of the major issues with this is the sheer amount of useless files that are littered through the node_modules folder; taking up space, causing long commit/checkout times, increasing latency on the network, causing additional stress on a CI server, etc. If you think about it, do you really need to deploy tests, examples, build files, attribute files, etc? ModClean is a simple utility that provides a full API and CLI utility to reduce the number of useless files. Even if you do not commit your node_modules folder, this utility is still useful when the application is deployed as you do not need these useless files wasting precious disk space on your server. Depending on the number of modules you are using, file reduction can be anywhere from hundreds to thousands. I work for a Fortune 500 company and we use this to reduce the amount of useless files and typically we remove over 500 files (roughly 100MB total) from our ~20 modules we use in our applications. It's a huge improvement in deployment time and commit/checkout time. -This module comes with a JSON file (patterns.json) that outlines file patterns that searched for through the node_modules folder recursively. This list is a basic list of commonly found files/folders within various node_modules that are junk, although it's by no means a complete list. Even though this is the default list, you can provide your own list of patterns to use instead. +**New!** In ModClean 2.0.0, patterns are now provided by plugins instead of a static `patterns.json` file as part of the module. By default, ModClean comes with [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) installed, providing the same patterns as before. You now have the ability to create your own patterns plugins and use multiple plugins to clean your modules. This allows flexibility with both the programmatic API and CLI. **IMPORTANT** -This module has been heavily tested in an enterprise environment used for large enterprise applications. The provided patterns in this module (see patterns.json) have worked very well when cleaning up useless files in many popular modules. There are hundreds of thousands of modules in NPM and we cannot simply cover them all. If you are using ModClean for the first time on your application, you should create a copy of the application so you can ensure it still runs properly after running ModClean. The patterns are set in a way to ensure no crutial module files are removed, although there could be one-off cases where a module could be affected and that's why I am stressing that testing and backups are important. There could still be many useless files left after the cleanup process since we cannot cover them all. If you find any files that should be removed, please create a pull request using the contributing guidelines at the bottom of this file. +This module has been heavily tested in an enterprise environment used for large enterprise applications. The provided patterns in [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) have worked very well when cleaning up useless files in many popular modules. There are hundreds of thousands of modules in NPM and I cannot simply cover them all. If you are using ModClean for the first time on your application, you should create a copy of the application so you can ensure it still runs properly after running ModClean. The patterns are set in a way to ensure no crutial module files are removed, although there could be one-off cases where a module could be affected and that's why I am stressing that testing and backups are important. If you find any files that should be removed, please create a pull request to [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) or create your own patterns plugin to share with the community. ### Removal Benchmark So how well does this module work? If we `npm install sails` and run ModClean on it, here are the results: -#### Using Safe Patterns -`modclean -n safe` +_All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v3.10.8_ + +#### Using Default Safe Patterns +`modclean --empty-dirs -n default:safe` or `modclean -d` | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ----------- | -| Before ModClean | 7,461 | 1,915 | 72.5 MB | -| After ModClean | 3,335 | 1,393 | 43.5 MB | -| Reduced | **4,126** | **522** | **29.0 MB** | +| Before ModClean | 16,126 | 1,942 | 118 MB | +| After ModClean | 12,139 | 1,504 | 95 MB | +| Reduced | **3,987** | **438** | **23 MB** | #### Using Safe and Caution Patterns -`modclean -n safe,caution` +`modclean --empty-dirs -n default:safe,default:caution` | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ----------- | -| Before ModClean | 7,461 | 1,915 | 72.5 MB | -| After ModClean | 3,029 | 1,393 | 38.8 MB | -| Reduced | **4,432** | **522** | **33.7 MB** | +| Before ModClean | 16,126 | 1,942 | 118 MB | +| After ModClean | 11,888 | 1,474 | 90 MB | +| Reduced | **4,238** | **468** | **28 MB** | #### Using Safe, Caution and Danger Patterns -`modclean -n safe,caution,danger` +`modclean --empty-dirs --patterns="default:*"` | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ----------- | -| Before ModClean | 7,461 | 1,915 | 72.5 MB | -| After ModClean | 2,957 | 1,393 | 35.9 MB | -| Reduced | **4,504** | **522** | **36.6 MB** | +| Before ModClean | 16,126 | 1,942 | 118 MB | +| After ModClean | 11,691 | 1,449 | 86 MB | +| Reduced | **4,435** | **493** | **32 MB** | That makes a huge difference in the amount of files and disk space. -View additional benchmarks in [BENCHMARK.md](https://github.com/KyleRoss/modclean/blob/master/BENCHMARK.md). If you would like to run some of your own benchmarks, you can use [modclean-benchmark](https://github.com/KyleRoss/modclean-benchmark). +View additional benchmarks in [BENCHMARK.md](https://github.com/ModClean/modclean/blob/master/BENCHMARK.md). If you would like to run some of your own benchmarks, you can use [modclean-benchmark](https://github.com/ModClean/modclean-benchmark). ## Install diff --git a/package.json b/package.json index acd16ea..dd2081c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modclean", - "version": "1.3.0", + "version": "2.0.0", "description": "Remove unwanted files and directories from your node_modules folder", "main": "index.js", "bin": { @@ -20,6 +20,10 @@ "module-cleanup", "node_modules", "delete", + "flatten", + "path", + "limit", + "less", "files", "folders" ], @@ -30,16 +34,26 @@ }, "homepage": "https://github.com/KyleRoss/modclean", "dependencies": { - "colors": "^1.1.2", - "commander": "^2.8.1", - "glob": "^7.0.0", - "inquirer": "^0.12.0", - "node-dir": "^0.1.9", - "rimraf": "^2.4.2", - "update-notifier": "^0.6.1" + "async-each-series": "^1.1.0", + "chalk": "^1.1.3", + "clui": "^0.3.1", + "commander": "^2.9.0", + "empty-dir": "^0.2.1", + "extend": "^3.0.0", + "glob": "^7.1.1", + "lodash.uniq": "^4.5.0", + "modclean-patterns-default": "^1.0.0", + "pad": "^1.0.2", + "rimraf": "^2.5.4", + "subdirs": "^1.0.0", + "update-notifier": "^1.0.3" }, "devDependencies": { - "mocha": "^2.2.5", - "should": "^7.0.4" - } + "mocha": "^3.2.0", + "should": "^11.1.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "engineStrict": true } From 37e5df984737a42558c5b255bfc052a1bba1dc0f Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 13:06:14 -0500 Subject: [PATCH 07/51] Fix aguments checking for process function --- lib/modclean.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index e37be62..5604136 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -192,8 +192,8 @@ class ModClean extends EventEmitter { } each(files, function(file, callback) { - if(processFn.length === 1) { - // If processFn has 1 argument (file), then assume sync + if(processFn.length <= 1) { + // If processFn has 0 or 1 argument (file), then assume sync if(processFn(file) !== false) self._deleteFile(file, (err, result) => { if(!err) results.push(file); callback(err); From 93988a19263912346df1ef8bd87b9dad0d4ae6f8 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 15:44:37 -0500 Subject: [PATCH 08/51] Updated benchmarks for 2017 --- BENCHMARK.md | 36 ++++++++++++++++++------------------ README.md | 32 ++++++++++++++++---------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/BENCHMARK.md b/BENCHMARK.md index a4fc83b..68fd6f3 100644 --- a/BENCHMARK.md +++ b/BENCHMARK.md @@ -1,40 +1,40 @@ # ModClean Benchmarks -This is an additional list of benchmarks to show other common modules/frameworks. All benchmark results are from [modclean-benchmark](https://github.com/KyleRoss/modclean-benchmark). Feel free to create your own benchmarks and submit them in this file through a pull request. +This is an additional list of benchmarks to show other common modules/frameworks. All benchmark results are from [modclean-benchmark](https://github.com/ModClean/modclean-benchmark). Feel free to create your own benchmarks and submit them in this file through a pull request. ### `npm install express lodash moment async` Example of standard modules used to create basic web applications. - modclean-benchmark -m express,lodash,moment,async -n safe + modclean-benchmark -m express,lodash,moment,async -n default:safe | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 753 | 81 | 5.13 MB | -| After ModClean | 612 | 78 | 4.76 MB | -| Reduced | **141** | **3** | **382.85 KB** | +| Before ModClean | 1,745 | 76 | 4.90 MB | +| After ModClean | 612 | 78 | 4.45 MB | +| Reduced | **144** | **2** | **459.59 KB** | ### `npm install grunt grunt-contrib-clean grunt-contrib-concat grunt-contrib-copy grunt-contrib-uglify` Example of a standard grunt installation. - modclean-benchmark -m grunt,grunt-contrib-clean,grunt-contrib-concat,grunt-contrib-copy,grunt-contrib-uglify -n safe + modclean-benchmark -m grunt,grunt-contrib-clean,grunt-contrib-concat,grunt-contrib-copy,grunt-contrib-uglify -n default:safe | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 1,402 | 291 | 8.99 MB | -| After ModClean | 859 | 226 | 6.28 MB | -| Reduced | **543** | **65** | **2.71 MB** | +| Before ModClean | 3,305 | 320 | 8.59 MB | +| After ModClean | 2,782 | 231 | 7.14 MB | +| Reduced | **523** | **89** | **1.44 MB** | ### `npm install lodash commander colors async express q underscore debug coffee-script request chalk mkdirp` Example of the most depended-upon modules from [npmjs.com](https://www.npmjs.com/) (4/28/2015). - modclean-benchmark -m lodash,commander,colors,async,express,q,underscore,debug,coffee-script,request,chalk,mkdirp -n safe + modclean-benchmark -m lodash,commander,colors,async,express,q,underscore,debug,coffee-script,request,chalk,mkdirp -n default:safe | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 1,285 | 222 | 5.09 MB | -| After ModClean | 819 | 190 | 3.62 MB | -| Reduced | **466** | **32** | **1.48 MB** | +| Before ModClean | 2,148 | 222 | 6.20 MB | +| After ModClean | 1,620 | 191 | 4.59 MB | +| Reduced | **528** | **31** | **1.61 MB** | --- @@ -42,12 +42,12 @@ Example of the most depended-upon modules from [npmjs.com](https://www.npmjs.com ## Large Application Examples ### Application Framework Example -At the company I work for, our homegrown application framework uses a module set similar to the below: +At the company I used to work for, our homegrown application framework uses a module set similar to the below: - modclean-benchmark -m async,body-parser,colors,compression,cookie-parser,edge,express,express-debug,express-session,fs-extra,glob,humanize,jade,js-yaml,less,mime,moment,mongoose,ms,multer,node-uuid,numeral,on-finished,serve-favicon,lodash -n safe + modclean-benchmark -m async,body-parser,colors,compression,cookie-parser,edge,express,express-debug,express-session,fs-extra,glob,humanize,jade,js-yaml,less,mime,moment,mongoose,ms,multer,node-uuid,numeral,on-finished,serve-favicon,lodash -n default:safe | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 5,905 | 1,265 | 84.53 MB | -| After ModClean | 2,730 | 824 | 36.76 MB | -| Reduced | **3,175** | **441** | **47.76 MB** | +| Before ModClean | 5,848 | 946 | 32.81 MB | +| After ModClean | 3,455 | 628 | 26.19 MB | +| Reduced | **2,393** | **318** | **6.62 MB** | diff --git a/README.md b/README.md index 8ce7d91..f6e60fb 100644 --- a/README.md +++ b/README.md @@ -17,34 +17,34 @@ This module has been heavily tested in an enterprise environment used for large ### Removal Benchmark So how well does this module work? If we `npm install sails` and run ModClean on it, here are the results: -_All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v3.10.8_ +_All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v4.0.5_ #### Using Default Safe Patterns `modclean --empty-dirs -n default:safe` or `modclean -d` -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ----------- | -| Before ModClean | 16,126 | 1,942 | 118 MB | -| After ModClean | 12,139 | 1,504 | 95 MB | -| Reduced | **3,987** | **438** | **23 MB** | +| | Total Files | Total Folders | Total Size | +| --------------- | ----------- | ------------- | ------------ | +| Before ModClean | 16,179 | 1,941 | 71.24 MB | +| After ModClean | 12,192 | 1,503 | 59.35 MB | +| Reduced | **3,987** | **438** | **11.88 MB** | #### Using Safe and Caution Patterns `modclean --empty-dirs -n default:safe,default:caution` -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ----------- | -| Before ModClean | 16,126 | 1,942 | 118 MB | -| After ModClean | 11,888 | 1,474 | 90 MB | -| Reduced | **4,238** | **468** | **28 MB** | +| | Total Files | Total Folders | Total Size | +| --------------- | ----------- | ------------- | ------------ | +| Before ModClean | 16,179 | 1,941 | 71.24 MB | +| After ModClean | 11,941 | 1,473 | 55.28 MB | +| Reduced | **4,238** | **468** | **15.95 MB** | #### Using Safe, Caution and Danger Patterns `modclean --empty-dirs --patterns="default:*"` -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ----------- | -| Before ModClean | 16,126 | 1,942 | 118 MB | -| After ModClean | 11,691 | 1,449 | 86 MB | -| Reduced | **4,435** | **493** | **32 MB** | +| | Total Files | Total Folders | Total Size | +| --------------- | ----------- | ------------- | ------------ | +| Before ModClean | 16,179 | 1,941 | 71.24 MB | +| After ModClean | 11,684 | 1,444 | 51.76 MB | +| Reduced | **4,495** | **497** | **19.47 MB** | That makes a huge difference in the amount of files and disk space. From 99c16de8f931ea4fca5a679de27e0c5fc18abab2 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 15:45:03 -0500 Subject: [PATCH 09/51] Changed `-d` option --- bin/modclean.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/modclean.js b/bin/modclean.js index 8387797..0a504f4 100644 --- a/bin/modclean.js +++ b/bin/modclean.js @@ -44,7 +44,7 @@ program .option('-I, --ignore ', 'Comma separated list of patterns to ignore', list) .option('--no-dirs', 'Exclude directories from being removed') .option('--no-dotfiles', 'Exclude dot files from being removed') - .option('-d, --empty', 'Remove empty directories') + .option('-k, --keep-empty', 'Keep empty directories') .parse(process.argv); class ModClean_CLI { @@ -106,7 +106,7 @@ class ModClean_CLI { noDirs: !program.dirs, dotFiles: !!program.dotfiles, errorHalt: !!program.errorHalt, - removeEmptyDirs: !!program.empty, + removeEmptyDirs: !program.keepEmpty, ignoreCase: !program.caseSensitive, test: !!program.test, process: function(file, cb) { From ee63bfa04e818b2fa7f3ef114fb09587a667e801 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Wed, 11 Jan 2017 15:45:13 -0500 Subject: [PATCH 10/51] More changes --- HISTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index c307e71..0b9bd1b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -31,8 +31,10 @@ ### ModClean CLI Changes * Rewrite using ES6 with new features and bug fixes. * **Breaking Change:** `-n, --patterns` option now takes list of plugins instead of a pattern rule name. +* **Removed:** `-d, --empty` option. * **New!** Added `-D, --modules-dir` option. * **New!** Added `-a, --additional-patterns` option. +* **New!** Added `-k, --keep-empty` option. * **New!** Added `--no-dotfiles` option. * Removed `inquirer` dependency in favor of `clui` and utility functions. * Reduced memory usage of the CLI. From 87ebdcb6db219e072e8e7e7171fa3d7e91859ef9 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 10:58:49 -0500 Subject: [PATCH 11/51] Added more benchmarks --- BENCHMARK.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/BENCHMARK.md b/BENCHMARK.md index 68fd6f3..574acb8 100644 --- a/BENCHMARK.md +++ b/BENCHMARK.md @@ -36,6 +36,37 @@ Example of the most depended-upon modules from [npmjs.com](https://www.npmjs.com | After ModClean | 1,620 | 191 | 4.59 MB | | Reduced | **528** | **31** | **1.61 MB** | +### `npm install lodash express debug request chalk moment async bluebird react underscore commander mkdirp` +Example of the most depended-upon modules from [npmjs.com](https://www.npmjs.com/) (1/11/2017). + + modclean-benchmark -m lodash,express,debug,request,chalk,moment,async,bluebird,react,underscore,commander,mkdirp -n default:safe + +| | Total Files | Total Folders | Total Size | +| --------------- | ----------- | ------------- | ------------- | +| Before ModClean | 3,770 | 309 | 11.29 MB | +| After ModClean | 3,175 | 275 | 9.20 MB | +| Reduced | **595** | **34** | **2.08 MB** | + +### `npm install browserify express pm2 grunt-cli npm karma bower cordova coffee-script gulp forever statsd grunt less yo` +Example of the top modules from [npmjs.com](https://www.npmjs.com/) (1/11/2017). + + modclean-benchmark -m browserify,express,pm2,grunt-cli,npm,karma,bower,cordova,coffee-script,gulp,forever,statsd,grunt,less,yo -n default:safe + +| | Total Files | Total Folders | Total Size | +| --------------- | ----------- | ------------- | ------------- | +| Before ModClean | 33,942 | 5,643 | 124.00 MB | +| After ModClean | 21,074 | 3,695 | 88.57 MB | +| Reduced | **12,868** | **1,948** | **35.43 MB** | + +#### With all patterns... + + modclean-benchmark -m browserify,express,pm2,grunt-cli,npm,karma,bower,cordova,coffee-script,gulp,forever,statsd,grunt,less,yo --patterns="default:*" + +| | Total Files | Total Folders | Total Size | +| --------------- | ----------- | ------------- | ------------- | +| Before ModClean | 33,942 | 5,643 | 124.00 MB | +| After ModClean | 19,751 | 3,544 | 65.55 MB | +| Reduced | **14,191** | **2,099** | **58.47 MB** | --- From 4a45be51e0551657c0f7f4ed576e462f34f3558c Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 10:58:57 -0500 Subject: [PATCH 12/51] Indentation --- lib/modclean.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modclean.js b/lib/modclean.js index 5604136..3f34f0f 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -103,7 +103,7 @@ class ModClean extends EventEmitter { this._patterns = this.utils.initPatterns(this.options); if(this.options.modulesDir !== false && path.basename(this.options.cwd) !== this.options.modulesDir) - this.options.cwd = path.join(this.options.cwd, this.options.modulesDir); + this.options.cwd = path.join(this.options.cwd, this.options.modulesDir); if(cb) this.clean(cb); } From 062667d4d6452dffbe6ce90288c1a6d221942864 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 10:59:09 -0500 Subject: [PATCH 13/51] Updated documentation --- README.md | 401 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 292 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index f6e60fb..ed086f6 100644 --- a/README.md +++ b/README.md @@ -54,26 +54,40 @@ View additional benchmarks in [BENCHMARK.md](https://github.com/ModClean/modclea Install locally - npm install modclean --save +```bash +npm install modclean --save +``` Install globally (CLI) - npm install modclean -g - +```bash +npm install modclean -g +``` ## CLI Usage If you want to use this module as a tool, you can use the provided CLI utility. After installing globally, you will now have access to the command `modclean`. There are several options available to customize how it should run. All options listed below are optional. ### Usage - modclean [-tsievrhV] [-p path] + modclean [-tsiPevrkhV] [-p, --path=string] [-D, --modules-dir=string] [-n, --patterns=list] [-a, --additional-patterns=list] [-I, --ignore=list] + +#### -p [path], --path [string] +Provide a different path to run ModClean in. By default, it uses `process.cwd()`. The path **must** be in a directory that contains a `node_modules` directory. + +#### -D, --modules-dir [string] +Change the default modules directory name. Default is `node_modules`. + +#### -n, --patterns [list] +Specify which pattern plugins/rules to use. Separate multiple groups by a single comma (no spaces). Default is `default:safe`. +Example: `modclean -n default:safe,default:caution` -#### -p [path], --path [path] -Provide a different path to run ModClean in. By default, it uses `process.cwd()`. The path **must** either be inside a `node_modules` directory or in a directory that contains a `node_modules` folder. +#### -a, --additional-patterns [list] +Specify custom glob patterns to be included in the search. +Example: `modclean --additional-patterns="*history,*.html,.config"` -#### -n, --patterns [patterns] -Specify which group(s) of patterns to use. Can be `safe`, `caution` or `danger`. Separate multiple groups by a single comma (no spaces). Default is `safe`. -Example: `modclean -n safe,caution` +#### -I, --ignore [list] +Comma-separated list of glob patterns to ignore during cleaning. Useful when a pattern matches a module name you do not want removed. +Example: `modclean --ignore="validate-npm-package-license,*history*"` #### -t, --test Run in test mode which will do everything ModClean does except delete the files. It's good practice to run this first to analyze the files that will be deleted. @@ -87,14 +101,14 @@ Run in interactive mode. For each file found, you will be prompted whether you w #### -P, --no-progress Turns off the progress bar when files are being deleted. -#### -I, --ignore -Comma-separated list of glob patterns to ignore during cleaning. - #### --no-dirs Do not delete directories, only files. -#### -d, --empty -Delete all empty directories after the cleanup process. Does not prompt for deletion when in `--interactive` mode. +#### --no-dotfiles +Exclude dot files from being deleted. + +#### -k, --keep-empty +Exclude empty directories from being deleted. #### -e, --error-halt Whether to halt the process when an error is encountered. The process is only halted when there is an issue deleting a file due to permissions or some other catastrophic issue. @@ -118,62 +132,70 @@ You can also use ModClean programmically so you can include it into your own uti ### Examples - // Require modclean module - var modclean = require('modclean'); +```js +// Require modclean module +const modclean = require('modclean'); +``` Run the basic ModClean process with a callback function when completed. - modclean(function(err, results) { - if(err) return console.error(err); - - console.log('Deleted Files Total:', results.length); - }); +```js +modclean(function(err, results) { + if(err) return console.error(err); + + console.log('Deleted Files Total:', results.length); +}); +``` Run the basic ModClean process with conditional file skipping. - modclean({ - process: function(file, files) { - // Skip .gitignore files - if(file.match(/\.gitignore/i)) { - return false; - } - - return true; +```js +modclean({ + process: function(file, files) { + // Skip .gitignore files + if(file.match(/\.gitignore/i)) { + return false; } - }).clean(function(err, results) { - if(err) return console.error(err); - console.log('Deleted Files Total:', results.length); - }); + return true; + } +}).clean(function(err, results) { + if(err) return console.error(err); + + console.log('Deleted Files Total:', results.length); +}); +``` More advanced usage. - var path = require('path'); - - var MC = new modclean.ModClean({ - // Define a custom path - cwd: path.join(process.cwd(), 'myApp/node/node_modules'), - // Only delete patterns.safe patterns along with html and png files - patterns: [modclean.patterns.safe, '*.html', '*.png'], - // Run in test mode so no files are deleted - test: true - }); +```js +const path = require('path'); - MC.on('deleted', function(file) { - // For every file deleted, log it - console.log((MC.options.test? 'TEST' : ''), file, 'deleted from filesystem'); - }); +let MC = new modclean.ModClean({ + // Define a custom path + cwd: path.join(process.cwd(), 'myApp/node/node_modules'), + // Only delete patterns.safe patterns along with html and png files + patterns: [modclean.patterns.safe, '*.html', '*.png'], + // Run in test mode so no files are deleted + test: true +}); + +MC.on('deleted', function(file) { + // For every file deleted, log it + console.log((MC.options.test? 'TEST' : ''), file, 'deleted from filesystem'); +}); + +// Run the cleanup process without using the 'clean' function +MC._find(null, function(err, files) { + if(err) return console.error('Error while searching for files', err); - // Run the cleanup process without using the 'clean' function - MC._find(null, function(err, files) { - if(err) return console.error('Error while searching for files', err); + MC._process(files, function(err, results) { + if(err) return console.error('Error while processing files', err); - MC._process(files, function(err, results) { - if(err) return console.error('Error while processing files', err); - - console.log('Deleted Files Total:', results.length); - }); + console.log('Deleted Files Total:', results.length); }); +}); +``` ### Options The options below can be used to modify how ModClean works. @@ -183,8 +205,16 @@ The options below can be used to modify how ModClean works. The path in which ModClean should recursively search through to find files to remove. If the path does not end with `options.modulesDir`, it will be appended to the path, allowing this script to run in the parent directory. #### patterns -*(Array)* **Default** `modclean.patterns.safe` (see patterns.json file) -Patterns to use as part of the search. These patterns are concatenated into a regex string and passed into `glob`. Anything allowed in `glob` can be used in the patterns. This option can also be an array of arrays in which will be flattened. +*(Array[string])* **Default** `["default:safe"]` +Patterns plugins/rules to use. Each value is either the full plugin module name (ex. `modclean-patterns-pluginname`) or just the last section of the module name (ex. `pluginname`). Plugins will usually have different rules to use, you can specify the rule name by appending a colon ':' and the rule name (ex. `pluginname:rule`). If a rule name is not provided, it will load the first rule found on the plugin. If you want to use all rules, you can use an asterisk as the rule name (ex. `pluginname:*`). By default, [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) is included. If you want to create your own plugin, see [Custom Patterns Plugins](#custom-pattern-plugins) below. + +#### additionalPatterns +*(Array[string])* **Default** `[]` +Additional custom `glob` patterns to include in the search. This will allow further customization without the need of creating your own patterns plugin. + +#### ignorePatterns +*(Array[string])* **Default** `[]` +Custom `glob` patterns to ignore during the search. Allows skipping matched items that would normally be removed, which is good for patterns that match existing module names you wish not to be removed. #### ignoreCase *(Boolean)* **Default** `true` @@ -192,7 +222,7 @@ Whether `glob` should ignore the case of the file names when searching. If you n #### process *(Function)* **Default:** `null` -Optional function to call before each file is deleted. This function can be used asynchronously or synchronously depending on the number of parameters provided. If the provided function has 1 or 2 parameters `function(file, files)`, it is synchronous, if it has 3 parameters `function(file, files, cb)`, it is asynchronous. When sync, you can `return false` to skip the current file being processed, otherwise when async, you can call the callback function `cb(false)` to skip the file. The **file** parameter is the current path with the filename appened of the file being processed. The **files** parameter is the full array of all the files. +Optional function to call before each file is deleted. This function can be used asynchronously or synchronously depending on the number of parameters provided. If the provided function has 0 or 1 parameters `function(file)`, it is synchronous, if it has 2 parameters `function(file, cb)`, it is asynchronous. When sync, you can `return false` to skip the current file being processed, otherwise when async, you can call the callback function `cb(false)` to skip the file. The **file** parameter is the current path with the filename appened of the file being processed. #### modulesDir *(String|Boolean)* **Default:** `"node_modules"` @@ -202,14 +232,14 @@ The modules directory name to use when looking for modules. This is only used wh *(Boolean)* **Default:** `true` Whether to remove empty directories after the cleanup process. This is usually a safe option to use. -#### ignore -*([String])* **Default:** `null` -Array of glob patterns (strings) to ignore while running the deletion process. - #### noDirs -*(Boolean) **Default:** `false` +*(Boolean)* **Default:** `false` Set to `true` to skip directories from being deleted during the cleaning process. +#### dotFiles +*(Boolean)* **Default** `true` +Set to `false` to skip dot files from being deleted during the cleaning process. + #### errorHalt *(Boolean)* **Default:** `false` Whether the script should exit with a filesystem error if one is encountered. This really only pertains to systems with complex permissions or Windows filesystems. The `rimraf` module will only throw an error if there is actually an issue deleting an existing file. If the file doesn't exist, it does not throw an error. @@ -220,65 +250,114 @@ Whether to run in test mode. If set to `true` everything will run (including all ### Methods and Properties -These are the methods and properties returned when calling `var modclean = require('modclean');`. +These are the methods and properties exported when calling `const modclean = require('modclean');`. #### modclean([options][,cb]) Create a new `ModClean` instance. It's the same as calling `new modclean.ModClean()`. If a callback function is provided, it will automatically call the `clean()` method and therefore `clean()` should not be called manually. If you need to set event listeners, set the callback function in the `clean()` method instead. -**options** *(Object)* - Options to configure how ModClean works. (Optional) -**cb** *(Function)* - Callback function to call once the process is completed `function(err, results)`. The `results` parameter contains an array of all the files that were successfully remove from the filesystem. +| Argument | Type | Required? | Description | Default | +|-----------|----------|-----------|------------------------------------------------------------------------------------------------------------------------|---------| +| `options` | Object | No | Optional options object to configure ModClean | `{}` | +| `cb` | Function | No | Optional callback function to call once cleaning complete. If not provided, `clean()` will not be called automatically | `null` | + +```js +const modclean = require('modclean'); + +modclean(function(err, results) { + // called once cleaning is complete. + if(err) { + console.error(err); + return; + } + + console.log(`${results.length} files removed!`); +}); +``` #### modclean.defaults *(Object)* - The default options used in all created ModClean instances. You may change the defaults at anytime if you will be creating multiple instances that need to use the same options. -#### modclean.patterns -*(Object)* - The full list of patterns provided in `patterns.json`. This returns 3 properties (`safe`, `caution`, `danger`) which determines the level of file removal. - #### modclean.ModClean([options][,cb]) -Create instance of the `ModClean` class. The parameters are the same as `modclean()`. The only difference between this and `modclean()` is that this must be called with `new`. +Access to the ModClean class constructor. + +### ModClean Class + +#### ModClean([options][,cb]) +Create instance of the `ModClean` class. Must be called with `new`. - var modclean = require('modclean'); +| Argument | Type | Required? | Description | Default | +|-----------|----------|-----------|------------------------------------------------------------------------------------------------------------------------|---------| +| `options` | Object | No | Optional options object to configure ModClean | `{}` | +| `cb` | Function | No | Optional callback function to call once cleaning complete. If not provided, `clean()` will not be called automatically | `null` | + +```js +const ModClean = require('modclean').ModClean; - // Create new instance - var MC = new modclean.ModClean(); +// Create new instance +let MC = new ModClean(); +``` + +#### clean([cb]) +Runs the ModClean process. Only needs to be called if a callback function is not provided to the `ModClean()` constructor. + +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| `cb` | Function | No | Optional callback function to call once cleaning complete. Called with `err` (error message if one occurred) and `results` (array of file paths removed) | `null` | -#### modclean.ModClean().clean([cb]) -Runs the ModClean process. Only needs to be called if a callback function is not provided to `modclean.ModClean()`. +#### cleanEmptyDirs(cb) +Finds all empty directories and deletes them from `options.cwd`. -**cb** *(Function)* - Callback function to call once the process is completed `function(err, results)`. The `results` parameter contains an array of all the files that were successfully remove from the filesystem. +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------|---------| +| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of directories deleted) | | -#### modclean.ModClean()._find(patterns, cb) -Internally used by ModClean to search for files based on the provided patterns. +#### _find(cb) +Internally used by ModClean to search for files based on the loaded patterns/rules. -**patterns** *(Array|null)* - Patterns to use for the search process. If set to `null`, it will default to `options.patterns`. -**cb** *(Function)* - Callback function to call once the search process is completed with an array of file paths `function(err, files)`. +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------|---------| +| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `files` (array of file paths found) | | -#### modclean.ModClean()._process(files, cb) -Internally used by ModClean to process each of the files. The processing includes running `options.process` and then calling `ModClean()._deleteFile()`. +#### _process(files, cb) +Internally used by ModClean to process each of the files. The processing includes running `options.process` and then calling `_deleteFile()`. -**files** *(Array)* - Array of file paths to process and send for deletion. -**cb** *(Function)* - Callback function to call once processing and deletion is complete `function(err, results)`. The results parameter contains an array of files that were successfully deleted (does not include skipped files). +| Argument | Type | Required? | Description | Default | +|----------|---------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| `files` | Array[String] | Yes | Array of file paths to be deleted. | | +| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of file paths deleted) | | -#### modclean.ModClean()._deleteFile(file, cb) +#### _deleteFile(file, cb) Internally used by ModClean to delete a file at the given path. -**file** *(String)* - File path to be deleted. Should not include `options.cwd` as it will be prepended. -**cb** *(Function)* - Callback function to be called once the file is deleted `function(err, file)`. The callback will not receive an error if `options.errorHalt = false`. +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| `file` | String | Yes | File path to be deleted | | +| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `files` (the file path deleted). The callback will not receive an error if `options.errorHalt = false` | | -#### modclean.ModClean()._removeEmpty(cb) -Internally used by ModClean to delete all empty directories within `options.cwd`. +#### _findEmptyDirs(cb) +Internally used by ModClean to find all empty directories within `options.cwd`. -**cb** *(Function)* - Callback function to be called once all empty directories have been deleted `function(err, results)`. +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------|---------| +| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of directories found) | | -#### modclean.ModClean().options -Compiled options object used by the ModClean instance. +#### _removeEmptyDirs(dirs, cb) +Internally used by ModClean to delete all empty directories provided. + +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------|---------| +| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of directories deleted) | | -#### modclean.ModClean().on(event, fn) -Creates an event handler on the ModClean instance. +#### on(event, fn) +Creates an event handler on the ModClean instance using `EventEmitter`. -**event** *(String)* - Any of the event names the are listed in the events section below. -**fn** *(Function)* - Function to call when the specified event is emitted. +| Argument | Type | Required? | Description | Default | +|----------|----------|-----------|-------------------------------------------------------|---------| +| `event` | String | Yes | Event name to listen to (events are documented below) | | +| `fn` | Function | Yes | Function to call once the specified event is emitted | | +#### options +Compiled options object used by the ModClean instance. ### Events The following events are emitted from the `ModClean` instance. @@ -286,39 +365,146 @@ The following events are emitted from the `ModClean` instance. #### start Emitted at the beginning of `clean()`. -**inst** *(Object)* - Provides access to the current ModClean instance. +| Argument | Type | Description | +|----------|----------|------------------------------------| +| `inst` | ModClean | Access to the instance of ModClean | + +#### beforeFind +Emitted before `_find()` function starts. + +| Argument | Type | Description | +|------------|---------------|----------------------------------------------------| +| `patterns` | Array[String] | Compiled list of `glob` patterns that will be used | +| `globOpts` | Object | The configuration object being passed into `glob` | #### files Emitted once a list of all found files has been compiled from the `_find()` method. -**files** *(Array)* - Array of file paths found. +| Argument | Type | Description | +|----------|---------------|-----------------------------------------| +| `files` | Array[String] | Array of file paths found to be removed | + +#### process +Emitted at the start of the `_process()` function. + +| Argument | Type | Description | +|----------|---------------|-----------------------------------------| +| `files` | Array[String] | Array of file paths found to be removed | #### deleted Emitted each time a file has been deleted from the file system by the `_deleteFile()` method. -**file** *(String)* - The file path that has been deleted. +| Argument | Type | Description | +|----------|--------|----------------------------------------------| +| `file` | String | File path that has been successfully deleted | #### finish Emitted once processing and deletion of files has completed by the `_process()` method. -**results** *(Array)* - List of file paths that were successfully deleted from the file system (not including skipped files). +| Argument | Type | Description | +|-----------|---------------|---------------------------------------------------| +| `results` | Array[String] | List of file paths that were successfully removed | #### complete Emitted once the entire ModClean process has completed before calling the main callback function. -**err** *(Object|String|null)* - Error (if any) that was thrown during the process. -**results** *(Array)* - List of file paths that were successfully deleted from the file system (not including skipped files). +| Argument | Type | Description | +|-----------|---------------|---------------------------------------------------| +| `err` | Error | Error object if one occurred during the process | +| `results` | Array[String] | List of file paths that were successfully removed | #### fileError Emitted if there was an error thrown while deleting a file/folder. Will emit even if `options.errorHalt = false`. -**err** *(Object|String)* - Error thrown by `rimraf`. -**file** *(String)* - File path of the file/folder that caused the error. +| Argument | Type | Description | +|----------|--------|--------------------------------| +| `err` | Error | Error object | +| `file` | String | The file that caused the error | #### error -Emitted if there was an error thrown somewhere in the module. +Emitted if there was an error thrown while searching for files. + +| Argument | Type | Description | +|----------|-------|--------------| +| `err` | Error | Error object | + +#### beforeEmptyDirs +Emitted before finding/removing empty directories. + +#### afterEmptyDirs +Emitted after finding/removing empty directories. + +| Argument | Type | Description | +|-----------|---------------|----------------------------------| +| `results` | Array[String] | Array of paths that were removed | + +#### emptyDirs +Emitted after a list of empty directories is found. + +| Argument | Type | Description | +|-----------|---------------|--------------------------------| +| `results` | Array[String] | Array of paths that were found | + +#### deletedEmptyDir +Emitted after an empty directory is deleted. + +| Argument | Type | Description | +|----------|--------|-------------------------------------| +| `dir` | String | The directory path that was deleted | -**err** *(Object|String)* - The error that was thrown. +#### emptyDirError +Emitted if an error occurred while deleting an empty directory. + +| Argument | Type | Description | +|----------|--------|-----------------------------------------| +| `dir` | String | The directory path that caused an error | +| `err` | Error | Error object thrown | + +--- + +## Custom Patterns Plugins +New in version 2.x, ModClean now supports pattern plugins to allow you to use various sets of patterns, along with custom ones. By default, ModClean comes with [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) preinstalled, but you can install or create your own plugins. + +### Installing 3rd Party Plugins +If you would like to use a 3rd party plugin, it's pretty simple to install. If you are using the CLI, the plugin should be installed globally (ex. `npm install -g modclean-patterns-pluginname`), otherwise programmatically, install locally (ex. `npm install modclean-patterns-pluginname --save`). + +### Available 3rd Party Plugins +*(None available yet!)* + +### Create Your Own +Creating your own patterns plugin is simple. It's a basic Node Module that must be prefixed with `modclean-patterns-` that is published to NPM. The module just needs to export and object that contains the pattern definitions (see [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) for an example). + +```js +module.exports = { + $default: 'basic', + + basic: { + patterns: [ + // ... glob patterns here + ], + ignore: [ + // ... glob patterns to ignore here + ] + } + + ], + + advanced: { + patterns: [ + // ... glob patterns here + ], + ignore: [ + // ... glob patterns to ignore here + ] + } +}; +``` + +Each key in the object is a rule name that is an object containing `patterns` and `ignore` arrays of glob patterns. The `patterns` section is glob patterns that will be found and removed and the `ignore` section is glob patterns to be ignored. Both keys are required, but can be empty arrays. + +An optional configuration parameter `$default` can be provided which tells ModClean the default rule to use if the user does not specify. If this is not provided, ModClean will use the first rule key it encounters. **Note:** Rules starting with `$` will be ignored. + +If you've created your own plugin, submit a pull request to add it to the list above! --- @@ -341,9 +527,6 @@ I'm not very picky on the code style as long as it roughly follows what is curre ### Run Tests If you are making a code change, please run the tests and ensure they pass before submitting a pull request. If you are adding new functionality, please ensure to write the tests for it. -### Patterns.json Changes -In case there are file patterns that were missed and this module could clean up additional files, feel free to submit a pull request adding the pattern. I will not accept pull reuqests that use wildcards on `.js` or `.json` files. If you notice a pattern that is causing issues with a particular module, submit a pull request or issue. There are 3 sections to the `patterns.json` file: (`safe`, `caution` and `danger`). Each of these sections determine the level of files to remove which includes additional patterns that match file/folder names. Safe patterns contain absolutely useless files that can be safely removed whereas caution and danger patterns are ones in which could cause issues with certain modules but will help significantly clean up more files. - --- ## License From 65be88050a3bf04322d97a80a7b0b7897520fe12 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 12:39:59 -0500 Subject: [PATCH 14/51] Removing tests for now --- README.md | 8 -- package.json | 4 - test.js | 395 --------------------------------------------------- 3 files changed, 407 deletions(-) delete mode 100644 test.js diff --git a/README.md b/README.md index ed086f6..183d51e 100644 --- a/README.md +++ b/README.md @@ -508,11 +508,6 @@ If you've created your own plugin, submit a pull request to add it to the list a --- -## Tests -This module has a good number of tests written for it that should cover most (if not all cases). If you would like to run the tests, please install `mocha` globally and install the `devDependencies` for this module. You can run the tests by calling `npm test`. The tests are ran using this modules own `node_modules` folder. - ---- - ## Issues If you find any bugs with either ModClean or the CLI Utility, please feel free to open an issue. Any feature requests may also be poseted in the issues. @@ -524,9 +519,6 @@ If you would like to contribute to this project, please ensure you follow the gu ### Code Style I'm not very picky on the code style as long as it roughly follows what is currently written in the modules. Just ensure that lines that should end with semi-colons do end with them. Comments are also very helpful for future contributors to know what's going on. -### Run Tests -If you are making a code change, please run the tests and ensure they pass before submitting a pull request. If you are adding new functionality, please ensure to write the tests for it. - --- ## License diff --git a/package.json b/package.json index dd2081c..ef97d9c 100644 --- a/package.json +++ b/package.json @@ -48,10 +48,6 @@ "subdirs": "^1.0.0", "update-notifier": "^1.0.3" }, - "devDependencies": { - "mocha": "^3.2.0", - "should": "^11.1.2" - }, "engines": { "node": ">=6.9.0" }, diff --git a/test.js b/test.js deleted file mode 100644 index c762975..0000000 --- a/test.js +++ /dev/null @@ -1,395 +0,0 @@ -/** - * ModClean - Tests - * @requires mocha (npm install mocha -g) - * @author Kyle Ross - */ - -/*global describe, it, before, beforeEach */ - -var should = require('should'), - path = require('path'), - modclean = require('./index.js'); - -describe('modclean', function() { - it('should be a function', function() { - modclean.should.be.a.Function; - }); - - describe('defaults', function() { - var patterns; - - before('load patterns.json', function() { - patterns = require('./patterns.json'); - }); - - it('should be an object', function() { - modclean.defaults.should.be.an.instanceOf(Object); - }); - - it('should have correct cwd', function() { - modclean.defaults.cwd.should.equal(process.cwd()); - }); - - it('should contain patterns from patterns.json', function() { - modclean.defaults.patterns.should.match(patterns.safe); - }); - }); - - describe('patterns', function() { - var patterns; - - before('load patterns.json', function() { - patterns = require('./patterns.json'); - }); - - it('should be an object', function() { - modclean.patterns.should.be.an.instanceOf(Object); - }); - - it('should contain patterns from patterns.json', function() { - modclean.patterns.should.match(patterns); - }); - - describe('safe', function() { - it('should contain patterns', function() { - modclean.patterns.should.have.property('safe'); - modclean.patterns.safe.length.should.be.greaterThan(0); - }); - }); - - describe('caution', function() { - it('should contain patterns', function() { - modclean.patterns.should.have.property('caution'); - modclean.patterns.caution.length.should.be.greaterThan(0); - }); - }); - - describe('danger', function() { - it('should contain patterns', function() { - modclean.patterns.should.have.property('danger'); - modclean.patterns.danger.length.should.be.greaterThan(0); - }); - }); - }); - - describe('ModClean', function() { - it('should be a function', function() { - modclean.ModClean.should.be.a.Function; - }); - - it('should have EventEmitter', function() { - modclean.ModClean.super_.name.should.equal('EventEmitter'); - }); - - it('should have clean method', function() { - modclean.ModClean.prototype.clean.should.be.a.Function; - }); - - it('should have _find method', function() { - modclean.ModClean.prototype._find.should.be.a.Function; - }); - - it('should have _process method', function() { - modclean.ModClean.prototype._process.should.be.a.Function; - }); - - it('should have _deleteFile method', function() { - modclean.ModClean.prototype._deleteFile.should.be.a.Function; - }); - }); -}); - -describe('ModClean instance', function() { - this.timeout(3000); - - describe('options', function() { - it('should have default options', function() { - var opts = new modclean.ModClean().options; - opts.cwd = process.cwd(); - opts.should.match(modclean.defaults); - }); - - describe('custom', function() { - var MC, - customPatterns = ['a', 'b', 'c']; - - before('set custom options', function() { - MC = new modclean.ModClean({ - patterns: customPatterns, - ignoreCase: false, - process: function() {}, - modulesDir: 'nm', - errorHalt: true, - test: true - }); - }); - - it('should have custom patterns', function() { - MC.options.patterns.should.match(customPatterns); - }); - - it('should flatten array of arrays for patterns', function() { - var _patterns = []; - _patterns = _patterns.concat(modclean.patterns.safe, modclean.patterns.caution); - - var MC = new modclean.ModClean({ - patterns: [modclean.patterns.safe, modclean.patterns.caution] - }); - - MC.options.patterns.should.match(_patterns); - }); - - it('should ignore case', function() { - MC.options.ignoreCase.should.be.false; - }); - - it('should have process function', function() { - MC.options.process.should.be.a.Function; - }); - - it('should have modulesDir appended to cwd', function() { - MC.options.cwd.should.equal(path.join(process.cwd(), MC.options.modulesDir)); - }); - - it('should errorHalt', function() { - MC.options.errorHalt.should.be.true; - }); - - it('should be in test mode', function() { - MC.options.test.should.be.true; - }); - - it('should go back to defaults in a new instance', function() { - var NMC = new modclean.ModClean(); - NMC.options.should.not.match(MC.options); - }); - }); - }); - - describe('constructor', function() { - it('should run clean if callback provided', function(done) { - var MC = new modclean.ModClean({ test: true }, function(err, results) { - results.should.be.an.Array; - done(); - }); - }); - - it('should except callback as first parameter', function(done) { - modclean.defaults.test = true; - - var MC = new modclean.ModClean(function(err, results) { - results.should.be.an.Array; - modclean.defaults.test = false; - done(); - }); - }); - }); - - describe('complete event', function() { - it('should emit complete event', function(done) { - var MC = new modclean.ModClean({ test: true }); - - MC.on('complete', function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - done(); - }); - - MC.clean(); - }); - }); - - describe('method', function() { - describe('clean', function() { - it('should run the clean method and complete', function(done) { - var MC = new modclean.ModClean({ test: true }); - MC.clean.should.be.a.Function; - - MC.clean(function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - done(); - }); - }); - - it('should emit start event', function(done) { - var MC = new modclean.ModClean({ test: true }); - - MC.on('start', function(inst) { - inst.should.be.instanceOf(modclean.ModClean); - done(); - }); - - MC.clean(); - }); - }); - - describe('_find', function() { - it('should find all index.js files (custom patterns)', function(done) { - var MC = new modclean.ModClean({ test: true }); - MC._find.should.be.a.Function; - - MC._find(['index.js'], function(err, files) { - should.not.exist(err); - files.should.be.an.Array; - done(); - }); - }); - - it('should emit files event', function(done) { - var MC = new modclean.ModClean({ test: true }); - - MC.on('files', function(files) { - files.should.be.an.Array; - files.length.should.be.greaterThan(0); - done(); - }); - - MC._find(['index.js'], function() {}); - }); - }); - - describe('_process', function() { - it('should find all index.js files (custom patterns)', function(done) { - var MC = new modclean.ModClean({ test: true }); - MC._process.should.be.a.Function; - - MC._find(['index.js'], function(err, files) { - should.not.exist(err); - files.should.be.an.Array; - - MC._process(files, function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.length.should.equal(files.length); - done(); - }); - }); - }); - - it('should emit finish event', function(done) { - var MC = new modclean.ModClean({ test: true }); - - MC.on('finish', function(results) { - results.should.be.an.Array; - results.length.should.be.greaterThan(0); - done(); - }); - - MC._find(['index.js'], function(err, files) { - MC._process(files, function() {}); - }); - }); - }); - - describe('_deleteFile', function() { - it('should delete a file', function(done) { - var MC = new modclean.ModClean({ test: true }); - - MC._deleteFile('someFile.txt', function(err, file) { - should.not.exist(err); - file.should.be.a.String; - done(); - }); - }); - - it('should emit deleted event', function(done) { - var MC = new modclean.ModClean({ test: true }); - - MC.on('deleted', function(file) { - file.should.be.a.String; - done(); - }); - - MC._deleteFile('someFile.txt', function() {}); - }); - }); - }); - - describe('process function', function() { - describe('async', function() { - it('should call the process function', function(done) { - var count = 0; - - new modclean.ModClean({ - patterns: ['index.js'], - removeEmptyDirs: false, - process: function(file, files, cb) { - file.should.be.a.String; - files.should.be.an.Array; - cb.should.be.a.Function; - count++; - cb(true); - }, - test: true - }, function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.length.should.equal(count); - done(); - }); - }); - - it('should skip files', function(done) { - var count = 0; - - new modclean.ModClean({ - patterns: ['index.js'], - removeEmptyDirs: false, - process: function(file, files, cb) { - count++; - cb(false); - }, - test: true - }, function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.length.should.not.equal(count); - done(); - }); - }); - }); - - describe('sync', function() { - it('should call the process function', function(done) { - var count = 0; - - new modclean.ModClean({ - patterns: ['index.js'], - removeEmptyDirs: false, - process: function(file, files) { - file.should.be.a.String; - files.should.be.an.Array; - count++; - return true; - }, - test: true - }, function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.length.should.equal(count); - done(); - }); - }); - - it('should skip files', function(done) { - var count = 0; - - new modclean.ModClean({ - patterns: ['index.js'], - removeEmptyDirs: false, - process: function(file, files) { - count++; - return false; - }, - test: true - }, function(err, results) { - should.not.exist(err); - results.should.be.an.Array; - results.length.should.not.equal(count); - done(); - }); - }); - }); - }); -}); From d79e2c1d14a73549d61f659ca020d4b83d2967db Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 12:49:01 -0500 Subject: [PATCH 15/51] Remove build status badge for now --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 183d51e..a4400a9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ModClean *Remove unwanted files and directories from your node_modules folder* -[![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) [![Build Status](https://img.shields.io/travis/KyleRoss/modclean.svg)](https://travis-ci.org/KyleRoss/modclean) ![NPM Dependencies](https://david-dm.org/KyleRoss/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/KyleRoss/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/KyleRoss/modclean.svg)](https://github.com/KyleRoss/modclean/issues) [![Package Quality](http://npm.packagequality.com/shield/modclean.svg)](http://packagequality.com/#?package=modclean) +[![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) ![NPM Dependencies](https://david-dm.org/KyleRoss/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/KyleRoss/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/KyleRoss/modclean.svg)](https://github.com/KyleRoss/modclean/issues) [![Package Quality](http://npm.packagequality.com/shield/modclean.svg)](http://packagequality.com/#?package=modclean) ### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use ModClean 1.3.0 instead. From 48f8551014f97f5dd16549c45976291df76e9656 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 12:49:28 -0500 Subject: [PATCH 16/51] Remove Travis CI for now --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dbac2f4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: node_js -node_js: - - "6.1" -before_install: - - npm install -g npm -before_script: - - npm install -script: 'mocha test.js' -notifications: - email: false From a02bf82d920cc59572d897ff2de76c9929cffab4 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 12:50:21 -0500 Subject: [PATCH 17/51] Update repo url --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ef97d9c..b50a5e4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/KyleRoss/modclean" + "url": "https://github.com/ModClean/modclean" }, "keywords": [ "module", @@ -30,9 +30,9 @@ "author": "Kyle Ross", "license": "MIT", "bugs": { - "url": "https://github.com/KyleRoss/modclean/issues" + "url": "https://github.com/ModClean/modclean/issues" }, - "homepage": "https://github.com/KyleRoss/modclean", + "homepage": "https://github.com/ModClean/modclean", "dependencies": { "async-each-series": "^1.1.0", "chalk": "^1.1.3", From 2393f319b770de6e9e3cf35c30667698a5a89abc Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Thu, 12 Jan 2017 13:27:03 -0500 Subject: [PATCH 18/51] Fix example commands --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a4400a9..9c9780f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ So how well does this module work? If we `npm install sails` and run ModClean on _All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v4.0.5_ #### Using Default Safe Patterns -`modclean --empty-dirs -n default:safe` or `modclean -d` +`modclean -n default:safe` or `modclean` | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------ | @@ -29,7 +29,7 @@ _All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v4.0.5_ | Reduced | **3,987** | **438** | **11.88 MB** | #### Using Safe and Caution Patterns -`modclean --empty-dirs -n default:safe,default:caution` +`modclean -n default:safe,default:caution` | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------ | @@ -38,7 +38,7 @@ _All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v4.0.5_ | Reduced | **4,238** | **468** | **15.95 MB** | #### Using Safe, Caution and Danger Patterns -`modclean --empty-dirs --patterns="default:*"` +`modclean --patterns="default:*"` | | Total Files | Total Folders | Total Size | | --------------- | ----------- | ------------- | ------------ | From 370ab8d46d900ff7e66de8f1e173f92404eff8a2 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Fri, 13 Jan 2017 09:22:28 -0500 Subject: [PATCH 19/51] Updated badge urls --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c9780f..85dbf6e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ModClean *Remove unwanted files and directories from your node_modules folder* -[![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) ![NPM Dependencies](https://david-dm.org/KyleRoss/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/KyleRoss/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/KyleRoss/modclean.svg)](https://github.com/KyleRoss/modclean/issues) [![Package Quality](http://npm.packagequality.com/shield/modclean.svg)](http://packagequality.com/#?package=modclean) +[![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) ![NPM Dependencies](https://david-dm.org/ModClean/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/ModClean/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/ModClean/modclean.svg)](https://github.com/ModClean/modclean/issues) [![Package Quality](http://npm.packagequality.com/shield/modclean.svg)](http://packagequality.com/#?package=modclean) ### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use ModClean 1.3.0 instead. From 6b1bcceed5559670c8d9fdce53961072b3cb7a5f Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Fri, 13 Jan 2017 10:58:53 -0500 Subject: [PATCH 20/51] Moving benchmarks to Wiki --- BENCHMARK.md | 84 ---------------------------------------------------- README.md | 2 +- 2 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 BENCHMARK.md diff --git a/BENCHMARK.md b/BENCHMARK.md deleted file mode 100644 index 574acb8..0000000 --- a/BENCHMARK.md +++ /dev/null @@ -1,84 +0,0 @@ -# ModClean Benchmarks -This is an additional list of benchmarks to show other common modules/frameworks. All benchmark results are from [modclean-benchmark](https://github.com/ModClean/modclean-benchmark). Feel free to create your own benchmarks and submit them in this file through a pull request. - -### `npm install express lodash moment async` -Example of standard modules used to create basic web applications. - - modclean-benchmark -m express,lodash,moment,async -n default:safe - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 1,745 | 76 | 4.90 MB | -| After ModClean | 612 | 78 | 4.45 MB | -| Reduced | **144** | **2** | **459.59 KB** | - - -### `npm install grunt grunt-contrib-clean grunt-contrib-concat grunt-contrib-copy grunt-contrib-uglify` -Example of a standard grunt installation. - - modclean-benchmark -m grunt,grunt-contrib-clean,grunt-contrib-concat,grunt-contrib-copy,grunt-contrib-uglify -n default:safe - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 3,305 | 320 | 8.59 MB | -| After ModClean | 2,782 | 231 | 7.14 MB | -| Reduced | **523** | **89** | **1.44 MB** | - - -### `npm install lodash commander colors async express q underscore debug coffee-script request chalk mkdirp` -Example of the most depended-upon modules from [npmjs.com](https://www.npmjs.com/) (4/28/2015). - - modclean-benchmark -m lodash,commander,colors,async,express,q,underscore,debug,coffee-script,request,chalk,mkdirp -n default:safe - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 2,148 | 222 | 6.20 MB | -| After ModClean | 1,620 | 191 | 4.59 MB | -| Reduced | **528** | **31** | **1.61 MB** | - -### `npm install lodash express debug request chalk moment async bluebird react underscore commander mkdirp` -Example of the most depended-upon modules from [npmjs.com](https://www.npmjs.com/) (1/11/2017). - - modclean-benchmark -m lodash,express,debug,request,chalk,moment,async,bluebird,react,underscore,commander,mkdirp -n default:safe - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 3,770 | 309 | 11.29 MB | -| After ModClean | 3,175 | 275 | 9.20 MB | -| Reduced | **595** | **34** | **2.08 MB** | - -### `npm install browserify express pm2 grunt-cli npm karma bower cordova coffee-script gulp forever statsd grunt less yo` -Example of the top modules from [npmjs.com](https://www.npmjs.com/) (1/11/2017). - - modclean-benchmark -m browserify,express,pm2,grunt-cli,npm,karma,bower,cordova,coffee-script,gulp,forever,statsd,grunt,less,yo -n default:safe - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 33,942 | 5,643 | 124.00 MB | -| After ModClean | 21,074 | 3,695 | 88.57 MB | -| Reduced | **12,868** | **1,948** | **35.43 MB** | - -#### With all patterns... - - modclean-benchmark -m browserify,express,pm2,grunt-cli,npm,karma,bower,cordova,coffee-script,gulp,forever,statsd,grunt,less,yo --patterns="default:*" - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 33,942 | 5,643 | 124.00 MB | -| After ModClean | 19,751 | 3,544 | 65.55 MB | -| Reduced | **14,191** | **2,099** | **58.47 MB** | - ---- - -## Large Application Examples - -### Application Framework Example -At the company I used to work for, our homegrown application framework uses a module set similar to the below: - - modclean-benchmark -m async,body-parser,colors,compression,cookie-parser,edge,express,express-debug,express-session,fs-extra,glob,humanize,jade,js-yaml,less,mime,moment,mongoose,ms,multer,node-uuid,numeral,on-finished,serve-favicon,lodash -n default:safe - -| | Total Files | Total Folders | Total Size | -| --------------- | ----------- | ------------- | ------------- | -| Before ModClean | 5,848 | 946 | 32.81 MB | -| After ModClean | 3,455 | 628 | 26.19 MB | -| Reduced | **2,393** | **318** | **6.62 MB** | diff --git a/README.md b/README.md index 85dbf6e..547db04 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ _All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v4.0.5_ That makes a huge difference in the amount of files and disk space. -View additional benchmarks in [BENCHMARK.md](https://github.com/ModClean/modclean/blob/master/BENCHMARK.md). If you would like to run some of your own benchmarks, you can use [modclean-benchmark](https://github.com/ModClean/modclean-benchmark). +View additional benchmarks on the Wiki: [Benchmarks](https://github.com/ModClean/modclean/wiki/Benchmarks). If you would like to run some of your own benchmarks, you can use [modclean-benchmark](https://github.com/ModClean/modclean-benchmark). ## Install From a1d994bcfafac8bcae2be1a9774c3a5ab2e17f99 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Fri, 13 Jan 2017 11:06:11 -0500 Subject: [PATCH 21/51] Add link to ModClean 1.x --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 547db04..18fa8a4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![npm version](https://img.shields.io/npm/v/modclean.svg)](https://www.npmjs.com/package/modclean) ![NPM Dependencies](https://david-dm.org/ModClean/modclean.svg) [![NPM Downloads](https://img.shields.io/npm/dm/modclean.svg)](https://www.npmjs.com/package/modclean) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/ModClean/modclean/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/ModClean/modclean.svg)](https://github.com/ModClean/modclean/issues) [![Package Quality](http://npm.packagequality.com/shield/modclean.svg)](http://packagequality.com/#?package=modclean) -### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use ModClean 1.3.0 instead. +### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use [ModClean 1.3.0](https://github.com/ModClean/modclean/tree/1.x) instead. In some environments (especially Enterprise), it's required to commit the `node_modules` folder into version control due to compatibility and vetting open source code. One of the major issues with this is the sheer amount of useless files that are littered through the node_modules folder; taking up space, causing long commit/checkout times, increasing latency on the network, causing additional stress on a CI server, etc. If you think about it, do you really need to deploy tests, examples, build files, attribute files, etc? ModClean is a simple utility that provides a full API and CLI utility to reduce the number of useless files. Even if you do not commit your node_modules folder, this utility is still useful when the application is deployed as you do not need these useless files wasting precious disk space on your server. From 3b380fa701e5dfdc5a614077804f2c3d4def3083 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Fri, 13 Jan 2017 11:45:22 -0500 Subject: [PATCH 22/51] Cleaning up docs --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 18fa8a4..9f8cb23 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,31 @@ ### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use [ModClean 1.3.0](https://github.com/ModClean/modclean/tree/1.x) instead. -In some environments (especially Enterprise), it's required to commit the `node_modules` folder into version control due to compatibility and vetting open source code. One of the major issues with this is the sheer amount of useless files that are littered through the node_modules folder; taking up space, causing long commit/checkout times, increasing latency on the network, causing additional stress on a CI server, etc. If you think about it, do you really need to deploy tests, examples, build files, attribute files, etc? ModClean is a simple utility that provides a full API and CLI utility to reduce the number of useless files. Even if you do not commit your node_modules folder, this utility is still useful when the application is deployed as you do not need these useless files wasting precious disk space on your server. +ModClean is a utility that finds and removes unnecessary files and folders from your `node_modules` directory based on [predefined](https://github.com/ModClean/modclean-patterns-default) and [custom](#custom-pattern-plugins) [glob](https://github.com/isaacs/node-glob) patterns. This utility comes with both a CLI and a programmatic API to provide customization for your environment. ModClean is used and tested in an Enterprise environment on a daily basis. -Depending on the number of modules you are using, file reduction can be anywhere from hundreds to thousands. I work for a Fortune 500 company and we use this to reduce the amount of useless files and typically we remove over 500 files (roughly 100MB total) from our ~20 modules we use in our applications. It's a huge improvement in deployment time and commit/checkout time. +## Why? +There are a few different reasons why you would want to use ModClean: +* **Commiting Modules.** Some evironments (especially Enterprise), it's required to commit the `node_modules` directory with your application into version control. This is due to compatibility, vetting and vunerability scanning rules for open source software. This can lead to issues with project size, checking out/pulling changes and the infamous 255 character path limit if you're unlucky enough to be on Windows or SVN. +* **Wasted space on your server.** Why waste space on your server with files not needed by you or the modules? +* **Packaged applications.** If you're required to package your application, you can reduce the size of the package quickly by removing unneeded files. +* **Compiled applications.** Other tools like, [NW.js](https://nwjs.io/) and [Electron](http://electron.atom.io/) make it easy to create cross-platform desktop apps, but depending on the modules, your app can become huge. Reduce down the size of the compiled application before shipping and make it faster for users to download. +* **Save space on your machine.** Depending on the amount of global modules you have installed, you can reduce their space by removing those gremlin files. +* **and much more!** + +The :cake: is a lie, but the [Benchmarks](https://github.com/ModClean/modclean/wiki/Benchmarks) are not. + +## How? **New!** In ModClean 2.0.0, patterns are now provided by plugins instead of a static `patterns.json` file as part of the module. By default, ModClean comes with [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) installed, providing the same patterns as before. You now have the ability to create your own patterns plugins and use multiple plugins to clean your modules. This allows flexibility with both the programmatic API and CLI. +ModClean scans the `node_modules` directory of your choosing, finding all files and folders that match the defined patterns and deleting them. Both the CLI and the programmatic API provides all the options needed to customize this process to your requirements. Depending on the number of modules your app requires, files can be reduced anywhere from hundreds to thousands and disk space can be reduced considerably. + +_(File and disk space reduction can also be different between the version of NPM and Operating System)_ + **IMPORTANT** This module has been heavily tested in an enterprise environment used for large enterprise applications. The provided patterns in [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) have worked very well when cleaning up useless files in many popular modules. There are hundreds of thousands of modules in NPM and I cannot simply cover them all. If you are using ModClean for the first time on your application, you should create a copy of the application so you can ensure it still runs properly after running ModClean. The patterns are set in a way to ensure no crutial module files are removed, although there could be one-off cases where a module could be affected and that's why I am stressing that testing and backups are important. If you find any files that should be removed, please create a pull request to [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) or create your own patterns plugin to share with the community. -### Removal Benchmark +## Removal Benchmark So how well does this module work? If we `npm install sails` and run ModClean on it, here are the results: _All tests ran on macOS 10.12.3 with Node v6.9.1 and NPM v4.0.5_ From 6832ad7c08a499f2044339c752ee4599f5ee2433 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 09:30:28 -0500 Subject: [PATCH 23/51] Make `emptyDirFilter` function a config option Allow configuration of the `emptyDirFilter` function to allow better checking for empty directories. --- lib/modclean.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/modclean.js b/lib/modclean.js index 3f34f0f..3bf922e 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -68,6 +68,16 @@ let defaults = { * @type {Boolean} */ removeEmptyDirs: true, + /** + * Filter function used when checking if a directory is empty + * @param {String} file File name to filter against + * @return {Boolean} `true` if is a valid file, `false` if invalid file + */ + emptyDirFilter: function(file) { + if(/Thumbs\.db$/i.test(file) || /\.DS_Store$/i.test(file)) return false; + + return true; + }, /** * Whether file deletion errors should halt the module from running and return the error to the callback (default `false`) * @type {Boolean} @@ -303,7 +313,7 @@ class ModClean extends EventEmitter { if(err || !Array.isArray(dirs)) return cb(err); each(dirs, (dir, dCb) => { - emptyDir(dir, emptyFilter, function(err, isEmpty) { + emptyDir(dir, self.options.emptyDirFilter || function() { return true; }, (err, isEmpty) => { if(err || !isEmpty) return dCb(); results.push(dir); dCb(); From 0ffad9b47b0150ae2b4e0561a56a84c1976a0e79 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 09:43:09 -0500 Subject: [PATCH 24/51] Use `Object.assign` instead of `extend` --- lib/modclean.js | 5 +---- package.json | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index 3bf922e..17d4c63 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -6,7 +6,6 @@ "use strict"; const glob = require('glob'); const rimraf = require('rimraf'); -const extend = require('extend'); const path = require('path'); const subdirs = require('subdirs'); const emptyDir = require('empty-dir'); @@ -106,9 +105,7 @@ class ModClean extends EventEmitter { this.utils = new Utils(); if(typeof options === 'function') cb = options; - if(!options || typeof options !== 'object') options = {}; - - this.options = options = extend({}, modclean.defaults, options); + this.options = Object.assign({}, modclean.defaults, options && typeof options === 'object' ? options : {}); this._patterns = this.utils.initPatterns(this.options); diff --git a/package.json b/package.json index b50a5e4..7005ce0 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "clui": "^0.3.1", "commander": "^2.9.0", "empty-dir": "^0.2.1", - "extend": "^3.0.0", "glob": "^7.1.1", "lodash.uniq": "^4.5.0", "modclean-patterns-default": "^1.0.0", From 1411af61041c706a2b51ee2fe1ef5f0a5f56c307 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 09:49:19 -0500 Subject: [PATCH 25/51] Removed duplicate `clean()` call --- lib/modclean.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modclean.js b/lib/modclean.js index 17d4c63..44c3d9d 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -359,7 +359,7 @@ module.exports = modclean; * @return {Object} New ModClean instance */ function modclean(options, cb) { - return new ModClean(options, cb).clean(); + return new ModClean(options, cb); } /** From b419523aab9f91e247c502f4dcb18dc84414688b Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 09:56:00 -0500 Subject: [PATCH 26/51] Added doc blocks --- lib/utils.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/utils.js b/lib/utils.js index e0b4787..17df80e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,6 +4,11 @@ const path = require('path'); class ModClean_Utils { + /** + * Initializes patterns based on the configuration + * @param {Object} opts Options object for ModClean + * @return {Object} The compiled and loaded patterns + */ initPatterns(opts) { let patDefs = opts.patterns, patterns = [], @@ -64,6 +69,12 @@ class ModClean_Utils { }; } + /** + * Parses pattern configuration item and attempts to load it + * @param {String} module Module name or path to load + * @param {String} def Raw definition provided in the configuration + * @return {Object} Object containing the found module name and the loaded patterns + */ _loadPatterns(module, def) { let patterns; From 61b36bc43fdfd3fa6d0a79c4d7fe4d681fec1734 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 10:42:13 -0500 Subject: [PATCH 27/51] Remove unused functions --- lib/modclean.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index 44c3d9d..b2dec41 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -194,10 +194,6 @@ class ModClean extends EventEmitter { this.emit('process', files); - function deleteFile() { - - } - each(files, function(file, callback) { if(processFn.length <= 1) { // If processFn has 0 or 1 argument (file), then assume sync @@ -299,13 +295,6 @@ class ModClean extends EventEmitter { let self = this, results = []; - function emptyFilter(fp) { - if(/Thumbs\.db$/i.test(fp)) return false; - if(/\.DS_Store$/i.test(fp)) return false; - - return true; - } - subdirs(this.options.cwd, function(err, dirs) { if(err || !Array.isArray(dirs)) return cb(err); From 42b17f7d5de4f9c6ccf154c35bae13e659c3b6cc Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 11:42:26 -0500 Subject: [PATCH 28/51] Use single-quotes instead of double --- lib/modclean.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modclean.js b/lib/modclean.js index b2dec41..2a4282c 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -12,7 +12,7 @@ const emptyDir = require('empty-dir'); const each = require('async-each-series'); const Utils = require('./utils.js'); -const EventEmitter = require("events").EventEmitter; +const EventEmitter = require('events').EventEmitter; let defaults = { /** From 07bf3e75a876c2561854c67ef8d1f0ecd97af0d4 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 11:42:49 -0500 Subject: [PATCH 29/51] Remove unused parameter --- lib/modclean.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index 2a4282c..891429a 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -197,7 +197,7 @@ class ModClean extends EventEmitter { each(files, function(file, callback) { if(processFn.length <= 1) { // If processFn has 0 or 1 argument (file), then assume sync - if(processFn(file) !== false) self._deleteFile(file, (err, result) => { + if(processFn(file) !== false) self._deleteFile(file, (err) => { if(!err) results.push(file); callback(err); }); @@ -205,7 +205,7 @@ class ModClean extends EventEmitter { } else { // If processFn more than 1 argument (file, cb), then assume async processFn(file, function(result) { - if(result !== false) self._deleteFile(file, (err, result) => { + if(result !== false) self._deleteFile(file, (err) => { if(!err) results.push(file); callback(err); }); From 410ee97ac27fefa19adf8fd9006a968859f573d7 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 11:43:26 -0500 Subject: [PATCH 30/51] Add new `errors` property --- lib/modclean.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/modclean.js b/lib/modclean.js index 891429a..b2d83f1 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -103,6 +103,7 @@ class ModClean extends EventEmitter { super(); this.utils = new Utils(); + this.errors = []; if(typeof options === 'function') cb = options; this.options = Object.assign({}, modclean.defaults, options && typeof options === 'object' ? options : {}); From 86cb32871709886603d4fc4f3a1b327c6d66a944 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 12:05:02 -0500 Subject: [PATCH 31/51] Pass reference of instance to utils --- lib/modclean.js | 2 +- lib/utils.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/modclean.js b/lib/modclean.js index b2d83f1..f05ad0c 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -102,7 +102,7 @@ class ModClean extends EventEmitter { constructor(options, cb) { super(); - this.utils = new Utils(); + this.utils = new Utils(this); this.errors = []; if(typeof options === 'function') cb = options; diff --git a/lib/utils.js b/lib/utils.js index 17df80e..eb5bf75 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,6 +4,10 @@ const path = require('path'); class ModClean_Utils { + constructor(inst) { + this._inst = inst; + } + /** * Initializes patterns based on the configuration * @param {Object} opts Options object for ModClean From 5622de99ff80c2cf022b6734a8e831c5fb97d959 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 12:05:59 -0500 Subject: [PATCH 32/51] Adding new `error` function to store + emit errors --- lib/utils.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/utils.js b/lib/utils.js index eb5bf75..e72360b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -106,6 +106,26 @@ class ModClean_Utils { patterns }; } + + /** + * Stores error details and emits error event + * @param {Error} err Error object + * @param {String} method Method in which the error occurred + * @param {Object} obj Optional object to combine into the stored error object + * @param {String} event Event name to emit, `false` disables + * @return {Object} The compiled error object + */ + error(err, method, obj={}, event='error') { + let errObj = Object.assign({ + error: err, + method: method + }, obj || {}); + + this._inst.errors.push(errObj); + if(event !== false) this._inst.emit(event, errObj); + + return errObj; + } } module.exports = ModClean_Utils; From bc749756d8979ac90009fae075c7f714c622949b Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:31:58 -0500 Subject: [PATCH 33/51] Updated license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 2398d1c..6f8e1fa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Kyle Ross +Copyright (c) 2017 Kyle Ross Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From bdbfd4c109301978f6580e9830845e50e5176c39 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:36:40 -0500 Subject: [PATCH 34/51] Cleanup code and dependencies --- bin/modclean.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bin/modclean.js b/bin/modclean.js index 0a504f4..2b77fbf 100644 --- a/bin/modclean.js +++ b/bin/modclean.js @@ -5,8 +5,6 @@ const chalk = require('chalk'); const program = require('commander'); const notifier = require('update-notifier'); const clui = require('clui'); -const pad = require('pad'); -const readline = require('readline'); const path = require('path'); const os = require('os'); const pkg = require('../package.json'); @@ -227,7 +225,7 @@ class ModClean_CLI { ); }); - inst.on('afterEmptyDirs', (dirs) => { + inst.on('afterEmptyDirs', () => { if(showProgress) process.stdout.write('\n'); this.log('event', 'afterEmptyDirs'); }); @@ -235,13 +233,13 @@ class ModClean_CLI { // Error Event (called as soon as an error is encountered) inst.on('error', (err) => { this.log('event', 'error'); - this.log('error', err); + this.log('error', err.error); }); // FileError Event (called when there was an error deleting a file) - inst.on('fileError', (err, file) => { + inst.on('fileError', (err) => { this.log('event', 'fileError'); - this.log('error', `${chalk.red.bold('FILE ERROR:')} ${err}\n${chalk.gray(file)}`); + this.log('error', `${chalk.red.bold('FILE ERROR:')} ${err.error}\n${chalk.gray(err.file)}`); }); // Finish Event (once processing/deleting all files is complete) From 6dfa748860525e8fb4462bcdf69d3989e885b0f1 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:37:00 -0500 Subject: [PATCH 35/51] Use new error reporting functionality --- lib/modclean.js | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index f05ad0c..f15dfde 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -127,11 +127,6 @@ class ModClean extends EventEmitter { this.emit('start', this); let done = (err, results) => { - /** - * @event complete - * @property {Mixed} err An error string/object if error was thrown - * @property {Array} results List of files successfully deleted - */ this.emit('complete', err, results); if(typeof cb === 'function') cb(err, results); }; @@ -169,11 +164,7 @@ class ModClean extends EventEmitter { this.emit('beforeFind', this._patterns.allow, globOpts); glob(`**/@(${this._patterns.allow.join('|')})`, globOpts, (err, files) => { - if(err) this.emit('error', err); - /** - * @event files - * @property {Array} files List of files found to be removed - */ + if(err) this.utils.error(err, '_find'); else this.emit('files', files); cb(err, files); }); @@ -234,10 +225,6 @@ class ModClean extends EventEmitter { opts = this.options; function done() { - /** - * @event deleted - * @property {String} file The deleted file name - */ self.emit('deleted', file); return cb(null, file); } @@ -247,12 +234,7 @@ class ModClean extends EventEmitter { rimraf(path.join(opts.cwd, file), (err) => { if(err) { - /** - * @event fileError - * @property {Mixed} err The error object/string - * @property {String} file The file that caused the error - */ - this.emit('fileError', err, file); + this.utils.error(err, '_deleteFile', { file }, 'fileError'); return opts.errorHalt? cb(err, file) : cb(); } @@ -297,10 +279,12 @@ class ModClean extends EventEmitter { results = []; subdirs(this.options.cwd, function(err, dirs) { - if(err || !Array.isArray(dirs)) return cb(err); + if(err) self.utils.error(err, '_findEmptyDirs'); + if(err || !Array.isArray(dirs)) return cb(err, []); each(dirs, (dir, dCb) => { emptyDir(dir, self.options.emptyDirFilter || function() { return true; }, (err, isEmpty) => { + if(err) self.utils.error(err, '_findEmptyDirs'); if(err || !isEmpty) return dCb(); results.push(dir); dCb(); @@ -324,11 +308,10 @@ class ModClean extends EventEmitter { each(dirs, (dir, dCb) => { rimraf(dir, (err) => { - if(!err) { + if(err) self.utils.error(err, '_removeEmptyDirs', { dir }, 'emptyDirError'); + else { results.push(dir); self.emit('deletedEmptyDir', dir); - } else { - self.emit('emptyDirError', dir, err); } dCb(); From 288dca97b9ab7fc6c8eea17515acc8423ba19e5d Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:37:15 -0500 Subject: [PATCH 36/51] Remove `pad` as a dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 7005ce0..4948038 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "glob": "^7.1.1", "lodash.uniq": "^4.5.0", "modclean-patterns-default": "^1.0.0", - "pad": "^1.0.2", "rimraf": "^2.5.4", "subdirs": "^1.0.0", "update-notifier": "^1.0.3" From f8e2d4c55b9626ff88f85a324596d93f8e763b04 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:37:51 -0500 Subject: [PATCH 37/51] Move documentation to wiki --- README.md | 449 +----------------------------------------------------- 1 file changed, 4 insertions(+), 445 deletions(-) diff --git a/README.md b/README.md index 9f8cb23..98c79a0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ### This documentation is for ModClean 2.x which requires Node v6.9+, if you need to support older versions, use [ModClean 1.3.0](https://github.com/ModClean/modclean/tree/1.x) instead. -ModClean is a utility that finds and removes unnecessary files and folders from your `node_modules` directory based on [predefined](https://github.com/ModClean/modclean-patterns-default) and [custom](#custom-pattern-plugins) [glob](https://github.com/isaacs/node-glob) patterns. This utility comes with both a CLI and a programmatic API to provide customization for your environment. ModClean is used and tested in an Enterprise environment on a daily basis. +ModClean is a utility that finds and removes unnecessary files and folders from your `node_modules` directory based on [predefined](https://github.com/ModClean/modclean-patterns-default) and [custom](https://github.com/ModClean/modclean/wiki/Custom-Pattern-Plugins) [glob](https://github.com/isaacs/node-glob) patterns. This utility comes with both a CLI and a programmatic API to provide customization for your environment. ModClean is used and tested in an Enterprise environment on a daily basis. ## Why? There are a few different reasons why you would want to use ModClean: @@ -79,447 +79,14 @@ Install globally (CLI) npm install modclean -g ``` -## CLI Usage -If you want to use this module as a tool, you can use the provided CLI utility. After installing globally, you will now have access to the command `modclean`. There are several options available to customize how it should run. All options listed below are optional. - -### Usage - - modclean [-tsiPevrkhV] [-p, --path=string] [-D, --modules-dir=string] [-n, --patterns=list] [-a, --additional-patterns=list] [-I, --ignore=list] - -#### -p [path], --path [string] -Provide a different path to run ModClean in. By default, it uses `process.cwd()`. The path **must** be in a directory that contains a `node_modules` directory. - -#### -D, --modules-dir [string] -Change the default modules directory name. Default is `node_modules`. - -#### -n, --patterns [list] -Specify which pattern plugins/rules to use. Separate multiple groups by a single comma (no spaces). Default is `default:safe`. -Example: `modclean -n default:safe,default:caution` - -#### -a, --additional-patterns [list] -Specify custom glob patterns to be included in the search. -Example: `modclean --additional-patterns="*history,*.html,.config"` - -#### -I, --ignore [list] -Comma-separated list of glob patterns to ignore during cleaning. Useful when a pattern matches a module name you do not want removed. -Example: `modclean --ignore="validate-npm-package-license,*history*"` - -#### -t, --test -Run in test mode which will do everything ModClean does except delete the files. It's good practice to run this first to analyze the files that will be deleted. - -#### -s, --case-sensitive -When files are searched, they are searched using case sensitive matching. (ex. `README.md` pattern would not match `readme.md` files) - -#### -i, --interactive -Run in interactive mode. For each file found, you will be prompted whether you want to delete or skip. - -#### -P, --no-progress -Turns off the progress bar when files are being deleted. - -#### --no-dirs -Do not delete directories, only files. - -#### --no-dotfiles -Exclude dot files from being deleted. - -#### -k, --keep-empty -Exclude empty directories from being deleted. - -#### -e, --error-halt -Whether to halt the process when an error is encountered. The process is only halted when there is an issue deleting a file due to permissions or some other catastrophic issue. - -#### -v, --verbose -Runs in verbose mode. This will display much more information during the process. - -#### -r, --run -Run the utility immediately without displaying the warning and having to confirm. - -#### -h, --help -Show help/usage screen. - -#### -V, --version -Display the version of ModClean that is installed. - ---- - -## API Documentation -You can also use ModClean programmically so you can include it into your own utilities and customize how it works. Just install ModClean locally to your project. - -### Examples - -```js -// Require modclean module -const modclean = require('modclean'); -``` - -Run the basic ModClean process with a callback function when completed. - -```js -modclean(function(err, results) { - if(err) return console.error(err); - - console.log('Deleted Files Total:', results.length); -}); -``` - -Run the basic ModClean process with conditional file skipping. - -```js -modclean({ - process: function(file, files) { - // Skip .gitignore files - if(file.match(/\.gitignore/i)) { - return false; - } - - return true; - } -}).clean(function(err, results) { - if(err) return console.error(err); - - console.log('Deleted Files Total:', results.length); -}); -``` - -More advanced usage. - -```js -const path = require('path'); - -let MC = new modclean.ModClean({ - // Define a custom path - cwd: path.join(process.cwd(), 'myApp/node/node_modules'), - // Only delete patterns.safe patterns along with html and png files - patterns: [modclean.patterns.safe, '*.html', '*.png'], - // Run in test mode so no files are deleted - test: true -}); - -MC.on('deleted', function(file) { - // For every file deleted, log it - console.log((MC.options.test? 'TEST' : ''), file, 'deleted from filesystem'); -}); - -// Run the cleanup process without using the 'clean' function -MC._find(null, function(err, files) { - if(err) return console.error('Error while searching for files', err); - - MC._process(files, function(err, results) { - if(err) return console.error('Error while processing files', err); - - console.log('Deleted Files Total:', results.length); - }); -}); -``` - -### Options -The options below can be used to modify how ModClean works. - -#### cwd -*(String)* **Default:** `process.cwd()` -The path in which ModClean should recursively search through to find files to remove. If the path does not end with `options.modulesDir`, it will be appended to the path, allowing this script to run in the parent directory. - -#### patterns -*(Array[string])* **Default** `["default:safe"]` -Patterns plugins/rules to use. Each value is either the full plugin module name (ex. `modclean-patterns-pluginname`) or just the last section of the module name (ex. `pluginname`). Plugins will usually have different rules to use, you can specify the rule name by appending a colon ':' and the rule name (ex. `pluginname:rule`). If a rule name is not provided, it will load the first rule found on the plugin. If you want to use all rules, you can use an asterisk as the rule name (ex. `pluginname:*`). By default, [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) is included. If you want to create your own plugin, see [Custom Patterns Plugins](#custom-pattern-plugins) below. - -#### additionalPatterns -*(Array[string])* **Default** `[]` -Additional custom `glob` patterns to include in the search. This will allow further customization without the need of creating your own patterns plugin. - -#### ignorePatterns -*(Array[string])* **Default** `[]` -Custom `glob` patterns to ignore during the search. Allows skipping matched items that would normally be removed, which is good for patterns that match existing module names you wish not to be removed. - -#### ignoreCase -*(Boolean)* **Default** `true` -Whether `glob` should ignore the case of the file names when searching. If you need to do strict searching, set this to `false`. - -#### process -*(Function)* **Default:** `null` -Optional function to call before each file is deleted. This function can be used asynchronously or synchronously depending on the number of parameters provided. If the provided function has 0 or 1 parameters `function(file)`, it is synchronous, if it has 2 parameters `function(file, cb)`, it is asynchronous. When sync, you can `return false` to skip the current file being processed, otherwise when async, you can call the callback function `cb(false)` to skip the file. The **file** parameter is the current path with the filename appened of the file being processed. - -#### modulesDir -*(String|Boolean)* **Default:** `"node_modules"` -The modules directory name to use when looking for modules. This is only used when setting the correct `options.cwd` path. If you do not want the modules directory to be appended to `options.cwd`, set this option to `false`. If `options.cwd` already ends with the value of this option, it will not be appended to the path. - -#### removeEmptyDirs -*(Boolean)* **Default:** `true` -Whether to remove empty directories after the cleanup process. This is usually a safe option to use. - -#### noDirs -*(Boolean)* **Default:** `false` -Set to `true` to skip directories from being deleted during the cleaning process. - -#### dotFiles -*(Boolean)* **Default** `true` -Set to `false` to skip dot files from being deleted during the cleaning process. - -#### errorHalt -*(Boolean)* **Default:** `false` -Whether the script should exit with a filesystem error if one is encountered. This really only pertains to systems with complex permissions or Windows filesystems. The `rimraf` module will only throw an error if there is actually an issue deleting an existing file. If the file doesn't exist, it does not throw an error. - -#### test -*(Boolean)* **Default:** `false` -Whether to run in test mode. If set to `true` everything will run (including all events), although the files will not actually be deleted from the filesystem. This is useful when you need to analyze the files to be deleted before actually deleting them. - - -### Methods and Properties -These are the methods and properties exported when calling `const modclean = require('modclean');`. - -#### modclean([options][,cb]) -Create a new `ModClean` instance. It's the same as calling `new modclean.ModClean()`. If a callback function is provided, it will automatically call the `clean()` method and therefore `clean()` should not be called manually. If you need to set event listeners, set the callback function in the `clean()` method instead. - -| Argument | Type | Required? | Description | Default | -|-----------|----------|-----------|------------------------------------------------------------------------------------------------------------------------|---------| -| `options` | Object | No | Optional options object to configure ModClean | `{}` | -| `cb` | Function | No | Optional callback function to call once cleaning complete. If not provided, `clean()` will not be called automatically | `null` | - -```js -const modclean = require('modclean'); - -modclean(function(err, results) { - // called once cleaning is complete. - if(err) { - console.error(err); - return; - } - - console.log(`${results.length} files removed!`); -}); -``` - -#### modclean.defaults -*(Object)* - The default options used in all created ModClean instances. You may change the defaults at anytime if you will be creating multiple instances that need to use the same options. - -#### modclean.ModClean([options][,cb]) -Access to the ModClean class constructor. - -### ModClean Class - -#### ModClean([options][,cb]) -Create instance of the `ModClean` class. Must be called with `new`. - -| Argument | Type | Required? | Description | Default | -|-----------|----------|-----------|------------------------------------------------------------------------------------------------------------------------|---------| -| `options` | Object | No | Optional options object to configure ModClean | `{}` | -| `cb` | Function | No | Optional callback function to call once cleaning complete. If not provided, `clean()` will not be called automatically | `null` | - -```js -const ModClean = require('modclean').ModClean; - -// Create new instance -let MC = new ModClean(); -``` - -#### clean([cb]) -Runs the ModClean process. Only needs to be called if a callback function is not provided to the `ModClean()` constructor. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `cb` | Function | No | Optional callback function to call once cleaning complete. Called with `err` (error message if one occurred) and `results` (array of file paths removed) | `null` | - -#### cleanEmptyDirs(cb) -Finds all empty directories and deletes them from `options.cwd`. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------|---------| -| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of directories deleted) | | - -#### _find(cb) -Internally used by ModClean to search for files based on the loaded patterns/rules. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `files` (array of file paths found) | | - -#### _process(files, cb) -Internally used by ModClean to process each of the files. The processing includes running `options.process` and then calling `_deleteFile()`. - -| Argument | Type | Required? | Description | Default | -|----------|---------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `files` | Array[String] | Yes | Array of file paths to be deleted. | | -| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of file paths deleted) | | - -#### _deleteFile(file, cb) -Internally used by ModClean to delete a file at the given path. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| -| `file` | String | Yes | File path to be deleted | | -| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `files` (the file path deleted). The callback will not receive an error if `options.errorHalt = false` | | - -#### _findEmptyDirs(cb) -Internally used by ModClean to find all empty directories within `options.cwd`. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------|---------| -| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of directories found) | | - -#### _removeEmptyDirs(dirs, cb) -Internally used by ModClean to delete all empty directories provided. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------|---------| -| `cb` | Function | Yes | Callback function to call once complete. Called with `err` (error message if one occurred) and `results` (array of directories deleted) | | - -#### on(event, fn) -Creates an event handler on the ModClean instance using `EventEmitter`. - -| Argument | Type | Required? | Description | Default | -|----------|----------|-----------|-------------------------------------------------------|---------| -| `event` | String | Yes | Event name to listen to (events are documented below) | | -| `fn` | Function | Yes | Function to call once the specified event is emitted | | - -#### options -Compiled options object used by the ModClean instance. - -### Events -The following events are emitted from the `ModClean` instance. - -#### start -Emitted at the beginning of `clean()`. - -| Argument | Type | Description | -|----------|----------|------------------------------------| -| `inst` | ModClean | Access to the instance of ModClean | - -#### beforeFind -Emitted before `_find()` function starts. - -| Argument | Type | Description | -|------------|---------------|----------------------------------------------------| -| `patterns` | Array[String] | Compiled list of `glob` patterns that will be used | -| `globOpts` | Object | The configuration object being passed into `glob` | - -#### files -Emitted once a list of all found files has been compiled from the `_find()` method. - -| Argument | Type | Description | -|----------|---------------|-----------------------------------------| -| `files` | Array[String] | Array of file paths found to be removed | - -#### process -Emitted at the start of the `_process()` function. - -| Argument | Type | Description | -|----------|---------------|-----------------------------------------| -| `files` | Array[String] | Array of file paths found to be removed | - -#### deleted -Emitted each time a file has been deleted from the file system by the `_deleteFile()` method. - -| Argument | Type | Description | -|----------|--------|----------------------------------------------| -| `file` | String | File path that has been successfully deleted | - -#### finish -Emitted once processing and deletion of files has completed by the `_process()` method. - -| Argument | Type | Description | -|-----------|---------------|---------------------------------------------------| -| `results` | Array[String] | List of file paths that were successfully removed | - -#### complete -Emitted once the entire ModClean process has completed before calling the main callback function. - -| Argument | Type | Description | -|-----------|---------------|---------------------------------------------------| -| `err` | Error | Error object if one occurred during the process | -| `results` | Array[String] | List of file paths that were successfully removed | - -#### fileError -Emitted if there was an error thrown while deleting a file/folder. Will emit even if `options.errorHalt = false`. - -| Argument | Type | Description | -|----------|--------|--------------------------------| -| `err` | Error | Error object | -| `file` | String | The file that caused the error | - -#### error -Emitted if there was an error thrown while searching for files. - -| Argument | Type | Description | -|----------|-------|--------------| -| `err` | Error | Error object | - -#### beforeEmptyDirs -Emitted before finding/removing empty directories. - -#### afterEmptyDirs -Emitted after finding/removing empty directories. - -| Argument | Type | Description | -|-----------|---------------|----------------------------------| -| `results` | Array[String] | Array of paths that were removed | - -#### emptyDirs -Emitted after a list of empty directories is found. - -| Argument | Type | Description | -|-----------|---------------|--------------------------------| -| `results` | Array[String] | Array of paths that were found | - -#### deletedEmptyDir -Emitted after an empty directory is deleted. - -| Argument | Type | Description | -|----------|--------|-------------------------------------| -| `dir` | String | The directory path that was deleted | - -#### emptyDirError -Emitted if an error occurred while deleting an empty directory. - -| Argument | Type | Description | -|----------|--------|-----------------------------------------| -| `dir` | String | The directory path that caused an error | -| `err` | Error | Error object thrown | --- -## Custom Patterns Plugins -New in version 2.x, ModClean now supports pattern plugins to allow you to use various sets of patterns, along with custom ones. By default, ModClean comes with [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) preinstalled, but you can install or create your own plugins. - -### Installing 3rd Party Plugins -If you would like to use a 3rd party plugin, it's pretty simple to install. If you are using the CLI, the plugin should be installed globally (ex. `npm install -g modclean-patterns-pluginname`), otherwise programmatically, install locally (ex. `npm install modclean-patterns-pluginname --save`). - -### Available 3rd Party Plugins -*(None available yet!)* - -### Create Your Own -Creating your own patterns plugin is simple. It's a basic Node Module that must be prefixed with `modclean-patterns-` that is published to NPM. The module just needs to export and object that contains the pattern definitions (see [modclean-patterns-default](https://github.com/ModClean/modclean-patterns-default) for an example). - -```js -module.exports = { - $default: 'basic', - - basic: { - patterns: [ - // ... glob patterns here - ], - ignore: [ - // ... glob patterns to ignore here - ] - } - - ], - - advanced: { - patterns: [ - // ... glob patterns here - ], - ignore: [ - // ... glob patterns to ignore here - ] - } -}; -``` - -Each key in the object is a rule name that is an object containing `patterns` and `ignore` arrays of glob patterns. The `patterns` section is glob patterns that will be found and removed and the `ignore` section is glob patterns to be ignored. Both keys are required, but can be empty arrays. +### [Read the CLI Documentation](https://github.com/ModClean/modclean/wiki/CLI) -An optional configuration parameter `$default` can be provided which tells ModClean the default rule to use if the user does not specify. If this is not provided, ModClean will use the first rule key it encounters. **Note:** Rules starting with `$` will be ignored. +### [Read the API Documentation](https://github.com/ModClean/modclean/wiki/API) -If you've created your own plugin, submit a pull request to add it to the list above! +### [Read the Custom Patterns Plugin Documentation](https://github.com/ModClean/modclean/wiki/Custom-Pattern-Plugins) --- @@ -528,13 +95,5 @@ If you find any bugs with either ModClean or the CLI Utility, please feel free t --- -## Contributing -If you would like to contribute to this project, please ensure you follow the guidelines below: - -### Code Style -I'm not very picky on the code style as long as it roughly follows what is currently written in the modules. Just ensure that lines that should end with semi-colons do end with them. Comments are also very helpful for future contributors to know what's going on. - ---- - ## License ModClean is licensed under the MIT license. Please see LICENSE in the repository for the full text. From 28e4928a0d945394415dfb03f292291710962a1a Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:38:01 -0500 Subject: [PATCH 38/51] Release 2.1.0 --- HISTORY.md | 13 ++++++++++++- package.json | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 0b9bd1b..19da971 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,17 @@ # ModClean History -## 2.0.0 (1/11/2016) +## 2.1.0 (1/17/2017) +* Added `emptyDirFilter` configuration option. +* Use `Object.assign` instead of `extend` module. + - Removed `extend` dependency. +* Removed unused `pad` dependency. +* Removed duplicate call to `clean()` in shortcut method. +* Misc. cleanup +* **Breaking Change:** All error events (`error`, `fileError` and `emptyDirsError`) now return error object. +* **New!** Added `errors` property on the ModClean class which contains all errors that occurred. +* Moved documentation to the Wiki. + +## 2.0.0 (1/11/2017) ### ModClean API Changes * Complete rewrite using ES6 and some breaking changes (now requires Node v6.9+) * No longer includes `patterns.json` file, instead uses plugins to allow further customization. diff --git a/package.json b/package.json index 4948038..4d07d04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modclean", - "version": "2.0.0", + "version": "2.1.0", "description": "Remove unwanted files and directories from your node_modules folder", "main": "index.js", "bin": { From 5b8c5762f25f8c2b472cc329ca8bbac01836ef3f Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Tue, 17 Jan 2017 15:43:20 -0500 Subject: [PATCH 39/51] Remove extra hr --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 98c79a0..5ac2dff 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,5 @@ npm install modclean -g ## Issues If you find any bugs with either ModClean or the CLI Utility, please feel free to open an issue. Any feature requests may also be poseted in the issues. ---- - ## License ModClean is licensed under the MIT license. Please see LICENSE in the repository for the full text. From a4c3969e0e01f1726ebfa48779d786f5d8a020cc Mon Sep 17 00:00:00 2001 From: Benjamin Santalucia Date: Tue, 5 Sep 2017 14:00:41 +0200 Subject: [PATCH 40/51] feat(symlinks): allow to include/exclude symlinked packages --- bin/modclean.js | 100 ++++---- lib/modclean.js | 87 ++++--- yarn.lock | 641 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 740 insertions(+), 88 deletions(-) mode change 100644 => 100755 bin/modclean.js create mode 100644 yarn.lock diff --git a/bin/modclean.js b/bin/modclean.js old mode 100644 new mode 100755 index 2b77fbf..8a80fb8 --- a/bin/modclean.js +++ b/bin/modclean.js @@ -36,6 +36,7 @@ program .option('-P, --no-progress', 'Hide progress bar') .option('-e, --error-halt', 'Halt script on error') .option('-v, --verbose', 'Run in verbose mode') + .option('-f, --follow-symlink', 'Clean symlinked packages as well') .option('-r, --run', 'Run immediately without warning') .option('-n, --patterns ', 'Patterns plugins/rules to use (defaults to "default:safe")', list) .option('-a, --additional-patterns ', 'Additional glob patterns to search for', list) @@ -48,14 +49,14 @@ program class ModClean_CLI { constructor() { this.log = utils.initLog(program.verbose); - + // Display CLI header console.log( "\n" + chalk.yellow.bold('MODCLEAN ') + chalk.gray(' Version ' + pkg.version) + "\n" ); - + // Display "running in test mode" message if(program.test) { console.log( @@ -63,27 +64,27 @@ class ModClean_CLI { chalk.gray('When running in test mode, files will not be deleted from the file system.') + "\n" ); } - + // Display warning message and confirmation prompt if(!program.run && !program.test) { console.log( chalk.red.bold('WARNING:') + "\n" + chalk.gray(utils.warningMsg) + "\n" ); - + return utils.confirm('Are you sure you want to continue?', (res) => { if(!res) return process.exit(0); this.start(); }); } - + this.start(); } - + start() { let self = this, platform = os.platform(); - + this.stats = { current: 0, total: 0, @@ -91,10 +92,10 @@ class ModClean_CLI { currentEmpty: 0, totalEmpty: 0 }; - + // Disable progress bar in interactive mode if(program.interactive) program.progress = false; - + let options = { cwd: program.path || process.cwd(), modulesDir: program.modulesDir || 'node_modules', @@ -107,15 +108,16 @@ class ModClean_CLI { removeEmptyDirs: !program.keepEmpty, ignoreCase: !program.caseSensitive, test: !!program.test, + followSymlink: !!program.followSymlink, process: function(file, cb) { self.stats.current += 1; if(!program.interactive) return cb(true); - + let name = path.relative(options.cwd, file); if(platform === 'win32') name = name.replace(/\\+/g, '/'); - + utils.confirm( - chalk.gray(`(${self.stats.current}/${self.stats.total})`) + + chalk.gray(`(${self.stats.current}/${self.stats.total})`) + `${name} ${chalk.gray(' - ')} ${chalk.yellow.bold('Delete File?')}`, function(res) { if(!res) self.stats.skipped.push(file); @@ -123,22 +125,22 @@ class ModClean_CLI { }); } }; - + this.modclean = new ModClean(options); - + this.initEvents(); this.modclean.clean(this.done.bind(this)); } - + initEvents() { var inst = this.modclean; - + let progressBar = new clui.Progress(40), spinner = new clui.Spinner('Loading...'), showProgress = true; - + if(!process.stdout.isTTY || program.interactive || !program.progress || program.verbose) showProgress = false; - + function updateProgress(current, total) { if(showProgress) { process.stdout.cursorTo(0); @@ -146,7 +148,7 @@ class ModClean_CLI { process.stdout.clearLine(1); } } - + function showSpinner(msg) { if(!process.stdout.isTTY || program.verbose || !program.progress) { console.log(msg); @@ -155,110 +157,110 @@ class ModClean_CLI { spinner.start(); } } - + // Start Event (searching for files) inst.on('start', () => { this.log('event', 'start'); this.log('verbose', '\n' + JSON.stringify(inst.options, null, 4)); - + showSpinner(`Searching for files in ${inst.options.cwd}...`); }); - + // Files Event (file list after searching complete) inst.on('files', (files) => { this.log('event', 'files'); if(process.stdout.isTTY) spinner.stop(); - + this.stats.total = files.length; - + console.log(`Found ${chalk.green.bold(files.length)} files/folders to remove\n`); }); - + inst.on('process', (files) => { this.log('event', 'process'); - + if(!showProgress && !program.interactive && !program.verbose) console.log('Deleting files, please wait...'); - + updateProgress(0, files.length); }); - + // Deleted Event (called for each file deleted) inst.on('deleted', (file) => { updateProgress(this.stats.current, this.stats.total); - + this.log( 'verbose', `${chalk.yellow.bold('DELETED')} (${this.stats.current}/${this.stats.total}) ${chalk.gray(file)}` ); }); - + inst.on('beforeEmptyDirs', () => { this.log('event', 'beforeEmptyDirs'); - + showSpinner(`Searching for empty directories in ${inst.options.cwd}...`); }); - + inst.on('emptyDirs', (dirs) => { this.log('event', 'emptyDirs'); - + if(process.stdout.isTTY) spinner.stop(); - + this.stats.totalEmpty = dirs.length; console.log(`\nFound ${chalk.green.bold(dirs.length)} empty directories to remove\n`); - + if(!dirs.length) return; - + if(!showProgress && !program.interactive && !program.verbose) console.log('Deleting empty directories, please wait...'); - + updateProgress(0, dirs.length); }); - + inst.on('deletedEmptyDir', (dir) => { this.stats.currentEmpty += 1; updateProgress(this.stats.currentEmpty, this.stats.totalEmpty); - + this.log( 'verbose', `${chalk.yellow.bold('DELETED EMPTY DIR')} (${this.stats.currentEmpty}/${this.stats.totalEmpty}) ${chalk.gray(dir)}` ); }); - + inst.on('afterEmptyDirs', () => { if(showProgress) process.stdout.write('\n'); this.log('event', 'afterEmptyDirs'); }); - + // Error Event (called as soon as an error is encountered) inst.on('error', (err) => { this.log('event', 'error'); this.log('error', err.error); }); - + // FileError Event (called when there was an error deleting a file) inst.on('fileError', (err) => { this.log('event', 'fileError'); this.log('error', `${chalk.red.bold('FILE ERROR:')} ${err.error}\n${chalk.gray(err.file)}`); }); - + // Finish Event (once processing/deleting all files is complete) inst.on('finish', (results) => { if(showProgress) process.stdout.write('\n'); this.log('event', 'finish'); - + this.log( 'verbose', `${chalk.green('FINISH')} Deleted ${chalk.yellow.bold(results.length)} files/folders of ${chalk.yellow.bold(this.stats.total)}` ); }); - + // Complete Event (once everything has completed) inst.on('complete', () => { this.log('event', 'complete'); }); } - + done(err, results) { console.log( "\n" + chalk.green.bold('FILES/FOLDERS DELETED') + "\n" + @@ -266,15 +268,15 @@ class ModClean_CLI { ` ${chalk.yellow.bold('Skipped: ')} ${this.stats.skipped.length}\n` + ` ${chalk.yellow.bold('Empty: ')} ${this.stats.totalEmpty}\n` ); - + setTimeout(() => { process.stdin.destroy(); - + if(err) { this.log('error', err); return process.exit(1); } - + process.exit(0); }, 500); } diff --git a/lib/modclean.js b/lib/modclean.js index f15dfde..9b6ab6a 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -10,6 +10,7 @@ const path = require('path'); const subdirs = require('subdirs'); const emptyDir = require('empty-dir'); const each = require('async-each-series'); +const fs = require('fs'); const Utils = require('./utils.js'); const EventEmitter = require('events').EventEmitter; @@ -74,7 +75,7 @@ let defaults = { */ emptyDirFilter: function(file) { if(/Thumbs\.db$/i.test(file) || /\.DS_Store$/i.test(file)) return false; - + return true; }, /** @@ -86,7 +87,12 @@ let defaults = { * Use test mode which will get the list of files and run the process without actually deleting the files (default `false`) * @type {Boolean} */ - test: false + test: false, + /** + * Force deletion to be done also in symlinked packages (when using npm link) (default `false`) + * @type {Boolean} + */ + followSymlink: false }; /** @@ -101,21 +107,21 @@ class ModClean extends EventEmitter { */ constructor(options, cb) { super(); - + this.utils = new Utils(this); this.errors = []; - + if(typeof options === 'function') cb = options; this.options = Object.assign({}, modclean.defaults, options && typeof options === 'object' ? options : {}); - + this._patterns = this.utils.initPatterns(this.options); - - if(this.options.modulesDir !== false && path.basename(this.options.cwd) !== this.options.modulesDir) + + if(this.options.modulesDir !== false && path.basename(this.options.cwd) !== this.options.modulesDir) this.options.cwd = path.join(this.options.cwd, this.options.modulesDir); - + if(cb) this.clean(cb); } - + /** * Automated clean process that finds and deletes items based on the ModClean options. * @param {Function} cb Callback to call once process is complete with `err` and `results`. @@ -123,29 +129,29 @@ class ModClean extends EventEmitter { */ clean(cb) { let opts = this.options; - + this.emit('start', this); - + let done = (err, results) => { this.emit('complete', err, results); if(typeof cb === 'function') cb(err, results); }; - + this._find((err, files) => { if(err) return done(err); - + this._process(files, (err, results) => { if(err || !opts.removeEmptyDirs) return done(err, results); - + this.cleanEmptyDirs((err, dirs) => { return done(err, Array.isArray(dirs)? results.concat(dirs) : results); }); }); }); - + return this; } - + /** * Finds files/folders based on the ModClean options. * @private @@ -160,16 +166,16 @@ class ModClean extends EventEmitter { ignore: this._patterns.ignore, nodir: opts.noDirs }; - + this.emit('beforeFind', this._patterns.allow, globOpts); - + glob(`**/@(${this._patterns.allow.join('|')})`, globOpts, (err, files) => { if(err) this.utils.error(err, '_find'); else this.emit('files', files); cb(err, files); }); } - + /** * Processes the found files and deletes the ones that pass `options.process`. * @private @@ -181,13 +187,16 @@ class ModClean extends EventEmitter { opts = this.options, processFn = typeof opts.process === 'function'? opts.process : function() { return true; }, results = []; - + if(!files.length) return cb(null, []); - + this.emit('process', files); - + each(files, function(file, callback) { - if(processFn.length <= 1) { + + if(!opts.followSymlink && fs.lstatSync(path.join(opts.cwd, path.parse(file).dir)).isSymbolicLink()) { + callback(); + } else if(processFn.length <= 1) { // If processFn has 0 or 1 argument (file), then assume sync if(processFn(file) !== false) self._deleteFile(file, (err) => { if(!err) results.push(file); @@ -213,7 +222,7 @@ class ModClean extends EventEmitter { cb(err, results); }); } - + /** * Deletes a single file/folder from the filesystem. * @private @@ -223,25 +232,25 @@ class ModClean extends EventEmitter { _deleteFile(file, cb) { let self = this, opts = this.options; - + function done() { self.emit('deleted', file); return cb(null, file); } - + // If test mode is enabled, just return the file. if(opts.test) return done(); - + rimraf(path.join(opts.cwd, file), (err) => { if(err) { this.utils.error(err, '_deleteFile', { file }, 'fileError'); return opts.errorHalt? cb(err, file) : cb(); } - + return done(); }); } - + /** * Finds and removes all empty directories. * @param {Function} cb Callback to call once complete. @@ -252,23 +261,23 @@ class ModClean extends EventEmitter { results = []; // If test mode is enabled or removeEmptyDirs is disabled, just return. if(opts.test || !opts.removeEmptyDirs) return cb(); - + this.emit('beforeEmptyDirs'); - + function done(err, res) { self.emit('afterEmptyDirs', results); return cb(err, res); } - + this._findEmptyDirs((err, dirs) => { if(err) return done(err); - + this._removeEmptyDirs(dirs, (err, res) => { return done(err, res); }); }); } - + /** * Finds all empty directories within `options.cwd`. * @private @@ -277,11 +286,11 @@ class ModClean extends EventEmitter { _findEmptyDirs(cb) { let self = this, results = []; - + subdirs(this.options.cwd, function(err, dirs) { if(err) self.utils.error(err, '_findEmptyDirs'); if(err || !Array.isArray(dirs)) return cb(err, []); - + each(dirs, (dir, dCb) => { emptyDir(dir, self.options.emptyDirFilter || function() { return true; }, (err, isEmpty) => { if(err) self.utils.error(err, '_findEmptyDirs'); @@ -295,7 +304,7 @@ class ModClean extends EventEmitter { }); }); } - + /** * Removes all empty directories provided in `dirs`. * @private @@ -305,7 +314,7 @@ class ModClean extends EventEmitter { _removeEmptyDirs(dirs, cb) { let self = this, results = []; - + each(dirs, (dir, dCb) => { rimraf(dir, (err) => { if(err) self.utils.error(err, '_removeEmptyDirs', { dir }, 'emptyDirError'); @@ -313,7 +322,7 @@ class ModClean extends EventEmitter { results.push(dir); self.emit('deletedEmptyDir', dir); } - + dCb(); }); }, (err) => { diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..2fd4d63 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,641 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +ansi-align@^1.1.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba" + dependencies: + string-width "^1.0.1" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +async-each-series@^1.1.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/async-each-series/-/async-each-series-1.1.0.tgz#f42fd8155d38f21a5b8ea07c28e063ed1700b138" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +boxen@^0.6.0: + version "0.6.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/boxen/-/boxen-0.6.0.tgz#8364d4248ac34ff0ef1b2f2bf49a6c60ce0d81b6" + dependencies: + ansi-align "^1.1.0" + camelcase "^2.1.0" + chalk "^1.1.1" + cli-boxes "^1.0.0" + filled-array "^1.0.0" + object-assign "^4.0.1" + repeating "^2.0.0" + string-width "^1.0.1" + widest-line "^1.0.0" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +camelcase@^2.1.0: + version "2.1.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +capture-stack-trace@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" + +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + +cli-color@0.3.2: + version "0.3.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/cli-color/-/cli-color-0.3.2.tgz#75fa5f728c308cc4ac594b05e06cc5d80daccd86" + dependencies: + d "~0.1.1" + es5-ext "~0.10.2" + memoizee "0.3.x" + timers-ext "0.1.x" + +clui@^0.3.1: + version "0.3.6" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/clui/-/clui-0.3.6.tgz#8e1e5cea7332a6e54083f59da0ccbe1d6f2fa787" + dependencies: + cli-color "0.3.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +commander@^2.9.0: + version "2.11.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +configstore@^2.0.0: + version "2.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1" + dependencies: + dot-prop "^3.0.0" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + object-assign "^4.0.1" + os-tmpdir "^1.0.0" + osenv "^0.1.0" + uuid "^2.0.1" + write-file-atomic "^1.1.2" + xdg-basedir "^2.0.0" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +create-error-class@^3.0.1: + version "3.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + dependencies: + capture-stack-trace "^1.0.0" + +d@1: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +d@~0.1.1: + version "0.1.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + dependencies: + es5-ext "~0.10.2" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +dot-prop@^3.0.0: + version "3.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" + dependencies: + is-obj "^1.0.0" + +duplexer2@^0.1.4: + version "0.1.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + dependencies: + readable-stream "^2.0.2" + +empty-dir@^0.2.1: + version "0.2.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/empty-dir/-/empty-dir-0.2.1.tgz#809ee48a1eb4ad1cb510c2572d66fd0ed84d01ab" + dependencies: + fs-exists-sync "^0.1.0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.11, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.5, es5-ext@~0.10.6: + version "0.10.30" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es5-ext/-/es5-ext-0.10.30.tgz#7141a16836697dbabfaaaeee41495ce29f52c939" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2: + version "2.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-symbol "^3.1" + +es6-iterator@~0.1.3: + version "0.1.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-iterator/-/es6-iterator-0.1.3.tgz#d6f58b8c4fc413c249b4baa19768f8e4d7c8944e" + dependencies: + d "~0.1.1" + es5-ext "~0.10.5" + es6-symbol "~2.0.1" + +es6-promise@^3.0.2: + version "3.3.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + +es6-symbol@^3.1, es6-symbol@~3.1: + version "3.1.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-symbol@~2.0.1: + version "2.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-symbol/-/es6-symbol-2.0.1.tgz#761b5c67cfd4f1d18afb234f691d678682cb3bf3" + dependencies: + d "~0.1.1" + es5-ext "~0.10.5" + +es6-weak-map@~0.1.4: + version "0.1.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-weak-map/-/es6-weak-map-0.1.4.tgz#706cef9e99aa236ba7766c239c8b9e286ea7d228" + dependencies: + d "~0.1.1" + es5-ext "~0.10.6" + es6-iterator "~0.1.3" + es6-symbol "~2.0.1" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +event-emitter@~0.3.4: + version "0.3.5" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +filled-array@^1.0.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84" + +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +glob@^7.0.5, glob@^7.1.1: + version "7.1.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +got@^5.0.0: + version "5.7.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35" + dependencies: + create-error-class "^3.0.1" + duplexer2 "^0.1.4" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + node-status-codes "^1.0.0" + object-assign "^4.0.1" + parse-json "^2.1.0" + pinkie-promise "^2.0.0" + read-all-stream "^3.0.0" + readable-stream "^2.0.5" + timed-out "^3.0.0" + unzip-response "^1.0.2" + url-parse-lax "^1.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@~2.0.3: + version "2.0.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + +is-stream@^1.0.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +latest-version@^2.0.0: + version "2.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b" + dependencies: + package-json "^2.0.0" + +lazy-req@^1.1.0: + version "1.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lazy-req/-/lazy-req-1.1.0.tgz#bdaebead30f8d824039ce0ce149d4daa07ba1fac" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lowercase-keys@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lru-queue@0.1: + version "0.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + dependencies: + es5-ext "~0.10.2" + +memoizee@0.3.x: + version "0.3.10" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/memoizee/-/memoizee-0.3.10.tgz#4eca0d8aed39ec9d017f4c5c2f2f6432f42e5c8f" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-weak-map "~0.1.4" + event-emitter "~0.3.4" + lru-queue "0.1" + next-tick "~0.2.2" + timers-ext "0.1" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@^0.5.0: + version "0.5.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +modclean-patterns-default@^1.0.0: + version "1.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/modclean-patterns-default/-/modclean-patterns-default-1.0.1.tgz#3a0de2c103b5cf9d7629ec5b6b2b72e716f35545" + +next-tick@1: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + +next-tick@~0.2.2: + version "0.2.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" + +node-status-codes@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +once@^1.3.0: + version "1.4.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.0: + version "0.1.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +package-json@^2.0.0: + version "2.4.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb" + dependencies: + got "^5.0.0" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" + +parse-json@^2.1.0: + version "2.2.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +rc@^1.0.1, rc@^1.1.6: + version "1.2.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-all-stream@^3.0.0: + version "3.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" + dependencies: + pinkie-promise "^2.0.0" + readable-stream "^2.0.0" + +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5: + version "2.3.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +registry-auth-token@^3.0.1: + version "3.3.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006" + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@^3.0.3: + version "3.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + dependencies: + rc "^1.0.1" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +rimraf@^2.5.4: + version "2.6.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + +safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + dependencies: + semver "^5.0.3" + +semver@^5.0.3, semver@^5.1.0: + version "5.4.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +slide@^1.1.5: + version "1.1.6" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +subdirs@^1.0.0: + version "1.0.1" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/subdirs/-/subdirs-1.0.1.tgz#d65264787476e4caf7efc5498fb740c69f626d48" + dependencies: + es6-promise "^3.0.2" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +timed-out@^3.0.0: + version "3.1.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217" + +timers-ext@0.1, timers-ext@0.1.x: + version "0.1.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/timers-ext/-/timers-ext-0.1.2.tgz#61cc47a76c1abd3195f14527f978d58ae94c5204" + dependencies: + es5-ext "~0.10.14" + next-tick "1" + +unzip-response@^1.0.2: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" + +update-notifier@^1.0.3: + version "1.0.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/update-notifier/-/update-notifier-1.0.3.tgz#8f92c515482bd6831b7c93013e70f87552c7cf5a" + dependencies: + boxen "^0.6.0" + chalk "^1.0.0" + configstore "^2.0.0" + is-npm "^1.0.0" + latest-version "^2.0.0" + lazy-req "^1.1.0" + semver-diff "^2.0.0" + xdg-basedir "^2.0.0" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^2.0.1: + version "2.0.3" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + +widest-line@^1.0.0: + version "1.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c" + dependencies: + string-width "^1.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^1.1.2: + version "1.3.4" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + slide "^1.1.5" + +xdg-basedir@^2.0.0: + version "2.0.0" + resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" + dependencies: + os-homedir "^1.0.0" From a7da6009369267a2bc5f847aa53fe62b76aad0fc Mon Sep 17 00:00:00 2001 From: Benjamin Santalucia Date: Tue, 5 Sep 2017 14:20:53 +0200 Subject: [PATCH 41/51] feat(symlinks): allow to include/exclude symlinked packages --- lib/modclean.js | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index 9b6ab6a..2ef410b 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -193,24 +193,34 @@ class ModClean extends EventEmitter { this.emit('process', files); each(files, function(file, callback) { - - if(!opts.followSymlink && fs.lstatSync(path.join(opts.cwd, path.parse(file).dir)).isSymbolicLink()) { - callback(); - } else if(processFn.length <= 1) { - // If processFn has 0 or 1 argument (file), then assume sync - if(processFn(file) !== false) self._deleteFile(file, (err) => { - if(!err) results.push(file); - callback(err); - }); - else callback(); - } else { - // If processFn more than 1 argument (file, cb), then assume async - processFn(file, function(result) { - if(result !== false) self._deleteFile(file, (err) => { + let next = () => { + if(processFn.length <= 1) { + // If processFn has 0 or 1 argument (file), then assume sync + if(processFn(file) !== false) self._deleteFile(file, (err) => { if(!err) results.push(file); callback(err); }); else callback(); + } else { + // If processFn more than 1 argument (file, cb), then assume async + processFn(file, function(result) { + if(result !== false) self._deleteFile(file, (err) => { + if(!err) results.push(file); + callback(err); + }); + else callback(); + }); + } + } + if(opts.followSymlink) { + next(); + } else { + fs.lstat(path.join(opts.cwd, path.parse(file).dir), (err, stat) => { + if(err || !stat.isSymbolicLink()) { + next(); + return; + } + callback(); }); } }, function(err) { From 2be468a9e612696653ba05f103166ba58e8846a3 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:16:11 -0500 Subject: [PATCH 42/51] Minor cleanup --- lib/modclean.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/modclean.js b/lib/modclean.js index 2ef410b..0b70aef 100644 --- a/lib/modclean.js +++ b/lib/modclean.js @@ -4,15 +4,15 @@ * @author Kyle Ross */ "use strict"; -const glob = require('glob'); -const rimraf = require('rimraf'); -const path = require('path'); -const subdirs = require('subdirs'); +const glob = require('glob'); +const rimraf = require('rimraf'); +const path = require('path'); +const subdirs = require('subdirs'); const emptyDir = require('empty-dir'); -const each = require('async-each-series'); -const fs = require('fs'); +const each = require('async-each-series'); +const fs = require('fs'); -const Utils = require('./utils.js'); +const Utils = require('./utils.js'); const EventEmitter = require('events').EventEmitter; let defaults = { @@ -88,11 +88,11 @@ let defaults = { * @type {Boolean} */ test: false, - /** + /** * Force deletion to be done also in symlinked packages (when using npm link) (default `false`) * @type {Boolean} */ - followSymlink: false + followSymlink: false }; /** @@ -211,7 +211,8 @@ class ModClean extends EventEmitter { else callback(); }); } - } + }; + if(opts.followSymlink) { next(); } else { From ecb2325289e8b7269dd4a1ad5a23168032e63967 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:16:35 -0500 Subject: [PATCH 43/51] Removing yarn.lock --- yarn.lock | 641 ------------------------------------------------------ 1 file changed, 641 deletions(-) delete mode 100644 yarn.lock diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 2fd4d63..0000000 --- a/yarn.lock +++ /dev/null @@ -1,641 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -ansi-align@^1.1.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba" - dependencies: - string-width "^1.0.1" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -async-each-series@^1.1.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/async-each-series/-/async-each-series-1.1.0.tgz#f42fd8155d38f21a5b8ea07c28e063ed1700b138" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -boxen@^0.6.0: - version "0.6.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/boxen/-/boxen-0.6.0.tgz#8364d4248ac34ff0ef1b2f2bf49a6c60ce0d81b6" - dependencies: - ansi-align "^1.1.0" - camelcase "^2.1.0" - chalk "^1.1.1" - cli-boxes "^1.0.0" - filled-array "^1.0.0" - object-assign "^4.0.1" - repeating "^2.0.0" - string-width "^1.0.1" - widest-line "^1.0.0" - -brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -camelcase@^2.1.0: - version "2.1.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -capture-stack-trace@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - -cli-color@0.3.2: - version "0.3.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/cli-color/-/cli-color-0.3.2.tgz#75fa5f728c308cc4ac594b05e06cc5d80daccd86" - dependencies: - d "~0.1.1" - es5-ext "~0.10.2" - memoizee "0.3.x" - timers-ext "0.1.x" - -clui@^0.3.1: - version "0.3.6" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/clui/-/clui-0.3.6.tgz#8e1e5cea7332a6e54083f59da0ccbe1d6f2fa787" - dependencies: - cli-color "0.3.2" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -commander@^2.9.0: - version "2.11.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -configstore@^2.0.0: - version "2.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1" - dependencies: - dot-prop "^3.0.0" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - object-assign "^4.0.1" - os-tmpdir "^1.0.0" - osenv "^0.1.0" - uuid "^2.0.1" - write-file-atomic "^1.1.2" - xdg-basedir "^2.0.0" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -create-error-class@^3.0.1: - version "3.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - dependencies: - capture-stack-trace "^1.0.0" - -d@1: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -d@~0.1.1: - version "0.1.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" - dependencies: - es5-ext "~0.10.2" - -deep-extend@~0.4.0: - version "0.4.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" - -dot-prop@^3.0.0: - version "3.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" - dependencies: - is-obj "^1.0.0" - -duplexer2@^0.1.4: - version "0.1.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - dependencies: - readable-stream "^2.0.2" - -empty-dir@^0.2.1: - version "0.2.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/empty-dir/-/empty-dir-0.2.1.tgz#809ee48a1eb4ad1cb510c2572d66fd0ed84d01ab" - dependencies: - fs-exists-sync "^0.1.0" - -error-ex@^1.2.0: - version "1.3.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" - dependencies: - is-arrayish "^0.2.1" - -es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.11, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.5, es5-ext@~0.10.6: - version "0.10.30" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es5-ext/-/es5-ext-0.10.30.tgz#7141a16836697dbabfaaaeee41495ce29f52c939" - dependencies: - es6-iterator "2" - es6-symbol "~3.1" - -es6-iterator@2: - version "2.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-symbol "^3.1" - -es6-iterator@~0.1.3: - version "0.1.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-iterator/-/es6-iterator-0.1.3.tgz#d6f58b8c4fc413c249b4baa19768f8e4d7c8944e" - dependencies: - d "~0.1.1" - es5-ext "~0.10.5" - es6-symbol "~2.0.1" - -es6-promise@^3.0.2: - version "3.3.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" - -es6-symbol@^3.1, es6-symbol@~3.1: - version "3.1.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-symbol@~2.0.1: - version "2.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-symbol/-/es6-symbol-2.0.1.tgz#761b5c67cfd4f1d18afb234f691d678682cb3bf3" - dependencies: - d "~0.1.1" - es5-ext "~0.10.5" - -es6-weak-map@~0.1.4: - version "0.1.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/es6-weak-map/-/es6-weak-map-0.1.4.tgz#706cef9e99aa236ba7766c239c8b9e286ea7d228" - dependencies: - d "~0.1.1" - es5-ext "~0.10.6" - es6-iterator "~0.1.3" - es6-symbol "~2.0.1" - -escape-string-regexp@^1.0.2: - version "1.0.5" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -event-emitter@~0.3.4: - version "0.3.5" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - -filled-array@^1.0.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84" - -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -glob@^7.0.5, glob@^7.1.1: - version "7.1.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -got@^5.0.0: - version "5.7.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35" - dependencies: - create-error-class "^3.0.1" - duplexer2 "^0.1.4" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - node-status-codes "^1.0.0" - object-assign "^4.0.1" - parse-json "^2.1.0" - pinkie-promise "^2.0.0" - read-all-stream "^3.0.0" - readable-stream "^2.0.5" - timed-out "^3.0.0" - unzip-response "^1.0.2" - url-parse-lax "^1.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@~2.0.3: - version "2.0.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@~1.3.0: - version "1.3.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-npm@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - -is-stream@^1.0.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -latest-version@^2.0.0: - version "2.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b" - dependencies: - package-json "^2.0.0" - -lazy-req@^1.1.0: - version "1.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lazy-req/-/lazy-req-1.1.0.tgz#bdaebead30f8d824039ce0ce149d4daa07ba1fac" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - -lowercase-keys@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - -lru-queue@0.1: - version "0.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - dependencies: - es5-ext "~0.10.2" - -memoizee@0.3.x: - version "0.3.10" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/memoizee/-/memoizee-0.3.10.tgz#4eca0d8aed39ec9d017f4c5c2f2f6432f42e5c8f" - dependencies: - d "~0.1.1" - es5-ext "~0.10.11" - es6-weak-map "~0.1.4" - event-emitter "~0.3.4" - lru-queue "0.1" - next-tick "~0.2.2" - timers-ext "0.1" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.2.0: - version "1.2.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -mkdirp@^0.5.0: - version "0.5.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -modclean-patterns-default@^1.0.0: - version "1.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/modclean-patterns-default/-/modclean-patterns-default-1.0.1.tgz#3a0de2c103b5cf9d7629ec5b6b2b72e716f35545" - -next-tick@1: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - -next-tick@~0.2.2: - version "0.2.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" - -node-status-codes@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -once@^1.3.0: - version "1.4.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.0: - version "0.1.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -package-json@^2.0.0: - version "2.4.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb" - dependencies: - got "^5.0.0" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" - -parse-json@^2.1.0: - version "2.2.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -rc@^1.0.1, rc@^1.1.6: - version "1.2.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -read-all-stream@^3.0.0: - version "3.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" - dependencies: - pinkie-promise "^2.0.0" - readable-stream "^2.0.0" - -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5: - version "2.3.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -registry-auth-token@^3.0.1: - version "3.3.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006" - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@^3.0.3: - version "3.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - dependencies: - rc "^1.0.1" - -repeating@^2.0.0: - version "2.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - -rimraf@^2.5.4: - version "2.6.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" - dependencies: - glob "^7.0.5" - -safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - dependencies: - semver "^5.0.3" - -semver@^5.0.3, semver@^5.1.0: - version "5.4.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" - -slide@^1.1.5: - version "1.1.6" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -subdirs@^1.0.0: - version "1.0.1" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/subdirs/-/subdirs-1.0.1.tgz#d65264787476e4caf7efc5498fb740c69f626d48" - dependencies: - es6-promise "^3.0.2" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -timed-out@^3.0.0: - version "3.1.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217" - -timers-ext@0.1, timers-ext@0.1.x: - version "0.1.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/timers-ext/-/timers-ext-0.1.2.tgz#61cc47a76c1abd3195f14527f978d58ae94c5204" - dependencies: - es5-ext "~0.10.14" - next-tick "1" - -unzip-response@^1.0.2: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" - -update-notifier@^1.0.3: - version "1.0.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/update-notifier/-/update-notifier-1.0.3.tgz#8f92c515482bd6831b7c93013e70f87552c7cf5a" - dependencies: - boxen "^0.6.0" - chalk "^1.0.0" - configstore "^2.0.0" - is-npm "^1.0.0" - latest-version "^2.0.0" - lazy-req "^1.1.0" - semver-diff "^2.0.0" - xdg-basedir "^2.0.0" - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - dependencies: - prepend-http "^1.0.1" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -uuid@^2.0.1: - version "2.0.3" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" - -widest-line@^1.0.0: - version "1.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c" - dependencies: - string-width "^1.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -write-file-atomic@^1.1.2: - version "1.3.4" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - slide "^1.1.5" - -xdg-basedir@^2.0.0: - version "2.0.0" - resolved "https://maven.rabobank.nl/nexus/content/groups/npmjs/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" - dependencies: - os-homedir "^1.0.0" From 0810064c112b9a640aadcdb93ab823ea1992d816 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:24:50 -0500 Subject: [PATCH 44/51] Update contributors --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d07d04..3754052 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,10 @@ "files", "folders" ], - "author": "Kyle Ross", + "author": "Kyle Ross ", + "contributors": [ + "Benjamin Santalucia (https://github.com/ben8p)" + ], "license": "MIT", "bugs": { "url": "https://github.com/ModClean/modclean/issues" From 9f764d07b42c2c63105821b8f1cdef0f3d642c3f Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:25:04 -0500 Subject: [PATCH 45/51] Cleanup formatting --- bin/modclean.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/modclean.js b/bin/modclean.js index 8a80fb8..2bb1242 100755 --- a/bin/modclean.js +++ b/bin/modclean.js @@ -1,15 +1,15 @@ #!/usr/bin/env node "use strict"; -const chalk = require('chalk'); -const program = require('commander'); +const chalk = require('chalk'); +const program = require('commander'); const notifier = require('update-notifier'); -const clui = require('clui'); -const path = require('path'); -const os = require('os'); -const pkg = require('../package.json'); +const clui = require('clui'); +const path = require('path'); +const os = require('os'); +const pkg = require('../package.json'); -const utils = require('./utils'); +const utils = require('./utils'); const modclean = require('../lib/modclean'); const ModClean = modclean.ModClean; @@ -36,7 +36,7 @@ program .option('-P, --no-progress', 'Hide progress bar') .option('-e, --error-halt', 'Halt script on error') .option('-v, --verbose', 'Run in verbose mode') - .option('-f, --follow-symlink', 'Clean symlinked packages as well') + .option('-f, --follow-symlink', 'Clean symlinked packages as well') .option('-r, --run', 'Run immediately without warning') .option('-n, --patterns ', 'Patterns plugins/rules to use (defaults to "default:safe")', list) .option('-a, --additional-patterns ', 'Additional glob patterns to search for', list) From 7f08d6b90f05e15e6a228387d9b217a432045254 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:25:14 -0500 Subject: [PATCH 46/51] Add .eslintrc.yml --- .eslintrc.yml | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .eslintrc.yml diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..04c34f1 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,60 @@ +--- +root: true + +parserOptions: + ecmaVersion: 7 + +env: + node: true + es6: true + mocha: true + jasmine: true + +extends: 'eslint:recommended' + +rules: + indent: [2, 4, { "SwitchCase": 1 }] + linebreak-style: [2, "unix"] + semi: [2, "always"] + comma-dangle: [2, "never"] + consistent-return: 0 + no-plusplus: [2, { "allowForLoopAfterthoughts": true }] + eqeqeq: [2, "smart"] + wrap-iife: [2, "any"] + no-empty-function: 2 + no-console: 0 + no-mixed-spaces-and-tabs: 2 + no-whitespace-before-property: 2 + space-before-function-paren: [2, "never"] + space-in-parens: [2, "never"] + array-callback-return: 2 + class-methods-use-this: 0 + dot-notation: [2, { "allowKeywords": true }] + no-alert: 1 + no-caller: 2 + no-else-return: 2 + no-eval: 2 + no-extend-native: 2 + no-extra-bind: 2 + no-floating-decimal: 2 + no-implied-eval: 2 + no-iterator: 2 + no-lone-blocks: 2 + no-multi-spaces: 2 + no-new-wrappers: 2 + no-octal: 2 + no-proto: 2 + no-redeclare: 2 + no-self-assign: 2 + no-self-compare: 2 + no-throw-literal: 2 + no-useless-concat: 2 + no-useless-escape: 2 + no-useless-return: 2 + no-with: 2 + yoda: 2 + no-dupe-keys: 2 + use-isnan: 2 + no-unreachable: 2 + no-tabs: 2 + object-curly-spacing: [2, "always"] From 5eaceaa0858cb37e14137f64f9876091c158581d Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:36:44 -0500 Subject: [PATCH 47/51] 2.1.1 --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 19da971..f762f69 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # ModClean History +## 2.1.1 (11/6/2017) +* **New!** Added options to ignore symlinked modules (@ben8p) #15 + - Added `followSymlink` configuration option. + - Added `-f`, `--follow-symlink` CLI option. +* Added `.eslintrc.yml` file to help enforce rules. +* Minor cleanup + ## 2.1.0 (1/17/2017) * Added `emptyDirFilter` configuration option. * Use `Object.assign` instead of `extend` module. From 9ffaf9b3cfbe2da9d5afacead040ea02a0c80668 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 13:57:57 -0500 Subject: [PATCH 48/51] 2.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3754052..bbcce65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modclean", - "version": "2.1.0", + "version": "2.1.1", "description": "Remove unwanted files and directories from your node_modules folder", "main": "index.js", "bin": { From 4a088ff49588f96714b420d6923337455417226b Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 14:08:32 -0500 Subject: [PATCH 49/51] Force modclean to use latest version of --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bbcce65..252096f 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "empty-dir": "^0.2.1", "glob": "^7.1.1", "lodash.uniq": "^4.5.0", - "modclean-patterns-default": "^1.0.0", + "modclean-patterns-default": "latest", "rimraf": "^2.5.4", "subdirs": "^1.0.0", "update-notifier": "^1.0.3" From 7891fdfd84000ab0845489d5b55b59cfc9229959 Mon Sep 17 00:00:00 2001 From: Kyle Ross Date: Mon, 6 Nov 2017 14:10:57 -0500 Subject: [PATCH 50/51] 2.1.2 --- HISTORY.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index f762f69..4547cf3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,8 @@ # ModClean History +## 2.1.2 (11/6/2017) +* Update package.json to use latest version of `modclean-patterns-default` + ## 2.1.1 (11/6/2017) * **New!** Added options to ignore symlinked modules (@ben8p) #15 - Added `followSymlink` configuration option. diff --git a/package.json b/package.json index 252096f..9fce442 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modclean", - "version": "2.1.1", + "version": "2.1.2", "description": "Remove unwanted files and directories from your node_modules folder", "main": "index.js", "bin": { From 17aeb4518acb84dacc260af001fb7e31a1e550de Mon Sep 17 00:00:00 2001 From: Shanan Holm Date: Tue, 19 Dec 2017 09:51:07 +1300 Subject: [PATCH 51/51] Fix minor typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ac2dff..e6bd6aa 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ModClean is a utility that finds and removes unnecessary files and folders from ## Why? There are a few different reasons why you would want to use ModClean: -* **Commiting Modules.** Some evironments (especially Enterprise), it's required to commit the `node_modules` directory with your application into version control. This is due to compatibility, vetting and vunerability scanning rules for open source software. This can lead to issues with project size, checking out/pulling changes and the infamous 255 character path limit if you're unlucky enough to be on Windows or SVN. +* **Commiting Modules.** Some environments (especially Enterprise), it's required to commit the `node_modules` directory with your application into version control. This is due to compatibility, vetting and vunerability scanning rules for open source software. This can lead to issues with project size, checking out/pulling changes and the infamous 255 character path limit if you're unlucky enough to be on Windows or SVN. * **Wasted space on your server.** Why waste space on your server with files not needed by you or the modules? * **Packaged applications.** If you're required to package your application, you can reduce the size of the package quickly by removing unneeded files. * **Compiled applications.** Other tools like, [NW.js](https://nwjs.io/) and [Electron](http://electron.atom.io/) make it easy to create cross-platform desktop apps, but depending on the modules, your app can become huge. Reduce down the size of the compiled application before shipping and make it faster for users to download.