Skip to content

Commit

Permalink
Fix exported identifier refs for re-exports (fixes #622)
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed May 4, 2024
1 parent 296ab06 commit 7b4da85
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { ComponentProps } from './interface';

export type { ComponentProps };

export default function Wrapper(props: ComponentProps) {
return props;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Component from './component';

Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface ComponentProps {
n: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ignoreExportsUsedInFile": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@fixtures/exports-in-file-allowed-re-export"
}
26 changes: 21 additions & 5 deletions packages/knip/src/typescript/getImportsAndExports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,15 +357,31 @@ const getImportsAndExports = (

const setRefs = (item: SerializableExport | SerializableExportMember) => {
if (!item.symbol) return;
const symbols = new Set<ts.Symbol>();
for (const match of sourceFile.text.matchAll(new RegExp(item.identifier.replace(/\$/g, '\\$'), 'g'))) {
const isDeclaration = match.index === item.pos || match.index === item.pos + 1; // off-by-one from `stripQuotes`
if (!isDeclaration) {
// @ts-expect-error ts.getTokenAtPosition is internal fn
const smbl = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, match.index));
// @ts-expect-error Keep it cheap
if (smbl && (item.symbol === smbl || item.symbol === smbl.declarations?.[0]?.name?.flowNode?.node?.symbol)) {
item.refs = 1;
break;
const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, match.index));
if (symbol) {
if (item.symbol === symbol) {
item.refs = 1;
break;
}
// @ts-expect-error Keep it cheap
const declaration = symbol.declarations?.[0];
if (declaration) {
if (item.symbol === declaration.name?.flowNode?.node?.symbol) {
item.refs = 1;
break;
}
if (ts.isImportSpecifier(declaration) && symbols.has(symbol)) {
// re-exported symbol is referenced
item.refs = 1;
break;
}
}
symbols.add(symbol);
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions packages/knip/test/exports-in-file-allowed-re-export.test.ts
Original file line number Diff line number Diff line change
@@ -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/exports-in-file-allowed-re-export');

test('Find unused exports respecting an ignoreExportsUsedInFile (re-export)', async () => {
const { counters } = await main({
...baseArguments,
cwd,
});

assert.deepEqual(counters, {
...baseCounters,
processed: 3,
total: 3,
});
});

0 comments on commit 7b4da85

Please sign in to comment.