Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eslint-plugin-vue

Package Overview
Dependencies
Maintainers
0
Versions
174
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-vue - npm Package Compare versions

Comparing version 9.31.0 to 9.32.0

lib/rules/restricted-component-names.js

2

lib/index.js

@@ -234,2 +234,3 @@ /*

'require-valid-default-prop': require('./rules/require-valid-default-prop'),
'restricted-component-names': require('./rules/restricted-component-names'),
'return-in-computed-property': require('./rules/return-in-computed-property'),

@@ -240,2 +241,3 @@ 'return-in-emits-validator': require('./rules/return-in-emits-validator'),

'singleline-html-element-content-newline': require('./rules/singleline-html-element-content-newline'),
'slot-name-casing': require('./rules/slot-name-casing'),
'sort-keys': require('./rules/sort-keys'),

@@ -242,0 +244,0 @@ 'space-in-parens': require('./rules/space-in-parens'),

@@ -9,2 +9,3 @@ /**

const casing = require('../utils/casing')
const { toRegExp } = require('../utils/regexp')
const svgAttributes = require('../utils/svg-attributes-weird-case.json')

@@ -60,2 +61,8 @@

additionalItems: false
},
ignoreTags: {
type: 'array',
items: { type: 'string' },
uniqueItems: true,
additionalItems: false
}

@@ -77,2 +84,7 @@ },

const useHyphenated = option !== 'never'
/** @type {RegExp[]} */
const ignoredTagsRegexps = (
(optionsPayload && optionsPayload.ignoreTags) ||
[]
).map(toRegExp)
const ignoredAttributes = ['data-', 'aria-', 'slot-scope', ...svgAttributes]

@@ -136,7 +148,13 @@

