Skip to content

Commit 5e143b2

Browse files
schmidsiljharb
authored andcommitted
[new] no-extraneous-dependencies: Implement support for bundledDependencies
- See https://npm.github.io/using-pkgs-docs/package-json/types/bundleddependencies.html - Fixes #1436.
1 parent 813a116 commit 5e143b2

File tree

16 files changed

+74
-4
lines changed

16 files changed

+74
-4
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
1010

1111
### Added
1212
- support `parseForESLint` from custom parser ([#1435], thanks [@JounQin])
13+
- [`no-extraneous-dependencies`]: Implement support for [bundledDependencies](https://npm.github.io/using-pkgs-docs/package-json/types/bundleddependencies.html) ([#1436], thanks [@schmidsi]))
1314

1415
### Fixed
1516
- `default`: make error message less confusing ([#1470], thanks [@golopot])
@@ -609,6 +610,7 @@ for info on changes for earlier releases.
609610

610611
[#1472]: https://github.com/benmosher/eslint-plugin-import/pull/1472
611612
[#1470]: https://github.com/benmosher/eslint-plugin-import/pull/1470
613+
[#1436]: https://github.com/benmosher/eslint-plugin-import/pull/1436
612614
[#1435]: https://github.com/benmosher/eslint-plugin-import/pull/1435
613615
[#1425]: https://github.com/benmosher/eslint-plugin-import/pull/1425
614616
[#1419]: https://github.com/benmosher/eslint-plugin-import/pull/1419
@@ -986,3 +988,4 @@ for info on changes for earlier releases.
986988
[@lencioni]: https://github.com/lencioni
987989
[@JounQin]: https://github.com/JounQin
988990
[@atikenny]: https://github.com/atikenny
991+
[@schmidsi]: https://github.com/schmidsi

docs/rules/no-extraneous-dependencies.md

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# import/no-extraneous-dependencies: Forbid the use of extraneous packages
22

3-
Forbid the import of external modules that are not declared in the `package.json`'s `dependencies`, `devDependencies`, `optionalDependencies` or `peerDependencies`.
3+
Forbid the import of external modules that are not declared in the `package.json`'s `dependencies`, `devDependencies`, `optionalDependencies`, `peerDependencies`, or `bundledDependencies`.
44
The closest parent `package.json` will be used. If no `package.json` is found, the rule will not lint anything. This behaviour can be changed with the rule option `packageDir`.
55

66
Modules have to be installed for this rule to work.
@@ -15,6 +15,8 @@ This rule supports the following options:
1515

1616
`peerDependencies`: If set to `false`, then the rule will show an error when `peerDependencies` are imported. Defaults to `false`.
1717

18+
`bundledDependencies`: If set to `false`, then the rule will show an error when `bundledDependencies` are imported. Defaults to `true`.
19+
1820
You can set the options like this:
1921

2022
```js
@@ -70,7 +72,10 @@ Given the following `package.json`:
7072
},
7173
"peerDependencies": {
7274
"react": ">=15.0.0 <16.0.0"
73-
}
75+
},
76+
"bundledDependencies": [
77+
"@generated/foo",
78+
]
7479
}
7580
```
7681

@@ -90,6 +95,10 @@ var test = require('ava');
9095
/* eslint import/no-extraneous-dependencies: ["error", {"optionalDependencies": false}] */
9196
import isArray from 'lodash.isarray';
9297
var isArray = require('lodash.isarray');
98+
99+
/* eslint import/no-extraneous-dependencies: ["error", {"bundledDependencies": false}] */
100+
import foo from '"@generated/foo"';
101+
var foo = require('"@generated/foo"');
93102
```
94103

95104

@@ -103,6 +112,7 @@ var foo = require('./foo');
103112
import test from 'ava';
104113
import find from 'lodash.find';
105114
import isArray from 'lodash.isarray';
115+
import foo from '"@generated/foo"';
106116

107117
/* eslint import/no-extraneous-dependencies: ["error", {"peerDependencies": true}] */
108118
import react from 'react';

src/rules/no-extraneous-dependencies.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@ function hasKeys(obj = {}) {
1111
return Object.keys(obj).length > 0
1212
}
1313

14+
function arrayOrKeys(arrayOrObject) {
15+
return Array.isArray(arrayOrObject) ? arrayOrObject : Object.keys(arrayOrObject)
16+
}
17+
1418
function extractDepFields(pkg) {
1519
return {
1620
dependencies: pkg.dependencies || {},
1721
devDependencies: pkg.devDependencies || {},
1822
optionalDependencies: pkg.optionalDependencies || {},
1923
peerDependencies: pkg.peerDependencies || {},
24+
// BundledDeps should be in the form of an array, but object notation is also supported by
25+
// `npm`, so we convert it to an array if it is an object
26+
bundledDependencies: arrayOrKeys(pkg.bundleDependencies || pkg.bundledDependencies || [])
2027
}
2128
}
2229

@@ -28,6 +35,7 @@ function getDependencies(context, packageDir) {
2835
devDependencies: {},
2936
optionalDependencies: {},
3037
peerDependencies: {},
38+
bundledDependencies: [],
3139
}
3240

3341
if (packageDir && packageDir.length > 0) {
@@ -63,6 +71,7 @@ function getDependencies(context, packageDir) {
6371
packageContent.devDependencies,
6472
packageContent.optionalDependencies,
6573
packageContent.peerDependencies,
74+
packageContent.bundledDependencies,
6675
].some(hasKeys)) {
6776
return null
6877
}
@@ -121,11 +130,13 @@ function reportIfMissing(context, deps, depsOptions, node, name) {
121130
const isInDevDeps = deps.devDependencies[packageName] !== undefined
122131
const isInOptDeps = deps.optionalDependencies[packageName] !== undefined
123132
const isInPeerDeps = deps.peerDependencies[packageName] !== undefined
133+
const isInBundledDeps = deps.bundledDependencies.indexOf(packageName) !== -1
124134

125135
if (isInDeps ||
126136
(depsOptions.allowDevDeps && isInDevDeps) ||
127137
(depsOptions.allowPeerDeps && isInPeerDeps) ||
128-
(depsOptions.allowOptDeps && isInOptDeps)
138+
(depsOptions.allowOptDeps && isInOptDeps) ||
139+
(depsOptions.allowBundledDeps && isInBundledDeps)
129140
) {
130141
return
131142
}
@@ -169,6 +180,7 @@ module.exports = {
169180
'devDependencies': { 'type': ['boolean', 'array'] },
170181
'optionalDependencies': { 'type': ['boolean', 'array'] },
171182
'peerDependencies': { 'type': ['boolean', 'array'] },
183+
'bundledDependencies': { 'type': ['boolean', 'array'] },
172184
'packageDir': { 'type': ['string', 'array'] },
173185
},
174186
'additionalProperties': false,
@@ -185,6 +197,7 @@ module.exports = {
185197
allowDevDeps: testConfig(options.devDependencies, filename) !== false,
186198
allowOptDeps: testConfig(options.optionalDependencies, filename) !== false,
187199
allowPeerDeps: testConfig(options.peerDependencies, filename) !== false,
200+
allowBundledDeps: testConfig(options.bundledDependencies, filename) !== false,
188201
}
189202

190203
// todo: use module visitor from module-utils core

tests/files/bundled-dependencies/as-array-bundle-deps/node_modules/@generated/bar/index.js

Whitespace-only changes.

tests/files/bundled-dependencies/as-array-bundle-deps/node_modules/@generated/foo/index.js

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"dummy": true,
3+
"bundleDependencies": ["@generated/foo"]
4+
}

tests/files/bundled-dependencies/as-object/node_modules/@generated/bar/index.js

Whitespace-only changes.

tests/files/bundled-dependencies/as-object/node_modules/@generated/foo/index.js

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"dummy": true,
3+
"bundledDependencies": {"@generated/foo": "latest"}
4+
}

tests/files/bundled-dependencies/race-condition/node_modules/@generated/bar/index.js

Whitespace-only changes.

tests/files/bundled-dependencies/race-condition/node_modules/@generated/foo/index.js

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dummy": true,
3+
"bundledDependencies": {"@generated/bar": "latest"},
4+
"bundleDependencies": ["@generated/foo"]
5+
}

tests/files/node_modules/@generated/bar/index.js

Whitespace-only changes.

tests/files/node_modules/@generated/foo/index.js

Whitespace-only changes.

tests/files/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
},
1616
"optionalDependencies": {
1717
"lodash.isarray": "^4.0.0"
18-
}
18+
},
19+
"bundledDependencies": ["@generated/foo"]
1920
}

tests/src/rules/no-extraneous-dependencies.js

+30
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const packageDirWithFlowTyped = path.join(__dirname, '../../files/with-flow-type
1818
const packageDirMonoRepoRoot = path.join(__dirname, '../../files/monorepo')
1919
const packageDirMonoRepoWithNested = path.join(__dirname, '../../files/monorepo/packages/nested-package')
2020
const packageDirWithEmpty = path.join(__dirname, '../../files/empty')
21+
const packageDirBundleDeps = path.join(__dirname, '../../files/bundled-dependencies/as-array-bundle-deps')
22+
const packageDirBundledDepsAsObject = path.join(__dirname, '../../files/bundled-dependencies/as-object')
23+
const packageDirBundledDepsRaceCondition = path.join(__dirname, '../../files/bundled-dependencies/race-condition')
2124

2225
ruleTester.run('no-extraneous-dependencies', rule, {
2326
valid: [
@@ -106,6 +109,19 @@ ruleTester.run('no-extraneous-dependencies', rule, {
106109
code: 'import rightpad from "right-pad";',
107110
options: [{packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested]}],
108111
}),
112+
test({ code: 'import foo from "@generated/foo"'}),
113+
test({
114+
code: 'import foo from "@generated/foo"',
115+
options: [{packageDir: packageDirBundleDeps}],
116+
}),
117+
test({
118+
code: 'import foo from "@generated/foo"',
119+
options: [{packageDir: packageDirBundledDepsAsObject}],
120+
}),
121+
test({
122+
code: 'import foo from "@generated/foo"',
123+
options: [{packageDir: packageDirBundledDepsRaceCondition}],
124+
}),
109125
],
110126
invalid: [
111127
test({
@@ -289,5 +305,19 @@ ruleTester.run('no-extraneous-dependencies', rule, {
289305
message: "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it",
290306
}],
291307
}),
308+
test({
309+
code: 'import bar from "@generated/bar"',
310+
errors: ["'@generated/bar' should be listed in the project's dependencies. Run 'npm i -S @generated/bar' to add it"],
311+
}),
312+
test({
313+
code: 'import foo from "@generated/foo"',
314+
options: [{bundledDependencies: false}],
315+
errors: ["'@generated/foo' should be listed in the project's dependencies. Run 'npm i -S @generated/foo' to add it"],
316+
}),
317+
test({
318+
code: 'import bar from "@generated/bar"',
319+
options: [{packageDir: packageDirBundledDepsRaceCondition}],
320+
errors: ["'@generated/bar' should be listed in the project's dependencies. Run 'npm i -S @generated/bar' to add it"],
321+
}),
292322
],
293323
})

0 commit comments

Comments
 (0)