Skip to content

Commit feae531

Browse files
Fix organizing imports with type aliases (aljazsim#9)
Co-authored-by: Aljaz Simonic <aljaz.simonic@purple.telstra.com>
1 parent cde545c commit feae531

27 files changed

+400
-12
lines changed

src/elements/import-node.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class ImportNode extends ElementNode
1111

1212
public isModuleReference = false;
1313
public nameBinding: string | null = null;
14-
public namedImports: string[] | null = null;
14+
public namedImports: { type: boolean, name: string, alias: string | null }[] | null = null;
1515
public namespace: string | null = null;
1616
public source: string;
1717

@@ -30,6 +30,7 @@ export class ImportNode extends ElementNode
3030

3131
const isRelativeReference = this.source.startsWith(".") || this.source.startsWith("..");
3232
const isAbsoluteReference = !isRelativeReference && (this.source.indexOf("/") > -1 || this.source.indexOf("\\") > -1);
33+
3334
this.isModuleReference = !isRelativeReference && !isAbsoluteReference;
3435
}
3536

@@ -58,7 +59,12 @@ export class ImportNode extends ElementNode
5859
}
5960
else if (ts.isNamedImports(node.importClause?.namedBindings))
6061
{
61-
this.namedImports = distinct(node.importClause?.namedBindings.elements.map(e => e.name.text.trim()));
62+
this.namedImports = distinct(node.importClause?.namedBindings.elements.map(e => (
63+
{
64+
type: e.isTypeOnly,
65+
name: e.name.text.trim(),
66+
alias: e.propertyName?.text.trim() ?? null
67+
})));
6268
}
6369
}
6470

src/helpers/array-helper.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ export function add<T>(items: T[] | null | undefined, itemToAdd: T)
1010

1111
export function distinct<T>(items: T[])
1212
{
13-
return items.filter((value, index, array) => array.indexOf(value) === index);
13+
return items.map(i => JSON.stringify(i))
14+
.filter((value, index, array) => array.indexOf(value) === index)
15+
.map(i => JSON.parse(i) as T);
1416
}
1517

1618
export function except<T>(items1: T[] | null | undefined, items2: T[] | null | undefined)

src/source-code/source-code-organizer.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export class SourceCodeOrganizer
120120

121121
if (configuration.sortImportsByName)
122122
{
123-
imports.filter(i => i.namedImports).forEach(i => i.namedImports = i.namedImports!.sort((a, b) => compareStrings(a, b)));
123+
imports.filter(i => i.namedImports).forEach(i => i.namedImports = i.namedImports!.sort((a, b) => compareStrings(a.name, b.name)));
124124
}
125125

