├── .gitignore
├── LICENSE
├── README.md
├── package.json
└── src
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Brian Mathews
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
recently-updated
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | See which packages you depend on were recently updated
14 |
15 |
16 | ***
17 |
18 | This cli tool shows you if packages you depend on had new versions published within the last 24 hours. If your CI builds start failing all of a sudden, or builds/tests are broken locally after a fresh npm install, seeing this list of packages and their latest publish times/versions might help you track the problem down quicker.
19 |
20 | ### Installation
21 |
22 | With npm, do:
23 | ```
24 | npm install -g recently-updated
25 | ```
26 |
27 | ### Usage
28 | In a project directory with a `./node_modules`, do:
29 | ```
30 | recently-updated
31 | ```
32 |
33 | ### Options
34 | Specify a cutoff by passing in `--hours`, `--days`, or `--weeks` (defaults to 24 hours):
35 | ```
36 | recently-updated --days 3
37 | ```
38 |
39 |
40 |

41 |
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "recently-updated",
3 | "version": "1.0.3",
4 | "main": "src/index.js",
5 | "description": "See which packages you depend on were recently updated",
6 | "bin": {
7 | "recently-updated": "./src/index.js"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "author": "Brian Mathews ",
13 | "license": "MIT",
14 | "dependencies": {
15 | "argparse": "^1.0.9",
16 | "async.map": "^0.5.2",
17 | "chalk": "^1.1.3",
18 | "mute": "^2.0.6",
19 | "npm": "^3.10.9",
20 | "semver": "^5.3.0"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "https://github.com/bmathews/recently-updated.git"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | var npm = require('npm');
4 | var asyncMap = require('async.map');
5 | var semver = require('semver');
6 | var chalk = require('chalk');
7 | var inspect = require('util').inspect;
8 | var ArgumentParser = require('argparse').ArgumentParser;
9 | var pkg = require('../package.json');
10 |
11 | var parser = new ArgumentParser({
12 | version: pkg.version,
13 | addHelp: true,
14 | description: 'List recently updated npm packages. ' +
15 | 'If no options are passed, it will look for packages updated within ' +
16 | 'the last 24 hours. Multiple cutoff arguments can be combined by ' +
17 | 'adding them together.'
18 | });
19 | parser.addArgument(
20 | [ '--hours' ],
21 | {
22 | help: 'time cutoff in hours',
23 | }
24 | );
25 | parser.addArgument(
26 | [ '-d', '--days' ],
27 | {
28 | help: 'time cutoff in days'
29 | }
30 | );
31 | parser.addArgument(
32 | [ '-w', '--weeks' ],
33 | {
34 | help: 'time cutoff in weeks'
35 | }
36 | );
37 | var args = parser.parseArgs();
38 |
39 | const _WEEKS = parseInt(args['weeks']) || 0;
40 | const _DAYS = parseInt(args['days']) || 0;
41 | const _HOURS = parseInt(args['hours']) || 0;
42 |
43 | var HOURS = _HOURS + 24 * (_DAYS + 7 * _WEEKS);
44 | if (!HOURS) {
45 | HOURS = 24;
46 | }
47 |
48 | npm.load({outfd: null}, function () {
49 | npm.commands.ls([], true, function (er, tree) {
50 | var flat = flatten(tree);
51 | addTimes(flat, function () {
52 | var lastFound = null;
53 | for (var key in flat) {
54 | var dep = flat[key];
55 | var nameDisplayed = false;
56 | for (var version in dep.times) {
57 | var time = dep.times[version];
58 | if (new Date().getTime() - new Date(time).getTime() < HOURS * 3600000) {
59 | dep.semvers.forEach(function (s) {
60 | if (semver.satisfies(version, s)) {
61 | lastFound = dep.name;
62 | if (!nameDisplayed) {
63 | console.log('\n' + dep.name + chalk.dim('\n\tfrom ranges [' + dep.semvers.join(', ') + ']'));
64 | nameDisplayed = true;
65 | }
66 | console.log(chalk.yellow('\t' + version + '\t' + time.toLocaleString()));
67 | }
68 | });
69 | }
70 | }
71 | }
72 |
73 | if (lastFound) {
74 | console.log('\n--------')
75 | console.log('(use "npm ls ' + lastFound + '" to see what depends on that package)');
76 | console.log('(use "npm issues ' + lastFound + '" to view the issues for that package)');
77 | } else {
78 | console.warn('No recently updated dependencies were found');
79 | }
80 | });
81 | });
82 | });
83 |
84 | // flatten the tree and de-dupe
85 | function flatten(tree, out, parent) {
86 | if (!out) out = {};
87 |
88 | // recursively iterate over tree
89 | if (tree.dependencies) {
90 | for (var dep in tree.dependencies) {
91 | out = flatten(tree.dependencies[dep], out, tree);
92 | }
93 | }
94 |
95 | if (tree.name) {
96 | // name -> details
97 | out[tree.name] = out[tree.name] || {
98 | name: tree.name,
99 | semvers: [],
100 | versions: {}
101 | };
102 |
103 | // out['babel'].versions['0.1.3'] -> { version, parent }
104 | out[tree.name].versions[tree.version] = {
105 | version: tree.version,
106 | parent: parent ? out[parent.name] : null
107 | };
108 |
109 | // rawSpec is the semver range that ended up pulling this package
110 | if (tree._requested) {
111 | out[tree.name].semvers.push(tree._requested.rawSpec);
112 | }
113 | }
114 |
115 | return out;
116 | }
117 |
118 | // fetch all the times a package was published
119 | function getTimesForPackage(dep, cb) {
120 | npm.commands.view([dep.name, 'time'], true, function (err, data) {
121 | cb(null, data);
122 | });
123 | }
124 |
125 | // fetch and merge in publish times into the flattened tree
126 | function addTimes(flat, cb) {
127 | var keys = Object.keys(flat);
128 | asyncMap(keys, function (key, mapcb) {
129 | const dep = flat[key];
130 | return getTimesForPackage(dep, function (err, res) {
131 | if (err || !res) {
132 | mapcb(null, dep);
133 | } else {
134 | const versionKeys = Object.keys(res);
135 | if (!versionKeys.length) {
136 | dep.times = {};
137 | mapcb(null, dep);
138 | } else {
139 | var versionMap = res[versionKeys[0]].time;
140 | var versions = Object.keys(versionMap);
141 | versions.splice(0, 2);
142 | dep.times = {};
143 | versions.forEach(function (v) {
144 | dep.times[v] = new Date(versionMap[v]);
145 | });
146 | mapcb(null, dep);
147 | }
148 |
149 | }
150 | });
151 | }, function (err, results) {
152 | cb(flat);
153 | });
154 | }
155 |
--------------------------------------------------------------------------------