import ts from 'typescript' ; import MagicString from 'magic-string' ; import { isParenthesizedObjectOrArrayLiteralExpression , getNamesFromLabeledStatement } from '../utils/tsAst' ; import { overwriteStr , preprendStr } from '../../utils/magic-string' ; export class ImplicitTopLevelNames { private map = new Set < ts . LabeledStatement > ( ) ; constructor ( private str , private astOffset ) { } add ( node ) { this . map . add ( node ) ; } handleReactiveStatement ( node , binaryExpression ) { if ( binaryExpression ) { this . wrapExpressionWithInvalidate ( binaryExpression . right ) ; } else { const start = node . getStart ( ) + this . astOffset ; const end = node . getEnd ( ) + this . astOffset ; this . str . prependLeft ( start , ';() => {' ) ; preprendStr ( this . str , end , '}' ) ; } } private wrapExpressionWithInvalidate ( expression ) { if ( ! expression ) { return ; } const start = expression . getStart ( ) + this . astOffset ; const end = expression . getEnd ( ) + this . astOffset ; if ( ts . isObjectLiteralExpression ( expression ) || ( expression . getText ( ) . startsWith ( '{' ) && this . isNodeStartsWithObjectLiteral ( expression ) ) || ts . isAsExpression ( expression ) ) { this . str . appendLeft ( start , '(' ) ; this . str . appendRight ( end , ')' ) ; } this . str . prependLeft ( start , '__sveltets_1_invalidate(() => ' ) ; preprendStr ( this . str , end , ')' ) ; } private isNodeStartsWithObjectLiteral ( node ) { if ( ts . isObjectLiteralExpression ( node ) ) { return true ; } if ( ts . isElementAccessExpression ( node ) ) { return this . isNodeStartsWithObjectLiteral ( node . expression ) ; } if ( ts . isBinaryExpression ( node ) ) { return this . isNodeStartsWithObjectLiteral ( node . left ) ; } if ( ts . isConditionalExpression ( node ) ) { return this . isNodeStartsWithObjectLiteral ( node . condition ) ; } return node . getChildren ( ) . filter ( ( e ) => e . pos === node . pos ) . some ( ( child ) => this . isNodeStartsWithObjectLiteral ( child ) ) ; } modifyCode ( rootVariables ) { for ( const node of this . map . values ( ) ) { const names = getNamesFromLabeledStatement ( node ) ; if ( names . length === 0 ) { continue ; } const implicitTopLevelNames = names . filter ( ( name ) => ! rootVariables . has ( name ) ) ; const pos = node . label . getStart ( ) ; if ( this . hasOnlyImplicitTopLevelNames ( names , implicitTopLevelNames ) ) { this . str . remove ( pos + this . astOffset , pos + this . astOffset + 2 ) ; this . str . prependRight ( pos + this . astOffset , 'let ' ) ; this . removeBracesFromParenthizedExpression ( node ) ; } else { implicitTopLevelNames . forEach ( ( name ) => { this . str . prependRight ( pos + this . astOffset , `let ${ name };\n` ) ; } ) ; } } } private hasOnlyImplicitTopLevelNames ( names , implicitTopLevelNames ) { return names . length === implicitTopLevelNames . length ; } private removeBracesFromParenthizedExpression ( node ) { if ( ts . isExpressionStatement ( node . statement ) && isParenthesizedObjectOrArrayLiteralExpression ( node . statement . expression ) ) { const start = node . statement . expression . getStart ( ) + this . astOffset ; this . str . overwrite ( start , start + 1 , '' , { contentOnly : true } ) ; const end = node . statement . expression . getEnd ( ) + this . astOffset - 1 ; overwriteStr ( this . str , end , ')' , true ) ; } } }