@impactor/nodejs 3.0.1
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/LICENSE +21 -0
- package/README.md +129 -0
- package/index.d.ts +7 -0
- package/index.js +11 -0
- package/nx.json +190 -0
- package/package.json +72 -0
- package/src/cache-fs.d.ts +8 -0
- package/src/cache-fs.js +32 -0
- package/src/cache-fs.spec.js +38 -0
- package/src/fs-sync.d.ts +41 -0
- package/src/fs-sync.js +212 -0
- package/src/fs-sync.spec.js +262 -0
- package/src/fs.d.ts +16 -0
- package/src/fs.js +188 -0
- package/src/fs.spec.js +251 -0
- package/src/https.d.ts +3 -0
- package/src/https.js +52 -0
- package/src/https.spec.js +19 -0
- package/src/process.d.ts +13 -0
- package/src/process.js +203 -0
- package/src/process.spec.js +49 -0
package/src/fs.spec.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { resolve } from "./fs-sync";
|
|
3
|
+
import {
|
|
4
|
+
copy,
|
|
5
|
+
getEntries,
|
|
6
|
+
getModifiedTime,
|
|
7
|
+
getSize,
|
|
8
|
+
isDir,
|
|
9
|
+
mkdir,
|
|
10
|
+
move,
|
|
11
|
+
read,
|
|
12
|
+
remove,
|
|
13
|
+
write
|
|
14
|
+
} from "./fs";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { objectType } from "@impactor/javascript";
|
|
17
|
+
import { utimes } from "node:fs/promises";
|
|
18
|
+
let dir = resolve(import.meta.dirname, "./test!!/fs"), file = dir + "/file.txt";
|
|
19
|
+
beforeEach(() => remove(dir).then(() => write(file, "ok")));
|
|
20
|
+
afterEach(() => remove(dir));
|
|
21
|
+
test("mkdir", () => {
|
|
22
|
+
expect(existsSync(`${dir}/mkdir`)).toBeFalsy();
|
|
23
|
+
return mkdir(`${dir}/mkdir`).then(
|
|
24
|
+
(_value) => expect(existsSync(dir)).toBeTruthy()
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
test("write", () => {
|
|
28
|
+
expect(existsSync(`${dir}/write.txt`)).toBeFalsy();
|
|
29
|
+
return mkdir(dir).then(() => write(`${dir}/write.txt`, "ok")).then(() => {
|
|
30
|
+
expect(existsSync(`${dir}/write.txt`)).toBeTruthy();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
test("write in non-existing dir", () => {
|
|
34
|
+
let file2 = dir + "/non-existing/file.txt";
|
|
35
|
+
expect(existsSync(file2)).toBeFalsy();
|
|
36
|
+
return write(file2, "ok").then(() => {
|
|
37
|
+
expect(existsSync(file2)).toBeTruthy();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
test("getSize", () => Promise.all([
|
|
41
|
+
write(`${dir}/get-size/file1.txt`, "ok"),
|
|
42
|
+
write(`${dir}/get-size/file2.txt`, "ok")
|
|
43
|
+
]).then(
|
|
44
|
+
() => Promise.all([
|
|
45
|
+
// file
|
|
46
|
+
getSize(`${dir}/get-size/file1.txt`),
|
|
47
|
+
// array of files and directories (sum of all sizes)
|
|
48
|
+
getSize([`${dir}/get-size/file1.txt`, `${dir}/get-size/file2.txt`])
|
|
49
|
+
// directory (sum of its contents sizes)
|
|
50
|
+
// getSize(`${dir}/get-size`),
|
|
51
|
+
])
|
|
52
|
+
).then((value) => expect(value).toEqual([2, 4])));
|
|
53
|
+
test("isDir", () => Promise.all([isDir(file), isDir(dir)]).then(
|
|
54
|
+
(value) => expect(value).toEqual([false, true])
|
|
55
|
+
));
|
|
56
|
+
test("getModifiedTime -> file", () => Promise.all([getModifiedTime(file), getModifiedTime(dir)]).then((value) => {
|
|
57
|
+
expect(Math.floor(value[0])).toBeGreaterThanOrEqual(1624906832178);
|
|
58
|
+
expect(Math.floor(value[1])).toBeGreaterThanOrEqual(1624906832178);
|
|
59
|
+
}));
|
|
60
|
+
test("move", () => {
|
|
61
|
+
let file2 = dir + "/file2.txt";
|
|
62
|
+
expect(existsSync(file)).toBeTruthy();
|
|
63
|
+
expect(existsSync(file2)).toBeFalsy();
|
|
64
|
+
return move(file, file2).then(() => {
|
|
65
|
+
expect(existsSync(file)).toBeFalsy();
|
|
66
|
+
expect(existsSync(file2)).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
test("read", () => {
|
|
70
|
+
let fileJson = dir + "/file.json", fileArray = dir + "/array.json", fileJsonComments = dir + "/comments.json";
|
|
71
|
+
let contentJsonComments = `
|
|
72
|
+
// this file is created to test reading .json files that contains comments
|
|
73
|
+
// to test stripComments()
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
/* it should remove all comments */
|
|
77
|
+
/* even this
|
|
78
|
+
multi-line comment
|
|
79
|
+
*/
|
|
80
|
+
// also this comment
|
|
81
|
+
|
|
82
|
+
"x": 1,
|
|
83
|
+
"hello": "ok"
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
return Promise.all([
|
|
87
|
+
read(file),
|
|
88
|
+
write(fileJson, { x: 1, y: 2 }).then(() => read(fileJson)),
|
|
89
|
+
write(fileArray, [1, 2, 3]).then(() => read(fileArray)),
|
|
90
|
+
write(fileJsonComments, contentJsonComments).then(
|
|
91
|
+
() => read(fileJsonComments)
|
|
92
|
+
)
|
|
93
|
+
]).then((value) => {
|
|
94
|
+
let [txt, json, array, jsonWithComments] = value;
|
|
95
|
+
expect(txt.length).toEqual(2);
|
|
96
|
+
expect(txt).toContain("ok");
|
|
97
|
+
expect(objectType(txt)).toEqual("string");
|
|
98
|
+
expect(objectType(json)).toEqual("object");
|
|
99
|
+
expect(objectType(jsonWithComments)).toEqual("object");
|
|
100
|
+
expect(objectType(array)).toEqual("array");
|
|
101
|
+
expect(json).toEqual({ x: 1, y: 2 });
|
|
102
|
+
expect(jsonWithComments).toEqual({ x: 1, hello: "ok" });
|
|
103
|
+
expect(array).toEqual([1, 2, 3]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
test("read from a non-existing file", () => expect(
|
|
107
|
+
read(`${dir}/non-existing.txt`, { maxAge: 24 * 60 * 60 })
|
|
108
|
+
).rejects.toThrow("no such file or directory"));
|
|
109
|
+
test("read: maxAge", (done) => {
|
|
110
|
+
let file2 = dir + "/file.txt";
|
|
111
|
+
write(file2, "ok").then(() => read(file2, { maxAge: 24 * 60 * 60 })).then((content) => {
|
|
112
|
+
expect(content).toEqual("ok");
|
|
113
|
+
done();
|
|
114
|
+
}).catch((error) => done(error));
|
|
115
|
+
});
|
|
116
|
+
test("read from an expired cache", () => {
|
|
117
|
+
let file2 = dir + "/file.txt";
|
|
118
|
+
let date = /* @__PURE__ */ new Date(), today = date.getDate();
|
|
119
|
+
date.setDate(today - 1);
|
|
120
|
+
let yesterday = date.getTime() / 1e3;
|
|
121
|
+
return expect(
|
|
122
|
+
write(file2, "ok").then(() => utimes(file2, yesterday, yesterday)).then(() => read(file2, { maxAge: 1 }))
|
|
123
|
+
).rejects.toThrow("expired file");
|
|
124
|
+
});
|
|
125
|
+
test("remove a dir", () => {
|
|
126
|
+
expect(existsSync(file)).toBeTruthy();
|
|
127
|
+
expect(existsSync(dir)).toBeTruthy();
|
|
128
|
+
return remove([dir]).then(() => {
|
|
129
|
+
expect(existsSync(file)).toBeFalsy();
|
|
130
|
+
expect(existsSync(dir)).toBeFalsy();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
test("remove a non-exists path", () => {
|
|
134
|
+
let file2 = `${dir}/non-existing/file.txt`;
|
|
135
|
+
return remove(file2).then(() => expect(existsSync(file2)).toBeFalsy());
|
|
136
|
+
});
|
|
137
|
+
test("copy a directory and its sub-directories", () => {
|
|
138
|
+
return Promise.all([
|
|
139
|
+
write(`${dir}/copy-dir/file.txt`, ""),
|
|
140
|
+
write(`${dir}/copy-dir/sub-dir/file2.txt`, "")
|
|
141
|
+
]).then(() => copy(`${dir}/copy-dir`, `${dir}/copy-dir2`)).then(() => {
|
|
142
|
+
expect(existsSync(`${dir}/copy-dir2/file.txt`)).toBeTruthy();
|
|
143
|
+
expect(existsSync(`${dir}/copy-dir2/sub-dir/file2.txt`)).toBeTruthy();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe("getEntries", () => {
|
|
147
|
+
let entries = ["file.txt", "file.js"];
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
return remove(dir).then(
|
|
150
|
+
() => Promise.all(
|
|
151
|
+
entries.map((element) => {
|
|
152
|
+
return write(`${dir}/${element}`, "").then(
|
|
153
|
+
() => write(`${dir}/subdir/${element}`, "")
|
|
154
|
+
);
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
test("list all entries recursively", () => {
|
|
160
|
+
return getEntries(dir).then((result) => {
|
|
161
|
+
expect(result.sort()).toEqual(
|
|
162
|
+
// all files in dir with full path
|
|
163
|
+
[
|
|
164
|
+
...entries.map((element) => resolve(`${dir}/${element}`)).concat(
|
|
165
|
+
entries.map((element) => resolve(`${dir}/subdir/${element}`))
|
|
166
|
+
),
|
|
167
|
+
resolve(`${dir}/subdir`)
|
|
168
|
+
].sort()
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
test("filter by function", () => {
|
|
173
|
+
return getEntries(dir, (element) => element.includes(".js")).then(
|
|
174
|
+
(result) => {
|
|
175
|
+
expect(result).toEqual([
|
|
176
|
+
resolve(`${dir}/file.js`),
|
|
177
|
+
resolve(`${dir}/subdir/file.js`)
|
|
178
|
+
]);
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
test("filter by regex", () => {
|
|
183
|
+
return getEntries(dir, /subdir/).then((result) => {
|
|
184
|
+
expect(result.sort()).toEqual(
|
|
185
|
+
[
|
|
186
|
+
...entries.map((element) => resolve(`${dir}/subdir/${element}`)),
|
|
187
|
+
resolve(`${dir}/subdir`)
|
|
188
|
+
].sort()
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
test("filter by type: files", () => {
|
|
193
|
+
return getEntries(dir, "files").then((result) => {
|
|
194
|
+
expect(result.sort()).toEqual(
|
|
195
|
+
entries.map((element) => resolve(`${dir}/${element}`)).concat(entries.map((element) => resolve(`${dir}/subdir/${element}`))).sort()
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
test("filter by type: dirs", () => getEntries(dir, "dirs").then((result) => {
|
|
200
|
+
expect(result).toEqual([resolve(`${dir}/subdir`)]);
|
|
201
|
+
}));
|
|
202
|
+
test("depth=0", async () => {
|
|
203
|
+
for (let element of entries) {
|
|
204
|
+
await write(`${dir}/subdir/extra/${element}`, "");
|
|
205
|
+
}
|
|
206
|
+
return getEntries(dir, void 0, 0).then((result) => {
|
|
207
|
+
expect(result.sort()).toEqual(
|
|
208
|
+
[
|
|
209
|
+
...entries.map((element) => resolve(`${dir}/${element}`)),
|
|
210
|
+
resolve(`${dir}/subdir`)
|
|
211
|
+
].sort()
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
test("depth=1", async () => {
|
|
216
|
+
for (let element of entries) {
|
|
217
|
+
await write(`${dir}/subdir/extra/${element}`, "");
|
|
218
|
+
}
|
|
219
|
+
return getEntries(dir, void 0, 1).then((result) => {
|
|
220
|
+
expect(result.sort()).toEqual(
|
|
221
|
+
[
|
|
222
|
+
...entries.map((element) => resolve(`${dir}/${element}`)),
|
|
223
|
+
resolve(`${dir}/subdir`),
|
|
224
|
+
resolve(`${dir}/subdir/extra`)
|
|
225
|
+
].concat(entries.map((element) => resolve(`${dir}/subdir/${element}`))).sort()
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
test("depth=2", async () => {
|
|
230
|
+
for (let element of entries) {
|
|
231
|
+
await write(`${dir}/subdir/extra/${element}`, "");
|
|
232
|
+
}
|
|
233
|
+
return getEntries(dir, void 0, 2).then((result) => {
|
|
234
|
+
expect(result.sort()).toEqual(
|
|
235
|
+
[
|
|
236
|
+
...entries.map((element) => resolve(`${dir}/${element}`)),
|
|
237
|
+
resolve(`${dir}/subdir`),
|
|
238
|
+
resolve(`${dir}/subdir/extra`)
|
|
239
|
+
].concat(entries.map((element) => resolve(`${dir}/subdir/${element}`))).concat(
|
|
240
|
+
entries.map((element) => resolve(`${dir}/subdir/extra/${element}`))
|
|
241
|
+
).sort()
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
test("non existing dir", () => {
|
|
246
|
+
expect.hasAssertions();
|
|
247
|
+
return expect(getEntries(dir + "/non-existing")).rejects.toThrow(
|
|
248
|
+
"no such file or directory"
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
});
|
package/src/https.d.ts
ADDED
package/src/https.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import https from "node:https";
|
|
2
|
+
import { parse } from "content-type";
|
|
3
|
+
import { objectType } from "@impactor/javascript";
|
|
4
|
+
function request(url, data, options) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
let isObject = ["array", "object"].includes(objectType(data));
|
|
7
|
+
let requestOptions = Object.assign(
|
|
8
|
+
{ method: data ? "POST" : "GET" },
|
|
9
|
+
options || {}
|
|
10
|
+
);
|
|
11
|
+
if (isObject) {
|
|
12
|
+
requestOptions.headers = {
|
|
13
|
+
"content-type": "application/json",
|
|
14
|
+
...requestOptions.headers
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
let responseChunks = [];
|
|
18
|
+
let request_ = https.request(url, requestOptions, (res) => {
|
|
19
|
+
res.setEncoding("utf8");
|
|
20
|
+
res.on("data", function(chunk) {
|
|
21
|
+
responseChunks.push(chunk.toString());
|
|
22
|
+
});
|
|
23
|
+
res.on("end", () => {
|
|
24
|
+
try {
|
|
25
|
+
let response = responseChunks.join("");
|
|
26
|
+
let type = parse(res).type;
|
|
27
|
+
if (type === "application/json") {
|
|
28
|
+
response = JSON.parse(response);
|
|
29
|
+
}
|
|
30
|
+
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
|
|
31
|
+
reject(
|
|
32
|
+
["array", "object"].includes(objectType(response)) ? { code: res.statusCode, ...response } : { code: res.statusCode, message: response }
|
|
33
|
+
);
|
|
34
|
+
} else {
|
|
35
|
+
resolve(response);
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
reject(error);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
request_.on("error", (error) => reject(error));
|
|
43
|
+
if (requestOptions.method.toUpperCase() === "POST") {
|
|
44
|
+
let dataString = isObject ? JSON.stringify(data) : data;
|
|
45
|
+
request_.write(dataString);
|
|
46
|
+
}
|
|
47
|
+
request_.end();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
request as default
|
|
52
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, test } from "@jest/globals";
|
|
2
|
+
import request from "./https";
|
|
3
|
+
test("request", (done) => {
|
|
4
|
+
request(`https://jsonplaceholder.typicode.com/posts/1`).then((result) => {
|
|
5
|
+
expect(result.userId).toEqual(1);
|
|
6
|
+
done();
|
|
7
|
+
}).catch((error) => done(error));
|
|
8
|
+
});
|
|
9
|
+
test("request: POST", (done) => {
|
|
10
|
+
request(`https://jsonplaceholder.typicode.com/posts`, {
|
|
11
|
+
title: "test post request"
|
|
12
|
+
}).then((result) => {
|
|
13
|
+
expect(result.title).toEqual("test post request");
|
|
14
|
+
done();
|
|
15
|
+
}).catch((error) => done(error));
|
|
16
|
+
});
|
|
17
|
+
test("request: catching errors", () => expect(
|
|
18
|
+
request(`https://jsonplaceholder.typicode.com/wrong-endpoint`)
|
|
19
|
+
).rejects.toEqual({ code: 404 }));
|
package/src/process.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ExecOptions, type ExecSyncOptions, type SpawnOptionsWithoutStdio } from 'node:child_process';
|
|
2
|
+
import { type Obj } from '@impactor/javascript';
|
|
3
|
+
export interface Argv {
|
|
4
|
+
cmd: Array<string>;
|
|
5
|
+
options: Obj;
|
|
6
|
+
external: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseArgv(arguments_?: string | Array<string>): Argv;
|
|
9
|
+
export declare function toArgv(argumentsObject: Argv): string;
|
|
10
|
+
export declare function execSync(cmd: string, options?: ExecSyncOptions): Buffer | string;
|
|
11
|
+
export declare function exec(cmd: string, options?: ExecOptions): Promise<string>;
|
|
12
|
+
export declare function spawn(cmd: string, options?: SpawnOptionsWithoutStdio): Promise<void>;
|
|
13
|
+
export declare function runTask(tasks: Obj, arguments_?: string | Array<string>): Promise<void>;
|
package/src/process.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { argv } from "node:process";
|
|
2
|
+
import {
|
|
3
|
+
exec as _exec,
|
|
4
|
+
execSync as _execSync,
|
|
5
|
+
spawn as _spawn
|
|
6
|
+
} from "node:child_process";
|
|
7
|
+
import {
|
|
8
|
+
chunk,
|
|
9
|
+
dotNotationToObject,
|
|
10
|
+
flatten,
|
|
11
|
+
objectType,
|
|
12
|
+
toNumber
|
|
13
|
+
} from "@impactor/javascript";
|
|
14
|
+
function parseArgv(arguments_) {
|
|
15
|
+
if (!arguments_) {
|
|
16
|
+
arguments_ = argv.slice(2);
|
|
17
|
+
} else if (typeof arguments_ === "string") {
|
|
18
|
+
arguments_ = arguments_.split(" ");
|
|
19
|
+
}
|
|
20
|
+
let argumentsObject = { cmd: [], options: {}, external: "" };
|
|
21
|
+
let setOption = (key, value) => {
|
|
22
|
+
if (typeof value === "string") {
|
|
23
|
+
if (value === "true") {
|
|
24
|
+
value = true;
|
|
25
|
+
} else if (value === "false") {
|
|
26
|
+
value = false;
|
|
27
|
+
} else {
|
|
28
|
+
value = toNumber(value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (argumentsObject.options[key]) {
|
|
32
|
+
if (!Array.isArray(argumentsObject.options[key])) {
|
|
33
|
+
argumentsObject.options[key] = [argumentsObject.options[key]];
|
|
34
|
+
}
|
|
35
|
+
value = argumentsObject.options[key].concat(value);
|
|
36
|
+
}
|
|
37
|
+
if (key.includes(".")) {
|
|
38
|
+
let keys = key.split(".");
|
|
39
|
+
key = keys.shift();
|
|
40
|
+
argumentsObject.options[key] = dotNotationToObject(keys, value);
|
|
41
|
+
} else {
|
|
42
|
+
argumentsObject.options[key] = value;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
if (arguments_.includes("--")) {
|
|
46
|
+
let chunks = chunk(arguments_, arguments_.indexOf("--") || 0);
|
|
47
|
+
argumentsObject.external = chunks[1]?.slice(1)?.join(" ");
|
|
48
|
+
arguments_ = chunks[0];
|
|
49
|
+
}
|
|
50
|
+
for (let index = 0; index < arguments_.length; index++) {
|
|
51
|
+
let argument = arguments_[index].trim();
|
|
52
|
+
if (argument === "") {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (/^--no-.+/.test(argument)) {
|
|
56
|
+
let match = argument.match(/^--no-(.+)/), key = match[1];
|
|
57
|
+
setOption(key, false);
|
|
58
|
+
} else if (/^--.+=/.test(argument)) {
|
|
59
|
+
let match = argument.match(/^--([^=]+)=(.*)$/s), key = match[1], value = match[2];
|
|
60
|
+
setOption(key, value);
|
|
61
|
+
} else if (/^--.+/.test(argument)) {
|
|
62
|
+
let match = argument.match(/^--(.+)/), key = match[1], next = arguments_[index + 1];
|
|
63
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
64
|
+
setOption(key, next);
|
|
65
|
+
index++;
|
|
66
|
+
} else {
|
|
67
|
+
setOption(key, true);
|
|
68
|
+
}
|
|
69
|
+
} else if (/^-[^-]+/.test(argument)) {
|
|
70
|
+
let letters = argument.slice(1, -1).split("");
|
|
71
|
+
let broken = false;
|
|
72
|
+
for (let index_ = 0; index_ < letters.length; index_++) {
|
|
73
|
+
let next2 = argument.slice(index_ + 2);
|
|
74
|
+
if (next2 === "-") {
|
|
75
|
+
setOption(letters[index_], "-");
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (/[A-Za-z]/.test(letters[index_]) && /=/.test(next2)) {
|
|
79
|
+
setOption(letters[index_], next2.split("=")[1]);
|
|
80
|
+
broken = true;
|
|
81
|
+
break;
|
|
82
|
+
} else if (/[A-Za-z]/.test(letters[index_]) && /-?\d+(?:\.\d*)?(?:e-?\d+)?$/.test(next2)) {
|
|
83
|
+
setOption(letters[index_], next2);
|
|
84
|
+
broken = true;
|
|
85
|
+
break;
|
|
86
|
+
} else if (letters[index_ + 1] && /\W/.test(letters[index_ + 1])) {
|
|
87
|
+
setOption(letters[index_], argument.slice(index_ + 2));
|
|
88
|
+
broken = true;
|
|
89
|
+
break;
|
|
90
|
+
} else {
|
|
91
|
+
setOption(letters[index_], true);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
let key = argument.at(-1), next = arguments_[index + 1];
|
|
95
|
+
if (!broken && key !== "-") {
|
|
96
|
+
if (next && !/^(?:-|--)[^-]/.test(next)) {
|
|
97
|
+
setOption(key, next);
|
|
98
|
+
index++;
|
|
99
|
+
} else {
|
|
100
|
+
setOption(key, true);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
argumentsObject.cmd.push(argument);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return argumentsObject;
|
|
108
|
+
}
|
|
109
|
+
function toArgv(argumentsObject) {
|
|
110
|
+
let argv2 = "";
|
|
111
|
+
for (let element of argumentsObject.cmd) {
|
|
112
|
+
argv2 += element + " ";
|
|
113
|
+
}
|
|
114
|
+
let setOption = function(key, value) {
|
|
115
|
+
let argv3 = "";
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
for (let element of value) {
|
|
118
|
+
argv3 += `--${key}=${element} `;
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
argv3 += `--${key}=${value} `;
|
|
122
|
+
}
|
|
123
|
+
return argv3;
|
|
124
|
+
};
|
|
125
|
+
for (let key in argumentsObject.options) {
|
|
126
|
+
if (argumentsObject.options.hasOwnProperty(key)) {
|
|
127
|
+
let value = argumentsObject.options[key];
|
|
128
|
+
if (objectType(value) === "object") {
|
|
129
|
+
for (let [k, v] of Object.entries(flatten({ [key]: value }))) {
|
|
130
|
+
argv2 += setOption(k, v);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
argv2 += setOption(key, value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (argumentsObject.external) {
|
|
138
|
+
let external = argumentsObject.external.trim();
|
|
139
|
+
if (external !== "") {
|
|
140
|
+
argv2 += `-- ${argumentsObject.external}`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return argv2;
|
|
144
|
+
}
|
|
145
|
+
function execSync(cmd, options) {
|
|
146
|
+
let opts = { stdio: "inherit", ...options };
|
|
147
|
+
return _execSync(cmd, opts);
|
|
148
|
+
}
|
|
149
|
+
function exec(cmd, options) {
|
|
150
|
+
return new Promise((res, rej) => {
|
|
151
|
+
_exec(cmd, options, (error, stdout, stderr) => {
|
|
152
|
+
if (error) {
|
|
153
|
+
error.message = error?.message || stderr || stdout;
|
|
154
|
+
rej(error);
|
|
155
|
+
} else {
|
|
156
|
+
res(stdout);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function spawn(cmd, options) {
|
|
162
|
+
return new Promise((res, rej) => {
|
|
163
|
+
let childProcess = _spawn(cmd, options);
|
|
164
|
+
childProcess.stdout.on("data", (data) => console.log(data.toString()));
|
|
165
|
+
childProcess.stderr.on("data", (err) => console.error(err.toString()));
|
|
166
|
+
childProcess.on("exit", (exitCode) => {
|
|
167
|
+
if (exitCode) rej();
|
|
168
|
+
else res();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
async function runTask(tasks, arguments_) {
|
|
173
|
+
let parsedArguments = parseArgv(arguments_), task = parsedArguments.cmd.shift();
|
|
174
|
+
if (!task) {
|
|
175
|
+
throw new Error("task not provided!");
|
|
176
|
+
} else if (!(task in tasks)) {
|
|
177
|
+
throw new Error(`unknown task ${task}`);
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
let optionsString = "";
|
|
181
|
+
for (let key in parsedArguments.options) {
|
|
182
|
+
optionsString += `--${key}=${parsedArguments.options[key]} `;
|
|
183
|
+
}
|
|
184
|
+
console.log(
|
|
185
|
+
`>> running the task: ${task} ${parsedArguments.cmd.join(
|
|
186
|
+
" "
|
|
187
|
+
)} ${optionsString}`
|
|
188
|
+
);
|
|
189
|
+
await tasks[task](...parsedArguments.cmd, parsedArguments.options);
|
|
190
|
+
console.log(">> Done");
|
|
191
|
+
} catch (error) {
|
|
192
|
+
error.task = task;
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export {
|
|
197
|
+
exec,
|
|
198
|
+
execSync,
|
|
199
|
+
parseArgv,
|
|
200
|
+
runTask,
|
|
201
|
+
spawn,
|
|
202
|
+
toArgv
|
|
203
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { expect, test } from "@jest/globals";
|
|
2
|
+
import { parseArgv, toArgv } from "./process";
|
|
3
|
+
let input = "cmd1 cmd2 - --a 1 --b=2 --c true --d false --e --no-f -g -h hh -ijk ok -l- --num=1 --arr=1 --arr=2 --obj.a.b=1 -mn-o -p - -q123 --whitespaces=a\nb c --no-exit -- ext1 ext2";
|
|
4
|
+
let parsed = {
|
|
5
|
+
cmd: ["cmd1", "cmd2", "-"],
|
|
6
|
+
options: {
|
|
7
|
+
a: 1,
|
|
8
|
+
b: 2,
|
|
9
|
+
c: true,
|
|
10
|
+
d: false,
|
|
11
|
+
e: true,
|
|
12
|
+
f: false,
|
|
13
|
+
g: true,
|
|
14
|
+
h: "hh",
|
|
15
|
+
i: true,
|
|
16
|
+
j: true,
|
|
17
|
+
k: "ok",
|
|
18
|
+
l: "-",
|
|
19
|
+
num: 1,
|
|
20
|
+
arr: [1, 2],
|
|
21
|
+
obj: { a: { b: 1 } },
|
|
22
|
+
m: true,
|
|
23
|
+
n: "-o",
|
|
24
|
+
p: "-",
|
|
25
|
+
q: 123,
|
|
26
|
+
whitespaces: "a\nb c",
|
|
27
|
+
exit: false
|
|
28
|
+
},
|
|
29
|
+
external: "ext1 ext2"
|
|
30
|
+
};
|
|
31
|
+
test("parseArgv()", () => {
|
|
32
|
+
expect(parseArgv(input)).toEqual(parsed);
|
|
33
|
+
});
|
|
34
|
+
test("parseArgv(): contains extra spaces", () => {
|
|
35
|
+
expect(parseArgv("cmd --a=1 --b=2")).toEqual({
|
|
36
|
+
cmd: ["cmd"],
|
|
37
|
+
options: { a: 1, b: 2 },
|
|
38
|
+
external: ""
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
test("toArgv()", () => {
|
|
42
|
+
let parsedArguments = {
|
|
43
|
+
cmd: ["cmd1", "cmd2"],
|
|
44
|
+
options: { a: "x", b: 1, c: true, d: false, e: { f: 1 }, g: [1, 2] },
|
|
45
|
+
external: "--external params"
|
|
46
|
+
};
|
|
47
|
+
let arguments_ = `cmd1 cmd2 --a=x --b=1 --c=true --d=false --e.f=1 --g=1 --g=2 -- --external params`;
|
|
48
|
+
expect(toArgv(parsedArguments)).toEqual(arguments_);
|
|
49
|
+
});
|