diff --git a/lib/rules/custom-event-name-casing.js b/lib/rules/custom-event-name-casing.js
index 5db15a875..6d215edd8 100644
--- a/lib/rules/custom-event-name-casing.js
+++ b/lib/rules/custom-event-name-casing.js
@@ -24,23 +24,27 @@ const { toRegExp } = require('../utils/regexp')
const ALLOWED_CASE_OPTIONS = ['kebab-case', 'camelCase']
const DEFAULT_CASE = 'camelCase'
+/**
+ * @typedef {object} NameWithLoc
+ * @property {string} name
+ * @property {SourceLocation} loc
+ */
/**
* Get the name param node from the given CallExpression
* @param {CallExpression} node CallExpression
- * @returns { Literal & { value: string } | null }
+ * @returns { NameWithLoc | null }
*/
function getNameParamNode(node) {
const nameLiteralNode = node.arguments[0]
- if (
- !nameLiteralNode ||
- nameLiteralNode.type !== 'Literal' ||
- typeof nameLiteralNode.value !== 'string'
- ) {
- // cannot check
- return null
+ if (nameLiteralNode && utils.isStringLiteral(nameLiteralNode)) {
+ const name = utils.getStringLiteralValue(nameLiteralNode)
+ if (name != null) {
+ return { name, loc: nameLiteralNode.loc }
+ }
}
- return /** @type {Literal & { value: string }} */ (nameLiteralNode)
+ // cannot check
+ return null
}
/**
* Get the callee member node from the given CallExpression
@@ -130,15 +134,15 @@ module.exports = {
}
/**
- * @param { Literal & { value: string } } nameLiteralNode
+ * @param { NameWithLoc } nameWithLoc
*/
- function verify(nameLiteralNode) {
- const name = nameLiteralNode.value
+ function verify(nameWithLoc) {
+ const name = nameWithLoc.name
if (isValidEventName(name) || ignores.some((re) => re.test(name))) {
return
}
context.report({
- node: nameLiteralNode,
+ loc: nameWithLoc.loc,
messageId: 'unexpected',
data: {
name,
@@ -155,8 +159,8 @@ module.exports = {
* @param {VueObjectData} [info]
*/
CallExpression(node, info) {
- const nameLiteralNode = getNameParamNode(node)
- if (!nameLiteralNode) {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
@@ -170,7 +174,7 @@ module.exports = {
emitReferenceIds.has(node.callee)
) {
// verify setup(props,{emit}) {emit()}
- verify(nameLiteralNode)
+ verify(nameWithLoc)
} else {
const emit = getCalleeMemberNode(node)
if (
@@ -180,7 +184,7 @@ module.exports = {
contextReferenceIds.has(emit.member.object)
) {
// verify setup(props,context) {context.emit()}
- verify(nameLiteralNode)
+ verify(nameWithLoc)
}
}
}
@@ -192,13 +196,13 @@ module.exports = {
{
CallExpression(node) {
const callee = node.callee
- const nameLiteralNode = getNameParamNode(node)
- if (!nameLiteralNode) {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
if (callee.type === 'Identifier' && callee.name === '$emit') {
- verify(nameLiteralNode)
+ verify(nameWithLoc)
}
}
},
diff --git a/lib/rules/no-restricted-custom-event.js b/lib/rules/no-restricted-custom-event.js
index 22d128cd8..7987250b4 100644
--- a/lib/rules/no-restricted-custom-event.js
+++ b/lib/rules/no-restricted-custom-event.js
@@ -56,23 +56,28 @@ function parseOption(option) {
return parsed
}
+/**
+ * @typedef {object} NameWithLoc
+ * @property {string} name
+ * @property {SourceLocation} loc
+ * @property {Range} range
+ */
/**
* Get the name param node from the given CallExpression
* @param {CallExpression} node CallExpression
- * @returns { Literal & { value: string } | null }
+ * @returns { NameWithLoc | null }
*/
function getNameParamNode(node) {
const nameLiteralNode = node.arguments[0]
- if (
- !nameLiteralNode ||
- nameLiteralNode.type !== 'Literal' ||
- typeof nameLiteralNode.value !== 'string'
- ) {
- // cannot check
- return null
+ if (nameLiteralNode && utils.isStringLiteral(nameLiteralNode)) {
+ const name = utils.getStringLiteralValue(nameLiteralNode)
+ if (name != null) {
+ return { name, loc: nameLiteralNode.loc, range: nameLiteralNode.range }
+ }
}
- return /** @type {Literal & { value: string }} */ (nameLiteralNode)
+ // cannot check
+ return null
}
/**
* Get the callee member node from the given CallExpression
@@ -134,17 +139,17 @@ module.exports = {
const options = context.options.map(parseOption)
/**
- * @param { Literal & { value: string } } nameLiteralNode
+ * @param { NameWithLoc } nameWithLoc
*/
- function verify(nameLiteralNode) {
- const name = nameLiteralNode.value
+ function verify(nameWithLoc) {
+ const name = nameWithLoc.name
for (const option of options) {
if (option.test(name)) {
const message =
option.message || `Using \`${name}\` event is not allowed.`
context.report({
- node: nameLiteralNode,
+ loc: nameWithLoc.loc,
messageId: 'restrictedEvent',
data: { message },
suggest: option.suggest
@@ -152,14 +157,14 @@ module.exports = {
{
fix(fixer) {
const sourceCode = context.getSourceCode()
- return fixer.replaceText(
- nameLiteralNode,
+ return fixer.replaceTextRange(
+ nameWithLoc.range,
`${
- sourceCode.text[nameLiteralNode.range[0]]
+ sourceCode.text[nameWithLoc.range[0]]
}${JSON.stringify(option.suggest)
.slice(1, -1)
.replace(/'/gu, "\\'")}${
- sourceCode.text[nameLiteralNode.range[1] - 1]
+ sourceCode.text[nameWithLoc.range[1] - 1]
}`
)
},
@@ -179,13 +184,13 @@ module.exports = {
{
CallExpression(node) {
const callee = node.callee
- const nameLiteralNode = getNameParamNode(node)
- if (!nameLiteralNode) {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
if (callee.type === 'Identifier' && callee.name === '$emit') {
- verify(nameLiteralNode)
+ verify(nameWithLoc)
}
}
},
@@ -239,8 +244,8 @@ module.exports = {
})
},
CallExpression(node, { node: vueNode }) {
- const nameLiteralNode = getNameParamNode(node)
- if (!nameLiteralNode) {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
@@ -254,7 +259,7 @@ module.exports = {
emitReferenceIds.has(node.callee)
) {
// verify setup(props,{emit}) {emit()}
- verify(nameLiteralNode)
+ verify(nameWithLoc)
} else {
const emit = getCalleeMemberNode(node)
if (
@@ -264,7 +269,7 @@ module.exports = {
contextReferenceIds.has(emit.member.object)
) {
// verify setup(props,context) {context.emit()}
- verify(nameLiteralNode)
+ verify(nameWithLoc)
}
}
}
@@ -275,8 +280,8 @@ module.exports = {
}),
{
CallExpression(node) {
- const nameLiteralNode = getNameParamNode(node)
- if (!nameLiteralNode) {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
@@ -284,7 +289,7 @@ module.exports = {
// verify $emit
if (emit && emit.name === '$emit') {
// verify this.$emit()
- verify(nameLiteralNode)
+ verify(nameWithLoc)
}
}
}
diff --git a/lib/rules/require-explicit-emits.js b/lib/rules/require-explicit-emits.js
index 1a69c982a..9f531ccba 100644
--- a/lib/rules/require-explicit-emits.js
+++ b/lib/rules/require-explicit-emits.js
@@ -54,6 +54,30 @@ const FIX_EMITS_AFTER_OPTIONS = new Set([
'renderTriggered',
'errorCaptured'
])
+
+/**
+ * @typedef {object} NameWithLoc
+ * @property {string} name
+ * @property {SourceLocation} loc
+ * @property {Range} range
+ */
+/**
+ * Get the name param node from the given CallExpression
+ * @param {CallExpression} node CallExpression
+ * @returns { NameWithLoc | null }
+ */
+function getNameParamNode(node) {
+ const nameLiteralNode = node.arguments[0]
+ if (nameLiteralNode && utils.isStringLiteral(nameLiteralNode)) {
+ const name = utils.getStringLiteralValue(nameLiteralNode)
+ if (name != null) {
+ return { name, loc: nameLiteralNode.loc, range: nameLiteralNode.range }
+ }
+ }
+
+ // cannot check
+ return null
+}
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
@@ -114,11 +138,11 @@ module.exports = {
/**
* @param {ComponentEmit[]} emits
* @param {ComponentProp[]} props
- * @param {Literal} nameLiteralNode
+ * @param {NameWithLoc} nameWithLoc
* @param {ObjectExpression | Program} vueDefineNode
*/
- function verifyEmit(emits, props, nameLiteralNode, vueDefineNode) {
- const name = `${nameLiteralNode.value}`
+ function verifyEmit(emits, props, nameWithLoc, vueDefineNode) {
+ const name = nameWithLoc.name
if (emits.some((e) => e.emitName === name || e.emitName == null)) {
return
}
@@ -129,7 +153,7 @@ module.exports = {
}
}
context.report({
- node: nameLiteralNode,
+ loc: nameWithLoc.loc,
messageId: 'missing',
data: {
name,
@@ -138,7 +162,7 @@ module.exports = {
? '`emits` option'
: '`defineEmits`'
},
- suggest: buildSuggest(vueDefineNode, emits, nameLiteralNode, context)
+ suggest: buildSuggest(vueDefineNode, emits, nameWithLoc, context)
})
}
@@ -155,13 +179,13 @@ module.exports = {
const callVisitor = {
/**
- * @param {CallExpression & { arguments: [Literal, ...Expression] }} node
+ * @param {CallExpression} node
* @param {VueObjectData} [info]
*/
- 'CallExpression[arguments.0.type=Literal]'(node, info) {
+ CallExpression(node, info) {
const callee = utils.skipChainExpression(node.callee)
- const nameLiteralNode = node.arguments[0]
- if (!nameLiteralNode || typeof nameLiteralNode.value !== 'string') {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
@@ -188,7 +212,7 @@ module.exports = {
verifyEmit(
emitsDeclarations,
vuePropsDeclarations.get(vueDefineNode) || [],
- nameLiteralNode,
+ nameWithLoc,
vueDefineNode
)
} else if (emit && emit.name === 'emit') {
@@ -201,7 +225,7 @@ module.exports = {
verifyEmit(
emitsDeclarations,
vuePropsDeclarations.get(vueDefineNode) || [],
- nameLiteralNode,
+ nameWithLoc,
vueDefineNode
)
}
@@ -216,7 +240,7 @@ module.exports = {
verifyEmit(
emitsDeclarations,
vuePropsDeclarations.get(vueDefineNode) || [],
- nameLiteralNode,
+ nameWithLoc,
vueDefineNode
)
}
@@ -227,11 +251,11 @@ module.exports = {
return utils.defineTemplateBodyVisitor(
context,
{
- /** @param { CallExpression & { argument: [Literal, ...Expression] } } node */
- 'CallExpression[arguments.0.type=Literal]'(node) {
+ /** @param { CallExpression } node */
+ CallExpression(node) {
const callee = utils.skipChainExpression(node.callee)
- const nameLiteralNode = /** @type {Literal} */ (node.arguments[0])
- if (!nameLiteralNode || typeof nameLiteralNode.value !== 'string') {
+ const nameWithLoc = getNameParamNode(node)
+ if (!nameWithLoc) {
// cannot check
return
}
@@ -242,7 +266,7 @@ module.exports = {
verifyEmit(
vueTemplateDefineData.emits,
vueTemplateDefineData.props,
- nameLiteralNode,
+ nameWithLoc,
vueTemplateDefineData.define
)
}
@@ -409,11 +433,11 @@ module.exports = {
/**
* @param {ObjectExpression|Program} define
* @param {ComponentEmit[]} emits
- * @param {Literal} nameNode
+ * @param {NameWithLoc} nameWithLoc
* @param {RuleContext} context
* @returns {Rule.SuggestionReportDescriptor[]}
*/
-function buildSuggest(define, emits, nameNode, context) {
+function buildSuggest(define, emits, nameWithLoc, context) {
const emitsKind =
define.type === 'ObjectExpression' ? '`emits` option' : '`defineEmits`'
const certainEmits = emits.filter((e) => e.key)
@@ -423,18 +447,18 @@ function buildSuggest(define, emits, nameNode, context) {
{
messageId: 'addOneOption',
data: {
- name: `${nameNode.value}`,
+ name: nameWithLoc.name,
emitsKind
},
fix(fixer) {
if (last.type === 'array') {
// Array
- return fixer.insertTextAfter(last.node, `, '${nameNode.value}'`)
+ return fixer.insertTextAfter(last.node, `, '${nameWithLoc.name}'`)
} else if (last.type === 'object') {
// Object
return fixer.insertTextAfter(
last.node,
- `, '${nameNode.value}': null`
+ `, '${nameWithLoc.name}': null`
)
} else {
// type
@@ -468,11 +492,11 @@ function buildSuggest(define, emits, nameNode, context) {
return [
{
messageId: 'addOneOption',
- data: { name: `${nameNode.value}`, emitsKind },
+ data: { name: `${nameWithLoc.name}`, emitsKind },
fix(fixer) {
return fixer.insertTextAfter(
leftBracket,
- `'${nameNode.value}'${
+ `'${nameWithLoc.name}'${
emitsOptionValue.elements.length > 0 ? ',' : ''
}`
)
@@ -486,11 +510,11 @@ function buildSuggest(define, emits, nameNode, context) {
return [
{
messageId: 'addOneOption',
- data: { name: `${nameNode.value}`, emitsKind },
+ data: { name: `${nameWithLoc.name}`, emitsKind },
fix(fixer) {
return fixer.insertTextAfter(
leftBrace,
- `'${nameNode.value}': null${
+ `'${nameWithLoc.name}': null${
emitsOptionValue.properties.length > 0 ? ',' : ''
}`
)
@@ -508,12 +532,12 @@ function buildSuggest(define, emits, nameNode, context) {
return [
{
messageId: 'addArrayEmitsOption',
- data: { name: `${nameNode.value}`, emitsKind },
+ data: { name: `${nameWithLoc.name}`, emitsKind },
fix(fixer) {
if (afterOptionNode) {
return fixer.insertTextAfter(
sourceCode.getTokenBefore(afterOptionNode),
- `\nemits: ['${nameNode.value}'],`
+ `\nemits: ['${nameWithLoc.name}'],`
)
} else if (object.properties.length > 0) {
const before =
@@ -521,7 +545,7 @@ function buildSuggest(define, emits, nameNode, context) {
object.properties[object.properties.length - 1]
return fixer.insertTextAfter(
before,
- `,\nemits: ['${nameNode.value}']`
+ `,\nemits: ['${nameWithLoc.name}']`
)
} else {
const objectLeftBrace = /** @type {Token} */ (
@@ -532,7 +556,7 @@ function buildSuggest(define, emits, nameNode, context) {
)
return fixer.insertTextAfter(
objectLeftBrace,
- `\nemits: ['${nameNode.value}']${
+ `\nemits: ['${nameWithLoc.name}']${
objectLeftBrace.loc.end.line < objectRightBrace.loc.start.line
? ''
: '\n'
@@ -543,12 +567,12 @@ function buildSuggest(define, emits, nameNode, context) {
},
{
messageId: 'addObjectEmitsOption',
- data: { name: `${nameNode.value}`, emitsKind },
+ data: { name: `${nameWithLoc.name}`, emitsKind },
fix(fixer) {
if (afterOptionNode) {
return fixer.insertTextAfter(
sourceCode.getTokenBefore(afterOptionNode),
- `\nemits: {'${nameNode.value}': null},`
+ `\nemits: {'${nameWithLoc.name}': null},`
)
} else if (object.properties.length > 0) {
const before =
@@ -556,7 +580,7 @@ function buildSuggest(define, emits, nameNode, context) {
object.properties[object.properties.length - 1]
return fixer.insertTextAfter(
before,
- `,\nemits: {'${nameNode.value}': null}`
+ `,\nemits: {'${nameWithLoc.name}': null}`
)
} else {
const objectLeftBrace = /** @type {Token} */ (
@@ -567,7 +591,7 @@ function buildSuggest(define, emits, nameNode, context) {
)
return fixer.insertTextAfter(
objectLeftBrace,
- `\nemits: {'${nameNode.value}': null}${
+ `\nemits: {'${nameWithLoc.name}': null}${
objectLeftBrace.loc.end.line < objectRightBrace.loc.start.line
? ''
: '\n'
diff --git a/tests/lib/rules/custom-event-name-casing.js b/tests/lib/rules/custom-event-name-casing.js
index 8ad20377e..f8e547992 100644
--- a/tests/lib/rules/custom-event-name-casing.js
+++ b/tests/lib/rules/custom-event-name-casing.js
@@ -621,6 +621,21 @@ tester.run('custom-event-name-casing', rule, {
line: 5
}
]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message: "Custom event name 'foo-bar' must be camelCase.",
+ line: 4
+ }
+ ]
}
]
})
diff --git a/tests/lib/rules/no-restricted-custom-event.js b/tests/lib/rules/no-restricted-custom-event.js
index 447f8122c..2dc4af4d6 100644
--- a/tests/lib/rules/no-restricted-custom-event.js
+++ b/tests/lib/rules/no-restricted-custom-event.js
@@ -291,6 +291,25 @@ tester.run('no-restricted-custom-event', rule, {
line: 5
}
]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ options: ['foo'],
+ errors: [
+ {
+ message: 'Using `foo` event is not allowed.',
+ line: 5
+ }
+ ]
}
]
})
diff --git a/tests/lib/rules/require-explicit-emits.js b/tests/lib/rules/require-explicit-emits.js
index 1146cd2cc..9a22c74a9 100644
--- a/tests/lib/rules/require-explicit-emits.js
+++ b/tests/lib/rules/require-explicit-emits.js
@@ -1857,6 +1857,35 @@ emits: {'foo': null}
line: 5
}
]
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+ `,
+ errors: [
+ {
+ message:
+ 'The "bar" event has been triggered but not declared on `defineEmits`.',
+ line: 5,
+ suggestions: [
+ {
+ desc: 'Add the "bar" to `defineEmits`.',
+ output: `
+
+ `
+ }
+ ]
+ }
+ ]
}
]
})