Skip to content

Commit

Permalink
feat: add conflict-classes
Browse files Browse the repository at this point in the history
  • Loading branch information
DanSnow committed Jun 30, 2021
1 parent cd9d539 commit 1dbf70a
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 0 deletions.
23 changes: 23 additions & 0 deletions docs/rules/conflict-classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# warning for using tailwindcss class which may conflict with our props

## Fail

```vue
<template>
<TextElement class="text-sm" />
<TextElement class="underline font-bold" />
<TextInput class="md:underline sm:font-bold lg:uppercase" />
</template>
```

## Pass

```vue
<template>
<TextElement class="foo bar" />
<TextElement class="m-1 p-1" />
<TextInput class="md:m-1 lg:p-1" />
<!-- This rule only checks TextElement and TextInput -->
<p class="font-sm" />
</template>
```
1 change: 1 addition & 0 deletions lib/configs/recommended.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ module.exports = {
'@storipress/block/link-element-no-literal-href': 'warn',
'@storipress/block/image-with-literal-src': 'warn',
'@storipress/block/seo-tags': 'warn',
'@storipress/block/conflict-classes': 'warn',
},
}
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'image-with-literal-src': require('./rules/image-with-literal-src'),
'image-without-src': require('./rules/image-without-src'),
'seo-tags': require('./rules/seo-tags'),
'conflict-classes': require('./rules/conflict-classes'),
},
configs: {
recommended: require('./configs/recommended'),
Expand Down
64 changes: 64 additions & 0 deletions lib/rules/conflict-classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const CONFLICT_PATTERN = [/text-.*/, /leading-.*/, /font-.*/]
const CONFLICT_NAMES = new Set(['uppercase', 'lowercase', 'capitalize', 'underline'])

module.exports = {
meta: {
type: 'problem',

docs: {
description: 'checking class which may conflict with props',
category: 'Possible Errors',
recommended: true,
},
messages: {
conflictClass: 'found class `{{ name }}` may conflict with prop',
},
schema: [], // no options
},
create(context) {
const sourceCode = context.getSourceCode()

function checkClasses(node) {
const { rawName } = node.parent.parent
// only check our elements
if (rawName !== 'TextInput' && rawName !== 'TextElement') {
return
}
const start = node.value.range[0]
const { value } = node.value
const classes = value.split(' ').map((raw) => {
const parts = raw.split(':')
return {
raw,
className: parts[parts.length - 1],
}
})

for (const { raw, className } of classes) {
if (CONFLICT_PATTERN.some((pattern) => className.match(pattern))) {
reportValue(value, raw)
continue
}
if (CONFLICT_NAMES.has(className)) {
reportValue(value, raw)
}
}

function reportValue(fullClass, className) {
const pos = fullClass.indexOf(className)
context.report({
loc: {
start: sourceCode.getLocFromIndex(start + pos),
end: sourceCode.getLocFromIndex(start + pos + className.length),
},
messageId: 'conflictClass',
data: { name: className },
})
}
}

return context.parserServices.defineTemplateBodyVisitor({
'VAttribute[directive=false][key.name=class]': checkClasses,
})
},
}
75 changes: 75 additions & 0 deletions tests/rules/confict-classes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const { ruleTester } = require('../_helper')
const rule = require('../../lib/rules/conflict-classes')

ruleTester.run('conflict-classes', rule, {
valid: [
{
code: `
<template>
<TextElement class="foo bar" />
</template>
`,
filename: 'test.vue',
},
{
code: `
<template>
<TextElement class="p-1 m-1" />
</template>
`,
filename: 'test.vue',
},
{
code: `
<template>
<TextInput class="lg:p-1 md:m-1" />
</template>
`,
filename: 'test.vue',
},
{
code: `
<template>
<p class="font-bold" />
</template>
`,
filename: 'test.vue',
},
],
invalid: [
{
code: `
<template>
<TextElement class="text-sm" />
</template>
`,
filename: 'test.vue',
errors: [{ messageId: 'conflictClass', data: { name: 'text-sm' } }],
},
{
code: `
<template>
<TextElement class="underline font-bold" />
</template>
`,
filename: 'test.vue',
errors: [
{ messageId: 'conflictClass', data: { name: 'underline' } },
{ messageId: 'conflictClass', data: { name: 'font-bold' } },
],
},
{
code: `
<template>
<TextInput class="md:underline sm:font-bold lg:uppercase" />
</template>
`,
filename: 'test.vue',
errors: [
{ messageId: 'conflictClass', data: { name: 'md:underline' } },
{ messageId: 'conflictClass', data: { name: 'sm:font-bold' } },
{ messageId: 'conflictClass', data: { name: 'lg:uppercase' } },
],
},
],
})

0 comments on commit 1dbf70a

Please sign in to comment.