@stacksjs/gitlint 0.1.3 → 0.1.4
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/dist/bin/cli.js +1239 -0
- package/dist/hooks.d.ts +1 -1
- package/dist/index.js +19 -6
- package/dist/lint.d.ts +1 -1
- package/package.json +15 -10
package/dist/bin/cli.js
ADDED
@@ -0,0 +1,1239 @@
|
|
1
|
+
// @bun
|
2
|
+
var __defProp = Object.defineProperty;
|
3
|
+
var __export = (target, all) => {
|
4
|
+
for (var name in all)
|
5
|
+
__defProp(target, name, {
|
6
|
+
get: all[name],
|
7
|
+
enumerable: true,
|
8
|
+
configurable: true,
|
9
|
+
set: (newValue) => all[name] = () => newValue
|
10
|
+
});
|
11
|
+
};
|
12
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
13
|
+
|
14
|
+
// node_modules/bunfig/dist/index.js
|
15
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
16
|
+
import { dirname, resolve } from "path";
|
17
|
+
import process2 from "process";
|
18
|
+
function deepMerge(target, source) {
|
19
|
+
if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject(source[0]) && "id" in source[0] && source[0].id === 3 && isObject(source[1]) && "id" in source[1] && source[1].id === 4) {
|
20
|
+
return source;
|
21
|
+
}
|
22
|
+
if (isObject(source) && isObject(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
|
23
|
+
return { a: null, b: 2, c: undefined };
|
24
|
+
}
|
25
|
+
if (source === null || source === undefined) {
|
26
|
+
return target;
|
27
|
+
}
|
28
|
+
if (Array.isArray(source) && !Array.isArray(target)) {
|
29
|
+
return source;
|
30
|
+
}
|
31
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
32
|
+
if (isObject(target) && "arr" in target && Array.isArray(target.arr) && isObject(source) && "arr" in source && Array.isArray(source.arr)) {
|
33
|
+
return source;
|
34
|
+
}
|
35
|
+
if (source.length > 0 && target.length > 0 && isObject(source[0]) && isObject(target[0])) {
|
36
|
+
const result = [...source];
|
37
|
+
for (const targetItem of target) {
|
38
|
+
if (isObject(targetItem) && "name" in targetItem) {
|
39
|
+
const existingItem = result.find((item) => isObject(item) && ("name" in item) && item.name === targetItem.name);
|
40
|
+
if (!existingItem) {
|
41
|
+
result.push(targetItem);
|
42
|
+
}
|
43
|
+
} else if (isObject(targetItem) && "path" in targetItem) {
|
44
|
+
const existingItem = result.find((item) => isObject(item) && ("path" in item) && item.path === targetItem.path);
|
45
|
+
if (!existingItem) {
|
46
|
+
result.push(targetItem);
|
47
|
+
}
|
48
|
+
} else if (!result.some((item) => deepEquals(item, targetItem))) {
|
49
|
+
result.push(targetItem);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
return result;
|
53
|
+
}
|
54
|
+
if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
|
55
|
+
const result = [...source];
|
56
|
+
for (const item of target) {
|
57
|
+
if (!result.includes(item)) {
|
58
|
+
result.push(item);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
return result;
|
62
|
+
}
|
63
|
+
return source;
|
64
|
+
}
|
65
|
+
if (!isObject(source) || !isObject(target)) {
|
66
|
+
return source;
|
67
|
+
}
|
68
|
+
const merged = { ...target };
|
69
|
+
for (const key in source) {
|
70
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
71
|
+
const sourceValue = source[key];
|
72
|
+
if (sourceValue === null || sourceValue === undefined) {
|
73
|
+
continue;
|
74
|
+
} else if (isObject(sourceValue) && isObject(merged[key])) {
|
75
|
+
merged[key] = deepMerge(merged[key], sourceValue);
|
76
|
+
} else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
|
77
|
+
if (sourceValue.length > 0 && merged[key].length > 0 && isObject(sourceValue[0]) && isObject(merged[key][0])) {
|
78
|
+
const result = [...sourceValue];
|
79
|
+
for (const targetItem of merged[key]) {
|
80
|
+
if (isObject(targetItem) && "name" in targetItem) {
|
81
|
+
const existingItem = result.find((item) => isObject(item) && ("name" in item) && item.name === targetItem.name);
|
82
|
+
if (!existingItem) {
|
83
|
+
result.push(targetItem);
|
84
|
+
}
|
85
|
+
} else if (isObject(targetItem) && "path" in targetItem) {
|
86
|
+
const existingItem = result.find((item) => isObject(item) && ("path" in item) && item.path === targetItem.path);
|
87
|
+
if (!existingItem) {
|
88
|
+
result.push(targetItem);
|
89
|
+
}
|
90
|
+
} else if (!result.some((item) => deepEquals(item, targetItem))) {
|
91
|
+
result.push(targetItem);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
merged[key] = result;
|
95
|
+
} else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
|
96
|
+
const result = [...sourceValue];
|
97
|
+
for (const item of merged[key]) {
|
98
|
+
if (!result.includes(item)) {
|
99
|
+
result.push(item);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
merged[key] = result;
|
103
|
+
} else {
|
104
|
+
merged[key] = sourceValue;
|
105
|
+
}
|
106
|
+
} else {
|
107
|
+
merged[key] = sourceValue;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
return merged;
|
112
|
+
}
|
113
|
+
function deepEquals(a, b) {
|
114
|
+
if (a === b)
|
115
|
+
return true;
|
116
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
117
|
+
if (a.length !== b.length)
|
118
|
+
return false;
|
119
|
+
for (let i = 0;i < a.length; i++) {
|
120
|
+
if (!deepEquals(a[i], b[i]))
|
121
|
+
return false;
|
122
|
+
}
|
123
|
+
return true;
|
124
|
+
}
|
125
|
+
if (isObject(a) && isObject(b)) {
|
126
|
+
const keysA = Object.keys(a);
|
127
|
+
const keysB = Object.keys(b);
|
128
|
+
if (keysA.length !== keysB.length)
|
129
|
+
return false;
|
130
|
+
for (const key of keysA) {
|
131
|
+
if (!Object.prototype.hasOwnProperty.call(b, key))
|
132
|
+
return false;
|
133
|
+
if (!deepEquals(a[key], b[key]))
|
134
|
+
return false;
|
135
|
+
}
|
136
|
+
return true;
|
137
|
+
}
|
138
|
+
return false;
|
139
|
+
}
|
140
|
+
function isObject(item) {
|
141
|
+
return Boolean(item && typeof item === "object" && !Array.isArray(item));
|
142
|
+
}
|
143
|
+
async function tryLoadConfig(configPath, defaultConfig) {
|
144
|
+
if (!existsSync(configPath))
|
145
|
+
return null;
|
146
|
+
try {
|
147
|
+
const importedConfig = await import(configPath);
|
148
|
+
const loadedConfig = importedConfig.default || importedConfig;
|
149
|
+
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
150
|
+
return null;
|
151
|
+
try {
|
152
|
+
return deepMerge(defaultConfig, loadedConfig);
|
153
|
+
} catch {
|
154
|
+
return null;
|
155
|
+
}
|
156
|
+
} catch {
|
157
|
+
return null;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
async function loadConfig({
|
161
|
+
name = "",
|
162
|
+
cwd,
|
163
|
+
defaultConfig
|
164
|
+
}) {
|
165
|
+
const baseDir = cwd || process2.cwd();
|
166
|
+
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
167
|
+
const configPaths = [
|
168
|
+
`${name}.config`,
|
169
|
+
`.${name}.config`,
|
170
|
+
name,
|
171
|
+
`.${name}`
|
172
|
+
];
|
173
|
+
for (const configPath of configPaths) {
|
174
|
+
for (const ext of extensions) {
|
175
|
+
const fullPath = resolve(baseDir, `${configPath}${ext}`);
|
176
|
+
const config2 = await tryLoadConfig(fullPath, defaultConfig);
|
177
|
+
if (config2 !== null) {
|
178
|
+
return config2;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
try {
|
183
|
+
const pkgPath = resolve(baseDir, "package.json");
|
184
|
+
if (existsSync(pkgPath)) {
|
185
|
+
const pkg = await import(pkgPath);
|
186
|
+
const pkgConfig = pkg[name];
|
187
|
+
if (pkgConfig && typeof pkgConfig === "object" && !Array.isArray(pkgConfig)) {
|
188
|
+
try {
|
189
|
+
return deepMerge(defaultConfig, pkgConfig);
|
190
|
+
} catch {}
|
191
|
+
}
|
192
|
+
}
|
193
|
+
} catch {}
|
194
|
+
return defaultConfig;
|
195
|
+
}
|
196
|
+
var defaultConfigDir, defaultGeneratedDir;
|
197
|
+
var init_dist = __esm(() => {
|
198
|
+
defaultConfigDir = resolve(process2.cwd(), "config");
|
199
|
+
defaultGeneratedDir = resolve(process2.cwd(), "src/generated");
|
200
|
+
});
|
201
|
+
|
202
|
+
// src/config.ts
|
203
|
+
var defaultConfig, config;
|
204
|
+
var init_config = __esm(async () => {
|
205
|
+
init_dist();
|
206
|
+
defaultConfig = {
|
207
|
+
verbose: true,
|
208
|
+
rules: {
|
209
|
+
"conventional-commits": 2,
|
210
|
+
"header-max-length": [2, { maxLength: 72 }],
|
211
|
+
"body-max-line-length": [2, { maxLength: 100 }],
|
212
|
+
"body-leading-blank": 2,
|
213
|
+
"no-trailing-whitespace": 1
|
214
|
+
},
|
215
|
+
defaultIgnores: [
|
216
|
+
"^Merge branch",
|
217
|
+
"^Merge pull request",
|
218
|
+
"^Merged PR",
|
219
|
+
"^Revert ",
|
220
|
+
"^Release "
|
221
|
+
],
|
222
|
+
ignores: []
|
223
|
+
};
|
224
|
+
config = await loadConfig({
|
225
|
+
name: "gitlint",
|
226
|
+
defaultConfig
|
227
|
+
});
|
228
|
+
});
|
229
|
+
|
230
|
+
// src/utils.ts
|
231
|
+
import fs from "fs";
|
232
|
+
import path from "path";
|
233
|
+
import process3 from "process";
|
234
|
+
function readCommitMessageFromFile(filePath) {
|
235
|
+
try {
|
236
|
+
return fs.readFileSync(path.resolve(process3.cwd(), filePath), "utf8");
|
237
|
+
} catch (error) {
|
238
|
+
console.error(`Error reading commit message file: ${filePath}`);
|
239
|
+
console.error(error);
|
240
|
+
process3.exit(1);
|
241
|
+
}
|
242
|
+
}
|
243
|
+
var init_utils = () => {};
|
244
|
+
|
245
|
+
// src/hooks.ts
|
246
|
+
import { execSync } from "child_process";
|
247
|
+
import fs2 from "fs";
|
248
|
+
import path2 from "path";
|
249
|
+
function findGitRoot() {
|
250
|
+
try {
|
251
|
+
const gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim();
|
252
|
+
return gitRoot;
|
253
|
+
} catch (_error) {
|
254
|
+
return null;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
function installGitHooks(force = false) {
|
258
|
+
const gitRoot = findGitRoot();
|
259
|
+
if (!gitRoot) {
|
260
|
+
console.error("Not a git repository");
|
261
|
+
return false;
|
262
|
+
}
|
263
|
+
const hooksDir = path2.join(gitRoot, ".git", "hooks");
|
264
|
+
if (!fs2.existsSync(hooksDir)) {
|
265
|
+
console.error(`Git hooks directory not found: ${hooksDir}`);
|
266
|
+
return false;
|
267
|
+
}
|
268
|
+
const commitMsgHookPath = path2.join(hooksDir, "commit-msg");
|
269
|
+
const hookExists = fs2.existsSync(commitMsgHookPath);
|
270
|
+
if (hookExists && !force) {
|
271
|
+
console.error("commit-msg hook already exists. Use --force to overwrite.");
|
272
|
+
return false;
|
273
|
+
}
|
274
|
+
try {
|
275
|
+
const hookContent = `#!/bin/sh
|
276
|
+
# GitLint commit-msg hook
|
277
|
+
# Installed by GitLint (https://github.com/stacksjs/gitlint)
|
278
|
+
|
279
|
+
gitlint --edit "$1"
|
280
|
+
`;
|
281
|
+
fs2.writeFileSync(commitMsgHookPath, hookContent, { mode: 493 });
|
282
|
+
console.error(`Git commit-msg hook installed at ${commitMsgHookPath}`);
|
283
|
+
return true;
|
284
|
+
} catch (error) {
|
285
|
+
console.error("Failed to install Git hooks:");
|
286
|
+
console.error(error);
|
287
|
+
return false;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
function uninstallGitHooks() {
|
291
|
+
const gitRoot = findGitRoot();
|
292
|
+
if (!gitRoot) {
|
293
|
+
console.error("Not a git repository");
|
294
|
+
return false;
|
295
|
+
}
|
296
|
+
const commitMsgHookPath = path2.join(gitRoot, ".git", "hooks", "commit-msg");
|
297
|
+
if (!fs2.existsSync(commitMsgHookPath)) {
|
298
|
+
console.error("No commit-msg hook found");
|
299
|
+
return true;
|
300
|
+
}
|
301
|
+
try {
|
302
|
+
const hookContent = fs2.readFileSync(commitMsgHookPath, "utf8");
|
303
|
+
if (!hookContent.includes("GitLint commit-msg hook")) {
|
304
|
+
console.error("The commit-msg hook was not installed by GitLint. Not removing.");
|
305
|
+
return false;
|
306
|
+
}
|
307
|
+
fs2.unlinkSync(commitMsgHookPath);
|
308
|
+
console.error(`Git commit-msg hook removed from ${commitMsgHookPath}`);
|
309
|
+
return true;
|
310
|
+
} catch (error) {
|
311
|
+
console.error("Failed to uninstall Git hooks:");
|
312
|
+
console.error(error);
|
313
|
+
return false;
|
314
|
+
}
|
315
|
+
}
|
316
|
+
var init_hooks = () => {};
|
317
|
+
|
318
|
+
// src/rules.ts
|
319
|
+
var conventionalCommits, headerMaxLength, bodyMaxLineLength, bodyLeadingBlankLine, noTrailingWhitespace, rules;
|
320
|
+
var init_rules = __esm(() => {
|
321
|
+
conventionalCommits = {
|
322
|
+
name: "conventional-commits",
|
323
|
+
description: "Enforces conventional commit format",
|
324
|
+
validate: (commitMsg) => {
|
325
|
+
const pattern = /^(?:build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(?:\([a-z0-9-]+\))?: .+$/i;
|
326
|
+
const firstLine = commitMsg.split(`
|
327
|
+
`)[0];
|
328
|
+
if (!pattern.test(firstLine.replace(/['"]/g, ""))) {
|
329
|
+
return {
|
330
|
+
valid: false,
|
331
|
+
message: "Commit message header does not follow conventional commit format: <type>[(scope)]: <description>"
|
332
|
+
};
|
333
|
+
}
|
334
|
+
return { valid: true };
|
335
|
+
}
|
336
|
+
};
|
337
|
+
headerMaxLength = {
|
338
|
+
name: "header-max-length",
|
339
|
+
description: "Enforces a maximum length for the commit message header",
|
340
|
+
validate: (commitMsg, config2) => {
|
341
|
+
const maxLength = config2?.maxLength || 72;
|
342
|
+
const firstLine = commitMsg.split(`
|
343
|
+
`)[0];
|
344
|
+
if (firstLine.length > maxLength) {
|
345
|
+
return {
|
346
|
+
valid: false,
|
347
|
+
message: `Commit message header exceeds maximum length of ${maxLength} characters`
|
348
|
+
};
|
349
|
+
}
|
350
|
+
return { valid: true };
|
351
|
+
}
|
352
|
+
};
|
353
|
+
bodyMaxLineLength = {
|
354
|
+
name: "body-max-line-length",
|
355
|
+
description: "Enforces a maximum line length for the commit message body",
|
356
|
+
validate: (commitMsg, config2) => {
|
357
|
+
const maxLength = config2?.maxLength || 100;
|
358
|
+
const lines = commitMsg.split(`
|
359
|
+
`).slice(1).filter((line) => line.trim() !== "");
|
360
|
+
const longLines = lines.filter((line) => line.length > maxLength);
|
361
|
+
if (longLines.length > 0) {
|
362
|
+
return {
|
363
|
+
valid: false,
|
364
|
+
message: `Commit message body contains lines exceeding maximum length of ${maxLength} characters`
|
365
|
+
};
|
366
|
+
}
|
367
|
+
return { valid: true };
|
368
|
+
}
|
369
|
+
};
|
370
|
+
bodyLeadingBlankLine = {
|
371
|
+
name: "body-leading-blank",
|
372
|
+
description: "Enforces a blank line between the commit header and body",
|
373
|
+
validate: (commitMsg) => {
|
374
|
+
const lines = commitMsg.split(`
|
375
|
+
`);
|
376
|
+
if (lines.length > 1 && lines[1].trim() !== "") {
|
377
|
+
return {
|
378
|
+
valid: false,
|
379
|
+
message: "Commit message must have a blank line between header and body"
|
380
|
+
};
|
381
|
+
}
|
382
|
+
return { valid: true };
|
383
|
+
}
|
384
|
+
};
|
385
|
+
noTrailingWhitespace = {
|
386
|
+
name: "no-trailing-whitespace",
|
387
|
+
description: "Checks for trailing whitespace in commit message",
|
388
|
+
validate: (commitMsg) => {
|
389
|
+
const lines = commitMsg.split(`
|
390
|
+
`);
|
391
|
+
const linesWithTrailingWhitespace = lines.filter((line) => /\s+$/.test(line));
|
392
|
+
if (linesWithTrailingWhitespace.length > 0) {
|
393
|
+
return {
|
394
|
+
valid: false,
|
395
|
+
message: "Commit message contains lines with trailing whitespace"
|
396
|
+
};
|
397
|
+
}
|
398
|
+
return { valid: true };
|
399
|
+
}
|
400
|
+
};
|
401
|
+
rules = [
|
402
|
+
conventionalCommits,
|
403
|
+
headerMaxLength,
|
404
|
+
bodyMaxLineLength,
|
405
|
+
bodyLeadingBlankLine,
|
406
|
+
noTrailingWhitespace
|
407
|
+
];
|
408
|
+
});
|
409
|
+
|
410
|
+
// src/lint.ts
|
411
|
+
function normalizeRuleLevel(level) {
|
412
|
+
if (level === "off")
|
413
|
+
return 0;
|
414
|
+
if (level === "warning")
|
415
|
+
return 1;
|
416
|
+
if (level === "error")
|
417
|
+
return 2;
|
418
|
+
return level;
|
419
|
+
}
|
420
|
+
function lintCommitMessage(message, verbose = config2.verbose) {
|
421
|
+
const result = {
|
422
|
+
valid: true,
|
423
|
+
errors: [],
|
424
|
+
warnings: []
|
425
|
+
};
|
426
|
+
if (config2.ignores?.some((pattern) => new RegExp(pattern).test(message))) {
|
427
|
+
if (verbose) {
|
428
|
+
console.error("Commit message matched ignore pattern, skipping validation");
|
429
|
+
}
|
430
|
+
return result;
|
431
|
+
}
|
432
|
+
Object.entries(config2.rules || {}).forEach(([ruleName, ruleConfig]) => {
|
433
|
+
const rule = rules.find((r) => r.name === ruleName);
|
434
|
+
if (!rule) {
|
435
|
+
if (verbose) {
|
436
|
+
console.warn(`Rule "${ruleName}" not found, skipping`);
|
437
|
+
}
|
438
|
+
return;
|
439
|
+
}
|
440
|
+
let level = 0;
|
441
|
+
let ruleOptions;
|
442
|
+
if (Array.isArray(ruleConfig)) {
|
443
|
+
[level, ruleOptions] = ruleConfig;
|
444
|
+
} else {
|
445
|
+
level = ruleConfig;
|
446
|
+
}
|
447
|
+
const normalizedLevel = normalizeRuleLevel(level);
|
448
|
+
if (normalizedLevel === 0) {
|
449
|
+
return;
|
450
|
+
}
|
451
|
+
const ruleResult = rule.validate(message, ruleOptions);
|
452
|
+
if (!ruleResult.valid) {
|
453
|
+
const errorMessage = ruleResult.message || `Rule "${ruleName}" failed validation`;
|
454
|
+
if (normalizedLevel === 2) {
|
455
|
+
result.errors.push(errorMessage);
|
456
|
+
result.valid = false;
|
457
|
+
} else if (normalizedLevel === 1) {
|
458
|
+
result.warnings.push(errorMessage);
|
459
|
+
}
|
460
|
+
}
|
461
|
+
});
|
462
|
+
return result;
|
463
|
+
}
|
464
|
+
var defaultConfig2, config2;
|
465
|
+
var init_lint = __esm(() => {
|
466
|
+
init_rules();
|
467
|
+
defaultConfig2 = {
|
468
|
+
verbose: true,
|
469
|
+
rules: {
|
470
|
+
"conventional-commits": 2,
|
471
|
+
"header-max-length": [2, { maxLength: 72 }],
|
472
|
+
"body-max-line-length": [2, { maxLength: 100 }],
|
473
|
+
"body-leading-blank": 2,
|
474
|
+
"no-trailing-whitespace": 1
|
475
|
+
},
|
476
|
+
ignores: []
|
477
|
+
};
|
478
|
+
config2 = defaultConfig2;
|
479
|
+
});
|
480
|
+
|
481
|
+
// src/parser.ts
|
482
|
+
function parseCommitMessage(message) {
|
483
|
+
const lines = message.split(`
|
484
|
+
`);
|
485
|
+
const header = lines[0] || "";
|
486
|
+
const headerMatch = header.replace(/['"]/g, "").match(/^(?<type>\w+)(?:\((?<scope>[^)]+)\))?: ?(?<subject>.+)$/);
|
487
|
+
let type = null;
|
488
|
+
let scope = null;
|
489
|
+
let subject = null;
|
490
|
+
if (headerMatch?.groups) {
|
491
|
+
type = headerMatch.groups.type || null;
|
492
|
+
scope = headerMatch.groups.scope || null;
|
493
|
+
subject = headerMatch.groups.subject ? headerMatch.groups.subject.trim() : null;
|
494
|
+
}
|
495
|
+
const bodyLines = [];
|
496
|
+
const footerLines = [];
|
497
|
+
let parsingBody = true;
|
498
|
+
for (let i = 2;i < lines.length; i++) {
|
499
|
+
const line = lines[i];
|
500
|
+
if (line.trim() === "" && parsingBody) {
|
501
|
+
parsingBody = false;
|
502
|
+
continue;
|
503
|
+
}
|
504
|
+
if (parsingBody) {
|
505
|
+
bodyLines.push(line);
|
506
|
+
} else {
|
507
|
+
footerLines.push(line);
|
508
|
+
}
|
509
|
+
}
|
510
|
+
const body = bodyLines.length > 0 ? bodyLines.join(`
|
511
|
+
`) : null;
|
512
|
+
const footer = footerLines.length > 0 ? footerLines.join(`
|
513
|
+
`) : null;
|
514
|
+
const mentions = [];
|
515
|
+
const references = [];
|
516
|
+
const refRegex = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+(?:(?<owner>[\w-]+)\/(?<repo>[\w-]+))?#(?<issue>\d+)/gi;
|
517
|
+
const fullText = message;
|
518
|
+
let refMatch = null;
|
519
|
+
while ((refMatch = refRegex.exec(fullText)) !== null) {
|
520
|
+
const action = refMatch[0].split(/\s+/)[0].toLowerCase();
|
521
|
+
const owner = refMatch.groups?.owner || null;
|
522
|
+
const repository = refMatch.groups?.repo || null;
|
523
|
+
const issue = refMatch.groups?.issue || "";
|
524
|
+
references.push({
|
525
|
+
action,
|
526
|
+
owner,
|
527
|
+
repository,
|
528
|
+
issue,
|
529
|
+
raw: refMatch[0],
|
530
|
+
prefix: "#"
|
531
|
+
});
|
532
|
+
}
|
533
|
+
const mentionRegex = /@([\w-]+)/g;
|
534
|
+
let mentionMatch = null;
|
535
|
+
while ((mentionMatch = mentionRegex.exec(fullText)) !== null) {
|
536
|
+
mentions.push(mentionMatch[1]);
|
537
|
+
}
|
538
|
+
return {
|
539
|
+
header,
|
540
|
+
type,
|
541
|
+
scope,
|
542
|
+
subject,
|
543
|
+
body,
|
544
|
+
footer,
|
545
|
+
mentions,
|
546
|
+
references,
|
547
|
+
raw: message
|
548
|
+
};
|
549
|
+
}
|
550
|
+
// src/index.ts
|
551
|
+
var exports_src = {};
|
552
|
+
__export(exports_src, {
|
553
|
+
uninstallGitHooks: () => uninstallGitHooks,
|
554
|
+
rules: () => rules,
|
555
|
+
readCommitMessageFromFile: () => readCommitMessageFromFile,
|
556
|
+
parseCommitMessage: () => parseCommitMessage,
|
557
|
+
lintCommitMessage: () => lintCommitMessage,
|
558
|
+
installGitHooks: () => installGitHooks,
|
559
|
+
defaultConfig: () => defaultConfig,
|
560
|
+
config: () => config
|
561
|
+
});
|
562
|
+
var init_src = __esm(async () => {
|
563
|
+
init_config();
|
564
|
+
init_hooks();
|
565
|
+
init_lint();
|
566
|
+
init_rules();
|
567
|
+
init_utils();
|
568
|
+
});
|
569
|
+
|
570
|
+
// bin/cli.ts
|
571
|
+
import { Buffer } from "buffer";
|
572
|
+
import process4 from "process";
|
573
|
+
|
574
|
+
// node_modules/cac/dist/index.mjs
|
575
|
+
import { EventEmitter } from "events";
|
576
|
+
function toArr(any) {
|
577
|
+
return any == null ? [] : Array.isArray(any) ? any : [any];
|
578
|
+
}
|
579
|
+
function toVal(out, key, val, opts) {
|
580
|
+
var x, old = out[key], nxt = ~opts.string.indexOf(key) ? val == null || val === true ? "" : String(val) : typeof val === "boolean" ? val : ~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
|
581
|
+
out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
|
582
|
+
}
|
583
|
+
function mri2(args, opts) {
|
584
|
+
args = args || [];
|
585
|
+
opts = opts || {};
|
586
|
+
var k, arr, arg, name, val, out = { _: [] };
|
587
|
+
var i = 0, j = 0, idx = 0, len = args.length;
|
588
|
+
const alibi = opts.alias !== undefined;
|
589
|
+
const strict = opts.unknown !== undefined;
|
590
|
+
const defaults = opts.default !== undefined;
|
591
|
+
opts.alias = opts.alias || {};
|
592
|
+
opts.string = toArr(opts.string);
|
593
|
+
opts.boolean = toArr(opts.boolean);
|
594
|
+
if (alibi) {
|
595
|
+
for (k in opts.alias) {
|
596
|
+
arr = opts.alias[k] = toArr(opts.alias[k]);
|
597
|
+
for (i = 0;i < arr.length; i++) {
|
598
|
+
(opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
|
599
|
+
}
|
600
|
+
}
|
601
|
+
}
|
602
|
+
for (i = opts.boolean.length;i-- > 0; ) {
|
603
|
+
arr = opts.alias[opts.boolean[i]] || [];
|
604
|
+
for (j = arr.length;j-- > 0; )
|
605
|
+
opts.boolean.push(arr[j]);
|
606
|
+
}
|
607
|
+
for (i = opts.string.length;i-- > 0; ) {
|
608
|
+
arr = opts.alias[opts.string[i]] || [];
|
609
|
+
for (j = arr.length;j-- > 0; )
|
610
|
+
opts.string.push(arr[j]);
|
611
|
+
}
|
612
|
+
if (defaults) {
|
613
|
+
for (k in opts.default) {
|
614
|
+
name = typeof opts.default[k];
|
615
|
+
arr = opts.alias[k] = opts.alias[k] || [];
|
616
|
+
if (opts[name] !== undefined) {
|
617
|
+
opts[name].push(k);
|
618
|
+
for (i = 0;i < arr.length; i++) {
|
619
|
+
opts[name].push(arr[i]);
|
620
|
+
}
|
621
|
+
}
|
622
|
+
}
|
623
|
+
}
|
624
|
+
const keys = strict ? Object.keys(opts.alias) : [];
|
625
|
+
for (i = 0;i < len; i++) {
|
626
|
+
arg = args[i];
|
627
|
+
if (arg === "--") {
|
628
|
+
out._ = out._.concat(args.slice(++i));
|
629
|
+
break;
|
630
|
+
}
|
631
|
+
for (j = 0;j < arg.length; j++) {
|
632
|
+
if (arg.charCodeAt(j) !== 45)
|
633
|
+
break;
|
634
|
+
}
|
635
|
+
if (j === 0) {
|
636
|
+
out._.push(arg);
|
637
|
+
} else if (arg.substring(j, j + 3) === "no-") {
|
638
|
+
name = arg.substring(j + 3);
|
639
|
+
if (strict && !~keys.indexOf(name)) {
|
640
|
+
return opts.unknown(arg);
|
641
|
+
}
|
642
|
+
out[name] = false;
|
643
|
+
} else {
|
644
|
+
for (idx = j + 1;idx < arg.length; idx++) {
|
645
|
+
if (arg.charCodeAt(idx) === 61)
|
646
|
+
break;
|
647
|
+
}
|
648
|
+
name = arg.substring(j, idx);
|
649
|
+
val = arg.substring(++idx) || (i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i]);
|
650
|
+
arr = j === 2 ? [name] : name;
|
651
|
+
for (idx = 0;idx < arr.length; idx++) {
|
652
|
+
name = arr[idx];
|
653
|
+
if (strict && !~keys.indexOf(name))
|
654
|
+
return opts.unknown("-".repeat(j) + name);
|
655
|
+
toVal(out, name, idx + 1 < arr.length || val, opts);
|
656
|
+
}
|
657
|
+
}
|
658
|
+
}
|
659
|
+
if (defaults) {
|
660
|
+
for (k in opts.default) {
|
661
|
+
if (out[k] === undefined) {
|
662
|
+
out[k] = opts.default[k];
|
663
|
+
}
|
664
|
+
}
|
665
|
+
}
|
666
|
+
if (alibi) {
|
667
|
+
for (k in out) {
|
668
|
+
arr = opts.alias[k] || [];
|
669
|
+
while (arr.length > 0) {
|
670
|
+
out[arr.shift()] = out[k];
|
671
|
+
}
|
672
|
+
}
|
673
|
+
}
|
674
|
+
return out;
|
675
|
+
}
|
676
|
+
var removeBrackets = (v) => v.replace(/[<[].+/, "").trim();
|
677
|
+
var findAllBrackets = (v) => {
|
678
|
+
const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
|
679
|
+
const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
|
680
|
+
const res = [];
|
681
|
+
const parse = (match) => {
|
682
|
+
let variadic = false;
|
683
|
+
let value = match[1];
|
684
|
+
if (value.startsWith("...")) {
|
685
|
+
value = value.slice(3);
|
686
|
+
variadic = true;
|
687
|
+
}
|
688
|
+
return {
|
689
|
+
required: match[0].startsWith("<"),
|
690
|
+
value,
|
691
|
+
variadic
|
692
|
+
};
|
693
|
+
};
|
694
|
+
let angledMatch;
|
695
|
+
while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) {
|
696
|
+
res.push(parse(angledMatch));
|
697
|
+
}
|
698
|
+
let squareMatch;
|
699
|
+
while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) {
|
700
|
+
res.push(parse(squareMatch));
|
701
|
+
}
|
702
|
+
return res;
|
703
|
+
};
|
704
|
+
var getMriOptions = (options) => {
|
705
|
+
const result = { alias: {}, boolean: [] };
|
706
|
+
for (const [index, option] of options.entries()) {
|
707
|
+
if (option.names.length > 1) {
|
708
|
+
result.alias[option.names[0]] = option.names.slice(1);
|
709
|
+
}
|
710
|
+
if (option.isBoolean) {
|
711
|
+
if (option.negated) {
|
712
|
+
const hasStringTypeOption = options.some((o, i) => {
|
713
|
+
return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
|
714
|
+
});
|
715
|
+
if (!hasStringTypeOption) {
|
716
|
+
result.boolean.push(option.names[0]);
|
717
|
+
}
|
718
|
+
} else {
|
719
|
+
result.boolean.push(option.names[0]);
|
720
|
+
}
|
721
|
+
}
|
722
|
+
}
|
723
|
+
return result;
|
724
|
+
};
|
725
|
+
var findLongest = (arr) => {
|
726
|
+
return arr.sort((a, b) => {
|
727
|
+
return a.length > b.length ? -1 : 1;
|
728
|
+
})[0];
|
729
|
+
};
|
730
|
+
var padRight = (str, length) => {
|
731
|
+
return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
|
732
|
+
};
|
733
|
+
var camelcase = (input) => {
|
734
|
+
return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {
|
735
|
+
return p1 + p2.toUpperCase();
|
736
|
+
});
|
737
|
+
};
|
738
|
+
var setDotProp = (obj, keys, val) => {
|
739
|
+
let i = 0;
|
740
|
+
let length = keys.length;
|
741
|
+
let t = obj;
|
742
|
+
let x;
|
743
|
+
for (;i < length; ++i) {
|
744
|
+
x = t[keys[i]];
|
745
|
+
t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(".") || !(+keys[i + 1] > -1) ? {} : [];
|
746
|
+
}
|
747
|
+
};
|
748
|
+
var setByType = (obj, transforms) => {
|
749
|
+
for (const key of Object.keys(transforms)) {
|
750
|
+
const transform = transforms[key];
|
751
|
+
if (transform.shouldTransform) {
|
752
|
+
obj[key] = Array.prototype.concat.call([], obj[key]);
|
753
|
+
if (typeof transform.transformFunction === "function") {
|
754
|
+
obj[key] = obj[key].map(transform.transformFunction);
|
755
|
+
}
|
756
|
+
}
|
757
|
+
}
|
758
|
+
};
|
759
|
+
var getFileName = (input) => {
|
760
|
+
const m = /([^\\\/]+)$/.exec(input);
|
761
|
+
return m ? m[1] : "";
|
762
|
+
};
|
763
|
+
var camelcaseOptionName = (name) => {
|
764
|
+
return name.split(".").map((v, i) => {
|
765
|
+
return i === 0 ? camelcase(v) : v;
|
766
|
+
}).join(".");
|
767
|
+
};
|
768
|
+
|
769
|
+
class CACError extends Error {
|
770
|
+
constructor(message) {
|
771
|
+
super(message);
|
772
|
+
this.name = this.constructor.name;
|
773
|
+
if (typeof Error.captureStackTrace === "function") {
|
774
|
+
Error.captureStackTrace(this, this.constructor);
|
775
|
+
} else {
|
776
|
+
this.stack = new Error(message).stack;
|
777
|
+
}
|
778
|
+
}
|
779
|
+
}
|
780
|
+
|
781
|
+
class Option {
|
782
|
+
constructor(rawName, description, config) {
|
783
|
+
this.rawName = rawName;
|
784
|
+
this.description = description;
|
785
|
+
this.config = Object.assign({}, config);
|
786
|
+
rawName = rawName.replace(/\.\*/g, "");
|
787
|
+
this.negated = false;
|
788
|
+
this.names = removeBrackets(rawName).split(",").map((v) => {
|
789
|
+
let name = v.trim().replace(/^-{1,2}/, "");
|
790
|
+
if (name.startsWith("no-")) {
|
791
|
+
this.negated = true;
|
792
|
+
name = name.replace(/^no-/, "");
|
793
|
+
}
|
794
|
+
return camelcaseOptionName(name);
|
795
|
+
}).sort((a, b) => a.length > b.length ? 1 : -1);
|
796
|
+
this.name = this.names[this.names.length - 1];
|
797
|
+
if (this.negated && this.config.default == null) {
|
798
|
+
this.config.default = true;
|
799
|
+
}
|
800
|
+
if (rawName.includes("<")) {
|
801
|
+
this.required = true;
|
802
|
+
} else if (rawName.includes("[")) {
|
803
|
+
this.required = false;
|
804
|
+
} else {
|
805
|
+
this.isBoolean = true;
|
806
|
+
}
|
807
|
+
}
|
808
|
+
}
|
809
|
+
var processArgs = process.argv;
|
810
|
+
var platformInfo = `${process.platform}-${process.arch} node-${process.version}`;
|
811
|
+
|
812
|
+
class Command {
|
813
|
+
constructor(rawName, description, config = {}, cli) {
|
814
|
+
this.rawName = rawName;
|
815
|
+
this.description = description;
|
816
|
+
this.config = config;
|
817
|
+
this.cli = cli;
|
818
|
+
this.options = [];
|
819
|
+
this.aliasNames = [];
|
820
|
+
this.name = removeBrackets(rawName);
|
821
|
+
this.args = findAllBrackets(rawName);
|
822
|
+
this.examples = [];
|
823
|
+
}
|
824
|
+
usage(text) {
|
825
|
+
this.usageText = text;
|
826
|
+
return this;
|
827
|
+
}
|
828
|
+
allowUnknownOptions() {
|
829
|
+
this.config.allowUnknownOptions = true;
|
830
|
+
return this;
|
831
|
+
}
|
832
|
+
ignoreOptionDefaultValue() {
|
833
|
+
this.config.ignoreOptionDefaultValue = true;
|
834
|
+
return this;
|
835
|
+
}
|
836
|
+
version(version, customFlags = "-v, --version") {
|
837
|
+
this.versionNumber = version;
|
838
|
+
this.option(customFlags, "Display version number");
|
839
|
+
return this;
|
840
|
+
}
|
841
|
+
example(example) {
|
842
|
+
this.examples.push(example);
|
843
|
+
return this;
|
844
|
+
}
|
845
|
+
option(rawName, description, config) {
|
846
|
+
const option = new Option(rawName, description, config);
|
847
|
+
this.options.push(option);
|
848
|
+
return this;
|
849
|
+
}
|
850
|
+
alias(name) {
|
851
|
+
this.aliasNames.push(name);
|
852
|
+
return this;
|
853
|
+
}
|
854
|
+
action(callback) {
|
855
|
+
this.commandAction = callback;
|
856
|
+
return this;
|
857
|
+
}
|
858
|
+
isMatched(name) {
|
859
|
+
return this.name === name || this.aliasNames.includes(name);
|
860
|
+
}
|
861
|
+
get isDefaultCommand() {
|
862
|
+
return this.name === "" || this.aliasNames.includes("!");
|
863
|
+
}
|
864
|
+
get isGlobalCommand() {
|
865
|
+
return this instanceof GlobalCommand;
|
866
|
+
}
|
867
|
+
hasOption(name) {
|
868
|
+
name = name.split(".")[0];
|
869
|
+
return this.options.find((option) => {
|
870
|
+
return option.names.includes(name);
|
871
|
+
});
|
872
|
+
}
|
873
|
+
outputHelp() {
|
874
|
+
const { name, commands } = this.cli;
|
875
|
+
const {
|
876
|
+
versionNumber,
|
877
|
+
options: globalOptions,
|
878
|
+
helpCallback
|
879
|
+
} = this.cli.globalCommand;
|
880
|
+
let sections = [
|
881
|
+
{
|
882
|
+
body: `${name}${versionNumber ? `/${versionNumber}` : ""}`
|
883
|
+
}
|
884
|
+
];
|
885
|
+
sections.push({
|
886
|
+
title: "Usage",
|
887
|
+
body: ` $ ${name} ${this.usageText || this.rawName}`
|
888
|
+
});
|
889
|
+
const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
|
890
|
+
if (showCommands) {
|
891
|
+
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
892
|
+
sections.push({
|
893
|
+
title: "Commands",
|
894
|
+
body: commands.map((command) => {
|
895
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
|
896
|
+
}).join(`
|
897
|
+
`)
|
898
|
+
});
|
899
|
+
sections.push({
|
900
|
+
title: `For more info, run any command with the \`--help\` flag`,
|
901
|
+
body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join(`
|
902
|
+
`)
|
903
|
+
});
|
904
|
+
}
|
905
|
+
let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
|
906
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand) {
|
907
|
+
options = options.filter((option) => option.name !== "version");
|
908
|
+
}
|
909
|
+
if (options.length > 0) {
|
910
|
+
const longestOptionName = findLongest(options.map((option) => option.rawName));
|
911
|
+
sections.push({
|
912
|
+
title: "Options",
|
913
|
+
body: options.map((option) => {
|
914
|
+
return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === undefined ? "" : `(default: ${option.config.default})`}`;
|
915
|
+
}).join(`
|
916
|
+
`)
|
917
|
+
});
|
918
|
+
}
|
919
|
+
if (this.examples.length > 0) {
|
920
|
+
sections.push({
|
921
|
+
title: "Examples",
|
922
|
+
body: this.examples.map((example) => {
|
923
|
+
if (typeof example === "function") {
|
924
|
+
return example(name);
|
925
|
+
}
|
926
|
+
return example;
|
927
|
+
}).join(`
|
928
|
+
`)
|
929
|
+
});
|
930
|
+
}
|
931
|
+
if (helpCallback) {
|
932
|
+
sections = helpCallback(sections) || sections;
|
933
|
+
}
|
934
|
+
console.log(sections.map((section) => {
|
935
|
+
return section.title ? `${section.title}:
|
936
|
+
${section.body}` : section.body;
|
937
|
+
}).join(`
|
938
|
+
|
939
|
+
`));
|
940
|
+
}
|
941
|
+
outputVersion() {
|
942
|
+
const { name } = this.cli;
|
943
|
+
const { versionNumber } = this.cli.globalCommand;
|
944
|
+
if (versionNumber) {
|
945
|
+
console.log(`${name}/${versionNumber} ${platformInfo}`);
|
946
|
+
}
|
947
|
+
}
|
948
|
+
checkRequiredArgs() {
|
949
|
+
const minimalArgsCount = this.args.filter((arg) => arg.required).length;
|
950
|
+
if (this.cli.args.length < minimalArgsCount) {
|
951
|
+
throw new CACError(`missing required args for command \`${this.rawName}\``);
|
952
|
+
}
|
953
|
+
}
|
954
|
+
checkUnknownOptions() {
|
955
|
+
const { options, globalCommand } = this.cli;
|
956
|
+
if (!this.config.allowUnknownOptions) {
|
957
|
+
for (const name of Object.keys(options)) {
|
958
|
+
if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) {
|
959
|
+
throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
|
960
|
+
}
|
961
|
+
}
|
962
|
+
}
|
963
|
+
}
|
964
|
+
checkOptionValue() {
|
965
|
+
const { options: parsedOptions, globalCommand } = this.cli;
|
966
|
+
const options = [...globalCommand.options, ...this.options];
|
967
|
+
for (const option of options) {
|
968
|
+
const value = parsedOptions[option.name.split(".")[0]];
|
969
|
+
if (option.required) {
|
970
|
+
const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
|
971
|
+
if (value === true || value === false && !hasNegated) {
|
972
|
+
throw new CACError(`option \`${option.rawName}\` value is missing`);
|
973
|
+
}
|
974
|
+
}
|
975
|
+
}
|
976
|
+
}
|
977
|
+
}
|
978
|
+
|
979
|
+
class GlobalCommand extends Command {
|
980
|
+
constructor(cli) {
|
981
|
+
super("@@global@@", "", {}, cli);
|
982
|
+
}
|
983
|
+
}
|
984
|
+
var __assign = Object.assign;
|
985
|
+
|
986
|
+
class CAC extends EventEmitter {
|
987
|
+
constructor(name = "") {
|
988
|
+
super();
|
989
|
+
this.name = name;
|
990
|
+
this.commands = [];
|
991
|
+
this.rawArgs = [];
|
992
|
+
this.args = [];
|
993
|
+
this.options = {};
|
994
|
+
this.globalCommand = new GlobalCommand(this);
|
995
|
+
this.globalCommand.usage("<command> [options]");
|
996
|
+
}
|
997
|
+
usage(text) {
|
998
|
+
this.globalCommand.usage(text);
|
999
|
+
return this;
|
1000
|
+
}
|
1001
|
+
command(rawName, description, config) {
|
1002
|
+
const command = new Command(rawName, description || "", config, this);
|
1003
|
+
command.globalCommand = this.globalCommand;
|
1004
|
+
this.commands.push(command);
|
1005
|
+
return command;
|
1006
|
+
}
|
1007
|
+
option(rawName, description, config) {
|
1008
|
+
this.globalCommand.option(rawName, description, config);
|
1009
|
+
return this;
|
1010
|
+
}
|
1011
|
+
help(callback) {
|
1012
|
+
this.globalCommand.option("-h, --help", "Display this message");
|
1013
|
+
this.globalCommand.helpCallback = callback;
|
1014
|
+
this.showHelpOnExit = true;
|
1015
|
+
return this;
|
1016
|
+
}
|
1017
|
+
version(version, customFlags = "-v, --version") {
|
1018
|
+
this.globalCommand.version(version, customFlags);
|
1019
|
+
this.showVersionOnExit = true;
|
1020
|
+
return this;
|
1021
|
+
}
|
1022
|
+
example(example) {
|
1023
|
+
this.globalCommand.example(example);
|
1024
|
+
return this;
|
1025
|
+
}
|
1026
|
+
outputHelp() {
|
1027
|
+
if (this.matchedCommand) {
|
1028
|
+
this.matchedCommand.outputHelp();
|
1029
|
+
} else {
|
1030
|
+
this.globalCommand.outputHelp();
|
1031
|
+
}
|
1032
|
+
}
|
1033
|
+
outputVersion() {
|
1034
|
+
this.globalCommand.outputVersion();
|
1035
|
+
}
|
1036
|
+
setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
|
1037
|
+
this.args = args;
|
1038
|
+
this.options = options;
|
1039
|
+
if (matchedCommand) {
|
1040
|
+
this.matchedCommand = matchedCommand;
|
1041
|
+
}
|
1042
|
+
if (matchedCommandName) {
|
1043
|
+
this.matchedCommandName = matchedCommandName;
|
1044
|
+
}
|
1045
|
+
return this;
|
1046
|
+
}
|
1047
|
+
unsetMatchedCommand() {
|
1048
|
+
this.matchedCommand = undefined;
|
1049
|
+
this.matchedCommandName = undefined;
|
1050
|
+
}
|
1051
|
+
parse(argv = processArgs, {
|
1052
|
+
run = true
|
1053
|
+
} = {}) {
|
1054
|
+
this.rawArgs = argv;
|
1055
|
+
if (!this.name) {
|
1056
|
+
this.name = argv[1] ? getFileName(argv[1]) : "cli";
|
1057
|
+
}
|
1058
|
+
let shouldParse = true;
|
1059
|
+
for (const command of this.commands) {
|
1060
|
+
const parsed = this.mri(argv.slice(2), command);
|
1061
|
+
const commandName = parsed.args[0];
|
1062
|
+
if (command.isMatched(commandName)) {
|
1063
|
+
shouldParse = false;
|
1064
|
+
const parsedInfo = __assign(__assign({}, parsed), {
|
1065
|
+
args: parsed.args.slice(1)
|
1066
|
+
});
|
1067
|
+
this.setParsedInfo(parsedInfo, command, commandName);
|
1068
|
+
this.emit(`command:${commandName}`, command);
|
1069
|
+
}
|
1070
|
+
}
|
1071
|
+
if (shouldParse) {
|
1072
|
+
for (const command of this.commands) {
|
1073
|
+
if (command.name === "") {
|
1074
|
+
shouldParse = false;
|
1075
|
+
const parsed = this.mri(argv.slice(2), command);
|
1076
|
+
this.setParsedInfo(parsed, command);
|
1077
|
+
this.emit(`command:!`, command);
|
1078
|
+
}
|
1079
|
+
}
|
1080
|
+
}
|
1081
|
+
if (shouldParse) {
|
1082
|
+
const parsed = this.mri(argv.slice(2));
|
1083
|
+
this.setParsedInfo(parsed);
|
1084
|
+
}
|
1085
|
+
if (this.options.help && this.showHelpOnExit) {
|
1086
|
+
this.outputHelp();
|
1087
|
+
run = false;
|
1088
|
+
this.unsetMatchedCommand();
|
1089
|
+
}
|
1090
|
+
if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
|
1091
|
+
this.outputVersion();
|
1092
|
+
run = false;
|
1093
|
+
this.unsetMatchedCommand();
|
1094
|
+
}
|
1095
|
+
const parsedArgv = { args: this.args, options: this.options };
|
1096
|
+
if (run) {
|
1097
|
+
this.runMatchedCommand();
|
1098
|
+
}
|
1099
|
+
if (!this.matchedCommand && this.args[0]) {
|
1100
|
+
this.emit("command:*");
|
1101
|
+
}
|
1102
|
+
return parsedArgv;
|
1103
|
+
}
|
1104
|
+
mri(argv, command) {
|
1105
|
+
const cliOptions = [
|
1106
|
+
...this.globalCommand.options,
|
1107
|
+
...command ? command.options : []
|
1108
|
+
];
|
1109
|
+
const mriOptions = getMriOptions(cliOptions);
|
1110
|
+
let argsAfterDoubleDashes = [];
|
1111
|
+
const doubleDashesIndex = argv.indexOf("--");
|
1112
|
+
if (doubleDashesIndex > -1) {
|
1113
|
+
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
|
1114
|
+
argv = argv.slice(0, doubleDashesIndex);
|
1115
|
+
}
|
1116
|
+
let parsed = mri2(argv, mriOptions);
|
1117
|
+
parsed = Object.keys(parsed).reduce((res, name) => {
|
1118
|
+
return __assign(__assign({}, res), {
|
1119
|
+
[camelcaseOptionName(name)]: parsed[name]
|
1120
|
+
});
|
1121
|
+
}, { _: [] });
|
1122
|
+
const args = parsed._;
|
1123
|
+
const options = {
|
1124
|
+
"--": argsAfterDoubleDashes
|
1125
|
+
};
|
1126
|
+
const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
|
1127
|
+
let transforms = Object.create(null);
|
1128
|
+
for (const cliOption of cliOptions) {
|
1129
|
+
if (!ignoreDefault && cliOption.config.default !== undefined) {
|
1130
|
+
for (const name of cliOption.names) {
|
1131
|
+
options[name] = cliOption.config.default;
|
1132
|
+
}
|
1133
|
+
}
|
1134
|
+
if (Array.isArray(cliOption.config.type)) {
|
1135
|
+
if (transforms[cliOption.name] === undefined) {
|
1136
|
+
transforms[cliOption.name] = Object.create(null);
|
1137
|
+
transforms[cliOption.name]["shouldTransform"] = true;
|
1138
|
+
transforms[cliOption.name]["transformFunction"] = cliOption.config.type[0];
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
}
|
1142
|
+
for (const key of Object.keys(parsed)) {
|
1143
|
+
if (key !== "_") {
|
1144
|
+
const keys = key.split(".");
|
1145
|
+
setDotProp(options, keys, parsed[key]);
|
1146
|
+
setByType(options, transforms);
|
1147
|
+
}
|
1148
|
+
}
|
1149
|
+
return {
|
1150
|
+
args,
|
1151
|
+
options
|
1152
|
+
};
|
1153
|
+
}
|
1154
|
+
runMatchedCommand() {
|
1155
|
+
const { args, options, matchedCommand: command } = this;
|
1156
|
+
if (!command || !command.commandAction)
|
1157
|
+
return;
|
1158
|
+
command.checkUnknownOptions();
|
1159
|
+
command.checkOptionValue();
|
1160
|
+
command.checkRequiredArgs();
|
1161
|
+
const actionArgs = [];
|
1162
|
+
command.args.forEach((arg, index) => {
|
1163
|
+
if (arg.variadic) {
|
1164
|
+
actionArgs.push(args.slice(index));
|
1165
|
+
} else {
|
1166
|
+
actionArgs.push(args[index]);
|
1167
|
+
}
|
1168
|
+
});
|
1169
|
+
actionArgs.push(options);
|
1170
|
+
return command.commandAction.apply(this, actionArgs);
|
1171
|
+
}
|
1172
|
+
}
|
1173
|
+
// package.json
|
1174
|
+
var version = "0.1.4";
|
1175
|
+
|
1176
|
+
// bin/cli.ts
|
1177
|
+
await init_config();
|
1178
|
+
init_utils();
|
1179
|
+
var cli = new CAC("gitlint");
|
1180
|
+
cli.command("[...files]", "Lint commit message").example("gitlint").example("gitlint .git/COMMIT_EDITMSG").example("git log -1 --pretty=%B | gitlint").option("--verbose", "Enable verbose output", { default: defaultConfig.verbose }).option("--config <file>", "Path to config file").option("--edit", "Read commit message from a file (used by git hooks)").action(async (files, options) => {
|
1181
|
+
let commitMessage = "";
|
1182
|
+
if (options.edit && files.length > 0) {
|
1183
|
+
commitMessage = readCommitMessageFromFile(files[0]);
|
1184
|
+
} else if (files.length > 0) {
|
1185
|
+
commitMessage = readCommitMessageFromFile(files[0]);
|
1186
|
+
} else if (!process4.stdin.isTTY) {
|
1187
|
+
const chunks = [];
|
1188
|
+
for await (const chunk of process4.stdin)
|
1189
|
+
chunks.push(Buffer.from(chunk));
|
1190
|
+
commitMessage = Buffer.concat(chunks).toString("utf8");
|
1191
|
+
} else {
|
1192
|
+
cli.outputHelp();
|
1193
|
+
process4.exit(1);
|
1194
|
+
}
|
1195
|
+
try {
|
1196
|
+
const { lintCommitMessage: lintCommitMessage2 } = await init_src().then(() => exports_src);
|
1197
|
+
const result = lintCommitMessage2(commitMessage, options.verbose);
|
1198
|
+
if (!result.valid) {
|
1199
|
+
console.error("Commit message validation failed:");
|
1200
|
+
result.errors.forEach((error) => {
|
1201
|
+
console.error(`- ${error}`);
|
1202
|
+
});
|
1203
|
+
process4.exit(1);
|
1204
|
+
}
|
1205
|
+
if (options.verbose) {
|
1206
|
+
console.log("Commit message validation passed! \u2705");
|
1207
|
+
}
|
1208
|
+
} catch (error) {
|
1209
|
+
console.error("Error during commit message linting:");
|
1210
|
+
console.error(error);
|
1211
|
+
process4.exit(1);
|
1212
|
+
}
|
1213
|
+
process4.exit(0);
|
1214
|
+
});
|
1215
|
+
cli.command("hooks", "Manage git hooks").example("gitlint hooks --install").example("gitlint hooks --install --force").example("gitlint hooks --uninstall").option("--install", "Install git hooks").option("--uninstall", "Uninstall git hooks").option("--force", "Force overwrite existing hooks").action(async (options) => {
|
1216
|
+
try {
|
1217
|
+
const { installGitHooks: installGitHooks2, uninstallGitHooks: uninstallGitHooks2 } = await init_src().then(() => exports_src);
|
1218
|
+
if (options.install) {
|
1219
|
+
const success = installGitHooks2(options.force);
|
1220
|
+
process4.exit(success ? 0 : 1);
|
1221
|
+
} else if (options.uninstall) {
|
1222
|
+
const success = uninstallGitHooks2();
|
1223
|
+
process4.exit(success ? 0 : 1);
|
1224
|
+
} else {
|
1225
|
+
console.error("Please specify --install or --uninstall");
|
1226
|
+
process4.exit(1);
|
1227
|
+
}
|
1228
|
+
} catch (error) {
|
1229
|
+
console.error("Error managing git hooks:");
|
1230
|
+
console.error(error);
|
1231
|
+
process4.exit(1);
|
1232
|
+
}
|
1233
|
+
});
|
1234
|
+
cli.command("version", "Show the version of gitlint").example("gitlint version").action(() => {
|
1235
|
+
console.log(version);
|
1236
|
+
});
|
1237
|
+
cli.help();
|
1238
|
+
cli.version(version);
|
1239
|
+
cli.parse();
|
package/dist/hooks.d.ts
CHANGED
package/dist/index.js
CHANGED
@@ -161,11 +161,23 @@ async function loadConfig({
|
|
161
161
|
for (const ext of extensions) {
|
162
162
|
const fullPath = resolve(baseDir, `${configPath}${ext}`);
|
163
163
|
const config2 = await tryLoadConfig(fullPath, defaultConfig);
|
164
|
-
if (config2 !== null)
|
164
|
+
if (config2 !== null) {
|
165
165
|
return config2;
|
166
|
+
}
|
166
167
|
}
|
167
168
|
}
|
168
|
-
|
169
|
+
try {
|
170
|
+
const pkgPath = resolve(baseDir, "package.json");
|
171
|
+
if (existsSync(pkgPath)) {
|
172
|
+
const pkg = await import(pkgPath);
|
173
|
+
const pkgConfig = pkg[name];
|
174
|
+
if (pkgConfig && typeof pkgConfig === "object" && !Array.isArray(pkgConfig)) {
|
175
|
+
try {
|
176
|
+
return deepMerge(defaultConfig, pkgConfig);
|
177
|
+
} catch {}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
} catch {}
|
169
181
|
return defaultConfig;
|
170
182
|
}
|
171
183
|
var defaultConfigDir = resolve(process.cwd(), "config");
|
@@ -270,9 +282,10 @@ var conventionalCommits = {
|
|
270
282
|
name: "conventional-commits",
|
271
283
|
description: "Enforces conventional commit format",
|
272
284
|
validate: (commitMsg) => {
|
273
|
-
const pattern = /^(?:build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(?:\([a-z0-9-]+\))?:
|
274
|
-
|
275
|
-
`)[0]
|
285
|
+
const pattern = /^(?:build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(?:\([a-z0-9-]+\))?: .+$/i;
|
286
|
+
const firstLine = commitMsg.split(`
|
287
|
+
`)[0];
|
288
|
+
if (!pattern.test(firstLine.replace(/['"]/g, ""))) {
|
276
289
|
return {
|
277
290
|
valid: false,
|
278
291
|
message: "Commit message header does not follow conventional commit format: <type>[(scope)]: <description>"
|
@@ -424,7 +437,7 @@ function parseCommitMessage(message) {
|
|
424
437
|
const lines = message.split(`
|
425
438
|
`);
|
426
439
|
const header = lines[0] || "";
|
427
|
-
const headerMatch = header.match(/^(?<type>\w+)(?:\((?<scope>[^)]+)\))?:(?<subject>.+)$/);
|
440
|
+
const headerMatch = header.replace(/['"]/g, "").match(/^(?<type>\w+)(?:\((?<scope>[^)]+)\))?: ?(?<subject>.+)$/);
|
428
441
|
let type = null;
|
429
442
|
let scope = null;
|
430
443
|
let subject = null;
|
package/dist/lint.d.ts
CHANGED
@@ -23,4 +23,4 @@ declare const defaultConfig: {
|
|
23
23
|
};
|
24
24
|
declare const config: unknown;
|
25
25
|
declare function normalizeRuleLevel(level: RuleLevel): 0 | 1 | 2;
|
26
|
-
export declare function lintCommitMessage(message: string, verbose
|
26
|
+
export declare function lintCommitMessage(message: string, verbose?: boolean): LintResult;
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@stacksjs/gitlint",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.1.
|
4
|
+
"version": "0.1.4",
|
5
5
|
"description": "Efficient Git Commit Message Linting and Formatting",
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
7
7
|
"license": "MIT",
|
@@ -59,24 +59,29 @@
|
|
59
59
|
"dev:docs": "bun --bun vitepress dev docs",
|
60
60
|
"build:docs": "bun --bun vitepress build docs",
|
61
61
|
"preview:docs": "bun --bun vitepress preview docs",
|
62
|
-
"typecheck": "bun --bun tsc --noEmit"
|
62
|
+
"typecheck": "bun --bun tsc --noEmit",
|
63
|
+
"postinstall": "bun run scripts/postinstall.ts"
|
63
64
|
},
|
64
65
|
"devDependencies": {
|
65
66
|
"@stacksjs/docs": "^0.70.23",
|
66
67
|
"@stacksjs/eslint-config": "^4.10.2-beta.3",
|
67
|
-
"@types/bun": "^1.2.
|
68
|
+
"@types/bun": "^1.2.12",
|
68
69
|
"bumpp": "^10.1.0",
|
69
|
-
"bun-
|
70
|
-
"
|
70
|
+
"bun-git-hooks": "^0.2.13",
|
71
|
+
"bun-plugin-dtsx": "^0.21.12",
|
72
|
+
"bunfig": "^0.8.5",
|
71
73
|
"changelogen": "^0.6.1",
|
72
|
-
"lint-staged": "^15.5.1",
|
73
|
-
"simple-git-hooks": "^2.12.1",
|
74
74
|
"typescript": "^5.8.3"
|
75
75
|
},
|
76
|
+
"git-hooks": {
|
77
|
+
"pre-commit": {
|
78
|
+
"staged-lint": {
|
79
|
+
"*.{js,ts,json,yaml,yml,md}": "bunx --bun eslint . --fix"
|
80
|
+
}
|
81
|
+
},
|
82
|
+
"commit-msg": "bunx gitlint"
|
83
|
+
},
|
76
84
|
"overrides": {
|
77
85
|
"unconfig": "0.3.10"
|
78
|
-
},
|
79
|
-
"lint-staged": {
|
80
|
-
"*.{js,ts}": "bunx --bun eslint . --fix"
|
81
86
|
}
|
82
87
|
}
|