@@ -2027,13 +2027,37 @@ namespace ts {
20272027 || kind === SyntaxKind . ExportAssignment ;
20282028 }
20292029
2030- export const syntaxMayBeASICandidate = or (
2031- syntaxRequiresTrailingCommaOrSemicolonOrASI ,
2032- syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI ,
2033- syntaxRequiresTrailingModuleBlockOrSemicolonOrASI ,
2034- syntaxRequiresTrailingSemicolonOrASI ) ;
2030+ function isMappedTypeNodeType ( node : Node ) {
2031+ return node . parent && isMappedTypeNode ( node . parent ) && node . parent . type === node ;
2032+ }
2033+
2034+ export function nodeMayBeASICandidate ( node : Node ) {
2035+ if ( syntaxRequiresTrailingCommaOrSemicolonOrASI ( node . kind ) ||
2036+ syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI ( node . kind ) ||
2037+ syntaxRequiresTrailingModuleBlockOrSemicolonOrASI ( node . kind ) ||
2038+ syntaxRequiresTrailingSemicolonOrASI ( node . kind ) ) {
2039+ return true ;
2040+ }
2041+ return isMappedTypeNodeType ( node ) ;
2042+ }
20352043
2036- export function isASICandidate ( node : Node , sourceFile : SourceFileLike ) : boolean {
2044+ export function nodeAllowsUnconventionalTrailingSemicolon ( node : Node , contextNode : Node , nextTokenKind : SyntaxKind | undefined , sourceFile : SourceFileLike ) {
2045+ if ( isMappedTypeNodeType ( node ) ) {
2046+ return rangeIsOnSingleLine ( contextNode , sourceFile as SourceFile ) ;
2047+ }
2048+ if ( syntaxRequiresTrailingCommaOrSemicolonOrASI ( node . kind ) ) {
2049+ return nextTokenKind === SyntaxKind . CloseBraceToken && rangeIsOnSingleLine ( contextNode , sourceFile as SourceFile ) ;
2050+ }
2051+ return false ;
2052+ }
2053+
2054+ /**
2055+ * @param strict Return true for positions that allow semicolons but conventionally
2056+ * drop them, even in code that largely contains semicolons. Examples include the last
2057+ * declaration inside the curly braces of single-line object type literals and mapped types,
2058+ * e.g. `type X = { x: string; }` and `type X<T> = { [K in keyof T]: T[K]; }`.
2059+ */
2060+ export function isASICandidate ( node : Node , sourceFile : SourceFileLike , strict ?: boolean ) : boolean {
20372061 const lastToken = node . getLastToken ( sourceFile ) ;
20382062 if ( lastToken && lastToken . kind === SyntaxKind . SemicolonToken ) {
20392063 return false ;
@@ -2060,20 +2084,28 @@ namespace ts {
20602084 return false ;
20612085 }
20622086
2087+ let nextToken = getNextToken ( ) ;
2088+ const contextNode = findAncestor ( node , or ( isObjectTypeDeclaration , isMappedTypeNode ) ) ;
2089+ if ( contextNode && nodeAllowsUnconventionalTrailingSemicolon ( node , contextNode , nextToken && nextToken . kind , sourceFile ) ) {
2090+ return ! ! strict ;
2091+ }
2092+
20632093 // See comment in parser’s `parseDoStatement`
20642094 if ( node . kind === SyntaxKind . DoStatement ) {
20652095 return true ;
20662096 }
20672097
2068- const topNode = findAncestor ( node , ancestor => ! ancestor . parent ) ! ;
2069- const nextToken = findNextToken ( node , topNode , sourceFile ) ;
20702098 if ( ! nextToken || nextToken . kind === SyntaxKind . CloseBraceToken ) {
20712099 return true ;
20722100 }
20732101
2074- const startLine = sourceFile . getLineAndCharacterOfPosition ( node . getEnd ( ) ) . line ;
2075- const endLine = sourceFile . getLineAndCharacterOfPosition ( nextToken . getStart ( sourceFile ) ) . line ;
2076- return startLine !== endLine ;
2102+ return ! positionsAreOnSameLine ( node . getEnd ( ) ,
2103+ nextToken . getStart ( sourceFile ) ,
2104+ sourceFile as SourceFile ) ;
2105+
2106+ function getNextToken ( ) : Node | undefined {
2107+ return nextToken || ( nextToken = findNextToken ( node , findAncestor ( node , ancestor => ! ancestor . parent ) ! , sourceFile ) ) ;
2108+ }
20772109 }
20782110
20792111 export function probablyUsesSemicolons ( sourceFile : SourceFile ) : boolean {
0 commit comments