my-code-style 1.4.0 → 1.6.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/CHANGELOG.md +131 -0
- package/LICENSE +21 -0
- package/README.md +185 -9
- package/bin/init +365 -66
- package/package.json +13 -10
- package/src/commitlint/scopes.cjs +83 -22
- package/src/eslint/base.cjs +21 -11
- package/src/eslint/flat/_shared.mjs +16 -11
- package/src/eslint/flat/base.mjs +21 -11
- package/src/eslint/flat/vue3.mjs +6 -1
- package/src/eslint/vue3.cjs +5 -1
- package/src/gitignore +1 -0
- package/src/husky/commit-msg +5 -1
- package/src/husky/pre-commit +6 -1
|
@@ -25,34 +25,88 @@ const PLURAL_MAP = {
|
|
|
25
25
|
assets: "asset",
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Plurals whose singular cannot be derived by rule: uncountables, irregulars,
|
|
29
|
+
// and the "-e" + "s" words that would otherwise lose their stem ("caches" →
|
|
30
|
+
// "cach"). Everything else is handled by the suffix rules below.
|
|
31
|
+
const EXACT_SINGULAR = {
|
|
32
|
+
news: "news",
|
|
33
|
+
series: "series",
|
|
34
|
+
movies: "movie",
|
|
35
|
+
cookies: "cookie",
|
|
36
|
+
caches: "cache",
|
|
37
|
+
chases: "chase",
|
|
38
|
+
niches: "niche",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Directory names that already read as singular (or are conventional as-is)
|
|
42
|
+
const NON_PLURAL = [
|
|
43
|
+
"status",
|
|
44
|
+
"canvas",
|
|
45
|
+
"bus",
|
|
46
|
+
"pass",
|
|
47
|
+
"process",
|
|
48
|
+
"css",
|
|
49
|
+
"less",
|
|
50
|
+
"scss",
|
|
51
|
+
"js",
|
|
52
|
+
"ts",
|
|
53
|
+
"types",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
// Singulars that legitimately end in "s"/"z" and therefore take "-es":
|
|
57
|
+
// bus → buses, status → statuses. Without this list "buses" would follow the
|
|
58
|
+
// "-se + s" reading and become "buse".
|
|
59
|
+
const SIBILANT_SINGULARS = [
|
|
60
|
+
"bus",
|
|
61
|
+
"gas",
|
|
62
|
+
"canvas",
|
|
63
|
+
"alias",
|
|
64
|
+
"atlas",
|
|
65
|
+
"bias",
|
|
66
|
+
"virus",
|
|
67
|
+
"status",
|
|
68
|
+
"process",
|
|
69
|
+
"lens",
|
|
70
|
+
"plus",
|
|
71
|
+
"focus",
|
|
72
|
+
"campus",
|
|
73
|
+
"analysis",
|
|
74
|
+
"axis",
|
|
75
|
+
"basis",
|
|
76
|
+
"crisis",
|
|
77
|
+
"thesis",
|
|
78
|
+
"quiz",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
// Unambiguous "-es" plurals: the stem ends in a doubled sibilant or a digraph
|
|
82
|
+
// (classes, boxes, churches, dishes, buzzes, batches, fixes).
|
|
83
|
+
const ES_PLURAL = /(?:ss|zz|ch|sh|x)es$/i
|
|
84
|
+
// "-ses"/"-zes" are ambiguous: "cases" is case + s, "buses" is bus + es.
|
|
85
|
+
const S_OR_Z_ES_PLURAL = /[sz]es$/i
|
|
86
|
+
|
|
28
87
|
function toSingular(name) {
|
|
29
88
|
if (!name) {
|
|
30
89
|
return name
|
|
31
90
|
}
|
|
32
|
-
|
|
33
|
-
|
|
91
|
+
const lower = name.toLowerCase()
|
|
92
|
+
if (PLURAL_MAP[lower]) {
|
|
93
|
+
return PLURAL_MAP[lower]
|
|
94
|
+
}
|
|
95
|
+
if (EXACT_SINGULAR[lower]) {
|
|
96
|
+
return EXACT_SINGULAR[lower]
|
|
34
97
|
}
|
|
35
|
-
if (
|
|
98
|
+
if (NON_PLURAL.includes(lower)) {
|
|
99
|
+
return name
|
|
100
|
+
}
|
|
101
|
+
if (/ies$/i.test(name) && name.length > 4) {
|
|
36
102
|
return name.slice(0, -3) + "y"
|
|
37
103
|
}
|
|
38
|
-
if (
|
|
104
|
+
if (ES_PLURAL.test(name)) {
|
|
39
105
|
return name.slice(0, -2)
|
|
40
106
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"bus",
|
|
45
|
-
"pass",
|
|
46
|
-
"process",
|
|
47
|
-
"css",
|
|
48
|
-
"less",
|
|
49
|
-
"scss",
|
|
50
|
-
"js",
|
|
51
|
-
"ts",
|
|
52
|
-
"types",
|
|
53
|
-
]
|
|
54
|
-
if (nonPluralEndingInS.includes(name.toLowerCase())) {
|
|
55
|
-
return name
|
|
107
|
+
if (S_OR_Z_ES_PLURAL.test(name)) {
|
|
108
|
+
const esStem = name.slice(0, -2).toLowerCase()
|
|
109
|
+
return SIBILANT_SINGULARS.includes(esStem) ? name.slice(0, -2) : name.slice(0, -1)
|
|
56
110
|
}
|
|
57
111
|
if (name.endsWith("s") && !name.endsWith("ss") && name.length > 3) {
|
|
58
112
|
return name.slice(0, -1)
|
|
@@ -71,11 +125,15 @@ function generateScopes(srcDirs = "src") {
|
|
|
71
125
|
|
|
72
126
|
for (const srcDir of dirs) {
|
|
73
127
|
const resolved = path.resolve(process.cwd(), srcDir)
|
|
74
|
-
|
|
128
|
+
let entries
|
|
129
|
+
try {
|
|
130
|
+
// A missing path, a plain file, or an unreadable directory must not
|
|
131
|
+
// break the commitlint config load — just yield no scopes.
|
|
132
|
+
entries = fs.readdirSync(resolved, { withFileTypes: true })
|
|
133
|
+
} catch {
|
|
75
134
|
continue
|
|
76
135
|
}
|
|
77
|
-
const scopes =
|
|
78
|
-
.readdirSync(resolved, { withFileTypes: true })
|
|
136
|
+
const scopes = entries
|
|
79
137
|
.filter((dirent) => dirent.isDirectory())
|
|
80
138
|
.map((dirent) => toSingular(dirent.name))
|
|
81
139
|
allScopes.push(...scopes)
|
|
@@ -89,6 +147,8 @@ function getStagedFiles(cwd) {
|
|
|
89
147
|
const output = execSync("git diff --cached --name-only -z", {
|
|
90
148
|
cwd,
|
|
91
149
|
stdio: ["pipe", "pipe", "ignore"],
|
|
150
|
+
// 大仓库/大量暂存文件可能超过默认 1MB,避免 ENOBUFS 导致 scope 猜测整体失败
|
|
151
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
92
152
|
}).toString()
|
|
93
153
|
return output.split("\0").filter(Boolean)
|
|
94
154
|
} catch {
|
|
@@ -96,6 +156,7 @@ function getStagedFiles(cwd) {
|
|
|
96
156
|
const output = execSync("git status --porcelain -z", {
|
|
97
157
|
cwd,
|
|
98
158
|
stdio: ["pipe", "pipe", "ignore"],
|
|
159
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
99
160
|
}).toString()
|
|
100
161
|
const parts = output.split("\0")
|
|
101
162
|
const staged = []
|
package/src/eslint/base.cjs
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// Base ESLint config for TypeScript projects
|
|
2
|
+
//
|
|
3
|
+
// Prettier 选项以 src/prettier/index.cjs 为唯一来源,避免 rules 与 prettier 配置文件漂移
|
|
4
|
+
const prettierConfig = require("../prettier/index.cjs")
|
|
5
|
+
const { overrides: _prettierOverrides, ...prettierOptions } = prettierConfig
|
|
6
|
+
|
|
2
7
|
module.exports = {
|
|
3
8
|
env: {
|
|
4
9
|
browser: true,
|
|
@@ -20,6 +25,17 @@ module.exports = {
|
|
|
20
25
|
jsx: true,
|
|
21
26
|
},
|
|
22
27
|
},
|
|
28
|
+
// Mirrors the ignores of src/eslint/flat/base.mjs: without them `eslint .`
|
|
29
|
+
// lints build output (dist/, coverage/) and fails on generated code.
|
|
30
|
+
ignorePatterns: [
|
|
31
|
+
"**/node_modules/**",
|
|
32
|
+
"**/dist/**",
|
|
33
|
+
"**/coverage/**",
|
|
34
|
+
"**/public/**",
|
|
35
|
+
"**/assets/iconfont/**",
|
|
36
|
+
"**/*.min.js",
|
|
37
|
+
"**/*.min.css",
|
|
38
|
+
],
|
|
23
39
|
plugins: ["@typescript-eslint", "prettier", "import"],
|
|
24
40
|
overrides: [
|
|
25
41
|
{
|
|
@@ -29,20 +45,14 @@ module.exports = {
|
|
|
29
45
|
],
|
|
30
46
|
rules: {
|
|
31
47
|
// Prettier 集成
|
|
32
|
-
"prettier/prettier": [
|
|
33
|
-
"error",
|
|
34
|
-
{
|
|
35
|
-
singleQuote: false,
|
|
36
|
-
tabWidth: 4,
|
|
37
|
-
semi: false,
|
|
38
|
-
trailingComma: "all",
|
|
39
|
-
endOfLine: "lf",
|
|
40
|
-
},
|
|
41
|
-
],
|
|
48
|
+
"prettier/prettier": ["error", prettierOptions],
|
|
42
49
|
// 格式化与风格
|
|
43
50
|
indent: "off",
|
|
44
51
|
semi: ["error", "never"],
|
|
45
|
-
|
|
52
|
+
// avoidEscape must stay true: Prettier keeps the cheaper quote when a
|
|
53
|
+
// string contains double quotes ('say "hi"'), so requiring an escape
|
|
54
|
+
// here produces an error that --fix can never resolve.
|
|
55
|
+
quotes: ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
|
|
46
56
|
curly: ["error", "all"],
|
|
47
57
|
"comma-dangle": ["error", "always-multiline"],
|
|
48
58
|
"object-curly-spacing": ["error", "always"],
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// Shared constants and rule objects for Flat Config files
|
|
2
|
+
import prettierConfig from "../../prettier/index.cjs"
|
|
3
|
+
|
|
4
|
+
// Prettier 选项以 src/prettier/index.cjs 为唯一来源,避免 rules 与配置文件漂移
|
|
5
|
+
const { overrides: _prettierOverrides, ...prettierOptions } = prettierConfig
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Prettier plugin rules — mirrors base.cjs prettier/prettier config
|
|
5
9
|
*/
|
|
6
10
|
export const prettierRules = {
|
|
7
|
-
"prettier/prettier": [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
],
|
|
11
|
+
"prettier/prettier": ["error", prettierOptions],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* .nvue 覆写:规则内联 options 会整体替换而非合并,必须携带完整选项
|
|
16
|
+
*/
|
|
17
|
+
export const nvuePrettierRules = {
|
|
18
|
+
"prettier/prettier": ["error", { ...prettierOptions, parser: "vue", semi: true }],
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -23,7 +25,10 @@ export const commonRules = {
|
|
|
23
25
|
// 缩进由 Prettier 统一负责,关闭 ESLint 缩进避免打架
|
|
24
26
|
indent: "off",
|
|
25
27
|
semi: ["error", "never"],
|
|
26
|
-
|
|
28
|
+
// avoidEscape must stay true: Prettier keeps the cheaper quote when a
|
|
29
|
+
// string contains double quotes ('say "hi"'), so requiring an escape here
|
|
30
|
+
// produces an error that --fix can never resolve.
|
|
31
|
+
quotes: ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
|
|
27
32
|
curly: ["error", "all"],
|
|
28
33
|
"comma-dangle": ["error", "always-multiline"],
|
|
29
34
|
"object-curly-spacing": ["error", "always"],
|
package/src/eslint/flat/base.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Base ESLint Flat Config for TypeScript projects (ESLint v9+)
|
|
2
2
|
// Equivalent to src/eslint/base.cjs but in flat config format
|
|
3
3
|
|
|
4
|
+
import js from "@eslint/js"
|
|
4
5
|
import tseslint from "typescript-eslint"
|
|
5
6
|
import importX from "eslint-plugin-import-x"
|
|
6
7
|
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
|
@@ -22,12 +23,17 @@ export default [
|
|
|
22
23
|
],
|
|
23
24
|
},
|
|
24
25
|
|
|
26
|
+
// ESLint 官方推荐规则
|
|
27
|
+
// Replaces: extends: ["eslint:recommended"] — 缺失它会让 flat 配置弱于传统配置
|
|
28
|
+
// (no-debugger / no-cond-assign / no-constant-condition / no-empty 等不会被检查)
|
|
29
|
+
js.configs.recommended,
|
|
30
|
+
|
|
25
31
|
// TypeScript recommended rules
|
|
26
|
-
// Replaces: extends: ["
|
|
32
|
+
// Replaces: extends: ["plugin:@typescript-eslint/recommended"]
|
|
27
33
|
...tseslint.configs.recommended,
|
|
28
34
|
|
|
29
35
|
// eslint-plugin-import-x configuration
|
|
30
|
-
// Replaces: plugin:import/recommended
|
|
36
|
+
// Replaces: plugin:import/recommended (ESLint 8 equivalent in src/eslint/base.cjs)
|
|
31
37
|
{
|
|
32
38
|
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,vue,nvue}"],
|
|
33
39
|
plugins: {
|
|
@@ -66,7 +72,16 @@ export default [
|
|
|
66
72
|
},
|
|
67
73
|
},
|
|
68
74
|
|
|
69
|
-
//
|
|
75
|
+
// Prettier integration (replaces: extends: ["plugin:prettier/recommended"])
|
|
76
|
+
eslintPluginPrettierRecommended,
|
|
77
|
+
|
|
78
|
+
// eslint-config-prettier disables rules that conflict with Prettier —
|
|
79
|
+
// including `curly`, which Prettier never rewrites.
|
|
80
|
+
eslintConfigPrettier,
|
|
81
|
+
|
|
82
|
+
// Shared rules for all JS, TS, JSX, TSX and Vue files.
|
|
83
|
+
// MUST come after eslint-config-prettier: in flat config a later entry wins,
|
|
84
|
+
// so explicit rules such as `curly` would otherwise be silently switched off.
|
|
70
85
|
{
|
|
71
86
|
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,vue,nvue}"],
|
|
72
87
|
rules: {
|
|
@@ -83,19 +98,14 @@ export default [
|
|
|
83
98
|
},
|
|
84
99
|
},
|
|
85
100
|
|
|
86
|
-
// .nvue file handling (uni-app)
|
|
101
|
+
// .nvue file handling (uni-app) — mirrors the legacy config, which parses
|
|
102
|
+
// nvue script blocks as modules so `import` works.
|
|
87
103
|
{
|
|
88
104
|
files: ["**/*.nvue"],
|
|
89
105
|
languageOptions: {
|
|
90
106
|
parserOptions: {
|
|
91
|
-
sourceType: "
|
|
107
|
+
sourceType: "module",
|
|
92
108
|
},
|
|
93
109
|
},
|
|
94
110
|
},
|
|
95
|
-
|
|
96
|
-
// Prettier integration (replaces: extends: ["plugin:prettier/recommended"])
|
|
97
|
-
eslintPluginPrettierRecommended,
|
|
98
|
-
|
|
99
|
-
// eslint-config-prettier MUST be last — disables conflicting ESLint rules
|
|
100
|
-
eslintConfigPrettier,
|
|
101
111
|
]
|
package/src/eslint/flat/vue3.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import pluginVue from "eslint-plugin-vue"
|
|
|
4
4
|
import tseslint from "typescript-eslint"
|
|
5
5
|
import parserVue from "vue-eslint-parser"
|
|
6
6
|
import baseConfig from "./base.mjs"
|
|
7
|
+
import { nvuePrettierRules } from "./_shared.mjs"
|
|
7
8
|
|
|
8
9
|
export default [
|
|
9
10
|
// Spread all base configs first
|
|
@@ -49,8 +50,12 @@ export default [
|
|
|
49
50
|
},
|
|
50
51
|
rules: {
|
|
51
52
|
"vue/comment-directive": "off",
|
|
53
|
+
// .nvue uses semicolons (see src/prettier/index.cjs), so the base
|
|
54
|
+
// `semi: ["error", "never"]` rule must be disabled here — mirrors
|
|
55
|
+
// the legacy config in src/eslint/vue3.cjs.
|
|
56
|
+
semi: "off",
|
|
52
57
|
// Prettier cannot infer a parser from the custom .nvue extension.
|
|
53
|
-
|
|
58
|
+
...nvuePrettierRules,
|
|
54
59
|
},
|
|
55
60
|
},
|
|
56
61
|
]
|
package/src/eslint/vue3.cjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Vue 3 ESLint config — extends base, adds Vue-specific rules
|
|
2
2
|
const base = require("./base.cjs")
|
|
3
3
|
|
|
4
|
+
// .nvue 覆写必须携带完整 Prettier 选项:规则内联 options 会整体替换而非合并
|
|
5
|
+
const prettierConfig = require("../prettier/index.cjs")
|
|
6
|
+
const { overrides: _prettierOverrides, ...prettierOptions } = prettierConfig
|
|
7
|
+
|
|
4
8
|
module.exports = {
|
|
5
9
|
...base,
|
|
6
10
|
parser: "vue-eslint-parser",
|
|
@@ -25,7 +29,7 @@ module.exports = {
|
|
|
25
29
|
rules: {
|
|
26
30
|
"vue/comment-directive": "off",
|
|
27
31
|
semi: "off",
|
|
28
|
-
"prettier/prettier": ["error", { parser: "vue", semi: true }],
|
|
32
|
+
"prettier/prettier": ["error", { ...prettierOptions, parser: "vue", semi: true }],
|
|
29
33
|
},
|
|
30
34
|
},
|
|
31
35
|
],
|
package/src/gitignore
CHANGED
package/src/husky/commit-msg
CHANGED
package/src/husky/pre-commit
CHANGED