struct JsModuleReference {
bool FormattingOff = false;
bool IsExport = false;
+ bool IsTypeOnly = false;
// Module references are sorted into these categories, in order.
enum ReferenceCategory {
SIDE_EFFECT, // "import 'something';"
if (Reference->Category == JsModuleReference::SIDE_EFFECT ||
PreviousReference->Category == JsModuleReference::SIDE_EFFECT ||
Reference->IsExport != PreviousReference->IsExport ||
+ Reference->IsTypeOnly != PreviousReference->IsTypeOnly ||
!PreviousReference->Prefix.empty() || !Reference->Prefix.empty() ||
!PreviousReference->DefaultImport.empty() ||
!Reference->DefaultImport.empty() || Reference->Symbols.empty() ||
bool parseStarBinding(const AdditionalKeywords &Keywords,
JsModuleReference &Reference) {
// * as prefix from '...';
+ if (Current->is(Keywords.kw_type) && Current->Next &&
+ Current->Next->is(tok::star)) {
+ Reference.IsTypeOnly = true;
+ nextToken();
+ }
if (Current->isNot(tok::star))
return false;
nextToken();
bool parseNamedBindings(const AdditionalKeywords &Keywords,
JsModuleReference &Reference) {
+ if (Current->is(Keywords.kw_type) && Current->Next &&
+ Current->Next->isOneOf(tok::identifier, tok::l_brace)) {
+ Reference.IsTypeOnly = true;
+ nextToken();
+ }
+
// eat a potential "import X, " prefix.
if (Current->is(tok::identifier)) {
Reference.DefaultImport = Current->TokenText;
nextToken();
if (Current->is(tok::r_brace))
break;
- if (!Current->isOneOf(tok::identifier, tok::kw_default))
+ bool isTypeOnly =
+ Current->is(Keywords.kw_type) && Current->Next &&
+ Current->Next->isOneOf(tok::identifier, tok::kw_default);
+ if (!isTypeOnly && !Current->isOneOf(tok::identifier, tok::kw_default))
return false;
JsImportedSymbol Symbol;
- Symbol.Symbol = Current->TokenText;
// Make sure to include any preceding comments.
Symbol.Range.setBegin(
Current->getPreviousNonComment()->Next->WhitespaceRange.getBegin());
+ if (isTypeOnly)
+ nextToken();
+ Symbol.Symbol = Current->TokenText;
nextToken();
if (Current->is(Keywords.kw_as)) {
// parsing the structural element, i.e. the declaration or expression for
// `export default`.
if (!IsImport && !FormatTok->isOneOf(tok::l_brace, tok::star) &&
- !FormatTok->isStringLiteral()) {
+ !FormatTok->isStringLiteral() &&
+ !(FormatTok->is(Keywords.kw_type) &&
+ Tokens->peekNextToken()->isOneOf(tok::l_brace, tok::star))) {
return;
}
" export class Y {}");
}
+TEST_F(FormatTestJS, ImportExportType) {
+ verifyFormat("import type {x, y} from 'y';\n"
+ "import type * as x from 'y';\n"
+ "import type x from 'y';\n"
+ "import {x, type yu, z} from 'y';\n");
+ verifyFormat("export type {x, y} from 'y';\n"
+ "export {x, type yu, z} from 'y';\n"
+ "export type {x, y};\n"
+ "export {x, type yu, z};\n");
+}
+
TEST_F(FormatTestJS, ClosureStyleCasts) {
verifyFormat("var x = /** @type {foo} */ (bar);");
}
"console.log(Z);\n");
}
+TEST_F(SortImportsTestJS, ImportExportType) {
+ verifySort("import type {sym} from 'a';\n"
+ "import {type sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "import type sym from 'd';\n"
+ "import type * as sym from 'e';\n"
+ "\n"
+ "let x = 1;",
+ "import {sym} from 'c';\n"
+ "import type {sym} from 'a';\n"
+ "import type * as sym from 'e';\n"
+ "import type sym from 'd';\n"
+ "import {type sym} from 'b';\n"
+ "let x = 1;");
+
+ // Symbols within import statement
+ verifySort("import {type sym1, type sym2 as a, sym3} from 'b';\n",
+ "import {type sym2 as a, type sym1, sym3} from 'b';\n");
+
+ // Merging
+ verifySort("import {X, type Z} from 'a';\n"
+ "import type {Y} from 'a';\n"
+ "\n"
+ "X + Y + Z;\n",
+ "import {X} from 'a';\n"
+ "import {type Z} from 'a';\n"
+ "import type {Y} from 'a';\n"
+ "\n"
+ "X + Y + Z;\n");
+
+ // Merging: empty imports
+ verifySort("import type {A} from 'foo';\n", "import type {} from 'foo';\n"
+ "import type {A} from 'foo';");
+
+ // Merging: exports
+ verifySort("export {A, type B} from 'foo';\n",
+ "export {A} from 'foo';\n"
+ "export {type B} from 'foo';");
+}
+
} // end namespace
} // end namespace format
} // end namespace clang