@tertium/hlpr 0.1.16 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +97 -2
  2. package/index.js +60 -13
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # hlpr
2
2
 
3
- A CLI utility for running shell scripts with variable substitution.
3
+ A CLI utility for running shell scripts with variable substitution and TypeScript-based commands.
4
4
 
5
5
  ## Installation
6
6
 
@@ -26,20 +26,87 @@ hlpr git fodd
26
26
  hlpr hello world
27
27
  hlpr ssh init dir
28
28
 
29
+ # TypeScript commands (nested structure)
30
+ hlpr file rename <directory> <style> [--dry|-n]
31
+
29
32
  # Continue execution even if commands fail
30
33
  hlpr -f ssh init dir
31
34
  ```
32
35
 
36
+ ## Available Commands
37
+
38
+ ### Shell Script Commands
39
+
40
+ Shell scripts support variable substitution using `{{variable}}` syntax.
41
+
42
+ - `git fodd` - Git command
43
+ - `hello world` - Hello world example
44
+ - `ssh init dir` - SSH initialization
45
+
46
+ ### TypeScript Commands
47
+
48
+ TypeScript commands are implemented as `.ts` files and run directly with Bun.
49
+
50
+ #### file rename
51
+
52
+ Recursively rename files and folders according to a specific case style.
53
+
54
+ **Supported styles:**
55
+
56
+ - `title_underscore` - Title_Case_With_Underscores
57
+ - `snake` - snake_case_lowercase
58
+ - `kebab` - kebab-case-lowercase
59
+ - `camel` - camelCaseLowerFirst
60
+ - `pascal` - PascalCaseUpperFirst
61
+ - `upper` - UPPER_CASE_WITH_UNDERSCORES
62
+ - `lower` - lower_case_with_underscores
63
+
64
+ **Examples:**
65
+
66
+ ```bash
67
+ # Dry run (preview changes without applying)
68
+ hlpr file rename ./my-project kebab --dry
69
+ hlpr file rename ./src snake -n
70
+
71
+ # Apply changes
72
+ hlpr file rename ./my-project kebab
73
+ hlpr file rename ./docs pascal
74
+ ```
75
+
76
+ **Features:**
77
+
78
+ - ✅ Recursive directory traversal
79
+ - ✅ Extension preservation (e.g., `file-name.md` core name only)
80
+ - ✅ Leading dot file support (e.g., `.gitignore`)
81
+ - ✅ Case-only rename handling (Windows compatibility)
82
+ - ✅ Conflict resolution (adds `_N` suffix)
83
+ - ✅ Dry-run mode (`--dry` or `-n`)
84
+
85
+ See `commands/file/rename/README.md` for detailed documentation.
86
+
33
87
  ## How It Works
34
88
 
89
+ ### Shell Scripts
90
+
35
91
  1. The command `hlpr hello world` looks for a script at `commands/hello/world.sh`
36
92
  2. If the script contains variables like `{{name}}`, you'll be prompted to enter values
37
93
  3. Each line of the script is executed with the variables replaced
38
94
 
95
+ ### TypeScript Commands
96
+
97
+ 1. The command `hlpr file rename` looks for a script at `commands/file/rename/rename.ts`
98
+ 2. Arguments are passed directly to the TypeScript module
99
+ 3. The module is executed with Bun runtime
100
+
39
101
  ## Directory Structure
40
102
 
41
103
  ```
42
104
  commands/
105
+ ├── file/
106
+ │ └── rename/
107
+ │ ├── rename.ts
108
+ │ ├── rename.test.ts
109
+ │ └── README.md
43
110
  ├── git/
44
111
  │ └── fodd.sh
45
112
  ├── hello/
@@ -53,22 +120,50 @@ commands/
53
120
 
54
121
  ## Adding Your Own Scripts
55
122
 
123
+ ### Shell Scripts
124
+
56
125
  1. Create a directory structure in `commands/<category>/`
57
126
  2. Add your `.sh` script files
58
127
  3. Use `{{variable}}` syntax for user inputs
59
128
 
60
129
  Example script (`commands/hello/world.sh`):
130
+
61
131
  ```bash
62
132
  echo "Hello World, {{name}}"
63
133
  ```
64
134
 
