123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /**
- * @author Yosuke Ota
- * See LICENSE file in root directory for full license.
- */
- 'use strict'
- const { Range } = require('semver')
- const utils = require('../utils')
- const FEATURES = {
- // Vue.js 2.5.0+
- 'slot-scope-attribute': require('./syntaxes/slot-scope-attribute'),
- // Vue.js 2.6.0+
- 'dynamic-directive-arguments': require('./syntaxes/dynamic-directive-arguments'),
- 'v-slot': require('./syntaxes/v-slot'),
- // >=2.6.0-beta.1 <=2.6.0-beta.3
- 'v-bind-prop-modifier-shorthand': require('./syntaxes/v-bind-prop-modifier-shorthand')
- }
- const cache = new Map()
- /**
- * Get the `semver.Range` object of a given range text.
- * @param {string} x The text expression for a semver range.
- * @returns {Range|null} The range object of a given range text.
- * It's null if the `x` is not a valid range text.
- */
- function getSemverRange (x) {
- const s = String(x)
- let ret = cache.get(s) || null
- if (!ret) {
- try {
- ret = new Range(s)
- } catch (_error) {
- // Ignore parsing error.
- }
- cache.set(s, ret)
- }
- return ret
- }
- /**
- * Merge two visitors.
- * @param {Visitor} x The visitor which is assigned.
- * @param {Visitor} y The visitor which is assigning.
- * @returns {Visitor} `x`.
- */
- function merge (x, y) {
- for (const key of Object.keys(y)) {
- if (typeof x[key] === 'function') {
- if (x[key]._handlers == null) {
- const fs = [x[key], y[key]]
- x[key] = node => fs.forEach(h => h(node))
- x[key]._handlers = fs
- } else {
- x[key]._handlers.push(y[key])
- }
- } else {
- x[key] = y[key]
- }
- }
- return x
- }
- module.exports = {
- meta: {
- type: 'suggestion',
- docs: {
- description: 'disallow unsupported Vue.js syntax on the specified version',
- category: undefined,
- url: 'https://eslint.vuejs.org/rules/no-unsupported-features.html'
- },
- fixable: 'code',
- schema: [
- {
- type: 'object',
- properties: {
- version: {
- type: 'string'
- },
- ignores: {
- type: 'array',
- items: {
- enum: Object.keys(FEATURES)
- },
- uniqueItems: true
- }
- },
- additionalProperties: false
- }
- ],
- messages: {
- // Vue.js 2.5.0+
- forbiddenSlotScopeAttribute: '`slot-scope` are not supported until Vue.js "2.5.0".',
- // Vue.js 2.6.0+
- forbiddenDynamicDirectiveArguments: 'Dynamic arguments are not supported until Vue.js "2.6.0".',
- forbiddenVSlot: '`v-slot` are not supported until Vue.js "2.6.0".',
- // >=2.6.0-beta.1 <=2.6.0-beta.3
- forbiddenVBindPropModifierShorthand: '`.prop` shorthand are not supported except Vue.js ">=2.6.0-beta.1 <=2.6.0-beta.3".'
- }
- },
- create (context) {
- const { version, ignores } = Object.assign(
- {
- version: null,
- ignores: []
- },
- context.options[0] || {}
- )
- if (!version) {
- // version is not set.
- return {}
- }
- const versionRange = getSemverRange(version)
- /**
- * Check whether a given case object is full-supported on the configured node version.
- * @param {{supported:string}} aCase The case object to check.
- * @returns {boolean} `true` if it's supporting.
- */
- function isNotSupportingVersion (aCase) {
- if (typeof aCase.supported === 'function') {
- return !aCase.supported(versionRange)
- }
- return versionRange.intersects(getSemverRange(`<${aCase.supported}`))
- }
- const templateBodyVisitor = Object.keys(FEATURES)
- .filter(syntaxName => !ignores.includes(syntaxName))
- .filter(syntaxName => isNotSupportingVersion(FEATURES[syntaxName]))
- .reduce((result, syntaxName) => {
- const visitor = FEATURES[syntaxName].createTemplateBodyVisitor(context)
- if (visitor) {
- merge(result, visitor)
- }
- return result
- }, {})
- return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
- }
- }
|