Skip to content

Commit

Permalink
Do literal text search in setRefs (closes #595 #596 #664)
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed Jun 3, 2024
1 parent 90fcd4c commit 6e64d60
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 3 deletions.
38 changes: 38 additions & 0 deletions packages/knip/fixtures/exports-special-characters/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export const $dollar = '$';
export const dollar$ = '$$';
export const _underscore = '_';

export class DollarMembers {
$member: string;
member$: string;
$method: () => string;
method$: () => string;
}

export class $Dollar {}

export type $DollarType = string;

export enum Characters {
' ' = ' ',
'-' = '-',
',' = ',',
':' = ':',
'?' = '?',
'.' = '.',
'(' = '(',
')' = ')',
'[' = '[',
']' = ']',
'{' = '{',
'}' = '}',
'@' = '@',
'*' = '*',
'/' = '/',
'\\' = '\\',
'+' = '+',
'|' = '|',
$ = '$',
Slash = '/',
Space = ' ',
}
5 changes: 5 additions & 0 deletions packages/knip/fixtures/exports-special-characters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { type Characters, DollarMembers } from './exports';

type Ref = Characters;

const instance = new DollarMembers();
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@fixtures/exports-special-characters",
"knip": {
"include": ["classMembers"]
}
}
9 changes: 6 additions & 3 deletions packages/knip/src/typescript/getImportsAndExports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,13 @@ 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`
let index = 0;
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
while (index < sourceFile.text.length && (index = sourceFile.text.indexOf(item.identifier, index)) !== -1) {
const isDeclaration = index === item.pos || index === item.pos + 1; // off-by-one from `stripQuotes`
if (!isDeclaration) {
// @ts-expect-error ts.getTokenAtPosition is internal fn
const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, match.index));
const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, index));
if (symbol) {
if (item.symbol === symbol) {
item.refs = 1;
Expand All @@ -437,6 +439,7 @@ const getImportsAndExports = (
symbols.add(symbol);
}
}
index += item.identifier.length;
}
};

Expand Down
58 changes: 58 additions & 0 deletions packages/knip/test/exports-special-characters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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-special-characters');

test('Handle special characters in named exports and members', async () => {
const { issues, counters } = await main({
...baseArguments,
cwd,
});

assert(issues.exports['exports.ts']['$dollar']);
assert(issues.exports['exports.ts']['dollar$']);
assert(issues.exports['exports.ts']['_underscore']);
assert(issues.exports['exports.ts']['$Dollar']);

assert(issues.types['exports.ts']['$DollarType']);

assert(issues.classMembers['exports.ts']['$member']);
assert(issues.classMembers['exports.ts']['member$']);
assert(issues.classMembers['exports.ts']['$method']);
assert(issues.classMembers['exports.ts']['method$']);

assert(issues.enumMembers['exports.ts']['-']);
assert(issues.enumMembers['exports.ts'][',']);
assert(issues.enumMembers['exports.ts'][':']);
assert(issues.enumMembers['exports.ts']['?']);
assert(issues.enumMembers['exports.ts']['.']);
assert(issues.enumMembers['exports.ts']['(']);
assert(issues.enumMembers['exports.ts'][')']);
assert(issues.enumMembers['exports.ts']['[']);
assert(issues.enumMembers['exports.ts'][']']);
assert(issues.enumMembers['exports.ts']['{']);
assert(issues.enumMembers['exports.ts']['}']);
assert(issues.enumMembers['exports.ts']['@']);
assert(issues.enumMembers['exports.ts']['*']);
assert(issues.enumMembers['exports.ts']['/']);
assert(issues.enumMembers['exports.ts']['\\\\']);
assert(issues.enumMembers['exports.ts']['+']);
assert(issues.enumMembers['exports.ts']['|']);
assert(issues.enumMembers['exports.ts']['$']);
assert(issues.enumMembers['exports.ts']['Slash']);
assert(issues.enumMembers['exports.ts']['Space']);

assert.deepEqual(counters, {
...baseCounters,
classMembers: 4,
enumMembers: 20,
exports: 4,
types: 1,
processed: 2,
total: 2,
});
});

0 comments on commit 6e64d60

Please sign in to comment.