feat: shared eslint configuration

pull/8414/head^2
Konv Suu 2025-11-14 11:53:59 +08:00
parent bed733dcf0
commit 631a2f255b
50 changed files with 5160 additions and 206 deletions

View File

@ -1,3 +1,3 @@
// @ts-check
import antdv from "@ant-design-vue/eslint-config"
export { default } from '@ant-design-vue/eslint-config/vue'
export default antdv()

View File

@ -1,5 +1,5 @@
<template>
<button @click="toggleTheme" class="fixed top-2 right-2">toggle {{ appearance }}</button>
<button class="fixed top-2 right-2" @click="toggleTheme">toggle {{ appearance }}</button>
<a-theme :appearance="appearance">
<RouterView />
</a-theme>

View File

@ -11,7 +11,6 @@
<RouterLink
:aria-disabled="item.path === route.path"
:to="item.path"
@click.stop="$event.currentTarget.blur()"
class="hover:bg-base-content/10 flex cursor-pointer flex-col rounded-lg px-3 py-2 transition duration-200"
>
{{ item.name }}
@ -20,7 +19,6 @@
<li v-for="child in item.children" :key="child.name">
<RouterLink
:to="child.path"
@click.stop="$event.currentTarget.blur()"
class="hover:bg-base-content/10 flex cursor-pointer flex-col rounded-lg px-3 py-2 text-xs opacity-80 transition duration-200"
>
{{ child.name }}

View File

@ -1,4 +1,5 @@
import { inject, InjectionKey, provide, Ref } from 'vue'
import type { InjectionKey, Ref } from 'vue';
import { inject, provide } from 'vue'
export interface LayoutOptions {
pageClass: Ref<string | undefined>

View File

@ -1,6 +1,7 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
import type { RouteRecordRaw} from 'vue-router';
import { RouterView } from 'vue-router'
import BasicLayout from './components/BasicLayout.vue'
import { Fragment, h } from 'vue'
import { h } from 'vue'
// /pages/button/basic.vue
const items = import.meta.glob('./pages/*/*.vue', { import: 'default', eager: true })

View File

@ -1,4 +1,4 @@
import { RouteRecordRaw } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
export function globRoutes(
baseName: string,

View File

@ -1,7 +1,7 @@
import tailwindcss from '@tailwindcss/vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path'
import { defineConfig, Plugin } from 'vite'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({

View File

@ -1,13 +1,7 @@
// @ts-check
import antdv from "./packages-config/eslint-config/dist/index.mjs"
import typescript from '@ant-design-vue/eslint-config/typescript'
/**
* @type {import('eslint').Linter.Config[]}
*/
export default [
...typescript,
{
ignores: ['apps/**', 'packages/**', 'packages-*/**'],
},
]
export default antdv({
vue: true,
typescript: true,
prettier: true,
})

View File

@ -28,6 +28,6 @@
},
"packageManager": "pnpm@10.21.0",
"engines": {
"node": ">=22"
"node": ">=20.0.0"
}
}

View File

@ -0,0 +1,12 @@
# @ant-design-vue/eslint-config
Shared ESLint configuration.
## Features
- Supports JavaScript, TypeScript, Vue.
- Integrates with Prettier for code formatting.
## Thanks
- [antfu/eslint-config](https://github.com/antfu/eslint-config)

View File

@ -1,5 +0,0 @@
import { Linter } from 'eslint'
declare const e: Linter.Config[]
export default e

View File

@ -0,0 +1,7 @@
import antdv from './src/index'
export default antdv({
vue: true,
typescript: true,
prettier: true,
})

View File

@ -1,21 +0,0 @@
// @ts-check
/**
* @type {Pick<import('eslint').Linter.Config, 'rules'>}
*/
export default {
rules: {
'no-redeclare': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
vars: 'all',
args: 'after-used',
ignoreRestSiblings: true,
argsIgnorePattern: '^_',
},
],
},
}

View File

@ -1,31 +0,0 @@
// @ts-check
import eslint from '@eslint/js'
import prettier from 'eslint-config-prettier'
import checkFile from 'eslint-plugin-check-file'
import tseslint from 'typescript-eslint'
import base from './eslint.config.base.js'
import ignore from './eslint.config.ignore.js'
export default tseslint.config(
ignore,
eslint.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/src/**/*.{ts}'],
plugins: {
'check-file': checkFile,
},
rules: {
'check-file/filename-naming-convention': [
'warn',
{
'**/*.ts': 'KEBAB_CASE',
},
{ ignoreMiddleExtensions: true },
],
},
},
base,
prettier,
)

View File

