Skip to content

Commit cdcf75e

Browse files
Improved rule list doc (vuejs#1860)
* Improved rule list doc * update markdownlint config * Update tools/update-docs-rules-index.js Co-authored-by: Flo Edelmann <[email protected]> * Update docs/.vuepress/components/rules-table.vue Co-authored-by: Flo Edelmann <[email protected]> * Update docs/.vuepress/components/rules-table.vue Co-authored-by: Flo Edelmann <[email protected]> * Update tools/update-docs-rules-index.js Co-authored-by: Flo Edelmann <[email protected]> * Update tools/update-docs-rules-index.js Co-authored-by: Flo Edelmann <[email protected]> * Update tools/lib/utils.js Co-authored-by: Flo Edelmann <[email protected]> * Update tools/lib/utils.js Co-authored-by: Flo Edelmann <[email protected]> * Update tools/update-docs-rules-index.js Co-authored-by: Flo Edelmann <[email protected]> * update * update markdownlint config Co-authored-by: Flo Edelmann <[email protected]>
1 parent c43e04f commit cdcf75e

File tree

8 files changed

+543
-435
lines changed

8 files changed

+543
-435
lines changed

.markdownlint.yml

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ no-inline-html:
55
- badge
66
- eslint-code-block
77
- sup
8+
- rules-table
9+
- span
810

911
# enforce consistency
1012
code-block-style:
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<template>
2+
<div>
3+
<div v-if="kindMarks.length > 1" class="filter-tool">
4+
Highlight:
5+
<label v-for="kindMark in kindMarks" :key="kindMark">
6+
<input type="checkbox" :value="kindMark" v-model="checkedKindMarks" />
7+
<span class="emoji">{{ kindMark }}</span>
8+
</label>
9+
</div>
10+
<div class="table-root" ref="tableRoot">
11+
<slot />
12+
</div>
13+
</div>
14+
</template>
15+
16+
<script>
17+
const emojis = ['3️⃣', '2️⃣', '⚠️']
18+
19+
export default {
20+
data() {
21+
return {
22+
kindMarks: [],
23+
checkedKindMarks: []
24+
}
25+
},
26+
mounted() {
27+
this.setup()
28+
},
29+
watch: {
30+
checkedKindMarks: {
31+
handler: 'filterTable',
32+
deep: true
33+
}
34+
},
35+
methods: {
36+
setup() {
37+
const kindMarks = new Set()
38+
const table = this.getTable()
39+
for (const row of table.rows) {
40+
row.cells[3].classList.add('emoji')
41+
const trimmed = row.cells[3].textContent.trim()
42+
if (!trimmed) {
43+
continue
44+
}
45+
const chars =
46+
trimmed.match(new RegExp(`${emojis.join('|')}|.`, 'ug')) || []
47+
for (const mark of chars) {
48+
kindMarks.add(mark)
49+
}
50+
}
51+
this.kindMarks = [...kindMarks]
52+
},
53+
getTable() {
54+
return this.$refs.tableRoot.querySelector('table')
55+
},
56+
filterTable() {
57+
const table = this.getTable()
58+
if (this.checkedKindMarks.length === 0) {
59+
table.classList.remove('highlighting')
60+
return
61+
}
62+
table.classList.add('highlighting')
63+
for (const row of table.rows) {
64+
if (row.cells[0].tagName === 'TH') {
65+
row.classList.add('highlight')
66+
continue
67+
}
68+
const rowMarks = row.cells[3].textContent
69+
const highlight = this.checkedKindMarks.every((mark) =>
70+
rowMarks.includes(mark)
71+
)
72+
row.classList.toggle('highlight', highlight)
73+
}
74+
}
75+
}
76+
}
77+
</script>
78+
79+
<style scoped>
80+
.table-root ::v-deep .highlighting tr {
81+
opacity: 0.3;
82+
}
83+
.table-root ::v-deep .highlighting .highlight {
84+
opacity: 1;
85+
}
86+
.filter-tool label {
87+
display: inline-flex;
88+
margin-right: 0.5ex;
89+
}
90+
</style>

docs/.vuepress/styles/index.styl

+6
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,9 @@
1818
}
1919
}
2020
}
21+
22+
.theme-container.rule-list .theme-default-content
23+
max-width: 1280px;
24+
25+
.emoji
26+
font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Apple Color Emoji", "Noto Color Emoji", "Noto Emoji", sans-serif

docs/rules/README.md

+272-373
Large diffs are not rendered by default.

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@
8181
"eslint-plugin-unicorn": "^40.1.0",
8282
"eslint-plugin-vue": "file:.",
8383
"espree": "^9.0.0",
84-
"lodash": "^4.17.21",
8584
"markdownlint-cli": "^0.31.1",
8685
"mocha": "^7.1.2",
8786
"nyc": "^15.1.0",

