vue hello world项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.9 KiB

3 years ago
/**
* @fileoverview enforce sort-keys in a manner that is compatible with order-in-components
* @author Loren Klingman
* Original ESLint sort-keys by Toru Nagashima
*/
'use strict'
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const naturalCompare = require('natural-compare')
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
/**
* Gets the property name of the given `Property` node.
*
* - If the property's key is an `Identifier` node, this returns the key's name
* whether it's a computed property or not.
* - If the property has a static name, this returns the static name.
* - Otherwise, this returns null.
* @param {Property} node The `Property` node to get.
* @returns {string|null} The property name or null.
* @private
*/
function getPropertyName(node) {
const staticName = utils.getStaticPropertyName(node)
if (staticName !== null) {
return staticName
}
return node.key.type === 'Identifier' ? node.key.name : null
}
/**
* Functions which check that the given 2 names are in specific order.
*
* Postfix `I` is meant insensitive.
* Postfix `N` is meant natural.
* @private
* @type { { [key: string]: (a:string, b:string) => boolean } }
*/
const isValidOrders = {
asc(a, b) {
return a <= b
},
ascI(a, b) {
return a.toLowerCase() <= b.toLowerCase()
},
ascN(a, b) {
return naturalCompare(a, b) <= 0
},
ascIN(a, b) {
return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0
},
desc(a, b) {
return isValidOrders.asc(b, a)
},
descI(a, b) {
return isValidOrders.ascI(b, a)
},
descN(a, b) {
return isValidOrders.ascN(b, a)
},
descIN(a, b) {
return isValidOrders.ascIN(b, a)
}
}
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'suggestion',
docs: {
description:
'enforce sort-keys in a manner that is compatible with order-in-components',
categories: null,
recommended: false,
url: 'https://eslint.vuejs.org/rules/sort-keys.html'
},
fixable: null,
schema: [
{
enum: ['asc', 'desc']
},
{
type: 'object',
properties: {
caseSensitive: {
type: 'boolean',
default: true
},
ignoreChildrenOf: {
type: 'array'
},
ignoreGrandchildrenOf: {
type: 'array'
},
minKeys: {
type: 'integer',
minimum: 2,
default: 2
},
natural: {
type: 'boolean',
default: false
},
runOutsideVue: {
type: 'boolean',
default: true
}
},
additionalProperties: false
}
],
messages: {
sortKeys:
"Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'."
}
},
/**
* @param {RuleContext} context - The rule context.
* @returns {RuleListener} AST event handlers.
*/
create(context) {
// Parse options.
const options = context.options[1]
const order = context.options[0] || 'asc'
/** @type {string[]} */
const ignoreGrandchildrenOf = (options &&
options.ignoreGrandchildrenOf) || [
'computed',
'directives',
'inject',
'props',
'watch'
]
/** @type {string[]} */
const ignoreChildrenOf = (options && options.ignoreChildrenOf) || ['model']
const insensitive = options && options.caseSensitive === false
const minKeys = options && options.minKeys
const natural = options && options.natural
const isValidOrder =
isValidOrders[order + (insensitive ? 'I' : '') + (natural ? 'N' : '')]
/**
* @typedef {object} ObjectStack
* @property {ObjectStack | null} ObjectStack.upper
* @property {string | null} ObjectStack.prevName
* @property {number} ObjectStack.numKeys
* @property {VueState} ObjectStack.vueState
*
* @typedef {object} VueState
* @property {Property} [VueState.currentProperty]
* @property {boolean} [VueState.isVueObject]
* @property {boolean} [VueState.within]
* @property {string} [VueState.propName]
* @property {number} [VueState.chainLevel]
* @property {boolean} [VueState.ignore]
*/
/**
* The stack to save the previous property's name for each object literals.
* @type {ObjectStack | null}
*/
let objectStack
return {
ObjectExpression(node) {
/** @type {VueState} */
const vueState = {}
const upperVueState = (objectStack && objectStack.vueState) || {}
objectStack = {
upper: objectStack,
prevName: null,
numKeys: node.properties.length,
vueState
}
vueState.isVueObject = utils.getVueObjectType(context, node) != null
if (vueState.isVueObject) {
vueState.within = vueState.isVueObject
// Ignore Vue object properties
vueState.ignore = true
} else {
if (upperVueState.within && upperVueState.currentProperty) {
const isChain = utils.isPropertyChain(
upperVueState.currentProperty,
node
)
if (isChain) {
let propName
let chainLevel
if (upperVueState.isVueObject) {
propName =
utils.getStaticPropertyName(upperVueState.currentProperty) ||
''
chainLevel = 1
} else {
propName = upperVueState.propName || ''
chainLevel = (upperVueState.chainLevel || 0) + 1
}
vueState.propName = propName
vueState.chainLevel = chainLevel
// chaining
vueState.within = true
// Judge whether to ignore the property.
if (chainLevel === 1) {
if (ignoreChildrenOf.includes(propName)) {
vueState.ignore = true
}
} else if (chainLevel === 2) {
if (ignoreGrandchildrenOf.includes(propName)) {
vueState.ignore = true
}
}
} else {
// chaining has broken.
vueState.within = false
}
}
}
},
'ObjectExpression:exit'() {
objectStack = objectStack && objectStack.upper
},
SpreadElement(node) {
if (!objectStack) {
return
}
if (node.parent.type === 'ObjectExpression') {
objectStack.prevName = null
}
},
'ObjectExpression > Property'(node) {
if (!objectStack) {
return
}
objectStack.vueState.currentProperty = node
if (objectStack.vueState.ignore) {
return
}
const prevName = objectStack.prevName
const numKeys = objectStack.numKeys
const thisName = getPropertyName(node)
if (thisName !== null) {
objectStack.prevName = thisName
}
if (prevName === null || thisName === null || numKeys < minKeys) {
return
}
if (!isValidOrder(prevName, thisName)) {
context.report({
node,
loc: node.key.loc,
messageId: 'sortKeys',
data: {
thisName,
prevName,
order,
insensitive: insensitive ? 'insensitive ' : '',
natural: natural ? 'natural ' : ''
}
})
}
}
}
}
}