@ -1,77 +0,0 @@
// @ts-check
import eslint from '@eslint/js';
import prettier from 'eslint-config-prettier';
import checkFile from 'eslint-plugin-check-file';
import pluginVue from 'eslint-plugin-vue';
import tseslint from 'typescript-eslint';
import base from './eslint.config.base.js';
import ignore from './eslint.config.ignore.js';
export default tseslint.config(
ignore,
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: {
parser: tseslint.parser,
sourceType: 'module',
},
},
},
{
files: ['**/src/**/*.{ts,vue}'],
plugins: {
'check-file': checkFile,
},
rules: {
'check-file/filename-naming-convention': [
'warn',
{
'**/*.vue': 'PASCAL_CASE',
'**/*.ts': 'KEBAB_CASE',
},
{ ignoreMiddleExtensions: true },
],
},
},
{
files: ['**/*.vue'],
rules: {
'check-file/no-index': 'warn',
},
},
eslint.configs.recommended,
...tseslint.configs.recommended,
// @ts-ignore
...pluginVue.configs['flat/recommended'],
base,
{
rules: {
'vue/require-default-prop': 'off',
'vue/no-mutating-props': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
semi: ['error', 'always'],
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true,
},
],
'vue/require-prop-types': 0,
'vue/v-on-event-hyphenation': 0,
'import/no-unresolved': [2, { ignore: ['^@ant-design-vue/table'] }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-types': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/consistent-type-imports': 'error',
},
},
prettier,
);

View File