tools/lib/utils.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
module.exports = { getPresetIds, formatItems }
2+
3+
const presetCategories = {
4+
base: null,
5+
essential: 'base',
6+
'vue3-essential': 'base',
7+
'strongly-recommended': 'essential',
8+
'vue3-strongly-recommended': 'vue3-essential',
9+
recommended: 'strongly-recommended',
10+
'vue3-recommended': 'vue3-strongly-recommended'
11+
// 'use-with-caution': 'recommended',
12+
// 'vue3-use-with-caution': 'vue3-recommended'
13+
}
14+
15+
function formatItems(items, suffix) {
16+
if (items.length === 1) {
17+
return `${items[0]}${suffix ? ` ${suffix[0]}` : ''}`
18+
}
19+
if (items.length === 2) {
20+
return `${items.join(' and ')}${suffix ? ` ${suffix[1]}` : ''}`
21+
}
22+
return `all of ${items.slice(0, -1).join(', ')} and ${[...items].pop()}${
23+
suffix ? ` ${suffix[1]}` : ''
24+
}`
25+
}
26+
27+
function getPresetIds(categoryIds) {
28+
const subsetCategoryIds = []
29+
for (const categoryId of categoryIds) {
30+
for (const [subsetCategoryId, supersetCategoryId] of Object.entries(
31+
presetCategories
32+
)) {
33+
if (supersetCategoryId === categoryId) {
34+
subsetCategoryIds.push(subsetCategoryId)
35+
}
36+
}
37+
}
38+
if (subsetCategoryIds.length === 0) {
39+
return categoryIds
40+
}
41+
return [...new Set([...categoryIds, ...getPresetIds(subsetCategoryIds)])]
42+
}

tools/update-docs-rules-index.js

+130-26
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@
77
const fs = require('fs')
88
const path = require('path')
99
const rules = require('./lib/rules')
10-
const categories = require('./lib/categories')
10+
const { getPresetIds, formatItems } = require('./lib/utils')
11+
12+
const VUE3_EMOJI = ':three:'
13+
const VUE2_EMOJI = ':two:'
1114

1215
// -----------------------------------------------------------------------------
16+
const categorizedRules = rules.filter(
17+
(rule) =>
18+
rule.meta.docs.categories &&
19+
!rule.meta.docs.extensionRule &&
20+
!rule.meta.deprecated
21+
)
1322
const uncategorizedRules = rules.filter(
1423
(rule) =>
1524
!rule.meta.docs.categories &&
@@ -24,16 +33,23 @@ const uncategorizedExtensionRule = rules.filter(
2433
)
2534
const deprecatedRules = rules.filter((rule) => rule.meta.deprecated)
2635

27-
function toRuleRow(rule) {
36+
const TYPE_MARK = {
37+
problem: ':warning:',
38+
suggestion: ':hammer:',
39+
layout: ':lipstick:'
40+
}
41+
42+
function toRuleRow(rule, kindMarks = []) {
2843
const mark = [
2944
rule.meta.fixable ? ':wrench:' : '',
3045
rule.meta.hasSuggestions ? ':bulb:' : '',
3146
rule.meta.deprecated ? ':warning:' : ''
3247
].join('')
48+
const kindMark = [...kindMarks, TYPE_MARK[rule.meta.type]].join('')
3349
const link = `[${rule.ruleId}](./${rule.name}.md)`
3450
const description = rule.meta.docs.description || '(no description)'
3551

36-
return `| ${link} | ${description} | ${mark} |`
52+
return `| ${link} | ${description} | ${mark} | ${kindMark} |`
3753
}
3854

3955
function toDeprecatedRuleRow(rule) {
@@ -46,25 +62,97 @@ function toDeprecatedRuleRow(rule) {
4662
return `| ${link} | ${replacedBy || '(no replacement)'} |`
4763
}
4864

49-
// -----------------------------------------------------------------------------
50-
let rulesTableContent = categories
51-
.map(
52-
(category) => `
53-
## ${category.title.vuepress}
54-
55-
Enforce all the rules in this category, as well as all higher priority rules, with:
65+
const categoryGroups = [
66+
{
67+
title: 'Base Rules (Enabling Correct ESLint Parsing)',
68+
description:
69+
'Rules in this category are enabled for all presets provided by eslint-plugin-vue.',
70+
categoryIdForVue3: 'base',
71+
categoryIdForVue2: 'base',
72+
useMark: false
73+
},
74+
{
75+
title: 'Priority A: Essential (Error Prevention)',
76+
categoryIdForVue3: 'vue3-essential',
77+
categoryIdForVue2: 'essential',
78+
useMark: true
79+
},
80+
{
81+
title: 'Priority B: Strongly Recommended (Improving Readability)',
82+
categoryIdForVue3: 'vue3-strongly-recommended',
83+
categoryIdForVue2: 'strongly-recommended',
84+
useMark: true
85+
},
86+
{
87+
title: 'Priority C: Recommended (Potentially Dangerous Patterns)',
88+
categoryIdForVue3: 'vue3-recommended',
89+
categoryIdForVue2: 'recommended',
90+
useMark: true
91+
}
92+
]
5693

57-
\`\`\`json
58-
{
59-
"extends": "plugin:vue/${category.categoryId}"
60-
}
61-
\`\`\`
94+
// -----------------------------------------------------------------------------
95+
let rulesTableContent = categoryGroups
96+
.map((group) => {
97+
const rules = categorizedRules.filter(
98+
(rule) =>
99+
rule.meta.docs.categories.includes(group.categoryIdForVue3) ||
100+
rule.meta.docs.categories.includes(group.categoryIdForVue2)
101+
)
102+
let content = `
103+
## ${group.title}
104+
`
62105

63-
| Rule ID | Description | |
64-
|:--------|:------------|:---|
65-
${category.rules.map(toRuleRow).join('\n')}
106+
if (group.description) {
107+
content += `
108+
${group.description}
109+
`
110+
}
111+
if (group.useMark) {
112+
const presetsForVue3 = getPresetIds([group.categoryIdForVue3]).map(
113+
(categoryId) => `\`"plugin:vue/${categoryId}"\``
114+
)
115+
const presetsForVue2 = getPresetIds([group.categoryIdForVue2]).map(
116+
(categoryId) => `\`"plugin:vue/${categoryId}"\``
117+
)
118+
content += `
119+
- ${VUE3_EMOJI} Indicates that the rule is for Vue 3 and is included in ${formatItems(
120+
presetsForVue3,
121+
['preset', 'presets']
122+
)}.
123+
- ${VUE2_EMOJI} Indicates that the rule is for Vue 2 and is included in ${formatItems(
124+
presetsForVue2,
125+
['preset', 'presets']
126+
)}.
66127
`
67-
)
128+
}
129+
130+
content += `
131+
<rules-table>
132+
133+
| Rule ID | Description | | |
134+
|:--------|:------------|:--:|:--:|
135+
${rules
136+
.map((rule) => {
137+
const mark = group.useMark
138+
? [
139+
rule.meta.docs.categories.includes(group.categoryIdForVue3)
140+
? [VUE3_EMOJI]
141+
: [],
142+
rule.meta.docs.categories.includes(group.categoryIdForVue2)
143+
? [VUE2_EMOJI]
144+
: []
145+
].flat()
146+
: []
147+
return toRuleRow(rule, mark)
148+
})
149+
.join('\n')}
150+
151+
</rules-table>
152+
`
153+
154+
return content
155+
})
68156
.join('')
69157

