@tertium/hlpr 0.3.3 → 0.3.5
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/bin/commands/file/rename/rename.js +23 -12
- package/bin/index.js +155 -19
- package/package.json +2 -1
|
@@ -8,30 +8,36 @@ function splitWords(s) {
|
|
|
8
8
|
function transformBasename(basename, style) {
|
|
9
9
|
if (!basename)
|
|
10
10
|
return basename;
|
|
11
|
-
if (basename.includes(".") && !basename.startsWith("."))
|
|
12
|
-
return basename;
|
|
13
11
|
const leadingDot = basename.startsWith(".") ? "." : "";
|
|
14
|
-
|
|
12
|
+
let core = leadingDot ? basename.slice(1) : basename;
|
|
13
|
+
let ext = "";
|
|
14
|
+
const firstDot = core.indexOf(".");
|
|
15
|
+
if (firstDot !== -1) {
|
|
16
|
+
ext = core.slice(firstDot);
|
|
17
|
+
core = core.slice(0, firstDot);
|
|
18
|
+
}
|
|
15
19
|
const words = splitWords(core);
|
|
16
20
|
if (words.length === 0)
|
|
17
21
|
return basename;
|
|
18
22
|
switch (style) {
|
|
19
23
|
case "title_underscore":
|
|
20
|
-
return leadingDot + words.map(cap).join("_");
|
|
24
|
+
return leadingDot + words.map(cap).join("_") + ext;
|
|
21
25
|
case "snake":
|
|
22
|
-
return leadingDot + words.map((w) => w.toLowerCase()).join("_");
|
|
26
|
+
return leadingDot + words.map((w) => w.toLowerCase()).join("_") + ext;
|
|
23
27
|
case "kebab":
|
|
24
|
-
return leadingDot + words.map((w) => w.toLowerCase()).join("-");
|
|
28
|
+
return leadingDot + words.map((w) => w.toLowerCase()).join("-") + ext;
|
|
25
29
|
case "camel":
|
|
26
|
-
return leadingDot + words.map((w, i) => i === 0 ? w.toLowerCase() : cap(w)).join("");
|
|
30
|
+
return leadingDot + words.map((w, i) => i === 0 ? w.toLowerCase() : cap(w)).join("") + ext;
|
|
27
31
|
case "pascal":
|
|
28
|
-
return leadingDot + words.map(cap).join("");
|
|
32
|
+
return leadingDot + words.map(cap).join("") + ext;
|
|
33
|
+
case "pascal_underscore":
|
|
34
|
+
return leadingDot + words.map(cap).join("_") + ext;
|
|
29
35
|
case "upper":
|
|
30
|
-
return leadingDot + words.join("_").toUpperCase();
|
|
36
|
+
return leadingDot + words.join("_").toUpperCase() + ext;
|
|
31
37
|
case "lower":
|
|
32
|
-
return leadingDot + words.join("_").toLowerCase();
|
|
38
|
+
return leadingDot + words.join("_").toLowerCase() + ext;
|
|
33
39
|
default:
|
|
34
|
-
return
|
|
40
|
+
return leadingDot + core + ext;
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
function cap(s) {
|
|
@@ -126,10 +132,15 @@ if (import.meta.url.endsWith(process.argv[1]?.replace(/\\/g, "/"))) {
|
|
|
126
132
|
const args = process.argv.slice(2);
|
|
127
133
|
const rootArg = args[0];
|
|
128
134
|
const styleArg = args[1];
|
|
135
|
+
if (args.includes("--help") || args.includes("-h") || args.includes("/help") || args.includes("/h") || args.includes("/?")) {
|
|
136
|
+
console.log("Usage: rename <root> <style> [--dry|-n]");
|
|
137
|
+
console.log("Styles: title_underscore, pascal_underscore, snake, kebab, camel, pascal, upper, lower");
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
129
140
|
const dryRun = args.includes("--dry") || args.includes("-n");
|
|
130
141
|
if (!rootArg || !styleArg) {
|
|
131
142
|
console.error("Usage: rename <root> <style> [--dry|-n]");
|
|
132
|
-
console.error("Styles: title_underscore, snake, kebab, camel, pascal, upper, lower");
|
|
143
|
+
console.error("Styles: title_underscore, pascal_underscore, snake, kebab, camel, pascal, upper, lower");
|
|
133
144
|
process.exit(1);
|
|
134
145
|
}
|
|
135
146
|
renameRecursive(rootArg, styleArg, { dryRun }).then((performed) => {
|
package/bin/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
// src/index.ts
|
|
2
4
|
import { readFile } from "node:fs/promises";
|
|
3
5
|
import { exec } from "node:child_process";
|
|
@@ -76,6 +78,142 @@ async function executeCommand(command, variables) {
|
|
|
76
78
|
});
|
|
77
79
|
});
|
|
78
80
|
}
|
|
81
|
+
async function showHelp() {
|
|
82
|
+
async function getCommandDescription(filePath) {
|
|
83
|
+
try {
|
|
84
|
+
const content = await readFile(filePath, "utf-8");
|
|
85
|
+
const descMatch = content.match(/(?:\/\/|#)\s*@description\s+(.+)/);
|
|
86
|
+
if (descMatch) {
|
|
87
|
+
return descMatch[1].trim();
|
|
88
|
+
}
|
|
89
|
+
const dir = path.dirname(filePath);
|
|
90
|
+
const readmePath = path.join(dir, "README.md");
|
|
91
|
+
if (fs.existsSync(readmePath)) {
|
|
92
|
+
const readme = await readFile(readmePath, "utf-8");
|
|
93
|
+
const firstLine = readme.split(`
|
|
94
|
+
`).find((line) => line.trim() && !line.startsWith("#"));
|
|
95
|
+
if (firstLine) {
|
|
96
|
+
return firstLine.trim();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
async function discoverCommands(commandsDir) {
|
|
103
|
+
const commands2 = [];
|
|
104
|
+
try {
|
|
105
|
+
const categories = await fs.promises.readdir(commandsDir, { withFileTypes: true });
|
|
106
|
+
for (const category of categories) {
|
|
107
|
+
if (!category.isDirectory())
|
|
108
|
+
continue;
|
|
109
|
+
const categoryPath = path.join(commandsDir, category.name);
|
|
110
|
+
const items = await fs.promises.readdir(categoryPath, { withFileTypes: true });
|
|
111
|
+
for (const item of items) {
|
|
112
|
+
if (item.isDirectory()) {
|
|
113
|
+
const nestedJsPath = path.join(categoryPath, item.name, `${item.name}.js`);
|
|
114
|
+
const nestedShPath = path.join(categoryPath, item.name, `${item.name}.sh`);
|
|
115
|
+
let commandPath;
|
|
116
|
+
let commandType;
|
|
117
|
+
if (fs.existsSync(nestedJsPath)) {
|
|
118
|
+
commandPath = nestedJsPath;
|
|
119
|
+
commandType = "typescript";
|
|
120
|
+
} else if (fs.existsSync(nestedShPath)) {
|
|
121
|
+
commandPath = nestedShPath;
|
|
122
|
+
commandType = "shell";
|
|
123
|
+
} else {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const description = await getCommandDescription(commandPath);
|
|
127
|
+
commands2.push({
|
|
128
|
+
category: category.name,
|
|
129
|
+
name: item.name,
|
|
130
|
+
type: commandType,
|
|
131
|
+
path: commandPath,
|
|
132
|
+
description
|
|
133
|
+
});
|
|
134
|
+
} else if (item.name.endsWith(".js")) {
|
|
135
|
+
const jsPath = path.join(categoryPath, item.name);
|
|
136
|
+
const commandName = path.basename(item.name, ".js");
|
|
137
|
+
if (commandName !== "test" && !commandName.endsWith(".test")) {
|
|
138
|
+
const description = await getCommandDescription(jsPath);
|
|
139
|
+
commands2.push({
|
|
140
|
+
category: category.name,
|
|
141
|
+
name: commandName,
|
|
142
|
+
type: "typescript",
|
|
143
|
+
path: jsPath,
|
|
144
|
+
description
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
} else if (item.name.endsWith(".sh")) {
|
|
148
|
+
const shPath = path.join(categoryPath, item.name);
|
|
149
|
+
const commandName = path.basename(item.name, ".sh");
|
|
150
|
+
const description = await getCommandDescription(shPath);
|
|
151
|
+
commands2.push({
|
|
152
|
+
category: category.name,
|
|
153
|
+
name: commandName,
|
|
154
|
+
type: "shell",
|
|
155
|
+
path: shPath,
|
|
156
|
+
description
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {}
|
|
162
|
+
return commands2;
|
|
163
|
+
}
|
|
164
|
+
const version = await getVersion();
|
|
165
|
+
const binCommandsDir = path.join(__dirname2, "commands");
|
|
166
|
+
const srcCommandsDir = path.join(__dirname2, "..", "src", "commands");
|
|
167
|
+
const commands = [
|
|
168
|
+
...await discoverCommands(binCommandsDir),
|
|
169
|
+
...await discoverCommands(srcCommandsDir)
|
|
170
|
+
];
|
|
171
|
+
const uniqueCommands = commands.filter((cmd, index, self) => index === self.findIndex((c) => c.category === cmd.category && c.name === cmd.name));
|
|
172
|
+
console.log(`
|
|
173
|
+
╔════════════════════════════════════════════════════════════╗`);
|
|
174
|
+
console.log(`║ hlpr - Helper CLI Tool v${version.padEnd(16)}║`);
|
|
175
|
+
console.log(`╚════════════════════════════════════════════════════════════╝
|
|
176
|
+
`);
|
|
177
|
+
console.log("USAGE:");
|
|
178
|
+
console.log(` hlpr [options] <category> <command> [args...]
|
|
179
|
+
`);
|
|
180
|
+
console.log("OPTIONS:");
|
|
181
|
+
console.log(" -f Force execution (continue on errors)");
|
|
182
|
+
console.log(" -v, --version Show version information");
|
|
183
|
+
console.log(" help, -h, --help, /h, /help, /?");
|
|
184
|
+
console.log(` Show this help message
|
|
185
|
+
`);
|
|
186
|
+
console.log(`AVAILABLE COMMANDS:
|
|
187
|
+
`);
|
|
188
|
+
const grouped = uniqueCommands.reduce((acc, cmd) => {
|
|
189
|
+
if (!acc[cmd.category]) {
|
|
190
|
+
acc[cmd.category] = [];
|
|
191
|
+
}
|
|
192
|
+
acc[cmd.category].push(cmd);
|
|
193
|
+
return acc;
|
|
194
|
+
}, {});
|
|
195
|
+
const sortedCategories = Object.keys(grouped).sort();
|
|
196
|
+
for (const category of sortedCategories) {
|
|
197
|
+
console.log(` ${category}:`);
|
|
198
|
+
const categoryCommands = grouped[category].sort((a, b) => a.name.localeCompare(b.name));
|
|
199
|
+
for (const cmd of categoryCommands) {
|
|
200
|
+
const typeLabel = cmd.type === "typescript" ? "(TS)" : "(sh)";
|
|
201
|
+
const cmdDisplay = cmd.category === cmd.name ? `hlpr ${cmd.name}`.padEnd(35) : `hlpr ${cmd.category} ${cmd.name}`.padEnd(35);
|
|
202
|
+
if (cmd.description) {
|
|
203
|
+
console.log(` ${cmdDisplay} ${typeLabel.padEnd(6)} ${cmd.description}`);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(` ${cmdDisplay} ${typeLabel}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
console.log("");
|
|
209
|
+
}
|
|
210
|
+
console.log("EXAMPLES:");
|
|
211
|
+
console.log(" hlpr help");
|
|
212
|
+
console.log(" hlpr file rename --style kebab --dir ./src");
|
|
213
|
+
console.log(" hlpr ssh init-dir");
|
|
214
|
+
console.log(` hlpr -f git precommit
|
|
215
|
+
`);
|
|
216
|
+
}
|
|
79
217
|
async function main() {
|
|
80
218
|
if (commandArgs[0] === "--version" || commandArgs[0] === "-v") {
|
|
81
219
|
const version = await getVersion();
|
|
@@ -83,17 +221,8 @@ async function main() {
|
|
|
83
221
|
process.exit(0);
|
|
84
222
|
}
|
|
85
223
|
if (commandArgs.length === 0 || commandArgs[0] === "help" || commandArgs[0] === "--help" || commandArgs[0] === "-h" || commandArgs[0] === "/help" || commandArgs[0] === "/h" || commandArgs[0] === "/?") {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const helpCommand = `node "${helpScriptPath}"`;
|
|
89
|
-
await executeCommand(helpCommand, {});
|
|
90
|
-
rl.close();
|
|
91
|
-
process.exit(0);
|
|
92
|
-
} else {
|
|
93
|
-
console.error("Please provide a command. Example: hlpr ssh init-dir");
|
|
94
|
-
console.error("Run 'hlpr help' for available commands.");
|
|
95
|
-
process.exit(1);
|
|
96
|
-
}
|
|
224
|
+
await showHelp();
|
|
225
|
+
process.exit(0);
|
|
97
226
|
}
|
|
98
227
|
try {
|
|
99
228
|
const category = commandArgs[0];
|
|
@@ -133,11 +262,20 @@ async function main() {
|
|
|
133
262
|
process.exit(1);
|
|
134
263
|
}
|
|
135
264
|
if (isTypeScriptCommand) {
|
|
136
|
-
|
|
265
|
+
let tsArgs;
|
|
266
|
+
if (restArgs.length > 0 && restArgs[0] && scriptPath) {
|
|
267
|
+
const subcategory = restArgs[0];
|
|
268
|
+
const isNested = path.basename(scriptPath) === `${subcategory}.js` && path.basename(path.dirname(scriptPath)) === subcategory;
|
|
269
|
+
tsArgs = isNested ? process.argv.slice(4) : process.argv.slice(3);
|
|
270
|
+
} else {
|
|
271
|
+
tsArgs = process.argv.slice(3);
|
|
272
|
+
}
|
|
137
273
|
const finalCommand = `node "${scriptPath}" ${tsArgs.join(" ")}`;
|
|
138
274
|
console.log(`Executing command: ${finalCommand}`);
|
|
139
275
|
const success2 = await executeCommand(finalCommand, {});
|
|
140
|
-
|
|
276
|
+
const helpFlags = ["-h", "--help", "help", "/h", "/help", "/?"];
|
|
277
|
+
const isHelpInvocation = tsArgs.some((arg) => helpFlags.includes(arg));
|
|
278
|
+
if (!success2 && !forceFlag && !isHelpInvocation) {
|
|
141
279
|
console.error("Command failed, stopping execution.");
|
|
142
280
|
process.exit(1);
|
|
143
281
|
}
|
|
@@ -179,9 +317,7 @@ async function main() {
|
|
|
179
317
|
rl.close();
|
|
180
318
|
}
|
|
181
319
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
main
|
|
187
|
-
};
|
|
320
|
+
main().catch((error) => {
|
|
321
|
+
console.error("Fatal error:", error);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tertium/hlpr",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Windows and *Nix utility for typical programming activity",
|
|
5
5
|
"author": "Vitalii Balabanov",
|
|
6
6
|
"email": "tertiumnon@gmail.com",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"build:commands": "node build-commands.js",
|
|
12
12
|
"build": "bun run build:main && bun run build:commands",
|
|
13
13
|
"test": "bun test",
|
|
14
|
+
"test:e2e": "node scripts/test-rename-e2e.cjs",
|
|
14
15
|
"prepublishOnly": "npm run build",
|
|
15
16
|
"release:minor": "node node_modules/@tertium/js/scripts/release.js minor",
|
|
16
17
|
"release:patch": "node node_modules/@tertium/js/scripts/release.js patch",
|