@ -1,27 +1,40 @@
{
"name": "@ant-design-vue/eslint-config",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
"./typescript": {
"import": "./eslint.config.typescript.js",
"types": "./config.d.ts"
},
"./vue": {
"import": "./eslint.config.vue.js",
"types": "./config.d.ts"
}
"scripts": {
"build": "pnpm run build:typegen && tsdown",
"build:typegen": "tsx scripts/typegen.ts",
"inspector": "eslint-config-inspector --config eslint-inspector.config.ts"
},
"devDependencies": {
"files": [
"dist"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"exports": {
".": "./dist/index.mjs"
},
"dependencies": {
"@eslint/js": "^8.56.0",
"@types/eslint__js": "^8.42.3",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-check-file": "^2.8.0",
"eslint-plugin-unused-imports": "^4.3.0",
"eslint-plugin-vue": "^9.27.0",
"typescript": "^5.8.2",
"globals": "^16.5.0",
"local-pkg": "^1.1.2",
"typescript-eslint": "^7.17.0",
"globby": "^14.1.0"
"vue-eslint-parser": "^10.2.0"
},
"devDependencies": {
"@eslint/config-inspector": "^1.3.0",
"@types/eslint": "^9.6.1",
"eslint": "^9.39.1",
"eslint-flat-config-utils": "^2.1.4",
"eslint-typegen": "^2.3.0",
"tsdown": "^0.16.4",
"tsx": "^4.20.6",
"typescript": "^5.8.2"
}
}

View File

@ -0,0 +1,23 @@
import fs from 'node:fs/promises'
import { flatConfigsToRulesDTS } from 'eslint-typegen/core'
import antdv from '../src'
const configs = await antdv({
typescript: true,
vue: true,
tailwind: true,
})
const configNames = configs.map(i => i.name).filter(Boolean) as string[]
let dts = await flatConfigsToRulesDTS(configs as any, {
includeAugmentation: false,
})
dts += `
// Names of all the configs
export type ConfigNames = ${configNames.map(i => `'${i}'`).join(' | ')}
`
await fs.writeFile('src/typegen.d.ts', dts)

View File

@ -0,0 +1,23 @@
import { pluginCheckFile } from "../plugins";
import { defineConfig } from 'eslint/config';
export const checkFile = defineConfig([
{
name: '@ant-design-vue/check-file',
files: ['**/src/**/*.{ts,vue}'],
plugins: {
'check-file': pluginCheckFile,
},
rules: {
'check-file/filename-naming-convention': [
'warn',
{
'**/*.vue': 'PASCAL_CASE',
'**/*.ts': 'KEBAB_CASE',
},
{ ignoreMiddleExtensions: true },
],
'check-file/no-index': 'warn',
},
},
])

View File

@ -1,9 +1,6 @@
// @ts-check
import { defineConfig } from 'eslint/config';
/**
* @type {Pick<import('eslint').Linter.Config, 'ignores'>}
*/
export default {
export const ignores= defineConfig({
ignores: [
'**/node_modules/',
'**/dist/',
@ -17,4 +14,6 @@ export default {
'.vscode/',
'**/.tsup/',
],
}
name: '@ant-design-vue/ignores'
})

View File

@ -0,0 +1,6 @@
export * from "./ignores"
export * from "./javascript"
export * from "./typescript"
export * from "./vue"
export * from "./check-file"
export * from "./prettier"

View File

@ -0,0 +1,42 @@
import { defineConfig } from "eslint/config";
import globals from 'globals'
import { jseslint, pluginUnusedImports } from '../plugins'
export const javascript = defineConfig([
{
name: '@ant-design-vue/js/rules',
plugins: {
js: jseslint,
'unused-imports': pluginUnusedImports,
},
extends: ["js/recommended"],
rules: {
'no-redeclare': 'off',
"no-unused-vars": "off",
'unused-imports/no-unused-imports': 'warn',
'unused-imports/no-unused-vars': 'error'
}
},
{
name: '@ant-design-vue/js/setup',
languageOptions: {
ecmaVersion: 'latest',
globals: {
...globals.browser,
...globals.es2021,
...globals.node,
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
sourceType: 'module',
},
linterOptions: {
reportUnusedDisableDirectives: true,
}
},
])

View File

@ -0,0 +1,7 @@
import { configPrettier } from "../plugins";
import { defineConfig } from 'eslint/config';
export const prettier = defineConfig({
name: '@ant-design-vue/prettier',
...configPrettier,
})

View File

@ -0,0 +1,24 @@
import { defineConfig } from "eslint/config";
import { tseslint } from "../plugins";
export const typescript = defineConfig([
{
// @ts-expect-error - xxx
extends: [...tseslint.configs.recommended],
name: '@ant-design-vue/typescript',
files: ['**/*.?([cm])ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-types': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/ban-ts-comment': 'off'
}
}
])

View File

@ -0,0 +1,39 @@
import { defineConfig } from "eslint/config";
import { parserVue, pluginVue } from "../plugins";
export const vue = defineConfig([
{
name: '@ant-design-vue/vue',
files: ['**/*.vue'],
extends: [
...pluginVue.configs['flat/recommended'],
],
rules: {
'vue/require-default-prop': 'off',
'vue/no-mutating-props': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
semi: ['error', 'always'],
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true,
},
],
'vue/require-prop-types': 0,
'vue/v-on-event-hyphenation': 0,
}
},
{
name: '@ant-design-vue/vue/setup',
files: ['**/*.vue'],
languageOptions: {
parser: parserVue,
parserOptions: {
parser: '@typescript-eslint/parser',
},
},
},
])

View File

@ -0,0 +1,61 @@
import {
FlatConfigComposer,
type Arrayable,
type Awaitable,
} from 'eslint-flat-config-utils'
import {
ignores,
javascript,
checkFile,
prettier,
typescript,
vue,
} from "./configs"
import { Config } from './types';
import { isPackageExists } from "local-pkg";
import { Linter } from 'eslint';
import { ConfigNames } from './typegen';
interface Options {
typescript?: boolean,
vue?: boolean,
prettier?: boolean,
}
export default (
options: Options = {},
...userConfigs: Awaitable<
Arrayable<Config> | FlatConfigComposer<any, any> | Linter.Config[]
>[]
): FlatConfigComposer<Config, ConfigNames> => {
const {
typescript: enableTypescript = isPackageExists('typescript'),
vue: enableVue = isPackageExists('vue'),
prettier: enablePrettier = isPackageExists('prettier'),
} = options
const configs: Config[][] = [
ignores,
checkFile,
javascript,
]
if (enableTypescript) {
configs.push(typescript)
}
if (enableVue) {
configs.push(vue)
}
if(enablePrettier) {
configs.push(prettier)
}
const composer = new FlatConfigComposer<Config, ConfigNames>(
...configs,
...(userConfigs as any),
)
return composer
}

View File

@ -0,0 +1,19 @@
//@ts-nocheck
import jseslint from "@eslint/js";
import tseslint from 'typescript-eslint'
import pluginVue from 'eslint-plugin-vue'
import pluginUnusedImports from 'eslint-plugin-unused-imports'
import pluginCheckFile from 'eslint-plugin-check-file'
import configPrettier from 'eslint-config-prettier'
import parserVue from 'vue-eslint-parser'
export {
jseslint,
tseslint,
pluginCheckFile,
pluginUnusedImports,
pluginVue,
configPrettier,
parserVue
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
import type { RuleOptions } from './typegen'
import type { Linter } from 'eslint'
export type Config = Linter.Config<Linter.RulesRecord & RuleOptions>

View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ESNext",
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": [
"**/*.ts"
]
}

View File

@ -1,4 +1,3 @@
// @ts-check
export { default } from '@ant-design-vue/eslint-config/vue';
import antdv from "@ant-design-vue/eslint-config"
export default antdv()

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from 'vitest'
import { Affix, Button } from '@ant-design-vue/ui'
import { describe, expect, it } from 'vitest'
import { Affix } from '@ant-design-vue/ui'
import { mount } from '@vue/test-utils'
describe('Affix', () => {

View File

@ -1,4 +1,4 @@
import { App, Plugin } from 'vue'
import type { App, Plugin } from 'vue'
import Affix from './Affix.vue'
import './style/index.css'

View File

@ -1,4 +1,4 @@
import { CSSProperties } from 'vue'
import type { CSSProperties } from 'vue'
function getDefaultTarget() {
return typeof window !== 'undefined' ? window : null

View File

@ -2,9 +2,9 @@
<button
ref="buttonRef"
:class="rootClass"
@click="handleClick"
:disabled="disabled"
:style="cssVars"
@click="handleClick"
>
<Wave :target="buttonRef" />
<slot name="loading">

View File

@ -1,4 +1,4 @@
import { App, Plugin } from 'vue'
import type { App, Plugin } from 'vue'
import Button from './Button.vue'
import './style/index.css'

View File

@ -1,4 +1,4 @@
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it } from 'vitest'
import { Flex } from '@ant-design-vue/ui'
import { mount } from '@vue/test-utils'

View File

@ -1,4 +1,4 @@
import { App, Plugin } from 'vue'
import type { App, Plugin } from 'vue'
import Flex from './Flex.vue'
import './style/index.css'

View File

@ -1,4 +1,4 @@
import { CSSProperties } from "vue"
import type { CSSProperties } from "vue"
type SizeType = 'small' | 'middle' | 'large' | undefined

View File

@ -1,4 +1,4 @@
import { App, Plugin } from 'vue'
import type { App, Plugin } from 'vue'
import Input from './Input.vue'
import './style/index.css'

View File

@ -1,4 +1,4 @@
import { PropType, ExtractPublicPropTypes } from 'vue'
import type { PropType, ExtractPublicPropTypes } from 'vue'
// Input Props
export const inputProps = {

View File

@ -1,5 +1,6 @@
import { inject, InjectionKey, provide } from 'vue'
import { ThemeProps } from './meta'
import type { InjectionKey} from 'vue';
import { inject, provide } from 'vue'
import type { ThemeProps } from './meta'
const ThemeSymbol: InjectionKey<ThemeProps> = Symbol('theme')

View File

@ -1,6 +1,6 @@
export * from './hook'
import { App, Plugin } from 'vue'
import type { App, Plugin } from 'vue'
import Theme from './Theme.vue'
export { Theme }

View File

@ -1,4 +1,4 @@
import { PropType, ExtractPublicPropTypes } from 'vue'
import type { PropType, ExtractPublicPropTypes } from 'vue'
export const defaultColor = '#1677FF'
// Theme Props

View File

@ -3,9 +3,9 @@
<Transition
appear
name="ant-wave-motion"
appearFromClass="ant-wave-motion-appear"
appearActiveClass="ant-wave-motion-appear"
appearToClass="ant-wave-motion-appear ant-wave-motion-appear-active"
appear-from-class="ant-wave-motion-appear"
appear-active-class="ant-wave-motion-appear"
appear-to-class="ant-wave-motion-appear ant-wave-motion-appear-active"
>
<div
v-if="show"
@ -25,7 +25,6 @@ import {
onMounted,
ref,
shallowRef,
Transition,
watch,
} from 'vue'
import { getTargetWaveColor } from './util'

View File

@ -1,4 +1,4 @@
import { App, Plugin } from 'vue'
import type { App, Plugin } from 'vue'
import Wave from './Wave.vue'
import './style/index.css'

View File

@ -1,5 +1,5 @@
export function isNotGrey(color: string) {
// eslint-disable-next-line no-useless-escape
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/);
if (match && match[1] && match[2] && match[3]) {
return !(match[1] === match[2] && match[2] === match[3]);

View File

@ -1,5 +1,5 @@
import './style/tailwind.css'
import { App } from 'vue'
import type { App } from 'vue'
import * as components from './components'
export * from './components'

View File

@ -8,6 +8,8 @@ try {
})
window.addEventListener('testPassive', null, opts)
window.removeEventListener('testPassive', null, opts)
} catch (e) {}
} catch (e) {
// noop
}
export default supportsPassive

View File

@ -6,7 +6,8 @@
"rootDir": ".",
"paths": {
"@/*": ["./src/*"],
"~/*": ["./assets/*"]
"~/*": ["./assets/*"],
"@ant-design-vue/ui": ["./src/index.ts"]
}
}
}

View File

@ -3,7 +3,6 @@ import vue from '@ant-design-vue/vite-config/vue'
import { resolve } from 'node:path'
import tailwindcss from '@tailwindcss/vite'
import { readdirSync, statSync } from 'node:fs'
import dts from 'vite-plugin-dts'
import pkg from './package.json'
// 获取所有组件目录