Socket
Socket
Sign inDemoInstall

eslint-plugin-vuetify

Package Overview
Dependencies
Maintainers
7
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-vuetify - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0-beta.0

lib/rules/no-deprecated-colors.js

14

lib/configs/base.js

@@ -1,7 +0,5 @@

'use strict'
'use strict';
module.exports = {
plugins: [
'vuetify'
],
plugins: ['vuetify'],
rules: {

@@ -11,7 +9,7 @@ 'vue/valid-v-slot': ['error', {

}],
'vuetify/no-deprecated-classes': 'error',
'vuetify/no-deprecated-colors': 'error',
'vuetify/no-deprecated-components': 'error',
'vuetify/no-deprecated-props': 'error',
'vuetify/no-deprecated-classes': 'error'
'vuetify/no-deprecated-props': 'error'
}
}
};

@@ -1,2 +0,2 @@

'use strict'
'use strict';

@@ -6,5 +6,4 @@ module.exports = {

rules: {
'vuetify/no-legacy-grid': 'error',
'vuetify/grid-unknown-attributes': 'error'
}
}
};

@@ -1,8 +0,8 @@

'use strict'
const path = require('path')
const requireindex = require('requireindex')
'use strict';
const path = require('path');
const requireindex = require('requireindex');
module.exports = {
configs: requireindex(path.join(__dirname, './configs')),
rules: requireindex(path.join(__dirname, './rules'))
}
};

@@ -1,20 +0,28 @@

'use strict'
'use strict';
const loadModule = require('../util/load-module')
const { hyphenate, classify, getAttributes } = require('../util/helpers')
const { isGridAttribute } = require('../util/grid-attributes')
const { addClass, removeAttr } = require('../util/fixers')
const {
hyphenate,
classify,
getAttributes
} = require('../util/helpers');
const {
isGridAttribute
} = require('../util/grid-attributes');
const {
addClass,
removeAttr
} = require('../util/fixers');
const {
components
} = require('vuetify');
const VGrid = {
VContainer: loadModule('vuetify/es5/components/VGrid/VContainer').default,
VRow: loadModule('vuetify/es5/components/VGrid/VRow').default,
VCol: loadModule('vuetify/es5/components/VGrid/VCol').default
}
VContainer: components.VContainer,
VRow: components.VRow,
VCol: components.VCol
};
const tags = Object.keys(VGrid).reduce((t, k) => {
t[classify(k)] = Object.keys(VGrid[k].options.props).map(p => hyphenate(p)).sort()
t[classify(k)] = Object.keys(VGrid[k].props).map(p => hyphenate(p)).sort();
return t;
}, {});
return t
}, {})
// ------------------------------------------------------------------------------

@@ -33,12 +41,12 @@ // Rule Definition

},
create (context) {
create(context) {
return context.parserServices.defineTemplateBodyVisitor({
VElement (element) {
const tag = classify(element.rawName)
if (!Object.keys(tags).includes(tag)) return
const attributes = getAttributes(element).filter(({ name }) => {
return !tags[tag].includes(name) && !isGridAttribute(tag, name)
})
VElement(element) {
const tag = classify(element.rawName);
if (!Object.keys(tags).includes(tag)) return;
const attributes = getAttributes(element).filter(({
name
}) => {
return !tags[tag].includes(name) && !isGridAttribute(tag, name);
});
if (attributes.length) {

@@ -52,19 +60,15 @@ context.report({

message: 'Attributes are no longer converted into classes',
fix (fixer) {
const fixableAttrs = attributes.map(({ node }) => node)
.filter(attr => !attr.directive)
if (!fixableAttrs.length) return
const className = fixableAttrs.map(node => node.key.rawName).join(' ')
return [
addClass(context, fixer, element, className),
...fixableAttrs.map(removeAttr.bind(this, context, fixer))
]
fix(fixer) {
const fixableAttrs = attributes.map(({
node
}) => node).filter(attr => !attr.directive);
if (!fixableAttrs.length) return;
const className = fixableAttrs.map(node => node.key.rawName).join(' ');
return [addClass(context, fixer, element, className), ...fixableAttrs.map(removeAttr.bind(this, context, fixer))];
}
})
});
}
}
})
});
}
}
};

@@ -1,52 +0,22 @@