/** @param {string} name */
function isIgnoredTagName(name) {
return ignoredTagsRegexps.some((re) => re.test(name))
}
return utils.defineTemplateBodyVisitor(context, {
VAttribute(node) {
const element = node.parent.parent
if (
!utils.isCustomComponent(node.parent.parent) &&
node.parent.parent.name !== 'slot'
(!utils.isCustomComponent(element) && element.name !== 'slot') ||
isIgnoredTagName(element.rawName)
)

@@ -143,0 +161,0 @@ return

@@ -9,2 +9,29 @@ /**

/** @param {VElement[]} elements */
function isConditionalGroup(elements) {
if (elements.length < 2) {
return false
}
const firstElement = elements[0]
const lastElement = elements[elements.length - 1]
const inBetweenElements = elements.slice(1, -1)
return (
utils.hasDirective(firstElement, 'if') &&
(utils.hasDirective(lastElement, 'else-if') ||
utils.hasDirective(lastElement, 'else')) &&
inBetweenElements.every((element) => utils.hasDirective(element, 'else-if'))
)
}
/** @param {VElement[]} elements */
function isMultiRootNodes(elements) {
if (elements.length > 1 && !isConditionalGroup(elements)) {
return true
}
return false
}
module.exports = {

@@ -21,3 +48,13 @@ meta: {

fixable: null,
schema: [],
schema: [
{
type: 'object',
properties: {
checkMultiRootNodes: {
type: 'boolean'
}
},
additionalProperties: false
}
],
messages: {

@@ -29,4 +66,9 @@ noDuplicateAttrInheritance: 'Set "inheritAttrs" to false.'

create(context) {
const options = context.options[0] || {}
const checkMultiRootNodes = options.checkMultiRootNodes === true
/** @type {string | number | boolean | RegExp | BigInt | null} */
let inheritsAttrs = true
/** @type {VReference[]} */
const attrsRefs = []

@@ -60,3 +102,3 @@ /** @param {ObjectExpression} node */

}
const attrsRef = node.references.find((reference) => {
const reference = node.references.find((reference) => {
if (reference.variable != null) {

@@ -69,12 +111,30 @@ // Not vm reference

if (attrsRef) {
context.report({
node: attrsRef.id,
messageId: 'noDuplicateAttrInheritance'
})
if (reference) {
attrsRefs.push(reference)
}
}
})
}),
{
'Program:exit'(program) {
const element = program.templateBody
if (element == null) {
return
}
const rootElements = element.children.filter(utils.isVElement)
if (!checkMultiRootNodes && isMultiRootNodes(rootElements)) return
if (attrsRefs.length > 0) {
for (const attrsRef of attrsRefs) {
context.report({
node: attrsRef.id,
messageId: 'noDuplicateAttrInheritance'
})
}
}
}
}
)
}
}

@@ -29,2 +29,5 @@ /**

uniqueItems: true
},
ignoreElementNamespaces: {
type: 'boolean'
}

@@ -45,2 +48,4 @@ },

const allow = new Set(options.allow)
/** @type {boolean} */
const ignoreElementNamespaces = options.ignoreElementNamespaces === true

@@ -67,3 +72,6 @@ /**

const element = node.parent.parent
if (utils.isCustomComponent(element) && !isAllowedComponent(element)) {
if (
utils.isCustomComponent(element, ignoreElementNamespaces) &&
!isAllowedComponent(element)
) {
context.report({

@@ -70,0 +78,0 @@ node,

95

lib/rules/prefer-use-template-ref.js

@@ -9,8 +9,40 @@ /**

/** @param expression {Expression | null} */
function expressionIsRef(expression) {
// @ts-ignore
return expression?.callee?.name === 'ref'
/**
* @typedef ScriptRef
* @type {{node: Expression, ref: string}}
*/
/**
* @param declarator {VariableDeclarator}
* @returns {ScriptRef}
* */
function convertDeclaratorToScriptRef(declarator) {
return {
// @ts-ignore
node: declarator.init,
// @ts-ignore
ref: declarator.id.name
}
}
/**
* @param body {(Statement | ModuleDeclaration)[]}
* @returns {ScriptRef[]}
* */
function getScriptRefsFromSetupFunction(body) {
/** @type {VariableDeclaration[]} */
const variableDeclarations = body.filter(
(child) => child.type === 'VariableDeclaration'
)
const variableDeclarators = variableDeclarations.map(
(declaration) => declaration.declarations[0]
)
const refDeclarators = variableDeclarators.filter((declarator) =>
// @ts-ignore
['ref', 'shallowRef'].includes(declarator.init?.callee?.name)
)
return refDeclarators.map(convertDeclaratorToScriptRef)
}
/** @type {import("eslint").Rule.RuleModule} */

@@ -22,3 +54,3 @@ module.exports = {

description:
'require using `useTemplateRef` instead of `ref` for template refs',
'require using `useTemplateRef` instead of `ref`/`shallowRef` for template refs',
categories: undefined,

@@ -29,3 +61,3 @@ url: 'https://eslint.vuejs.org/rules/prefer-use-template-ref.html'

messages: {
preferUseTemplateRef: "Replace 'ref' with 'useTemplateRef'."
preferUseTemplateRef: "Replace '{{name}}' with 'useTemplateRef'."
}

@@ -39,7 +71,2 @@ },

/**
* @typedef ScriptRef
* @type {{node: Expression, ref: string}}
*/
/**
* @type ScriptRef[] */

@@ -49,26 +76,24 @@ const scriptRefs = []

return utils.compositingVisitors(
utils.defineTemplateBodyVisitor(
context,
{
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value?.value) {
templateRefs.add(node.value.value)
}
utils.defineTemplateBodyVisitor(context, {
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value?.value) {
templateRefs.add(node.value.value)
}
},
{
VariableDeclarator(declarator) {
if (!expressionIsRef(declarator.init)) {
return
}
}
}),
utils.defineVueVisitor(context, {
onSetupFunctionEnter(node) {
// @ts-ignore
const newScriptRefs = getScriptRefsFromSetupFunction(node.body.body)
scriptRefs.push({
// @ts-ignore
node: declarator.init,
// @ts-ignore
ref: declarator.id.name
})
}
scriptRefs.push(...newScriptRefs)
}
),
}),
utils.defineScriptSetupVisitor(context, {
Program(node) {
const newScriptRefs = getScriptRefsFromSetupFunction(node.body)
scriptRefs.push(...newScriptRefs)
}
}),
{

@@ -87,3 +112,7 @@ 'Program:exit'() {

node: scriptRef.node,
messageId: 'preferUseTemplateRef'
messageId: 'preferUseTemplateRef',
data: {
// @ts-ignore
name: scriptRef.node?.callee?.name
}
})

@@ -90,0 +119,0 @@ }

@@ -101,26 +101,18 @@ /**

utils.defineScriptSetupVisitor(context, {
onDefineSlotsEnter(node) {
const typeArguments =
'typeArguments' in node ? node.typeArguments : node.typeParameters
const param = /** @type {TypeNode|undefined} */ (
typeArguments?.params[0]
)
if (!param) return
onDefineSlotsEnter(_node, slots) {
for (const slot of slots) {
if (!slot.slotName) {
continue
}
if (param.type === 'TSTypeLiteral') {
for (const memberNode of param.members) {
const slotName = getSlotsName(memberNode)
if (!slotName) continue
if (slotsDefined.has(slotName)) {
context.report({
node: memberNode,
messageId: 'alreadyDefinedSlot',
data: {
slotName
}
})
} else {
slotsDefined.add(slotName)
}
if (slotsDefined.has(slot.slotName)) {
context.report({
node: slot.node,
messageId: 'alreadyDefinedSlot',
data: {
slotName: slot.slotName
}
})
} else {
slotsDefined.add(slot.slotName)
}

@@ -127,0 +119,0 @@ }

@@ -5,2 +5,3 @@ 'use strict'

const casing = require('../utils/casing')
const { toRegExp } = require('../utils/regexp')

@@ -39,2 +40,8 @@ module.exports = {

additionalItems: false
},
ignoreTags: {
type: 'array',
items: { type: 'string' },
uniqueItems: true,
additionalItems: false
}

@@ -61,2 +68,7 @@ },

const ignoredAttributes = (optionsPayload && optionsPayload.ignore) || []
/** @type {RegExp[]} */
const ignoredTagsRegexps = (
(optionsPayload && optionsPayload.ignoreTags) ||
[]
).map(toRegExp)
const autofix = Boolean(optionsPayload && optionsPayload.autofix)

@@ -105,5 +117,16 @@

/** @param {string} name */
function isIgnoredTagName(name) {
return ignoredTagsRegexps.some((re) => re.test(name))
}
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name.name='on']"(node) {
if (!utils.isCustomComponent(node.parent.parent)) return
const element = node.parent.parent
if (
!utils.isCustomComponent(element) ||
isIgnoredTagName(element.rawName)
) {
return
}
if (!node.key.argument || node.key.argument.type !== 'VIdentifier') {

@@ -110,0 +133,0 @@ return

@@ -8,7 +8,9 @@ const {

flattenTypeNodes,
isTSInterfaceBody
isTSInterfaceBody,
extractRuntimeSlots
} = require('./ts-ast')
const {
getComponentPropsFromTypeDefineTypes,
getComponentEmitsFromTypeDefineTypes
getComponentEmitsFromTypeDefineTypes,
getComponentSlotsFromTypeDefineTypes
} = require('./ts-types')

@@ -26,2 +28,5 @@

* @typedef {import('../index').ComponentUnknownEmit} ComponentUnknownEmit
* @typedef {import('../index').ComponentTypeSlot} ComponentTypeSlot
* @typedef {import('../index').ComponentInferTypeSlot} ComponentInferTypeSlot
* @typedef {import('../index').ComponentUnknownSlot} ComponentUnknownSlot
*/

@@ -32,3 +37,4 @@

getComponentPropsFromTypeDefine,
getComponentEmitsFromTypeDefine
getComponentEmitsFromTypeDefine,
getComponentSlotsFromTypeDefine
}

@@ -92,1 +98,28 @@

}
/**
* Get all slots by looking at all component's properties
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode} slotsNode Type with slots definition
* @return {(ComponentTypeSlot|ComponentInferTypeSlot|ComponentUnknownSlot)[]} Array of component slots
*/
function getComponentSlotsFromTypeDefine(context, slotsNode) {
/** @type {(ComponentTypeSlot|ComponentInferTypeSlot|ComponentUnknownSlot)[]} */
const result = []
for (const defNode of flattenTypeNodes(
context,
/** @type {TSESTreeTypeNode} */ (slotsNode)
)) {
if (isTSInterfaceBody(defNode) || isTSTypeLiteral(defNode)) {
result.push(...extractRuntimeSlots(defNode))
} else {
result.push(
...getComponentSlotsFromTypeDefineTypes(
context,
/** @type {TypeNode} */ (defNode)
)
)
}
}
return result
}

@@ -18,2 +18,4 @@ const { getScope } = require('../scope')

* @typedef {import('../index').ComponentUnknownEmit} ComponentUnknownEmit
* @typedef {import('../index').ComponentTypeSlot} ComponentTypeSlot
* @typedef {import('../index').ComponentUnknownSlot} ComponentUnknownSlot
*/

@@ -30,3 +32,4 @@

extractRuntimeProps,
extractRuntimeEmits
extractRuntimeEmits,
extractRuntimeSlots
}

@@ -215,2 +218,34 @@

/**
* @param {TSESTreeTSTypeLiteral | TSESTreeTSInterfaceBody} node
* @returns {IterableIterator<ComponentTypeSlot | ComponentUnknownSlot>}
*/
function* extractRuntimeSlots(node) {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
for (const member of members) {
if (
member.type === 'TSPropertySignature' ||
member.type === 'TSMethodSignature'
) {
if (member.key.type !== 'Identifier' && member.key.type !== 'Literal') {
yield {
type: 'unknown',
slotName: null,
node: /** @type {Expression} */ (member.key)
}
continue
}
yield {
type: 'type',
key: /** @type {Identifier | Literal} */ (member.key),
slotName:
member.key.type === 'Identifier'
? member.key.name
: `${member.key.value}`,
node: /** @type {TSPropertySignature | TSMethodSignature} */ (member)
}
}
}
}
/**
* @param {TSESTreeParameter} eventName

@@ -217,0 +252,0 @@ * @param {TSCallSignatureDeclaration | TSFunctionType} member

@@ -27,2 +27,4 @@ const {

* @typedef {import('../index').ComponentUnknownEmit} ComponentUnknownEmit
* @typedef {import('../index').ComponentInferTypeSlot} ComponentInferTypeSlot
* @typedef {import('../index').ComponentUnknownSlot} ComponentUnknownSlot
*/

@@ -33,2 +35,3 @@

getComponentEmitsFromTypeDefineTypes,
getComponentSlotsFromTypeDefineTypes,
inferRuntimeTypeFromTypeNode

@@ -128,3 +131,31 @@ }

/**
* Get all slots by looking at all component's properties
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode} slotsNode Type with slots definition
* @return {(ComponentInferTypeSlot|ComponentUnknownSlot)[]} Array of component slots
*/
function getComponentSlotsFromTypeDefineTypes(context, slotsNode) {
const services = getTSParserServices(context)
const tsNode = services && services.tsNodeMap.get(slotsNode)
const type = tsNode && services.checker.getTypeAtLocation(tsNode)
if (
!type ||
isAny(type) ||
isUnknown(type) ||
isNever(type) ||
isNull(type)
) {
return [
{
type: 'unknown',
slotName: null,
node: slotsNode
}
]
}
return [...extractRuntimeSlots(type, slotsNode)]
}
/**
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode|Expression} node

@@ -267,2 +298,19 @@ * @returns {string[]}

* @param {Type} type
* @param {TypeNode} slotsNode Type with slots definition
* @returns {IterableIterator<ComponentInferTypeSlot>}
*/
function* extractRuntimeSlots(type, slotsNode) {
for (const property of type.getProperties()) {
const name = property.getName()
yield {
type: 'infer-type',
slotName: name,
node: slotsNode
}
}
}
/**
* @param {Type} type
* @returns {Iterable<Type>}

@@ -269,0 +317,0 @@ */

{
"name": "eslint-plugin-vue",
"version": "9.31.0",
"version": "9.32.0",
"description": "Official ESLint plugin for Vue.js",

@@ -70,3 +70,3 @@ "main": "lib/index.js",

"@ota-meshi/site-kit-eslint-editor-vue": "^0.2.4",
"@stylistic/eslint-plugin": "^2.9.0",
"@stylistic/eslint-plugin": "~2.10.0",
"@types/eslint": "^8.56.2",

@@ -73,0 +73,0 @@ "@types/eslint-visitor-keys": "^3.3.2",

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc