my-code-style 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -0
- package/bin/init.cjs +476 -0
- package/package.json +121 -0
- package/src/commitlint/base.cjs +48 -0
- package/src/commitlint/scopes.cjs +46 -0
- package/src/editorconfig +18 -0
- package/src/eslint/base.cjs +55 -0
- package/src/eslint/flat/_shared.mjs +55 -0
- package/src/eslint/flat/base.mjs +78 -0
- package/src/eslint/flat/uniapp.mjs +17 -0
- package/src/eslint/flat/vue3.mjs +46 -0
- package/src/eslint/uniapp.cjs +22 -0
- package/src/eslint/vue3.cjs +23 -0
- package/src/gitattributes +19 -0
- package/src/husky/commit-msg +4 -0
- package/src/husky/pre-commit +4 -0
- package/src/prettier/index.cjs +21 -0
- package/src/prettierignore +16 -0
- package/src/stylelint/index.cjs +55 -0
- package/src/stylelint/less-override.cjs +22 -0
- package/src/stylelint/less.cjs +49 -0
- package/src/versionrc/index.cjs +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# my-code-style
|
|
2
|
+
|
|
3
|
+
Lint/Format/Git 配置工程化 npm 包。适用于 Vue 3 + TypeScript + uni-app 项目。
|
|
4
|
+
|
|
5
|
+
支持 ESLint v8 (.eslintrc.cjs) 和 ESLint v9 (Flat Config) 双格式,支持 SCSS 和 Less 样式检查。
|
|
6
|
+
|
|
7
|
+
## 快速开始
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add -D my-code-style
|
|
11
|
+
npx my-code-style-init
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
init 脚本会自动检测项目的 ESLint 版本和 CSS 预处理器,生成对应的配置文件。
|
|
15
|
+
|
|
16
|
+
## 生成的文件
|
|
17
|
+
|
|
18
|
+
| 文件 | 说明 |
|
|
19
|
+
|------|------|
|
|
20
|
+
| `.eslintrc.cjs` | ESLint 配置(v8 项目,TypeScript + Vue 3 + uni-app) |
|
|
21
|
+
| `eslint.config.ts` | ESLint Flat Config(v9 项目,TypeScript + Vue 3 + uni-app) |
|
|
22
|
+
| `.prettierrc.cjs` | Prettier 格式化配置 |
|
|
23
|
+
| `.prettierignore` | Prettier 忽略文件(跳过二进制文件) |
|
|
24
|
+
| `.stylelintrc.cjs` | Stylelint CSS/SCSS/Less 配置 |
|
|
25
|
+
| `.commitlintrc.cjs` | Git commit 信息规范 |
|
|
26
|
+
| `.versionrc.js` | 自动版本号 + CHANGELOG 生成 |
|
|
27
|
+
| `.editorconfig` | 编辑器基础配置 |
|
|
28
|
+
| `.gitattributes` | Git 文件类型处理(统一 EOL=LF,标记二进制文件) |
|
|
29
|
+
| `.husky/commit-msg` | Git hook: commitlint |
|
|
30
|
+
| `.husky/pre-commit` | Git hook: lint-staged |
|
|
31
|
+
|
|
32
|
+
## 手动使用(不通过 init)
|
|
33
|
+
|
|
34
|
+
### ESLint v8 (.eslintrc.cjs)
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// .eslintrc.cjs
|
|
38
|
+
module.exports = require("my-code-style/eslint/uniapp")
|
|
39
|
+
|
|
40
|
+
// 或按需引入中间层
|
|
41
|
+
const base = require("my-code-style/eslint") // 基础 TypeScript
|
|
42
|
+
const vue3 = require("my-code-style/eslint/vue3") // + Vue 3 规则
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### ESLint v9 (Flat Config / eslint.config.ts)
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// eslint.config.ts
|
|
49
|
+
import uniappConfig from "my-code-style/eslint/flat/uniapp"
|
|
50
|
+
|
|
51
|
+
// 或按需引入中间层
|
|
52
|
+
import baseConfig from "my-code-style/eslint/flat/base"
|
|
53
|
+
import vue3Config from "my-code-style/eslint/flat/vue3"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 其他配置
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
// .prettierrc.cjs
|
|
60
|
+
module.exports = require("my-code-style/prettier")
|
|
61
|
+
|
|
62
|
+
// .stylelintrc.cjs
|
|
63
|
+
module.exports = require("my-code-style/stylelint")
|
|
64
|
+
|
|
65
|
+
// .commitlintrc.cjs
|
|
66
|
+
const base = require("my-code-style/commitlint")
|
|
67
|
+
const { generateScopes, guessCurrentScope } = require("my-code-style/commitlint/scopes")
|
|
68
|
+
module.exports = { ...base, prompt: { ...base.prompt, scopes: generateScopes("src") } }
|
|
69
|
+
|
|
70
|
+
// .versionrc.js
|
|
71
|
+
module.exports = require("my-code-style/versionrc")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 配置覆盖
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
// .eslintrc.cjs
|
|
78
|
+
const base = require("my-code-style/eslint/uniapp")
|
|
79
|
+
module.exports = {
|
|
80
|
+
...base,
|
|
81
|
+
rules: {
|
|
82
|
+
...base.rules,
|
|
83
|
+
"no-console": "warn",
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 导出入口
|
|
89
|
+
|
|
90
|
+
| 入口 | 说明 |
|
|
91
|
+
|------|------|
|
|
92
|
+
| `my-code-style` | 默认:ESLint v8 uni-app 配置 |
|
|
93
|
+
| `my-code-style/eslint` | 基础 ESLint v8(TypeScript) |
|
|
94
|
+
| `my-code-style/eslint/vue3` | + Vue 3 规则 |
|
|
95
|
+
| `my-code-style/eslint/uniapp` | + uni-app globals |
|
|
96
|
+
| `my-code-style/eslint/flat` | ESLint v9 Flat Config 基础 |
|
|
97
|
+
| `my-code-style/eslint/flat/vue3` | + Vue 3 规则 |
|
|
98
|
+
| `my-code-style/eslint/flat/uniapp` | + uni-app globals |
|
|
99
|
+
| `my-code-style/prettier` | Prettier 配置 |
|
|
100
|
+
| `my-code-style/stylelint` | Stylelint 配置(SCSS + Less) |
|
|
101
|
+
| `my-code-style/stylelint/less-override` | Less 专用覆写配置 |
|
|
102
|
+
| `my-code-style/commitlint` | Commitlint 基础配置 |
|
|
103
|
+
| `my-code-style/commitlint/scopes` | 动态 scope 工具函数 |
|
|
104
|
+
| `my-code-style/versionrc` | standard-version 配置 |
|
|
105
|
+
|
|
106
|
+
## 特性
|
|
107
|
+
|
|
108
|
+
- **双引号**:`quotes: ["error", "double", { avoidEscape: false, allowTemplateLiterals: true }]`
|
|
109
|
+
- **4 空格缩进**:`tabWidth: 4`
|
|
110
|
+
- **无分号**:`semi: false`(`.nvue` 文件除外)
|
|
111
|
+
- **行宽 100**:`printWidth: 100`
|
|
112
|
+
- **小程序适配**:允许 `rpx` 单位、`page` 标签、`::v-deep` 伪类
|
|
113
|
+
- **uni-app globals**:`uni`、`UniApp`、`wx`、`getCurrentPages` 等
|
|
114
|
+
- **Less 支持**:`postcss-less` 解析、`@` 变量放行、`::v-deep` 伪元素
|
|
115
|
+
- **双 ESLint 格式**:v8 (.eslintrc.cjs) 和 v9 (Flat Config) 自动检测
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
package/bin/init.cjs
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* my-code-style-init CLI
|
|
4
|
+
*
|
|
5
|
+
* Scaffolds lint/format/git config files into the current project.
|
|
6
|
+
* Auto-detects ESLint version and CSS preprocessor for optimal config generation.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx my-code-style-init
|
|
10
|
+
* npx my-code-style-init --dry-run
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require("fs")
|
|
14
|
+
const path = require("path")
|
|
15
|
+
const { execSync } = require("child_process")
|
|
16
|
+
|
|
17
|
+
const PROJECT_ROOT = process.cwd()
|
|
18
|
+
const PACKAGE_DIR = path.resolve(__dirname, "..")
|
|
19
|
+
|
|
20
|
+
// --- Helpers ---
|
|
21
|
+
|
|
22
|
+
function log(msg) {
|
|
23
|
+
console.log(` ${msg}`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function success(msg) {
|
|
27
|
+
console.log(` ✓ ${msg}`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function warn(msg) {
|
|
31
|
+
console.log(` ⚠ ${msg}`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ensureDir(dirPath) {
|
|
35
|
+
if (!fs.existsSync(dirPath)) {
|
|
36
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writeFile(dest, content) {
|
|
41
|
+
const destDir = path.dirname(dest)
|
|
42
|
+
ensureDir(destDir)
|
|
43
|
+
fs.writeFileSync(dest, content, "utf8")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fileExists(relative) {
|
|
47
|
+
return fs.existsSync(path.resolve(PROJECT_ROOT, relative))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readTemplate(relativePath) {
|
|
51
|
+
return fs.readFileSync(path.resolve(PACKAGE_DIR, relativePath), "utf8")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getProjectPkg() {
|
|
55
|
+
const pkgPath = path.resolve(PROJECT_ROOT, "package.json")
|
|
56
|
+
if (!fs.existsSync(pkgPath)) return null
|
|
57
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getDependencyVersion(pkg, depName) {
|
|
61
|
+
return (pkg.dependencies && pkg.dependencies[depName])
|
|
62
|
+
|| (pkg.devDependencies && pkg.devDependencies[depName])
|
|
63
|
+
|| null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- Detection helpers ---
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Detect ESLint major version from package.json
|
|
70
|
+
* Returns 8, 9, or null (not installed)
|
|
71
|
+
*/
|
|
72
|
+
function detectEslintVersion() {
|
|
73
|
+
const pkg = getProjectPkg()
|
|
74
|
+
if (!pkg) return null
|
|
75
|
+
const version = getDependencyVersion(pkg, "eslint")
|
|
76
|
+
if (!version) return null
|
|
77
|
+
// Handle range specifiers like "^9.0.0", ">=8.0.0", "9.x"
|
|
78
|
+
const match = version.match(/(\d+)/)
|
|
79
|
+
return match ? parseInt(match[1], 10) : null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Detect CSS preprocessor in use
|
|
84
|
+
* Returns "scss", "less", or "none"
|
|
85
|
+
*/
|
|
86
|
+
function detectCssPreprocessor() {
|
|
87
|
+
const pkg = getProjectPkg()
|
|
88
|
+
if (!pkg) return "scss"
|
|
89
|
+
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
|
|
90
|
+
|
|
91
|
+
const hasSass = allDeps["sass"] || allDeps["node-sass"] || allDeps["sass-loader"]
|
|
92
|
+
const hasLess = allDeps["less"] || allDeps["less-loader"]
|
|
93
|
+
|
|
94
|
+
if (hasLess && !hasSass) return "less"
|
|
95
|
+
if (hasSass) return "scss"
|
|
96
|
+
if (hasLess) return "less"
|
|
97
|
+
|
|
98
|
+
// Fallback: check for existing stylelint config
|
|
99
|
+
if (fileExists(".stylelintrc.cjs") || fileExists(".stylelintrc.js")) {
|
|
100
|
+
try {
|
|
101
|
+
const content = fs.readFileSync(
|
|
102
|
+
path.resolve(PROJECT_ROOT, ".stylelintrc.cjs"),
|
|
103
|
+
"utf8"
|
|
104
|
+
)
|
|
105
|
+
if (content.includes("postcss-less")) return "less"
|
|
106
|
+
} catch {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return "scss" // default
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Detect if project is a uni-app project
|
|
114
|
+
*/
|
|
115
|
+
function isUniAppProject() {
|
|
116
|
+
const pkg = getProjectPkg()
|
|
117
|
+
if (!pkg) return false
|
|
118
|
+
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
|
|
119
|
+
return !!(allDeps["@dcloudio/uni-app"] || allDeps["uni-app"])
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// --- Config templates ---
|
|
123
|
+
|
|
124
|
+
function eslintrcContent(isUniApp) {
|
|
125
|
+
const importPath = isUniApp
|
|
126
|
+
? "my-code-style/eslint/uniapp"
|
|
127
|
+
: "my-code-style/eslint/vue3"
|
|
128
|
+
|
|
129
|
+
return `// ESLint config — powered by my-code-style
|
|
130
|
+
// https://www.npmjs.com/package/my-code-style
|
|
131
|
+
const config = require("${importPath}")
|
|
132
|
+
|
|
133
|
+
// 如果需要引入 unplugin-auto-import 生成的 globals,取消下面的注释:
|
|
134
|
+
// config.extends = [...config.extends, "./.eslintrc-auto-import.json"]
|
|
135
|
+
|
|
136
|
+
module.exports = config
|
|
137
|
+
`
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function eslintFlatConfigContent(isUniApp) {
|
|
141
|
+
const importPath = isUniApp
|
|
142
|
+
? "my-code-style/eslint/flat/uniapp"
|
|
143
|
+
: "my-code-style/eslint/flat/vue3"
|
|
144
|
+
|
|
145
|
+
return `// ESLint Flat Config — powered by my-code-style
|
|
146
|
+
// https://www.npmjs.com/package/my-code-style
|
|
147
|
+
import uniappConfig from "${importPath}"
|
|
148
|
+
|
|
149
|
+
export default uniappConfig
|
|
150
|
+
`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function prettierrcContent() {
|
|
154
|
+
return `// Prettier config — powered by my-code-style
|
|
155
|
+
module.exports = require("my-code-style/prettier")
|
|
156
|
+
`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function prettierignoreContent() {
|
|
160
|
+
return readTemplate("src/prettierignore")
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function stylelintrcContent(cssPreprocessor) {
|
|
164
|
+
const importPath = cssPreprocessor === "less"
|
|
165
|
+
? "my-code-style/stylelint/less"
|
|
166
|
+
: "my-code-style/stylelint"
|
|
167
|
+
|
|
168
|
+
return `// Stylelint config — powered by my-code-style
|
|
169
|
+
module.exports = require("${importPath}")
|
|
170
|
+
`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function commitlintrcContent() {
|
|
174
|
+
return `// Commitlint config — powered by my-code-style
|
|
175
|
+
const base = require("my-code-style/commitlint")
|
|
176
|
+
const { generateScopes, guessCurrentScope } = require("my-code-style/commitlint/scopes")
|
|
177
|
+
|
|
178
|
+
const scopes = generateScopes("src")
|
|
179
|
+
const scopeComplete = guessCurrentScope()
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
...base,
|
|
183
|
+
prompt: {
|
|
184
|
+
...base.prompt,
|
|
185
|
+
customScopesAlign: !scopeComplete ? "top" : "bottom",
|
|
186
|
+
defaultScope: scopeComplete,
|
|
187
|
+
scopes: [...scopes, "mock"],
|
|
188
|
+
allowEmptyIssuePrefixs: false,
|
|
189
|
+
allowCustomIssuePrefixs: false,
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
`
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function versionrcContent() {
|
|
196
|
+
return `// standard-version config — powered by my-code-style
|
|
197
|
+
module.exports = require("my-code-style/versionrc")
|
|
198
|
+
`
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function huskyCommitMsg() {
|
|
202
|
+
return `#!/usr/bin/env sh
|
|
203
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
204
|
+
|
|
205
|
+
npx --no-install commitlint --edit
|
|
206
|
+
`
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function huskyPreCommit() {
|
|
210
|
+
return `#!/usr/bin/env sh
|
|
211
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
212
|
+
|
|
213
|
+
npx --no-install -- lint-staged
|
|
214
|
+
`
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function editorconfigContent() {
|
|
218
|
+
return readTemplate("src/editorconfig")
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function gitattributesContent() {
|
|
222
|
+
return readTemplate("src/gitattributes")
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Generate the lint-staged config based on detected CSS preprocessor
|
|
227
|
+
*/
|
|
228
|
+
function getLintStagedConfig(cssPreprocessor) {
|
|
229
|
+
const styleFiles = cssPreprocessor === "less"
|
|
230
|
+
? "**/*.{vue,css,less,html}"
|
|
231
|
+
: cssPreprocessor === "none"
|
|
232
|
+
? "**/*.{vue,html}"
|
|
233
|
+
: "**/*.{vue,css,scss,html}"
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
"**/*.{html,vue,ts,cjs,json,md}": ["prettier --write"],
|
|
237
|
+
"**/*.{vue,js,ts,jsx,tsx}": ["eslint --cache --fix"],
|
|
238
|
+
[styleFiles]: ["stylelint --fix"],
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// --- Main ---
|
|
243
|
+
|
|
244
|
+
function main() {
|
|
245
|
+
const dryRun = process.argv.includes("--dry-run")
|
|
246
|
+
const label = dryRun ? "[DRY RUN] " : ""
|
|
247
|
+
|
|
248
|
+
console.log("")
|
|
249
|
+
console.log(" my-code-style-init — 初始化项目配置")
|
|
250
|
+
console.log("")
|
|
251
|
+
|
|
252
|
+
if (dryRun) {
|
|
253
|
+
warn("Dry run mode — no files will be written")
|
|
254
|
+
console.log("")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// --- Detection phase ---
|
|
258
|
+
const eslintVersion = detectEslintVersion()
|
|
259
|
+
const cssPreprocessor = detectCssPreprocessor()
|
|
260
|
+
const uniApp = isUniAppProject()
|
|
261
|
+
|
|
262
|
+
// Determine ESLint format
|
|
263
|
+
const useFlatConfig = eslintVersion === 9
|
|
264
|
+
const eslintFormat = useFlatConfig ? "flat" : "eslintrc"
|
|
265
|
+
|
|
266
|
+
log(`检测到 ESLint 版本: ${eslintVersion || "未知"} → 使用 ${eslintFormat} 格式`)
|
|
267
|
+
log(`检测到 CSS 预处理器: ${cssPreprocessor}`)
|
|
268
|
+
if (uniApp) log(`检测到 uni-app 项目`)
|
|
269
|
+
console.log("")
|
|
270
|
+
|
|
271
|
+
// --- Build file list based on detection ---
|
|
272
|
+
const files = []
|
|
273
|
+
|
|
274
|
+
if (useFlatConfig) {
|
|
275
|
+
files.push({
|
|
276
|
+
path: "eslint.config.ts",
|
|
277
|
+
exists: fileExists("eslint.config.ts") || fileExists("eslint.config.js") || fileExists("eslint.config.mjs"),
|
|
278
|
+
content: eslintFlatConfigContent(uniApp),
|
|
279
|
+
})
|
|
280
|
+
} else {
|
|
281
|
+
files.push({
|
|
282
|
+
path: ".eslintrc.cjs",
|
|
283
|
+
exists: fileExists(".eslintrc.cjs"),
|
|
284
|
+
content: eslintrcContent(uniApp),
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
files.push({
|
|
289
|
+
path: ".prettierrc.cjs",
|
|
290
|
+
exists: fileExists(".prettierrc.cjs"),
|
|
291
|
+
content: prettierrcContent(),
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
files.push({
|
|
295
|
+
path: ".prettierignore",
|
|
296
|
+
exists: fileExists(".prettierignore"),
|
|
297
|
+
content: prettierignoreContent(),
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
// Stylelint: always generate, but note if no CSS preprocessor
|
|
301
|
+
if (cssPreprocessor !== "none") {
|
|
302
|
+
files.push({
|
|
303
|
+
path: ".stylelintrc.cjs",
|
|
304
|
+
exists: fileExists(".stylelintrc.cjs"),
|
|
305
|
+
content: stylelintrcContent(cssPreprocessor),
|
|
306
|
+
})
|
|
307
|
+
} else {
|
|
308
|
+
log("跳过 Stylelint 配置 (未检测到 CSS 预处理器)")
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
files.push({
|
|
312
|
+
path: ".commitlintrc.cjs",
|
|
313
|
+
exists: fileExists(".commitlintrc.cjs"),
|
|
314
|
+
content: commitlintrcContent(),
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
files.push({
|
|
318
|
+
path: ".versionrc.js",
|
|
319
|
+
exists: fileExists(".versionrc.js"),
|
|
320
|
+
content: versionrcContent(),
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
files.push({
|
|
324
|
+
path: ".editorconfig",
|
|
325
|
+
exists: fileExists(".editorconfig"),
|
|
326
|
+
content: editorconfigContent(),
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
files.push({
|
|
330
|
+
path: ".gitattributes",
|
|
331
|
+
exists: fileExists(".gitattributes"),
|
|
332
|
+
content: gitattributesContent(),
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// Show what will be created/overwritten
|
|
336
|
+
for (const f of files) {
|
|
337
|
+
if (f.exists) {
|
|
338
|
+
warn(`${label}覆盖 ${f.path}`)
|
|
339
|
+
} else {
|
|
340
|
+
success(`${label}创建 ${f.path}`)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Husky hooks
|
|
345
|
+
const huskyDir = path.resolve(PROJECT_ROOT, ".husky")
|
|
346
|
+
const hasHusky = fs.existsSync(huskyDir)
|
|
347
|
+
if (!hasHusky) {
|
|
348
|
+
success(`${label}创建 .husky/ 目录`)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const hooks = [
|
|
352
|
+
{ path: ".husky/commit-msg", content: huskyCommitMsg() },
|
|
353
|
+
{ path: ".husky/pre-commit", content: huskyPreCommit() },
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
for (const h of hooks) {
|
|
357
|
+
if (fileExists(h.path)) {
|
|
358
|
+
warn(`${label}覆盖 ${h.path}`)
|
|
359
|
+
} else {
|
|
360
|
+
success(`${label}创建 ${h.path}`)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (dryRun) {
|
|
365
|
+
console.log("")
|
|
366
|
+
warn("Dry run 结束,未修改任何文件")
|
|
367
|
+
console.log("")
|
|
368
|
+
return
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Actually write files
|
|
372
|
+
for (const f of files) {
|
|
373
|
+
writeFile(f.path, f.content)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Husky
|
|
377
|
+
if (!hasHusky) {
|
|
378
|
+
ensureDir(huskyDir)
|
|
379
|
+
}
|
|
380
|
+
for (const h of hooks) {
|
|
381
|
+
writeFile(h.path, h.content)
|
|
382
|
+
fs.chmodSync(path.resolve(PROJECT_ROOT, h.path), 0o755)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// package.json scripts + lint-staged
|
|
386
|
+
modifyPackageJson({
|
|
387
|
+
scripts: {
|
|
388
|
+
prepare: "husky install",
|
|
389
|
+
release: "standard-version",
|
|
390
|
+
cz: "czg",
|
|
391
|
+
},
|
|
392
|
+
"lint-staged": getLintStagedConfig(cssPreprocessor),
|
|
393
|
+
})
|
|
394
|
+
success("更新 package.json (scripts + lint-staged)")
|
|
395
|
+
|
|
396
|
+
// Check for missing peerDependencies
|
|
397
|
+
checkPeerDeps({ useFlatConfig, cssPreprocessor })
|
|
398
|
+
|
|
399
|
+
console.log("")
|
|
400
|
+
success("配置初始化完成!")
|
|
401
|
+
console.log("")
|
|
402
|
+
console.log(" 下一步:")
|
|
403
|
+
console.log(" 1. 确保已安装 peerDependencies:")
|
|
404
|
+
console.log(" pnpm add -D my-code-style")
|
|
405
|
+
if (useFlatConfig) {
|
|
406
|
+
console.log(" 2. Flat Config 需要的额外依赖:")
|
|
407
|
+
console.log(" pnpm add -D typescript-eslint globals eslint-plugin-import-x @eslint/js")
|
|
408
|
+
}
|
|
409
|
+
console.log(" 3. 初始化 husky:")
|
|
410
|
+
console.log(" pnpm prepare")
|
|
411
|
+
console.log(" 4. 使用 czg 提交 commit:")
|
|
412
|
+
console.log(" pnpm cz")
|
|
413
|
+
console.log("")
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function modifyPackageJson(updates) {
|
|
417
|
+
const pkgPath = path.resolve(PROJECT_ROOT, "package.json")
|
|
418
|
+
let pkg = {}
|
|
419
|
+
if (fileExists("package.json")) {
|
|
420
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Merge scripts
|
|
424
|
+
if (updates.scripts) {
|
|
425
|
+
pkg.scripts = { ...pkg.scripts, ...updates.scripts }
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Merge lint-staged
|
|
429
|
+
if (updates["lint-staged"]) {
|
|
430
|
+
pkg["lint-staged"] = { ...pkg["lint-staged"], ...updates["lint-staged"] }
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 4) + "\n", "utf8")
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function checkPeerDeps({ useFlatConfig = false, cssPreprocessor = "scss" } = {}) {
|
|
437
|
+
const baseDeps = [
|
|
438
|
+
"eslint",
|
|
439
|
+
"prettier",
|
|
440
|
+
"@commitlint/cli",
|
|
441
|
+
"husky",
|
|
442
|
+
"lint-staged",
|
|
443
|
+
"czg",
|
|
444
|
+
"standard-version",
|
|
445
|
+
]
|
|
446
|
+
|
|
447
|
+
const flatDeps = useFlatConfig
|
|
448
|
+
? ["typescript-eslint", "globals", "eslint-plugin-import-x", "@eslint/js"]
|
|
449
|
+
: []
|
|
450
|
+
|
|
451
|
+
const cssDeps = cssPreprocessor === "scss"
|
|
452
|
+
? ["stylelint", "postcss-scss"]
|
|
453
|
+
: cssPreprocessor === "less"
|
|
454
|
+
? ["stylelint", "postcss-less"]
|
|
455
|
+
: []
|
|
456
|
+
|
|
457
|
+
const allPeerDeps = [...baseDeps, ...flatDeps, ...cssDeps]
|
|
458
|
+
|
|
459
|
+
const pkgPath = path.resolve(PROJECT_ROOT, "package.json")
|
|
460
|
+
if (!fileExists("package.json")) return
|
|
461
|
+
|
|
462
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
463
|
+
const allDeps = {
|
|
464
|
+
...(pkg.dependencies || {}),
|
|
465
|
+
...(pkg.devDependencies || {}),
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const missing = allPeerDeps.filter((dep) => !allDeps[dep])
|
|
469
|
+
if (missing.length > 0) {
|
|
470
|
+
console.log("")
|
|
471
|
+
warn("以下 peerDependencies 未安装,建议安装:")
|
|
472
|
+
warn(`pnpm add -D ${missing.join(" ")}`)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
main()
|
package/package.json
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-code-style",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lint/Format/Git 配置工程化 npm 包(ESLint + Prettier + Stylelint + Commitlint + Husky + standard-version)",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "src/eslint/uniapp.cjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"my-code-style-init": "./bin/init.cjs"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/eslint/uniapp.cjs",
|
|
12
|
+
"./eslint": "./src/eslint/base.cjs",
|
|
13
|
+
"./eslint/vue3": "./src/eslint/vue3.cjs",
|
|
14
|
+
"./eslint/uniapp": "./src/eslint/uniapp.cjs",
|
|
15
|
+
"./eslint/flat": "./src/eslint/flat/base.mjs",
|
|
16
|
+
"./eslint/flat/vue3": "./src/eslint/flat/vue3.mjs",
|
|
17
|
+
"./eslint/flat/uniapp": "./src/eslint/flat/uniapp.mjs",
|
|
18
|
+
"./prettier": "./src/prettier/index.cjs",
|
|
19
|
+
"./stylelint": "./src/stylelint/index.cjs",
|
|
20
|
+
"./stylelint/less": "./src/stylelint/less.cjs",
|
|
21
|
+
"./stylelint/less-override": "./src/stylelint/less-override.cjs",
|
|
22
|
+
"./commitlint": "./src/commitlint/base.cjs",
|
|
23
|
+
"./commitlint/scopes": "./src/commitlint/scopes.cjs",
|
|
24
|
+
"./versionrc": "./src/versionrc/index.cjs"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"src",
|
|
28
|
+
"bin",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"release": "standard-version"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@commitlint/cli": ">=18.0.0",
|
|
39
|
+
"@commitlint/config-conventional": ">=18.0.0",
|
|
40
|
+
"@eslint/eslintrc": ">=3.0.0",
|
|
41
|
+
"@eslint/js": ">=9.0.0",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": ">=6.0.0",
|
|
43
|
+
"@typescript-eslint/parser": ">=6.0.0",
|
|
44
|
+
"czg": ">=1.0.0",
|
|
45
|
+
"eslint": ">=8.0.0",
|
|
46
|
+
"eslint-config-prettier": ">=9.0.0",
|
|
47
|
+
"eslint-config-standard": ">=17.0.0",
|
|
48
|
+
"eslint-import-resolver-typescript": ">=3.0.0",
|
|
49
|
+
"eslint-plugin-import": ">=2.0.0",
|
|
50
|
+
"eslint-plugin-import-x": ">=3.0.0",
|
|
51
|
+
"eslint-plugin-prettier": ">=5.0.0",
|
|
52
|
+
"eslint-plugin-vue": ">=9.0.0",
|
|
53
|
+
"globals": ">=15.0.0",
|
|
54
|
+
"husky": ">=8.0.0",
|
|
55
|
+
"lint-staged": ">=15.0.0",
|
|
56
|
+
"postcss-html": ">=1.0.0",
|
|
57
|
+
"postcss-less": ">=6.0.0",
|
|
58
|
+
"postcss-scss": ">=4.0.0",
|
|
59
|
+
"prettier": ">=3.0.0",
|
|
60
|
+
"standard-version": ">=9.0.0",
|
|
61
|
+
"stylelint": ">=16.0.0",
|
|
62
|
+
"stylelint-config-html": ">=1.0.0",
|
|
63
|
+
"stylelint-config-recess-order": ">=4.0.0",
|
|
64
|
+
"stylelint-config-recommended": ">=14.0.0",
|
|
65
|
+
"stylelint-config-recommended-scss": ">=14.0.0",
|
|
66
|
+
"stylelint-config-recommended-vue": ">=1.0.0",
|
|
67
|
+
"stylelint-prettier": ">=5.0.0",
|
|
68
|
+
"typescript-eslint": ">=8.0.0"
|
|
69
|
+
},
|
|
70
|
+
"peerDependenciesMeta": {
|
|
71
|
+
"@eslint/eslintrc": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@eslint/js": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"eslint-plugin-vue": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"eslint-plugin-import-x": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"globals": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"postcss-less": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"postcss-scss": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"postcss-html": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"stylelint-config-recommended-scss": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"stylelint-config-recommended-vue": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"stylelint-config-html": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"typescript-eslint": {
|
|
105
|
+
"optional": true
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"devDependencies": {
|
|
109
|
+
"standard-version": "^9.5.0"
|
|
110
|
+
},
|
|
111
|
+
"keywords": [
|
|
112
|
+
"eslint-config",
|
|
113
|
+
"prettier-config",
|
|
114
|
+
"stylelint-config",
|
|
115
|
+
"commitlint-config",
|
|
116
|
+
"vue",
|
|
117
|
+
"uni-app",
|
|
118
|
+
"typescript"
|
|
119
|
+
],
|
|
120
|
+
"license": "MIT"
|
|
121
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Commitlint base config (without project-specific dynamic scopes)
|
|
2
|
+
module.exports = {
|
|
3
|
+
ignores: [(commit) => commit.includes("init")],
|
|
4
|
+
extends: ["@commitlint/config-conventional"],
|
|
5
|
+
rules: {
|
|
6
|
+
"body-leading-blank": [2, "always"],
|
|
7
|
+
"footer-leading-blank": [1, "always"],
|
|
8
|
+
"header-max-length": [2, "always", 108],
|
|
9
|
+
"subject-empty": [2, "never"],
|
|
10
|
+
"type-empty": [2, "never"],
|
|
11
|
+
"subject-case": [0],
|
|
12
|
+
"type-enum": [
|
|
13
|
+
2,
|
|
14
|
+
"always",
|
|
15
|
+
[
|
|
16
|
+
"feat",
|
|
17
|
+
"fix",
|
|
18
|
+
"perf",
|
|
19
|
+
"style",
|
|
20
|
+
"docs",
|
|
21
|
+
"test",
|
|
22
|
+
"refactor",
|
|
23
|
+
"build",
|
|
24
|
+
"ci",
|
|
25
|
+
"chore",
|
|
26
|
+
"revert",
|
|
27
|
+
"wip",
|
|
28
|
+
"workflow",
|
|
29
|
+
"types",
|
|
30
|
+
"release",
|
|
31
|
+
],
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
prompt: {
|
|
35
|
+
alias: {
|
|
36
|
+
f: "docs: fix typos",
|
|
37
|
+
r: "docs: update README",
|
|
38
|
+
s: "style: update code format",
|
|
39
|
+
b: "build: bump dependencies",
|
|
40
|
+
c: "chore: update config",
|
|
41
|
+
},
|
|
42
|
+
typesAppend: [
|
|
43
|
+
{ value: "wip", name: "wip: work in process" },
|
|
44
|
+
{ value: "workflow", name: "workflow: workflow improvements" },
|
|
45
|
+
{ value: "types", name: "types: type definition file changes" },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Commitlint scopes helper — dynamically generate scopes from project directories
|
|
2
|
+
const fs = require("fs")
|
|
3
|
+
const path = require("path")
|
|
4
|
+
const { execSync } = require("child_process")
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate scopes by reading directory names
|
|
8
|
+
* @param {string|string[]} srcDirs - Source directory name(s) relative to cwd
|
|
9
|
+
* @returns {string[]} Scope names
|
|
10
|
+
*/
|
|
11
|
+
function generateScopes(srcDirs = "src") {
|
|
12
|
+
const dirs = Array.isArray(srcDirs) ? srcDirs : [srcDirs]
|
|
13
|
+
const allScopes = []
|
|
14
|
+
|
|
15
|
+
for (const srcDir of dirs) {
|
|
16
|
+
const resolved = path.resolve(process.cwd(), srcDir)
|
|
17
|
+
if (!fs.existsSync(resolved)) continue
|
|
18
|
+
const scopes = fs
|
|
19
|
+
.readdirSync(resolved, { withFileTypes: true })
|
|
20
|
+
.filter((dirent) => dirent.isDirectory())
|
|
21
|
+
.map((dirent) => dirent.name.replace(/s$/, ""))
|
|
22
|
+
allScopes.push(...scopes)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return [...new Set(allScopes)]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Guess the current scope from git status (modified files under src/)
|
|
30
|
+
* @returns {string|undefined}
|
|
31
|
+
*/
|
|
32
|
+
function guessCurrentScope() {
|
|
33
|
+
try {
|
|
34
|
+
const output = execSync("git status --porcelain || true").toString().trim()
|
|
35
|
+
const line = output.split("\n").find((r) => r.includes("M src"))
|
|
36
|
+
if (!line) return undefined
|
|
37
|
+
return line
|
|
38
|
+
.replace(/\//g, "%%")
|
|
39
|
+
.match(/src%%((\w|-)*)/)?.[1]
|
|
40
|
+
?.replace(/s$/, "")
|
|
41
|
+
} catch {
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { generateScopes, guessCurrentScope }
|
package/src/editorconfig
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
indent_style = space
|
|
6
|
+
indent_size = 4
|
|
7
|
+
end_of_line = lf
|
|
8
|
+
insert_final_newline = true
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
|
|
11
|
+
[*.md]
|
|
12
|
+
trim_trailing_whitespace = false
|
|
13
|
+
|
|
14
|
+
[*.{json,yml,yaml}]
|
|
15
|
+
indent_size = 2
|
|
16
|
+
|
|
17
|
+
[*.nvue]
|
|
18
|
+
indent_size = 4
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Base ESLint config for TypeScript projects
|
|
2
|
+
module.exports = {
|
|
3
|
+
env: {
|
|
4
|
+
browser: true,
|
|
5
|
+
es2021: true,
|
|
6
|
+
node: true,
|
|
7
|
+
},
|
|
8
|
+
extends: [
|
|
9
|
+
"eslint:recommended",
|
|
10
|
+
"plugin:@typescript-eslint/recommended",
|
|
11
|
+
"plugin:import/recommended",
|
|
12
|
+
"standard",
|
|
13
|
+
"prettier",
|
|
14
|
+
"plugin:prettier/recommended",
|
|
15
|
+
],
|
|
16
|
+
parserOptions: {
|
|
17
|
+
ecmaVersion: "latest",
|
|
18
|
+
parser: "@typescript-eslint/parser",
|
|
19
|
+
sourceType: "module",
|
|
20
|
+
},
|
|
21
|
+
plugins: ["@typescript-eslint", "prettier", "import"],
|
|
22
|
+
rules: {
|
|
23
|
+
// Prettier 集成
|
|
24
|
+
"prettier/prettier": ["error", { singleQuote: false, tabWidth: 4 }],
|
|
25
|
+
// Import 相关
|
|
26
|
+
"import/no-unresolved": "off",
|
|
27
|
+
"import/extensions": [
|
|
28
|
+
"error",
|
|
29
|
+
"ignorePackages",
|
|
30
|
+
{ js: "never", jsx: "never", ts: "never", tsx: "never" },
|
|
31
|
+
],
|
|
32
|
+
"import/prefer-default-export": ["off"],
|
|
33
|
+
"import/no-extraneous-dependencies": "off",
|
|
34
|
+
// TypeScript
|
|
35
|
+
"@typescript-eslint/no-redeclare": "error",
|
|
36
|
+
// 常用关闭
|
|
37
|
+
"no-console": ["off"],
|
|
38
|
+
"no-plusplus": "off",
|
|
39
|
+
"no-shadow": "off",
|
|
40
|
+
"no-underscore-dangle": "off",
|
|
41
|
+
"no-use-before-define": "off",
|
|
42
|
+
"no-undef": "off",
|
|
43
|
+
"no-unused-vars": "off",
|
|
44
|
+
"no-param-reassign": "off",
|
|
45
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
46
|
+
"no-redeclare": "off",
|
|
47
|
+
"prefer-promise-reject-errors": "off",
|
|
48
|
+
// 引号规则
|
|
49
|
+
quotes: ["error", "double", { avoidEscape: false, allowTemplateLiterals: true }],
|
|
50
|
+
},
|
|
51
|
+
settings: {
|
|
52
|
+
"import/parsers": { "@typescript-eslint/parser": [".ts", ".tsx"] },
|
|
53
|
+
"import/resolver": { typescript: {} },
|
|
54
|
+
},
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Shared constants and rule objects for Flat Config files
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prettier plugin rules — mirrors base.cjs prettier/prettier config
|
|
5
|
+
*/
|
|
6
|
+
export const prettierRules = {
|
|
7
|
+
"prettier/prettier": ["error", { singleQuote: false, tabWidth: 4 }],
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Common rules shared across all flat config levels — mirrors base.cjs rules
|
|
12
|
+
*/
|
|
13
|
+
export const commonRules = {
|
|
14
|
+
// Import rules (using import-x for flat config compatibility)
|
|
15
|
+
"import-x/no-unresolved": "off",
|
|
16
|
+
"import-x/extensions": [
|
|
17
|
+
"error",
|
|
18
|
+
"ignorePackages",
|
|
19
|
+
{ js: "never", jsx: "never", ts: "never", tsx: "never" },
|
|
20
|
+
],
|
|
21
|
+
"import-x/prefer-default-export": "off",
|
|
22
|
+
"import-x/no-extraneous-dependencies": "off",
|
|
23
|
+
// TypeScript
|
|
24
|
+
"@typescript-eslint/no-redeclare": "error",
|
|
25
|
+
// Commonly disabled
|
|
26
|
+
"no-console": "off",
|
|
27
|
+
"no-plusplus": "off",
|
|
28
|
+
"no-shadow": "off",
|
|
29
|
+
"no-underscore-dangle": "off",
|
|
30
|
+
"no-use-before-define": "off",
|
|
31
|
+
"no-undef": "off",
|
|
32
|
+
"no-unused-vars": "off",
|
|
33
|
+
"no-param-reassign": "off",
|
|
34
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
35
|
+
"no-redeclare": "off",
|
|
36
|
+
"prefer-promise-reject-errors": "off",
|
|
37
|
+
// Quotes
|
|
38
|
+
quotes: ["error", "double", { avoidEscape: false, allowTemplateLiterals: true }],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Uni-app global variables — mirrors uniapp.cjs globals
|
|
43
|
+
*/
|
|
44
|
+
export const uniappGlobals = {
|
|
45
|
+
$t: true,
|
|
46
|
+
uni: true,
|
|
47
|
+
UniApp: true,
|
|
48
|
+
wx: true,
|
|
49
|
+
WechatMiniprogram: true,
|
|
50
|
+
getCurrentPages: true,
|
|
51
|
+
UniHelper: true,
|
|
52
|
+
Page: true,
|
|
53
|
+
App: true,
|
|
54
|
+
NodeJS: true,
|
|
55
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Base ESLint Flat Config for TypeScript projects (ESLint v9+)
|
|
2
|
+
// Equivalent to src/eslint/base.cjs but in flat config format
|
|
3
|
+
|
|
4
|
+
import tseslint from "typescript-eslint"
|
|
5
|
+
import importX from "eslint-plugin-import-x"
|
|
6
|
+
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
|
7
|
+
import eslintConfigPrettier from "eslint-config-prettier"
|
|
8
|
+
import globals from "globals"
|
|
9
|
+
import { prettierRules, commonRules } from "./_shared.mjs"
|
|
10
|
+
|
|
11
|
+
export default [
|
|
12
|
+
// Ignore patterns
|
|
13
|
+
{
|
|
14
|
+
ignores: ["**/node_modules/**", "**/dist/**", "**/coverage/**"],
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// TypeScript recommended rules
|
|
18
|
+
// Replaces: extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"]
|
|
19
|
+
...tseslint.configs.recommended,
|
|
20
|
+
|
|
21
|
+
// eslint-plugin-import-x configuration
|
|
22
|
+
// Replaces: plugin:import/recommended + eslint-config-standard (via FlatCompat)
|
|
23
|
+
{
|
|
24
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
|
25
|
+
plugins: {
|
|
26
|
+
"import-x": importX,
|
|
27
|
+
},
|
|
28
|
+
rules: {
|
|
29
|
+
...importX.configs.recommended.rules,
|
|
30
|
+
},
|
|
31
|
+
settings: {
|
|
32
|
+
"import-x/resolver": {
|
|
33
|
+
typescript: true,
|
|
34
|
+
node: true,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Prettier integration
|
|
40
|
+
// Replaces: extends: ["plugin:prettier/recommended"]
|
|
41
|
+
eslintPluginPrettierRecommended,
|
|
42
|
+
|
|
43
|
+
// eslint-config-prettier MUST be last — disables conflicting ESLint rules
|
|
44
|
+
eslintConfigPrettier,
|
|
45
|
+
|
|
46
|
+
// Global language options + shared rules
|
|
47
|
+
{
|
|
48
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
|
49
|
+
languageOptions: {
|
|
50
|
+
ecmaVersion: "latest",
|
|
51
|
+
sourceType: "module",
|
|
52
|
+
globals: {
|
|
53
|
+
...globals.browser,
|
|
54
|
+
...globals.node,
|
|
55
|
+
...globals.es2021,
|
|
56
|
+
},
|
|
57
|
+
parser: tseslint.parser,
|
|
58
|
+
parserOptions: {
|
|
59
|
+
ecmaVersion: "latest",
|
|
60
|
+
sourceType: "module",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
rules: {
|
|
64
|
+
...prettierRules,
|
|
65
|
+
...commonRules,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// .nvue file handling (uni-app)
|
|
70
|
+
{
|
|
71
|
+
files: ["**/*.nvue"],
|
|
72
|
+
languageOptions: {
|
|
73
|
+
parserOptions: {
|
|
74
|
+
sourceType: "script",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// uni-app ESLint Flat Config — extends vue3 flat config, adds uni-app globals
|
|
2
|
+
|
|
3
|
+
import vue3Config from "./vue3.mjs"
|
|
4
|
+
import { uniappGlobals } from "./_shared.mjs"
|
|
5
|
+
|
|
6
|
+
export default [
|
|
7
|
+
// Spread all Vue 3 configs
|
|
8
|
+
...vue3Config,
|
|
9
|
+
|
|
10
|
+
// Add uni-app globals
|
|
11
|
+
{
|
|
12
|
+
files: ["**/*.{vue,ts,js,nvue}"],
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: uniappGlobals,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Vue 3 ESLint Flat Config — extends base, adds Vue-specific rules
|
|
2
|
+
|
|
3
|
+
import pluginVue from "eslint-plugin-vue"
|
|
4
|
+
import baseConfig from "./base.mjs"
|
|
5
|
+
|
|
6
|
+
export default [
|
|
7
|
+
// Spread all base configs first
|
|
8
|
+
...baseConfig,
|
|
9
|
+
|
|
10
|
+
// Vue 3 essential rules
|
|
11
|
+
// Replaces: extends: ["plugin:vue/vue3-essential"]
|
|
12
|
+
// Must scope to .vue files only
|
|
13
|
+
...pluginVue.configs["flat/essential"].map((config) => ({
|
|
14
|
+
...config,
|
|
15
|
+
files: ["**/*.vue"],
|
|
16
|
+
})),
|
|
17
|
+
|
|
18
|
+
// Vue plugin setup and custom overrides
|
|
19
|
+
{
|
|
20
|
+
files: ["**/*.vue"],
|
|
21
|
+
languageOptions: {
|
|
22
|
+
parserOptions: {
|
|
23
|
+
parser: "@typescript-eslint/parser",
|
|
24
|
+
sourceType: "module",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
"vue/multi-word-component-names": "off",
|
|
29
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
30
|
+
"vue/no-mutating-props": ["error", { shallowOnly: true }],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// .nvue specific overrides
|
|
35
|
+
{
|
|
36
|
+
files: ["*.nvue"],
|
|
37
|
+
languageOptions: {
|
|
38
|
+
parserOptions: {
|
|
39
|
+
sourceType: "script",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
rules: {
|
|
43
|
+
"vue/comment-directive": "off",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// uni-app ESLint config — extends Vue 3, adds uni-app globals and rules
|
|
2
|
+
const vue3 = require("./vue3.cjs")
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
...vue3,
|
|
6
|
+
extends: vue3.extends,
|
|
7
|
+
globals: {
|
|
8
|
+
$t: true,
|
|
9
|
+
uni: true,
|
|
10
|
+
UniApp: true,
|
|
11
|
+
wx: true,
|
|
12
|
+
WechatMiniprogram: true,
|
|
13
|
+
getCurrentPages: true,
|
|
14
|
+
UniHelper: true,
|
|
15
|
+
Page: true,
|
|
16
|
+
App: true,
|
|
17
|
+
NodeJS: true,
|
|
18
|
+
},
|
|
19
|
+
rules: {
|
|
20
|
+
...vue3.rules,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Vue 3 ESLint config — extends base, adds Vue-specific rules
|
|
2
|
+
const base = require("./base.cjs")
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
...base,
|
|
6
|
+
extends: [...base.extends, "plugin:vue/vue3-essential"],
|
|
7
|
+
plugins: [...base.plugins, "vue"],
|
|
8
|
+
overrides: [
|
|
9
|
+
{
|
|
10
|
+
env: { node: true },
|
|
11
|
+
files: [".eslintrc.{js,cjs}", "*.nvue"],
|
|
12
|
+
parserOptions: { sourceType: "script" },
|
|
13
|
+
rules: { "vue/comment-directive": "off" },
|
|
14
|
+
},
|
|
15
|
+
...(base.overrides || []),
|
|
16
|
+
],
|
|
17
|
+
rules: {
|
|
18
|
+
...base.rules,
|
|
19
|
+
"vue/multi-word-component-names": "off",
|
|
20
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
21
|
+
"vue/no-mutating-props": ["error", { shallowOnly: true }],
|
|
22
|
+
},
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
* text eol=lf
|
|
2
|
+
|
|
3
|
+
# Binary files — do not apply EOL conversion
|
|
4
|
+
*.aar binary
|
|
5
|
+
*.png binary
|
|
6
|
+
*.jpg binary
|
|
7
|
+
*.jpeg binary
|
|
8
|
+
*.gif binary
|
|
9
|
+
*.ico binary
|
|
10
|
+
*.webp binary
|
|
11
|
+
*.bmp binary
|
|
12
|
+
*.dex binary
|
|
13
|
+
*.DS_Store binary
|
|
14
|
+
*.jar binary
|
|
15
|
+
*.ttf binary
|
|
16
|
+
*.woff binary
|
|
17
|
+
*.woff2 binary
|
|
18
|
+
*.apk binary
|
|
19
|
+
*.plist binary
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Prettier config
|
|
2
|
+
module.exports = {
|
|
3
|
+
singleQuote: false,
|
|
4
|
+
printWidth: 100,
|
|
5
|
+
tabWidth: 4,
|
|
6
|
+
useTabs: false,
|
|
7
|
+
semi: false,
|
|
8
|
+
trailingComma: "all",
|
|
9
|
+
endOfLine: "lf",
|
|
10
|
+
htmlWhitespaceSensitivity: "ignore",
|
|
11
|
+
overrides: [
|
|
12
|
+
{
|
|
13
|
+
files: "*.json",
|
|
14
|
+
options: { trailingComma: "none" },
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
files: "*.nvue",
|
|
18
|
+
options: { semi: true },
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Stylelint config for Vue/SCSS/Less projects
|
|
2
|
+
module.exports = {
|
|
3
|
+
root: true,
|
|
4
|
+
extends: [
|
|
5
|
+
"stylelint-config-recommended",
|
|
6
|
+
"stylelint-config-recommended-scss",
|
|
7
|
+
"stylelint-config-recommended-vue/scss",
|
|
8
|
+
"stylelint-config-html/vue",
|
|
9
|
+
"stylelint-config-recess-order",
|
|
10
|
+
],
|
|
11
|
+
plugins: ["stylelint-prettier"],
|
|
12
|
+
overrides: [
|
|
13
|
+
{
|
|
14
|
+
files: ["**/*.{vue,html}"],
|
|
15
|
+
customSyntax: "postcss-html",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
files: ["**/*.{css,scss}"],
|
|
19
|
+
customSyntax: "postcss-scss",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
files: ["**/*.less"],
|
|
23
|
+
customSyntax: "postcss-less",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
rules: {
|
|
27
|
+
"prettier/prettier": true,
|
|
28
|
+
// Less and SCSS both use @ for non-standard at-rules
|
|
29
|
+
"at-rule-no-unknown": null,
|
|
30
|
+
// 允许 global、export、v-deep 等伪类
|
|
31
|
+
"selector-pseudo-class-no-unknown": [
|
|
32
|
+
true,
|
|
33
|
+
{ ignorePseudoClasses: ["global", "export", "v-deep", "deep"] },
|
|
34
|
+
],
|
|
35
|
+
// Allow ::v-deep / /deep/ pseudo-elements
|
|
36
|
+
"selector-pseudo-element-no-unknown": [
|
|
37
|
+
true,
|
|
38
|
+
{ ignorePseudoElements: ["v-deep", "deep", "ng-deep"] },
|
|
39
|
+
],
|
|
40
|
+
// Less built-in functions unknown to Stylelint
|
|
41
|
+
"function-no-unknown": null,
|
|
42
|
+
// 允许小程序单位 rpx
|
|
43
|
+
"unit-no-unknown": [true, { ignoreUnits: ["rpx"] }],
|
|
44
|
+
// 允许小程序 page 标签
|
|
45
|
+
"selector-type-no-unknown": [true, { ignoreTypes: ["page"] }],
|
|
46
|
+
"comment-empty-line-before": "never",
|
|
47
|
+
"custom-property-empty-line-before": "never",
|
|
48
|
+
"no-empty-source": null,
|
|
49
|
+
"comment-no-empty": null,
|
|
50
|
+
"no-duplicate-selectors": null,
|
|
51
|
+
"scss/comment-no-empty": null,
|
|
52
|
+
"selector-class-pattern": null,
|
|
53
|
+
"font-family-no-missing-generic-family-keyword": null,
|
|
54
|
+
},
|
|
55
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Less-specific Stylelint override configuration
|
|
2
|
+
// Usage: merge into your Stylelint config when your project uses Less
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
overrides: [
|
|
6
|
+
{
|
|
7
|
+
files: ["**/*.less"],
|
|
8
|
+
customSyntax: "postcss-less",
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
rules: {
|
|
12
|
+
// Less uses @ for variables, mixins, etc. — disable globally
|
|
13
|
+
"at-rule-no-unknown": null,
|
|
14
|
+
// Less built-in functions (darken, lighten, fade...) unknown to Stylelint
|
|
15
|
+
"function-no-unknown": null,
|
|
16
|
+
// Allow ::v-deep / /deep/ / ::ng-deep pseudo-elements
|
|
17
|
+
"selector-pseudo-element-no-unknown": [
|
|
18
|
+
true,
|
|
19
|
+
{ ignorePseudoElements: ["v-deep", "deep", "ng-deep"] },
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Stylelint config for Vue/Less projects
|
|
2
|
+
module.exports = {
|
|
3
|
+
root: true,
|
|
4
|
+
extends: [
|
|
5
|
+
"stylelint-config-recommended",
|
|
6
|
+
"stylelint-config-recommended-vue",
|
|
7
|
+
"stylelint-config-html/vue",
|
|
8
|
+
"stylelint-config-recess-order",
|
|
9
|
+
],
|
|
10
|
+
plugins: ["stylelint-prettier"],
|
|
11
|
+
overrides: [
|
|
12
|
+
{
|
|
13
|
+
files: ["**/*.{vue,html}"],
|
|
14
|
+
customSyntax: "postcss-html",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
files: ["**/*.less"],
|
|
18
|
+
customSyntax: "postcss-less",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
rules: {
|
|
22
|
+
"prettier/prettier": true,
|
|
23
|
+
// Less uses @ for variables/mixins/etc
|
|
24
|
+
"at-rule-no-unknown": null,
|
|
25
|
+
// 允许 global、export、v-deep 等伪类
|
|
26
|
+
"selector-pseudo-class-no-unknown": [
|
|
27
|
+
true,
|
|
28
|
+
{ ignorePseudoClasses: ["global", "export", "v-deep", "deep"] },
|
|
29
|
+
],
|
|
30
|
+
// Allow ::v-deep / /deep/ pseudo-elements
|
|
31
|
+
"selector-pseudo-element-no-unknown": [
|
|
32
|
+
true,
|
|
33
|
+
{ ignorePseudoElements: ["v-deep", "deep", "ng-deep"] },
|
|
34
|
+
],
|
|
35
|
+
// Less built-in functions unknown to Stylelint
|
|
36
|
+
"function-no-unknown": null,
|
|
37
|
+
// 允许小程序单位 rpx
|
|
38
|
+
"unit-no-unknown": [true, { ignoreUnits: ["rpx"] }],
|
|
39
|
+
// 允许小程序 page 标签
|
|
40
|
+
"selector-type-no-unknown": [true, { ignoreTypes: ["page"] }],
|
|
41
|
+
"comment-empty-line-before": "never",
|
|
42
|
+
"custom-property-empty-line-before": "never",
|
|
43
|
+
"no-empty-source": null,
|
|
44
|
+
"comment-no-empty": null,
|
|
45
|
+
"no-duplicate-selectors": null,
|
|
46
|
+
"selector-class-pattern": null,
|
|
47
|
+
"font-family-no-missing-generic-family-keyword": null,
|
|
48
|
+
},
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// standard-version config
|
|
2
|
+
module.exports = {
|
|
3
|
+
header: "## 变更日志\n",
|
|
4
|
+
types: [
|
|
5
|
+
{ type: "feat", section: "✨ Features | 新功能" },
|
|
6
|
+
{ type: "fix", section: "🐛 Bug Fixes | Bug 修复" },
|
|
7
|
+
],
|
|
8
|
+
skip: {
|
|
9
|
+
bump: false,
|
|
10
|
+
changelog: false,
|
|
11
|
+
commit: false,
|
|
12
|
+
tag: false,
|
|
13
|
+
},
|
|
14
|
+
}
|