135
+ ### TypeScript Commands
136
+
137
+ 1. Create a nested directory structure in `commands/<category>/<command>/`
138
+ 2. Add your TypeScript file named `<command>.ts`
139
+ 3. Implement CLI argument parsing and logic
140
+ 4. Optionally add tests in `<command>.test.ts`
141
+
142
+ Example structure:
143
+
144
+ ```text
145
+ commands/
146
+ └── file/
147
+ └── rename/
148
+ ├── rename.ts # Main implementation
149
+ ├── rename.test.ts # Tests
150
+ └── README.md # Documentation
151
+ ```
152
+
65
153
  ## Command Naming
66
154
 
67
155
  The utility maps command arguments to script files:
156
+
157
+ **Shell Scripts:**
158
+
68
159
  - `hlpr git fodd` → runs `commands/git/fodd.sh`
69
160
  - `hlpr hello world` → runs `commands/hello/world.sh`
70
161
  - `hlpr ssh init dir` → runs `commands/ssh/initdir.sh`
71
162
 
163
+ **TypeScript Commands:**
164
+
165
+ - `hlpr file rename <args>` → runs `commands/file/rename/rename.ts`
166
+
72
167
  ## Error Handling
73
168
 
74
169
  By default, the utility stops execution if any command fails. Use the `-f` flag to continue execution despite failures:
@@ -99,4 +194,4 @@ npm link
99
194
 
100
195
  ## License
101
196
 
102
- MIT
197
+ MIT
package/index.js CHANGED
@@ -39,9 +39,9 @@ var rl = readline.createInterface({
39
39
  output: process.stdout
40
40
  });
