Skip to content

Commit

Permalink
Add initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kenzable committed Nov 21, 2018
1 parent f55bdc9 commit b008486
Show file tree
Hide file tree
Showing 12 changed files with 8,000 additions and 63 deletions.
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/package-lock.json
/reports
/templates
/tests
12 changes: 12 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "lifion",
"rules": {
"global-require": "off",
"import/no-dynamic-require": "off",
"no-console": "off",
"no-process-exit": "off",
"node/shebang": "off",
"security/detect-child-process": "off",
"security/detect-non-literal-require": "off"
}
}
65 changes: 4 additions & 61 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,61 +1,4 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next
.DS_Store
/.eslintcache
/node_modules
/reports
5 changes: 5 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.test.js
/.*
/docs
/reports
/templates
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stable
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,21 @@
# lifion-check-dependencies
Check that installed NPM modules are the latest-currently available version.
# lifion-verify-deps

Verifies that installed NPM modules are the latest currently available version.

## Getting Started

To install the module:

```sh
npm install lifion-verify-deps --save
```

To run:

```sh
lifion-verify-deps
```

## License

[MIT](./LICENSE)
18 changes: 18 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env node

'use strict';

const verifyDeps = require('../lib');

async function run() {
const currentDir = process.cwd();
try {
await verifyDeps(currentDir);
process.exit(0);
} catch (err) {
console.error(err.message);
process.exit(1);
}
}

run();
89 changes: 89 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict';

const chalk = require('chalk');
const path = require('path');
const semver = require('semver');
const { exec } = require('child_process');
const { promisify } = require('util');

const execAsync = promisify(exec);
const pkgs = [];

async function getLatestVersions(name) {
const { stdout } = await execAsync(`npm view ${name} versions --json`);
try {
return JSON.parse(stdout);
} catch (err) {
return [];
}
}

async function getLatestVersion(name, wanted) {
const versions = await getLatestVersions(name);
const applicableVersions = versions.filter(i => semver.satisfies(i, wanted));
applicableVersions.sort((a, b) => semver.rcompare(a, b));
return applicableVersions[0];
}

function getInstalledVersion(currentDir, name) {
try {
return require(path.join(currentDir, 'node_modules', name, 'package.json')).version;
} catch (err) {
return null;
}
}

function pushPkgs(currentDir, deps = {}, type) {
return Object.keys(deps).map(async name => {
let wanted = deps[name];
if (!wanted.startsWith('^')) wanted = `^${wanted}`;
const installed = getInstalledVersion(currentDir, name);
const latest = await getLatestVersion(name, wanted);
const wantedFixed = wanted.startsWith('^') ? wanted.substr(1) : wanted;
const shouldBeInstalled =
installed === null || wantedFixed !== installed || installed !== latest;
if (shouldBeInstalled) {
const warning =
installed !== null
? `outdated: ${chalk.red(
wantedFixed !== installed ? wantedFixed : installed
)}${chalk.green(latest)}`
: chalk.red('not installed');
console.log(`${chalk.red(name)} is ${warning}`);
}
pkgs.push({ name, wanted, installed, type, latest, shouldBeInstalled });
});
}

function getPkgIds(filteredPkgs) {
return filteredPkgs.map(({ name, latest }) => `${name}@${latest}`).join(' ');
}

async function verifyDeps(currentDir) {
const { dependencies, devDependencies } = require(path.join(currentDir, 'package.json'));
console.log(chalk.blue('Checking NPM module versions…\n'));
await Promise.all([
...pushPkgs(currentDir, dependencies, 'prod'),
...pushPkgs(currentDir, devDependencies, 'dev')
]);
const toInstall = pkgs.filter(({ shouldBeInstalled }) => shouldBeInstalled);
if (toInstall.length > 0) {
console.log(`\n${chalk.bold('To resolve this, run:')}`);
const prodPkgs = toInstall.filter(({ type }) => type === 'prod');
if (prodPkgs.length > 0) {
console.log(`npm i ${getPkgIds(prodPkgs)}`);
}
const devPkgs = toInstall.filter(({ type }) => type === 'dev');
if (devPkgs.length > 0) {
console.log(`npm i -D ${getPkgIds(devPkgs)}`);
}
if (prodPkgs.length > 0 || devPkgs.length > 0) {
console.log();
}
throw new Error(chalk.red('Please update your installed modules.'));
} else {
console.log(chalk.green('All NPM modules are up to date.'));
}
}

module.exports = verifyDeps;
9 changes: 9 additions & 0 deletions lib/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

const verifyDeps = require('.');

describe('verifyDeps', () => {
it('exports as a function', () => {
expect(typeof verifyDeps).toBe('function');
});
});
Loading

0 comments on commit b008486

Please sign in to comment.