Skip to content

Commit 681608f

Browse files
Merge branch 'master' into feat/force-types-on-object-props
2 parents 9a5320b + cae6d29 commit 681608f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+5610
-154
lines changed

docs/.vuepress/components/eslint-code-block.vue

+68-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<eslint-editor
44
:linter="linter"
55
:config="config"
6-
:code="code"
6+
v-model="code"
77
:style="{ height }"
88
class="eslint-code-block"
99
:filename="filename"
@@ -43,23 +43,50 @@ export default {
4343
language: {
4444
type: String,
4545
default: 'html'
46+
},
47+
/**
48+
* If enabled, `@typescript-eslint/parser` will be used.
49+
* This must be enabled when used for `ts` code blocks.
50+
*/
51+
typescript: {
52+
type: Boolean,
53+
default: false
4654
}
4755
},
4856
4957
data() {
58+
const code = this.computeCodeFromSlot()
59+
// The height is determined in the initial processing.
60+
// This is because later code changes do not change the height.
61+
const lines = code.split('\n').length
62+
const height = `${Math.max(120, 19 * lines)}px`
5063
return {
64+
code,
65+
height,
5166
linter: null,
5267
preprocess: processors['.vue'].preprocess,
5368
postprocess: processors['.vue'].postprocess,
5469
format: {
5570
insertSpaces: true,
5671
tabSize: 2
57-
}
72+
},
73+
tsEslintParser: null
5874
}
5975
},
6076
6177
computed: {
6278
config() {
79+
let parser = null // Use default parser (`espree`)
80+
if (this.typescript) {
81+
// Use `@typescript-eslint/parser`.
82+
parser = this.tsEslintParser
83+
} else if (this.langTs) {
84+
// Use `@typescript-eslint/parser` only when `<script lang="ts">` or `<script lang="typescript">`.
85+
parser = {
86+
ts: this.tsEslintParser,
87+
typescript: this.tsEslintParser
88+
}
89+
}
6390
return {
6491
globals: {
6592
console: false,
@@ -90,6 +117,7 @@ export default {
90117
rules: this.rules,
91118
parser: 'vue-eslint-parser',
92119
parserOptions: {
120+
parser,
93121
ecmaVersion: 'latest',
94122
sourceType: 'module',
95123
ecmaFeatures: {
@@ -99,24 +127,37 @@ export default {
99127
}
100128
},
101129
102-
code() {
103-
return `${this.computeCodeFromSlot(this.$slots.default).trim()}\n`
104-
},
130+
/**
131+
* Checks whether code may be using lang="ts" or lang="typescript".
132+
* @returns {boolean} If `true`, may be using lang="ts" or lang="typescript".
133+
*/
134+
langTs() {
135+
return /lang\s*=\s*(?:"ts"|ts|'ts'|"typescript"|typescript|'typescript')/u.test(
136+
this.code
137+
)
138+
}
139+
},
105140
106-
height() {
107-
const lines = this.code.split('\n').length
108-
return `${Math.max(120, 19 * lines)}px`
141+
watch: {
142+
typescript(value) {
143+
if (value) {
144+
this.loadTypescriptESLint()
145+
}
146+
},
147+
langTs(value) {
148+
if (value) {
149+
this.loadTypescriptESLint()
150+
}
109151
}
110152
},
111153
112154
methods: {
113-
computeCodeFromSlot(nodes) {
114-
if (!Array.isArray(nodes)) {
115-
return ''
116-
}
117-
return nodes
118-
.map((node) => node.text || this.computeCodeFromSlot(node.children))
119-
.join('')
155+
computeCodeFromSlot() {
156+
return `${computeCodeFromSlot(this.$slots.default).trim()}\n`
157+
},
158+
159+
async loadTypescriptESLint() {
160+
this.tsEslintParser = await import('@typescript-eslint/parser')
120161
}
121162
},
122163
@@ -126,6 +167,9 @@ export default {
126167
import('eslint/lib/linter'),
127168
import('espree').then(() => import('vue-eslint-parser'))
128169
])
170+
if (this.langTs || this.typescript) {
171+
await this.loadTypescriptESLint()
172+
}
129173
130174
const linter = (this.linter = new Linter())
131175
@@ -136,6 +180,15 @@ export default {
136180
linter.defineParser('vue-eslint-parser', { parseForESLint })
137181
}
138182
}
183+
184+
function computeCodeFromSlot(nodes) {
185+
if (!Array.isArray(nodes)) {
186+
return ''
187+
}
188+
return nodes
189+
.map((node) => node.text || computeCodeFromSlot(node.children))
190+
.join('')
191+
}
139192
</script>
140193

141194
<style>

docs/.vuepress/config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ module.exports = {
132132
'@eslint/eslintrc/universal': path.resolve(
133133
__dirname,
134134
'../../node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs'
135-
)
135+
),
136+
globby: require.resolve('./shim/globby')
136137
}
137138
}
138139
}

docs/.vuepress/enhanceApp.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default (
1212
window.process = new Proxy(
1313
{
1414
env: {},
15-
cwd: () => undefined
15+
cwd: () => ''
1616
},
1717
{
1818
get(target, name) {

docs/.vuepress/shim/globby.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {}

docs/.vuepress/shim/module.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
module.exports = {
2-
createRequire: () => () => null
2+
createRequire: () => (module) => {
3+
if (module === 'espree') {
4+
return require('espree')
5+
}
6+
if (module === 'eslint-scope') {
7+
return require('eslint-scope')
8+
}
9+
throw new Error(`Not implemented: ${module}`)
10+
}
311
}

docs/rules/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ For example:
212212
| [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: | :hammer: |
213213
| [vue/component-options-name-casing](./component-options-name-casing.md) | enforce the casing of component name in `components` options | :wrench::bulb: | :hammer: |
214214
| [vue/custom-event-name-casing](./custom-event-name-casing.md) | enforce specific casing for custom event name | | :hammer: |
215+
| [vue/define-emits-declaration](./define-emits-declaration.md) | enforce declaration style of `defineEmits` | | :hammer: |
215216
| [vue/define-macros-order](./define-macros-order.md) | enforce order of `defineEmits` and `defineProps` compiler macros | :wrench: | :lipstick: |
217+
| [vue/define-props-declaration](./define-props-declaration.md) | enforce declaration style of `defineProps` | | :hammer: |
216218
| [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | | :hammer: |
217219
| [vue/html-comment-content-newline](./html-comment-content-newline.md) | enforce unified line brake in HTML comments | :wrench: | :lipstick: |
218220
| [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: | :lipstick: |
@@ -227,6 +229,8 @@ For example:
227229
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | | :hammer: |
228230
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | | :hammer: |
229231
| [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | :bulb: | :hammer: |
232+
| [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | disallow destructuring of ref objects that can lead to loss of reactivity | | :warning: |
233+
| [vue/no-required-prop-with-default](./no-required-prop-with-default.md) | enforce props with default values ​​to be optional | :wrench::bulb: | :warning: |
230234
| [vue/no-restricted-block](./no-restricted-block.md) | disallow specific block | | :hammer: |
231235
| [vue/no-restricted-call-after-await](./no-restricted-call-after-await.md) | disallow asynchronously called restricted methods | | :hammer: |
232236
| [vue/no-restricted-class](./no-restricted-class.md) | disallow specific classes in Vue components | | :warning: |
@@ -248,6 +252,7 @@ For example:
248252
| [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: | :hammer: |
249253
| [vue/no-v-text](./no-v-text.md) | disallow use of v-text | | :hammer: |
250254
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: | :lipstick: |
255+
| [vue/padding-line-between-tags](./padding-line-between-tags.md) | require or disallow newlines between sibling tags in template | :wrench: | :lipstick: |
251256
| [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md) | enforce `Boolean` comes first in component prop types | :bulb: | :warning: |
252257
| [vue/prefer-separate-static-class](./prefer-separate-static-class.md) | require static class names in template to be in a separate `class` attribute | :wrench: | :hammer: |
253258
| [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md) | require shorthand form attribute when `v-bind` value is `true` | :bulb: | :hammer: |
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/define-emits-declaration
5+
description: enforce declaration style of `defineEmits`
6+
since: v9.5.0
7+
---
8+
# vue/define-emits-declaration
9+
10+
> enforce declaration style of `defineEmits`
11+
12+
## :book: Rule Details
13+
14+
This rule enforces `defineEmits` typing style which you should use `type-based` or `runtime` declaration.
15+
16+
This rule only works in setup script and `lang="ts"`.
17+
18+
<eslint-code-block :rules="{'vue/define-emits-declaration': ['error']}">
19+
20+
```vue
21+
<script setup lang="ts">
22+
/* ✓ GOOD */
23+
const emit = defineEmits<{
24+
(e: 'change', id: number): void
25+
(e: 'update', value: string): void
26+
}>()
27+
28+
/* ✗ BAD */
29+
const emit = defineEmits({
30+
change: (id) => typeof id == 'number',
31+
update: (value) => typeof value == 'string'
32+
})
33+
34+
/* ✗ BAD */
35+
const emit = defineEmits(['change', 'update'])
36+
</script>
37+
```
38+
39+
</eslint-code-block>
40+
41+
## :wrench: Options
42+
43+
```json
44+
"vue/define-emits-declaration": ["error", "type-based" | "runtime"]
45+
```
46+
47+
- `type-based` (default) enforces type-based declaration
48+
- `runtime` enforces runtime declaration
49+
50+
### `runtime`
51+
52+
<eslint-code-block :rules="{'vue/define-emits-declaration': ['error', 'runtime']}">
53+
54+
```vue
55+
<script setup lang="ts">
56+
/* ✗ BAD */
57+
const emit = defineEmits<{
58+
(e: 'change', id: number): void
59+
(e: 'update', value: string): void
60+
}>()
61+
62+
/* ✓ GOOD */
63+
const emit = defineEmits({
64+
change: (id) => typeof id == 'number',
65+
update: (value) => typeof value == 'string'
66+
})
67+
68+
/* ✓ GOOD */
69+
const emit = defineEmits(['change', 'update'])
70+
</script>
71+
```
72+
73+
</eslint-code-block>
74+
75+
## :couple: Related Rules
76+
77+
- [vue/define-props-declaration](./define-props-declaration.md)
78+
- [vue/valid-define-emits](./valid-define-emits.md)
79+
80+
## :books: Further Reading
81+
82+
- [`defineEmits`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
83+
- [Typescript-only-features of `defineEmits`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
84+
- [Guide - Typing-component-emits](https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits)
85+
86+
## :rocket: Version
87+
88+
This rule was introduced in eslint-plugin-vue v9.5.0
89+
90+
## :mag: Implementation
91+
92+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-emits-declaration.js)
93+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-emits-declaration.js)
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/define-props-declaration
5+
description: enforce declaration style of `defineProps`
6+
since: v9.5.0
7+
---
8+
# vue/define-props-declaration
9+
10+
> enforce declaration style of `defineProps`
11+
12+
## :book: Rule Details
13+
14+
This rule enforces `defineProps` typing style which you should use `type-based` or `runtime` declaration.
15+
16+
This rule only works in setup script and `lang="ts"`.
17+
18+
<eslint-code-block :rules="{'vue/define-props-declaration': ['error']}">
19+
20+
```vue
21+
<script setup lang="ts">
22+
/* ✓ GOOD */
23+
const props = defineProps<{
24+
kind: string,
25+
options: { title: string }
26+
}>()
27+
28+
/* ✗ BAD */
29+
const props = defineProps({
30+
kind: { type: String },
31+
options: { type: Object as PropType<{ title: string }> }
32+
})
33+
</script>
34+
```
35+
36+
</eslint-code-block>
37+
38+
## :wrench: Options
39+
40+
```json
41+
"vue/define-props-declaration": ["error", "type-based" | "runtime"]
42+
```
43+
44+
- `type-based` (default) enforces type-based declaration
45+
- `runtime` enforces runtime declaration
46+
47+
### `"runtime"`
48+
49+
<eslint-code-block :rules="{'vue/define-emits-declaration': ['error', 'runtime']}">
50+
51+
```vue
52+
<script setup lang="ts">
53+
/* ✓ GOOD */
54+
const props = defineProps({
55+
kind: { type: String },
56+
options: { type: Object as PropType<{ title: string }> }
57+
})
58+
59+
/* ✗ BAD */
60+
const props = defineProps<{
61+
kind: string,
62+
options: { title: string }
63+
}>()
64+
</script>
65+
```
66+
67+
</eslint-code-block>
68+
69+
## :couple: Related Rules
70+
71+
- [vue/define-emits-declaration](./define-emits-declaration.md)
72+
- [vue/valid-define-props](./valid-define-props.md)
73+
74+
## :books: Further Reading
75+
76+
- [`defineProps`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
77+
- [Typescript-only-features of `defineProps`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
78+
- [Guide - Typing-component-props](https://vuejs.org/guide/typescript/composition-api.html#typing-component-props)
79+
80+
## :rocket: Version
81+
82+
This rule was introduced in eslint-plugin-vue v9.5.0
83+
84+
## :mag: Implementation
85+
86+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-props-declaration.js)
87+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-props-declaration.js)

0 commit comments

Comments
 (0)