Type-only import specifiers (#45998)

* Parse type-only import specifiers

* Add type-only export specifiers

* Update transform and emit

* Update checking

* Fix elision when combined with importsNotUsedAsValues=preserve

* Accept baselines

* Add test

* WIP auto imports updates

* First auto-imports test working

* More auto-import tests

* Fix auto imports of type-only exports

* Add test for promoting type-only import

* Sort import/export specifiers by type-onlyness

* Update completions for `import { type |`

* Update other completions tests

* Respect organize imports sorting when promoting type-only to regular while adding a specifier

* Fix comment mistakes

* Update src/services/codefixes/importFixes.ts

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>

* Rearrange some order of assignments in parser

* Split huge if statement

* Remove redundant check

* Update new transformer

* Fix import statement completions

* Fix type keyword completions good grief

* Fix last tests

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Andrew Branch
2021-09-27 12:38:30 -07:00
committed by GitHub
co-authored by Daniel Rosenwasser
parent 26aef89a72
commit e160bc8c0d
80 changed files with 1842 additions and 349 deletions
+62 -13
View File
@@ -7404,27 +7404,76 @@ namespace ts {
let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
let checkIdentifierStart = scanner.getTokenPos();
let checkIdentifierEnd = scanner.getTextPos();
const identifierName = parseIdentifierName();
let isTypeOnly = false;
let propertyName: Identifier | undefined;
let name: Identifier;
if (token() === SyntaxKind.AsKeyword) {
propertyName = identifierName;
parseExpected(SyntaxKind.AsKeyword);
checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
checkIdentifierStart = scanner.getTokenPos();
checkIdentifierEnd = scanner.getTextPos();
name = parseIdentifierName();
let canParseAsKeyword = true;
let name = parseIdentifierName();
if (name.escapedText === "type") {
// If the first token of an import specifier is 'type', there are a lot of possibilities,
// especially if we see 'as' afterwards:
//
// import { type } from "mod"; - isTypeOnly: false, name: type
// import { type as } from "mod"; - isTypeOnly: true, name: as
// import { type as as } from "mod"; - isTypeOnly: false, name: as, propertyName: type
// import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as
if (token() === SyntaxKind.AsKeyword) {
// { type as ...? }
const firstAs = parseIdentifierName();
if (token() === SyntaxKind.AsKeyword) {
// { type as as ...? }
const secondAs = parseIdentifierName();
if (tokenIsIdentifierOrKeyword(token())) {
// { type as as something }
isTypeOnly = true;
propertyName = firstAs;
name = parseNameWithKeywordCheck();
canParseAsKeyword = false;
}
else {
// { type as as }
propertyName = name;
name = secondAs;
canParseAsKeyword = false;
}
}
else if (tokenIsIdentifierOrKeyword(token())) {
// { type as something }
propertyName = name;
canParseAsKeyword = false;
name = parseNameWithKeywordCheck();
}
else {
// { type as }
isTypeOnly = true;
name = firstAs;
}
}
else if (tokenIsIdentifierOrKeyword(token())) {
// { type something ...? }
isTypeOnly = true;
name = parseNameWithKeywordCheck();
}
}
else {
name = identifierName;
if (canParseAsKeyword && token() === SyntaxKind.AsKeyword) {
propertyName = name;
parseExpected(SyntaxKind.AsKeyword);
name = parseNameWithKeywordCheck();
}
if (kind === SyntaxKind.ImportSpecifier && checkIdentifierIsKeyword) {
parseErrorAt(checkIdentifierStart, checkIdentifierEnd, Diagnostics.Identifier_expected);
}
const node = kind === SyntaxKind.ImportSpecifier
? factory.createImportSpecifier(propertyName, name)
: factory.createExportSpecifier(propertyName, name);
? factory.createImportSpecifier(isTypeOnly, propertyName, name)
: factory.createExportSpecifier(isTypeOnly, propertyName, name);
return finishNode(node, pos);
function parseNameWithKeywordCheck() {
checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
checkIdentifierStart = scanner.getTokenPos();
checkIdentifierEnd = scanner.getTextPos();
return parseIdentifierName();
}
}
function parseNamespaceExport(pos: number): NamespaceExport {