Skip to content

Commit ebad5d0

Browse files
committed
fix: use case-insensitive specifier sorting (match typescript-parser)
The old TypeScript Hero source code uses stringSort (ASCII comparison) for specifiers, but the typescript-parser code generator overrides this with case-insensitive sorting using toLowerCase(). This commit fixes specifierSort to use toLowerCase() + stringSort, matching what users actually see with the old extension. Updated unit tests accordingly. All 389 unit tests and 203 comparison tests passing.
1 parent 0ed1910 commit ebad5d0

3 files changed

Lines changed: 25 additions & 20 deletions

File tree

src/imports/import-utilities.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,18 @@ function getImportFirstSpecifier(imp: Import): string {
7676
}
7777

7878
/**
79-
* Order specifiers by name.
80-
* Uses stringSort (not localeStringSort) to match old TypeScript Hero behavior.
79+
* Order specifiers by name using case-insensitive comparison.
80+
*
81+
* NOTE: Old TypeScript Hero extension's source code uses stringSort (ASCII),
82+
* but the typescript-parser code generator (namedImport.js line 19-28) overrides
83+
* this with case-insensitive sorting using toLowerCase(). Since we don't use
84+
* typescript-parser, we need to match what users actually see (case-insensitive).
8185
*/
8286
export function specifierSort(i1: SymbolSpecifier, i2: SymbolSpecifier): number {
83-
return stringSort(i1.specifier, i2.specifier);
87+
// Match typescript-parser's case-insensitive sort behavior
88+
const strA = i1.specifier.toLowerCase();
89+
const strB = i2.specifier.toLowerCase();
90+
return stringSort(strA, strB);
8491
}
8592

8693
/**

tests/unit/import-manager.edge-cases-audit.test.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -301,17 +301,16 @@ const x = A + Z;
301301
// B6: Unicode and case ordering
302302
// ============================================================================
303303

304-
test('B6: Unicode and mixed case specifiers (ASCII order)', async () => {
305-
// Old TypeScript Hero uses stringSort (ASCII comparison) for specifiers.
306-
// ASCII order for these characters: A, B, a, b, Ä, ä, Ω, α, β
307-
// (Uppercase ASCII before lowercase ASCII, then extended chars by code point)
304+
test('B6: Unicode and mixed case specifiers (case-insensitive)', async () => {
305+
// typescript-parser uses toLowerCase() for case-insensitive sorting.
306+
// Order: A/a, B/b, Ä/ä, α, β, Ω (grouped by lowercase, then by original case within ties)
308307
const content = `import { Ω, α, β, A, a, B, b, Ä, ä } from 'lib';
309308
310309
const x = A + a + B + b + Ä + ä + α + β + Ω;
311310
`;
312311

313-
// ASCII sort order: A, B, a, b, Ä, ä, Ω, α, β
314-
const expected = `import { A, B, a, b, Ä, ä, Ω, α, β } from 'lib';
312+
// Case-insensitive sort (toLowerCase + stringSort): A, a, B, b, Ä, ä, α, β, Ω
313+
const expected = `import { A, a, B, b, Ä, ä, α, β, Ω } from 'lib';
315314
316315
const x = A + a + B + b + Ä + ä + α + β + Ω;
317316
`;
@@ -324,7 +323,7 @@ const x = A + a + B + b + Ä + ä + α + β + Ω;
324323
await applyEditsToDocument(doc, edits);
325324

326325
const result = doc.getText();
327-
assert.strictEqual(result, expected, 'Specifiers must be sorted using ASCII order (matching old extension)');
326+
assert.strictEqual(result, expected, 'Specifiers must be sorted case-insensitively (matching typescript-parser)');
328327
} finally {
329328
await deleteTempDocument(doc);
330329
}

tests/unit/import-manager.test.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -445,12 +445,11 @@ const y = map;
445445
}
446446
});
447447

448-
test('12a. Sort specifiers using ASCII order (matching old TypeScript Hero)', async () => {
449-
// Old TypeScript Hero uses stringSort (ASCII comparison) for specifiers.
450-
// This means uppercase letters come before lowercase (A-Z before a-z).
451-
// ASCII order: Component, OnInit, inject (capitals first)
452-
// localeCompare order: Component, inject, OnInit (case-insensitive)
453-
// We match old extension behavior: ASCII order.
448+
test('12a. Sort specifiers case-insensitively (Component, inject, OnInit)', async () => {
449+
// typescript-parser code generator uses case-insensitive sorting (toLowerCase)
450+
// even though old TypeScript Hero source uses stringSort (ASCII).
451+
// We match what users actually see: case-insensitive alphabetical order.
452+
// Case-insensitive order: Component, inject, OnInit (natural alphabetical)
454453
const content = `import { OnInit, Component, inject } from '@angular/core';
455454
456455
const x = Component;
@@ -471,10 +470,10 @@ const z: OnInit = {};
471470
assert.ok(match, 'Should have named imports');
472471
const specifiers = match![1].split(',').map(s => s.trim());
473472

474-
// Verify ASCII order: Component, OnInit, inject (uppercase before lowercase)
475-
// This matches old TypeScript Hero's stringSort behavior
476-
assert.deepStrictEqual(specifiers, ['Component', 'OnInit', 'inject'],
477-
'Specifiers should be sorted using ASCII order (Component, OnInit, inject) to match old extension');
473+
// Verify case-insensitive order: Component, inject, OnInit
474+
// This matches what typescript-parser's code generator produces
475+
assert.deepStrictEqual(specifiers, ['Component', 'inject', 'OnInit'],
476+
'Specifiers should be sorted case-insensitively (Component, inject, OnInit)');
478477
} finally {
479478
await deleteTempDocument(doc);
480479
}

0 commit comments

Comments
 (0)