'use strict'
'use strict';
const { classify, getAttributes } = require('../util/helpers')
const { removeAttr } = require('../util/fixers')
const { getInstalledVuetifyVersion } = require('../util/get-installed-vuetify-version')
// const spacers = {
// 0: 0,
// 1: 1,
// 2: 2,
// 3: 4,
// 4: 6,
// 5: 12
// }
/** @type {Map<RegExp, (args: string[]) => string> | Map<string, string>} */
const replacements = new Map([
// ['shrink', 'flex-grow-0'],
// ['grow', 'flex-shrink-0'],
[/^text-xs-(left|right|center|justify)$/, ([align]) => `text-${align}`],
// ['child-flex', false],
['scroll-y', 'overflow-y-auto'],
['hide-overflow', 'overflow-hidden'],
['show-overflow', 'overflow-visible'],
['no-wrap', 'text-no-wrap'],
['ellipsis', 'text-truncate'],
['left', 'float-left'],
['right', 'float-right']
// TODO: only run fixer once
// [/([mp][axytblr])-(\d)/, (type, n) => `${type}-${spacers[n]}`]
])
const replacements = new Map([[/^rounded-(r|l|tr|tl|br|bl)(.*)$/, ([side, rest]) => {
side = {
r: 'e',
l: 's',
tr: 'te',
tl: 'ts',
br: 'be',
bl: 'bs'
}[side];
return `rounded-${side}${rest}`;
}], [/^border-([rl])(.*)$/, ([side, rest]) => {
side = {
r: 'e',
l: 's'
}[side];
return `border-${side}${rest}`;
}], [/^text-xs-(left|right|center|justify)$/, ([align]) => `text-${align}`], ['scroll-y', 'overflow-y-auto'], ['hide-overflow', 'overflow-hidden'], ['show-overflow', 'overflow-visible'], ['no-wrap', 'text-no-wrap'], ['ellipsis', 'text-truncate'], ['left', 'float-left'], ['right', 'float-right'], ['display-4', 'text-h1'], ['display-3', 'text-h2'], ['display-2', 'text-h3'], ['display-1', 'text-h4'], ['headline', 'text-h5'], ['title', 'text-h6'], ['subtitle-1', 'text-subtitle-1'], ['subtitle-2', 'text-subtitle-2'], ['body-1', 'text-body-1'], ['body-2', 'text-body-2'], ['caption', 'text-caption'], ['caption', 'text-caption'], ['overline', 'text-overline']]);
if (getInstalledVuetifyVersion() >= '2.3.0') {
replacements
.set('display-4', 'text-h1')
.set('display-3', 'text-h2')
.set('display-2', 'text-h3')
.set('display-1', 'text-h4')
.set('headline', 'text-h5')
.set('title', 'text-h6')
.set('subtitle-1', 'text-subtitle-1')
.set('subtitle-2', 'text-subtitle-2')
.set('body-1', 'text-body-1')
.set('body-2', 'text-body-2')
.set('caption', 'text-caption')
.set('overline', 'text-overline')
}
// These components treat attributes like classes
const gridComponents = ['VContainer', 'VLayout', 'VFlex', 'VSpacer']
// ------------------------------------------------------------------------------

@@ -68,91 +38,30 @@ // Rule Definition

},
create (context) {
create(context) {
return context.parserServices.defineTemplateBodyVisitor({
VElement (element) {
const tag = classify(element.rawName)
if (!gridComponents.includes(tag)) return
getAttributes(element).forEach(attr => {
for (const replacer of replacements) {
if (typeof replacer[0] === 'string' && replacer[0] === attr.name) {
const replacement = replacer[1]
return context.report({
messageId: 'replacedWith',
data: {
a: attr.name,
b: replacement
},
node: attr.node,
fix (fixer) {
return fixer.replaceText(attr.node, replacement)
}
})
}
if (replacer[0] instanceof RegExp) {
const matches = (replacer[0].exec(attr.name) || []).slice(1)
const replace = replacer[1]
if (matches.length && typeof replace === 'function') {
const replacement = replace(matches)
return context.report({
messageId: 'replacedWith',
data: {
a: attr.name,
b: replacement
},
node: attr.node,
fix (fixer) {
return fixer.replaceText(attr.node, replacement)
}
})
}
}
}
// Remove <v-layout row> as it conflicts with <v-row> styles
// https://github.com/vuetifyjs/vuetify/commit/3f435b5a
if (tag === 'VLayout' && attr.name === 'row') {
return context.report({
node: attr.node,
message: `Don't use "row" on <v-layout>, see https://github.com/vuetifyjs/vuetify/commit/3f435b5a`,
fix (fixer) {
if (!attr.node.directive) return removeAttr(context, fixer, attr.node)
}
})
}
})
},
'VAttribute[key.name="class"]' (node) {
if (!node.value || !node.value.value) return
const classes = node.value.value.split(/\s+/).filter(s => !!s)
const source = context.getSourceCode()
const changed = []
'VAttribute[key.name="class"]'(node) {
if (!node.value || !node.value.value) return;
const classes = node.value.value.split(/\s+/).filter(s => !!s);
const source = context.getSourceCode();
const changed = [];
classes.forEach(className => {
for (const replacer of replacements) {
if (typeof replacer[0] === 'string' && replacer[0] === className) {
return changed.push([className, replacer[1]])
return changed.push([className, replacer[1]]);
}
if (replacer[0] instanceof RegExp) {
const matches = (replacer[0].exec(className) || []).slice(1)
const replace = replacer[1]
const matches = (replacer[0].exec(className) || []).slice(1);
const replace = replacer[1];
if (matches.length && typeof replace === 'function') {
return changed.push([className, replace(matches)])
return changed.push([className, replace(matches)]);
}
}
}
})
});
changed.forEach(change => {
const idx = node.value.value.indexOf(change[0]) + 1
const range = [
node.value.range[0] + idx,
node.value.range[0] + idx + change[0].length
]
const idx = node.value.value.indexOf(change[0]) + 1;
const range = [node.value.range[0] + idx, node.value.range[0] + idx + change[0].length];
const loc = {
start: source.getLocFromIndex(range[0]),
end: source.getLocFromIndex(range[1])
}
};
context.report({

@@ -165,10 +74,10 @@ loc,

},
fix (fixer) {
return fixer.replaceTextRange(range, change[1])
fix(fixer) {
return fixer.replaceTextRange(range, change[1]);
}
})
})
});
});
}
})
});
}
}
};

