import type { Rule , SourceCode } from 'eslint' ; import type { AttributeToken , TagToken , Token as PugToken } from 'pug-lexer' ; import type { AssignmentProperty , ASTNode , CallExpression , Declaration , ESNode , Expression , Identifier , Literal , MemberExpression , MethodDefinition , NewExpression , ObjectExpression , PrivateIdentifier , Property , SpreadElement , Super , TemplateLiteral , TSAsExpression , VAttribute , VDirective , VDocumentFragment , VElement , VExpressionContainer , VText } from '../util-types/ast' ; import type { Token } from '../util-types/node' ; import type { ParserServices , TemplateListener } from '../util-types/parser-services' ; import type { VueObjectType } from '../util-types/utils' ; import { isHtmlWellKnownElementName } from './html-element' ; import { getAttributeTokens } from './pug-utils' ; import { isSvgWellKnownElementName } from './svg-element' ; param name returns export function isVueEventBinding ( name ) { return /^(v-on:|@).*/ . test ( name ) ; } param name returns export function isVueExpression ( name ) { return /^((v-(bind|slot))?:|v-(model|slot|if|for|else-if|text|html)|#).*/ . test ( name ) ; } param name param val returns export function isVueVForWithOf ( name , val ) { return 'v-for' === name && val . includes ( 'of' ) ; } param name returns export function isVueVBindExpression ( name ) { return 'v-bind' === name ; } param name returns export function isVueVOnExpression ( name ) { return 'v-on' === name ; } export function isVueBinding ( token ) { return ( isVueExpression ( token . name ) || isVueEventBinding ( token . name ) || ( typeof token . val === 'string' && isVueVForWithOf ( token . name , token . val ) ) || isVueVBindExpression ( token . name ) || isVueVOnExpression ( token . name ) ) ; } /**
 * Gets the parent node of the given node. This method returns a value ignoring `X as F`.
 */ export function getParent ( node ) { let parent = node . parent ; while ( parent . type === 'TSAsExpression' ) { parent = parent . parent ; } return parent ; } export function isVueFile ( path ) { return path . endsWith ( '.vue' ) || path . endsWith ( '.jsx' ) ; } param node param path export function isVueComponentFile ( node , path ) { return isVueFile ( path ) && node . type === 'ExportDefaultDeclaration' && node . declaration . type === 'ObjectExpression' ; } param node returns export function skipTSAsExpression < T extends Expression | Super | SpreadElement | Declaration > ( node ) { if ( ! node ) { return node ; } if ( node . type === 'TSAsExpression' ) { return skipTSAsExpression ( node . expression ) ; } return node ; } /**
 * Checks whether the given node is VElement.
 */ export function isVElement ( node ) { return node . type === 'VElement' ; } param node param name param value returns export function getAttribute ( node , name , value ? ) { return ( node . startTag . attributes . find ( ( node ) => { return ( ! node . directive && node . key . name === name && ( value === undefined || ( node . value != null && node . value . value === value ) ) ) ; } ) ?? null ) ; } param node param name param value returns export function hasAttribute ( node , name , value ? ) { return Boolean ( getAttribute ( node , name , value ) ) ; } param context returns export function getScriptSetupElement ( context ) { const parserServices = context . parserServices ; const df = parserServices . getDocumentFragment ?. ( ) ; if ( ! df ) { return null ; } const scripts = df . children . filter ( isVElement ) . filter ( ( e ) => e . name === 'script' ) ; if ( scripts . length === 2 ) { return scripts . find ( ( e ) => hasAttribute ( e , 'setup' ) ) ?? null ; } else { const script = scripts [ 0 ] ; if ( script && hasAttribute ( script , 'setup' ) ) { return script ; } } return null ; } param node param stringOnly return export function getStringLiteralValue ( node , stringOnly ? ) { if ( node . type === 'Literal' ) { if ( node . value == null ) { if ( ! stringOnly && node . bigint != null ) { return node . bigint ; } return null ; } if ( typeof node . value === 'string' ) { return node . value ; } if ( ! stringOnly ) { return String ( node . value ) ; } return null ; } if ( node . type === 'TemplateLiteral' ) { if ( node . expressions . length === 0 && node . quasis . length === 1 ) { return node . quasis [ 0 ] ! . value . cooked ; } } return null ; } param node return export function getStaticPropertyName ( node ) { if ( node . type === 'Property' || node . type === 'MethodDefinition' ) { if ( ! node . computed ) { const key = node . key ; if ( key . type === 'Identifier' ) { return key . name ; } } const key = node . key ; return getStringLiteralValue ( key ) ; } else if ( node . type === 'MemberExpression' ) { if ( ! node . computed ) { const property = node . property ; if ( property . type === 'Identifier' ) { return property . name ; } return null ; } const property = node . property ; return getStringLiteralValue ( property ) ; } return null ; } param node function getVueComponentDefinitionType ( node ) { const parent = getParent ( node ) ; if ( parent . type === 'CallExpression' ) { const callee = parent . callee ; if ( callee . type === 'MemberExpression' ) { const calleeObject = skipTSAsExpression ( callee . object ) ; if ( calleeObject . type === 'Identifier' ) { const propName = getStaticPropertyName ( callee ) ; if ( calleeObject . name === 'Vue' ) { const maybeFullVueComponentForVue2 = propName && isObjectArgument ( parent ) ; return maybeFullVueComponentForVue2 && ( propName === 'component' || propName === 'mixin' || propName === 'extend' ) ? propName : null ; } const maybeFullVueComponent = propName && isObjectArgument ( parent ) ; return maybeFullVueComponent && ( propName === 'component' || propName === 'mixin' ) ? propName : null ; } } if ( callee . type === 'Identifier' ) { if ( callee . name === 'component' ) { const isDestructedVueComponent = isObjectArgument ( parent ) ; return isDestructedVueComponent ? 'component' : null ; } if ( callee . name === 'createApp' ) { const isAppVueComponent = isObjectArgument ( parent ) ; return isAppVueComponent ? 'createApp' : null ; } if ( callee . name === 'defineComponent' ) { const isDestructedVueComponent = isObjectArgument ( parent ) ; return isDestructedVueComponent ? 'defineComponent' : null ; } } } return null ; function isObjectArgument ( node ) { return node . arguments . length > 0 && skipTSAsExpression ( node . arguments . slice ( - 1 ) [ 0 ] ! ) . type === 'ObjectExpression' ; } } param node export function isVueInstance ( node ) { const callee = node . callee ; return Boolean ( node . type === 'NewExpression' && callee . type === 'Identifier' && callee . name === 'Vue' && node . arguments . length && skipTSAsExpression ( node . arguments [ 0 ] ! ) . type === 'ObjectExpression' ) ; } const componentComments = new WeakMap ( ) ; param context return export function getComponentComments ( context ) { let tokens = componentComments . get ( context ) ; if ( tokens ) { return tokens ; } const sourceCode = context . getSourceCode ( ) ; tokens = sourceCode . getAllComments ( ) . filter ( ( comment ) => /@vue\/component/g . test ( comment . value ) ) as Token [ ] ; componentComments . set ( context , tokens ) ; return tokens ; } param context param node returns export function getVueObjectType ( context , node ) { if ( node . type !== 'ObjectExpression' ) { return null ; } const parent = getParent ( node ) ; if ( parent . type === 'ExportDefaultDeclaration' ) { const filePath = context . getFilename ( ) ; if ( isVueComponentFile ( parent , filePath ) && skipTSAsExpression ( parent . declaration ) === node ) { const scriptSetup = getScriptSetupElement ( context ) ; if ( scriptSetup && scriptSetup . range [ 0 ] <= parent . range [ 0 ] && parent . range [ 1 ] <= scriptSetup . range [ 1 ] ) { return null ; } return 'export' ; } } else if ( parent . type === 'CallExpression' ) { if ( getVueComponentDefinitionType ( node ) != null && skipTSAsExpression ( parent . arguments . slice ( - 1 ) [ 0 ] ! ) === node ) { return 'definition' ; } } else if ( parent . type === 'NewExpression' ) { if ( isVueInstance ( parent ) && skipTSAsExpression ( parent . arguments [ 0 ] ! ) === node ) { return 'instance' ; } } if ( getComponentComments ( context ) ?. some ( ( el ) => el . loc . end . line === node . loc . start . line - 1 ) ) { return 'mark' ; } return null ; } export function compositingVisitors < T > ( visitor , ... visitors ) { for ( const v of visitors ) { for ( const key in v ) { if ( visitor [ key ] ) { const o = visitor [ key ] ; visitor [ key ] = ( ... args ) => { o ( ... args ) ; v [ key as keyof ( Rule . RuleListener | Rule . NodeListener ) ] ( ... args ) ; } ; } else { visitor [ key ] = v [ key ] ; } } } return visitor ; } export function executeOnVueComponent ( context , cb ) { return { 'ObjectExpression:exit' ( node ) { const type = getVueObjectType ( context , node ) ; if ( ! type || ( type !== 'mark' && type !== 'export' && type !== 'definition' ) ) { return ; } cb ( node , type ) ; } } ; } param context param cb export function executeOnVueInstance ( context , cb ) { return { 'ObjectExpression:exit' ( node ) { const type = getVueObjectType ( context , node ) ; if ( ! type || type !== 'instance' ) { return ; } cb ( node , type ) ; } } ; } export function executeOnVue ( context , cb ) { return compositingVisitors ( executeOnVueComponent ( context , cb ) , executeOnVueInstance ( context , cb ) ) ; } /**
 * Checks whether the given node is Property.
 */ export function isProperty ( node ) { if ( node . type !== 'Property' ) { return false ; } return ! node . parent || node . parent . type === 'ObjectExpression' ; } /**
 * Checks whether the given value is defined.
 */ export function isDef < T > ( v ) { return v != null ; } export function getRegisteredVueComponents ( componentObject ) { const componentsNode = componentObject . properties . find ( ( property ) => property . type === 'Property' && getStaticPropertyName ( property ) === 'components' && property . value . type === 'ObjectExpression' ) ; if ( ! componentsNode || ! ( 'value' in componentsNode ) || componentsNode . value . type !== 'ObjectExpression' ) { return [ ] ; } return componentsNode . value . properties . filter ( isProperty ) . map ( ( node ) => { const name = getStaticPropertyName ( node ) ; return name ? { node , name } : null ; } ) . filter ( isDef ) ; } param token returns export function isCustomComponent ( tag , tokens ) { if ( ! isHtmlWellKnownElementName ( tag . val ) && ! isSvgWellKnownElementName ( tag . val ) ) { return true ; } const attributeTokens = getAttributeTokens ( tag , tokens ) ; const hasIsAttribute = attributeTokens . some ( ( { name } ) => /^(v-bind)?:?is$/ . test ( name ) ) ; return hasIsAttribute ; }