Skip to content

Commit

Permalink
Support exported types
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed Oct 2, 2024
1 parent b0bb643 commit 2a251fa
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 18 deletions.
1 change: 1 addition & 0 deletions packages/knip/fixtures/fix/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ USED;
identifier;
a;
NS.One;
NS.Nine;
9 changes: 9 additions & 0 deletions packages/knip/fixtures/fix/reexported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,13 @@ export { Two, Three };

export { Four as Fourth, Five as Fifth };

type Six = any;
type Seven = unknown;
const Eight = 8;
const Nine = 9;
type Ten = unknown[];
;

export { type Seven, Eight, Nine, type Ten };

export const One = 1;
2 changes: 1 addition & 1 deletion packages/knip/fixtures/fix/reexports.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { RangeSlider } from './reexported';
export { Rating } from './reexported';
export { One } from './reexported';
export { One, Six, Seven, Eight, Nine, Ten } from './reexported';
export { Col, Col as KCol } from './reexported';
export { Row as KRow, Row } from './reexported';
17 changes: 7 additions & 10 deletions packages/knip/src/IssueFixer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { readFile, rm, writeFile } from 'node:fs/promises';
import type { Fixes } from './types/exports.js';
import type { Fix, Fixes } from './types/exports.js';
import type { Issues } from './types/issues.js';
import { cleanExport } from './util/clean-export.js';
import { load, save } from './util/package-json.js';
Expand All @@ -20,8 +20,8 @@ export class IssueFixer {
isFixUnusedTypes = true;
isFixUnusedExports = true;

unusedTypeNodes: Map<string, Set<[number, number, boolean]>> = new Map();
unusedExportNodes: Map<string, Set<[number, number, boolean]>> = new Map();
unusedTypeNodes: Map<string, Set<Fix>> = new Map();
unusedExportNodes: Map<string, Set<Fix>> = new Map();

constructor({ isEnabled, cwd, fixTypes = [], isRemoveFiles }: Fixer) {
this.isEnabled = isEnabled;
Expand Down Expand Up @@ -77,16 +77,13 @@ export class IssueFixer {
private async removeUnusedExportKeywords(issues: Issues) {
const filePaths = new Set([...this.unusedTypeNodes.keys(), ...this.unusedExportNodes.keys()]);
for (const filePath of filePaths) {
const exportPositions: Fixes = [
...(this.isFixUnusedTypes ? (this.unusedTypeNodes.get(filePath) ?? []) : []),
...(this.isFixUnusedExports ? (this.unusedExportNodes.get(filePath) ?? []) : []),
].sort((a, b) => b[0] - a[0]);
const types = (this.isFixUnusedTypes && this.unusedTypeNodes.get(filePath)) || [];
const exports = (this.isFixUnusedExports && this.unusedExportNodes.get(filePath)) || [];
const exportPositions = [...types, ...exports].filter(fix => fix !== undefined).sort((a, b) => b[0] - a[0]);

if (exportPositions.length > 0) {
const sourceFileText = exportPositions.reduce(
(text, [start, end, isCleanable]) => {
return cleanExport({ text, start, end, isCleanable: Boolean(isCleanable) });
},
(text, [start, end, isCleanable]) => cleanExport({ text, start, end, isCleanable: Boolean(isCleanable) }),
await readFile(filePath, 'utf-8')
);

Expand Down
53 changes: 47 additions & 6 deletions packages/knip/src/util/clean-export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,23 @@ export const cleanExport = ({ text, start, end, isCleanable }: FixerOptions) =>
}

if (bracketCloseIndex === -1) {
return beforeStart + (commaIndex === -1 ? afterEnd : afterEnd.substring(commaIndex + 1));
let x = 0;
let j = beforeStart.length - 1;
while (j >= 0) {
const char = beforeStart[j];
if (!/\s/.test(char)) {
if (beforeStart.substring(j - 3, j + 1) === 'type') {
x = 5;
}
break;
}
j--;
}

return (
beforeStart.substring(0, beforeStart.length - x) +
(commaIndex === -1 ? afterEnd : afterEnd.substring(commaIndex + 1))
);
}

let j = beforeStart.length - 1;
Expand All @@ -40,25 +56,50 @@ export const cleanExport = ({ text, start, end, isCleanable }: FixerOptions) =>
bracketOpenIndex = j;
break;
}
if (!/\s/.test(char)) break;
if (!/\s/.test(char)) {
if (beforeStart.substring(j - 3, j + 1) === 'type') {
j = j - 4;
continue;
}
break;
}
j--;
}

if (bracketCloseIndex !== -1 && bracketOpenIndex !== -1) {
const toBracket = beforeStart.substring(0, bracketOpenIndex).trim();
if (toBracket.endsWith('export')) {
const exportLength = toBracket.endsWith('export') ? 6 : toBracket.endsWith('export type') ? 12 : 0;
if (exportLength) {
const fromBracket = afterEnd.substring(bracketCloseIndex + 1).trim();
if (fromBracket.startsWith('from')) {
const quoteMatch = afterEnd.match(/['"].*?['"]/);
if (quoteMatch?.index) {
const fromSpecifierLength = quoteMatch.index + quoteMatch[0].length;
return toBracket.substring(0, toBracket.length - 6) + afterEnd.substring(fromSpecifierLength);
return toBracket.substring(0, toBracket.length - exportLength) + afterEnd.substring(fromSpecifierLength);
}
}

return toBracket.substring(0, toBracket.length - 6) + afterEnd.substring(bracketCloseIndex + 1);
return toBracket.substring(0, toBracket.length - exportLength) + afterEnd.substring(bracketCloseIndex + 1);
}
}

return beforeStart + (commaIndex === -1 ? afterEnd : afterEnd.substring(commaIndex + 1));
{
let x = 0;
let j = beforeStart.length - 1;
while (j >= 0) {
const char = beforeStart[j];
if (!/\s/.test(char)) {
if (beforeStart.substring(j - 3, j + 1) === 'type') {
x = 5;
}
break;
}
j--;
}

return (
beforeStart.substring(0, beforeStart.length - x) +
(commaIndex === -1 ? afterEnd : afterEnd.substring(commaIndex + 1))
);
}
};
11 changes: 10 additions & 1 deletion packages/knip/test/fix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ module.exports = { identifier, };
await readContents('reexports.js'),
`;
;
export { One } from './reexported';
export { One, Nine, } from './reexported';
;
;
`,
Expand All @@ -76,6 +76,15 @@ const Five = 5;
;
type Six = any;
type Seven = unknown;
const Eight = 8;
const Nine = 9;
type Ten = unknown[];
;
export { Nine, };
export const One = 1;
`,
],
Expand Down
25 changes: 25 additions & 0 deletions packages/knip/test/util/clean-export.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,29 @@ test('fixer', () => {
const text = 'export { AB } from "specifier"';
assert.deepEqual(cleanExport(getOpts(text, 'AB')), '');
}

{
const text = 'export type { AB }';
assert.deepEqual(cleanExport(getOpts(text, 'AB')), '');
}

{
const text = 'export { type AB }';
assert.deepEqual(cleanExport(getOpts(text, 'AB')), '');
}

{
const text = 'export { type AB, type CD, type EF }';
assert.deepEqual(cleanExport(getOpts(text, 'CD')), 'export { type AB, type EF }');
}

{
const text = 'export { type AB, CD, type EF }';
assert.deepEqual(cleanExport(getOpts(text, 'AB')), 'export { CD, type EF }');
}

{
const text = 'export { AB, CD, type EF }';
assert.deepEqual(cleanExport(getOpts(text, 'EF')), 'export { AB, CD, }');
}
});

0 comments on commit 2a251fa

Please sign in to comment.