@@ -1,12 +0,13 @@

'use strict'
'use strict';
const { hyphenate, classify } = require('../util/helpers')
const { getInstalledVuetifyVersion } = require('../util/get-installed-vuetify-version')
const {
hyphenate,
classify
} = require('../util/helpers');
const replacements = {
VListTile: 'v-list-item',
VListTileAction: 'v-list-item-action',
VListTileAvatar: 'v-list-item-avatar',
VListTileAvatar: false,
VListTileActionText: 'v-list-item-action-text',
VListTileContent: 'v-list-item-content',
VListTileContent: false,
VListTileTitle: 'v-list-item-title',

@@ -16,12 +17,27 @@ VListTileSubTitle: 'v-list-item-subtitle',

VToolbarSideIcon: 'v-app-bar-nav-icon',
// Possible typos
VListItemSubTitle: 'v-list-item-subtitle',
VListTileSubtitle: 'v-list-item-subtitle'
}
VListTileSubtitle: 'v-list-item-subtitle',
VContent: 'v-main',
VBannerActions: false,
VBannerText: false,
VBottomSheet: false,
VCalendar: false,
VData: false,
VDataIterator: false,
VDataTable: false,
VDatePicker: false,
VOtpInput: false,
VOverflowBtn: false,
VPicker: false,
VSimpleCheckbox: 'v-checkbox-btn',
VSkeletonLoader: false,
VSparkline: false,
VSpeedDial: false,
VStepper: false,
VTimePicker: false,
VTreeview: false,
VVirtualScroll: false
};
if (getInstalledVuetifyVersion() >= '2.3.0') {
replacements.VContent = 'v-main'
}
// ------------------------------------------------------------------------------

@@ -44,11 +60,9 @@ // Rule Definition

},
create (context) {
create(context) {
return context.parserServices.defineTemplateBodyVisitor({
VElement (element) {
const tag = classify(element.rawName)
const tokens = context.parserServices.getTemplateBodyTokenStore()
VElement(element) {
const tag = classify(element.rawName);
const tokens = context.parserServices.getTemplateBodyTokenStore();
if (Object.prototype.hasOwnProperty.call(replacements, tag)) {
const replacement = replacements[tag]
const replacement = replacements[tag];
if (replacement) {

@@ -62,15 +76,12 @@ context.report({

},
fix (fixer) {
const open = tokens.getFirstToken(element.startTag)
const endTag = element.endTag
fix(fixer) {
const open = tokens.getFirstToken(element.startTag);
const endTag = element.endTag;
if (!endTag) {
return fixer.replaceText(open, `<${replacement}`)
return fixer.replaceText(open, `<${replacement}`);
}
const endTagOpen = tokens.getFirstToken(endTag)
return [
fixer.replaceText(open, `<${replacement}`),
fixer.replaceText(endTagOpen, `</${replacement}`)
]
const endTagOpen = tokens.getFirstToken(endTag);
return [fixer.replaceText(open, `<${replacement}`), fixer.replaceText(endTagOpen, `</${replacement}`)];
}
})
});
} else {

@@ -80,9 +91,11 @@ context.report({

messageId: 'removed',
data: { name: hyphenate(tag) }
})
data: {
name: hyphenate(tag)
}
});
}
}
}
})
});
}
}
};

@@ -1,112 +0,805 @@

