Skip to content

Commit

Permalink
feat: support dependency check in esm modules (#869)
Browse files Browse the repository at this point in the history
Auto-detect `type: "module"` in a project's `package.json` and switch
`detective` implementation to `detective-es6` which understands `import`.
  • Loading branch information
achingbrain authored Jul 29, 2021
1 parent 58c710c commit 48712b1
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 63 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
"release-major": "node cli.js release --type major"
},
"dependencies": {
"@achingbrain/dependency-check": "^4.1.0",
"@electron/get": "^1.12.3",
"@polka/send-type": "^0.5.2",
"@types/chai": "^4.2.16",
Expand All @@ -80,6 +79,8 @@
"conventional-github-releaser": "^3.1.5",
"copyfiles": "^2.4.1",
"cors": "^2.8.5",
"dependency-check": "^5.0.0-1",
"detective-es6": "^2.2.0",
"electron-mocha": "^10.0.0",
"env-paths": "^2.2.1",
"esbuild": "^0.12.15",
Expand Down
11 changes: 9 additions & 2 deletions src/config/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,23 @@ const defaults = {
input: [
'package.json',
'.aegir.js',
'.aegir.cjs',
'src/**/*.js',
'src/**/*.cjs',
'test/**/*.js',
'test/**/*.cjs',
'benchmarks/**/*.js',
'benchmarks/**/*.cjs',
'utils/**/*.js',
'!./test/fixtures/**/*.js'
'utils/**/*.cjs',
'!./test/fixtures/**/*.js',
'!./test/fixtures/**/*.cjs'
],
productionOnly: false,
productionInput: [
'package.json',
'src/**/*.js'
'src/**/*.js',
'src/**/*.cjs'
],
ignore: [
'@types/*'
Expand Down
13 changes: 12 additions & 1 deletion src/dependency-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const path = require('path')
const execa = require('execa')
const merge = require('merge-options')
const { pkg } = require('./utils')

/**
* @typedef {import("execa").Options} ExecaOptions
Expand Down Expand Up @@ -34,9 +35,19 @@ const check = (argv, execaOptions) => {
.concat(argv.fileConfig.dependencyCheck.ignore)
.reduce((acc, i) => acc.concat('-i', i), /** @type {string[]} */ ([]))

const args = [...input, '--missing', ...noDev, ...ignore]

if (pkg.type === 'module') {
// use detective-es6 for js, regular detective for cjs
args.push(
'--extensions', 'cjs:detective',
'--extensions', 'js:detective-es6'
)
}

return execa(
'dependency-check',
[...input, '--missing', ...noDev, ...ignore, ...forwardOptions],
[...args, ...forwardOptions],
merge(
{
localDir: path.join(__dirname, '..'),
Expand Down
105 changes: 46 additions & 59 deletions test/dependency-check.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,50 @@
/* eslint-env mocha */
'use strict'

const { check } = require('../src/dependency-check')
const { expect } = require('../utils/chai')
const path = require('path')
const merge = require('merge-options')
const { userConfig } = require('../src/config/user')

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

return merge(argv, overrides)
}
const bin = require.resolve('../')
const execa = require('execa')

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

it('should fail for missing deps in an esm project', async () => {
await expect(
execa(bin, ['dependency-check'], {
cwd: path.join(__dirname, 'fixtures/dependency-check/esm-fail')
})
).to.eventually.be.rejected()
.with.property('message')
.that.include('execa')
.and.include('pico')
})

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

it('should pass when there are no missing deps in an esm project', async () => {
await expect(
execa(bin, ['dependency-check'], {
cwd: path.join(__dirname, 'fixtures/dependency-check/esm-pass')
})
).to.eventually.be.fulfilled()
})

it('should forward options', async () => {
await expect(
check(yargsv({ '--': ['--unused'] }), {
execa(bin, ['dependency-check', '--', '--unused'], {
cwd: path.join(__dirname, 'fixtures/dependency-check/pass')
})
).to.eventually.be.rejectedWith(
Expand All @@ -50,7 +54,7 @@ describe('dependency check', () => {

it('should fail for missing production deps', async () => {
await expect(
check(yargsv({ productionOnly: true }), {
execa(bin, ['dependency-check', '-p'], {
cwd: path.join(__dirname, 'fixtures/dependency-check/fail-prod')
})
).to.eventually.be.rejectedWith('execa')
Expand All @@ -60,16 +64,12 @@ describe('dependency check', () => {
const file = 'derp/foo.js'

await expect(
check(
yargsv({
input: [file]
}),
{
cwd: path.join(
__dirname,
'fixtures/dependency-check/pass-certain-files'
)
}
execa(bin, ['dependency-check', file], {
cwd: path.join(
__dirname,
'fixtures/dependency-check/pass-certain-files'
)
}
)
).to.eventually.be.fulfilled()
})
Expand All @@ -78,46 +78,33 @@ describe('dependency check', () => {
const file = 'derp/foo.js'

await expect(
check(
yargsv({
productionOnly: true,
input: [file]
}),
{
cwd: path.join(
__dirname,
'fixtures/dependency-check/pass-certain-files'
)
}
execa(bin, ['dependency-check', file, '-p'], {
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']
}),
{
cwd: path.join(__dirname, 'fixtures/dependency-check/fail')
}
execa(bin, ['dependency-check', '-i', 'execa'], {
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']
}),
{
cwd: path.join(
__dirname,
'fixtures/dependency-check/with-aegir-config'
)
}
execa(bin, ['dependency-check', '--', '--unused'], {
cwd: path.join(
__dirname,
'fixtures/dependency-check/with-aegir-config'
)
}
)
).to.eventually.be.fulfilled()
})
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/dependency-check/esm-fail/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "esm-dep-check-fail",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"eslintConfig": {
"parserOptions": {
"sourceType": "module"
}
}
}
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/esm-fail/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @ts-nocheck
/* eslint-disable no-unused-vars */
import execa from 'execa'
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/esm-fail/src/other.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @ts-nocheck
/* eslint-disable no-unused-vars */
const pico = require('pico')
15 changes: 15 additions & 0 deletions test/fixtures/dependency-check/esm-pass/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "esm-dep-check-pass",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"eslintConfig": {
"parserOptions": {
"sourceType": "module"
}
},
"dependencies": {
"execa": "1.0.0",
"pico": "1.0.0"
}
}
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/esm-pass/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @ts-nocheck
/* eslint-disable no-unused-vars */
import execa from 'execa'
3 changes: 3 additions & 0 deletions test/fixtures/dependency-check/esm-pass/src/other.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @ts-nocheck
/* eslint-disable no-unused-vars */
const execa = require('execa')

0 comments on commit 48712b1

Please sign in to comment.