@sandro-sikic/maker 1.0.7 → 1.0.8
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/example.js +13 -0
- package/index.js +31 -76
- package/package.json +4 -9
- package/test/index.test.js +1161 -0
- package/__tests__/index.test.js +0 -119
- package/babel.config.js +0 -10
- package/jest.config.cjs +0 -8
package/example.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as maker from './index.js';
|
|
2
|
+
|
|
3
|
+
(async () => {
|
|
4
|
+
maker.init();
|
|
5
|
+
|
|
6
|
+
const spinner = await maker.spinner('Testing maker.run()...').start();
|
|
7
|
+
|
|
8
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
9
|
+
|
|
10
|
+
await maker.run('echo "Hello, World!"');
|
|
11
|
+
|
|
12
|
+
spinner.succeed('Done!');
|
|
13
|
+
})();
|
package/index.js
CHANGED
|
@@ -1,37 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// small helper to lazily load (CJS-first, ESM dynamic import fallback)
|
|
5
|
-
function makeLazyLoader(moduleId) {
|
|
6
|
-
let _cached = null;
|
|
7
|
-
return async function load() {
|
|
8
|
-
if (_cached) return _cached;
|
|
9
|
-
try {
|
|
10
|
-
/* eslint-disable global-require, import/no-dynamic-require */
|
|
11
|
-
const mod = require(moduleId);
|
|
12
|
-
_cached = mod && (mod.default || mod);
|
|
13
|
-
return _cached;
|
|
14
|
-
} catch (err) {
|
|
15
|
-
try {
|
|
16
|
-
const ns = await import(moduleId);
|
|
17
|
-
_cached = ns && (ns.default || ns);
|
|
18
|
-
return _cached;
|
|
19
|
-
} catch (e) {
|
|
20
|
-
// unable to load in this environment — return null so callers can continue
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const loadPrompts = makeLazyLoader('@inquirer/prompts');
|
|
28
|
-
const loadOra = makeLazyLoader('ora');
|
|
1
|
+
import * as inquirer from '@inquirer/prompts';
|
|
2
|
+
import * as ora from 'ora';
|
|
29
3
|
|
|
30
4
|
function init() {
|
|
31
5
|
// Ensure we're running in an interactive terminal
|
|
32
6
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
33
7
|
console.error(
|
|
34
|
-
'\n
|
|
8
|
+
'\n⚠ This TUI requires an interactive terminal. Run this in a terminal (not the Node REPL or a non-interactive/debug console).\n',
|
|
35
9
|
);
|
|
36
10
|
process.exit(1);
|
|
37
11
|
}
|
|
@@ -49,21 +23,28 @@ function init() {
|
|
|
49
23
|
* @param {number} [opts.maxLines=10000] - Maximum number of lines to retain in captured output.
|
|
50
24
|
* @returns {Promise<{output:string, stdout:string, stderr:string, code:number|null, isError:boolean, error:Error|null}>}
|
|
51
25
|
*/
|
|
52
|
-
function run(command, opts = {}) {
|
|
26
|
+
async function run(command, opts = {}) {
|
|
27
|
+
if (typeof command !== 'string' || !command.trim()) {
|
|
28
|
+
throw new TypeError('run() requires a non-empty string command');
|
|
29
|
+
}
|
|
30
|
+
|
|
53
31
|
const { maxLines = 10000 } = opts;
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const convertedCommand = path.join(...split);
|
|
32
|
+
// import spawn at runtime so test mocks (vi.mock) take effect per-test
|
|
33
|
+
const { spawn } = await import('child_process');
|
|
57
34
|
|
|
58
35
|
function trimToLastNLines(s, n) {
|
|
59
36
|
if (!s) return s;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
37
|
+
// remove trailing newlines for accurate line counting
|
|
38
|
+
const hadTrailingNewline = /(?:\r?\n)$/.test(s);
|
|
39
|
+
const trimmed = s.replace(/(?:\r?\n)+$/g, '');
|
|
40
|
+
const lines = trimmed.split(/\r?\n/);
|
|
41
|
+
if (lines.length <= n) return hadTrailingNewline ? trimmed + '\n' : trimmed;
|
|
42
|
+
const out = lines.slice(-n).join('\n');
|
|
43
|
+
return hadTrailingNewline ? out + '\n' : out;
|
|
63
44
|
}
|
|
64
45
|
|
|
65
46
|
return new Promise((resolve) => {
|
|
66
|
-
const child = spawn(
|
|
47
|
+
const child = spawn(command, { shell: true, stdio: 'pipe' });
|
|
67
48
|
|
|
68
49
|
let stdout = '';
|
|
69
50
|
let stderr = '';
|
|
@@ -118,34 +99,19 @@ function run(command, opts = {}) {
|
|
|
118
99
|
});
|
|
119
100
|
}
|
|
120
101
|
|
|
121
|
-
/**
|
|
122
|
-
* Register a callback to run when the process receives SIGINT (Ctrl+C).
|
|
123
|
-
* - Accepts a function (may be async).
|
|
124
|
-
* - Returns a disposer function that removes the listener.
|
|
125
|
-
*
|
|
126
|
-
* Note: spinner is started asynchronously (ora is lazy-loaded); onExit itself
|
|
127
|
-
* remains synchronous so callers can register the handler immediately.
|
|
128
|
-
*/
|
|
129
102
|
function onExit(cb) {
|
|
130
103
|
if (typeof cb !== 'function') {
|
|
131
104
|
throw new TypeError('onExit requires a callback function');
|
|
132
105
|
}
|
|
133
106
|
|
|
134
107
|
let called = false;
|
|
135
|
-
let exiting = null;
|
|
136
|
-
|
|
137
|
-
// start spinner asynchronously (best-effort; ignore load errors)
|
|
138
|
-
loadOra().then((o) => {
|
|
139
|
-
try {
|
|
140
|
-
exiting = o('Gracefully shutting down...').start();
|
|
141
|
-
} catch (e) {
|
|
142
|
-
/* ignore */
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
108
|
|
|
146
109
|
const handler = async () => {
|
|
147
110
|
if (called) return;
|
|
148
111
|
called = true;
|
|
112
|
+
|
|
113
|
+
const exiting = spinner('Gracefully shutting down...').start();
|
|
114
|
+
|
|
149
115
|
try {
|
|
150
116
|
await Promise.resolve(cb());
|
|
151
117
|
} catch (err) {
|
|
@@ -159,28 +125,17 @@ function onExit(cb) {
|
|
|
159
125
|
};
|
|
160
126
|
|
|
161
127
|
process.on('SIGINT', handler);
|
|
128
|
+
process.on('SIGTERM', handler);
|
|
129
|
+
process.on('SIGQUIT', handler);
|
|
162
130
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// `prompt(...args)` forwards the call to the underlying module and returns its result.
|
|
169
|
-
async function prompt(...args) {
|
|
170
|
-
const p = await loadPrompts();
|
|
171
|
-
if (!p) return null; // prompts unavailable in this environment
|
|
172
|
-
return p(...args);
|
|
131
|
+
return () => {
|
|
132
|
+
process.off('SIGINT', handler);
|
|
133
|
+
process.off('SIGTERM', handler);
|
|
134
|
+
process.off('SIGQUIT', handler);
|
|
135
|
+
};
|
|
173
136
|
}
|
|
174
137
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return s(...args);
|
|
178
|
-
}
|
|
138
|
+
const prompt = inquirer.default ?? inquirer;
|
|
139
|
+
const spinner = ora.default ?? ora;
|
|
179
140
|
|
|
180
|
-
|
|
181
|
-
init,
|
|
182
|
-
run,
|
|
183
|
-
onExit,
|
|
184
|
-
prompt,
|
|
185
|
-
spinner,
|
|
186
|
-
};
|
|
141
|
+
export { init, run, onExit, prompt, spinner };
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sandro-sikic/maker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "A library to create dev cli",
|
|
5
6
|
"main": "index.js",
|
|
6
7
|
"types": "index.d.ts",
|
|
7
8
|
"scripts": {
|
|
8
|
-
"test": "
|
|
9
|
+
"test": "vitest"
|
|
9
10
|
},
|
|
10
11
|
"author": "sandro-sikic",
|
|
11
12
|
"repository": {
|
|
@@ -18,12 +19,6 @@
|
|
|
18
19
|
"ora": "^9.3.0"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
|
-
"
|
|
22
|
-
"pkg": "^5.8.1",
|
|
23
|
-
"rimraf": "^5.0.1",
|
|
24
|
-
"jest": "^29.7.0",
|
|
25
|
-
"babel-jest": "^29.7.0",
|
|
26
|
-
"@babel/core": "^7.23.0",
|
|
27
|
-
"@babel/preset-env": "^7.23.0"
|
|
22
|
+
"vitest": "^4.0.18"
|
|
28
23
|
}
|
|
29
24
|
}
|