knip 1.0.0-alpha.2 → 1.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -70
- package/dist/cli.js +5 -3
- package/dist/configuration-chief.js +4 -21
- package/dist/{util/constants.d.ts → constants.d.ts} +0 -0
- package/dist/{util/constants.js → constants.js} +1 -1
- package/dist/index.js +12 -14
- package/dist/npm-scripts/helpers.js +1 -1
- package/dist/npm-scripts/index.js +1 -1
- package/dist/plugins/babel/index.js +7 -12
- package/dist/plugins/eslint/helpers.d.ts +2 -0
- package/dist/plugins/eslint/helpers.js +21 -0
- package/dist/plugins/eslint/index.d.ts +1 -0
- package/dist/plugins/eslint/index.js +29 -32
- package/dist/plugins/eslint/types.d.ts +4 -0
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/mocha/index.d.ts +5 -0
- package/dist/plugins/mocha/index.js +17 -0
- package/dist/plugins/next/index.d.ts +0 -1
- package/dist/plugins/next/index.js +0 -1
- package/dist/plugins/playwright/index.d.ts +0 -1
- package/dist/plugins/playwright/index.js +0 -1
- package/dist/plugins/postcss/index.d.ts +0 -1
- package/dist/plugins/postcss/index.js +0 -1
- package/dist/plugins/remix/index.d.ts +0 -1
- package/dist/plugins/remix/index.js +0 -1
- package/dist/plugins/rollup/index.d.ts +0 -1
- package/dist/plugins/rollup/index.js +0 -1
- package/dist/plugins/stryker/index.d.ts +4 -0
- package/dist/plugins/stryker/index.js +15 -0
- package/dist/plugins/stryker/types.d.ts +5 -0
- package/dist/plugins/stryker/types.js +1 -0
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/config.d.ts +2 -1
- package/dist/types/plugins.d.ts +5 -3
- package/dist/types/validate.d.ts +159 -35
- package/dist/types/validate.js +3 -11
- package/dist/util/externalImports.js +5 -4
- package/dist/util/glob.d.ts +7 -1
- package/dist/util/glob.js +12 -3
- package/dist/util/help.js +6 -3
- package/dist/util/loader.js +11 -0
- package/dist/util/parseArgs.d.ts +12 -12
- package/dist/util/parseArgs.js +12 -12
- package/dist/workspace-worker.d.ts +1 -17
- package/dist/workspace-worker.js +46 -62
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -21,22 +21,23 @@ The dots don't connect themselves. This is where Knip comes in:
|
|
|
21
21
|
- [x] Finds duplicate exports.
|
|
22
22
|
- [x] Finds unused members of classes and enums
|
|
23
23
|
- [x] Built-in support for monorepos (workspaces)
|
|
24
|
-
- [x] Growing list of built-in plugins
|
|
24
|
+
- [x] Growing list of [built-in plugins][1]
|
|
25
|
+
- [x] Checks npm scripts for used and unlisted dependencies
|
|
25
26
|
- [x] Supports JavaScript (without `tsconfig.json`, or TypeScript `allowJs: true`).
|
|
26
|
-
- [x] Features multiple [reporters][
|
|
27
|
+
- [x] Features multiple [reporters][2] and supports [custom reporters][3]
|
|
27
28
|
- [x] Run Knip as part of your CI environment to detect issues and prevent regressions.
|
|
28
29
|
|
|
29
30
|
Knip really shines in larger projects. A little bit of configuration will pay off, I promise. A comparison with similar
|
|
30
|
-
tools answers the question [why another unused file/dependency/export finder?][
|
|
31
|
+
tools answers the question [why another unused file/dependency/export finder?][4]
|
|
31
32
|
|
|
32
33
|
Knip is a fresh take on keeping your projects clean & tidy!
|
|
33
34
|
|
|
34
|
-
[![An orange cow with scissors, Van Gogh style][
|
|
35
|
+
[![An orange cow with scissors, Van Gogh style][6]][5] <sup>_“An orange cow with scissors, Van Gogh style” - generated
|
|
35
36
|
with OpenAI_</sup>
|
|
36
37
|
|
|
37
38
|
## Roadmap
|
|
38
39
|
|
|
39
|
-
Please report any false positives by [opening an issue in this repo][
|
|
40
|
+
Please report any false positives by [opening an issue in this repo][7]. Bonus points for adding a public repository or
|
|
40
41
|
opening a pull request with a directory and example files in `test/fixtures`. Correctness and bug fixes have priority
|
|
41
42
|
over new features:
|
|
42
43
|
|
|
@@ -92,15 +93,17 @@ Knip works by creating two sets of files:
|
|
|
92
93
|
-c/--config [file] Configuration file path (default: ./knip.json, knip.jsonc or package.json#knip)
|
|
93
94
|
-t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
|
|
94
95
|
--production Analyze only production source files (e.g. no tests, devDependencies, exported types)
|
|
96
|
+
--strict Consider only direct dependencies of workspaces. Not devDependencies, not ancestor workspaces.
|
|
95
97
|
--workspace Analyze a single workspace (default: analyze all configured workspaces)
|
|
96
98
|
--include Report only listed issue type(s), can be repeated
|
|
97
99
|
--exclude Exclude issue type(s) from report, can be repeated
|
|
98
100
|
--no-progress Don't show dynamic progress updates
|
|
101
|
+
--no-exit-code Always exit with code zero (0)
|
|
99
102
|
--max-issues Maximum number of issues before non-zero exit code (default: 0)
|
|
100
103
|
--reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
|
|
101
104
|
--reporter-options Pass extra options to the reporter (as JSON string, see example)
|
|
102
105
|
--debug Show debug output
|
|
103
|
-
--debug-level Set verbosity of debug output (default: 1, max:
|
|
106
|
+
--debug-level Set verbosity of debug output (default: 1, max: 3)
|
|
104
107
|
--performance Measure running time of expensive functions and display stats table
|
|
105
108
|
|
|
106
109
|
Issue types: files, dependencies, unlisted, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates
|
|
@@ -212,31 +215,46 @@ analyze unused dependencies and exports from any directory with a `package.json`
|
|
|
212
215
|
|
|
213
216
|
Knip contains a growing list of plugins:
|
|
214
217
|
|
|
215
|
-
- Babel
|
|
216
|
-
- Capacitor
|
|
217
|
-
- Changesets
|
|
218
|
-
- Cypress
|
|
219
|
-
- ESLint
|
|
220
|
-
- Gatsby
|
|
221
|
-
- Jest
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
218
|
+
- [Babel][9]
|
|
219
|
+
- [Capacitor][10]
|
|
220
|
+
- [Changesets][11]
|
|
221
|
+
- [Cypress][12]
|
|
222
|
+
- [ESLint][13]
|
|
223
|
+
- [Gatsby][14]
|
|
224
|
+
- [Jest][15]
|
|
225
|
+
- [Mocha][16]
|
|
226
|
+
- [Next.js][17]
|
|
227
|
+
- [Nx][18]
|
|
228
|
+
- [Playwright][19]
|
|
229
|
+
- [PostCSS][20]
|
|
230
|
+
- [Remark][21]
|
|
231
|
+
- [Remix][22]
|
|
232
|
+
- [Rollup][23]
|
|
233
|
+
- [Storybook][24]
|
|
234
|
+
- [Stryker][25]
|
|
230
235
|
|
|
231
236
|
Plugins are automatically activated, no need to enable anything. Each plugin is automatically enabled based on simple
|
|
232
237
|
heuristics. Most of them check whether one or one of a few (dev) dependencies are listed in `package.json`. Once
|
|
233
|
-
enabled, they add
|
|
238
|
+
enabled, they add a set of configuration and/or entry files for Knip to analyze. These defaults can be overriden.
|
|
234
239
|
|
|
235
|
-
Most
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
+
Most plugins use one or both of the following file types:
|
|
241
|
+
|
|
242
|
+
- `config` - custom dependency resolvers are applied to these files
|
|
243
|
+
- `entryFiles` - files to include with the analysis of the rest of the source code
|
|
244
|
+
|
|
245
|
+
Many configuration files use `require` or `import` statements to use dependencies, so they can be analyzed like the rest
|
|
246
|
+
of the source files. These configuration files are also `entryFiles`.
|
|
247
|
+
|
|
248
|
+
Many plugins also include `config` files. They are parsed by custom dependency resolvers.
|
|
249
|
+
|
|
250
|
+
- The `eslint` plugin tells Knip that an `"prettier"` entry in the array of plugins means that the
|
|
251
|
+
`eslint-plugin-prettier` dependency should be installed.
|
|
252
|
+
- The `storybook` plugin understands that `core.builder: 'webpack5'` in `main.js` means that the
|
|
253
|
+
`@storybook/builder-webpack5` and `@storybook/manager-webpack5` dependencies are required.
|
|
254
|
+
- Static configuration files such as JSON and YAML always require a custom dependency resolver.
|
|
255
|
+
|
|
256
|
+
The only thing that a custom dependency resolver function does is return all referenced dependencies for the
|
|
257
|
+
configuration files it is given.
|
|
240
258
|
|
|
241
259
|
## Configuration
|
|
242
260
|
|
|
@@ -258,26 +276,34 @@ export const merge = function () {};
|
|
|
258
276
|
|
|
259
277
|
Knip does not report public exports and types as unused.
|
|
260
278
|
|
|
261
|
-
### Production
|
|
262
|
-
|
|
263
|
-
The default configuration for Knip is loose and targets all project code. Non-production files such as tests often
|
|
264
|
-
import production files, which will prevent the production files from being reported as unused.
|
|
279
|
+
### Production Mode
|
|
265
280
|
|
|
266
|
-
|
|
267
|
-
|
|
281
|
+
The default mode for Knip is holistic and targets all project code. For instance test files usually import production
|
|
282
|
+
files. This prevents the production files or its exports from being reported as unused, while both of them can be
|
|
283
|
+
removed. This is why Knip has a production mode.
|
|
268
284
|
|
|
269
|
-
To
|
|
270
|
-
|
|
271
|
-
production like so:
|
|
285
|
+
To analyze only production code, add an exclamation mark behind the patterns that are meant for production and use the
|
|
286
|
+
`--production` flag to analyze (only) them. Here's an example:
|
|
272
287
|
|
|
273
288
|
```json
|
|
274
289
|
{
|
|
275
290
|
"entryFiles": ["build/script.js", "src/index.ts!"],
|
|
276
|
-
"projectFiles": ["src/**/*.ts"]
|
|
291
|
+
"projectFiles": ["src/**/*.ts!"]
|
|
277
292
|
}
|
|
278
293
|
```
|
|
279
294
|
|
|
280
|
-
Configuration,
|
|
295
|
+
Configuration files, test files and build scripts should not be included. Knip looks for unused files, dependencies and
|
|
296
|
+
export values in production code only.
|
|
297
|
+
|
|
298
|
+
#### Strict
|
|
299
|
+
|
|
300
|
+
Additionally, the `--strict` flag can be used to:
|
|
301
|
+
|
|
302
|
+
- Consider only `dependencies` (not `devDependencies`) when finding unused or unlisted dependencies.
|
|
303
|
+
- Assume each workspace is self-contained: they have their own `dependencies` (and not rely on packages of ancestor
|
|
304
|
+
workspaces).
|
|
305
|
+
|
|
306
|
+
#### Plugins
|
|
281
307
|
|
|
282
308
|
Plugins also have this distinction. For instance, Next.js entry files for pages (`pages/**/*.tsx`) and Remix routes
|
|
283
309
|
(`app/routes/**/*.tsx`) are production code, while Jest and Playwright entry files (e.g. `*.spec.ts`) are not. All of
|
|
@@ -288,10 +314,10 @@ locations. The more plugins Knip will have, the more projects can be analyzed ou
|
|
|
288
314
|
|
|
289
315
|
Knip provides the following built-in reporters:
|
|
290
316
|
|
|
291
|
-
- [`json`][
|
|
292
|
-
- [`symbol`][
|
|
293
|
-
- [`compact`][
|
|
294
|
-
- [`codeowners`][
|
|
317
|
+
- [`json`][26]
|
|
318
|
+
- [`symbol`][27] (default)
|
|
319
|
+
- [`compact`][28]
|
|
320
|
+
- [`codeowners`][29]
|
|
295
321
|
|
|
296
322
|
### Custom Reporters
|
|
297
323
|
|
|
@@ -346,11 +372,11 @@ per file like this:
|
|
|
346
372
|
]
|
|
347
373
|
```
|
|
348
374
|
|
|
349
|
-
The keys match the [known issue types][
|
|
375
|
+
The keys match the [known issue types][30].
|
|
350
376
|
|
|
351
377
|
#### Usage Ideas
|
|
352
378
|
|
|
353
|
-
Use tools like [miller][
|
|
379
|
+
Use tools like [miller][31] or [jtbl][32] to consume the JSON and render a table in the terminal.
|
|
354
380
|
|
|
355
381
|
##### Table
|
|
356
382
|
|
|
@@ -467,12 +493,12 @@ collect the various issues in one go?
|
|
|
467
493
|
|
|
468
494
|
This table is a work in progress, but here's a first impression. Based on their docs (please report any mistakes):
|
|
469
495
|
|
|
470
|
-
| Feature | **knip** | [depcheck][
|
|
496
|
+
| Feature | **knip** | [depcheck][33] | [unimported][34] | [ts-unused-exports][35] | [ts-prune][36] | [find-unused-exports][37] |
|
|
471
497
|
| :-------------------------------- | :------: | :------------: | :--------------: | :---------------------: | :------------: | :-----------------------: |
|
|
472
498
|
| Unused files | ✅ | - | ✅ | - | - | - |
|
|
473
499
|
| Unused dependencies | ✅ | ✅ | ✅ | - | - | - |
|
|
474
500
|
| Unlisted dependencies | ✅ | ✅ | ✅ | - | - | - |
|
|
475
|
-
| [Custom dependency resolvers][
|
|
501
|
+
| [Custom dependency resolvers][38] | ✅ | ✅ | ❌ | - | - | - |
|
|
476
502
|
| Unused exports | ✅ | - | - | ✅ | ✅ | ✅ |
|
|
477
503
|
| Unused class members | ✅ | - | - | - | - | - |
|
|
478
504
|
| Unused enum members | ✅ | - | - | - | - | - |
|
|
@@ -481,7 +507,7 @@ This table is a work in progress, but here's a first impression. Based on their
|
|
|
481
507
|
| Custom reporters | ✅ | - | - | - | - | - |
|
|
482
508
|
| JavaScript support | ✅ | ✅ | ✅ | - | - | ✅ |
|
|
483
509
|
| Configure entry files | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
484
|
-
| [Support monorepos][
|
|
510
|
+
| [Support monorepos][39] | ✅ | - | - | - | - | - |
|
|
485
511
|
| ESLint plugin available | - | - | - | ✅ | - | - |
|
|
486
512
|
|
|
487
513
|
✅ = Supported, ❌ = Not supported, - = Out of scope
|
|
@@ -498,27 +524,42 @@ userland territory, much like code linters.
|
|
|
498
524
|
Knip is Dutch for a "cut". A Dutch expression is "to be ge**knip**t for something", which means to be perfectly suited
|
|
499
525
|
for the job. I'm motivated to make knip perfectly suited for the job of cutting projects to perfection! ✂️
|
|
500
526
|
|
|
501
|
-
[1]: #
|
|
502
|
-
[2]: #
|
|
503
|
-
[3]: #
|
|
504
|
-
[4]:
|
|
505
|
-
[5]:
|
|
506
|
-
[6]:
|
|
507
|
-
[7]:
|
|
527
|
+
[1]: #plugins
|
|
528
|
+
[2]: #reporters
|
|
529
|
+
[3]: #custom-reporters
|
|
530
|
+
[4]: #really-another-unused-filedependencyexport-finder
|
|
531
|
+
[5]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
|
|
532
|
+
[6]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
|
|
533
|
+
[7]: https://github.com/webpro/knip/issues
|
|
508
534
|
[8]: ./assets/how-it-works.drawio.svg
|
|
509
|
-
[9]:
|
|
510
|
-
[10]:
|
|
511
|
-
[11]:
|
|
512
|
-
[12]:
|
|
513
|
-
[13]:
|
|
514
|
-
[14]:
|
|
515
|
-
[15]:
|
|
516
|
-
[16]:
|
|
517
|
-
[17]:
|
|
518
|
-
[18]:
|
|
519
|
-
[19]:
|
|
520
|
-
[20]:
|
|
521
|
-
[21]:
|
|
522
|
-
[22]:
|
|
523
|
-
[23]:
|
|
524
|
-
[24]:
|
|
535
|
+
[9]: ./src/plugins/babel
|
|
536
|
+
[10]: ./src/plugins/capacitor
|
|
537
|
+
[11]: ./src/plugins/changesets
|
|
538
|
+
[12]: ./src/plugins/cypress
|
|
539
|
+
[13]: ./src/plugins/eslint
|
|
540
|
+
[14]: ./src/plugins/gatsby
|
|
541
|
+
[15]: ./src/plugins/jest
|
|
542
|
+
[16]: ./src/plugins/mocha
|
|
543
|
+
[17]: ./src/plugins/next
|
|
544
|
+
[18]: ./src/plugins/nx
|
|
545
|
+
[19]: ./src/plugins/playwright
|
|
546
|
+
[20]: ./src/plugins/postcss
|
|
547
|
+
[21]: ./src/plugins/remark
|
|
548
|
+
[22]: ./src/plugins/remix
|
|
549
|
+
[23]: ./src/plugins/rollup
|
|
550
|
+
[24]: ./src/plugins/storybook
|
|
551
|
+
[25]: ./src/plugins/stryker
|
|
552
|
+
[26]: #json
|
|
553
|
+
[27]: #symbol-default
|
|
554
|
+
[28]: #compact
|
|
555
|
+
[29]: #code-owners
|
|
556
|
+
[30]: #reading-the-report
|
|
557
|
+
[31]: https://github.com/johnkerl/miller
|
|
558
|
+
[32]: https://github.com/kellyjonbrazil/jtbl
|
|
559
|
+
[33]: https://github.com/depcheck/depcheck
|
|
560
|
+
[34]: https://github.com/smeijer/unimported
|
|
561
|
+
[35]: https://github.com/pzavolinsky/ts-unused-exports
|
|
562
|
+
[36]: https://github.com/nadeesha/ts-prune
|
|
563
|
+
[37]: https://github.com/jaydenseric/find-unused-exports
|
|
564
|
+
[38]: #custom-dependency-resolvers
|
|
565
|
+
[39]: #monorepos-1
|
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { register } from 'esbuild-register/dist/node.js';
|
|
4
|
-
import { printHelp } from './util/help.js';
|
|
5
4
|
import reporters from './reporters/index.js';
|
|
6
5
|
import { ConfigurationError } from './util/errors.js';
|
|
6
|
+
import { printHelp } from './util/help.js';
|
|
7
7
|
import parsedArgs from './util/parseArgs.js';
|
|
8
8
|
import { measure } from './util/performance.js';
|
|
9
9
|
import { main } from './index.js';
|
|
10
10
|
register();
|
|
11
|
-
const { values: { help, '
|
|
11
|
+
const { values: { debug: isDebug = false, help, 'max-issues': maxIssues = '0', 'no-exit-code': noExitCode = false, 'no-gitignore': isNoGitIgnore = false, 'no-progress': noProgress = false, production: isProduction = false, reporter = 'symbols', 'reporter-options': reporterOptions = '', strict: isStrict = false, tsConfig, }, } = parsedArgs;
|
|
12
12
|
if (help) {
|
|
13
13
|
printHelp();
|
|
14
14
|
process.exit(0);
|
|
@@ -20,6 +20,7 @@ const run = async () => {
|
|
|
20
20
|
try {
|
|
21
21
|
const { report, issues, counters } = await main({
|
|
22
22
|
cwd,
|
|
23
|
+
tsConfigFile: tsConfig,
|
|
23
24
|
gitignore: !isNoGitIgnore,
|
|
24
25
|
isStrict,
|
|
25
26
|
isProduction,
|
|
@@ -30,8 +31,9 @@ const run = async () => {
|
|
|
30
31
|
.filter(reportGroup => report[reportGroup])
|
|
31
32
|
.reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
|
|
32
33
|
await measure.print();
|
|
33
|
-
if (totalErrorCount > Number(maxIssues))
|
|
34
|
+
if (!noExitCode && totalErrorCount > Number(maxIssues)) {
|
|
34
35
|
process.exit(totalErrorCount);
|
|
36
|
+
}
|
|
35
37
|
}
|
|
36
38
|
catch (error) {
|
|
37
39
|
if (error instanceof ConfigurationError) {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import micromatch from 'micromatch';
|
|
3
|
+
import { ROOT_WORKSPACE_NAME } from './constants.js';
|
|
4
|
+
import * as plugins from './plugins/index.js';
|
|
3
5
|
import { LocalConfiguration } from './types/validate.js';
|
|
4
6
|
import { arrayify } from './util/array.js';
|
|
5
|
-
import { ROOT_WORKSPACE_NAME } from './util/constants.js';
|
|
6
7
|
import { ConfigurationError } from './util/errors.js';
|
|
7
8
|
import { findFile, loadJSON } from './util/fs.js';
|
|
8
9
|
import { _dirGlob } from './util/glob.js';
|
|
9
10
|
import parsedArgs from './util/parseArgs.js';
|
|
10
11
|
import { resolveIncludedIssueTypes } from './util/resolveIncludedIssueTypes.js';
|
|
11
12
|
import { workspaceSorter } from './util/sort.js';
|
|
12
|
-
const { values: {
|
|
13
|
+
const { values: { config: rawConfigArg, workspace: rawWorkspaceArg, include = [], exclude = [], strict: isStrict = false, production: isProduction = false, }, } = parsedArgs;
|
|
13
14
|
const defaultConfig = {
|
|
14
15
|
include: [],
|
|
15
16
|
exclude: [],
|
|
@@ -25,23 +26,7 @@ const defaultConfig = {
|
|
|
25
26
|
},
|
|
26
27
|
},
|
|
27
28
|
};
|
|
28
|
-
const PLUGIN_NAMES =
|
|
29
|
-
'babel',
|
|
30
|
-
'capacitor',
|
|
31
|
-
'changesets',
|
|
32
|
-
'cypress',
|
|
33
|
-
'eslint',
|
|
34
|
-
'gatsby',
|
|
35
|
-
'jest',
|
|
36
|
-
'next',
|
|
37
|
-
'nx',
|
|
38
|
-
'playwright',
|
|
39
|
-
'postcss',
|
|
40
|
-
'remark',
|
|
41
|
-
'remix',
|
|
42
|
-
'rollup',
|
|
43
|
-
'storybook',
|
|
44
|
-
];
|
|
29
|
+
const PLUGIN_NAMES = Object.keys(plugins);
|
|
45
30
|
export default class ConfigurationChief {
|
|
46
31
|
cwd = process.cwd();
|
|
47
32
|
config;
|
|
@@ -103,12 +88,10 @@ export default class ConfigurationChief {
|
|
|
103
88
|
const config = isObject ? arrayify(pluginConfig.config) : arrayify(pluginConfig);
|
|
104
89
|
const entryFiles = isObject && 'entryFiles' in pluginConfig ? arrayify(pluginConfig.entryFiles) : [];
|
|
105
90
|
const projectFiles = isObject && 'projectFiles' in pluginConfig ? arrayify(pluginConfig.projectFiles) : entryFiles;
|
|
106
|
-
const sampleFiles = isObject && 'sampleFiles' in pluginConfig ? arrayify(pluginConfig.sampleFiles) : [];
|
|
107
91
|
workspaces[workspaceName][pluginName] = {
|
|
108
92
|
config,
|
|
109
93
|
entryFiles,
|
|
110
94
|
projectFiles,
|
|
111
|
-
sampleFiles,
|
|
112
95
|
};
|
|
113
96
|
}
|
|
114
97
|
}
|
|
File without changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export const ROOT_WORKSPACE_NAME = '.';
|
|
2
2
|
export const TEST_FILE_PATTERNS = ['**/*.{test,spec}.{js,jsx,ts,tsx}', '**/__tests__/**/*.{js,jsx,ts,tsx}'];
|
|
3
|
-
export const IGNORED_GLOBAL_BINARIES = ['npm', 'npx', 'node', 'yarn', 'pnpm'];
|
|
3
|
+
export const IGNORED_GLOBAL_BINARIES = ['npm', 'npx', 'node', 'yarn', 'pnpm', 'deno', 'git'];
|
|
4
4
|
export const FIRST_ARGUMENT_AS_BINARY_EXCEPTIONS = ['cross-env', 'dotenv'];
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import ConfigurationChief from './configuration-chief.js';
|
|
3
|
+
import { ROOT_WORKSPACE_NAME } from './constants.js';
|
|
3
4
|
import DependencyDeputy from './dependency-deputy.js';
|
|
4
5
|
import IssueCollector from './issue-collector.js';
|
|
5
6
|
import ProjectPrincipal from './project-principal.js';
|
|
6
7
|
import SourceLab from './source-lab.js';
|
|
7
8
|
import { compact } from './util/array.js';
|
|
8
|
-
import { ROOT_WORKSPACE_NAME } from './util/constants.js';
|
|
9
9
|
import { debugLogObject, debugLogFiles } from './util/debug.js';
|
|
10
10
|
import { _findExternalImportModuleSpecifiers } from './util/externalImports.js';
|
|
11
11
|
import { findFile, loadJSON } from './util/fs.js';
|
|
@@ -14,7 +14,7 @@ import { _findDuplicateExportedNames } from './util/project.js';
|
|
|
14
14
|
import { loadTSConfig } from './util/tsconfig-loader.js';
|
|
15
15
|
import WorkspaceWorker from './workspace-worker.js';
|
|
16
16
|
export const main = async (unresolvedConfiguration) => {
|
|
17
|
-
const { cwd, gitignore, isStrict, isProduction, isShowProgress } = unresolvedConfiguration;
|
|
17
|
+
const { cwd, tsConfigFile, gitignore, isStrict, isProduction, isShowProgress } = unresolvedConfiguration;
|
|
18
18
|
const chief = new ConfigurationChief({ cwd });
|
|
19
19
|
const deputy = new DependencyDeputy();
|
|
20
20
|
debugLogObject(1, 'Unresolved configuration', unresolvedConfiguration);
|
|
@@ -22,8 +22,6 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
22
22
|
collector.updateMessage('Reading configuration and manifest files...');
|
|
23
23
|
await chief.loadLocalConfig();
|
|
24
24
|
const workspaces = await chief.getActiveWorkspaces();
|
|
25
|
-
if (!chief.manifest || !chief.manifestPath)
|
|
26
|
-
throw new Error('mani');
|
|
27
25
|
debugLogObject(1, 'Included workspaces', workspaces);
|
|
28
26
|
const report = chief.resolveIncludedIssueTypes();
|
|
29
27
|
const workspaceDirs = Object.values(workspaces)
|
|
@@ -42,7 +40,7 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
42
40
|
if (!manifestPath || !manifest)
|
|
43
41
|
continue;
|
|
44
42
|
deputy.addWorkspace({ name, dir, manifestPath, manifest });
|
|
45
|
-
const tsConfigFilePath = path.join(dir, 'tsconfig.json');
|
|
43
|
+
const tsConfigFilePath = path.join(dir, tsConfigFile ?? 'tsconfig.json');
|
|
46
44
|
const tsConfig = await loadTSConfig(tsConfigFilePath);
|
|
47
45
|
if (isRoot && tsConfig) {
|
|
48
46
|
principal.tsConfigFilePath = tsConfigFilePath;
|
|
@@ -188,15 +186,15 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
188
186
|
}
|
|
189
187
|
else {
|
|
190
188
|
issue.symbol = deputy.resolvePackageName(issue.symbol);
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
189
|
+
if (deputy.isInternalDependency(name, issue.symbol))
|
|
190
|
+
continue;
|
|
191
|
+
if (workspaceDependencies.includes(issue.symbol))
|
|
192
|
+
continue;
|
|
193
|
+
if (isStrict) {
|
|
194
|
+
collector.addIssue(issue);
|
|
195
|
+
}
|
|
196
|
+
else if (!rootDependencies.includes(issue.symbol)) {
|
|
197
|
+
collector.addIssue(issue);
|
|
200
198
|
}
|
|
201
199
|
}
|
|
202
200
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { FIRST_ARGUMENT_AS_BINARY_EXCEPTIONS } from '../
|
|
3
|
+
import { FIRST_ARGUMENT_AS_BINARY_EXCEPTIONS } from '../constants.js';
|
|
4
4
|
const require = createRequire(process.cwd());
|
|
5
5
|
const normalizeBinaries = (command) => command.replace(/(\.\/)?node_modules\/\.bin\/(\w+)/, '$2').replace(/\$\(npm bin\)\/(\w+)/, '$1');
|
|
6
6
|
const stripEnvironmentVariables = (value) => value.replace(/([A-Z][^ ]*)=([^ ])+ /g, '');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { IGNORED_GLOBAL_BINARIES } from '../constants.js';
|
|
1
2
|
import { compact } from '../util/array.js';
|
|
2
|
-
import { IGNORED_GLOBAL_BINARIES } from '../util/constants.js';
|
|
3
3
|
import { timerify } from '../util/performance.js';
|
|
4
4
|
import { getBinariesFromScripts, getPackageManifest } from './helpers.js';
|
|
5
5
|
const findManifestDependencies = async (ignoreBinaries, manifest, isRoot, dir, cwd) => {
|
|
@@ -20,22 +20,17 @@ const api = {
|
|
|
20
20
|
caller: () => true,
|
|
21
21
|
};
|
|
22
22
|
const getDependenciesFromConfig = (config) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return compact([...presets, ...plugins]);
|
|
27
|
-
}
|
|
28
|
-
return [];
|
|
23
|
+
const presets = config.presets?.map(preset => (typeof preset === 'string' ? preset : preset[0])).map(resolvePresetName) ?? [];
|
|
24
|
+
const plugins = config.plugins?.map(plugin => (typeof plugin === 'string' ? plugin : plugin[0])).map(resolvePluginName) ?? [];
|
|
25
|
+
return compact([...presets, ...plugins]);
|
|
29
26
|
};
|
|
30
27
|
const findBabelDependencies = async (configFilePath, { manifest }) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
let config = await load(configFilePath);
|
|
28
|
+
let config = configFilePath.endsWith('package.json')
|
|
29
|
+
? manifest.babel
|
|
30
|
+
: await load(configFilePath);
|
|
36
31
|
if (typeof config === 'function') {
|
|
37
32
|
config = config(api);
|
|
38
33
|
}
|
|
39
|
-
return getDependenciesFromConfig(config);
|
|
34
|
+
return config ? getDependenciesFromConfig(config) : [];
|
|
40
35
|
};
|
|
41
36
|
export const findDependencies = timerify(findBabelDependencies);
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { ESLintConfig } from './types.js';
|
|
2
|
+
export declare const resolvePluginPackageName: (pluginName: string) => string;
|
|
3
|
+
export declare const customResolvePluginPackageNames: (extend: string) => string | undefined;
|
|
2
4
|
export declare const getDependenciesFromSettings: (settings?: ESLintConfig['settings']) => string[];
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import { compact } from '../../util/array.js';
|
|
2
|
+
import { getPackageName } from '../../util/modules.js';
|
|
3
|
+
export const resolvePluginPackageName = (pluginName) => {
|
|
4
|
+
return pluginName.startsWith('@')
|
|
5
|
+
? pluginName.includes('/')
|
|
6
|
+
? pluginName
|
|
7
|
+
: `${pluginName}/eslint-plugin`
|
|
8
|
+
: `eslint-plugin-${pluginName}`;
|
|
9
|
+
};
|
|
10
|
+
export const customResolvePluginPackageNames = (extend) => {
|
|
11
|
+
if (extend.includes('/node_modules/'))
|
|
12
|
+
return getPackageName(extend);
|
|
13
|
+
if (extend.startsWith('/') || extend.startsWith('.'))
|
|
14
|
+
return;
|
|
15
|
+
if (extend.includes(':')) {
|
|
16
|
+
const pluginName = extend.replace(/^plugin:/, '').replace(/(\/|:).+$/, '');
|
|
17
|
+
if (pluginName === 'eslint')
|
|
18
|
+
return;
|
|
19
|
+
return resolvePluginPackageName(pluginName);
|
|
20
|
+
}
|
|
21
|
+
return extend.includes('eslint') ? getPackageName(extend) : resolvePluginPackageName(extend);
|
|
22
|
+
};
|
|
2
23
|
const getImportPluginDependencies = (settings) => {
|
|
3
24
|
const knownKeys = ['typescript'];
|
|
4
25
|
if (Array.isArray(settings))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';
|
|
2
2
|
export declare const isEnabled: IsPluginEnabledCallback;
|
|
3
3
|
export declare const CONFIG_FILE_PATTERNS: string[];
|
|
4
|
+
export declare const ENTRY_FILE_PATTERNS: string[];
|
|
4
5
|
export declare const findDependencies: GenericPluginCallback;
|
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import { ESLint } from 'eslint';
|
|
3
2
|
import { compact } from '../../util/array.js';
|
|
3
|
+
import { _firstGlob } from '../../util/glob.js';
|
|
4
|
+
import load from '../../util/loader.js';
|
|
4
5
|
import { getPackageName } from '../../util/modules.js';
|
|
5
6
|
import { timerify } from '../../util/performance.js';
|
|
6
|
-
import { getDependenciesFromSettings } from './helpers.js';
|
|
7
|
-
export const isEnabled = ({ dependencies }) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const resolvePluginPackageName = (pluginName) => {
|
|
13
|
-
return pluginName.startsWith('@')
|
|
14
|
-
? pluginName.includes('/')
|
|
15
|
-
? pluginName
|
|
16
|
-
: `${pluginName}/eslint-plugin`
|
|
17
|
-
: `eslint-plugin-${pluginName}`;
|
|
18
|
-
};
|
|
19
|
-
const findESLintDependencies = async (configFilePath, { cwd, config }) => {
|
|
20
|
-
if (path.basename(configFilePath) === 'eslint.config.js') {
|
|
7
|
+
import { resolvePluginPackageName, customResolvePluginPackageNames, getDependenciesFromSettings } from './helpers.js';
|
|
8
|
+
export const isEnabled = ({ dependencies }) => dependencies.has('eslint');
|
|
9
|
+
export const CONFIG_FILE_PATTERNS = ['.eslintrc', '.eslintrc.{js,json}', 'package.json'];
|
|
10
|
+
export const ENTRY_FILE_PATTERNS = ['eslint.config.js'];
|
|
11
|
+
const findESLintDependencies = async (configFilePath, { cwd, manifest, workspaceConfig }) => {
|
|
12
|
+
if (configFilePath.endsWith('package.json') && !manifest.eslintConfig)
|
|
21
13
|
return [];
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
const config = configFilePath.endsWith('package.json')
|
|
15
|
+
? manifest.eslintConfig
|
|
16
|
+
: await load(configFilePath);
|
|
17
|
+
const rootExtends = config?.extends ? [config.extends].flat().map(customResolvePluginPackageNames) : [];
|
|
18
|
+
const patterns = [workspaceConfig.entryFiles, ...(config?.overrides?.map(overrides => overrides.files) ?? [])];
|
|
19
|
+
const samples = await Promise.all(patterns.map(patterns => _firstGlob({ patterns, cwd })));
|
|
20
|
+
const sampleFilePaths = samples
|
|
21
|
+
.filter((filePath) => typeof filePath !== 'undefined')
|
|
22
|
+
.map(String);
|
|
23
|
+
const engine = new ESLint({ cwd, overrideConfigFile: configFilePath, useEslintrc: false });
|
|
24
|
+
const calculateConfigForFile = async (sampleFile) => await engine.calculateConfigForFile(sampleFile);
|
|
25
|
+
const dependencies = await Promise.all(sampleFilePaths.map(calculateConfigForFile)).then(configs => configs.flatMap(config => {
|
|
26
|
+
if (!config)
|
|
27
|
+
return [];
|
|
28
|
+
const plugins = config.plugins?.map(resolvePluginPackageName) ?? [];
|
|
29
|
+
const parsers = config.parser ? [config.parser] : [];
|
|
30
|
+
const extraParsers = config.parserOptions?.babelOptions?.presets ?? [];
|
|
31
|
+
const settings = config.settings ? getDependenciesFromSettings(config.settings) : [];
|
|
32
|
+
return [...parsers, ...extraParsers, ...plugins, ...settings].map(getPackageName);
|
|
33
|
+
}));
|
|
34
|
+
return compact([...rootExtends, ...dependencies.flat()]);
|
|
38
35
|
};
|
|
39
36
|
export const findDependencies = timerify(findESLintDependencies);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type ESLintConfig = {
|
|
2
|
+
extends?: string | string[];
|
|
2
3
|
parser?: string;
|
|
3
4
|
parserOptions?: {
|
|
4
5
|
babelOptions?: {
|
|
@@ -7,4 +8,7 @@ export type ESLintConfig = {
|
|
|
7
8
|
};
|
|
8
9
|
plugins?: string[];
|
|
9
10
|
settings?: Record<string, Record<string, unknown>>;
|
|
11
|
+
overrides?: {
|
|
12
|
+
files: string[];
|
|
13
|
+
}[];
|
|
10
14
|
};
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * as cypress from './cypress/index.js';
|
|
|
5
5
|
export * as eslint from './eslint/index.js';
|
|
6
6
|
export * as gatsby from './gatsby/index.js';
|
|
7
7
|
export * as jest from './jest/index.js';
|
|
8
|
+
export * as mocha from './mocha/index.js';
|
|
8
9
|
export * as next from './next/index.js';
|
|
9
10
|
export * as nx from './nx/index.js';
|
|
10
11
|
export * as playwright from './playwright/index.js';
|
|
@@ -13,3 +14,4 @@ export * as remark from './remark/index.js';
|
|
|
13
14
|
export * as remix from './remix/index.js';
|
|
14
15
|
export * as rollup from './rollup/index.js';
|
|
15
16
|
export * as storybook from './storybook/index.js';
|
|
17
|
+
export * as stryker from './stryker/index.js';
|