Skip to content

Commit e9820a6

Browse files
crisbetokirjs
authored andcommitted
fix(migrations): avoid trailing whitespaces in unused imports migration (#61698)
Follow-up to #61674 where we were leaving behind some whitespace, e.g. `[One, Two, Three]` would turn into `[One ]`. These changes only preserve the whitespace if the node is preceded by a newline. This wasn't caught by tests, because they were stripping away whitespaces before asserting. I've also reworked the tests to be sensitive to formatting changes. PR Close #61698
1 parent 517ad23 commit e9820a6

File tree

2 files changed

+55
-117
lines changed

2 files changed

+55
-117
lines changed

packages/core/schematics/ng-generate/cleanup-unused-imports/unused_imports_migration.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,15 +364,18 @@ function getArrayElementRemovalUpdate(
364364
// trailing comma at the end of the line is fine.
365365
if (parent.elements[parent.elements.length - 1] === node) {
366366
for (let i = position - 1; i >= 0; i--) {
367-
if (sourceText[i] === ',' || sourceText[i] === ' ') {
367+
const char = sourceText[i];
368+
if (char === ',' || char === ' ') {
368369
position--;
369370
} else {
371+
if (whitespaceOrLineFeed.test(char)) {
372+
// Replace the node with its leading whitespace to preserve the formatting.
373+
// This only needs to happen if we're breaking on a newline.
374+
toInsert = getLeadingLineWhitespaceOfNode(node);
375+
}
370376
break;
371377
}
372378
}
373-
374-
// Replace the node with its leading whitespace to preserve the formatting.
375-
toInsert = getLeadingLineWhitespaceOfNode(node);
376379
}
377380

378381
return new TextUpdate({position, end, toInsert});

packages/core/schematics/test/cleanup_unused_imports_migration_spec.ts

Lines changed: 48 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ describe('cleanup unused imports schematic', () => {
2929
return runner.runSchematic('cleanup-unused-imports', {}, tree);
3030
}
3131

32-
function stripWhitespace(content: string) {
33-
return content.replace(/\s+/g, '');
34-
}
35-
3632
beforeEach(() => {
3733
runner = new SchematicTestRunner('test', runfiles.resolvePackageRelative('../collection.json'));
3834
host = new TempScopedNodeJsSyncHost();
@@ -95,19 +91,10 @@ describe('cleanup unused imports schematic', () => {
9591

9692
await runMigration();
9793

94+
const contents = tree.readContent('comp.ts');
9895
expect(logs.pop()).toBe('Removed 2 imports in 1 file');
99-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
100-
stripWhitespace(`
101-
import {Component} from '@angular/core';
102-
import {One} from './directives';
103-
104-
@Component({
105-
imports: [One],
106-
template: '<div one></div>',
107-
})
108-
export class Comp {}
109-
`),
110-
);
96+
expect(contents).toContain('imports: [One],');
97+
expect(contents).toContain(`import {One} from './directives';`);
11198
});
11299

113100
it('should clean up an array where all imports are not used', async () => {
@@ -127,18 +114,10 @@ describe('cleanup unused imports schematic', () => {
127114

128115
await runMigration();
129116

117+
const contents = tree.readContent('comp.ts');
130118
expect(logs.pop()).toBe('Removed 3 imports in 1 file');
131-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
132-
stripWhitespace(`
133-
import {Component} from '@angular/core';
134-
135-
@Component({
136-
imports: [],
137-
template: '',
138-
})
139-
export class Comp {}
140-
`),
141-
);
119+
expect(contents).toContain('imports: [],');
120+
expect(contents).not.toContain(`from './directives'`);
142121
});
143122

144123
it('should clean up an array where aliased imports are not used', async () => {
@@ -157,20 +136,11 @@ describe('cleanup unused imports schematic', () => {
157136
);
158137

159138
await runMigration();
139+
const contents = tree.readContent('comp.ts');
160140

161141
expect(logs.pop()).toBe('Removed 2 imports in 1 file');
162-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
163-
stripWhitespace(`
164-
import {Component} from '@angular/core';
165-
import {One as OneAlias} from './directives';
166-
167-
@Component({
168-
imports: [OneAlias],
169-
template: '<div one></div>',
170-
})
171-
export class Comp {}
172-
`),
173-
);
142+
expect(contents).toContain('imports: [OneAlias],');
143+
expect(contents).toContain(`import {One as OneAlias} from './directives';`);
174144
});
175145

176146
it('should preserve import declaration if unused import is still used within the file', async () => {
@@ -195,26 +165,11 @@ describe('cleanup unused imports schematic', () => {
195165
);
196166

197167
await runMigration();
168+
const contents = tree.readContent('comp.ts');
198169

199170
expect(logs.pop()).toBe('Removed 1 import in 1 file');
200-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
201-
stripWhitespace(`
202-
import {Component} from '@angular/core';
203-
import {One} from './directives';
204-
205-
@Component({
206-
imports: [],
207-
template: '',
208-
})
209-
export class Comp {}
210-
211-
@Component({
212-
imports: [One],
213-
template: '<div one></div>',
214-
})
215-
export class OtherComp {}
216-
`),
217-
);
171+
expect(contents).toContain('imports: [],');
172+
expect(contents).toContain(`import {One} from './directives';`);
218173
});
219174

220175
it('should not touch a file where all imports are used', async () => {
@@ -282,62 +237,51 @@ describe('cleanup unused imports schematic', () => {
282237
);
283238

284239
await runMigration();
240+
const contents = tree.readContent('comp.ts');
285241

286242
expect(logs.pop()).toBe('Removed 2 imports in 1 file');
287-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
288-
stripWhitespace(`
289-
import {Component} from '@angular/core';
290-
import {One} from './directives';
291-
292-
@Component({
293-
imports: [One],
294-
template: '<div one></div>',
295-
})
296-
export class Comp {}
297-
`),
298-
);
243+
expect(contents).toContain('imports: [One],');
244+
expect(contents).toContain(`import {One} from './directives';`);
299245
});
300246

301247
it('should preserve comments when removing unused imports', async () => {
302248
writeFile(
303249
'comp.ts',
304-
`
305-
import {Component} from '@angular/core';
306-
import {One, Two, Three} from './directives';
307-
308-
@Component({
309-
imports: [
310-
// Start
311-
Three,
312-
One,
313-
Two,
314-
// End
315-
],
316-
template: '<div one></div>',
317-
})
318-
export class Comp {}
319-
`,
250+
[
251+
`import {Component} from '@angular/core';`,
252+
`import {One, Two, Three} from './directives';`,
253+
``,
254+
`@Component({`,
255+
` imports: [`,
256+
` // Start`,
257+
` Three,`,
258+
` One,`,
259+
` Two,`,
260+
` // End`,
261+
` ],`,
262+
` template: '<div one></div>',`,
263+
`})`,
264+
`export class Comp {}`,
265+
].join('\n'),
320266
);
321267

322268
await runMigration();
323269

324270
expect(logs.pop()).toBe('Removed 2 imports in 1 file');
325-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
326-
stripWhitespace(`
327-
import {Component} from '@angular/core';
328-
import {One} from './directives';
329-
330-
@Component({
331-
imports: [
332-
// Start
333-
One,
334-
// End
335-
],
336-
template: '<div one></div>',
337-
})
338-
export class Comp {}
339-
`),
340-
);
271+
expect(tree.readContent('comp.ts').split('\n')).toEqual([
272+
`import {Component} from '@angular/core';`,
273+
`import {One} from './directives';`,
274+
``,
275+
`@Component({`,
276+
` imports: [`,
277+
` // Start`,
278+
` One,`,
279+
` // End`,
280+
` ],`,
281+
` template: '<div one></div>',`,
282+
`})`,
283+
`export class Comp {}`,
284+
]);
341285
});
342286

343287
it('should preserve inline comments and strip trailing comma when removing imports from same line', async () => {
@@ -356,19 +300,10 @@ describe('cleanup unused imports schematic', () => {
356300
);
357301

358302
await runMigration();
303+
const contents = tree.readContent('comp.ts');
359304

360305
expect(logs.pop()).toBe('Removed 2 imports in 1 file');
361-
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(
362-
stripWhitespace(`
363-
import {Component} from '@angular/core';
364-
import {One} from './directives';
365-
366-
@Component({
367-
imports: [/* Start */ One /* End */],
368-
template: '<div one></div>',
369-
})
370-
export class Comp {}
371-
`),
372-
);
306+
expect(contents).toContain(' imports: [/* Start */ One/* End */],');
307+
expect(contents).toContain(`import {One} from './directives';`);
373308
});
374309
});

0 commit comments

Comments
 (0)