preguito 0.1.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 +258 -0
- package/dist/cli-sea.cjs +4165 -0
- package/dist/cli.mjs +1234 -0
- package/dist/index.cjs +276 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.mts +37 -0
- package/dist/index.mjs +269 -0
- package/package.json +42 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let node_fs_promises = require("node:fs/promises");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_os = require("node:os");
|
|
6
|
+
|
|
7
|
+
//#region src/utils/errors.ts
|
|
8
|
+
var PrequitoError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "PrequitoError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var TemplateMissingVariableError = class extends PrequitoError {
|
|
15
|
+
missingVariables;
|
|
16
|
+
constructor(missing) {
|
|
17
|
+
super(`Missing template variable(s): ${missing.join(", ")}. Provide shortcodes or check your config.`);
|
|
18
|
+
this.name = "TemplateMissingVariableError";
|
|
19
|
+
this.missingVariables = missing;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var TemplateMissingMessageError = class extends PrequitoError {
|
|
23
|
+
constructor(placeholderName) {
|
|
24
|
+
super(`Template requires a message (the <${placeholderName}> placeholder). Provide it as the last argument(s).`);
|
|
25
|
+
this.name = "TemplateMissingMessageError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/template/engine.ts
|
|
31
|
+
const VARIABLE_PATTERN = /\{\{(\w+)\}\}/g;
|
|
32
|
+
const MESSAGE_PLACEHOLDER_PATTERN = /<(\w+)>/;
|
|
33
|
+
function parseTemplate(template) {
|
|
34
|
+
const variables = [];
|
|
35
|
+
let match;
|
|
36
|
+
const pattern = new RegExp(VARIABLE_PATTERN.source, "g");
|
|
37
|
+
while ((match = pattern.exec(template)) !== null) if (!variables.includes(match[1])) variables.push(match[1]);
|
|
38
|
+
const messageMatch = MESSAGE_PLACEHOLDER_PATTERN.exec(template);
|
|
39
|
+
return {
|
|
40
|
+
variables,
|
|
41
|
+
messagePlaceholder: messageMatch ? messageMatch[1] : null
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function renderTemplate(template, context, message) {
|
|
45
|
+
const parsed = parseTemplate(template);
|
|
46
|
+
const missing = parsed.variables.filter((v) => !(v in context));
|
|
47
|
+
if (missing.length > 0) throw new TemplateMissingVariableError(missing);
|
|
48
|
+
let result = template.replace(/\{\{(\w+)\}\}/g, (_, varName) => {
|
|
49
|
+
return context[varName];
|
|
50
|
+
});
|
|
51
|
+
if (parsed.messagePlaceholder) {
|
|
52
|
+
if (!message) throw new TemplateMissingMessageError(parsed.messagePlaceholder);
|
|
53
|
+
result = result.replace(/<\w+>/, message);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
function mergeContext(defaults, overrides) {
|
|
58
|
+
return {
|
|
59
|
+
...defaults,
|
|
60
|
+
...overrides
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/config/types.ts
|
|
66
|
+
const PREDEFINED_TYPES = [
|
|
67
|
+
{
|
|
68
|
+
key: "f",
|
|
69
|
+
label: "feat"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: "x",
|
|
73
|
+
label: "fix"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: "c",
|
|
77
|
+
label: "chore"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: "t",
|
|
81
|
+
label: "test"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
key: "l",
|
|
85
|
+
label: "lint"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
key: "r",
|
|
89
|
+
label: "refactor"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: "o",
|
|
93
|
+
label: "docs"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: "y",
|
|
97
|
+
label: "style"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: "e",
|
|
101
|
+
label: "perf"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "b",
|
|
105
|
+
label: "build"
|
|
106
|
+
}
|
|
107
|
+
];
|
|
108
|
+
const PREDEFINED_ENVIRONMENTS = [
|
|
109
|
+
{
|
|
110
|
+
key: "p",
|
|
111
|
+
label: "prd"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
key: "u",
|
|
115
|
+
label: "uat"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
key: "h",
|
|
119
|
+
label: "homolog"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: "d",
|
|
123
|
+
label: "dev"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: "s",
|
|
127
|
+
label: "staging"
|
|
128
|
+
}
|
|
129
|
+
];
|
|
130
|
+
function generateTemplate(features, hasPrefix) {
|
|
131
|
+
const { cardId, type, environment } = features;
|
|
132
|
+
return [
|
|
133
|
+
cardId ? hasPrefix ? "[{{prefix}}-{{card_id}}]" : "[{{card_id}}]" : "",
|
|
134
|
+
type && environment ? "{{type}}({{environment}}):" : type ? "{{type}}:" : environment ? "({{environment}}):" : "",
|
|
135
|
+
"<message>"
|
|
136
|
+
].filter((p) => p !== "").join(" ");
|
|
137
|
+
}
|
|
138
|
+
const DEFAULT_CONFIG = {
|
|
139
|
+
template: "{{type}}: <message>",
|
|
140
|
+
features: {
|
|
141
|
+
cardId: false,
|
|
142
|
+
type: true,
|
|
143
|
+
environment: false
|
|
144
|
+
},
|
|
145
|
+
types: [
|
|
146
|
+
{
|
|
147
|
+
key: "f",
|
|
148
|
+
label: "feat"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "x",
|
|
152
|
+
label: "fix"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
key: "c",
|
|
156
|
+
label: "chore"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
key: "t",
|
|
160
|
+
label: "test"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
key: "r",
|
|
164
|
+
label: "refactor"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
key: "o",
|
|
168
|
+
label: "docs"
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
environments: [],
|
|
172
|
+
defaults: {}
|
|
173
|
+
};
|
|
174
|
+
const CONFIG_PATHS = [
|
|
175
|
+
".preguitorc",
|
|
176
|
+
".preguitorc.json",
|
|
177
|
+
".config/preguito/config.json"
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/config/loader.ts
|
|
182
|
+
async function findConfigPath() {
|
|
183
|
+
const cwd = process.cwd();
|
|
184
|
+
const home = (0, node_os.homedir)();
|
|
185
|
+
for (const relPath of [".preguitorc", ".preguitorc.json"]) {
|
|
186
|
+
const fullPath = (0, node_path.join)(cwd, relPath);
|
|
187
|
+
if ((0, node_fs.existsSync)(fullPath)) return fullPath;
|
|
188
|
+
}
|
|
189
|
+
for (const relPath of CONFIG_PATHS) {
|
|
190
|
+
const fullPath = (0, node_path.join)(home, relPath);
|
|
191
|
+
if ((0, node_fs.existsSync)(fullPath)) return fullPath;
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
async function loadConfig() {
|
|
196
|
+
const configPath = await findConfigPath();
|
|
197
|
+
if (!configPath) return null;
|
|
198
|
+
const raw = await (0, node_fs_promises.readFile)(configPath, "utf-8");
|
|
199
|
+
return validateConfig(JSON.parse(raw));
|
|
200
|
+
}
|
|
201
|
+
async function loadConfigOrDefault() {
|
|
202
|
+
return await loadConfig() ?? DEFAULT_CONFIG;
|
|
203
|
+
}
|
|
204
|
+
function validateConfig(obj) {
|
|
205
|
+
if (typeof obj !== "object" || obj === null) throw new Error("Config must be a JSON object");
|
|
206
|
+
const config = obj;
|
|
207
|
+
if (typeof config.template !== "string" || config.template.trim() === "") throw new Error("Config must have a non-empty \"template\" string field");
|
|
208
|
+
const defaults = {};
|
|
209
|
+
if (config.defaults !== void 0) {
|
|
210
|
+
if (typeof config.defaults !== "object" || config.defaults === null) throw new Error("\"defaults\" must be an object");
|
|
211
|
+
for (const [key, value] of Object.entries(config.defaults)) {
|
|
212
|
+
if (typeof value !== "string") throw new Error(`Default value for "${key}" must be a string`);
|
|
213
|
+
defaults[key] = value;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let features;
|
|
217
|
+
if (config.features && typeof config.features === "object") {
|
|
218
|
+
const f = config.features;
|
|
219
|
+
features = {
|
|
220
|
+
cardId: Boolean(f.cardId),
|
|
221
|
+
type: Boolean(f.type),
|
|
222
|
+
environment: Boolean(f.environment)
|
|
223
|
+
};
|
|
224
|
+
} else features = inferFeaturesFromTemplate(config.template);
|
|
225
|
+
let types = [];
|
|
226
|
+
if (Array.isArray(config.types)) types = validateShortcodeArray(config.types, "types");
|
|
227
|
+
else if (features.type) types = [...PREDEFINED_TYPES];
|
|
228
|
+
let environments = [];
|
|
229
|
+
if (Array.isArray(config.environments)) environments = validateShortcodeArray(config.environments, "environments");
|
|
230
|
+
else if (features.environment) environments = [...PREDEFINED_ENVIRONMENTS];
|
|
231
|
+
return {
|
|
232
|
+
template: config.template,
|
|
233
|
+
features,
|
|
234
|
+
types,
|
|
235
|
+
environments,
|
|
236
|
+
defaults
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function inferFeaturesFromTemplate(template) {
|
|
240
|
+
const parsed = parseTemplate(template);
|
|
241
|
+
return {
|
|
242
|
+
cardId: parsed.variables.includes("card_id"),
|
|
243
|
+
type: parsed.variables.includes("type"),
|
|
244
|
+
environment: parsed.variables.includes("environment")
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function validateShortcodeArray(arr, fieldName) {
|
|
248
|
+
const result = [];
|
|
249
|
+
for (let i = 0; i < arr.length; i++) {
|
|
250
|
+
const item = arr[i];
|
|
251
|
+
if (typeof item !== "object" || item === null || typeof item.key !== "string" || typeof item.label !== "string") throw new Error(`${fieldName}[${i}] must be an object with "key" and "label" string fields`);
|
|
252
|
+
const entry = item;
|
|
253
|
+
if (entry.key.length !== 1) throw new Error(`${fieldName}[${i}].key must be a single character, got "${entry.key}"`);
|
|
254
|
+
result.push({
|
|
255
|
+
key: entry.key,
|
|
256
|
+
label: entry.label
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
async function writeConfig(config) {
|
|
262
|
+
const configDir = (0, node_path.join)((0, node_os.homedir)(), ".config", "preguito");
|
|
263
|
+
const configPath = (0, node_path.join)(configDir, "config.json");
|
|
264
|
+
if (!(0, node_fs.existsSync)(configDir)) await (0, node_fs_promises.mkdir)(configDir, { recursive: true });
|
|
265
|
+
await (0, node_fs_promises.writeFile)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
266
|
+
return configPath;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
exports.generateTemplate = generateTemplate;
|
|
271
|
+
exports.loadConfig = loadConfig;
|
|
272
|
+
exports.loadConfigOrDefault = loadConfigOrDefault;
|
|
273
|
+
exports.mergeContext = mergeContext;
|
|
274
|
+
exports.parseTemplate = parseTemplate;
|
|
275
|
+
exports.renderTemplate = renderTemplate;
|
|
276
|
+
exports.writeConfig = writeConfig;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/template/engine.d.ts
|
|
2
|
+
interface TemplateContext {
|
|
3
|
+
[key: string]: string;
|
|
4
|
+
}
|
|
5
|
+
interface ParsedTemplate {
|
|
6
|
+
variables: string[];
|
|
7
|
+
messagePlaceholder: string | null;
|
|
8
|
+
}
|
|
9
|
+
declare function parseTemplate(template: string): ParsedTemplate;
|
|
10
|
+
declare function renderTemplate(template: string, context: TemplateContext, message?: string): string;
|
|
11
|
+
declare function mergeContext(defaults: TemplateContext, overrides: TemplateContext): TemplateContext;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/config/types.d.ts
|
|
14
|
+
interface ShortcodeEntry {
|
|
15
|
+
key: string;
|
|
16
|
+
label: string;
|
|
17
|
+
}
|
|
18
|
+
interface PrequitoFeatures {
|
|
19
|
+
cardId: boolean;
|
|
20
|
+
type: boolean;
|
|
21
|
+
environment: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface PrequitoConfig {
|
|
24
|
+
template: string;
|
|
25
|
+
features: PrequitoFeatures;
|
|
26
|
+
types: ShortcodeEntry[];
|
|
27
|
+
environments: ShortcodeEntry[];
|
|
28
|
+
defaults: Record<string, string>;
|
|
29
|
+
}
|
|
30
|
+
declare function generateTemplate(features: PrequitoFeatures, hasPrefix: boolean): string;
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/config/loader.d.ts
|
|
33
|
+
declare function loadConfig(): Promise<PrequitoConfig | null>;
|
|
34
|
+
declare function loadConfigOrDefault(): Promise<PrequitoConfig>;
|
|
35
|
+
declare function writeConfig(config: PrequitoConfig): Promise<string>;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { type ParsedTemplate, type PrequitoConfig, type PrequitoFeatures, type ShortcodeEntry, type TemplateContext, generateTemplate, loadConfig, loadConfigOrDefault, mergeContext, parseTemplate, renderTemplate, writeConfig };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/template/engine.d.ts
|
|
2
|
+
interface TemplateContext {
|
|
3
|
+
[key: string]: string;
|
|
4
|
+
}
|
|
5
|
+
interface ParsedTemplate {
|
|
6
|
+
variables: string[];
|
|
7
|
+
messagePlaceholder: string | null;
|
|
8
|
+
}
|
|
9
|
+
declare function parseTemplate(template: string): ParsedTemplate;
|
|
10
|
+
declare function renderTemplate(template: string, context: TemplateContext, message?: string): string;
|
|
11
|
+
declare function mergeContext(defaults: TemplateContext, overrides: TemplateContext): TemplateContext;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/config/types.d.ts
|
|
14
|
+
interface ShortcodeEntry {
|
|
15
|
+
key: string;
|
|
16
|
+
label: string;
|
|
17
|
+
}
|
|
18
|
+
interface PrequitoFeatures {
|
|
19
|
+
cardId: boolean;
|
|
20
|
+
type: boolean;
|
|
21
|
+
environment: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface PrequitoConfig {
|
|
24
|
+
template: string;
|
|
25
|
+
features: PrequitoFeatures;
|
|
26
|
+
types: ShortcodeEntry[];
|
|
27
|
+
environments: ShortcodeEntry[];
|
|
28
|
+
defaults: Record<string, string>;
|
|
29
|
+
}
|
|
30
|
+
declare function generateTemplate(features: PrequitoFeatures, hasPrefix: boolean): string;
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/config/loader.d.ts
|
|
33
|
+
declare function loadConfig(): Promise<PrequitoConfig | null>;
|
|
34
|
+
declare function loadConfigOrDefault(): Promise<PrequitoConfig>;
|
|
35
|
+
declare function writeConfig(config: PrequitoConfig): Promise<string>;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { type ParsedTemplate, type PrequitoConfig, type PrequitoFeatures, type ShortcodeEntry, type TemplateContext, generateTemplate, loadConfig, loadConfigOrDefault, mergeContext, parseTemplate, renderTemplate, writeConfig };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
|
|
6
|
+
//#region src/utils/errors.ts
|
|
7
|
+
var PrequitoError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "PrequitoError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var TemplateMissingVariableError = class extends PrequitoError {
|
|
14
|
+
missingVariables;
|
|
15
|
+
constructor(missing) {
|
|
16
|
+
super(`Missing template variable(s): ${missing.join(", ")}. Provide shortcodes or check your config.`);
|
|
17
|
+
this.name = "TemplateMissingVariableError";
|
|
18
|
+
this.missingVariables = missing;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var TemplateMissingMessageError = class extends PrequitoError {
|
|
22
|
+
constructor(placeholderName) {
|
|
23
|
+
super(`Template requires a message (the <${placeholderName}> placeholder). Provide it as the last argument(s).`);
|
|
24
|
+
this.name = "TemplateMissingMessageError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/template/engine.ts
|
|
30
|
+
const VARIABLE_PATTERN = /\{\{(\w+)\}\}/g;
|
|
31
|
+
const MESSAGE_PLACEHOLDER_PATTERN = /<(\w+)>/;
|
|
32
|
+
function parseTemplate(template) {
|
|
33
|
+
const variables = [];
|
|
34
|
+
let match;
|
|
35
|
+
const pattern = new RegExp(VARIABLE_PATTERN.source, "g");
|
|
36
|
+
while ((match = pattern.exec(template)) !== null) if (!variables.includes(match[1])) variables.push(match[1]);
|
|
37
|
+
const messageMatch = MESSAGE_PLACEHOLDER_PATTERN.exec(template);
|
|
38
|
+
return {
|
|
39
|
+
variables,
|
|
40
|
+
messagePlaceholder: messageMatch ? messageMatch[1] : null
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function renderTemplate(template, context, message) {
|
|
44
|
+
const parsed = parseTemplate(template);
|
|
45
|
+
const missing = parsed.variables.filter((v) => !(v in context));
|
|
46
|
+
if (missing.length > 0) throw new TemplateMissingVariableError(missing);
|
|
47
|
+
let result = template.replace(/\{\{(\w+)\}\}/g, (_, varName) => {
|
|
48
|
+
return context[varName];
|
|
49
|
+
});
|
|
50
|
+
if (parsed.messagePlaceholder) {
|
|
51
|
+
if (!message) throw new TemplateMissingMessageError(parsed.messagePlaceholder);
|
|
52
|
+
result = result.replace(/<\w+>/, message);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function mergeContext(defaults, overrides) {
|
|
57
|
+
return {
|
|
58
|
+
...defaults,
|
|
59
|
+
...overrides
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/config/types.ts
|
|
65
|
+
const PREDEFINED_TYPES = [
|
|
66
|
+
{
|
|
67
|
+
key: "f",
|
|
68
|
+
label: "feat"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "x",
|
|
72
|
+
label: "fix"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: "c",
|
|
76
|
+
label: "chore"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: "t",
|
|
80
|
+
label: "test"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
key: "l",
|
|
84
|
+
label: "lint"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "r",
|
|
88
|
+
label: "refactor"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: "o",
|
|
92
|
+
label: "docs"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
key: "y",
|
|
96
|
+
label: "style"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
key: "e",
|
|
100
|
+
label: "perf"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: "b",
|
|
104
|
+
label: "build"
|
|
105
|
+
}
|
|
106
|
+
];
|
|
107
|
+
const PREDEFINED_ENVIRONMENTS = [
|
|
108
|
+
{
|
|
109
|
+
key: "p",
|
|
110
|
+
label: "prd"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
key: "u",
|
|
114
|
+
label: "uat"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: "h",
|
|
118
|
+
label: "homolog"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
key: "d",
|
|
122
|
+
label: "dev"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: "s",
|
|
126
|
+
label: "staging"
|
|
127
|
+
}
|
|
128
|
+
];
|
|
129
|
+
function generateTemplate(features, hasPrefix) {
|
|
130
|
+
const { cardId, type, environment } = features;
|
|
131
|
+
return [
|
|
132
|
+
cardId ? hasPrefix ? "[{{prefix}}-{{card_id}}]" : "[{{card_id}}]" : "",
|
|
133
|
+
type && environment ? "{{type}}({{environment}}):" : type ? "{{type}}:" : environment ? "({{environment}}):" : "",
|
|
134
|
+
"<message>"
|
|
135
|
+
].filter((p) => p !== "").join(" ");
|
|
136
|
+
}
|
|
137
|
+
const DEFAULT_CONFIG = {
|
|
138
|
+
template: "{{type}}: <message>",
|
|
139
|
+
features: {
|
|
140
|
+
cardId: false,
|
|
141
|
+
type: true,
|
|
142
|
+
environment: false
|
|
143
|
+
},
|
|
144
|
+
types: [
|
|
145
|
+
{
|
|
146
|
+
key: "f",
|
|
147
|
+
label: "feat"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
key: "x",
|
|
151
|
+
label: "fix"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
key: "c",
|
|
155
|
+
label: "chore"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
key: "t",
|
|
159
|
+
label: "test"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
key: "r",
|
|
163
|
+
label: "refactor"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
key: "o",
|
|
167
|
+
label: "docs"
|
|
168
|
+
}
|
|
169
|
+
],
|
|
170
|
+
environments: [],
|
|
171
|
+
defaults: {}
|
|
172
|
+
};
|
|
173
|
+
const CONFIG_PATHS = [
|
|
174
|
+
".preguitorc",
|
|
175
|
+
".preguitorc.json",
|
|
176
|
+
".config/preguito/config.json"
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/config/loader.ts
|
|
181
|
+
async function findConfigPath() {
|
|
182
|
+
const cwd = process.cwd();
|
|
183
|
+
const home = homedir();
|
|
184
|
+
for (const relPath of [".preguitorc", ".preguitorc.json"]) {
|
|
185
|
+
const fullPath = join(cwd, relPath);
|
|
186
|
+
if (existsSync(fullPath)) return fullPath;
|
|
187
|
+
}
|
|
188
|
+
for (const relPath of CONFIG_PATHS) {
|
|
189
|
+
const fullPath = join(home, relPath);
|
|
190
|
+
if (existsSync(fullPath)) return fullPath;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
async function loadConfig() {
|
|
195
|
+
const configPath = await findConfigPath();
|
|
196
|
+
if (!configPath) return null;
|
|
197
|
+
const raw = await readFile(configPath, "utf-8");
|
|
198
|
+
return validateConfig(JSON.parse(raw));
|
|
199
|
+
}
|
|
200
|
+
async function loadConfigOrDefault() {
|
|
201
|
+
return await loadConfig() ?? DEFAULT_CONFIG;
|
|
202
|
+
}
|
|
203
|
+
function validateConfig(obj) {
|
|
204
|
+
if (typeof obj !== "object" || obj === null) throw new Error("Config must be a JSON object");
|
|
205
|
+
const config = obj;
|
|
206
|
+
if (typeof config.template !== "string" || config.template.trim() === "") throw new Error("Config must have a non-empty \"template\" string field");
|
|
207
|
+
const defaults = {};
|
|
208
|
+
if (config.defaults !== void 0) {
|
|
209
|
+
if (typeof config.defaults !== "object" || config.defaults === null) throw new Error("\"defaults\" must be an object");
|
|
210
|
+
for (const [key, value] of Object.entries(config.defaults)) {
|
|
211
|
+
if (typeof value !== "string") throw new Error(`Default value for "${key}" must be a string`);
|
|
212
|
+
defaults[key] = value;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
let features;
|
|
216
|
+
if (config.features && typeof config.features === "object") {
|
|
217
|
+
const f = config.features;
|
|
218
|
+
features = {
|
|
219
|
+
cardId: Boolean(f.cardId),
|
|
220
|
+
type: Boolean(f.type),
|
|
221
|
+
environment: Boolean(f.environment)
|
|
222
|
+
};
|
|
223
|
+
} else features = inferFeaturesFromTemplate(config.template);
|
|
224
|
+
let types = [];
|
|
225
|
+
if (Array.isArray(config.types)) types = validateShortcodeArray(config.types, "types");
|
|
226
|
+
else if (features.type) types = [...PREDEFINED_TYPES];
|
|
227
|
+
let environments = [];
|
|
228
|
+
if (Array.isArray(config.environments)) environments = validateShortcodeArray(config.environments, "environments");
|
|
229
|
+
else if (features.environment) environments = [...PREDEFINED_ENVIRONMENTS];
|
|
230
|
+
return {
|
|
231
|
+
template: config.template,
|
|
232
|
+
features,
|
|
233
|
+
types,
|
|
234
|
+
environments,
|
|
235
|
+
defaults
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function inferFeaturesFromTemplate(template) {
|
|
239
|
+
const parsed = parseTemplate(template);
|
|
240
|
+
return {
|
|
241
|
+
cardId: parsed.variables.includes("card_id"),
|
|
242
|
+
type: parsed.variables.includes("type"),
|
|
243
|
+
environment: parsed.variables.includes("environment")
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function validateShortcodeArray(arr, fieldName) {
|
|
247
|
+
const result = [];
|
|
248
|
+
for (let i = 0; i < arr.length; i++) {
|
|
249
|
+
const item = arr[i];
|
|
250
|
+
if (typeof item !== "object" || item === null || typeof item.key !== "string" || typeof item.label !== "string") throw new Error(`${fieldName}[${i}] must be an object with "key" and "label" string fields`);
|
|
251
|
+
const entry = item;
|
|
252
|
+
if (entry.key.length !== 1) throw new Error(`${fieldName}[${i}].key must be a single character, got "${entry.key}"`);
|
|
253
|
+
result.push({
|
|
254
|
+
key: entry.key,
|
|
255
|
+
label: entry.label
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
async function writeConfig(config) {
|
|
261
|
+
const configDir = join(homedir(), ".config", "preguito");
|
|
262
|
+
const configPath = join(configDir, "config.json");
|
|
263
|
+
if (!existsSync(configDir)) await mkdir(configDir, { recursive: true });
|
|
264
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
265
|
+
return configPath;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
export { generateTemplate, loadConfig, loadConfigOrDefault, mergeContext, parseTemplate, renderTemplate, writeConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "preguito",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A lazy git CLI tool with commit templates and shortcuts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"guito": "./dist/cli.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsdown",
|
|
14
|
+
"dev": "tsdown --watch",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"lint": "tsc --noEmit",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"build:sea": "npm run build && node scripts/build-sea.js",
|
|
20
|
+
"build:deb": "npm run build:sea && bash scripts/build-deb.sh"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"git",
|
|
24
|
+
"cli",
|
|
25
|
+
"commit",
|
|
26
|
+
"template",
|
|
27
|
+
"lazy"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"commander": "^13.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"tsdown": "^0.20.0",
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"vitest": "^3.0.0",
|
|
37
|
+
"@types/node": "^22.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|