'use strict'
'use strict';
const { hyphenate, classify, mergeDeep } = require('../util/helpers')
const { getInstalledVuetifyVersion } = require('../util/get-installed-vuetify-version')
const {
hyphenate,
classify
} = require('../util/helpers');
const size = {
maxHeight: false,
maxWidth: false,
minHeight: false,
minWidth: false
};
const sizes = {
large: {
name: 'size',
value: 'large'
},
medium: {
name: 'size',
value: 'medium'
},
small: {
name: 'size',
value: 'small'
},
xLarge: {
name: 'size',
value: 'x-large'
},
xSmall: {
name: 'size',
value: 'x-small'
}
};
const inputs = {
appendOuterIcon: 'append-icon',
backgroundColor: 'bg-color',
box: {
name: 'variant',
value: 'filled'
},
errorCount: 'max-errors',
filled: {
name: 'variant',
value: 'filled'
},
flat: false,
fullWidth: false,
height: false,
hideSpinButtons: false,
loaderHeight: false,
outline: {
name: 'variant',
value: 'outlined'
},
outlined: {
name: 'variant',
value: 'outlined'
},
rounded: false,
shaped: false,
solo: {
name: 'variant',
value: 'solo'
},
soloInverted: false,
success: false,
successMessages: false,
validateOnBlur: {
name: 'validate-on',
value: 'blur'
},
value: 'model-value'
};
const select = {
allowOverflow: false,
attach: {
custom: ':menu-props="{ attach: true }"'
},
autoSelectFirst: false,
cacheItems: false,
deletableChips: 'closable-chips',
disableLookup: false,
itemColor: {
custom: 'item-props.color'
},
itemDisabled: {
custom: 'item-props.disabled'
},
itemText: 'item-title',
searchInput: 'search',
smallChips: false,
valueComparator: false,
filter: 'customFilter',
...inputs
};
const theme = {
dark: false,
light: false
};
const link = {
append: false,
exactActiveClass: false,
exactPath: false,
nuxt: false
};
const overlay = {
hideOverlay: {
name: 'scrim',
value: false
},
internalActivator: false,
overlayColor: {
name: 'scrim',
value: value => value
},
overlayOpacity: false,
value: 'model-value',
returnValue: false
};
const replacements = {
VBtn: {
outline: 'outlined',
flat: 'text',
round: 'rounded'
VAppBar: {
app: false,
clippedLeft: false,
clippedRight: false,
collapseOnScroll: false,
elevateOnScroll: false,
fadeImgOnScroll: false,
fixed: false,
hideOnScroll: false,
invertedScroll: false,
outlined: 'border',
prominent: false,
scrollOffScreen: false,
scrollTarget: false,
scrollThreshold: false,
shaped: false,
short: false,
shrinkOnScroll: false,
tile: false,
width: false,
...theme,
...size
},
VAlert: {
outline: 'outlined'
border: {
name: 'border',
value: value => ({
right: 'end',
left: 'start'
})[value]
},
outline: {
name: 'variant',
value: 'outlined'
},
coloredBorder: {
custom: 'border-color'
},
dismissible: 'closable',
mode: false,
origin: false,
outlined: {
name: 'variant',
value: 'outlined'
},
shaped: false,
tile: {
name: 'rounded',
value: 0
},
transition: false,
...theme
},
VAvatar: {
height: {
custom: 'size'
},
width: {
custom: 'size'
},
left: 'start',
right: 'end',
...size
},
VBadge: {
avatar: false,
mode: false,
origin: false,
overlap: false
},
VBanner: {
app: false,
iconColor: false,
mobileBreakPoint: false,
outlined: false,
shaped: false,
value: false
},
VBottomNavigation: {
active: { custom: 'value or v-model' }
activeClass: 'selected-class',
app: false,
fixed: false,
hideOnScroll: false,
inputValue: 'model-value',
scrollTarget: false,
scrollThreshold: false,
width: false,
...size
},
VBreadcrumbs: {
large: false,
...theme
},
VBreadcrumbsItem: {
link: false,
ripple: false,
...link
},
VBtn: {
activeClass: 'selected-class',
bottom: {
name: 'location',
value: 'bottom'
},
depressed: {
name: 'variant',
value: 'depressed'
},
fab: false,
flat: {
name: 'variant',
value: 'flat'
},
inputValue: false,
left: {
name: 'location',
value: 'left'
},
link: false,
outline: {
name: 'variant',
value: 'outlined'
},
outlined: {
name: 'variant',
value: 'outlined'
},
plain: {
name: 'variant',
value: 'plain'
},
retainFocusOnClick: false,
right: {
name: 'location',
value: 'right'
},
round: 'rounded',
shaped: false,
text: {
name: 'variant',
value: 'text'
},
tile: false,
top: {
name: 'location',
value: 'top'
},
...link,
...theme,
...sizes
},
VBtnToggle: {
activeClass: 'selected-class',
backgroundColor: false,
borderless: false,
dense: {
custom: 'density'
},
shaped: false,
tile: {
name: 'rounded',
value: 0
},
value: 'model-value',
valueComparator: false,
...theme
},
VCard: {
activeClass: false,
loaderHeight: false,
outlined: {
name: 'variant',
value: 'outlined'
},
raised: {
name: 'elevation',
value: 8
},
shaped: false,
tile: {
name: 'rounded',
value: 0
},
...link
},
VCarousel: {
hideControls: { custom: ':show-arrows="false"' }
activeClass: 'selected-class',
max: false,
multiple: false,
progressColor: {
custom: 'progress="<color>"'
},
showArrowsOnHover: {
name: 'show-arrows',
value: 'hover'
},
touchless: false,
valueComparator: false,
vertical: {
name: 'direction',
value: 'vertical'
},
value: 'model-value',
...theme
},
VCarouselItem: {
activeClass: 'selected-class',
exact: false,
href: false,
link: false,
replace: false,
ripple: false,
target: false,
to: false,
...link
},
VCheckbox: {
backgroundColor: false,
errorCount: 'max-errors',
hideSpinButtons: false,
hint: false,
inputValue: 'model-value',
offIcon: 'false-icon',
onIcon: 'true-icon',
offValue: 'false-value',
onValue: 'true-value',
success: false,
successMessages: false,
validateOnBlur: {
name: 'validate-on',
value: 'blur'
}
},
VChip: {
outline: 'outlined',
selected: 'value'
active: false,
close: 'cloasable',
inputValue: 'model-value',
outline: {
name: 'variant',
value: 'outlined'
},
outlined: {
name: 'variant',
value: 'outlined'
},
selected: 'value',
textColor: false,
...link
},
VDataIterator: {
expand: 'showExpand',
contentClass: false,
contentProps: false,
contentTag: false,
disableInitialSort: 'sortBy',
filter: 'customFilter',
pagination: 'options',
totalItems: 'serverItemsLength',
hideActions: 'hideDefaultFooter',
rowsPerPageItems: { custom: 'footer-props.itemsPerPageOptions' },
rowsPerPageText: { custom: 'footer-props.itemsPerPageText' },
prevIcon: { custom: 'footer-props.prevIcon' },
nextIcon: { custom: 'footer-props.nextIcon' }
VChipGroup: {
activeClass: 'selected-class',
centerActive: false,
mobileBreakPoint: false,
nextIcon: false,
prevIcon: false,
showArrows: false,
value: 'model-value'
},
VDataTable: {
sortIcon: { custom: 'header-props.sortIcon' },
hideHeaders: 'hideDefaultHeader',
selectAll: 'showSelect'
VColorPicker: {
flat: false,
hideModeSwitch: false,
value: 'model-value'
},
VExpansionPanels: {
expand: 'multiple'
activeClass: 'selected-class',
flat: false,
focusable: false,
hover: false,
tile: false,
value: 'model-value',
valueComparator: false
},
VTextField: {
box: 'filled',
outline: 'outlined',
mask: false
...inputs
},
VTextarea: {
box: 'filled',
outline: 'outlined',
mask: false
...inputs
},
VFileInput: {
type: false,
...inputs
},
VSelect: {
box: 'filled',
combobox: { custom: '<v-combobox />' },
outline: 'outlined'
...select
},
VAutocomplete: {
box: 'filled',
outline: 'outlined'
...select
},
VCombobox: {
box: 'filled',
outline: 'outlined'
...select
},
VInput: {
...inputs
},
VDialog: {
...overlay
},
VMenu: {
allowOverflow: false,
auto: false,
bottom: {
custom: 'location and origin'
},
closeOnClick: {
name: 'persistent',
value: true
},
left: {
custom: 'location and origin'
},
nudgeBottom: {
custom: 'offset'
},
nudgeLeft: {
custom: 'offset'
},
nudgeRight: {
custom: 'offset'
},
nudgeTop: {
custom: 'offset'
},
offsetOverflow: false,
offsetX: false,
offsetY: false,
positionX: false,
positionY: false,
right: {
custom: 'location and origin'
},
rounded: false,
tile: false,
top: {
custom: 'location and origin'
},
value: 'model-value',
...overlay
},
VFooter: {
fixed: false,
outlined: false,
padless: false,
shaped: false,
tile: false,
width: false,
...size
},
VForm: {
value: 'model-value'
},
VHover: {
value: 'model-value'
},
VIcon: {
dense: false,
disabled: false,
left: 'start',
right: 'end'
},
VImg: {
contain: {
custom: 'cover'
},
contentClass: false,
height: false,
position: false,
...theme,
...size
},
VItemGroup: {
activeClass: 'selected-class',
value: 'model-value',
valueComparator: false
},
VItem: {
activeClass: 'selected-class'
},
VLazy: {
value: 'model-value'
},
VList: {
expand: false,
flat: false,
outlined: false,
subheader: false,
threeLine: {
name: 'lines',
value: 'three'
},
twoLine: {
name: 'lines',
value: 'two'
},
tile: false
},
VListGroup: {
activeClass: false,
disabled: false,
eager: false,
group: false,
noAction: false,
ripple: false,
subGroup: false
},
VListItem: {
avatar: false
append: false,
ripple: false,
selectable: {
custom: 'value'
},
threeLine: {
name: 'lines',
value: 'three'
},
twoLine: {
name: 'lines',
value: 'two'
},
inputValue: {
custom: 'active'
},
...link
},
VToolbar: {
app: { custom: '<v-app-bar app />' },
manualScroll: { custom: '<v-app-bar :value="false" />' },
clippedLeft: { custom: '<v-app-bar clipped-left />' },
clippedRight: { custom: '<v-app-bar clipped-right />' },
invertedScroll: { custom: '<v-app-bar inverted-scroll />' },
scrollOffScreen: { custom: '<v-app-bar scroll-off-screen />' },
scrollTarget: { custom: '<v-app-bar scroll-target />' },
scrollThreshold: { custom: '<v-app-bar scroll-threshold />' },
card: 'flat'
VNavigationDrawer: {
app: false,
bottom: {
name: 'location',
value: 'bottom'
},
clipped: false,
fixed: false,
height: false,
hideOverlay: {
name: 'scrim',
value: false
},
miniVariant: 'rail',
miniVariantWidth: 'rail-width',
mobileBreakPoint: false,
overlayColor: {
name: 'scrim',
value: value => value
},
overlayOpacity: false,
right: {
name: 'location',
value: 'right'
},
src: 'image',
stateless: false,
value: 'model-value'
},
VOverlay: {
color: {
name: 'scrim',
value: value => value
},
opacity: false,
value: 'model-value'
},
VPagination: {
circle: 'rounded',
value: 'model-value',
wrapperAriaLabel: 'aria-label'
},
VProgressCircular: {
button: false,
value: 'model-value'
},
VProgressLinear: {
absolute: false,
backgroundColor: 'bg-color',
backgroundOpacity: 'bg-opacity',
bottom: false,
fixed: false,
query: false,
top: false,
value: 'model-value'
},
VRadio: {
activeClass: 'false',
offIcon: 'false-icon',
onIcon: 'true-icon',
offValue: 'false-value',
onValue: 'true-value'
},
VRadioGroup: {
activeClass: false,
backgroundColor: false,
column: false,
multiple: false,
...inputs
},
VSlider: {
backgroundColor: false,
tickLabels: 'ticks',
ticks: {
custom: 'show-ticks'
},
vertical: {
name: 'direction',
value: 'vertical'
},
height: false,
loading: false,
inverseLabel: false,
...inputs,
...theme
},
VRangeSlider: {
backgroundColor: false,
tickLabels: 'ticks',
ticks: {
custom: 'show-ticks'
},
vertical: {
name: 'direction',
value: 'vertical'
},
height: false,
loading: false,
inverseLabel: false,
...inputs,
...theme
},
VRating: {
backgroundColor: false,
closeDelay: false,
halfIcon: false,
iconLabel: 'item-aria-label',
large: false,
openDelay: false,
value: 'model-value',
...sizes
},
VSheet: {
outlined: false,
shaped: false,
tile: false
},
VSlideGroup: {
activeClass: 'selected-class',
mobileBreakPoint: false,
value: 'model-value',
valueComparator: false,
...theme
},
VSnackbar: {
autoHeight: false
}
}
if (getInstalledVuetifyVersion() >= '2.3.0') {
mergeDeep(replacements, {
VBanner: {
mobileBreakPoint: 'mobileBreakpoint'
app: false,
bottom: {
name: 'location',
value: 'bottom'
},
VDataIterator: {
mobileBreakPoint: 'mobileBreakpoint'
centered: {
custom: 'location'
},
VNavigationDrawer: {
mobileBreakPoint: 'mobileBreakpoint'
elevation: false,
left: {
name: 'location',
value: 'left'
},
VSlideGroup: {
mobileBreakPoint: 'mobileBreakpoint'
outlined: {
name: 'variant',
value: 'outlined'
},
VTabs: {
mobileBreakPoint: 'mobileBreakpoint'
right: {
name: 'location',
value: 'right'
},
shaped: false,
text: false,
tile: false,
top: {
name: 'location',
value: 'top'
},
value: 'model-value'
},
VSwitch: {
...inputs
},
VSystemBar: {
app: false,
fixed: false,
lightsOut: false
},
VTabs: {
activeClass: false,
alignWithTitle: {
name: 'align-tabs',
value: 'title'
},
backgroundColor: 'bg-color',
value: 'model-value',
...theme
},
VTab: {
activeClass: 'selected-class',
link: false,
...link
},
VThemeProvider: {
root: false
},
VTimeline: {
alignTop: {
name: 'align',
value: 'top'
},
reverse: false
},
VTimelineItem: {
color: 'dot-color',
left: false,
right: false,
...theme,
...sizes
},
VToolbar: {
bottom: false,
outlined: false,
prominent: false,
shaped: false,
short: false,
src: 'image',
tile: false,
width: false,
...size
},
VToolbarItems: {
tag: false
},
VTooltip: {
allowOverflow: false,
bottom: {
custom: 'location and origin'
},
closeOnClick: {
name: 'persistent',
value: true
},
left: {
custom: 'location and origin'
},
nudgeBottom: {
custom: 'offset'
},
nudgeLeft: {
custom: 'offset'
},
nudgeRight: {
custom: 'offset'
},
nudgeTop: {
custom: 'offset'
},
positionX: false,
positionY: false,
right: {
custom: 'location and origin'
},
top: {
custom: 'location and origin'
},
value: 'model-value',
...overlay
},
VWindow: {
activeClass: 'selected-class',
showArrowsOnHover: false,
touchless: false,
value: 'model-value',
valueComparator: false,
vertical: {
name: 'direction',
value: 'vertical'
}
})
}
},
VWindowItem: {
activeClass: 'selected-class'
}
};
mergeDeep(replacements.VDataTable, replacements.VDataIterator)
// ------------------------------------------------------------------------------

@@ -129,22 +822,10 @@ // Rule Definition

},
create (context) {
create(context) {
return context.parserServices.defineTemplateBodyVisitor({
VAttribute (attr) {
if (
attr.directive &&
(attr.key.name.name !== 'bind' || !attr.key.argument)
) return
const tag = classify(attr.parent.parent.rawName)
if (!Object.keys(replacements).includes(tag)) return
const propName = attr.directive
? hyphenate(attr.key.argument.rawName)
: hyphenate(attr.key.rawName)
const propNameNode = attr.directive
? attr.key.argument
: attr
VAttribute(attr) {
if (attr.directive && (attr.key.name.name !== 'bind' || !attr.key.argument)) return;
const tag = classify(attr.parent.parent.rawName);
if (!Object.keys(replacements).includes(tag)) return;
const propName = attr.directive ? hyphenate(attr.key.argument.rawName) : hyphenate(attr.key.rawName);
const propNameNode = attr.directive ? attr.key.argument : attr;
Object.entries(replacements[tag]).forEach(([test, replace]) => {

@@ -155,5 +836,7 @@ if (hyphenate(test) === propName) {

messageId: 'removed',
data: { name: propName },
data: {
name: propName
},
node: propNameNode
})
});
} else if (typeof replace === 'string') {

@@ -167,7 +850,9 @@ context.report({

node: propNameNode,
fix (fixer) {
return fixer.replaceText(propNameNode, replace)
fix(fixer) {
return fixer.replaceText(propNameNode, replace);
}
})
} else if (typeof replace === 'object' && Object.hasOwnProperty.call(replace, 'custom')) {
});
} else if (typeof replace === 'object' && 'name' in replace && 'value' in replace) {
const value = typeof replace.value === 'function' ? replace.value(attr.value?.value) : replace.value;
if (value == null) return;
context.report({

@@ -177,12 +862,29 @@ messageId: 'replacedWith',

a: propName,
b: `${replace.name}="${value}"`
},
node: propNameNode,
fix(fixer) {
if (attr.directive) {
const expression = attr.value.expression.raw;
return [fixer.replaceText(propNameNode, replace.name), fixer.replaceText(attr.value, `"${expression} && '${value}'"`)];
} else {
return fixer.replaceText(attr, `${replace.name}="${value}"`);
}
}
});
} else if (typeof replace === 'object' && 'custom' in replace) {
context.report({
messageId: 'replacedWith',
data: {
a: propName,
b: replace.custom
},
node: propNameNode
})
});
}
}
})
});
}
})
});
}
}
};

