Skip to content

Commit

Permalink
Merge branch 'master' into feat/force-types-on-object-props
Browse files Browse the repository at this point in the history
  • Loading branch information
przemyslawjanpietrzak committed Oct 3, 2022
2 parents 9a5320b + cae6d29 commit 6857256
Show file tree
Hide file tree
Showing 65 changed files with 5,608 additions and 152 deletions.
83 changes: 68 additions & 15 deletions docs/.vuepress/components/eslint-code-block.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<eslint-editor
:linter="linter"
:config="config"
:code="code"
v-model="code"
:style="{ height }"
class="eslint-code-block"
:filename="filename"
Expand Down Expand Up @@ -43,23 +43,50 @@ export default {
language: {
type: String,
default: 'html'
},
/**
* If enabled, `@typescript-eslint/parser` will be used.
* This must be enabled when used for `ts` code blocks.
*/
typescript: {
type: Boolean,
default: false
}
},
data() {
const code = this.computeCodeFromSlot()
// The height is determined in the initial processing.
// This is because later code changes do not change the height.
const lines = code.split('\n').length
const height = `${Math.max(120, 19 * lines)}px`
return {
code,
height,
linter: null,
preprocess: processors['.vue'].preprocess,
postprocess: processors['.vue'].postprocess,
format: {
insertSpaces: true,
tabSize: 2
}
},
tsEslintParser: null
}
},
computed: {
config() {
let parser = null // Use default parser (`espree`)
if (this.typescript) {
// Use `@typescript-eslint/parser`.
parser = this.tsEslintParser
} else if (this.langTs) {
// Use `@typescript-eslint/parser` only when `<script lang="ts">` or `<script lang="typescript">`.
parser = {
ts: this.tsEslintParser,
typescript: this.tsEslintParser
}
}
return {
globals: {
console: false,
Expand Down Expand Up @@ -90,6 +117,7 @@ export default {
rules: this.rules,
parser: 'vue-eslint-parser',
parserOptions: {
parser,
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
Expand All @@ -99,24 +127,37 @@ export default {
}
},
code() {
return `${this.computeCodeFromSlot(this.$slots.default).trim()}\n`
},
/**
* Checks whether code may be using lang="ts" or lang="typescript".
* @returns {boolean} If `true`, may be using lang="ts" or lang="typescript".
*/
langTs() {
return /lang\s*=\s*(?:"ts"|ts|'ts'|"typescript"|typescript|'typescript')/u.test(
this.code
)
}
},
height() {
const lines = this.code.split('\n').length
return `${Math.max(120, 19 * lines)}px`
watch: {
typescript(value) {
if (value) {
this.loadTypescriptESLint()
}
},
langTs(value) {
if (value) {
this.loadTypescriptESLint()
}
}
},
methods: {
computeCodeFromSlot(nodes) {
if (!Array.isArray(nodes)) {
return ''
}
return nodes
.map((node) => node.text || this.computeCodeFromSlot(node.children))
.join('')
computeCodeFromSlot() {
return `${computeCodeFromSlot(this.$slots.default).trim()}\n`
},
async loadTypescriptESLint() {
this.tsEslintParser = await import('@typescript-eslint/parser')
}
},
Expand All @@ -126,6 +167,9 @@ export default {
import('eslint/lib/linter'),
import('espree').then(() => import('vue-eslint-parser'))
])
if (this.langTs || this.typescript) {
await this.loadTypescriptESLint()
}
const linter = (this.linter = new Linter())
Expand All @@ -136,6 +180,15 @@ export default {
linter.defineParser('vue-eslint-parser', { parseForESLint })
}
}
function computeCodeFromSlot(nodes) {
if (!Array.isArray(nodes)) {
return ''
}
return nodes
.map((node) => node.text || computeCodeFromSlot(node.children))
.join('')
}
</script>

<style>
Expand Down
3 changes: 2 additions & 1 deletion docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ module.exports = {
'@eslint/eslintrc/universal': path.resolve(
__dirname,
'../../node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs'
)
),
globby: require.resolve('./shim/globby')
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/.vuepress/enhanceApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default (
window.process = new Proxy(
{
env: {},
cwd: () => undefined
cwd: () => ''
},
{
get(target, name) {
Expand Down
1 change: 1 addition & 0 deletions docs/.vuepress/shim/globby.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
10 changes: 9 additions & 1 deletion docs/.vuepress/shim/module.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
module.exports = {
createRequire: () => () => null
createRequire: () => (module) => {
if (module === 'espree') {
return require('espree')
}
if (module === 'eslint-scope') {
return require('eslint-scope')
}
throw new Error(`Not implemented: ${module}`)
}
}
5 changes: 5 additions & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ For example:
| [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: | :hammer: |
| [vue/component-options-name-casing](./component-options-name-casing.md) | enforce the casing of component name in `components` options | :wrench::bulb: | :hammer: |
| [vue/custom-event-name-casing](./custom-event-name-casing.md) | enforce specific casing for custom event name | | :hammer: |
| [vue/define-emits-declaration](./define-emits-declaration.md) | enforce declaration style of `defineEmits` | | :hammer: |
| [vue/define-macros-order](./define-macros-order.md) | enforce order of `defineEmits` and `defineProps` compiler macros | :wrench: | :lipstick: |
| [vue/define-props-declaration](./define-props-declaration.md) | enforce declaration style of `defineProps` | | :hammer: |
| [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | | :hammer: |
| [vue/html-comment-content-newline](./html-comment-content-newline.md) | enforce unified line brake in HTML comments | :wrench: | :lipstick: |
| [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: | :lipstick: |
Expand All @@ -227,6 +229,8 @@ For example:
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | | :hammer: |
| [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class | | :hammer: |
| [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | :bulb: | :hammer: |
| [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | disallow destructuring of ref objects that can lead to loss of reactivity | | :warning: |
| [vue/no-required-prop-with-default](./no-required-prop-with-default.md) | enforce props with default values ​​to be optional | :wrench::bulb: | :warning: |
| [vue/no-restricted-block](./no-restricted-block.md) | disallow specific block | | :hammer: |
| [vue/no-restricted-call-after-await](./no-restricted-call-after-await.md) | disallow asynchronously called restricted methods | | :hammer: |
| [vue/no-restricted-class](./no-restricted-class.md) | disallow specific classes in Vue components | | :warning: |
Expand All @@ -248,6 +252,7 @@ For example:
| [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: | :hammer: |
| [vue/no-v-text](./no-v-text.md) | disallow use of v-text | | :hammer: |
| [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: | :lipstick: |
| [vue/padding-line-between-tags](./padding-line-between-tags.md) | require or disallow newlines between sibling tags in template | :wrench: | :lipstick: |
| [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md) | enforce `Boolean` comes first in component prop types | :bulb: | :warning: |
| [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: |
| [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md) | require shorthand form attribute when `v-bind` value is `true` | :bulb: | :hammer: |
Expand Down
93 changes: 93 additions & 0 deletions docs/rules/define-emits-declaration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/define-emits-declaration
description: enforce declaration style of `defineEmits`
since: v9.5.0
---
# vue/define-emits-declaration

> enforce declaration style of `defineEmits`
## :book: Rule Details

This rule enforces `defineEmits` typing style which you should use `type-based` or `runtime` declaration.

This rule only works in setup script and `lang="ts"`.

<eslint-code-block :rules="{'vue/define-emits-declaration': ['error']}">

```vue
<script setup lang="ts">
/* ✓ GOOD */
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
/* ✗ BAD */
const emit = defineEmits({
change: (id) => typeof id == 'number',
update: (value) => typeof value == 'string'
})
/* ✗ BAD */
const emit = defineEmits(['change', 'update'])
</script>
```

</eslint-code-block>

## :wrench: Options

```json
"vue/define-emits-declaration": ["error", "type-based" | "runtime"]
```

- `type-based` (default) enforces type-based declaration
- `runtime` enforces runtime declaration

### `runtime`

<eslint-code-block :rules="{'vue/define-emits-declaration': ['error', 'runtime']}">

```vue
<script setup lang="ts">
/* ✗ BAD */
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
/* ✓ GOOD */
const emit = defineEmits({
change: (id) => typeof id == 'number',
update: (value) => typeof value == 'string'
})
/* ✓ GOOD */
const emit = defineEmits(['change', 'update'])
</script>
```

</eslint-code-block>

## :couple: Related Rules

- [vue/define-props-declaration](./define-props-declaration.md)
- [vue/valid-define-emits](./valid-define-emits.md)

## :books: Further Reading

- [`defineEmits`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
- [Typescript-only-features of `defineEmits`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
- [Guide - Typing-component-emits](https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits)

## :rocket: Version

This rule was introduced in eslint-plugin-vue v9.5.0

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-emits-declaration.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-emits-declaration.js)
87 changes: 87 additions & 0 deletions docs/rules/define-props-declaration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/define-props-declaration
description: enforce declaration style of `defineProps`
since: v9.5.0
---
# vue/define-props-declaration

> enforce declaration style of `defineProps`
## :book: Rule Details

This rule enforces `defineProps` typing style which you should use `type-based` or `runtime` declaration.

This rule only works in setup script and `lang="ts"`.

<eslint-code-block :rules="{'vue/define-props-declaration': ['error']}">

```vue
<script setup lang="ts">
/* ✓ GOOD */
const props = defineProps<{
kind: string,
options: { title: string }
}>()
/* ✗ BAD */
const props = defineProps({
kind: { type: String },
options: { type: Object as PropType<{ title: string }> }
})
</script>
```

</eslint-code-block>

## :wrench: Options

```json
"vue/define-props-declaration": ["error", "type-based" | "runtime"]
```

- `type-based` (default) enforces type-based declaration
- `runtime` enforces runtime declaration

### `"runtime"`

<eslint-code-block :rules="{'vue/define-emits-declaration': ['error', 'runtime']}">

```vue
<script setup lang="ts">
/* ✓ GOOD */
const props = defineProps({
kind: { type: String },
options: { type: Object as PropType<{ title: string }> }
})
/* ✗ BAD */
const props = defineProps<{
kind: string,
options: { title: string }
}>()
</script>
```

</eslint-code-block>

## :couple: Related Rules

- [vue/define-emits-declaration](./define-emits-declaration.md)
- [vue/valid-define-props](./valid-define-props.md)

## :books: Further Reading

- [`defineProps`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
- [Typescript-only-features of `defineProps`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
- [Guide - Typing-component-props](https://vuejs.org/guide/typescript/composition-api.html#typing-component-props)

## :rocket: Version

This rule was introduced in eslint-plugin-vue v9.5.0

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-props-declaration.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-props-declaration.js)

0 comments on commit 6857256

Please sign in to comment.