70158
// -----------------------------------------------------------------------------
@@ -90,9 +178,13 @@ For example:
90178
}
91179
if (uncategorizedRules.length > 0) {
92180
rulesTableContent += `
93-
| Rule ID | Description | |
94-
|:--------|:------------|:---|
95-
${uncategorizedRules.map(toRuleRow).join('\n')}
181+
<rules-table>
182+
183+
| Rule ID | Description | | |
184+
|:--------|:------------|:--:|:--:|
185+
${uncategorizedRules.map((rule) => toRuleRow(rule)).join('\n')}
186+
187+
</rules-table>
96188
`
97189
}
98190
if (uncategorizedExtensionRule.length > 0) {
@@ -101,9 +193,13 @@ if (uncategorizedExtensionRule.length > 0) {
101193
102194
The following rules extend the rules provided by ESLint itself and apply them to the expressions in the \`<template>\`.
103195
104-
| Rule ID | Description | |
105-
|:--------|:------------|:---|
106-
${uncategorizedExtensionRule.map(toRuleRow).join('\n')}
196+
<rules-table>
197+
198+
| Rule ID | Description | | |
199+
|:--------|:------------|:--:|:--:|
200+
${uncategorizedExtensionRule.map((rule) => toRuleRow(rule)).join('\n')}
201+
202+
</rules-table>
107203
`
108204
}
109205

@@ -127,6 +223,7 @@ fs.writeFileSync(
127223
readmeFilePath,
128224
`---
129225
sidebarDepth: 0
226+
pageClass: rule-list
130227
---
131228
132229
# Available rules
@@ -139,5 +236,12 @@ sidebarDepth: 0
139236
:bulb: Indicates that some problems reported by the rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
140237
:::
141238
142-
${rulesTableContent}`
239+
Mark indicating rule type:
240+
241+
- <span class="emoji">:warning:</span> Possible Problems: These rules relate to possible logic errors in code.
242+
- :hammer: Suggestions: These rules suggest alternate ways of doing things.
243+
- :lipstick: Layout & Formatting: These rules care about how the code looks rather than how it executes.
244+
245+
${rulesTableContent.trim()}
246+
`
143247
)

0 commit comments

Comments
 (0)