ldx 1.0.0 → 1.0.2
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 +228 -2
- package/ldx.js +152 -0
- package/package.json +45 -8
package/README.md
CHANGED
|
@@ -1,3 +1,229 @@
|
|
|
1
|
-
#
|
|
2
|
-
transform command outputs to custom logs
|
|
1
|
+
# LDX - Logging Developer Experience
|
|
3
2
|
|
|
3
|
+
A lightweight tool to enhance your development workflow by processing command output with customizable transformations.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Custom static transformations**: Define string patterns in your command output and replace them with custom messages.
|
|
8
|
+
|
|
9
|
+
- **Custom dynamic transformations**: Supports function-based transformations
|
|
10
|
+
|
|
11
|
+
- **Easy Integration**: Works seamlessly with any command-line tool or script.
|
|
12
|
+
|
|
13
|
+
- **Developer-Friendly**: Improves readability and reduces noise in logs.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
To install LDX globally, run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm install -g ldx
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Alternatively, you can install it locally in your project:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm install --save-dev ldx
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### 1. Create a Configuration File
|
|
32
|
+
|
|
33
|
+
Create a `ldx.config.js` file in your project's root directory. This file defines the patterns and transformations for your command output.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
module.exports = {
|
|
39
|
+
// Simple string replacement
|
|
40
|
+
"Test match 1": "✅ Test match 1 processed",
|
|
41
|
+
|
|
42
|
+
// Function-based processing
|
|
43
|
+
"Function match": (line) => `Processed: ${line}`,
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Run Commands with LDX
|
|
48
|
+
|
|
49
|
+
Use LDX to run your commands and transform their output:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ldx <your-command>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For example:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
ldx pnpm run dev
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Configuration Options
|
|
62
|
+
|
|
63
|
+
### String Matching
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
module.exports = {
|
|
67
|
+
"String contained in output line": "Replacement text for the entire line"
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Function Processing
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
module.exports = {
|
|
75
|
+
"String contained in output line": (line) => {
|
|
76
|
+
// Custom processing logic
|
|
77
|
+
return `Formatted: ${line}`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Requirements
|
|
83
|
+
|
|
84
|
+
- Node.js >= 18.0.0
|
|
85
|
+
|
|
86
|
+
## Error Handling
|
|
87
|
+
|
|
88
|
+
LDX provides clear error messages for common issues:
|
|
89
|
+
|
|
90
|
+
### Missing Configuration File
|
|
91
|
+
|
|
92
|
+
If `ldx.config.js` is not found in your project root:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
Oops, no ldx.config.js file found!
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Solution**: Create a `ldx.config.js` file in your project root. You can copy `ldx.config.example.js` as a starting point.
|
|
99
|
+
|
|
100
|
+
### Invalid Configuration
|
|
101
|
+
|
|
102
|
+
If your configuration has invalid values:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
LDX: Configuration error - Invalid value type for key "myKey". Expected string or function, got number.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Solution**: Ensure all configuration values are either strings or functions.
|
|
109
|
+
|
|
110
|
+
### Empty Configuration
|
|
111
|
+
|
|
112
|
+
If your configuration object is empty:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
LDX: Configuration error - Configuration cannot be empty.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Solution**: Add at least one pattern-transformation pair to your configuration.
|
|
119
|
+
|
|
120
|
+
### Command Not Found
|
|
121
|
+
|
|
122
|
+
If the command you're trying to run doesn't exist:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
Error executing command: Failed to start command: spawn xyz ENOENT
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Solution**: Verify the command exists and is in your PATH.
|
|
129
|
+
|
|
130
|
+
### Function Processing Errors
|
|
131
|
+
|
|
132
|
+
If a transformer function throws an error, LDX will:
|
|
133
|
+
|
|
134
|
+
1. Log a warning: `LDX: provided function errored: <error message>`
|
|
135
|
+
2. Output the original unprocessed line (to prevent data loss)
|
|
136
|
+
|
|
137
|
+
**Solution**: Add try-catch blocks in your transformer functions for graceful error handling.
|
|
138
|
+
|
|
139
|
+
## Testing
|
|
140
|
+
|
|
141
|
+
Run tests with:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pnpm test
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Usage examples
|
|
148
|
+
|
|
149
|
+
### Basic Usage
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
ldx echo "Testing LDX tool"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### With package manager scripts
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
ldx pnpm run dev
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### With complex commands
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
ldx docker-compose up
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Development
|
|
168
|
+
|
|
169
|
+
1. Clone the repository
|
|
170
|
+
|
|
171
|
+
2. Install dependencies:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
pnpm i
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
3. Make changes
|
|
178
|
+
|
|
179
|
+
4. Run tests:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
pnpm test
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Releasing
|
|
186
|
+
|
|
187
|
+
This project uses [standard-version](https://github.com/conventional-changelog/standard-version) for automated versioning and changelog generation based on [Conventional Commits](https://www.conventionalcommits.org/).
|
|
188
|
+
|
|
189
|
+
### Commit Message Format
|
|
190
|
+
|
|
191
|
+
```text
|
|
192
|
+
<type>(<scope>): <description>
|
|
193
|
+
|
|
194
|
+
[optional body]
|
|
195
|
+
|
|
196
|
+
[optional footer(s)]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `build`
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
git commit -m "feat: add support for regex patterns"
|
|
205
|
+
git commit -m "fix: handle empty lines in output"
|
|
206
|
+
git commit -m "docs: update configuration examples"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Creating a Release
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Automatic version bump based on commits (recommended)
|
|
213
|
+
pnpm release
|
|
214
|
+
|
|
215
|
+
# Specific version bumps
|
|
216
|
+
pnpm release:patch # 1.0.2 -> 1.0.3
|
|
217
|
+
pnpm release:minor # 1.0.2 -> 1.1.0
|
|
218
|
+
pnpm release:major # 1.0.2 -> 2.0.0
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
After running the release command:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
git push --follow-tags origin main && npm publish
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Contributing
|
|
228
|
+
|
|
229
|
+
Contributions are welcome! Please open issues or pull requests in this repo.
|
package/ldx.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { resolve } = require("node:path");
|
|
4
|
+
const { spawn } = require("child_process");
|
|
5
|
+
|
|
6
|
+
// Load configuration
|
|
7
|
+
const project = resolve(process.cwd(), "ldx.config.js");
|
|
8
|
+
let config;
|
|
9
|
+
try {
|
|
10
|
+
config = require(project);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.error("Oops, no ldx.config.js file found!");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Validate configuration
|
|
17
|
+
function validateConfig(cfg) {
|
|
18
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) {
|
|
19
|
+
return { valid: false, error: "Configuration must be a non-null object." };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const entries = Object.entries(cfg);
|
|
23
|
+
if (entries.length === 0) {
|
|
24
|
+
return { valid: false, error: "Configuration cannot be empty." };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const [key, value] of entries) {
|
|
28
|
+
if (typeof value !== "string" && typeof value !== "function") {
|
|
29
|
+
return {
|
|
30
|
+
valid: false,
|
|
31
|
+
error: `Invalid value type for key "${key}". Expected string or function, got ${typeof value}.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { valid: true };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const validation = validateConfig(config);
|
|
40
|
+
if (!validation.valid) {
|
|
41
|
+
console.error(`LDX: Configuration error - ${validation.error}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(
|
|
46
|
+
"Thank you for using LDX! Collaborate or report issues at https://github.com/leog/ldx \n"
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Function to execute the command and process output
|
|
50
|
+
function executeAndProcessCommand() {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const command = process.argv.slice(2);
|
|
53
|
+
if (command.length === 0) {
|
|
54
|
+
reject(new Error("No command provided."));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const child = spawn(command[0], command.slice(1));
|
|
59
|
+
|
|
60
|
+
// Handle spawn errors (e.g., command not found)
|
|
61
|
+
child.on("error", (error) => {
|
|
62
|
+
reject(new Error(`Failed to start command: ${error.message}`));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Handle stdout
|
|
66
|
+
if (child.stdout) {
|
|
67
|
+
child.stdout.on("data", (data) => {
|
|
68
|
+
const lines = data.toString().split("\n");
|
|
69
|
+
lines.forEach((line) => {
|
|
70
|
+
const processedLine = processOutput(line);
|
|
71
|
+
if (processedLine) {
|
|
72
|
+
console.log(processedLine); // Output the processed line
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle stderr
|
|
79
|
+
if (child.stderr) {
|
|
80
|
+
child.stderr.on("data", (data) => {
|
|
81
|
+
const lines = data.toString().split("\n");
|
|
82
|
+
lines.forEach((line) => {
|
|
83
|
+
const processedLine = processOutput(line);
|
|
84
|
+
if (processedLine) {
|
|
85
|
+
console.error(processedLine); // Output processed error line to stderr
|
|
86
|
+
} else if (line.trim()) {
|
|
87
|
+
console.error(line); // Pass through unmatched non-empty lines
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Handle process exit
|
|
94
|
+
child.on("close", (code) => {
|
|
95
|
+
if (code !== 0) {
|
|
96
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
97
|
+
} else {
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Function to process each line of output
|
|
105
|
+
function processOutput(line) {
|
|
106
|
+
// Check static matches first (O(n) lookup, but optimized for small n)
|
|
107
|
+
const match = Object.entries(config).find(([key]) => line.includes(key));
|
|
108
|
+
|
|
109
|
+
if (match) {
|
|
110
|
+
const [key, value] = match;
|
|
111
|
+
|
|
112
|
+
// Handle string values
|
|
113
|
+
if (typeof value === "string") {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle function values
|
|
118
|
+
if (typeof value === "function") {
|
|
119
|
+
try {
|
|
120
|
+
return value(line);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
console.warn("LDX: provided function errored: ", e.message);
|
|
123
|
+
return line; // Return original line on error to prevent data loss
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Handle invalid configurations
|
|
128
|
+
console.warn(
|
|
129
|
+
`Invalid configuration for key: ${key}. Expected string or function.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// No match found
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Export functions for testing
|
|
138
|
+
module.exports = {
|
|
139
|
+
processOutput,
|
|
140
|
+
executeAndProcessCommand,
|
|
141
|
+
validateConfig,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Execute the command if this script is run directly
|
|
145
|
+
/* v8 ignore start */
|
|
146
|
+
if (require.main === module) {
|
|
147
|
+
// Main execution
|
|
148
|
+
executeAndProcessCommand()
|
|
149
|
+
.then(() => console.log("Command executed successfully."))
|
|
150
|
+
.catch((error) => console.error("Error executing command:", error));
|
|
151
|
+
}
|
|
152
|
+
/* v8 ignore end */
|
package/package.json
CHANGED
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldx",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"main": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"main": "ldx.js",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=18.0.0"
|
|
7
|
+
},
|
|
8
|
+
"bin": {
|
|
9
|
+
"ldx": "ldx.js"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/leog/ldx.git"
|
|
7
14
|
},
|
|
8
|
-
"keywords": [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
"keywords": [
|
|
16
|
+
"transform",
|
|
17
|
+
"output",
|
|
18
|
+
"logging",
|
|
19
|
+
"developer",
|
|
20
|
+
"experience",
|
|
21
|
+
"dx"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"package.json",
|
|
25
|
+
"ldx.js",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "Leo Giovanetti",
|
|
30
|
+
"email": "hello@leog.me",
|
|
31
|
+
"url": "https://x.com/leog"
|
|
32
|
+
},
|
|
33
|
+
"license": "Apache-2.0",
|
|
34
|
+
"description": "Enhancing logging developer experience",
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@vitest/coverage-v8": "3.0.9",
|
|
37
|
+
"mock-require": "^3.0.3",
|
|
38
|
+
"standard-version": "^9.5.0",
|
|
39
|
+
"vitest": "^3.0.9"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "vitest --coverage",
|
|
43
|
+
"release": "standard-version",
|
|
44
|
+
"release:minor": "standard-version --release-as minor",
|
|
45
|
+
"release:major": "standard-version --release-as major",
|
|
46
|
+
"release:patch": "standard-version --release-as patch",
|
|
47
|
+
"release:first": "standard-version --first-release"
|
|
48
|
+
}
|
|
12
49
|
}
|