Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/eslint-plugin-import
Path: blob/main/tests/src/rules/no-extraneous-dependencies.js
829 views
1
import { getTSParsers, test, testFilePath } from '../utils';
2
import typescriptConfig from '../../../config/typescript';
3
import path from 'path';
4
import fs from 'fs';
5
6
import { RuleTester } from 'eslint';
7
import flatMap from 'array.prototype.flatmap';
8
9
const ruleTester = new RuleTester();
10
const typescriptRuleTester = new RuleTester(typescriptConfig);
11
const rule = require('rules/no-extraneous-dependencies');
12
13
const packageDirWithSyntaxError = path.join(__dirname, '../../files/with-syntax-error');
14
const packageFileWithSyntaxErrorMessage = (() => {
15
try {
16
JSON.parse(fs.readFileSync(path.join(packageDirWithSyntaxError, 'package.json')));
17
} catch (error) {
18
return error.message;
19
}
20
})();
21
const packageDirWithFlowTyped = path.join(__dirname, '../../files/with-flow-typed');
22
const packageDirWithTypescriptDevDependencies = path.join(__dirname, '../../files/with-typescript-dev-dependencies');
23
const packageDirMonoRepoRoot = path.join(__dirname, '../../files/monorepo');
24
const packageDirMonoRepoWithNested = path.join(__dirname, '../../files/monorepo/packages/nested-package');
25
const packageDirWithEmpty = path.join(__dirname, '../../files/empty');
26
const packageDirBundleDeps = path.join(__dirname, '../../files/bundled-dependencies/as-array-bundle-deps');
27
const packageDirBundledDepsAsObject = path.join(__dirname, '../../files/bundled-dependencies/as-object');
28
const packageDirBundledDepsRaceCondition = path.join(__dirname, '../../files/bundled-dependencies/race-condition');
29
30
const {
31
dependencies: deps,
32
devDependencies: devDeps,
33
} = require('../../files/package.json');
34
35
ruleTester.run('no-extraneous-dependencies', rule, {
36
valid: [
37
...flatMap(Object.keys(deps).concat(Object.keys(devDeps)), (pkg) => [
38
test({ code: `import "${pkg}"` }),
39
test({ code: `import foo, { bar } from "${pkg}"` }),
40
test({ code: `require("${pkg}")` }),
41
test({ code: `var foo = require("${pkg}")` }),
42
test({ code: `export { foo } from "${pkg}"` }),
43
test({ code: `export * from "${pkg}"` }),
44
]),
45
test({ code: 'import "eslint"' }),
46
test({ code: 'import "eslint/lib/api"' }),
47
test({ code: 'import "fs"' }),
48
test({ code: 'import "./foo"' }),
49
test({ code: 'import "@org/package"' }),
50
51
test({ code: 'import "electron"', settings: { 'import/core-modules': ['electron'] } }),
52
test({
53
code: 'import "eslint"',
54
options: [{ peerDependencies: true }],
55
}),
56
57
// 'project' type
58
test({
59
code: 'import "importType"',
60
settings: { 'import/resolver': { node: { paths: [ path.join(__dirname, '../../files') ] } } },
61
}),
62
test({
63
code: 'import chai from "chai"',
64
options: [{ devDependencies: ['*.spec.js'] }],
65
filename: 'foo.spec.js',
66
}),
67
test({
68
code: 'import chai from "chai"',
69
options: [{ devDependencies: ['*.spec.js'] }],
70
filename: path.join(process.cwd(), 'foo.spec.js'),
71
}),
72
test({
73
code: 'import chai from "chai"',
74
options: [{ devDependencies: ['*.test.js', '*.spec.js'] }],
75
filename: path.join(process.cwd(), 'foo.spec.js'),
76
}),
77
test({ code: 'require(6)' }),
78
test({
79
code: 'import "doctrine"',
80
options: [{ packageDir: path.join(__dirname, '../../../') }],
81
}),
82
test({
83
code: 'import type MyType from "myflowtyped";',
84
options: [{ packageDir: packageDirWithFlowTyped }],
85
parser: require.resolve('babel-eslint'),
86
}),
87
test({
88
code: `
89
// @flow
90
import typeof TypeScriptModule from 'typescript';
91
`,
92
options: [{ packageDir: packageDirWithFlowTyped }],
93
parser: require.resolve('babel-eslint'),
94
}),
95
test({
96
code: 'import react from "react";',
97
options: [{ packageDir: packageDirMonoRepoWithNested }],
98
}),
99
test({
100
code: 'import leftpad from "left-pad";',
101
options: [{ packageDir: [packageDirMonoRepoWithNested, packageDirMonoRepoRoot] }],
102
}),
103
test({
104
code: 'import leftpad from "left-pad";',
105
options: [{ packageDir: packageDirMonoRepoRoot }],
106
}),
107
test({
108
code: 'import react from "react";',
109
options: [{ packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested] }],
110
}),
111
test({
112
code: 'import leftpad from "left-pad";',
113
options: [{ packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested] }],
114
}),
115
test({
116
code: 'import rightpad from "right-pad";',
117
options: [{ packageDir: [packageDirMonoRepoRoot, packageDirMonoRepoWithNested] }],
118
}),
119
test({ code: 'import foo from "@generated/foo"' }),
120
test({
121
code: 'import foo from "@generated/foo"',
122
options: [{ packageDir: packageDirBundleDeps }],
123
}),
124
test({
125
code: 'import foo from "@generated/foo"',
126
options: [{ packageDir: packageDirBundledDepsAsObject }],
127
}),
128
test({
129
code: 'import foo from "@generated/foo"',
130
options: [{ packageDir: packageDirBundledDepsRaceCondition }],
131
}),
132
test({ code: 'export function getToken() {}' }),
133
test({ code: 'export class Component extends React.Component {}' }),
134
test({ code: 'export function Component() {}' }),
135
test({ code: 'export const Component = () => {}' }),
136
137
test({
138
code: 'import "not-a-dependency"',
139
filename: path.join(packageDirMonoRepoRoot, 'foo.js'),
140
options: [{ packageDir: packageDirMonoRepoRoot }],
141
settings: { 'import/core-modules': ['not-a-dependency'] },
142
}),
143
test({
144
code: 'import "@generated/bar/module"',
145
settings: { 'import/core-modules': ['@generated/bar'] },
146
}),
147
test({
148
code: 'import "@generated/bar/and/sub/path"',
149
settings: { 'import/core-modules': ['@generated/bar'] },
150
}),
151
// check if "rxjs" dependency declaration fix the "rxjs/operators subpackage
152
test({
153
code: 'import "rxjs/operators"',
154
}),
155
156
test({
157
code: 'import "esm-package/esm-module";',
158
}),
159
160
test({
161
code: `
162
import "alias/esm-package/esm-module";
163
import 'expose-loader?exposes[]=$&exposes[]=jQuery!jquery';
164
`,
165
settings: { 'import/resolver': 'webpack' },
166
}),
167
],
168
invalid: [
169
test({
170
code: 'import "not-a-dependency"',
171
filename: path.join(packageDirMonoRepoRoot, 'foo.js'),
172
options: [{ packageDir: packageDirMonoRepoRoot }],
173
errors: [{
174
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
175
}],
176
}),
177
test({
178
code: 'import "not-a-dependency"',
179
filename: path.join(packageDirMonoRepoWithNested, 'foo.js'),
180
options: [{ packageDir: packageDirMonoRepoRoot }],
181
errors: [{
182
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
183
}],
184
}),
185
test({
186
code: 'import "not-a-dependency"',
187
options: [{ packageDir: packageDirMonoRepoRoot }],
188
errors: [{
189
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
190
}],
191
}),
192
test({
193
code: 'import "not-a-dependency"',
194
errors: [{
195
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
196
}],
197
}),
198
test({
199
code: 'var donthaveit = require("@org/not-a-dependency")',
200
errors: [{
201
message: '\'@org/not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S @org/not-a-dependency\' to add it',
202
}],
203
}),
204
test({
205
code: 'var donthaveit = require("@org/not-a-dependency/foo")',
206
errors: [{
207
message: '\'@org/not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S @org/not-a-dependency\' to add it',
208
}],
209
}),
210
test({
211
code: 'import "eslint"',
212
options: [{ devDependencies: false, peerDependencies: false }],
213
errors: [{
214
message: '\'eslint\' should be listed in the project\'s dependencies, not devDependencies.',
215
}],
216
}),
217
test({
218
code: 'import "lodash.isarray"',
219
options: [{ optionalDependencies: false }],
220
errors: [{
221
message: '\'lodash.isarray\' should be listed in the project\'s dependencies, not optionalDependencies.',
222
}],
223
}),
224
test({
225
code: 'var foo = require("not-a-dependency")',
226
errors: [{
227
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
228
}],
229
}),
230
test({
231
code: 'var glob = require("glob")',
232
options: [{ devDependencies: false }],
233
errors: [{
234
message: '\'glob\' should be listed in the project\'s dependencies, not devDependencies.',
235
}],
236
}),
237
test({
238
code: 'import chai from "chai"',
239
options: [{ devDependencies: ['*.test.js'] }],
240
filename: 'foo.tes.js',
241
errors: [{
242
message: '\'chai\' should be listed in the project\'s dependencies, not devDependencies.',
243
}],
244
}),
245
test({
246
code: 'import chai from "chai"',
247
options: [{ devDependencies: ['*.test.js'] }],
248
filename: path.join(process.cwd(), 'foo.tes.js'),
249
errors: [{
250
message: '\'chai\' should be listed in the project\'s dependencies, not devDependencies.',
251
}],
252
}),
253
test({
254
code: 'import chai from "chai"',
255
options: [{ devDependencies: ['*.test.js', '*.spec.js'] }],
256
filename: 'foo.tes.js',
257
errors: [{
258
message: '\'chai\' should be listed in the project\'s dependencies, not devDependencies.',
259
}],
260
}),
261
test({
262
code: 'import chai from "chai"',
263
options: [{ devDependencies: ['*.test.js', '*.spec.js'] }],
264
filename: path.join(process.cwd(), 'foo.tes.js'),
265
errors: [{
266
message: '\'chai\' should be listed in the project\'s dependencies, not devDependencies.',
267
}],
268
}),
269
test({
270
code: 'var eslint = require("lodash.isarray")',
271
options: [{ optionalDependencies: false }],
272
errors: [{
273
message: '\'lodash.isarray\' should be listed in the project\'s dependencies, not optionalDependencies.',
274
}],
275
}),
276
test({
277
code: 'import "not-a-dependency"',
278
options: [{ packageDir: path.join(__dirname, '../../../') }],
279
errors: [{
280
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
281
}],
282
}),
283
test({
284
code: 'import "bar"',
285
options: [{ packageDir: path.join(__dirname, './doesn-exist/') }],
286
errors: [{
287
message: 'The package.json file could not be found.',
288
}],
289
}),
290
test({
291
code: 'import foo from "foo"',
292
options: [{ packageDir: packageDirWithSyntaxError }],
293
errors: [{
294
message: 'The package.json file could not be parsed: ' + packageFileWithSyntaxErrorMessage,
295
}],
296
}),
297
test({
298
code: 'import leftpad from "left-pad";',
299
filename: path.join(packageDirMonoRepoWithNested, 'foo.js'),
300
options: [{ packageDir: packageDirMonoRepoWithNested }],
301
errors: [{
302
message: "'left-pad' should be listed in the project's dependencies. Run 'npm i -S left-pad' to add it",
303
}],
304
}),
305
test({
306
code: 'import react from "react";',
307
filename: path.join(packageDirMonoRepoRoot, 'foo.js'),
308
errors: [{
309
message: "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it",
310
}],
311
}),
312
test({
313
code: 'import react from "react";',
314
filename: path.join(packageDirMonoRepoWithNested, 'foo.js'),
315
options: [{ packageDir: packageDirMonoRepoRoot }],
316
errors: [{
317
message: "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it",
318
}],
319
}),
320
test({
321
code: 'import "react";',
322
filename: path.join(packageDirWithEmpty, 'index.js'),
323
options: [{ packageDir: packageDirWithEmpty }],
324
errors: [{
325
message: "'react' should be listed in the project's dependencies. Run 'npm i -S react' to add it",
326
}],
327
}),
328
test({
329
code: 'import bar from "@generated/bar"',
330
errors: ["'@generated/bar' should be listed in the project's dependencies. Run 'npm i -S @generated/bar' to add it"],
331
}),
332
test({
333
code: 'import foo from "@generated/foo"',
334
options: [{ bundledDependencies: false }],
335
errors: ["'@generated/foo' should be listed in the project's dependencies. Run 'npm i -S @generated/foo' to add it"],
336
}),
337
test({
338
code: 'import bar from "@generated/bar"',
339
options: [{ packageDir: packageDirBundledDepsRaceCondition }],
340
errors: ["'@generated/bar' should be listed in the project's dependencies. Run 'npm i -S @generated/bar' to add it"],
341
}),
342
test({
343
code: 'export { foo } from "not-a-dependency";',
344
errors: [{
345
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
346
}],
347
}),
348
test({
349
code: 'export * from "not-a-dependency";',
350
errors: [{
351
message: '\'not-a-dependency\' should be listed in the project\'s dependencies. Run \'npm i -S not-a-dependency\' to add it',
352
}],
353
}),
354
test({
355
code: 'import chai from "alias/chai";',
356
settings: { 'import/resolver': 'webpack' },
357
errors: [{
358
// missing dependency is chai not alias
359
message: "'chai' should be listed in the project's dependencies. Run 'npm i -S chai' to add it",
360
}],
361
}),
362
363
test({
364
code: 'import "not-a-dependency"',
365
filename: path.join(packageDirMonoRepoRoot, 'foo.js'),
366
options: [{ packageDir: packageDirMonoRepoRoot }],
367
errors: [{
368
message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`,
369
}],
370
}),
371
372
test({
373
code: 'import "esm-package-not-in-pkg-json/esm-module";',
374
errors: [{
375
message: `'esm-package-not-in-pkg-json' should be listed in the project's dependencies. Run 'npm i -S esm-package-not-in-pkg-json' to add it`,
376
}],
377
}),
378
],
379
});
380
381
describe('TypeScript', () => {
382
getTSParsers()
383
// Type-only imports were added in TypeScript ESTree 2.23.0
384
.filter((parser) => parser !== require.resolve('typescript-eslint-parser'))
385
.forEach((parser) => {
386
const parserConfig = {
387
parser,
388
settings: {
389
'import/parsers': { [parser]: ['.ts'] },
390
'import/resolver': ['node', 'typescript'],
391
},
392
};
393
394
ruleTester.run('no-extraneous-dependencies', rule, {
395
valid: [
396
test(Object.assign({
397
code: 'import type T from "a";',
398
options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }],
399
}, parserConfig)),
400
],
401
invalid: [
402
test(Object.assign({
403
code: 'import T from "a";',
404
options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }],
405
errors: [{
406
message: "'a' should be listed in the project's dependencies, not devDependencies.",
407
}],
408
}, parserConfig)),
409
],
410
});
411
});
412
});
413
414
typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', rule, {
415
valid: [
416
test({
417
code: 'import type MyType from "not-a-dependency";',
418
filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'),
419
parser: require.resolve('babel-eslint'),
420
}),
421
test({
422
code: 'import type { MyType } from "not-a-dependency";',
423
filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'),
424
parser: require.resolve('babel-eslint'),
425
}),
426
],
427
invalid: [
428
],
429
});
430
431