From fc95526767dd75b63e113c81504f5859d0f19746 Mon Sep 17 00:00:00 2001 From: Lars Kappert Date: Wed, 12 Feb 2025 17:04:08 +0100 Subject: [PATCH] Fix unused `@types` DT dependencies (resolve #942) --- .../knip/fixtures/dependencies-types/index.ts | 5 ++++ .../node_modules/@eslint/js/package.json | 6 +++++ .../node_modules/@types/ajv/package.json | 8 ++++++ .../node_modules/@types/eslint/package.json | 24 ++++++++++++++++++ .../@types/eslint__js/package.json | 11 ++++++++ .../node_modules/ajv/package.json | 7 ++++++ .../fixtures/dependencies-types/package.json | 12 +++++++++ packages/knip/src/DependencyDeputy.ts | 9 ++++--- packages/knip/test/definitely-typed.test.ts | 1 + packages/knip/test/dependencies-types.test.ts | 25 +++++++++++++++++++ 10 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 packages/knip/fixtures/dependencies-types/index.ts create mode 100644 packages/knip/fixtures/dependencies-types/node_modules/@eslint/js/package.json create mode 100644 packages/knip/fixtures/dependencies-types/node_modules/@types/ajv/package.json create mode 100644 packages/knip/fixtures/dependencies-types/node_modules/@types/eslint/package.json create mode 100644 packages/knip/fixtures/dependencies-types/node_modules/@types/eslint__js/package.json create mode 100644 packages/knip/fixtures/dependencies-types/node_modules/ajv/package.json create mode 100644 packages/knip/fixtures/dependencies-types/package.json create mode 100644 packages/knip/test/dependencies-types.test.ts diff --git a/packages/knip/fixtures/dependencies-types/index.ts b/packages/knip/fixtures/dependencies-types/index.ts new file mode 100644 index 000000000..e66170406 --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/index.ts @@ -0,0 +1,5 @@ +import { meta } from '@eslint/js'; +import { Ajv } from 'ajv'; + +console.log(meta); +console.log(Ajv); diff --git a/packages/knip/fixtures/dependencies-types/node_modules/@eslint/js/package.json b/packages/knip/fixtures/dependencies-types/node_modules/@eslint/js/package.json new file mode 100644 index 000000000..06a731741 --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/node_modules/@eslint/js/package.json @@ -0,0 +1,6 @@ +{ + "name": "@eslint/js", + "version": "9.20.0", + "main": "./src/index.js", + "types": "./types/index.d.ts" +} diff --git a/packages/knip/fixtures/dependencies-types/node_modules/@types/ajv/package.json b/packages/knip/fixtures/dependencies-types/node_modules/@types/ajv/package.json new file mode 100644 index 000000000..35583843c --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/node_modules/@types/ajv/package.json @@ -0,0 +1,8 @@ +{ + "name": "@types/ajv", + "version": "0.0.5", + "main": "", + "dependencies": {}, + "peerDependencies": {}, + "typesPublisherContentHash": "1a2c56d67ba8a8314ab6b7f3cec558506c1b7f5c7e91161332df137b4f798798" +} diff --git a/packages/knip/fixtures/dependencies-types/node_modules/@types/eslint/package.json b/packages/knip/fixtures/dependencies-types/node_modules/@types/eslint/package.json new file mode 100644 index 000000000..8572927b2 --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/node_modules/@types/eslint/package.json @@ -0,0 +1,24 @@ +{ + "name": "@types/eslint", + "version": "9.6.1", + "main": "", + "types": "index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts" + }, + "./use-at-your-own-risk": { + "types": "./use-at-your-own-risk.d.ts" + }, + "./rules": { + "types": "./rules/index.d.ts" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + }, + "typesPublisherContentHash": "bc2620143f844d291da2d199e7b8e2605e3277f1941a508dc72ac92843b149b6", + "typeScriptVersion": "4.8" +} diff --git a/packages/knip/fixtures/dependencies-types/node_modules/@types/eslint__js/package.json b/packages/knip/fixtures/dependencies-types/node_modules/@types/eslint__js/package.json new file mode 100644 index 000000000..88386875a --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/node_modules/@types/eslint__js/package.json @@ -0,0 +1,11 @@ +{ + "name": "@types/eslint__js", + "version": "8.42.3", + "main": "", + "types": "index.d.ts", + "dependencies": { + "@types/eslint": "*" + }, + "typesPublisherContentHash": "50440df96b971d2888a11ded23bb4fa236598d71ddda2a17ce1319a9fe86d6e0", + "typeScriptVersion": "4.5" +} diff --git a/packages/knip/fixtures/dependencies-types/node_modules/ajv/package.json b/packages/knip/fixtures/dependencies-types/node_modules/ajv/package.json new file mode 100644 index 000000000..fca60303c --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/node_modules/ajv/package.json @@ -0,0 +1,7 @@ +{ + "name": "ajv", + "version": "8.17.1", + "description": "Another JSON Schema Validator", + "main": "dist/ajv.js", + "types": "dist/ajv.d.ts" +} diff --git a/packages/knip/fixtures/dependencies-types/package.json b/packages/knip/fixtures/dependencies-types/package.json new file mode 100644 index 000000000..84571126d --- /dev/null +++ b/packages/knip/fixtures/dependencies-types/package.json @@ -0,0 +1,12 @@ +{ + "name": "@fixtures/dependencies-types", + "dependencies": { + "@eslint/js": "^9.20.0", + "ajv": "^8.17.1" + }, + "devDependencies": { + "@types/ajv": "^0.0.5", + "@types/eslint__js": "^8.42.3", + "@types/node": "^20.14.8" + } +} diff --git a/packages/knip/src/DependencyDeputy.ts b/packages/knip/src/DependencyDeputy.ts index b3fb36a22..e4f640ffb 100644 --- a/packages/knip/src/DependencyDeputy.ts +++ b/packages/knip/src/DependencyDeputy.ts @@ -162,7 +162,7 @@ export class DependencyDeputy { } getHasTypesIncluded(workspaceName: string) { - return this.installedBinaries.get(workspaceName); + return this.hasTypesIncluded.get(workspaceName); } addReferencedDependency(workspaceName: string, packageName: string) { @@ -214,7 +214,8 @@ export class DependencyDeputy { if (closestWorkspaceName || closestWorkspaceNameForTypes) { if (closestWorkspaceName) this.addReferencedDependency(closestWorkspaceName, packageName); - if (closestWorkspaceNameForTypes) this.addReferencedDependency(closestWorkspaceNameForTypes, typesPackageName); + if (closestWorkspaceNameForTypes && !this.hasTypesIncluded.get(closestWorkspaceNameForTypes)?.has(packageName)) + this.addReferencedDependency(closestWorkspaceNameForTypes, typesPackageName); return true; } this.addReferencedDependency(workspace.name, packageName); @@ -277,7 +278,7 @@ export class DependencyDeputy { if (IGNORE_DEFINITELY_TYPED.has(typedPackageName)) return true; // The `pkg` dependency already has types included, i.e. this `@types/pkg` is obsolete - if (hasTypesIncluded?.has(typedDependency)) return false; + if (hasTypesIncluded?.has(typedPackageName)) return false; // Ignore typed dependencies that have a host dependency that's referenced // Example: `next` (host) has `react-dom` and/or `@types/react-dom` (peer), peers can be ignored if host `next` is referenced @@ -287,7 +288,7 @@ export class DependencyDeputy { ]; if (hostDependencies.length) return !!hostDependencies.find(host => isReferencedDependency(host.name, true)); - if (!referencedDependencies) return false; + if (!referencedDependencies?.has(dependency)) return false; return referencedDependencies.has(typedPackageName); } diff --git a/packages/knip/test/definitely-typed.test.ts b/packages/knip/test/definitely-typed.test.ts index 3fc46a35d..21ccc3d03 100644 --- a/packages/knip/test/definitely-typed.test.ts +++ b/packages/knip/test/definitely-typed.test.ts @@ -14,6 +14,7 @@ test('Find unused DT @types', async () => { }); assert(issues.devDependencies['package.json']['@types/unused']); + assert(issues.devDependencies['package.json']['@types/mocha']); assert.deepEqual(counters, { ...baseCounters, diff --git a/packages/knip/test/dependencies-types.test.ts b/packages/knip/test/dependencies-types.test.ts new file mode 100644 index 000000000..fad8bb4da --- /dev/null +++ b/packages/knip/test/dependencies-types.test.ts @@ -0,0 +1,25 @@ +import { test } from 'bun:test'; +import assert from 'node:assert/strict'; +import { main } from '../src/index.js'; +import { resolve } from '../src/util/path.js'; +import baseArguments from './helpers/baseArguments.js'; +import baseCounters from './helpers/baseCounters.js'; + +const cwd = resolve('fixtures/dependencies-types'); + +test('Find unused @types dependencies', async () => { + const { issues, counters } = await main({ + ...baseArguments, + cwd, + }); + + assert(issues.devDependencies['package.json']['@types/ajv']); + assert(issues.devDependencies['package.json']['@types/eslint__js']); + + assert.deepEqual(counters, { + ...baseCounters, + devDependencies: 2, + processed: 1, + total: 1, + }); +});