@@ -1,30 +0,26 @@

function addClass (context, fixer, element, className) {
const classNode = element.startTag.attributes.find(attr => attr.key.name === 'class')
"use strict";
function addClass(context, fixer, element, className) {
const classNode = element.startTag.attributes.find(attr => attr.key.name === 'class');
if (classNode && classNode.value) {
// class=""
return fixer.replaceText(classNode.value, `"${classNode.value.value} ${className}"`)
return fixer.replaceText(classNode.value, `"${classNode.value.value} ${className}"`);
} else if (classNode) {
// class
return fixer.insertTextAfter(classNode, `="${className}"`)
return fixer.insertTextAfter(classNode, `="${className}"`);
} else {
// nothing
return fixer.insertTextAfter(
context.parserServices.getTemplateBodyTokenStore().getFirstToken(element.startTag),
` class="${className}"`
)
return fixer.insertTextAfter(context.parserServices.getTemplateBodyTokenStore().getFirstToken(element.startTag), ` class="${className}"`);
}
}
function removeAttr (context, fixer, node) {
const source = context.getSourceCode().text
let [start, end] = node.range
function removeAttr(context, fixer, node) {
const source = context.getSourceCode().text;
let [start, end] = node.range;
// Remove extra whitespace before attributes
start -= /\s*$/g.exec(source.substring(0, start))[0].length
return fixer.removeRange([start, end])
start -= /\s*$/g.exec(source.substring(0, start))[0].length;
return fixer.removeRange([start, end]);
}
module.exports = {
addClass,
removeAttr
}
};

@@ -1,28 +0,15 @@

const Module = require('module')
const path = require('path')
"use strict";
// https://github.com/benmosher/eslint-plugin-import/pull/1591
// https://github.com/benmosher/eslint-plugin-import/pull/1602
// Polyfill Node's `Module.createRequireFromPath` if not present (added in Node v10.12.0)
// Use `Module.createRequire` if available (added in Node v12.2.0)
// eslint-disable-next-line node/no-deprecated-api
const createRequire = Module.createRequire || Module.createRequireFromPath || function (filename) {
const mod = new Module(filename, null)
mod.filename = filename
mod.paths = Module._nodeModulePaths(path.dirname(filename))
mod._compile(`module.exports = require;`, filename)
return mod.exports
}
function getInstalledVuetifyVersion () {
const {
createRequire
} = require('module');
const path = require('path');
function getInstalledVuetifyVersion() {
try {
const installedVuetify = createRequire(path.resolve(process.cwd(), 'package.json'))('vuetify/package.json')
return installedVuetify.version
const installedVuetify = createRequire(path.resolve(process.cwd(), 'package.json'))('vuetify/package.json');
return installedVuetify.version;
} catch (e) {}
}
module.exports = {
getInstalledVuetifyVersion
}
};

@@ -1,7 +0,6 @@

const alignmentClasses = [
/^align-(content-)?(start|baseline|center|end|space-around|space-between)$/,
/^justify-(start|center|end|space-around|space-between)$/,
/^justify-between$/ // No idea where this was from or if it's a typo, but it's in the docs
]
"use strict";
const alignmentClasses = [/^align-(content-)?(start|baseline|center|end|space-around|space-between)$/, /^justify-(start|center|end|space-around|space-between)$/, /^justify-between$/ // No idea where this was from or if it's a typo, but it's in the docs
];
// These attributes have alternative props, so shouldn't be turned into classes by the fixer

@@ -11,18 +10,11 @@ const noFix = {

VRow: [...alignmentClasses, 'row', 'column', 'reverse', 'wrap'],
VCol: [
/^align-self-(start|baseline|center|end)$/,
/^offset-(xs|sm|md|lg|xl)\d{1,2}$/,
/^order-(xs|sm|md|lg|xl)\d{1,2}$/,
/^(xs|sm|md|lg|xl)\d{1,2}$/
]
}
function isGridAttribute (tag, name) {
VCol: [/^align-self-(start|baseline|center|end)$/, /^offset-(xs|sm|md|lg|xl)\d{1,2}$/, /^order-(xs|sm|md|lg|xl)\d{1,2}$/, /^(xs|sm|md|lg|xl)\d{1,2}$/]
};
function isGridAttribute(tag, name) {
return noFix[tag] && noFix[tag].some(match => {
return match instanceof RegExp ? match.test(name) : name === match
})
return match instanceof RegExp ? match.test(name) : name === match;
});
}
module.exports = {
isGridAttribute
}
};

