Skip to content

Commit 31af37d

Browse files
committed
cleanup
1 parent 155be8c commit 31af37d

8 files changed

Lines changed: 100 additions & 107 deletions

File tree

.github/workflows/test.yml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,8 @@ jobs:
1717
steps:
1818
- name: Checkout code
1919
uses: actions/checkout@v4
20-
21-
- name: Checkout old-typescript-hero submodule at pinned commit
22-
run: |
23-
git submodule update --init --depth 1
24-
cd comparison-test-harness/old-typescript-hero
25-
git fetch --depth 1 origin 2cc666ec1d7d443aba1466a94e6e4c50a1424b83
26-
git checkout 2cc666ec1d7d443aba1466a94e6e4c50a1424b83
20+
with:
21+
submodules: true # Checkout submodule at pinned commit from repo
2722

2823
- name: Setup Node.js
2924
uses: actions/setup-node@v4

CLAUDE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ assert.equal(newResult, expected, 'New extension must produce correct output');
280280

281281
---
282282

283-
## 📝 Configuration (13 Options)
283+
## 📝 Configuration (15 Options)
284284

285285
All settings are under `miniTypescriptHero.imports.*`:
286286

@@ -305,6 +305,10 @@ All settings are under `miniTypescriptHero.imports.*`:
305305
### Blank Lines
306306
13. `blankLinesAfterImports` (one/two/preserve/legacy) - How many blank lines after imports
307307

308+
### Behavior & Compatibility
309+
14. `organizeOnSave` (boolean) - Automatically organize imports when saving files
310+
15. `legacyMode` (boolean) - **INTERNAL!** Replicate old TypeScript Hero bugs exactly (auto-set by migration)
311+
308312
---
309313

310314
## 🐛 Bug Status (Session 18 Update)

comparison-test-harness/debug-parser.ts

Lines changed: 0 additions & 39 deletions
This file was deleted.

comparison-test-harness/debug-test.js

Lines changed: 0 additions & 23 deletions
This file was deleted.

comparison-test-harness/get-actual-output.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

comparison-test-harness/test-cases/06-edge-cases.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,4 +491,35 @@ const result = filter(doubled, x => x > 2).reduce((acc, val) => acc + val, 0);
491491
assert.equal(oldResult, expected, 'Old extension must produce correct output');
492492
assert.equal(newResult, expected, 'New extension must produce correct output');
493493
});
494+
495+
test('123. Comments between imports: Indentation preserved', async () => {
496+
// PROOF: Both extensions preserve comment indentation when moving comments after imports
497+
// OLD EXTENSION: Puts blank line AFTER imports, BEFORE comments, BEFORE code
498+
// NEW EXTENSION: Matches old extension exactly (indentation preserved)
499+
const input = `import { B } from './b';
500+
// This is an indented comment
501+
import { A } from './a';
502+
// This is a more indented comment
503+
import { C } from './c';
504+
505+
console.log(A, B, C);
506+
`;
507+
508+
const oldResult = await organizeImportsOld(input);
509+
const newResult = await organizeImportsNew(input);
510+
511+
// Expected: Imports sorted, comments moved after with indentation preserved
512+
// VERIFIED from REAL old extension output (ran test to capture actual behavior)
513+
const expected = `import { A } from './a';
514+
import { B } from './b';
515+
import { C } from './c';
516+
517+
// This is an indented comment
518+
// This is a more indented comment
519+
console.log(A, B, C);
520+
`;
521+
522+
assert.equal(oldResult, expected, 'Old extension must produce correct output');
523+
assert.equal(newResult, expected, 'New extension must produce correct output');
524+
});
494525
});

src/imports/import-manager.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,31 @@ export class ImportManager {
253253
}
254254
}
255255