41
41
  function prompt(question) {
42
- return new Promise((resolve2) => {
42
+ return new Promise((resolve) => {
43
43
  rl.question(question, (answer) => {
44
- resolve2(answer);
44
+ resolve(answer);
45
45
  });
46
46
  });
47
47
  }
@@ -50,7 +50,7 @@ async function executeCommand(command, variables) {
50
50
  for (const [key, value] of Object.entries(variables)) {
51
51
  processedCommand = processedCommand.replace(new RegExp(`{{${key}}}`, "g"), value);
52
52
  }
53
- return new Promise((resolve2) => {
53
+ return new Promise((resolve) => {
54
54
  console.log(`Executing: ${processedCommand}`);
55
55
  const childProcess = exec(processedCommand);
56
56
  if (childProcess.stdout) {
@@ -65,10 +65,10 @@ async function executeCommand(command, variables) {
65
65
  }
66
66
  childProcess.on("exit", (code) => {
67
67
  if (code === 0) {
68
- resolve2(true);
68
+ resolve(true);
69
69
  } else {
70
70
  console.error(`Command failed with exit code ${code}`);
71
- resolve2(false);
71
+ resolve(false);
72
72
  }
73
73
  });
74
74
  });
@@ -79,22 +79,69 @@ async function main() {
79
79
  console.log(`hlpr version ${version}`);
80
80
  process.exit(0);
81
81
  }
82
- if (commandArgs.length === 0) {
83
- console.error("Please provide a command. Example: hlpr ssh init-dir");
84
- process.exit(1);
82
+ if (commandArgs.length === 0 || commandArgs[0] === "help" || commandArgs[0] === "--help" || commandArgs[0] === "-h" || commandArgs[0] === "/help" || commandArgs[0] === "/h" || commandArgs[0] === "/?") {
83
+ const __filename2 = fileURLToPath(import.meta.url);
84
+ const __dirname2 = path.dirname(__filename2);
85
+ const helpScriptPath = path.join(__dirname2, "commands", "help", "help.ts");
86
+ if (fs.existsSync(helpScriptPath)) {
87
+ const helpCommand = `bun "${helpScriptPath}"`;
88
+ await executeCommand(helpCommand, {});
89
+ rl.close();
90
+ process.exit(0);
91
+ } else {
92
+ console.error("Please provide a command. Example: hlpr ssh init-dir");
93
+ console.error("Run 'hlpr help' for available commands.");
94
+ process.exit(1);
95
+ }
85
96
  }
86
97
  try {
87
98
  const category = commandArgs[0];
88
99
  const restArgs = commandArgs.slice(1);
89
- const scriptName = restArgs.join("");
90
100
  const __filename2 = fileURLToPath(import.meta.url);
91
101
  const __dirname2 = path.dirname(__filename2);
92
- const scriptDir = path.resolve(__dirname2, "..");
93
- const scriptPath = path.join(scriptDir, "commands", category, `${scriptName}.sh`);
94
- if (!fs.existsSync(scriptPath)) {
95
- console.error(`Script not found: ${scriptPath}`);
102
+ const scriptDir = __dirname2;
103
+ let scriptPath;
104
+ let isTypeScriptCommand = false;
105
+ if (restArgs.length > 0) {
106
+ const subcategory = restArgs[0];
107
+ const nestedTsPath = path.join(scriptDir, "commands", category, subcategory, `${subcategory}.ts`);
108
+ if (fs.existsSync(nestedTsPath)) {
109
+ scriptPath = nestedTsPath;
110
+ isTypeScriptCommand = true;
111
+ }
112
+ }
113
+ if (!isTypeScriptCommand) {
114
+ const tsPath = path.join(scriptDir, "commands", category, `${category}.ts`);
115
+ if (fs.existsSync(tsPath)) {
116
+ scriptPath = tsPath;
117
+ isTypeScriptCommand = true;
118
+ }
119
+ }
120
+ if (!isTypeScriptCommand && restArgs.length > 0) {
121
+ const scriptName = restArgs.join("");
122
+ scriptPath = path.join(scriptDir, "commands", category, `${scriptName}.sh`);
123
+ if (!fs.existsSync(scriptPath)) {
124
+ console.error(`Script not found: ${scriptPath}`);
125
+ process.exit(1);
126
+ }
127
+ }
128
+ if (!scriptPath) {
129
+ console.error(`Command not found. Usage: hlpr ${category} <args>`);
96
130
  process.exit(1);
97
131
  }
132
+ if (isTypeScriptCommand) {
133
+ const tsArgs = restArgs.length > 0 && restArgs[0] && fs.existsSync(path.join(scriptDir, "commands", category, restArgs[0])) ? process.argv.slice(4) : process.argv.slice(3);
134
+ const finalCommand = `bun "${scriptPath}" ${tsArgs.join(" ")}`;
135
+ console.log(`Executing TypeScript command: ${finalCommand}`);
136
+ const success2 = await executeCommand(finalCommand, {});
137
+ if (!success2 && !forceFlag) {
138
+ console.error("Command failed, stopping execution.");
139
+ process.exit(1);
140
+ }
141
+ console.log("Command completed successfully!");
142
+ rl.close();
143
+ return;
144
+ }
98
145
  const scriptContent = await readFile(scriptPath, "utf-8");
99
146
  const variableRegex = /{{([^}]+)}}/g;
100
147
  const variables = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tertium/hlpr",
3
- "version": "0.1.16",
3
+ "version": "0.3.0",
4
4
  "description": "Windows and *Nix utility for typical programming activity",
5
5
  "author": "Vitalii Balabanov",
6
6
  "email": "tertiumnon@gmail.com",
@@ -9,13 +9,13 @@
9
9
  "scripts": {
10
10
  "build": "bun build ./src/index.ts --outfile ./index.js --target node",
11
11
  "prepublishOnly": "npm run build",
12
- "release:minor": "node node_modules/@tertium/shared-js-configs-and-types/scripts/release.js minor",
13
- "release:patch": "node node_modules/@tertium/shared-js-configs-and-types/scripts/release.js patch",
14
- "release:major": "node node_modules/@tertium/shared-js-configs-and-types/scripts/release.js major",
12
+ "release:minor": "node node_modules/@tertium/js/scripts/release.js minor",
13
+ "release:patch": "node node_modules/@tertium/js/scripts/release.js patch",
14
+ "release:major": "node node_modules/@tertium/js/scripts/release.js major",
15
15
  "prepare": "husky"
16
16
  },
17
17
  "devDependencies": {
18
- "@tertium/shared-js-configs-and-types": "^0.14.0",
18
+ "@tertium/js": "^0.16.0",
19
19
  "@types/node": "^22.13.10",
20
20
  "husky": "^9.1.7",
21
21
  "typescript": "^5.8.3"