author Yosuke Ota
  import type { AST as JSONAST } from 'jsonc-eslint-parser' ; import { getStaticJSONValue } from 'jsonc-eslint-parser' ; import type { AST as YAMLAST } from 'yaml-eslint-parser' ; import { getStaticYAMLValue } from 'yaml-eslint-parser' ; import { extname } from 'path' ; import { defineCustomBlocksVisitor , getLocaleMessages } from '../utils/index' ; import debugBuilder from 'debug' ; import type { RuleContext , RuleListener } from '../types' ; import { getMessageSyntaxVersions , getReportIndex } from '../utils/message-compiler/utils' ; import { parse } from '../utils/message-compiler/parser' ; import { parse as parseForV8 } from '../utils/message-compiler/parser-v8' ; import type { CompileError } from '@intlify/message-compiler' ; const debug = debugBuilder ( 'eslint-plugin-vue-i18n:valid-message-syntax' ) ; function create ( context ) { const filename = context . getFilename ( ) ; const sourceCode = context . getSourceCode ( ) ; const allowNotString = Boolean ( context . options [ 0 ] ?. allowNotString ) ; const messageSyntaxVersions = getMessageSyntaxVersions ( context ) ; function * extractMessageErrors ( message ) { if ( messageSyntaxVersions . v9 ) { yield * parse ( message ) . errors ; } if ( messageSyntaxVersions . v8 ) { yield * parseForV8 ( message ) . errors ; } } function verifyMessage ( message , reportNode , getReportOffset ) { if ( typeof message !== 'string' ) { if ( ! allowNotString ) { const type = message === null ? 'null' : message instanceof RegExp ? 'RegExp' : typeof message ; context . report ( { message : `Unexpected '${ type }' message` , loc : reportNode . loc } ) ; } } else { for ( const error of extractMessageErrors ( message ) ) { messageSyntaxVersions . reportIfMissingSetting ( ) ; const reportOffset = getReportOffset ?. ( error ) ; context . report ( { message : error . message , loc : reportOffset != null ? sourceCode . getLocFromIndex ( reportOffset ) : reportNode . loc } ) ; } } } /**
     * Create node visitor for JSON
     */ function createVisitorForJson ( ) { function verifyExpression ( node , parent ) { let message ; let getReportOffset = null ; if ( node ) { if ( node . type === 'JSONArrayExpression' || node . type === 'JSONObjectExpression' ) { return ; } message = getStaticJSONValue ( node ) ; getReportOffset = error => getReportIndex ( node , error . location ! . start . offset ) ; } else { message = null ; } verifyMessage ( message , node || parent , getReportOffset ) ; } return { JSONProperty ( node ) { verifyExpression ( node . value , node ) ; } , JSONArrayExpression ( node ) { for ( const element of node . elements ) { verifyExpression ( element , node ) ; } } } ; } /**
     * Create node visitor for YAML
     */ function createVisitorForYaml ( ) { const yamlKeyNodes = new Set < YAMLAST . YAMLContent | YAMLAST . YAMLWithMeta > ( ) ; function withinKey ( node ) { for ( const keyNode of yamlKeyNodes ) { if ( keyNode . range [ 0 ] <= node . range [ 0 ] && node . range [ 0 ] < keyNode . range [ 1 ] ) { return true ; } } return false ; } function verifyContent ( node , parent ) { let message ; let getReportOffset = null ; if ( node ) { const valueNode = node . type === 'YAMLWithMeta' ? node . value : node ; if ( ! valueNode || valueNode . type === 'YAMLMapping' || valueNode . type === 'YAMLSequence' ) { return ; } message = getStaticYAMLValue ( node ) ; getReportOffset = error => getReportIndex ( valueNode , error . location ! . start . offset ) ; } else { message = null ; } if ( message != null && typeof message === 'object' ) { return ; } verifyMessage ( message , node || parent , getReportOffset ) ; } return { YAMLPair ( node ) { if ( withinKey ( node ) ) { return ; } if ( node . key != null ) { yamlKeyNodes . add ( node . key ) ; } verifyContent ( node . value , node ) ; } , YAMLSequence ( node ) { if ( withinKey ( node ) ) { return ; } for ( const entry of node . entries ) { verifyContent ( entry , node ) ; } } } ; } if ( extname ( filename ) === '.vue' ) { return defineCustomBlocksVisitor ( context , createVisitorForJson , createVisitorForYaml ) ; } else if ( context . parserServices . isJSON || context . parserServices . isYAML ) { const localeMessages = getLocaleMessages ( context ) ; const targetLocaleMessage = localeMessages . findExistLocaleMessage ( filename ) ; if ( ! targetLocaleMessage ) { debug ( `ignore ${ filename } in valid-message-syntax` ) ; return { } ; } if ( context . parserServices . isJSON ) { return createVisitorForJson ( ) ; } else if ( context . parserServices . isYAML ) { return createVisitorForYaml ( ) ; } return { } ; } else { debug ( `ignore ${ filename } in valid-message-syntax` ) ; return { } ; } } export = { meta : { type : 'layout' , docs : { description : 'disallow invalid message syntax' , category : 'Recommended' , recommended : false } , fixable : null , schema : [ { type : 'object' , properties : { allowNotString : { type : 'boolean' } } , additionalProperties : false } ] } , create } ;