diff --git a/packages/knip/fixtures/import-equals/index.ts b/packages/knip/fixtures/import-equals/index.ts new file mode 100644 index 000000000..20bb7b27b --- /dev/null +++ b/packages/knip/fixtures/import-equals/index.ts @@ -0,0 +1,4 @@ +import * as NS from './my-module.js'; +import local = require('./local.js'); +import external = require('external'); +import something = NS.something; diff --git a/packages/knip/fixtures/import-equals/local.ts b/packages/knip/fixtures/import-equals/local.ts new file mode 100644 index 000000000..b2a29e559 --- /dev/null +++ b/packages/knip/fixtures/import-equals/local.ts @@ -0,0 +1 @@ +export default () => 1 diff --git a/packages/knip/fixtures/import-equals/my-module.ts b/packages/knip/fixtures/import-equals/my-module.ts new file mode 100644 index 000000000..1954434e2 --- /dev/null +++ b/packages/knip/fixtures/import-equals/my-module.ts @@ -0,0 +1 @@ +export const something = {}; diff --git a/packages/knip/fixtures/import-equals/package.json b/packages/knip/fixtures/import-equals/package.json new file mode 100644 index 000000000..f82273eae --- /dev/null +++ b/packages/knip/fixtures/import-equals/package.json @@ -0,0 +1,6 @@ +{ + "name": "@fixtures/import-equals", + "dependencies": { + "external": "*" + } +} diff --git a/packages/knip/fixtures/import-equals/tsconfig.json b/packages/knip/fixtures/import-equals/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/packages/knip/fixtures/import-equals/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/packages/knip/src/typescript/getImportsAndExports.ts b/packages/knip/src/typescript/getImportsAndExports.ts index ecdb95a83..85624f0cf 100644 --- a/packages/knip/src/typescript/getImportsAndExports.ts +++ b/packages/knip/src/typescript/getImportsAndExports.ts @@ -347,6 +347,17 @@ const getImportsAndExports = ( } } + if ( + isTopLevel && + ts.isImportEqualsDeclaration(node) && + ts.isQualifiedName(node.moduleReference) && + ts.isIdentifier(node.moduleReference.left) + ) { + // Pattern: import name = NS.identifier + const { left, right } = node.moduleReference; + if (sourceFile.locals?.get(left.text)) maybeAddAccessExpressionAsNsImport(left.text, right.text); + } + if (ts.isTypeReferenceNode(node) && ts.isQualifiedName(node.typeName)) { const [ns, ...right] = [node.typeName.left.getText(), node.typeName.right.getText()].join('.').split('.'); const members = right.map((_r, index) => right.slice(0, index + 1).join('.')); diff --git a/packages/knip/test/import-equals.test.ts b/packages/knip/test/import-equals.test.ts new file mode 100644 index 000000000..b55451339 --- /dev/null +++ b/packages/knip/test/import-equals.test.ts @@ -0,0 +1,21 @@ +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/import-equals'); + +test('Find used exports through import-equal NS.member access', async () => { + const { counters } = await main({ + ...baseArguments, + cwd, + }); + + assert.deepEqual(counters, { + ...baseCounters, + processed: 3, + total: 3, + }); +});