Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add -p flag to dependency-check to only check prod deps #672

Merged
merged 11 commits into from
Nov 16, 2020
34 changes: 30 additions & 4 deletions cmds/z-dependency-check.js
Original file line number Diff line number Diff line change
@@ -8,18 +8,44 @@ const EPILOG = `
Supports options forwarding with '--' for more info check https://github.com/maxogden/dependency-check#cli-usage
`

const commandName = depCheck.commandNames[0]

module.exports = {
command: 'dependency-check',
aliases: ['dep-check', 'dep'],
command: `${commandName} [input...]`,
aliases: depCheck.commandNames.filter(name => name !== commandName),
desc: 'Run `dependency-check` cli with aegir defaults.',
builder: (yargs) => {
yargs
.epilog(EPILOG)
.example('aegir dependency-check -- --unused', 'To check unused packages in your repo.')
.example('aegir dependency-check -- --unused --ignore typescript', 'To check unused packages in your repo, ignoring typescript.')
.positional('input', {
describe: 'Files to check',
type: 'array',
default: depCheck.defaultInput
})
.option('p', {
alias: 'production-only',
describe: 'Check production dependencies and paths only',
type: 'boolean',
default: false
})
.option('i', {
alias: 'ignore',
describe: 'Ignore these dependencies when considering which are used and which are not',
type: 'array',
default: []
})
},
async handler (argv) {
const spinner = ora('Checking dependencies').start()
await depCheck(argv)
spinner.succeed()

try {
await depCheck(argv, process.argv)
spinner.succeed()
} catch (err) {
spinner.fail()
throw err
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@
"release-major": "node cli.js release --no-build --no-test --type major"
},
"dependencies": {
"@achingbrain/dependency-check": "^4.1.0",
"@babel/cli": "^7.10.1",
"@babel/core": "^7.10.2",
"@babel/plugin-transform-regenerator": "^7.10.1",
@@ -70,7 +71,6 @@
"conventional-github-releaser": "^3.1.3",
"cors": "^2.8.5",
"cosmiconfig": "^7.0.0",
"dependency-check": "^4.1.0",
"dirty-chai": "^2.0.1",
"documentation": "^13.0.1",
"electron-mocha": "^8.2.0",
62 changes: 58 additions & 4 deletions src/dependency-check.js
Original file line number Diff line number Diff line change
@@ -9,25 +9,77 @@ const merge = require('merge-options')

const defaultInput = [
'package.json',
'.aegir.js*',
'./test/**/*.js',
'./src/**/*.js',
'!./test/fixtures/**/*.js'
]

const commandNames = ['dependency-check', 'dep-check', 'dep']

/**
* Returns true if the user invoked the command with non-flag or
* optional args
*
* @param {Array} input - input files maybe passed by the user, maybe defaults
* @param {Array} processArgs - process.argv or similar
* @returns {boolean}
*/
const hasPassedFileArgs = (input, processArgs) => {
// if any of the passed paths are not in the process.argv used to invoke
// this command, we have been passed defaults and not user input
for (const path of input) {
if (!processArgs.includes(path)) {
return false
}
}

return true
}

/**
* Check dependencies
*
* @param {object} argv - Command line arguments passed to the process.
* @param {Array} processArgs - Unparsed command line arguments used to start the process
* @param {ExecaOptions} execaOptions - execa options.
* @returns {ExecaChildProcess} - Child process that does dependency check.
*/
const check = (argv = { _: [] }, execaOptions) => {
const input = argv._.slice(1)
const check = (argv = { _: [], input: [], ignore: [] }, processArgs = [], execaOptions) => {
const forwardOptions = argv['--'] ? argv['--'] : []
const defaults = input.length ? input : defaultInput
let input = argv.input
const ignore = argv.ignore

if (argv.productionOnly) {
if (!hasPassedFileArgs(input, processArgs)) {
input = [
'package.json',
'./src/**/*.js'
]
}

forwardOptions.push('--no-dev')
}

if (ignore.length) {
// this allows us to specify ignores on a per-module basis while also orchestrating the command across multiple modules.
//
// e.g. npm script in package.json:
// "dependency-check": "aegir dependency-check -i cross-env",
//
// .travis.yml:
// lerna run dependency-check -- -p -- --unused
//
// results in the following being run in the package:
// aegir dependency-check -i cross-env -p -- --unused
ignore.forEach(i => {
forwardOptions.push('-i', i)
})
}

return execa('dependency-check',
[
...defaults,
...input,
'--missing',
...forwardOptions
],
@@ -42,3 +94,5 @@ const check = (argv = { _: [] }, execaOptions) => {
}

module.exports = check
module.exports.defaultInput = defaultInput
module.exports.commandNames = commandNames
77 changes: 70 additions & 7 deletions test/dependency-check.js
Original file line number Diff line number Diff line change
@@ -4,23 +4,86 @@
const check = require('../src/dependency-check')
const { expect } = require('../utils/chai')
const path = require('path')
const merge = require('merge-options')

// returns an object that looks like the yargs input
const yargsv = (overrides = {}) => {
const argv = {
_: [
'dependency-check'
],
input: check.defaultInput,
ignore: []
}

return merge(argv, overrides)
}

const argv = (input = []) => {
return [
'node', 'aegir', 'dependency-check'
].concat(input)
}

describe('dependency check', () => {
it('should fail for missing deps', async () => {
await expect(check(undefined, {
await expect(check(yargsv(), argv(), {
cwd: path.join(__dirname, 'fixtures/dependency-check/fail')
})).to.eventually.rejectedWith('execa')
})).to.eventually.be.rejectedWith('execa')
})

it('should pass when theres no missing deps', async () => {
await expect(check(undefined, {
it('should pass when there are no missing deps', async () => {
await expect(check(yargsv(), argv(), {
cwd: path.join(__dirname, 'fixtures/dependency-check/pass')
})).to.eventually.fulfilled()
})).to.eventually.be.fulfilled()
})

it('should forward options', async () => {
await expect(check({ _: [], '--': ['--unused'] }, {
await expect(check(yargsv({ '--': ['--unused'] }), argv(), {
cwd: path.join(__dirname, 'fixtures/dependency-check/pass')
})).to.eventually.rejectedWith('Modules in package.json not used in code: pico')
})).to.eventually.be.rejectedWith('Modules in package.json not used in code: pico')
})

it('should fail for missing production deps', async () => {
await expect(check(yargsv({ productionOnly: true }), argv(), {
cwd: path.join(__dirname, 'fixtures/dependency-check/fail-prod')
})).to.eventually.be.rejectedWith('execa')
})

it('should pass for passed files', async () => {
const file = 'derp/foo.js'

await expect(check(yargsv({
input: [file]
}), argv(file), {
cwd: path.join(__dirname, 'fixtures/dependency-check/pass-certain-files')
})).to.eventually.be.fulfilled()
})

it('should pass for passed production files', async () => {
const file = 'derp/foo.js'

await expect(check(yargsv({
productionOnly: true,
input: [file]
}), argv(file), {
cwd: path.join(__dirname, 'fixtures/dependency-check/pass-certain-files')
})).to.eventually.be.fulfilled()
})

it('should pass for ignored modules', async () => {
await expect(check(yargsv({
ignore: ['execa']
}), argv(), {
cwd: path.join(__dirname, 'fixtures/dependency-check/fail')
})).to.eventually.be.fulfilled()
})

it('should pass for modules used in .aegir.js', async () => {
await expect(check(yargsv({
'--': ['--unused']
}), argv(['--', '--unused']), {
cwd: path.join(__dirname, 'fixtures/dependency-check/with-aegir-config')
})).to.eventually.be.fulfilled()
})
})
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/fail-prod/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'
/* eslint-disable no-unused-vars */
const execa = require('execa')
8 changes: 8 additions & 0 deletions test/fixtures/dependency-check/fail-prod/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "dep-check-fail-prod",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {
"execa": "1.0.0"
}
}
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/pass-certain-files/derp/foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'
/* eslint-disable no-unused-vars */
const execa = require('execa')
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/pass-certain-files/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'
/* eslint-disable no-unused-vars */
const pico = require('pico')
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "dep-check-pass",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"execa": "1.0.0"
}
}
1 change: 0 additions & 1 deletion test/fixtures/dependency-check/pass/package.json
Original file line number Diff line number Diff line change
@@ -7,4 +7,3 @@
"pico": "1.0.0"
}
}

3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/with-aegir-config/.aegir.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'
/* eslint-disable no-unused-vars */
const execa = require('execa')
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/with-aegir-config/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'
/* eslint-disable no-unused-vars */
const pico = require('pico')
11 changes: 11 additions & 0 deletions test/fixtures/dependency-check/with-aegir-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "dep-check-pass",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"pico": "1.0.0"
},
"devDependencies": {
"execa": "1.0.0"
}
}