first-base 2.0.2 → 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/.node-version +1 -1
- package/.npm-version +1 -1
- package/README.md +5 -0
- package/dist/find-root-dir.d.ts +1 -0
- package/dist/find-root-dir.js +51 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +297 -0
- package/dist/index.js.flow +43 -0
- package/dist/sanitizers.d.ts +1 -0
- package/dist/sanitizers.js +39 -0
- package/package.json +8 -16
- package/tsconfig.json +22 -0
package/.node-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v24.
|
|
1
|
+
v24.14.1
|
package/.npm-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.
|
|
1
|
+
11.11.0
|
package/README.md
CHANGED
|
@@ -183,3 +183,8 @@ The only things that changed between 1.x and 2.0 are:
|
|
|
183
183
|
- Linux arm64 and x86_64
|
|
184
184
|
- macOS arm64 and x86_64
|
|
185
185
|
- Windows arm64 and x86_64
|
|
186
|
+
|
|
187
|
+
## Upgrading from 2.x
|
|
188
|
+
|
|
189
|
+
- The type definitions for `RunContext.write` and `RunContext.kill` were narrowed slightly. The runtime behavior of those methods didn't change.
|
|
190
|
+
- `RunContext.debug()` is deprecated (but not removed). Use `spawn` option `debug: true` instead.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function findRootDir(startingDir: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.findRootDir = findRootDir;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const nice_path_1 = require("nice-path");
|
|
9
|
+
const strongRootDirIndicators = [".git", ".hg"];
|
|
10
|
+
const weakRootDirIndicators = ["package-lock.json", ".gitignore", ".hgignore"];
|
|
11
|
+
const veryWeakRootDirIndicators = ["package.json", "README.md"];
|
|
12
|
+
function hasFile(dir, filename) {
|
|
13
|
+
const fullPath = dir.concat(filename).toString();
|
|
14
|
+
try {
|
|
15
|
+
return fs_1.default.existsSync(fullPath);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function findRootDir(startingDir) {
|
|
22
|
+
const start = new nice_path_1.Path(startingDir).normalize();
|
|
23
|
+
const searchDirs = [start];
|
|
24
|
+
let currentPath = start.replaceLast([]);
|
|
25
|
+
while (currentPath.segments.length > 0) {
|
|
26
|
+
searchDirs.push(currentPath);
|
|
27
|
+
currentPath = currentPath.replaceLast([]);
|
|
28
|
+
}
|
|
29
|
+
for (const dir of searchDirs) {
|
|
30
|
+
for (const indicator of strongRootDirIndicators) {
|
|
31
|
+
if (hasFile(dir, indicator)) {
|
|
32
|
+
return dir.toString();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const dir of searchDirs) {
|
|
37
|
+
for (const indicator of weakRootDirIndicators) {
|
|
38
|
+
if (hasFile(dir, indicator)) {
|
|
39
|
+
return dir.toString();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const dir of searchDirs) {
|
|
44
|
+
for (const indicator of veryWeakRootDirIndicators) {
|
|
45
|
+
if (hasFile(dir, indicator)) {
|
|
46
|
+
return dir.toString();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return start.toString();
|
|
51
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { sanitizers } from "./sanitizers";
|
|
2
|
+
export type Options = {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
env?: {
|
|
5
|
+
[varName: string]: string | undefined;
|
|
6
|
+
};
|
|
7
|
+
argv0?: string;
|
|
8
|
+
detached?: boolean;
|
|
9
|
+
uid?: number;
|
|
10
|
+
gid?: number;
|
|
11
|
+
shell?: boolean | string;
|
|
12
|
+
windowsVerbatimArguments?: boolean;
|
|
13
|
+
windowsHide?: boolean;
|
|
14
|
+
pty?: boolean;
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type RunContext = {
|
|
18
|
+
result: {
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
code: null | number;
|
|
22
|
+
error: null | Error;
|
|
23
|
+
};
|
|
24
|
+
cleanResult(): {
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
code: null | number;
|
|
28
|
+
error: null | Error;
|
|
29
|
+
};
|
|
30
|
+
completion: Promise<void>;
|
|
31
|
+
/** @deprecated pass `debug: true` as an option to {@link spawn} instead. */
|
|
32
|
+
debug(): RunContext;
|
|
33
|
+
outputContains(value: string | RegExp): Promise<void>;
|
|
34
|
+
clearOutputContainsBuffer(): void;
|
|
35
|
+
write(data: string | Buffer): void;
|
|
36
|
+
close(stream: "stdin" | "stdout" | "stderr"): void;
|
|
37
|
+
kill(signal?: NodeJS.Signals): void;
|
|
38
|
+
};
|
|
39
|
+
declare const allInflightRunContexts: Set<RunContext>;
|
|
40
|
+
declare function spawn(cmd: string): RunContext;
|
|
41
|
+
declare function spawn(cmd: string, args: Array<string>): RunContext;
|
|
42
|
+
declare function spawn(cmd: string, options: Options): RunContext;
|
|
43
|
+
declare function spawn(cmd: string, args: Array<string>, options: Options): RunContext;
|
|
44
|
+
export { spawn, sanitizers, allInflightRunContexts };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.allInflightRunContexts = exports.sanitizers = void 0;
|
|
7
|
+
exports.spawn = spawn;
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const strip_ansi_1 = __importDefault(require("strip-ansi"));
|
|
10
|
+
const sanitizers_1 = require("./sanitizers");
|
|
11
|
+
Object.defineProperty(exports, "sanitizers", { enumerable: true, get: function () { return sanitizers_1.sanitizers; } });
|
|
12
|
+
const allInflightRunContexts = new Set();
|
|
13
|
+
exports.allInflightRunContexts = allInflightRunContexts;
|
|
14
|
+
function spawn(cmd, argsOrOptions, passedOptions) {
|
|
15
|
+
let args;
|
|
16
|
+
let options;
|
|
17
|
+
if (Array.isArray(argsOrOptions)) {
|
|
18
|
+
args = argsOrOptions;
|
|
19
|
+
}
|
|
20
|
+
else if (typeof argsOrOptions === "object") {
|
|
21
|
+
options = argsOrOptions;
|
|
22
|
+
}
|
|
23
|
+
if (passedOptions && !options) {
|
|
24
|
+
options = passedOptions;
|
|
25
|
+
}
|
|
26
|
+
if (!args) {
|
|
27
|
+
args = [];
|
|
28
|
+
}
|
|
29
|
+
if (!options) {
|
|
30
|
+
options = {};
|
|
31
|
+
}
|
|
32
|
+
let child;
|
|
33
|
+
let stdin;
|
|
34
|
+
let stdout;
|
|
35
|
+
let stderr;
|
|
36
|
+
let unreffable = null;
|
|
37
|
+
let running;
|
|
38
|
+
let debug = options.debug ?? false;
|
|
39
|
+
let outputContainsBuffer = "";
|
|
40
|
+
let pendingOutputContainsRequests = new Set();
|
|
41
|
+
const disposables = [];
|
|
42
|
+
const debugLog = (...msg) => {
|
|
43
|
+
if (debug) {
|
|
44
|
+
console.log(...msg);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const runContext = {
|
|
48
|
+
result: {
|
|
49
|
+
// All of the stdout and stderr the process has written so far.
|
|
50
|
+
stdout: "",
|
|
51
|
+
stderr: "",
|
|
52
|
+
// Exit status code, if the process has finished.
|
|
53
|
+
code: null,
|
|
54
|
+
// if the process errored out, this will be the Error
|
|
55
|
+
error: null,
|
|
56
|
+
},
|
|
57
|
+
// Return a version of result which has had the string sanitizers run on it
|
|
58
|
+
cleanResult() {
|
|
59
|
+
return Object.assign({}, runContext.result, {
|
|
60
|
+
stdout: sanitizers_1.sanitizers.reduce((str, transformFn) => transformFn(str), runContext.result.stdout),
|
|
61
|
+
stderr: sanitizers_1.sanitizers.reduce((str, transformFn) => transformFn(str), runContext.result.stderr),
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
// Promise that gets resolved when the child process completes.
|
|
65
|
+
// Actual value gets filled in below.
|
|
66
|
+
completion: Promise.resolve(),
|
|
67
|
+
debug() {
|
|
68
|
+
debug = true;
|
|
69
|
+
return this;
|
|
70
|
+
},
|
|
71
|
+
// Returns a Promise that resolves once the child process output
|
|
72
|
+
// (combined stdout and stderr) contains the passed string or
|
|
73
|
+
// matches the passed RegExp. Ignores ansi control characters.
|
|
74
|
+
outputContains(value) {
|
|
75
|
+
debugLog(`Waiting for output to contain ${JSON.stringify(value)}...`);
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const request = { value, resolve: undefined, reject: undefined };
|
|
78
|
+
request.resolve = () => {
|
|
79
|
+
pendingOutputContainsRequests.delete(request);
|
|
80
|
+
resolve();
|
|
81
|
+
};
|
|
82
|
+
request.reject = (error) => {
|
|
83
|
+
pendingOutputContainsRequests.delete(request);
|
|
84
|
+
reject(error);
|
|
85
|
+
};
|
|
86
|
+
pendingOutputContainsRequests.add(request);
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
clearOutputContainsBuffer() {
|
|
90
|
+
outputContainsBuffer = "";
|
|
91
|
+
},
|
|
92
|
+
// Call this function to write into stdin.
|
|
93
|
+
write(data) {
|
|
94
|
+
stdin.write(data);
|
|
95
|
+
},
|
|
96
|
+
// Call this function to close stdin, stdout, or stderr.
|
|
97
|
+
close(stream) {
|
|
98
|
+
switch (String(stream).toLowerCase()) {
|
|
99
|
+
case "stdin": {
|
|
100
|
+
if ("end" in stdin) {
|
|
101
|
+
stdin.end();
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case "stdout": {
|
|
106
|
+
if ("destroy" in stdout) {
|
|
107
|
+
stdout.destroy();
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "stderr": {
|
|
112
|
+
if (stderr != null && "destroy" in stderr) {
|
|
113
|
+
stderr.destroy();
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
default: {
|
|
118
|
+
throw new Error(`Invalid stream name: '${stream}'. Valid names are 'stdin', 'stdout', or 'stderr'.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
// Call this function to send a signal to the child process.
|
|
123
|
+
// You can pass "SIGTERM", "SIGKILL", etc. Defaults to "SIGINT".
|
|
124
|
+
kill(signal = "SIGINT") {
|
|
125
|
+
if (running) {
|
|
126
|
+
child.kill(signal);
|
|
127
|
+
}
|
|
128
|
+
if (unreffable != null) {
|
|
129
|
+
unreffable.unref();
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
if (options.pty) {
|
|
134
|
+
debugLog("pty option was true; using node-pty");
|
|
135
|
+
const ptySpawn = require("@lydell/node-pty").spawn;
|
|
136
|
+
const ptyChild = ptySpawn(cmd, args, options);
|
|
137
|
+
child = ptyChild;
|
|
138
|
+
stdin = ptyChild;
|
|
139
|
+
stdout = ptyChild;
|
|
140
|
+
stderr = null; // no way to tell between stdout and stderr with pty
|
|
141
|
+
// no unreffable equivalent on ptyChild
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
debugLog("pty option was NOT true; using child_process");
|
|
145
|
+
const nonPtyChild = (0, child_process_1.spawn)(cmd, args, options);
|
|
146
|
+
child = nonPtyChild;
|
|
147
|
+
stdin = nonPtyChild.stdin;
|
|
148
|
+
stdout = nonPtyChild.stdout;
|
|
149
|
+
stderr = nonPtyChild.stderr;
|
|
150
|
+
unreffable = nonPtyChild;
|
|
151
|
+
}
|
|
152
|
+
running = true;
|
|
153
|
+
allInflightRunContexts.add(runContext);
|
|
154
|
+
if ("on" in child) {
|
|
155
|
+
debugLog("using 'on' method to listen for child spawn event");
|
|
156
|
+
child.on("spawn", () => {
|
|
157
|
+
debugLog("'spawn' event");
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
debugLog("child had no 'on' method, so child spawn event listener wasn't set up");
|
|
162
|
+
}
|
|
163
|
+
const checkForPendingOutputRequestsToResolve = () => {
|
|
164
|
+
pendingOutputContainsRequests.forEach((request) => {
|
|
165
|
+
if (typeof request.value === "string") {
|
|
166
|
+
if ((0, strip_ansi_1.default)(outputContainsBuffer).indexOf(request.value) != -1) {
|
|
167
|
+
request.resolve();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (request.value instanceof RegExp) {
|
|
171
|
+
if (request.value.test((0, strip_ansi_1.default)(outputContainsBuffer))) {
|
|
172
|
+
request.resolve();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
if ("setEncoding" in stdout) {
|
|
178
|
+
debugLog("setting stdout encoding to utf-8");
|
|
179
|
+
stdout.setEncoding("utf-8");
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
debugLog("not setting stdout encoding because the setEncoding method was not present");
|
|
183
|
+
}
|
|
184
|
+
const handleStdoutData = (data) => {
|
|
185
|
+
runContext.result.stdout += data;
|
|
186
|
+
outputContainsBuffer += data;
|
|
187
|
+
debugLog(`STDOUT: ${data.toString()}`);
|
|
188
|
+
checkForPendingOutputRequestsToResolve();
|
|
189
|
+
};
|
|
190
|
+
if ("onData" in stdout) {
|
|
191
|
+
debugLog("using 'onData' method to listen for stdout data event");
|
|
192
|
+
// the pty instance returned by node-pty
|
|
193
|
+
// requires attaching handlers differently
|
|
194
|
+
stdout.onData(handleStdoutData);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
debugLog("using 'on' method to listen for stdout data event");
|
|
198
|
+
stdout.on("data", handleStdoutData);
|
|
199
|
+
}
|
|
200
|
+
if (stderr) {
|
|
201
|
+
debugLog("setting stderr encoding to utf-8");
|
|
202
|
+
stderr.setEncoding("utf-8");
|
|
203
|
+
// this is never a pty instance,
|
|
204
|
+
// so we don't need to deal with onData here:
|
|
205
|
+
debugLog("using 'on' method to listen for stderr data event");
|
|
206
|
+
stderr.on("data", (data) => {
|
|
207
|
+
runContext.result.stderr += data;
|
|
208
|
+
outputContainsBuffer += data;
|
|
209
|
+
debugLog(`STDERR: ${data.toString()}`);
|
|
210
|
+
checkForPendingOutputRequestsToResolve();
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
debugLog("stderr isn't present (pty mixes stdout and stderr together), so not setting encoding or setting up data event listener for stderr");
|
|
215
|
+
}
|
|
216
|
+
runContext.completion = new Promise((resolve) => {
|
|
217
|
+
let hasFinished = false;
|
|
218
|
+
const finish = (reason) => {
|
|
219
|
+
debugLog("in finish", runContext.result);
|
|
220
|
+
if (hasFinished) {
|
|
221
|
+
debugLog("finish called more than once; ignoring");
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
running = false;
|
|
225
|
+
allInflightRunContexts.delete(runContext);
|
|
226
|
+
resolve();
|
|
227
|
+
for (const request of pendingOutputContainsRequests) {
|
|
228
|
+
request.reject(new Error(`Child process ${reason} before its output contained the requested content: ${request.value}`));
|
|
229
|
+
}
|
|
230
|
+
for (const disposable of disposables) {
|
|
231
|
+
disposable.dispose();
|
|
232
|
+
}
|
|
233
|
+
hasFinished = true;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
if ("on" in child) {
|
|
237
|
+
debugLog("using 'on' method to listen for child close event");
|
|
238
|
+
child.on("close", (code, signal) => {
|
|
239
|
+
debugLog("'close' event", { code, signal });
|
|
240
|
+
if (code != null) {
|
|
241
|
+
runContext.result.code = code;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
debugLog("child had no 'on' method, so child close event listener wasn't set up");
|
|
247
|
+
}
|
|
248
|
+
if ("onExit" in child) {
|
|
249
|
+
debugLog("using 'onExit' method to listen for child exit event");
|
|
250
|
+
const disposable = child.onExit(({ exitCode, signal }) => {
|
|
251
|
+
debugLog("onExit", { exitCode, signal });
|
|
252
|
+
if (exitCode != null) {
|
|
253
|
+
runContext.result.code = exitCode;
|
|
254
|
+
}
|
|
255
|
+
finish("exited");
|
|
256
|
+
});
|
|
257
|
+
disposables.push(disposable);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
debugLog("using 'on' method to listen for child exit event");
|
|
261
|
+
child.on("exit", (code) => {
|
|
262
|
+
debugLog("'exit' event", { code });
|
|
263
|
+
if (code != null) {
|
|
264
|
+
runContext.result.code = code;
|
|
265
|
+
}
|
|
266
|
+
debugLog("NOTE: 'exit' event doesn't resolve completion. Waiting for 'close' event.");
|
|
267
|
+
});
|
|
268
|
+
debugLog("using 'on' method to listen for child close event");
|
|
269
|
+
child.on("close", (code) => {
|
|
270
|
+
debugLog("'close' event", { code });
|
|
271
|
+
if (code != null) {
|
|
272
|
+
runContext.result.code = code;
|
|
273
|
+
}
|
|
274
|
+
finish("exited");
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if ("on" in child) {
|
|
278
|
+
debugLog("using 'on' method to listen for child error event");
|
|
279
|
+
child.on("error", (error) => {
|
|
280
|
+
debugLog("'error' event", { error });
|
|
281
|
+
if (typeof error === "object" &&
|
|
282
|
+
error !== null &&
|
|
283
|
+
error.code === "EIO") {
|
|
284
|
+
// not real; process is about to exit
|
|
285
|
+
debugLog("Ignoring spurious EIO error:", error);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
runContext.result.error = error;
|
|
289
|
+
finish("errored");
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
debugLog("child had no 'on' method, so child error event listener wasn't set up");
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
return runContext;
|
|
297
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
export type Options = {
|
|
3
|
+
cwd?: ?string,
|
|
4
|
+
env?: ?{ [varName: string]: string | undefined },
|
|
5
|
+
argv0?: ?string,
|
|
6
|
+
detached?: ?boolean,
|
|
7
|
+
uid?: ?number,
|
|
8
|
+
gid?: ?number,
|
|
9
|
+
shell?: ?boolean | string,
|
|
10
|
+
windowsVerbatimArguments?: ?boolean,
|
|
11
|
+
windowsHide?: ?boolean,
|
|
12
|
+
pty?: ?boolean,
|
|
13
|
+
debug?: ?boolean,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type RunContext = {
|
|
17
|
+
result: {
|
|
18
|
+
stdout: string,
|
|
19
|
+
stderr: string,
|
|
20
|
+
code: null | number,
|
|
21
|
+
error: null | Error,
|
|
22
|
+
},
|
|
23
|
+
completion: Promise<void>,
|
|
24
|
+
debug(): RunContext,
|
|
25
|
+
outputContains(value: string | RegExp): Promise<void>,
|
|
26
|
+
clearOutputContainsBuffer(): void,
|
|
27
|
+
write(data: string | Buffer): void,
|
|
28
|
+
close(stream: "stdin" | "stdout" | "stderr"): void,
|
|
29
|
+
kill(signal?: string): void,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
interface Exports {
|
|
33
|
+
spawn(cmd: string): RunContext;
|
|
34
|
+
spawn(cmd: string, args: Array<string>): RunContext;
|
|
35
|
+
spawn(cmd: string, options: Options): RunContext;
|
|
36
|
+
spawn(cmd: string, args: Array<string>, options: Options): RunContext;
|
|
37
|
+
|
|
38
|
+
sanitizers: Array<(str: string) => string>;
|
|
39
|
+
|
|
40
|
+
allInflightRunContexts: Set<RunContext>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare module.exports: Exports;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sanitizers: Array<(str: string) => string>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sanitizers = void 0;
|
|
7
|
+
const strip_ansi_1 = __importDefault(require("strip-ansi"));
|
|
8
|
+
const find_root_dir_1 = require("./find-root-dir");
|
|
9
|
+
function escapeRegex(str) {
|
|
10
|
+
return str.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
11
|
+
}
|
|
12
|
+
const replaceRootDir = Object.assign(function _replaceRootDir(str) {
|
|
13
|
+
const here = process.cwd();
|
|
14
|
+
const rootDir = replaceRootDir.cache.get(here) || (0, find_root_dir_1.findRootDir)(here);
|
|
15
|
+
replaceRootDir.cache.set(here, rootDir);
|
|
16
|
+
return str.replace(new RegExp(escapeRegex(rootDir), "g"), "<rootDir>");
|
|
17
|
+
}, {
|
|
18
|
+
cache: new Map(),
|
|
19
|
+
});
|
|
20
|
+
exports.sanitizers = [
|
|
21
|
+
strip_ansi_1.default,
|
|
22
|
+
replaceRootDir,
|
|
23
|
+
function replaceCwd(str) {
|
|
24
|
+
return str.replace(new RegExp(escapeRegex(process.cwd()), "g"), "<cwd>");
|
|
25
|
+
},
|
|
26
|
+
function collapseStackTrace(str) {
|
|
27
|
+
return (str
|
|
28
|
+
// replace stack trace lines with a single "at somewhere" line
|
|
29
|
+
// explanation of regexp:
|
|
30
|
+
// newline, optional ansi escape, zero or more whitespace(s), "at",
|
|
31
|
+
// whitespace(s), several non-newline characters... and that whole
|
|
32
|
+
// thing can happen more than once
|
|
33
|
+
.replaceAll(/(?:\n(?:\x1B\[\d+m)?(\s*)at\s+[^\n]+)+/g, "\n$1at somewhere"));
|
|
34
|
+
},
|
|
35
|
+
function omitThrowLineNumber(str) {
|
|
36
|
+
// omit line number from eg. "<rootDir>/dist/match.js:57\n\t\t\t\tthrow"
|
|
37
|
+
return str.replaceAll(/(\.js):\d+(\s+throw)/g, "$1$2");
|
|
38
|
+
},
|
|
39
|
+
];
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "first-base",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Integration testing for CLI applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/suchipi/first-base.git"
|
|
@@ -15,22 +16,13 @@
|
|
|
15
16
|
"strip-ansi": "^5.0.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
|
-
"@
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"babel-jest": "^30.2.0",
|
|
23
|
-
"eslint": "^8.56.0",
|
|
24
|
-
"eslint-config-unobtrusive": "^1.2.5",
|
|
25
|
-
"eslint-plugin-import": "^2.29.1",
|
|
26
|
-
"jest": "^30.2.0",
|
|
27
|
-
"prettier": "^3.8.1"
|
|
19
|
+
"@types/node": "^22.0.0",
|
|
20
|
+
"prettier": "^3.8.1",
|
|
21
|
+
"typescript": "^6.0.2",
|
|
22
|
+
"vitest": "^4.1.2"
|
|
28
23
|
},
|
|
29
24
|
"scripts": {
|
|
30
|
-
"build": "mkdir -p dist; rm -rf dist/*;
|
|
31
|
-
"test": "
|
|
32
|
-
},
|
|
33
|
-
"jest": {
|
|
34
|
-
"prettierPath": null
|
|
25
|
+
"build": "mkdir -p dist; rm -rf dist/*; tsc && rm -f dist/*.test.js dist/*.test.d.ts && cp src/index.js.flow dist/",
|
|
26
|
+
"test": "vitest run"
|
|
35
27
|
}
|
|
36
28
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["src/**/*.ts"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"types": ["node"],
|
|
14
|
+
|
|
15
|
+
// need this because @lydell/node-pty/package.json specifies "types" field
|
|
16
|
+
// but its "exports" field doesn't have "types".
|
|
17
|
+
//
|
|
18
|
+
// We can remove this after this is merged: https://github.com/lydell/node-pty/pull/15
|
|
19
|
+
"moduleResolution": "Node10",
|
|
20
|
+
"ignoreDeprecations": "6.0"
|
|
21
|
+
}
|
|
22
|
+
}
|