123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- #!/usr/bin/env node
- 'use strict';
- const resolve = require('path').resolve;
- const pkg = require('../package.json');
- const printable = require('printable');
- const minimatch = require('minimatch');
- const program = require('commander');
- const pjoin = require('path').join;
- const util = require('util');
- const path = require('path');
- const Autod = require('..');
- const fs = require('fs');
- require('colors');
- const argv = program
- .version(pkg.version)
- .option('-p, --path [root path]', 'the root path to be parse', '.')
- .option('-t, --test <test/benchmark/example directory paths>', 'modules in these paths will be tread as devDependencies')
- .option('-e, --exclude <exclude directory path>', 'exclude parse directory, split by `,`')
- .option('-r, --registry <remote registry>', 'get latest version from which registry')
- .option('-f, --prefix [version prefix]', 'version prefix, can be `~` or `^`')
- .option('-F, --devprefix [dev dependencies version prefix]', 'develop dependencies version prefix, can be `~` or `^`')
- .option('-w, --write', 'write dependencies into package.json')
- .option('-i, --ignore', 'ignore errors, display the dependencies or write the dependencies.')
- .option('-m, --map', 'display all the dependencies require by which file')
- .option('-d, --dep <dependently modules>', 'modules that not require in source file, but you need them as dependencies')
- .option('-D, --devdep <dev dependently modules>', 'modules that not require in source file, but you need them in as devDependencies')
- .option('-k, --keep <dependently modules>', 'modules that you want to keep version in package.json file')
- .option('-s, --semver <dependencies@version>', 'auto update these modules within the specified semver')
- .option('-n, --notransform', 'disable transfrom es next, don\'t support es6 modules')
- .option('-P, --plugin <name>', 'plugin module name')
- .option('--check', 'check missing dependencies and devDependencies')
- .parse(process.argv);
- let options = {};
- let confPath;
- try {
- confPath = require.resolve(path.resolve('.autod.conf'));
- options = require(confPath);
- } catch (err) {
- if (err.code !== 'MODULE_NOT_FOUND') {
- console.error('load config %s error:', confPath);
- console.error(err.stack);
- }
- // ignore
- }
- for (const key in argv) {
- if (argv[key] !== undefined) {
- options[key] = argv[key];
- }
- }
- [ 'exclude', 'dep', 'devdep', 'test', 'keep' ].forEach(function(key) {
- options[key] = split(options[key]);
- });
- options.semver = processSemver(options.semver);
- if (options.prefix && options.prefix !== '^') {
- options.prefix = '~';
- }
- if (options.devprefix && options.devprefix !== '^') {
- options.devprefix = '~';
- }
- options.root = options.path = options.path || '.';
- // don't write when check
- if (options.check) options.write = false;
- // get registry from pacakge.json
- // default to Chinese Mirror
- if (!options.registry) {
- let modulePackage;
- try {
- modulePackage = fs.readFileSync('package.json', 'utf8');
- modulePackage = JSON.parse(modulePackage);
- } catch (err) {
- modulePackage = {};
- }
- options.registry = 'https://registry.npmjs.org';
- // get from npm env
- if (process.env.npm_config_registry) options.registry = process.env.npm_config_registry;
- // get from package.json
- if (modulePackage.publishConfig && modulePackage.publishConfig.registry) {
- options.registry = modulePackage.publishConfig.registry;
- }
- }
- const autod = new Autod(options);
- if (options.check) {
- const allDeps = autod.findDependencies();
- const result = { dependencies: {}, devDependencies: {} };
- allDeps.dependencies.forEach(dep => {
- result.dependencies[dep] = true;
- });
- allDeps.devDependencies.forEach(dep => {
- result.devDependencies[dep] = true;
- });
- comparePackage(result);
- } else {
- autod.findVersions().then(result => {
- result.map = autod.dependencyMap;
- if (autod.errors.length) {
- autod.errors.forEach(err => {
- console.error('[ERROR]'.red, err.message);
- });
- if (!options.ignore) process.exit(1);
- }
- log('\n[DEPENDENCIES]\n'.green);
- comparePackage(result);
- if (options.map) {
- log('\n[DEPENDENCY MAP]'.green);
- printDependencyMap(result.map);
- }
- });
- }
- function comparePackage(result) {
- let pkgInfo;
- let pkgStr;
- let pkgExist = true;
- const pkgPath = pjoin(resolve(options.path), 'package.json');
- // add prefix
- if (options.prefix) {
- for (const key in result.dependencies) {
- result.dependencies[key] = options.prefix + result.dependencies[key];
- }
- }
- const devprefix = options.devprefix ? options.devprefix : options.prefix;
- if (devprefix) {
- for (const key in result.devDependencies) {
- result.devDependencies[key] = devprefix + result.devDependencies[key];
- }
- }
- try {
- pkgInfo = require(pkgPath);
- pkgStr = fs.readFileSync(pkgPath, 'utf-8');
- } catch (err) {
- if (err.code === 'MODULE_NOT_FOUND') {
- pkgInfo = {};
- pkgExist = false;
- } else {
- log(output(result));
- console.error('`package.json` parsed error: %s', err.message);
- process.exit(1);
- }
- }
- if (!pkgExist) {
- log(output(result));
- if (options.write) {
- console.log('[WARN]'.yellow + ' `package.json` not exist, auto generate and write dependencies.');
- fs.writeFileSync(pkgPath, '{\n' + output(result) + '\n}\n', 'utf-8');
- }
- process.exit(0);
- }
- if (options.keep) {
- // keep these modules version, won't change by autod
- options.keep.forEach(function(key) {
- for (const pkgKey in result.dependencies) {
- if (minimatch(pkgKey, key)) {
- delete result.dependencies[pkgKey];
- }
- }
- for (const pkgKey in result.devDependencies) {
- if (minimatch(pkgKey, key)) {
- delete result.devDependencies[pkgKey];
- }
- }
- const dependencies = pkgInfo.dependencies;
- const devDependencies = pkgInfo.devDependencies;
- for (const pkgKey in dependencies) {
- if (minimatch(pkgKey, key)) {
- result.dependencies[pkgKey] = dependencies[pkgKey];
- }
- }
- for (const pkgKey in devDependencies) {
- if (minimatch(pkgKey, key)) {
- result.devDependencies[key] = devDependencies[key];
- }
- }
- });
- }
- if (pkgInfo.dependencies) {
- pkgStr = pkgStr.replace(/( |\t)*"dependencies"\s*:\s*{(.|\n)*?}/,
- outputDep('dependencies', result.dependencies));
- } else {
- pkgStr = pkgStr.replace(/(\s*)(\}\n*\s*)$/, function(end, before, after) {
- return ',' + before + outputDep('dependencies', result.dependencies) + '\n' + after;
- });
- }
- if (pkgInfo.devDependencies) {
- // merge parsed into devDependencies
- for (const key in pkgInfo.devDependencies) {
- if (!result.devDependencies[key]) {
- result.devDependencies[key] = pkgInfo.devDependencies[key];
- }
- }
- pkgStr = pkgStr.replace(/( |\t)*"devDependencies"\s*:\s*{(.|\n)*?}/,
- outputDep('devDependencies', result.devDependencies));
- } else {
- pkgStr = pkgStr.replace(/(\s*)(\}\n*\s*)$/, function(end, before, after) {
- return ',' + before + outputDep('devDependencies', result.devDependencies) + '\n' + after;
- });
- }
- log(output(result));
- printUpdates('Dependencies', result.dependencies, pkgInfo.dependencies, true);
- printUpdates('DevDependencies', result.devDependencies, pkgInfo.devDependencies);
- if (options.write) {
- console.log('[INFO]'.green + ' Write dependencies into package.json.');
- fs.writeFileSync(pkgPath, pkgStr, 'utf-8');
- }
- if (options.check) {
- const missingDependencies = [];
- const missingDevDependencies = [];
- const deps = result.dependencies || {};
- const old = pkgInfo.dependencies || {};
- for (const key in deps) {
- if (!old[key]) {
- missingDependencies.push(key);
- }
- }
- const devDeps = result.devDependencies || {};
- const devOld = pkgInfo.devDependencies || {};
- for (const key in devDeps) {
- if (!devOld[key] && !old[key]) {
- missingDevDependencies.push(key);
- }
- }
- if (missingDependencies.length > 0) {
- console.error('[ERROR]'.red + ' Missing dependencies: ' +
- JSON.stringify(missingDependencies) + '\n');
- }
- if (missingDevDependencies.length > 0) {
- console.error('[ERROR]'.red + ' Missing devDependencies: ' +
- JSON.stringify(missingDevDependencies) + '\n');
- }
- if (missingDependencies.length > 0 || missingDevDependencies.length > 0) {
- process.exit(1);
- }
- }
- }
- function processSemver(semver) {
- semver = semver || [];
- if (typeof semver === 'string') {
- semver = semver.split(/\s*,\s*/);
- }
- const map = {};
- semver.forEach(function(m) {
- let prefix = '';
- if (m.indexOf('@') === 0) {
- prefix = '@';
- m = m.slice(1);
- }
- m = m.split('@');
- if (m.length !== 2) return;
- map[prefix + m[0]] = m[1];
- });
- return map;
- }
- function outputDep(name, values) {
- let str = util.format(' "%s": {\n', name);
- const deps = [];
- for (const key in values) {
- deps.push(util.format(' "%s": "%s"', key, values[key]));
- }
- str += deps.sort().join(',\n') + '\n }';
- return str;
- }
- function output(result) {
- let str = outputDep('dependencies', result.dependencies);
- if (!Object.keys(result.devDependencies).length) {
- return str;
- }
- str += ',\n' + outputDep('devDependencies', result.devDependencies);
- return str;
- }
- function printUpdates(title, latest, old, remove) {
- latest = latest || {};
- old = old || {};
- const arr = [[ 'Package Name', 'Old Version', 'Latest Version' ]];
- for (const key in latest) {
- if (!old[key]) {
- arr.push([ key, '-', latest[key] ]);
- } else if (old[key] !== latest[key]) {
- arr.push([ key, old[key], latest[key] ]);
- }
- }
- if (remove) {
- for (const key in old) {
- if (!latest[key]) {
- arr.push([ key, old[key], 'remove' ]);
- }
- }
- }
- if (arr.length > 1) {
- log((title + ' updates').yellow + '\n');
- log(printable.print(arr));
- log();
- } else {
- log(('nothing to update in ' + title).green + '\n');
- }
- }
- function printDependencyMap(map) {
- Object.keys(map).sort().forEach(function(name) {
- console.log((' ' + name).cyan);
- console.log((' ' + map[name].join('\n ')).grey);
- });
- }
- function split(str) {
- if (typeof str === 'string') {
- return str.split(/\s*,\s*/);
- }
- return str;
- }
- function log() {
- if (options.check) return;
- console.log.apply(console, arguments);
- }
|