@@ -1,67 +0,44 @@

'use strict'
'use strict';
function hyphenate (
/* istanbul ignore next */
str = ''
) {
return str.replace(/\B([A-Z])/g, '-$1').toLowerCase()
function hyphenate( /* istanbul ignore next */
str = '') {
return str.replace(/\B([A-Z])/g, '-$1').toLowerCase();
}
function classify (str) {
return str
.replace(/(?:^|[-_])(\w)/g, c => c.toUpperCase())
.replace(/[-_]/g, '')
function classify(str) {
return str.replace(/(?:^|[-_])(\w)/g, c => c.toUpperCase()).replace(/[-_]/g, '');
}
const specialAttrs = [
'style', 'class', 'id',
'contenteditable', 'draggable', 'spellcheck',
'key', 'ref', 'slot', 'is', 'slot-scope'
]
function isBuiltinAttribute (name) {
return specialAttrs.includes(name) ||
name.startsWith('data-') ||
name.startsWith('aria-')
const specialAttrs = ['style', 'class', 'id', 'contenteditable', 'draggable', 'spellcheck', 'key', 'ref', 'slot', 'is', 'slot-scope'];
function isBuiltinAttribute(name) {
return specialAttrs.includes(name) || name.startsWith('data-') || name.startsWith('aria-');
}
function getAttributes (element) {
const attrs = []
function getAttributes(element) {
const attrs = [];
element.startTag.attributes.forEach(node => {
if (node.directive && (node.key.name.name !== 'bind' || !node.key.argument)) return
const name = hyphenate(node.directive ? node.key.argument.name : node.key.rawName)
if (!isBuiltinAttribute(name)) attrs.push({ name, node })
})
return attrs
if (node.directive && (node.key.name.name !== 'bind' || !node.key.argument)) return;
const name = hyphenate(node.directive ? node.key.argument.name : node.key.rawName);
if (!isBuiltinAttribute(name)) attrs.push({
name,
node
});
});
return attrs;
}
function isObject (obj) {
return obj !== null && typeof obj === 'object'
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
function mergeDeep (source, target) {
function mergeDeep(source, target) {
for (const key in target) {
const sourceProperty = source[key]
const targetProperty = target[key]
const sourceProperty = source[key];
const targetProperty = target[key];
// Only continue deep merging if
// both properties are objects
if (
isObject(sourceProperty) &&
isObject(targetProperty)
) {
source[key] = mergeDeep(sourceProperty, targetProperty)
continue
if (isObject(sourceProperty) && isObject(targetProperty)) {
source[key] = mergeDeep(sourceProperty, targetProperty);
continue;
}
source[key] = targetProperty
source[key] = targetProperty;
}
return source
return source;
}
module.exports = {

@@ -74,2 +51,2 @@ hyphenate,

mergeDeep
}
};
{
"name": "eslint-plugin-vuetify",
"version": "1.1.0",
"version": "2.0.0-beta.0",
"description": "An eslint plugin for Vuetify",

@@ -10,6 +10,8 @@ "main": "lib/index.js",

"scripts": {
"build": "rimraf lib && babel src --out-dir lib",
"test": "mocha tests --recursive --reporter dot",
"test:coverage": "nyc mocha tests --recursive --reporter dot",
"test:ci": "nyc --reporter=lcov mocha tests --recursive --reporter dot",
"lint": "eslint lib tests"
"lint": "eslint src tests",
"prepublishOnly": "npm run build"
},

@@ -21,21 +23,25 @@ "files": [

"dependencies": {
"eslint-plugin-vue": "^7.0.0",
"eslint-plugin-vue": "^9.6.0",
"requireindex": "^1.2.0"
},
"devDependencies": {
"eslint": "^8.0.1",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"husky": "^6.0.0",
"mocha": "^9.1.3",
"@babel/cli": "^7.19.3",
"@babel/core": "^7.19.6",
"@babel/preset-env": "^7.19.4",
"eslint": "^8.26.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.3.0",
"eslint-plugin-promise": "^6.1.1",
"husky": "^8.0.1",
"mocha": "^10.1.0",
"nyc": "^15.1.0",
"vue": "^2.6.14",
"vuetify": "^2.5.10"
"rimraf": "^3.0.2",
"vue": "^3.2.41",
"vuetify": "^3.0.0-beta.15"
},
"peerDependencies": {
"eslint": "^6.2.0 || ^7.0.0 || ^8.0.0",
"vuetify": "^2.0.0"
"eslint": "^8.0.0",
"vuetify": "^3.0.0"
}
}
# eslint-plugin-vuetify
> An eslint plugin for Vuetify.
> Built for https://github.com/vuetifyjs/vuetify/pull/7327, requires vuetify >=2.0.0

@@ -48,2 +46,3 @@ <br>

- Prevent the use of classes that have been removed from Vuetify ([`no-deprecated-classes`])
- Prevent the use of the old theme class syntax ([`no-deprecated-colors`])

@@ -54,7 +53,5 @@ ### Grid system

- Prevent the use of legacy grid components and props ([`no-legacy-grid`])
- Warn about unknown attributes not being converted to classes on new grid components ([`grid-unknown-attributes`])
[`no-legacy-grid`]: ./docs/rules/no-legacy-grid.md
[`grid-unknown-attributes`]: ./docs/rules/grid-unknown-attributes.md

@@ -64,2 +61,3 @@ [`no-deprecated-components`]: ./docs/rules/no-deprecated-components.md

[`no-deprecated-classes`]: ./docs/rules/no-deprecated-classes.md
[`no-deprecated-colors`]: ./docs/rules/no-deprecated-colors.md

@@ -66,0 +64,0 @@

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