Skip to content

Commit

Permalink
feat: make rule compatible with options api
Browse files Browse the repository at this point in the history
  • Loading branch information
Mussin Benarbia committed Jan 12, 2024
1 parent 8c0cdba commit 63c62ab
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 15 deletions.
69 changes: 62 additions & 7 deletions lib/rules/require-explicit-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

const utils = require('../utils')

/**
* @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
*/

module.exports = {
meta: {
type: 'problem',
Expand All @@ -17,26 +21,77 @@ module.exports = {
fixable: null,
schema: [],
messages: {
requireExplicitSlots:
'Slots must be explicitly defined with the defineSlots macro.',
requireExplicitSlots: 'Slots must be explicitly defined.',
alreadyDefinedSlot: 'Slot {{slotName}} is already defined.'
}
},
/** @param {RuleContext} context */
create(context) {
const sourceCode = context.getSourceCode()
const documentFragment =
sourceCode.parserServices.getDocumentFragment &&
sourceCode.parserServices.getDocumentFragment()
if (!documentFragment) {
return {}
}
const scripts = documentFragment.children.filter(
(element) => utils.isVElement(element) && element.name === 'script'
)
if (scripts.every((script) => !utils.hasAttribute(script, 'lang', 'ts'))) {
return {}
}
const slotsDefined = new Set()

return utils.compositingVisitors(
utils.defineScriptSetupVisitor(context, {
onDefineSlotsEnter(node) {
const typeArgument = node.typeArguments.params[0]
const slotsDefinitions = typeArgument.members
const typeArguments = node.typeArguments
const param = /** @type {TypeNode|undefined} */ (
typeArguments?.params[0]
)
if (!param) return

if (param.type === 'TSTypeLiteral') {
for (const memberNode of param.members) {
const slotName = memberNode.key.name
if (slotsDefined.has(slotName)) {
context.report({
node: memberNode,
messageId: 'alreadyDefinedSlot',
data: {
slotName
}
})
} else {
slotsDefined.add(slotName)
}
}
}
}
}),
utils.executeOnVue(context, (obj) => {
const slotsProperty = obj.properties.find(
(property) => property.key.name === 'slots'
)
if (!slotsProperty) return

const slotsTypeHelper =
slotsProperty.value.typeAnnotation.typeName.name === 'SlotsType' &&
slotsProperty.value.typeAnnotation
if (!slotsTypeHelper) return

const typeArguments = slotsTypeHelper.typeArguments
const param = /** @type {TypeNode|undefined} */ (
typeArguments?.params[0]
)
if (!param) return

for (const slotDefinition of slotsDefinitions) {
const slotName = slotDefinition.key.name
if (param.type === 'TSTypeLiteral') {
for (const memberNode of param.members) {
const slotName = memberNode.key.name
if (slotsDefined.has(slotName)) {
context.report({
node: slotDefinition,
node: memberNode,
messageId: 'alreadyDefinedSlot',
data: {
slotName
Expand Down
105 changes: 97 additions & 8 deletions tests/lib/rules/require-explicit-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,76 @@ tester.run('require-explicit-slots', rule, {
foo(props: { msg: string }): any
}>()
</script>`
},

{
filename: 'test.vue',
code: `
<template>
<div>
<slot />
</div>
</template>
<script lang="ts">
import { SlotsType } from 'vue'
defineComponent({
slots: Object as SlotsType<{
default: { msg: string }
}>,
})
</script>`
},
{
filename: 'test.vue',
code: `
<template>
<div>
<slot></slot>
</div>
</template>
<script lang="ts">
import { SlotsType } from 'vue'
defineComponent({
slots: Object as SlotsType<{
default: { msg: string }
}>,
})
</script>`
},
{
filename: 'test.vue',
code: `
<template>
<div>
<slot name="foo"></slot>
</div>
</template>
<script lang="ts">
import { SlotsType } from 'vue'
defineComponent({
slots: Object as SlotsType<{
foo(props: { msg: string }): any
}>,
})
</script>`
},
// does not report any error if the script is not TS
{
filename: 'test.vue',
code: `
<template>
<div>
<slot name="foo"></slot>
</div>
</template>
<script setup>
</script>`,
parserOptions: {
parser: null
}
}
],
invalid: [
Expand All @@ -74,8 +144,7 @@ tester.run('require-explicit-slots', rule, {
</script>`,
errors: [
{
message:
'Slots must be explicitly defined with the defineSlots macro.'
message: 'Slots must be explicitly defined.'
}
]
},
Expand All @@ -91,8 +160,7 @@ tester.run('require-explicit-slots', rule, {
</script>`,
errors: [
{
message:
'Slots must be explicitly defined with the defineSlots macro.'
message: 'Slots must be explicitly defined.'
}
]
},
Expand All @@ -108,8 +176,7 @@ tester.run('require-explicit-slots', rule, {
</script>`,
errors: [
{
message:
'Slots must be explicitly defined with the defineSlots macro.'
message: 'Slots must be explicitly defined.'
}
]
},
Expand All @@ -128,8 +195,30 @@ tester.run('require-explicit-slots', rule, {
</script>`,
errors: [
{
message:
'Slots must be explicitly defined with the defineSlots macro.'
message: 'Slots must be explicitly defined.'
}
]
},
{
filename: 'test.vue',
code: `
<template>
<div>
<slot name="foo" />
</div>
</template>
<script lang="ts">
import { SlotsType } from 'vue'
defineComponent({
slots: Object as SlotsType<{
default: { msg: string }
}>,
})
</script>`,
errors: [
{
message: 'Slots must be explicitly defined.'
}
]
},
Expand Down

0 comments on commit 63c62ab

Please sign in to comment.