20250609 eslint V8

This commit is contained in:
mac·ufutx 2025-06-09 17:10:41 +08:00
parent be53ff00c0
commit 3d9f87d02b
11 changed files with 179 additions and 132 deletions

View File

@ -4,4 +4,10 @@ dist
public public
*.d.ts *.d.ts
.vscode .vscode
.husky .husky
# 忽略目录
coverage/
# 忽略文件
*.d.ts
*.log

View File

@ -1,60 +1,59 @@
// .eslintrc.cjs // .eslintrc.cjs
const path = require('path')
module.exports = { module.exports = {
root: true, root: true,
env: { env: {
browser: true, browser: true,
node: true, node: true,
es6: true, es6: true
}, },
extends: [ extends: [
'eslint:recommended', 'eslint:recommended',
'plugin:vue/vue3-recommended', // Vue 3 推荐规则 'plugin:vue/vue3-recommended', // 加载 Vue 3 推荐规则
'@vue/eslint-config-typescript', // TypeScript 支持 '@vue/eslint-config-typescript', // 加载 TypeScript 支持
'prettier', // 关闭与 Prettier 冲突的规则(需先安装 prettier 'plugin:prettier/recommended' // 确保放在最后
'plugin:prettier/recommended', // 将 Prettier 规则作为 ESLint 规则 ],
parser: 'vue-eslint-parser', // 解析 Vue 模板
parserOptions: {
parser: '@typescript-eslint/parser', // 解析 TypeScript 代码
project: path.resolve(__dirname, 'tsconfig.app.json'), // 使用正确的 TSConfig
tsconfigRootDir: __dirname,
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: ['vue', 'prettier', '@typescript-eslint'], // 显式注册所有插件
rules: {
'vue/no-undef-components': 'error',
// 针对非 Vue 文件禁用 Vue 规则
// 'vue/no-unregistered-components': [
// 'error',
// { ignorePatterns: ['^router$', '^store$'] } // 可选:忽略特定组件名称
// ],
'vue/multi-word-component-names': [
'error',
{
ignores: ['Test'] // 可选:忽略特定组件名
}
], ],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
rules: {
// 自定义规则(根据团队需求调整)
'vue/multi-word-component-names': 'off',
'prettier/prettier': 'error', // 将 Prettier 错误视为 ESLint 错误
// 强制使用 === 而非 ==
'eqeqeq': 'error',
// 组件 props 必须有类型和默认值 // 其他自定义规则
'vue/require-default-prop': 'error', 'prettier/prettier': 'error',
'vue/require-prop-types': 'error', '@typescript-eslint/no-unused-vars': 'warn',
'max-len': 'off'
// 限制每行最大长度 },
'max-len': ['warn', {code: 120}], overrides: [
// {
// 禁止使用未定义的组件 // files: ['*.vue'], // 仅对 Vue 文件应用 Vue 规则
'vue/no-unregistered-components': 'error', // rules: {
// 'vue/no-unregistered-components': 'error'
// 组件名必须为 PascalCase // }
'vue/component-name-in-template-casing': ['error', 'PascalCase'], // },
{
// 禁止使用 v-html防止 XSS files: ['*.ts', '*.js'], // 非 Vue 文件不应用 Vue 规则
'vue/no-v-html': 'warn', rules: {
// 自定义规则(根据团队需求调整) 'vue/no-unregistered-components': 'off'
'vue/multi-word-component-names': 'off', // 允许单字组件名 }
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', }
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', ]
'@typescript-eslint/no-unused-vars': ['warn', {argsIgnorePattern: '^_'}], }
},
overrides: [
// 针对测试文件的规则
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
env: {
jest: true,
},
},
],
}

View File

@ -1,5 +1,4 @@
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
npm test
npx lint-staged npx lint-staged

View File

@ -26,21 +26,23 @@
}, },
"devDependencies": { "devDependencies": {
"@types/axios": "^0.9.36", "@types/axios": "^0.9.36",
"@typescript-eslint/eslint-plugin": "^8.33.1", "@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "^8.33.1", "@typescript-eslint/parser": "5.62.0",
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^4.2.3",
"@vue/eslint-config-typescript": "^14.5.0", "@vue/eslint-config-typescript": "12.0.0",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"eslint": "^9.28.0", "eslint": "8.57.1",
"eslint-plugin-vue": "^10.2.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.0.0",
"husky": "^8.0.0", "husky": "^8.0.0",
"less": "^4.3.0", "less": "^4.3.0",
"less-loader": "^12.3.0", "less-loader": "^12.3.0",
"lint-staged": "^16.1.0", "lint-staged": "^16.1.0",
"postcss": "^8.4.24", "postcss": "^8.4.24",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"typescript": "~5.8.3", "typescript": "5.1.6",
"vite": "^4.4.9", "vite": "^4.4.9",
"vite-plugin-image-optimizer": "^1.1.8", "vite-plugin-image-optimizer": "^1.1.8",
"vue-tsc": "^2.2.8" "vue-tsc": "^2.2.8"

View File

@ -4,6 +4,7 @@ import HelloWorld from './components/HelloWorld.vue'
<template> <template>
<div> <div>
<!-- <NonExistentComponent />-->
<a href="https://vite.dev" target="_blank"> <a href="https://vite.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" /> <img src="/vite.svg" class="logo" alt="Vite logo" />
</a> </a>
@ -21,9 +22,11 @@ import HelloWorld from './components/HelloWorld.vue'
will-change: filter; will-change: filter;
transition: filter 300ms; transition: filter 300ms;
} }
.logo:hover { .logo:hover {
filter: drop-shadow(0 0 2em #646cffaa); filter: drop-shadow(0 0 2em #646cffaa);
} }
.logo.vue:hover { .logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa); filter: drop-shadow(0 0 2em #42b883aa);
} }

View File

@ -1 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img"
class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198">
<path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path>
<path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path>
<path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 517 B

View File

@ -22,12 +22,12 @@ const fetchData = async () => {
data.value = res data.value = res
// 2. GET // 2. GET
const params = { page: 1, limit: 10 } // const params = { page: 1, limit: 10 }
const paginatedData = await request.get('/api/users', params) // const paginatedData = await request.get('/api/users', params)
// 3. // 3.
const config = { headers: { 'X-Custom-Header': 'token' } } // const config = { headers: { 'X-Custom-Header': 'token' } }
const specialData = await request.get('/api/special', null, config) // const specialData = await request.get('/api/special', null, config)
} catch (error) { } catch (error) {
console.error('请求失败:', error) console.error('请求失败:', error)
} finally { } finally {
@ -36,13 +36,13 @@ const fetchData = async () => {
} }
// POST // POST
const createItem = async () => { // const createItem = async () => {
try { // try {
const payload = { name: '新项目', status: 'active' } // const payload = { name: '', status: 'active' }
const res = await request.post('/api/items', payload) // const res = await request.post('/api/items', payload)
console.log('创建成功:', res) // console.log(':', res)
} catch (error) { // } catch (error) {
console.error('创建失败:', error) // console.error(':', error)
} // }
} // }
</script> </script>

View File

@ -1,79 +1,84 @@
:root { :root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5; line-height: 1.5;
font-weight: 400; font-weight: 400;
color-scheme: light dark; color-scheme: light dark;
color: rgba(255, 255, 255, 0.87); color: rgba(255, 255, 255, 0.87);
background-color: #242424; background-color: #242424;
font-synthesis: none; font-synthesis: none;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
a { a {
font-weight: 500; font-weight: 500;
color: #646cff; color: #646cff;
text-decoration: inherit; text-decoration: inherit;
} }
a:hover { a:hover {
color: #535bf2; color: #535bf2;
} }
body { body {
margin: 0; margin: 0;
display: flex; display: flex;
place-items: center; place-items: center;
min-width: 320px; min-width: 320px;
min-height: 100vh; min-height: 100vh;
} }
h1 { h1 {
font-size: 3.2em; font-size: 3.2em;
line-height: 1.1; line-height: 1.1;
} }
button { button {
border-radius: 8px; border-radius: 8px;
border: 1px solid transparent; border: 1px solid transparent;
padding: 0.6em 1.2em; padding: 0.6em 1.2em;
font-size: 1em; font-size: 1em;
font-weight: 500; font-weight: 500;
font-family: inherit; font-family: inherit;
background-color: #1a1a1a; background-color: #1a1a1a;
cursor: pointer; cursor: pointer;
transition: border-color 0.25s; transition: border-color 0.25s;
} }
button:hover { button:hover {
border-color: #646cff; border-color: #646cff;
} }
button:focus, button:focus,
button:focus-visible { button:focus-visible {
outline: 4px auto -webkit-focus-ring-color; outline: 4px auto -webkit-focus-ring-color;
} }
.card { .card {
padding: 2em; padding: 2em;
} }
#app { #app {
max-width: 1280px; max-width: 1280px;
margin: 0 auto; margin: 0 auto;
padding: 2rem; padding: 2rem;
text-align: center; text-align: center;
} }
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {
:root { :root {
color: #213547; color: #213547;
background-color: #ffffff; background-color: #ffffff;
} }
a:hover {
color: #747bff; a:hover {
} color: #747bff;
button { }
background-color: #f9f9f9;
} button {
background-color: #f9f9f9;
}
} }

View File

@ -3,17 +3,33 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["src/*"] // "@/*": [
"src/*"
]
//
}, },
"jsx": "preserve",
// Vue JSX
"types": [
"vite/client",
"vue"
],
// Vue
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true, // "erasableSyntaxOnly": true, //
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true // "noUncheckedSideEffectImports": true
}, },
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] "include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue", // Vue
"vite.config.ts", // Vite
"src/main.ts" //
]
} }

4
tsconfig.eslint.json Normal file
View File

@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts"] //
}

View File

@ -17,9 +17,17 @@
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true, "erasableSyntaxOnlsy": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
"include": ["vite.config.ts"] // "include": ["vite.config.ts"]
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue", // Vue
"vite.config.ts", // Vite
"src/main.ts" //
]
} }