Skip to content

Commit a3856c7

Browse files
devversionjelbourn
authored andcommitted
fix(ng-update): do not always use double quotes for generated imports (#16131)
Currently when someone uses single quote import declarations in their project and the `ng update` migration for Material runs, the Material imports are rewritten into multiple imports referring to individual secondary entry-points, but the actual existing quote style is ignored. This means that the linter can sometimes complain after the migration has been performed. Ideally we'd run the tslint fix task after the migration ran in order to fix all lint rule failures which are specific to the given project, but this is not a best-practice yet and we need to decide on the CLI-side whether the CLI should automatically run the fixers after generator/migration schematics ran. See: angular/angular-cli#14532 Unfortunately we can't do the same thing for the whitespace within the `NamedImports` declaration because the whitespace for object literal expressions is hard-coded. See: https://github.com/microsoft/TypeScript/blob/6a559e37ee0d660fcc94f086a34370e79e94b17a/src/compiler/emitter.ts#L3796-L3797 (`emitObjectLiteralExpression` does not allow configuring the format flags) Fixes #14532
1 parent eef132b commit a3856c7

File tree

3 files changed

+27
-12
lines changed

3 files changed

+27
-12
lines changed
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import { c as c2 } from "@angular/material/c";
2-
import { a } from "@angular/material/a";
3-
import { b } from "@angular/material/b";
4-
import { c } from "@angular/material/c";
5-
import { a as a2 } from "@angular/material/a";
1+
import { c as c2 } from '@angular/material/c';
2+
import { a } from '@angular/material/a';
3+
import { b } from '@angular/material/b';
4+
import { c } from '@angular/material/c';
5+
import { a as a2 } from '@angular/material/a';
66

77
// unsorted
8-
import { a } from "@angular/material/a";
9-
import { b } from "@angular/material/b";
10-
import { c } from "@angular/material/c";
8+
import { a } from '@angular/material/a';
9+
import { b } from '@angular/material/b';
10+
import { c } from '@angular/material/c';
1111

12-
import { /* comment */ a as a3 } from "@angular/material/a";
12+
import { /* comment */ a as a3 } from '@angular/material/a';
1313

1414
// primary entry-point export
15-
import { VERSION } from "@angular/material/core";
15+
import { VERSION } from '@angular/material/core';
1616

1717
// type import
1818
import { SomeInterface } from "@angular/material/types";

src/material/schematics/ng-update/test-cases/misc/material-imports_input.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ import {/* comment */ a as a3} from '@angular/material';
1111
import {VERSION} from '@angular/material';
1212

1313
// type import
14-
import {SomeInterface} from '@angular/material';
14+
import {SomeInterface} from "@angular/material";

src/material/schematics/ng-update/upgrade-rules/package-imports-v8/updateAngularMaterialImportsRule.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ function walk(ctx: Lint.WalkContext<boolean>, checker: ts.TypeChecker): void {
8888
return ctx.addFailureAtNode(declaration, Rule.NO_IMPORT_NAMED_SYMBOLS_FAILURE_STR);
8989
}
9090

91+
// Whether the existing import declaration is using a single quote module specifier.
92+
const singleQuoteImport = declaration.moduleSpecifier.getText()[0] === `'`;
93+
9194
// Map which consists of secondary entry-points and import specifiers which are used
9295
// within the current import declaration.
9396
const importMap = new Map<string, ts.ImportSpecifier[]>();
@@ -144,7 +147,7 @@ function walk(ctx: Lint.WalkContext<boolean>, checker: ts.TypeChecker): void {
144147
const newImport = ts.createImportDeclaration(
145148
undefined, undefined,
146149
ts.createImportClause(undefined, ts.createNamedImports(elements)),
147-
ts.createStringLiteral(`${materialModuleSpecifier}/${name}`));
150+
createStringLiteral(`${materialModuleSpecifier}/${name}`, singleQuoteImport));
148151
return printer.printNode(ts.EmitHint.Unspecified, newImport, sf);
149152
})
150153
.join('\n');
@@ -166,6 +169,18 @@ function walk(ctx: Lint.WalkContext<boolean>, checker: ts.TypeChecker): void {
166169
sf.statements.forEach(cb);
167170
}
168171

172+
/**
173+
* Creates a string literal from the specified text.
174+
* @param text Text of the string literal.
175+
* @param singleQuotes Whether single quotes should be used when printing the literal node.
176+
*/
177+
function createStringLiteral(text: string, singleQuotes: boolean): ts.StringLiteral {
178+
const literal = ts.createStringLiteral(text);
179+
// See: https://github.com/microsoft/TypeScript/blob/master/src/compiler/utilities.ts#L584-L590
180+
literal['singleQuote'] = singleQuotes;
181+
return literal;
182+
}
183+
169184
/** Gets the symbol that contains the value declaration of the given node. */
170185
function getDeclarationSymbolOfNode(node: ts.Node, checker: ts.TypeChecker): ts.Symbol|undefined {
171186
const symbol = checker.getSymbolAtLocation(node);

0 commit comments

Comments
 (0)