StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
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.
254 lines
8.4 KiB
254 lines
8.4 KiB
/**
|
|
* @author Toru Nagashima
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
'use strict'
|
|
|
|
const utils = require('../utils')
|
|
|
|
/**
|
|
* Get all `v-slot` directives on a given element.
|
|
* @param {VElement} node The VElement node to check.
|
|
* @returns {VAttribute[]} The array of `v-slot` directives.
|
|
*/
|
|
function getSlotDirectivesOnElement (node) {
|
|
return node.startTag.attributes.filter(attribute =>
|
|
attribute.directive &&
|
|
attribute.key.name.name === 'slot'
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get all `v-slot` directives on the children of a given element.
|
|
* @param {VElement} node The VElement node to check.
|
|
* @returns {VAttribute[][]}
|
|
* The array of the group of `v-slot` directives.
|
|
* The group bundles `v-slot` directives of element sequence which is connected
|
|
* by `v-if`/`v-else-if`/`v-else`.
|
|
*/
|
|
function getSlotDirectivesOnChildren (node) {
|
|
return node.children
|
|
.reduce(({ groups, vIf }, childNode) => {
|
|
if (childNode.type === 'VElement') {
|
|
let connected
|
|
if (utils.hasDirective(childNode, 'if')) {
|
|
connected = false
|
|
vIf = true
|
|
} else if (utils.hasDirective(childNode, 'else-if')) {
|
|
connected = vIf
|
|
vIf = true
|
|
} else if (utils.hasDirective(childNode, 'else')) {
|
|
connected = vIf
|
|
vIf = false
|
|
} else {
|
|
connected = false
|
|
vIf = false
|
|
}
|
|
|
|
if (connected) {
|
|
groups[groups.length - 1].push(childNode)
|
|
} else {
|
|
groups.push([childNode])
|
|
}
|
|
} else if (childNode.type !== 'VText' || childNode.value.trim() !== '') {
|
|
vIf = false
|
|
}
|
|
return { groups, vIf }
|
|
}, { groups: [], vIf: false })
|
|
.groups
|
|
.map(group =>
|
|
group
|
|
.map(childElement =>
|
|
childElement.name === 'template'
|
|
? utils.getDirective(childElement, 'slot')
|
|
: null
|
|
)
|
|
.filter(Boolean)
|
|
)
|
|
.filter(group => group.length >= 1)
|
|
}
|
|
|
|
/**
|
|
* Get the normalized name of a given `v-slot` directive node.
|
|
* @param {VAttribute} node The `v-slot` directive node.
|
|
* @returns {string} The normalized name.
|
|
*/
|
|
function getNormalizedName (node, sourceCode) {
|
|
return node.key.argument == null ? 'default' : sourceCode.getText(node.key.argument)
|
|
}
|
|
|
|
/**
|
|
* Get all `v-slot` directives which are distributed to the same slot as a given `v-slot` directive node.
|
|
* @param {VAttribute[][]} vSlotGroups The result of `getAllNamedSlotElements()`.
|
|
* @param {VElement} currentVSlot The current `v-slot` directive node.
|
|
* @returns {VAttribute[][]} The array of the group of `v-slot` directives.
|
|
*/
|
|
function filterSameSlot (vSlotGroups, currentVSlot, sourceCode) {
|
|
const currentName = getNormalizedName(currentVSlot, sourceCode)
|
|
return vSlotGroups
|
|
.map(vSlots =>
|
|
vSlots.filter(vSlot => getNormalizedName(vSlot, sourceCode) === currentName)
|
|
)
|
|
.filter(slots => slots.length >= 1)
|
|
}
|
|
|
|
/**
|
|
* Check whether a given argument node is using an iteration variable that the element defined.
|
|
* @param {VExpressionContainer|VIdentifier|null} argument The argument node to check.
|
|
* @param {VElement} element The element node which has the argument.
|
|
* @returns {boolean} `true` if the argument node is using the iteration variable.
|
|
*/
|
|
function isUsingIterationVar (argument, element) {
|
|
if (argument && argument.type === 'VExpressionContainer') {
|
|
for (const { variable } of argument.references) {
|
|
if (
|
|
variable != null &&
|
|
variable.kind === 'v-for' &&
|
|
variable.id.range[0] > element.startTag.range[0] &&
|
|
variable.id.range[1] < element.startTag.range[1]
|
|
) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Check whether a given argument node is using an scope variable that the directive defined.
|
|
* @param {VAttribute} vSlot The `v-slot` directive to check.
|
|
* @returns {boolean} `true` if that argument node is using a scope variable the directive defined.
|
|
*/
|
|
function isUsingScopeVar (vSlot) {
|
|
const argument = vSlot.key.argument
|
|
const value = vSlot.value
|
|
|
|
if (argument && value && argument.type === 'VExpressionContainer') {
|
|
for (const { variable } of argument.references) {
|
|
if (
|
|
variable != null &&
|
|
variable.kind === 'scope' &&
|
|
variable.id.range[0] > value.range[0] &&
|
|
variable.id.range[1] < value.range[1]
|
|
) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'enforce valid `v-slot` directives',
|
|
category: undefined, // essential
|
|
// TODO Change with major version.
|
|
// category: 'essential',
|
|
url: 'https://eslint.vuejs.org/rules/valid-v-slot.html'
|
|
},
|
|
fixable: null,
|
|
schema: [],
|
|
messages: {
|
|
ownerMustBeCustomElement: "'v-slot' directive must be owned by a custom element, but '{{name}}' is not.",
|
|
namedSlotMustBeOnTemplate: "Named slots must use '<template>' on a custom element.",
|
|
defaultSlotMustBeOnTemplate: "Default slot must use '<template>' on a custom element when there are other named slots.",
|
|
disallowDuplicateSlotsOnElement: "An element cannot have multiple 'v-slot' directives.",
|
|
disallowDuplicateSlotsOnChildren: "An element cannot have multiple '<template>' elements which are distributed to the same slot.",
|
|
disallowArgumentUseSlotParams: "Dynamic argument of 'v-slot' directive cannot use that slot parameter.",
|
|
disallowAnyModifier: "'v-slot' directive doesn't support any modifier.",
|
|
requireAttributeValue: "'v-slot' directive on a custom element requires that attribute value."
|
|
}
|
|
},
|
|
|
|
create (context) {
|
|
const sourceCode = context.getSourceCode()
|
|
|
|
return utils.defineTemplateBodyVisitor(context, {
|
|
"VAttribute[directive=true][key.name.name='slot']" (node) {
|
|
const isDefaultSlot = node.key.argument == null || node.key.argument.name === 'default'
|
|
const element = node.parent.parent
|
|
const parentElement = element.parent
|
|
const ownerElement = element.name === 'template' ? parentElement : element
|
|
const vSlotsOnElement = getSlotDirectivesOnElement(element)
|
|
const vSlotGroupsOnChildren = getSlotDirectivesOnChildren(ownerElement)
|
|
|
|
// Verify location.
|
|
if (!utils.isCustomComponent(ownerElement)) {
|
|
context.report({
|
|
node,
|
|
messageId: 'ownerMustBeCustomElement',
|
|
data: { name: ownerElement.rawName }
|
|
})
|
|
}
|
|
if (!isDefaultSlot && element.name !== 'template') {
|
|
context.report({
|
|
node,
|
|
messageId: 'namedSlotMustBeOnTemplate'
|
|
})
|
|
}
|
|
if (ownerElement === element && vSlotGroupsOnChildren.length >= 1) {
|
|
context.report({
|
|
node,
|
|
messageId: 'defaultSlotMustBeOnTemplate'
|
|
})
|
|
}
|
|
|
|
// Verify duplication.
|
|
if (vSlotsOnElement.length >= 2 && vSlotsOnElement[0] !== node) {
|
|
// E.g., <my-component #one #two>
|
|
context.report({
|
|
node,
|
|
messageId: 'disallowDuplicateSlotsOnElement'
|
|
})
|
|
}
|
|
if (ownerElement === parentElement) {
|
|
const vSlotGroupsOfSameSlot = filterSameSlot(vSlotGroupsOnChildren, node, sourceCode)
|
|
const vFor = utils.getDirective(element, 'for')
|
|
if (
|
|
vSlotGroupsOfSameSlot.length >= 2 &&
|
|
!vSlotGroupsOfSameSlot[0].includes(node)
|
|
) {
|
|
// E.g., <template #one></template>
|
|
// <template #one></template>
|
|
context.report({
|
|
node,
|
|
messageId: 'disallowDuplicateSlotsOnChildren'
|
|
})
|
|
}
|
|
if (vFor && !isUsingIterationVar(node.key.argument, element)) {
|
|
// E.g., <template v-for="x of xs" #one></template>
|
|
context.report({
|
|
node,
|
|
messageId: 'disallowDuplicateSlotsOnChildren'
|
|
})
|
|
}
|
|
}
|
|
|
|
// Verify argument.
|
|
if (isUsingScopeVar(node)) {
|
|
context.report({
|
|
node,
|
|
messageId: 'disallowArgumentUseSlotParams'
|
|
})
|
|
}
|
|
|
|
// Verify modifiers.
|
|
if (node.key.modifiers.length >= 1) {
|
|
context.report({
|
|
node,
|
|
messageId: 'disallowAnyModifier'
|
|
})
|
|
}
|
|
|
|
// Verify value.
|
|
if (ownerElement === element && isDefaultSlot && !utils.hasAttributeValue(node)) {
|
|
context.report({
|
|
node,
|
|
messageId: 'requireAttributeValue'
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|