Skip to content

Commit

Permalink
Add line, col & pos to exports/types issues (#288)
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed Oct 22, 2023
1 parent ec6473b commit 92c4a80
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 20 deletions.
31 changes: 17 additions & 14 deletions src/ProjectPrincipal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isGitIgnoredSync } from 'globby';
import ts from 'typescript';
import { DEFAULT_EXTENSIONS } from './constants.js';
import { IGNORED_FILE_EXTENSIONS } from './constants.js';
import { getJSDocTags, isInModuleBlock } from './typescript/ast-helpers.js';
import { getJSDocTags, getLineAndCharacterOfPosition, isInModuleBlock } from './typescript/ast-helpers.js';
import { createHosts } from './typescript/createHosts.js';
import { getImportsAndExports } from './typescript/getImportsAndExports.js';
import { SourceFileManager } from './typescript/SourceFileManager.js';
Expand Down Expand Up @@ -254,19 +254,17 @@ export class ProjectPrincipal {
}

public findUnusedMembers(filePath: string, members: ExportItemMember[]) {
return members
.filter(member => {
if (getJSDocTags(member.node).has('@public')) return false;
const referencedSymbols = this.findReferences(filePath, member.pos);
const files = referencedSymbols
.flatMap(refs => refs.references)
.filter(ref => !ref.isDefinition)
.map(ref => ref.fileName);
const internalRefs = files.filter(f => f === filePath);
const externalRefs = files.filter(f => f !== filePath);
return externalRefs.length === 0 && internalRefs.length === 0;
})
.map(member => member.identifier);
return members.filter(member => {
if (getJSDocTags(member.node).has('@public')) return false;
const referencedSymbols = this.findReferences(filePath, member.pos);
const files = referencedSymbols
.flatMap(refs => refs.references)
.filter(ref => !ref.isDefinition)
.map(ref => ref.fileName);
const internalRefs = files.filter(f => f === filePath);
const externalRefs = files.filter(f => f !== filePath);
return externalRefs.length === 0 && internalRefs.length === 0;
});
}

private findReferences(filePath: string, pos: number) {
Expand All @@ -277,4 +275,9 @@ export class ProjectPrincipal {
return [];
}
}

public getPos(node: ts.Node, pos: number) {
const { line, character } = getLineAndCharacterOfPosition(node, pos);
return { pos, line: line + 1, col: character + 1 };
}
}
31 changes: 27 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,25 @@ export const main = async (unresolvedConfiguration: CommandLineOptions) => {
if (report.enumMembers && exportedItem.type === 'enum' && exportedItem.members) {
if (isProduction) continue;
principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
collector.addIssue({ type: 'enumMembers', filePath, symbol: member, parentSymbol: symbol });
collector.addIssue({
type: 'enumMembers',
filePath,
symbol: member.identifier,
parentSymbol: symbol,
...principal.getPos(member.node, member.pos),
});
});
}

if (report.classMembers && exportedItem.type === 'class' && exportedItem.members) {
principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
collector.addIssue({ type: 'classMembers', filePath, symbol: member, parentSymbol: symbol });
collector.addIssue({
type: 'classMembers',
filePath,
symbol: member.identifier,
parentSymbol: symbol,
...principal.getPos(member.node, member.pos),
});
});
}

Expand All @@ -429,10 +441,21 @@ export const main = async (unresolvedConfiguration: CommandLineOptions) => {
if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
if (isProduction) continue;
const type = isStar ? 'nsTypes' : 'types';
collector.addIssue({ type, filePath, symbol, symbolType: exportedItem.type });
collector.addIssue({
type,
filePath,
symbol,
symbolType: exportedItem.type,
...principal.getPos(exportedItem.node, exportedItem.pos),
});
} else {
const type = isStar ? 'nsExports' : 'exports';
collector.addIssue({ type, filePath, symbol });
collector.addIssue({
type,
filePath,
symbol,
...principal.getPos(exportedItem.node, exportedItem.pos),
});
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/types/issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export type Issue = {
symbolType?: SymbolType;
parentSymbol?: string;
severity?: IssueSeverity;
pos?: number;
line?: number;
col?: number;
};

export type IssueSet = Set<string>;
Expand Down
4 changes: 4 additions & 0 deletions src/typescript/ast-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,7 @@ export const getJSDocTags = (node: ts.Node) => {
}
return tags;
};

export const getLineAndCharacterOfPosition = (node: ts.Node, pos: number) => {
return node.getSourceFile().getLineAndCharacterOfPosition(pos);
};
4 changes: 2 additions & 2 deletions src/typescript/getImportsAndExports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ export const getImportsAndExports = (sourceFile: BoundSourceFile, options: GetIm
const item = exports.get(identifier)!;
const crew = [...item.members, ...members];
const tags = new Set([...item.jsDocTags, ...jsDocTags]);
exports.set(identifier, { ...item, node, type, pos, members: crew, jsDocTags: tags });
exports.set(identifier, { ...item, members: crew, jsDocTags: tags });
} else {
exports.set(identifier, { node, type, pos, members, jsDocTags });
exports.set(identifier, { node, type, members, jsDocTags, pos });
}

if (!jsDocTags.has('@alias')) {
Expand Down
16 changes: 16 additions & 0 deletions test/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ test('Find unused exports', async () => {
assert.equal(Object.values(issues.duplicates).length, 1);
assert.equal(issues.duplicates['my-module.ts']['exportedResult|default'].symbols?.[0], 'exportedResult');

assert.equal(issues.exports['default.ts']['NamedExport'].line, 1);
assert.equal(issues.exports['default.ts']['NamedExport'].col, 14);
assert.equal(issues.exports['default.ts']['NamedExport'].pos, 13);

assert.equal(issues.types['my-module.ts']['MyAnyType'].line, 19);
assert.equal(issues.types['my-module.ts']['MyAnyType'].col, 13);
assert.equal(issues.types['my-module.ts']['MyAnyType'].pos, 702);

assert.equal(issues.nsExports['my-namespace.ts']['nsUnusedKey'].line, 3);
assert.equal(issues.nsExports['my-namespace.ts']['nsUnusedKey'].col, 14);
assert.equal(issues.nsExports['my-namespace.ts']['nsUnusedKey'].pos, 84);

assert.equal(issues.nsTypes['my-namespace.ts']['MyNamespace'].line, 5);
assert.equal(issues.nsTypes['my-namespace.ts']['MyNamespace'].col, 18);
assert.equal(issues.nsTypes['my-namespace.ts']['MyNamespace'].pos, 119);

assert.deepEqual(counters, {
...baseCounters,
exports: 8,
Expand Down

0 comments on commit 92c4a80

Please sign in to comment.