| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 | 
							- /**
 
-  * @author Yosuke Ota
 
-  * See LICENSE file in root directory for full license.
 
-  */
 
- 'use strict'
 
- // ------------------------------------------------------------------------------
 
- // Requirements
 
- // ------------------------------------------------------------------------------
 
- const utils = require('../utils')
 
- const regexp = require('../utils/regexp')
 
- const casing = require('../utils/casing')
 
- /**
 
-  * @typedef { { names: { [tagName in string]: Set<string> }, regexps: { name: RegExp, attrs: Set<string> }[], cache: { [tagName in string]: Set<string> } } } TargetAttrs
 
-  */
 
- // ------------------------------------------------------------------------------
 
- // Constants
 
- // ------------------------------------------------------------------------------
 
- // https://dev.w3.org/html5/html-author/charref
 
- const DEFAULT_ALLOWLIST = [
 
-   '(',
 
-   ')',
 
-   ',',
 
-   '.',
 
-   '&',
 
-   '+',
 
-   '-',
 
-   '=',
 
-   '*',
 
-   '/',
 
-   '#',
 
-   '%',
 
-   '!',
 
-   '?',
 
-   ':',
 
-   '[',
 
-   ']',
 
-   '{',
 
-   '}',
 
-   '<',
 
-   '>',
 
-   '\u00b7', // "·"
 
-   '\u2022', // "•"
 
-   '\u2010', // "‐"
 
-   '\u2013', // "–"
 
-   '\u2014', // "—"
 
-   '\u2212', // "−"
 
-   '|'
 
- ]
 
- const DEFAULT_ATTRIBUTES = {
 
-   '/.+/': [
 
-     'title',
 
-     'aria-label',
 
-     'aria-placeholder',
 
-     'aria-roledescription',
 
-     'aria-valuetext'
 
-   ],
 
-   input: ['placeholder'],
 
-   img: ['alt']
 
- }
 
- const DEFAULT_DIRECTIVES = ['v-text']
 
- // --------------------------------------------------------------------------
 
- // Helpers
 
- // --------------------------------------------------------------------------
 
- /**
 
-  * Parse attributes option
 
-  * @param {any} options
 
-  * @returns {TargetAttrs}
 
-  */
 
- function parseTargetAttrs(options) {
 
-   /** @type {TargetAttrs} */
 
-   const result = { names: {}, regexps: [], cache: {} }
 
-   for (const tagName of Object.keys(options)) {
 
-     /** @type { Set<string> } */
 
-     const attrs = new Set(options[tagName])
 
-     if (regexp.isRegExp(tagName)) {
 
-       result.regexps.push({
 
-         name: regexp.toRegExp(tagName),
 
-         attrs
 
-       })
 
-     } else {
 
-       result.names[tagName] = attrs
 
-     }
 
-   }
 
-   return result
 
- }
 
- /**
 
-  * Get a string from given expression container node
 
-  * @param {VExpressionContainer} value
 
-  * @returns { string | null }
 
-  */
 
- function getStringValue(value) {
 
-   const expression = value.expression
 
-   if (!expression) {
 
-     return null
 
-   }
 
-   if (expression.type !== 'Literal') {
 
-     return null
 
-   }
 
-   if (typeof expression.value === 'string') {
 
-     return expression.value
 
-   }
 
-   return null
 
- }
 
- // ------------------------------------------------------------------------------
 
- // Rule Definition
 
- // ------------------------------------------------------------------------------
 