256+
/**
257+
* Helper method to remove trailing /index from import library names.
258+
* Creates new Import objects instead of mutating readonly properties.
259+
*/
260+
private removeTrailingIndexFromImports(imports: Import[]): Import[] {
261+
return imports.map(imp => {
262+
if (!imp.libraryName.endsWith('/index')) {
263+
return imp;
264+
}
265+
const newLibraryName = imp.libraryName.replace(/\/index$/, '');
266+
267+
// Create new import object instead of mutating readonly property
268+
if (imp instanceof NamedImport) {
269+
return new NamedImport(newLibraryName, imp.specifiers, imp.defaultAlias);
270+
} else if (imp instanceof NamespaceImport) {
271+
return new NamespaceImport(newLibraryName, imp.alias);
272+
} else if (imp instanceof ExternalModuleImport) {
273+
return new ExternalModuleImport(newLibraryName, imp.alias);
274+
} else if (imp instanceof StringImport) {
275+
return new StringImport(newLibraryName);
276+
}
277+
return imp;
278+
});
279+
}
280+
256281
/**
257282
* Organize imports: remove unused, sort, and group.
258283
* Returns TextEdits to apply the changes.
@@ -351,9 +376,7 @@ export class ImportManager {
351376
// In modern mode, /index removal happens first so imports like './lib/index' and './lib' can merge
352377
// In legacy mode, we do it after merging to replicate the old extension's bug
353378
if (this.config.removeTrailingIndex(this.document.uri) && !this.config.legacyMode(this.document.uri)) {
354-
for (const imp of keep.filter(lib => lib.libraryName.endsWith('/index'))) {
355-
imp.libraryName = imp.libraryName.replace(/\/index$/, '');
356-
}
379+
keep = this.removeTrailingIndexFromImports(keep);
357380
}
358381

359382
// Merge imports from same module (configurable)
@@ -448,9 +471,7 @@ export class ImportManager {
448471
// In legacy mode, we replicate the old extension's bug where /index removal happens after merging
449472
// This means './lib/index' and './lib' won't merge because they're different at merge time
450473
if (this.config.removeTrailingIndex(this.document.uri) && this.config.legacyMode(this.document.uri)) {
451-
for (const imp of keep.filter(lib => lib.libraryName.endsWith('/index'))) {
452-
imp.libraryName = imp.libraryName.replace(/\/index$/, '');
453-
}
474+
keep = this.removeTrailingIndexFromImports(keep);
454475
}
455476

456477
// Group imports
@@ -517,10 +538,12 @@ export class ImportManager {
517538
// Extract comments between imports (old TypeScript Hero moves them after imports)
518539
const commentsBetweenImports: string[] = [];
519540
for (let i = importSectionStartLine; i <= importSectionEndLine; i++) {
520-
const lineText = this.document.lineAt(i).text.trim();
541+
const lineText = this.document.lineAt(i).text;
542+
const trimmedText = lineText.trim();
521543
// Check if line is a comment (and not an import statement)
522-
if ((lineText.startsWith('//') || lineText.startsWith('/*') || lineText.startsWith('*')) &&
523-
!lineText.includes('import ')) {
544+
// Check the trimmed version but preserve the original with indentation
545+
if ((trimmedText.startsWith('//') || trimmedText.startsWith('/*') || trimmedText.startsWith('*')) &&
546+
!trimmedText.includes('import ')) {
524547
commentsBetweenImports.push(lineText);
525548
}
526549
}

src/test/import-manager.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,4 +2880,34 @@ const r = React;
28802880
await deleteTempDocument(doc);
28812881
}
28822882
});
2883+
2884+
test('90. Comments between imports: Indentation preserved', async () => {
2885+
// CRITICAL: Comments between imports should preserve their indentation
2886+
// Scenario: Comments with 2-space indentation between imports
2887+
// Expected: Comments moved after imports WITH their original indentation
2888+
2889+
const content = `import { B } from './b';
2890+
// This is an indented comment
2891+
import { A } from './a';
2892+
2893+
console.log(A, B);
2894+
`;
2895+
2896+
const doc = await createTempDocument(content);
2897+
try {
2898+
const manager = new ImportManager(doc, config);
2899+
const edits = manager.organizeImports();
2900+
const result = await applyEditsToDocument(doc, edits);
2901+
2902+
// Verify imports are organized
2903+
assert.ok(result.includes("import { A } from './a';"), 'Should have import A');
2904+
assert.ok(result.includes("import { B } from './b';"), 'Should have import B');
2905+
2906+
// CRITICAL: Verify comments preserve their indentation
2907+
assert.ok(result.includes(' // This is an indented comment'),
2908+
'Two-space indented comment should preserve indentation');
2909+
} finally {
2910+
await deleteTempDocument(doc);
2911+
}
2912+
});
28832913
});

0 commit comments

Comments
 (0)