126126
if (configuration.groupImportsBySource)
@@ -274,7 +274,7 @@ export class SourceCodeOrganizer
274274
{
275275
for (const identifier of import1.namedImports)
276276
{
277-
if (!SourceCodeAnalyzer.hasReference(sourceFile, identifier))
277+
if (!SourceCodeAnalyzer.hasReference(sourceFile, identifier.name))
278278
{
279279
remove(import1.namedImports, identifier);
280280

src/source-code/source-code-printer.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class SourceCodePrinter
8585
{
8686
const source = node.source;
8787
const quote = configuration.quote === ImportSourceFilePathQuoteType.Single ? "'" : '"';
88-
const namedImports = (node.namedImports ?? []).filter(ni => ni && ni.trim().length > 0);
88+
const namedImports = (node.namedImports ?? []).filter(ni => ni && ni.name.trim().length > 0);
8989
const nameBinding = node.nameBinding;
9090
const namespace = node.namespace;
9191

@@ -97,7 +97,7 @@ export class SourceCodePrinter
9797
}
9898
else if (namedImports.length > 0)
9999
{
100-
return new SourceCode(`import ${nameBinding}, { ${namedImports.join(", ")} } from ${quote}${source}${quote};`);
100+
return new SourceCode(`import ${nameBinding}, { ${namedImports.map(ni => (ni.type ? "type " : "") + (ni.alias ? (ni.alias + " as ") : "") + ni.name).join(", ")} } from ${quote}${source}${quote};`);
101101
}
102102
else
103103
{
@@ -110,7 +110,7 @@ export class SourceCodePrinter
110110
}
111111
else if (namedImports.length > 0)
112112
{
113-
return new SourceCode(`import { ${namedImports.join(", ")} } from ${quote}${source}${quote};`);
113+
return new SourceCode(`import { ${namedImports.map(ni => (ni.type ? "type " : "") + (ni.alias ? (ni.alias + " as ") : "") + ni.name).join(", ")} } from ${quote}${source}${quote};`);
114114
}
115115
else
116116
{

test/helpers/organize-test-helper.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,36 @@ import { getFileNameWithoutExtension, joinPath } from "../../src/helpers/file-sy
22
import { getTestConfigurationFilePaths } from "./configuration-helper.js";
33
import { OrganizeTestParameters } from "./organize-test-parameters.js";
44

5-
// #region Exported Functions (1)
5+
// #region Exported Functions (2)
6+
7+
export function getOrganizeSpecialCaseTestParameters()
8+
{
9+
const specialTestCaseOutputDirectoryPath = "./test/organize-files/special-cases";
10+
const specialTestCase1tOutput = `${specialTestCaseOutputDirectoryPath}/special-test-case-1`;
11+
12+
const specialTestCaseInputDirectoryPath = './test/organize-files/special-cases';
13+
const specialTestCase1Input = `${specialTestCaseInputDirectoryPath}/special-test-case-1/special-test-case-1.ts`;
14+
15+
const tests = [
16+
{ name: "Special Test Case 1", inputFilePath: specialTestCase1Input, outputDirectoryPath: specialTestCase1tOutput },
17+
];
18+
const memberOrganizeParameters: OrganizeTestParameters[] = [];
19+
20+
for (const test of tests)
21+
{
22+
for (const configurationFilePath of getTestConfigurationFilePaths())
23+
{
24+
const description = `organize ${test.name}: ${getFileNameWithoutExtension(configurationFilePath).replaceAll("-", " ")}`;
25+
const inputFilePath = test.inputFilePath;
26+
const outputDirectoryPath = test.outputDirectoryPath;
27+
const outputFilePath = joinPath(outputDirectoryPath, getFileNameWithoutExtension(configurationFilePath) + ".ts");
28+
29+
memberOrganizeParameters.push(new OrganizeTestParameters(description, configurationFilePath, inputFilePath, outputFilePath));
30+
}
31+
}
32+
33+
return memberOrganizeParameters;
34+
}
635

736
export function getOrganizeTestParameters()
837
{
@@ -13,7 +42,7 @@ export function getOrganizeTestParameters()
1342
const testOutputModuleDirectoryPath = `${testOutputDirectoryPath}/module`;
1443
const testOutputTypeDirectoryPath = `${testOutputDirectoryPath}/type`;
1544
const testOutputVariableDirectoryPath = `${testOutputDirectoryPath}/variable`;
16-
45+
1746
const testInputDirectoryPath = './test/organize-files/ts-files';
1847
const testInputClassFilePath = `${testInputDirectoryPath}/class/test-class.ts`;
1948
const testInputFunctionFilePath = `${testInputDirectoryPath}/function/test-function.ts`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { expect, test } from "vitest";
2+
3+
import { Configuration } from "../../src/configuration/configuration.js";
4+
import { deleteFile, fileExists, readFile, writeFile } from "../../src/helpers/file-system-helper.js";
5+
import { SourceCodeOrganizer } from "../../src/source-code/source-code-organizer.js";
6+
import { getOrganizeSpecialCaseTestParameters } from "../helpers/organize-test-helper.js";
7+
8+
for (const otp of getOrganizeSpecialCaseTestParameters())
9+
{
10+
test(otp.description, async () =>
11+
{
12+
// arrange
13+
const configuration = await Configuration.getConfiguration(otp.configurationFilePath);
14+
const sourceCodeFilePath = otp.input;
15+
const sourceCode = await readFile(sourceCodeFilePath);
16+
const expectedOrganizedSourceCodeFilePath = otp.output;
17+
18+
// act
19+
const organizedSourceCodeFilePath = expectedOrganizedSourceCodeFilePath + ".invalid";
20+
const organizedSourceCode = await SourceCodeOrganizer.organizeSourceCode(sourceCodeFilePath, sourceCode, configuration);
21+
22+
if (!(await fileExists(expectedOrganizedSourceCodeFilePath)))
23+
{
24+
await writeFile(expectedOrganizedSourceCodeFilePath, organizedSourceCode, false);
25+
}
26+
27+
const expectedOrganizedSourceCode = await readFile(expectedOrganizedSourceCodeFilePath);
28+
29+
if (organizedSourceCode === expectedOrganizedSourceCode)
30+
{
31+
if (await fileExists(organizedSourceCodeFilePath))
32+
{
33+
await deleteFile(organizedSourceCodeFilePath);
34+
}
35+
}
36+
else
37+
{
38+
await writeFile(organizedSourceCodeFilePath, organizedSourceCode, true);
39+
}
40+
41+
// assert
42+
expect(organizedSourceCode).toBe(expectedOrganizedSourceCode);
43+
});
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
// #region Exported Functions (2)
5+
6+
export function gen(): string
7+
{
8+
return uuidv4();
9+
}
10+
11+
export function test(date: DateTime): Duration
12+
{
13+
return date.diffNow('day');
14+
}
15+
16+
// #endregion Exported Functions
17+
18+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
// #region Exported Functions (2)
5+
6+
export function gen(): string
7+
{
8+
return uuidv4();
9+
}
10+
11+
export function test(date: DateTime): Duration
12+
{
13+
return date.diffNow('day');
14+
}
15+
16+
// #endregion
17+
18+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
// #region Exported Functions
5+
6+
export function gen(): string
7+
{
8+
return uuidv4();
9+
}
10+
11+
export function test(date: DateTime): Duration
12+
{
13+
return date.diffNow('day');
14+
}
15+
16+
// #endregion
17+
18+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from 'luxon';
2+
import { v4 as uuidv4 } from 'uuid';
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
export function gen(): string
5+
{
6+
return uuidv4();
7+
}
8+
9+
export function test(date: DateTime): Duration
10+
{
11+
return date.diffNow('day');
12+
}
13+
14+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
// #region Exported Functions (2)
5+
6+
export function gen(): string
7+
{
8+
return uuidv4();
9+
}
10+
11+
export function test(date: DateTime): Duration
12+
{
13+
return date.diffNow('day');
14+
}
15+
16+
// #endregion Exported Functions
17+
18+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { DateTime, type Duration } from "luxon";
2+
import { v4 as uuidv4 } from "uuid";
3+
4+
// #region Exported Functions (2)
5+
6+
export function test(date: DateTime): Duration
7+
{
8+
return date.diffNow('day');
9+
}
10+
11+
export function gen(): string
12+
{
13+
return uuidv4();
14+
}
15+
16+
// #endregion Exported Functions
17+
18+
test(DateTime.fromISO('2016-05-25T09:08:34.123+06:00'));

0 commit comments

Comments
 (0)