- module.exports = {
 
-   meta: {
 
-     type: 'suggestion',
 
-     docs: {
 
-       description: 'disallow the use of bare strings in `<template>`',
 
-       categories: undefined,
 
-       url: 'https://eslint.vuejs.org/rules/no-bare-strings-in-template.html'
 
-     },
 
-     schema: [
 
-       {
 
-         type: 'object',
 
-         properties: {
 
-           allowlist: {
 
-             type: 'array',
 
-             items: { type: 'string' },
 
-             uniqueItems: true
 
-           },
 
-           attributes: {
 
-             type: 'object',
 
-             patternProperties: {
 
-               '^(?:\\S+|/.*/[a-z]*)$': {
 
-                 type: 'array',
 
-                 items: { type: 'string' },
 
-                 uniqueItems: true
 
-               }
 
-             },
 
-             additionalProperties: false
 
-           },
 
-           directives: {
 
-             type: 'array',
 
-             items: { type: 'string', pattern: '^v-' },
 
-             uniqueItems: true
 
-           }
 
-         }
 
-       }
 
-     ],
 
-     messages: {
 
-       unexpected: 'Unexpected non-translated string used.',
 
-       unexpectedInAttr: 'Unexpected non-translated string used in `{{attr}}`.'
 
-     }
 
-   },
 
-   /** @param {RuleContext} context */
 
-   create(context) {
 
-     /**
 
-      * @typedef { { upper: ElementStack | null, name: string, attrs: Set<string> } } ElementStack
 
-      */
 
-     const opts = context.options[0] || {}
 
-     /** @type {string[]} */
 
-     const allowlist = opts.allowlist || DEFAULT_ALLOWLIST
 
-     const attributes = parseTargetAttrs(opts.attributes || DEFAULT_ATTRIBUTES)
 
-     const directives = opts.directives || DEFAULT_DIRECTIVES
 
-     const allowlistRe = new RegExp(
 
-       allowlist.map((w) => regexp.escape(w)).join('|'),
 
-       'gu'
 
-     )
 
-     /** @type {ElementStack | null} */
 
-     let elementStack = null
 
-     /**
 
-      * Gets the bare string from given string
 
-      * @param {string} str
 
-      */
 
-     function getBareString(str) {
 
-       return str.trim().replace(allowlistRe, '').trim()
 
-     }
 
-     /**
 
-      * Get the attribute to be verified from the element name.
 
-      * @param {string} tagName
 
-      * @returns {Set<string>}
 
-      */
 
-     function getTargetAttrs(tagName) {
 
-       if (attributes.cache[tagName]) {
 
-         return attributes.cache[tagName]
 
-       }
 
-       /** @type {string[]} */
 
-       const result = []
 
-       if (attributes.names[tagName]) {
 
-         result.push(...attributes.names[tagName])
 
-       }
 
-       for (const { name, attrs } of attributes.regexps) {
 
-         name.lastIndex = 0
 
-         if (name.test(tagName)) {
 
-           result.push(...attrs)
 
-         }
 
-       }
 
-       if (casing.isKebabCase(tagName)) {
 
-         result.push(...getTargetAttrs(casing.pascalCase(tagName)))
 
-       }
 
-       return (attributes.cache[tagName] = new Set(result))
 
-     }
 
-     return utils.defineTemplateBodyVisitor(context, {
 
-       /** @param {VText} node */
 
-       VText(node) {
 
-         if (getBareString(node.value)) {
 
-           context.report({
 
-             node,
 
-             messageId: 'unexpected'
 
-           })
 
-         }
 
-       },
 
-       /**
 
-        * @param {VElement} node
 
-        */
 
-       VElement(node) {
 
-         elementStack = {
 
-           upper: elementStack,
 
-           name: node.rawName,
 
-           attrs: getTargetAttrs(node.rawName)
 
-         }
 
-       },
 
-       'VElement:exit'() {
 
-         elementStack = elementStack && elementStack.upper
 
-       },
 
-       /** @param {VAttribute|VDirective} node */
 
-       VAttribute(node) {
 
-         if (!node.value || !elementStack) {
 
-           return
 
-         }
 
-         if (node.directive === false) {
 
-           const attrs = elementStack.attrs
 
-           if (!attrs.has(node.key.rawName)) {
 
-             return
 
-           }
 
-           if (getBareString(node.value.value)) {
 
-             context.report({
 
-               node: node.value,
 
-               messageId: 'unexpectedInAttr',
 
-               data: {
 
-                 attr: node.key.rawName
 
-               }
 
-             })
 
-           }
 
-         } else {
 
-           const directive = `v-${node.key.name.name}`
 
-           if (!directives.includes(directive)) {
 
-             return
 
-           }
 
-           const str = getStringValue(node.value)
 
-           if (str && getBareString(str)) {
 
-             context.report({
 
-               node: node.value,
 
-               messageId: 'unexpectedInAttr',
 
-               data: {
 
-                 attr: directive
 
-               }
 
-             })
 
-           }
 
-         }
 
-       }
 
-     })
 
-   }
 
- }
 
 
  |