Skip to content

Commit 0fad36e

Browse files
thePunderWomanAndrewKushnir
authored andcommitted
fix(migrations): tweaks to formatting in control flow migration (#53058)
This addresses a few minor formatting issues with the control flow migration. fixes: #53017 PR Close #53058
1 parent 291deac commit 0fad36e

File tree

7 files changed

+136
-33
lines changed

7 files changed

+136
-33
lines changed

packages/core/schematics/ng-generate/control-flow-migration/cases.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ const cases = [
2929
* Replaces structural directive ngSwitch instances with new switch.
3030
* Returns null if the migration failed (e.g. there was a syntax error).
3131
*/
32-
export function migrateCase(template: string): {migrated: string, errors: MigrateError[]} {
32+
export function migrateCase(template: string):
33+
{migrated: string, errors: MigrateError[], changed: boolean} {
3334
let errors: MigrateError[] = [];
3435
let parsed = parseTemplate(template);
3536
if (parsed === null) {
36-
return {migrated: template, errors};
37+
return {migrated: template, errors, changed: false};
3738
}
3839

3940
let result = template;
@@ -73,7 +74,9 @@ export function migrateCase(template: string): {migrated: string, errors: Migrat
7374
nestLevel = el.nestCount;
7475
}
7576

76-
return {migrated: result, errors};
77+
const changed = visitor.elements.length > 0;
78+
79+
return {migrated: result, errors, changed};
7780
}
7881

7982
function migrateNgSwitchCase(etm: ElementToMigrate, tmpl: string, offset: number): Result {

packages/core/schematics/ng-generate/control-flow-migration/fors.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ export const stringPairs = new Map([
3232
* Replaces structural directive ngFor instances with new for.
3333
* Returns null if the migration failed (e.g. there was a syntax error).
3434
*/
35-
export function migrateFor(template: string): {migrated: string, errors: MigrateError[]} {
35+
export function migrateFor(template: string):
36+
{migrated: string, errors: MigrateError[], changed: boolean} {
3637
let errors: MigrateError[] = [];
3738
let parsed = parseTemplate(template);
3839
if (parsed === null) {
39-
return {migrated: template, errors};
40+
return {migrated: template, errors, changed: false};
4041
}
4142

4243
let result = template;
@@ -68,7 +69,9 @@ export function migrateFor(template: string): {migrated: string, errors: Migrate
6869
nestLevel = el.nestCount;
6970
}
7071

71-
return {migrated: result, errors};
72+
const changed = visitor.elements.length > 0;
73+
74+
return {migrated: result, errors, changed};
7275
}
7376

7477
function migrateNgFor(etm: ElementToMigrate, tmpl: string, offset: number): Result {

packages/core/schematics/ng-generate/control-flow-migration/ifs.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ const ifs = [
2525
* Replaces structural directive ngif instances with new if.
2626
* Returns null if the migration failed (e.g. there was a syntax error).
2727
*/
28-
export function migrateIf(template: string): {migrated: string, errors: MigrateError[]} {
28+
export function migrateIf(template: string):
29+
{migrated: string, errors: MigrateError[], changed: boolean} {
2930
let errors: MigrateError[] = [];
3031
let parsed = parseTemplate(template);
3132
if (parsed === null) {
32-
return {migrated: template, errors};
33+
return {migrated: template, errors, changed: false};
3334
}
3435

3536
let result = template;
@@ -61,7 +62,9 @@ export function migrateIf(template: string): {migrated: string, errors: MigrateE
6162
nestLevel = el.nestCount;
6263
}
6364

64-
return {migrated: result, errors};
65+
const changed = visitor.elements.length > 0;
66+
67+
return {migrated: result, errors, changed};
6568
}
6669

6770
function migrateNgIf(etm: ElementToMigrate, tmpl: string, offset: number): Result {

packages/core/schematics/ng-generate/control-flow-migration/migration.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export function migrateTemplate(
2929
const switchResult = migrateSwitch(forResult.migrated);
3030
const caseResult = migrateCase(switchResult.migrated);
3131
migrated = processNgTemplates(caseResult.migrated);
32-
if (format) {
32+
const changed =
33+
ifResult.changed || forResult.changed || switchResult.changed || caseResult.changed;
34+
if (format && changed) {
3335
migrated = formatTemplate(migrated);
3436
}
3537
file.removeCommonModule = canRemoveCommonModule(template);

packages/core/schematics/ng-generate/control-flow-migration/switches.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ const switches = [
2121
* Replaces structural directive ngSwitch instances with new switch.
2222
* Returns null if the migration failed (e.g. there was a syntax error).
2323
*/
24-
export function migrateSwitch(template: string): {migrated: string, errors: MigrateError[]} {
24+
export function migrateSwitch(template: string):
25+
{migrated: string, errors: MigrateError[], changed: boolean} {
2526
let errors: MigrateError[] = [];
2627
let parsed = parseTemplate(template);
2728
if (parsed === null) {
28-
return {migrated: template, errors};
29+
return {migrated: template, errors, changed: false};
2930
}
3031

3132
let result = template;
@@ -59,7 +60,9 @@ export function migrateSwitch(template: string): {migrated: string, errors: Migr
5960
nestLevel = el.nestCount;
6061
}
6162

62-
return {migrated: result, errors};
63+
const changed = visitor.elements.length > 0;
64+
65+
return {migrated: result, errors, changed};
6366
}
6467

6568
function migrateNgSwitch(etm: ElementToMigrate, tmpl: string, offset: number): Result {

packages/core/schematics/ng-generate/control-flow-migration/util.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,30 @@ export function analyze(sourceFile: ts.SourceFile, analyzedFiles: Map<string, An
3333
});
3434
}
3535

36+
function checkIfShouldChange(decl: ts.ImportDeclaration, removeCommonModule: boolean) {
37+
// should change if you can remove the common module
38+
// if it's not safe to remove the common module
39+
// and that's the only thing there, we should do nothing.
40+
const clause = decl.getChildAt(1) as ts.ImportClause;
41+
return !(
42+
!removeCommonModule && clause.namedBindings && ts.isNamedImports(clause.namedBindings) &&
43+
clause.namedBindings.elements.length === 1 &&
44+
clause.namedBindings.elements[0].getText() === 'CommonModule');
45+
}
46+
3647
function updateImportDeclaration(decl: ts.ImportDeclaration, removeCommonModule: boolean): string {
3748
const clause = decl.getChildAt(1) as ts.ImportClause;
3849
const updatedClause = updateImportClause(clause, removeCommonModule);
3950
if (updatedClause === null) {
4051
return '';
4152
}
42-
const printer = ts.createPrinter();
53+
// removeComments is set to true to prevent duplication of comments
54+
// when the import declaration is at the top of the file, but right after a comment
55+
// without this, the comment gets duplicated when the declaration is updated.
56+
// the typescript AST includes that preceding comment as part of the import declaration full text.
57+
const printer = ts.createPrinter({
58+
removeComments: true,
59+
});
4360
const updated = ts.factory.updateImportDeclaration(
4461
decl, decl.modifiers, updatedClause, decl.moduleSpecifier, undefined);
4562
return printer.printNode(ts.EmitHint.Unspecified, updated, clause.getSourceFile());
@@ -326,7 +343,7 @@ export function removeImports(
326343
template: string, node: ts.Node, removeCommonModule: boolean): string {
327344
if (template.startsWith('imports') && ts.isPropertyAssignment(node)) {
328345
return updateClassImports(node, removeCommonModule);
329-
} else if (ts.isImportDeclaration(node)) {
346+
} else if (ts.isImportDeclaration(node) && checkIfShouldChange(node, removeCommonModule)) {
330347
return updateImportDeclaration(node, removeCommonModule);
331348
}
332349
return template;
@@ -423,29 +440,29 @@ export function formatTemplate(tmpl: string): string {
423440

424441
// regex for matching an html element opening
425442
// <div thing="stuff" [binding]="true"> || <div thing="stuff" [binding]="true"
426-
const openElRegex = /^\s*<([a-z]+)(?![^>]*\/>)[^>]*>?/;
443+
const openElRegex = /^\s*<([a-z0-9]+)(?![^>]*\/>)[^>]*>?/;
427444

428445
// match closing block or else block
429446
// } | } @else
430447
const closeBlockRegex = /^\s*\}\s*$|^\s*\}\s\@else/;
431448

432449
// matches closing of an html element
433450
// </element>
434-
const closeElRegex = /\s*<\/([a-z\-]+)*>/;
451+
const closeElRegex = /\s*<\/([a-z0-9\-]+)*>/;
435452

436453
// matches closing of a self closing html element when the element is on multiple lines
437454
// [binding]="value" />
438455
const closeMultiLineElRegex = /^\s*([a-z0-9\-\[\]]+)?=?"?([^<]+)?"?\s?\/>$/;
439456

440457
// matches an open and close of an html element on a single line with no breaks
441458
// <div>blah</div>
442-
const singleLineElRegex = /^\s*<([a-z]+)(?![^>]*\/>)[^>]*>.*<\/([a-z\-]+)*>/;
459+
const singleLineElRegex = /^\s*<([a-z0-9]+)(?![^>]*\/>)[^>]*>.*<\/([a-z0-9\-]+)*>/;
443460
const lines = tmpl.split('\n');
444461
const formatted = [];
445462
let indent = '';
446-
for (let line of lines) {
447-
if (line.trim() === '') {
448-
// skip blank lines
463+
for (let [index, line] of lines.entries()) {
464+
if (line.trim() === '' && index !== 0 && index !== lines.length - 1) {
465+
// skip blank lines except if it's the first line or last line
449466
continue;
450467
}
451468
if ((closeBlockRegex.test(line) ||

0 commit comments

Comments
 (0)