import type { TSESLint , TSESTree } from '@typescript-eslint/experimental-utils' ; import { isModuleDeclaration , isVariableDeclaration , isObjectExpression , isExpressionStatement , isAssignmentExpression , isMemberExpression , isIdentifier , } from '../utils/nodes' ; import { areEquivalent } from '../utils/equivalence' ; import docsUrl from '../utils/docs-url' ; const rule = { meta : { messages : { declarePropertiesInsideObject : 'Declare one or more properties of this object inside of the object literal syntax instead of using separate statements.' , } , schema : [ ] , type : 'suggestion' , docs : { description : 'Object literal syntax should be used' , recommended : 'error' , url : docsUrl ( __filename ) , } , } , create ( context ) { return { BlockStatement : ( node ) => checkObjectInitialization ( ( node as TSESTree . BlockStatement ) . body , context ) , Program : ( node ) => { const statements = ( node as TSESTree . Program ) . body . filter ( ( statement ) : statement is TSESTree . Statement => ! isModuleDeclaration ( statement ) ) ; checkObjectInitialization ( statements , context ) ; } , } ; } , } ; function checkObjectInitialization ( statements , context ) { let index = 0 ; while ( index < statements . length - 1 ) { const objectDeclaration = getObjectDeclaration ( statements [ index ] ) ; if ( objectDeclaration && isIdentifier ( objectDeclaration . id ) ) { if ( isPropertyAssignment ( statements [ index + 1 ] , objectDeclaration . id , context . getSourceCode ( ) ) ) { context . report ( { messageId : 'declarePropertiesInsideObject' , node : objectDeclaration } ) ; } } index ++ ; } } function getObjectDeclaration ( statement ) { if ( isVariableDeclaration ( statement ) ) { return statement . declarations . find ( declaration => ! ! declaration . init && isEmptyObjectExpression ( declaration . init ) ) ; } return undefined ; } function isEmptyObjectExpression ( expression ) { return isObjectExpression ( expression ) && expression . properties . length === 0 ; } function isPropertyAssignment ( statement , objectIdentifier , sourceCode ) { if ( isExpressionStatement ( statement ) && isAssignmentExpression ( statement . expression ) ) { const { left , right } = statement . expression ; if ( isMemberExpression ( left ) ) { return ( ! left . computed && isSingleLineExpression ( right , sourceCode ) && areEquivalent ( left . object , objectIdentifier , sourceCode ) ) ; } } return false ; } function isSingleLineExpression ( expression , sourceCode ) { const first = sourceCode . getFirstToken ( expression ) ! . loc ; const last = sourceCode . getLastToken ( expression ) ! . loc ; return first . start . line === last . end . line ; } export = rule ;