/** * @author Yosuke Ota * See LICENSE file in root directory for full license. */ 'use strict' const utils = require('../utils') module.exports = { meta: { type: 'suggestion', docs: { description: 'disallow static inline `style` attributes', categories: undefined, url: 'https://eslint.vuejs.org/rules/no-static-inline-styles.html' }, fixable: null, schema: [ { type: 'object', properties: { allowBinding: { type: 'boolean' } }, additionalProperties: false } ], messages: { forbiddenStaticInlineStyle: 'Static inline `style` are forbidden.', forbiddenStyleAttr: '`style` attributes are forbidden.' } }, /** @param {RuleContext} context */ create(context) { /** * Checks whether if the given property node is a static value. * @param {Property} prop property node to check * @returns {boolean} `true` if the given property node is a static value. */ function isStaticValue(prop) { return ( !prop.computed && prop.value.type === 'Literal' && (prop.key.type === 'Identifier' || prop.key.type === 'Literal') ) } /** * Gets the static properties of a given expression node. * - If `SpreadElement` or computed property exists, it gets only the static properties before it. * `:style="{ color: 'red', display: 'flex', ...spread, width: '16px' }"` * ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ * - If non-static object exists, it gets only the static properties up to that object. * `:style="[ { color: 'red' }, { display: 'flex', color, width: '16px' }, { height: '16px' } ]"` * ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ * - If all properties are static properties, it returns one root node. * `:style="[ { color: 'red' }, { display: 'flex', width: '16px' } ]"` * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * @param {VDirective} node `:style` node to check * @returns {Property[] | [VDirective]} the static properties. */ function getReportNodes(node) { const { value } = node if (!value) { return [] } const { expression } = value if (!expression) { return [] } let elements if (expression.type === 'ObjectExpression') { elements = [expression] } else if (expression.type === 'ArrayExpression') { elements = expression.elements } else { return [] } const staticProperties = [] for (const element of elements) { if (!element) { continue } if (element.type !== 'ObjectExpression') { return staticProperties } let isAllStatic = true for (const prop of element.properties) { if (prop.type === 'SpreadElement' || prop.computed) { // If `SpreadElement` or computed property exists, it gets only the static properties before it. return staticProperties } if (isStaticValue(prop)) { staticProperties.push(prop) } else { isAllStatic = false } } if (!isAllStatic) { // If non-static object exists, it gets only the static properties up to that object. return staticProperties } } // If all properties are static properties, it returns one root node. return [node] } /** * Reports if the value is static. * @param {VDirective} node `:style` node to check */ function verifyVBindStyle(node) { for (const n of getReportNodes(node)) { context.report({ node: n, messageId: 'forbiddenStaticInlineStyle' }) } } /** @type {TemplateListener} */ const visitor = { /** @param {VAttribute} node */ "VAttribute[directive=false][key.name='style']"(node) { context.report({ node, messageId: 'forbiddenStyleAttr' }) } } if (!context.options[0] || !context.options[0].allowBinding) { visitor[ "VAttribute[directive=true][key.name.name='bind'][key.argument.name='style']" ] = verifyVBindStyle } return utils.defineTemplateBodyVisitor(context, visitor) } }