my-code-style 1.2.0 → 1.5.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 +109 -0
- package/LICENSE +21 -0
- package/README.md +131 -46
- package/bin/init +797 -130
- package/package.json +224 -123
- package/src/commitlint/base.cjs +0 -1
- package/src/commitlint/scopes.cjs +199 -30
- package/src/eslint/base.cjs +29 -3
- package/src/eslint/flat/_shared.mjs +11 -9
- package/src/eslint/flat/base.mjs +39 -17
- package/src/eslint/flat/vue3.mjs +12 -7
- package/src/eslint/vue3.cjs +13 -5
- package/src/gitignore +11 -0
- package/src/husky/commit-msg +1 -4
- package/src/husky/pre-commit +0 -3
- package/src/prettier/index.cjs +1 -2
- package/src/stylelint/index.cjs +22 -1
- package/src/stylelint/less.cjs +17 -2
package/package.json
CHANGED
|
@@ -1,127 +1,228 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
2
|
+
"name": "my-code-style",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Lint/Format/Git 配置工程化 npm 包(ESLint + Prettier + Stylelint + Commitlint + Husky + standard-version)",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "src/eslint/uniapp.cjs",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/myvn/codeStyle.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/myvn/codeStyle#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/myvn/codeStyle/issues"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"my-code-style-init": "./bin/init"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./src/eslint/uniapp.cjs",
|
|
20
|
+
"./eslint": "./src/eslint/base.cjs",
|
|
21
|
+
"./eslint/vue3": "./src/eslint/vue3.cjs",
|
|
22
|
+
"./eslint/uniapp": "./src/eslint/uniapp.cjs",
|
|
23
|
+
"./eslint/flat": "./src/eslint/flat/base.mjs",
|
|
24
|
+
"./eslint/flat/vue3": "./src/eslint/flat/vue3.mjs",
|
|
25
|
+
"./eslint/flat/uniapp": "./src/eslint/flat/uniapp.mjs",
|
|
26
|
+
"./prettier": "./src/prettier/index.cjs",
|
|
27
|
+
"./stylelint": "./src/stylelint/index.cjs",
|
|
28
|
+
"./stylelint/less": "./src/stylelint/less.cjs",
|
|
29
|
+
"./stylelint/less-override": "./src/stylelint/less-override.cjs",
|
|
30
|
+
"./commitlint": "./src/commitlint/base.cjs",
|
|
31
|
+
"./commitlint/scopes": "./src/commitlint/scopes.cjs",
|
|
32
|
+
"./versionrc": "./src/versionrc/index.cjs"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"src",
|
|
36
|
+
"bin",
|
|
37
|
+
"README.md",
|
|
38
|
+
"CHANGELOG.md"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"registry": "https://registry.npmjs.org/"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"test": "node --test tests/*.test.cjs demo-test/*.test.cjs",
|
|
46
|
+
"test:demo": "node --test demo-test/*.test.cjs",
|
|
47
|
+
"test:integration:setup": "node demo-test/setup-integration.cjs",
|
|
48
|
+
"test:integration": "node --test demo-test/integration/*.test.cjs",
|
|
49
|
+
"test:legacy:setup": "node demo-test/setup-legacy.cjs",
|
|
50
|
+
"test:legacy": "node --test demo-test/legacy/*.test.cjs",
|
|
51
|
+
"test:all": "npm test && npm run test:integration && npm run test:legacy",
|
|
52
|
+
"lint": "eslint .",
|
|
53
|
+
"lint:fix": "eslint . --fix",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"prepare": "node -e \"if (!process.env.CI) { try { require('child_process').execSync('husky', { stdio: 'ignore' }) } catch {} }\"",
|
|
56
|
+
"release": "standard-version",
|
|
57
|
+
"release:push": "standard-version && git push --follow-tags origin HEAD",
|
|
58
|
+
"pub": "npm run release:push",
|
|
59
|
+
"cz": "czg",
|
|
60
|
+
"link": "pnpm link --global",
|
|
61
|
+
"unlink": "pnpm unlink --global",
|
|
62
|
+
"pack": "npm pack"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=18"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"@commitlint/cli": "^19.0.0",
|
|
69
|
+
"@commitlint/config-conventional": "^19.0.0",
|
|
70
|
+
"@eslint/eslintrc": "^3.0.0",
|
|
71
|
+
"@eslint/js": "^9.0.0",
|
|
72
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
73
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
74
|
+
"czg": "^1.0.0",
|
|
75
|
+
"eslint": "^8.57.0 || ^9.0.0",
|
|
76
|
+
"eslint-config-prettier": "^10.1.8",
|
|
77
|
+
"eslint-import-resolver-typescript": "^3.0.0",
|
|
78
|
+
"eslint-plugin-import": "^2.0.0",
|
|
79
|
+
"eslint-plugin-import-x": "^4.0.0",
|
|
80
|
+
"eslint-plugin-prettier": "^5.2.0",
|
|
81
|
+
"eslint-plugin-vue": "^10.0.0",
|
|
82
|
+
"globals": "^16.0.0",
|
|
83
|
+
"husky": "^9.0.0",
|
|
84
|
+
"lint-staged": "^16.0.0",
|
|
85
|
+
"postcss-html": "^2.0.0",
|
|
86
|
+
"postcss-less": "^6.0.0",
|
|
87
|
+
"postcss-scss": "^4.0.0",
|
|
88
|
+
"prettier": "^3.0.0",
|
|
89
|
+
"standard-version": "^9.0.0",
|
|
90
|
+
"stylelint": "^16.0.0",
|
|
91
|
+
"stylelint-config-html": "^2.0.0",
|
|
92
|
+
"stylelint-config-recess-order": "^5.0.0",
|
|
93
|
+
"stylelint-config-recommended": "^17.0.0",
|
|
94
|
+
"stylelint-config-recommended-scss": "^16.0.0",
|
|
95
|
+
"stylelint-config-recommended-vue": "^2.0.0",
|
|
96
|
+
"stylelint-prettier": "^5.0.0",
|
|
97
|
+
"typescript": "^5.0.0",
|
|
98
|
+
"typescript-eslint": "^8.0.0",
|
|
99
|
+
"vue-eslint-parser": "^10.3.0"
|
|
100
|
+
},
|
|
101
|
+
"peerDependenciesMeta": {
|
|
102
|
+
"@commitlint/cli": {
|
|
103
|
+
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"@commitlint/config-conventional": {
|
|
106
|
+
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"@eslint/eslintrc": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"@eslint/js": {
|
|
112
|
+
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"@typescript-eslint/eslint-plugin": {
|
|
115
|
+
"optional": true
|
|
116
|
+
},
|
|
117
|
+
"@typescript-eslint/parser": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"czg": {
|
|
121
|
+
"optional": true
|
|
122
|
+
},
|
|
123
|
+
"eslint": {
|
|
124
|
+
"optional": true
|
|
125
|
+
},
|
|
126
|
+
"eslint-config-prettier": {
|
|
127
|
+
"optional": true
|
|
128
|
+
},
|
|
129
|
+
"eslint-import-resolver-typescript": {
|
|
130
|
+
"optional": true
|
|
131
|
+
},
|
|
132
|
+
"eslint-plugin-import": {
|
|
133
|
+
"optional": true
|
|
134
|
+
},
|
|
135
|
+
"eslint-plugin-import-x": {
|
|
136
|
+
"optional": true
|
|
137
|
+
},
|
|
138
|
+
"eslint-plugin-prettier": {
|
|
139
|
+
"optional": true
|
|
140
|
+
},
|
|
141
|
+
"eslint-plugin-vue": {
|
|
142
|
+
"optional": true
|
|
143
|
+
},
|
|
144
|
+
"globals": {
|
|
145
|
+
"optional": true
|
|
146
|
+
},
|
|
147
|
+
"husky": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"lint-staged": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"postcss-html": {
|
|
154
|
+
"optional": true
|
|
155
|
+
},
|
|
156
|
+
"postcss-less": {
|
|
157
|
+
"optional": true
|
|
158
|
+
},
|
|
159
|
+
"postcss-scss": {
|
|
160
|
+
"optional": true
|
|
161
|
+
},
|
|
162
|
+
"prettier": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"standard-version": {
|
|
166
|
+
"optional": true
|
|
167
|
+
},
|
|
168
|
+
"stylelint": {
|
|
169
|
+
"optional": true
|
|
170
|
+
},
|
|
171
|
+
"stylelint-config-html": {
|
|
172
|
+
"optional": true
|
|
173
|
+
},
|
|
174
|
+
"stylelint-config-recess-order": {
|
|
175
|
+
"optional": true
|
|
176
|
+
},
|
|
177
|
+
"stylelint-config-recommended": {
|
|
178
|
+
"optional": true
|
|
179
|
+
},
|
|
180
|
+
"stylelint-config-recommended-scss": {
|
|
181
|
+
"optional": true
|
|
182
|
+
},
|
|
183
|
+
"stylelint-config-recommended-vue": {
|
|
184
|
+
"optional": true
|
|
185
|
+
},
|
|
186
|
+
"stylelint-prettier": {
|
|
187
|
+
"optional": true
|
|
188
|
+
},
|
|
189
|
+
"typescript": {
|
|
190
|
+
"optional": true
|
|
191
|
+
},
|
|
192
|
+
"typescript-eslint": {
|
|
193
|
+
"optional": true
|
|
194
|
+
},
|
|
195
|
+
"vue-eslint-parser": {
|
|
196
|
+
"optional": true
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"devDependencies": {
|
|
200
|
+
"standard-version": "^9.5.0"
|
|
201
|
+
},
|
|
202
|
+
"keywords": [
|
|
203
|
+
"eslint-config",
|
|
204
|
+
"prettier-config",
|
|
205
|
+
"stylelint-config",
|
|
206
|
+
"commitlint-config",
|
|
207
|
+
"vue",
|
|
208
|
+
"uni-app",
|
|
209
|
+
"typescript"
|
|
210
|
+
],
|
|
211
|
+
"license": "MIT",
|
|
212
|
+
"lint-staged": {
|
|
213
|
+
"**/*.{vue,nvue}": [
|
|
214
|
+
"prettier --write",
|
|
215
|
+
"eslint --fix"
|
|
216
|
+
],
|
|
217
|
+
"**/*.{js,ts,jsx,tsx,cjs,mjs,mts,cts}": [
|
|
218
|
+
"prettier --write",
|
|
219
|
+
"eslint --fix"
|
|
30
220
|
],
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"unlink": "pnpm unlink --global",
|
|
34
|
-
"pack": "npm pack",
|
|
35
|
-
"release": "standard-version"
|
|
36
|
-
},
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=18"
|
|
39
|
-
},
|
|
40
|
-
"peerDependencies": {
|
|
41
|
-
"@commitlint/cli": "^19.0.0",
|
|
42
|
-
"@commitlint/config-conventional": "^19.0.0",
|
|
43
|
-
"@eslint/eslintrc": "^3.0.0",
|
|
44
|
-
"@eslint/js": "^9.0.0",
|
|
45
|
-
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
46
|
-
"@typescript-eslint/parser": "^8.0.0",
|
|
47
|
-
"czg": "^1.0.0",
|
|
48
|
-
"eslint": "^9.0.0",
|
|
49
|
-
"eslint-config-prettier": "^10.1.8",
|
|
50
|
-
"eslint-import-resolver-typescript": "^3.0.0",
|
|
51
|
-
"eslint-plugin-import": "^2.0.0",
|
|
52
|
-
"eslint-plugin-import-x": "^4.0.0",
|
|
53
|
-
"eslint-plugin-prettier": "^5.2.0",
|
|
54
|
-
"eslint-plugin-vue": "^10.0.0",
|
|
55
|
-
"globals": "^16.0.0",
|
|
56
|
-
"husky": "^9.0.0",
|
|
57
|
-
"lint-staged": "^16.0.0",
|
|
58
|
-
"postcss-html": "^1.0.0",
|
|
59
|
-
"postcss-less": "^6.0.0",
|
|
60
|
-
"postcss-scss": "^4.0.0",
|
|
61
|
-
"prettier": "^3.0.0",
|
|
62
|
-
"standard-version": "^9.0.0",
|
|
63
|
-
"stylelint": "^16.0.0",
|
|
64
|
-
"stylelint-config-html": "^1.0.0",
|
|
65
|
-
"stylelint-config-recess-order": "^5.0.0",
|
|
66
|
-
"stylelint-config-recommended": "^17.0.0",
|
|
67
|
-
"stylelint-config-recommended-scss": "^16.0.0",
|
|
68
|
-
"stylelint-config-recommended-vue": "^2.0.0",
|
|
69
|
-
"stylelint-prettier": "^5.0.0",
|
|
70
|
-
"typescript-eslint": "^8.0.0",
|
|
71
|
-
"vue-eslint-parser": "^9.0.0"
|
|
72
|
-
},
|
|
73
|
-
"peerDependenciesMeta": {
|
|
74
|
-
"@eslint/eslintrc": {
|
|
75
|
-
"optional": true
|
|
76
|
-
},
|
|
77
|
-
"@eslint/js": {
|
|
78
|
-
"optional": true
|
|
79
|
-
},
|
|
80
|
-
"eslint-plugin-vue": {
|
|
81
|
-
"optional": true
|
|
82
|
-
},
|
|
83
|
-
"eslint-plugin-import-x": {
|
|
84
|
-
"optional": true
|
|
85
|
-
},
|
|
86
|
-
"globals": {
|
|
87
|
-
"optional": true
|
|
88
|
-
},
|
|
89
|
-
"postcss-less": {
|
|
90
|
-
"optional": true
|
|
91
|
-
},
|
|
92
|
-
"postcss-scss": {
|
|
93
|
-
"optional": true
|
|
94
|
-
},
|
|
95
|
-
"postcss-html": {
|
|
96
|
-
"optional": true
|
|
97
|
-
},
|
|
98
|
-
"stylelint-config-recommended-scss": {
|
|
99
|
-
"optional": true
|
|
100
|
-
},
|
|
101
|
-
"stylelint-config-recommended-vue": {
|
|
102
|
-
"optional": true
|
|
103
|
-
},
|
|
104
|
-
"stylelint-config-html": {
|
|
105
|
-
"optional": true
|
|
106
|
-
},
|
|
107
|
-
"typescript-eslint": {
|
|
108
|
-
"optional": true
|
|
109
|
-
},
|
|
110
|
-
"vue-eslint-parser": {
|
|
111
|
-
"optional": true
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
"devDependencies": {
|
|
115
|
-
"standard-version": "^9.5.0"
|
|
116
|
-
},
|
|
117
|
-
"keywords": [
|
|
118
|
-
"eslint-config",
|
|
119
|
-
"prettier-config",
|
|
120
|
-
"stylelint-config",
|
|
121
|
-
"commitlint-config",
|
|
122
|
-
"vue",
|
|
123
|
-
"uni-app",
|
|
124
|
-
"typescript"
|
|
221
|
+
"**/*.{html,css}": [
|
|
222
|
+
"prettier --write"
|
|
125
223
|
],
|
|
126
|
-
"
|
|
224
|
+
"**/*.{json,json5,md,yml,yaml}": [
|
|
225
|
+
"prettier --write"
|
|
226
|
+
]
|
|
227
|
+
}
|
|
127
228
|
}
|
package/src/commitlint/base.cjs
CHANGED
|
@@ -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]
|
|
97
|
+
}
|
|
98
|
+
if (NON_PLURAL.includes(lower)) {
|
|
99
|
+
return name
|
|
34
100
|
}
|
|
35
|
-
if (
|
|
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)
|
|
@@ -84,19 +142,130 @@ function generateScopes(srcDirs = "src") {
|
|
|
84
142
|
return [...new Set(allScopes)]
|
|
85
143
|
}
|
|
86
144
|
|
|
145
|
+
function getStagedFiles(cwd) {
|
|
146
|
+
try {
|
|
147
|
+
const output = execSync("git diff --cached --name-only -z", {
|
|
148
|
+
cwd,
|
|
149
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
150
|
+
}).toString()
|
|
151
|
+
return output.split("\0").filter(Boolean)
|
|
152
|
+
} catch {
|
|
153
|
+
try {
|
|
154
|
+
const output = execSync("git status --porcelain -z", {
|
|
155
|
+
cwd,
|
|
156
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
157
|
+
}).toString()
|
|
158
|
+
const parts = output.split("\0")
|
|
159
|
+
const staged = []
|
|
160
|
+
for (let i = 0; i < parts.length; i++) {
|
|
161
|
+
const entry = parts[i]
|
|
162
|
+
if (!entry) continue
|
|
163
|
+
const x = entry[0]
|
|
164
|
+
const isRenameOrCopy = x === "R" || x === "C"
|
|
165
|
+
const filePath = entry.slice(3)
|
|
166
|
+
if (x !== " " && x !== "?" && x !== "!") {
|
|
167
|
+
if (filePath) staged.push(filePath)
|
|
168
|
+
}
|
|
169
|
+
if (isRenameOrCopy) {
|
|
170
|
+
i++
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return staged
|
|
174
|
+
} catch {
|
|
175
|
+
return []
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
87
180
|
/**
|
|
88
|
-
* Guess the current scope from
|
|
89
|
-
*
|
|
181
|
+
* Guess the current scope from staged files in the git index.
|
|
182
|
+
* Only staged files are considered (unstaged and untracked changes are ignored).
|
|
183
|
+
* Multi-directory strategy: votes by the number of staged changes under each candidate scope directory;
|
|
184
|
+
* in case of a tie, the first encountered scope among staged files is chosen.
|
|
185
|
+
*
|
|
186
|
+
* @param {string|string[]} [srcDirs="src"] - Source directory name(s) relative to cwd
|
|
187
|
+
* @returns {string|undefined} Guessed scope name, or undefined if none matched
|
|
90
188
|
*/
|
|
91
|
-
function guessCurrentScope() {
|
|
189
|
+
function guessCurrentScope(srcDirs = "src") {
|
|
92
190
|
try {
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
191
|
+
const cwd = process.cwd()
|
|
192
|
+
const rawPrefix = execSync("git rev-parse --show-prefix", {
|
|
193
|
+
cwd,
|
|
194
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
195
|
+
})
|
|
196
|
+
.toString()
|
|
197
|
+
.trim()
|
|
198
|
+
const prefix = rawPrefix.replace(/\\/g, "/")
|
|
199
|
+
|
|
200
|
+
const stagedFiles = getStagedFiles(cwd)
|
|
201
|
+
if (!stagedFiles || stagedFiles.length === 0) {
|
|
96
202
|
return undefined
|
|
97
203
|
}
|
|
98
|
-
|
|
99
|
-
|
|
204
|
+
|
|
205
|
+
const normalizedDirs = (Array.isArray(srcDirs) ? srcDirs : [srcDirs])
|
|
206
|
+
.map((d) => d.replace(/\\/g, "/").replace(/^\/+|\/+$/g, ""))
|
|
207
|
+
.filter(Boolean)
|
|
208
|
+
|
|
209
|
+
const candidateScopes = []
|
|
210
|
+
|
|
211
|
+
for (const file of stagedFiles) {
|
|
212
|
+
const normalizedFile = file.replace(/\\/g, "/")
|
|
213
|
+
if (prefix && !normalizedFile.startsWith(prefix)) {
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
const relativeToCwd = prefix ? normalizedFile.slice(prefix.length) : normalizedFile
|
|
217
|
+
|
|
218
|
+
for (const srcDir of normalizedDirs) {
|
|
219
|
+
const dirPrefix = srcDir ? `${srcDir}/` : ""
|
|
220
|
+
if (relativeToCwd.startsWith(dirPrefix)) {
|
|
221
|
+
const subPath = relativeToCwd.slice(dirPrefix.length)
|
|
222
|
+
const segments = subPath.split("/").filter(Boolean)
|
|
223
|
+
// Must have at least 2 segments (directory + file) to belong to a scope directory
|
|
224
|
+
if (segments.length >= 2) {
|
|
225
|
+
const rawName = segments[0]
|
|
226
|
+
const resolvedDir = path.resolve(cwd, srcDir, rawName)
|
|
227
|
+
if (fs.existsSync(resolvedDir)) {
|
|
228
|
+
if (fs.statSync(resolvedDir).isDirectory()) {
|
|
229
|
+
candidateScopes.push(toSingular(rawName))
|
|
230
|
+
break
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
// If deleted on disk, having segments >= 2 indicates it was a directory in git
|
|
234
|
+
candidateScopes.push(toSingular(rawName))
|
|
235
|
+
break
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (candidateScopes.length === 0) {
|
|
243
|
+
return undefined
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Multi-directory selection strategy: vote by frequency; first encountered breaks ties
|
|
247
|
+
const counts = new Map()
|
|
248
|
+
const order = []
|
|
249
|
+
|
|
250
|
+
for (const scope of candidateScopes) {
|
|
251
|
+
if (!counts.has(scope)) {
|
|
252
|
+
counts.set(scope, 0)
|
|
253
|
+
order.push(scope)
|
|
254
|
+
}
|
|
255
|
+
counts.set(scope, counts.get(scope) + 1)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let bestScope = undefined
|
|
259
|
+
let maxCount = 0
|
|
260
|
+
for (const scope of order) {
|
|
261
|
+
const count = counts.get(scope)
|
|
262
|
+
if (count > maxCount) {
|
|
263
|
+
maxCount = count
|
|
264
|
+
bestScope = scope
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return bestScope
|
|
100
269
|
} catch {
|
|
101
270
|
return undefined
|
|
102
271
|
}
|