reslop 0.2.4 → 0.2.6
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/CHANGELOG.md +25 -1
- package/LICENSE +1 -1
- package/README.md +12 -13
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -12
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +48 -18
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +196 -404
- package/lib/deps.js +240 -399
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +138 -141
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +90 -206
- package/lib/editor.js +98 -55
- package/lib/files.js +86 -103
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +182 -344
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +157 -132
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +16 -26
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +22 -35
- package/lib/highlight/txt.js +9 -22
- package/lib/highlight.js +17 -46
- package/lib/keys.js +62 -85
- package/lib/manifest.js +67 -130
- package/lib/npm-commands.js +119 -189
- package/lib/remote.js +141 -197
- package/lib/render/branches.js +66 -73
- package/lib/render/chrome.js +264 -323
- package/lib/render/commits.js +187 -500
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -228
- package/lib/render/files.js +45 -99
- package/lib/render/frame.js +129 -183
- package/lib/render/notes.js +144 -160
- package/lib/render/npm.js +234 -112
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +131 -170
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -28
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -38
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -78
- package/lib/report-test.js +256 -141
- package/lib/report-tools.js +96 -161
- package/lib/review-merge.js +114 -152
- package/lib/review.js +115 -189
- package/lib/runs.js +225 -0
- package/lib/select.js +89 -75
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +82 -63
- package/lib/session/branches.js +193 -247
- package/lib/session/change-actions.js +171 -214
- package/lib/session/commit.js +280 -342
- package/lib/session/compose.js +559 -674
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +164 -157
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +85 -53
- package/lib/session/items.js +73 -104
- package/lib/session/lifecycle.js +403 -541
- package/lib/session/load.js +48 -68
- package/lib/session/navigation.js +96 -87
- package/lib/session/npm.js +409 -345
- package/lib/session/ops.js +196 -63
- package/lib/session/pointer.js +23 -15
- package/lib/session/progress.js +33 -21
- package/lib/session/review.js +33 -54
- package/lib/session/tasks.js +333 -0
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -136
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +52 -83
- package/lib/session/watch.js +208 -204
- package/lib/session.js +112 -237
- package/lib/update.js +45 -116
- package/lib/utilities.js +67 -89
- package/lib/wrap.js +33 -44
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/update.js
CHANGED
|
@@ -8,7 +8,7 @@ const metautil = require('metautil');
|
|
|
8
8
|
const { jsonParse, isHashObject } = metautil;
|
|
9
9
|
const utilities = require('./utilities.js');
|
|
10
10
|
const { parseVersion, cmpVersion, npmBin } = utilities;
|
|
11
|
-
const { npmOpts, runProc, oneLine } = utilities;
|
|
11
|
+
const { npmOpts, runProc, oneLine, asText } = utilities;
|
|
12
12
|
|
|
13
13
|
const PKG = require('../package.json');
|
|
14
14
|
|
|
@@ -21,21 +21,10 @@ const INSTALL_MS = 120000;
|
|
|
21
21
|
const REGISTRY = 'https://registry.npmjs.org';
|
|
22
22
|
|
|
23
23
|
const RELEASE = /^\d+\.\d+\.\d+$/;
|
|
24
|
-
const
|
|
25
|
-
checkedAt: 0,
|
|
26
|
-
compatible: '',
|
|
27
|
-
major: '',
|
|
28
|
-
skipped: '',
|
|
29
|
-
});
|
|
24
|
+
const NO_TARGETS = { compatible: '', major: '' };
|
|
30
25
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const toInt = (text) => parseInt(text, 10);
|
|
34
|
-
|
|
35
|
-
const isNpmInstall = (pkgDir) => {
|
|
36
|
-
const parent = path.basename(path.dirname(pkgDir));
|
|
37
|
-
return parent === 'node_modules';
|
|
38
|
-
};
|
|
26
|
+
const isNpmInstall = (pkgDir) =>
|
|
27
|
+
path.basename(path.dirname(pkgDir)) === 'node_modules';
|
|
39
28
|
|
|
40
29
|
const cacheFile = (env = process.env) => {
|
|
41
30
|
if (env.XDG_CACHE_HOME) {
|
|
@@ -51,29 +40,26 @@ const cacheFile = (env = process.env) => {
|
|
|
51
40
|
|
|
52
41
|
const readCache = (file) => {
|
|
53
42
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
43
|
+
const data = jsonParse(fs.readFileSync(file, 'utf8'));
|
|
44
|
+
if (isHashObject(data)) {
|
|
45
|
+
return {
|
|
46
|
+
checkedAt: parseInt(data.checkedAt, 10) || 0,
|
|
47
|
+
compatible: asText(data.compatible),
|
|
48
|
+
major: asText(data.major) || asText(data.latest),
|
|
49
|
+
skipped: asText(data.skipped) || asText(data.handled),
|
|
50
|
+
};
|
|
58
51
|
}
|
|
59
|
-
const checkedAt = toInt(data.checkedAt) || 0;
|
|
60
|
-
const compatible = asText(data.compatible);
|
|
61
|
-
const major = asText(data.major) || asText(data.latest);
|
|
62
|
-
const skipped = asText(data.skipped) || asText(data.handled);
|
|
63
|
-
return { checkedAt, compatible, major, skipped };
|
|
64
52
|
} catch {
|
|
65
|
-
|
|
53
|
+
// missing or unreadable cache
|
|
66
54
|
}
|
|
55
|
+
return { checkedAt: 0, compatible: '', major: '', skipped: '' };
|
|
67
56
|
};
|
|
68
57
|
|
|
69
58
|
const writeCache = (file, data) => {
|
|
59
|
+
const { checkedAt, compatible, major, skipped } = data;
|
|
60
|
+
const body = JSON.stringify({ checkedAt, compatible, major, skipped });
|
|
70
61
|
try {
|
|
71
62
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
72
|
-
const checkedAt = data.checkedAt ?? 0;
|
|
73
|
-
const compatible = data.compatible ?? '';
|
|
74
|
-
const major = data.major ?? '';
|
|
75
|
-
const skipped = data.skipped ?? '';
|
|
76
|
-
const body = JSON.stringify({ checkedAt, compatible, major, skipped });
|
|
77
63
|
fs.writeFileSync(file, `${body}\n`);
|
|
78
64
|
} catch {
|
|
79
65
|
// ignore cache write errors
|
|
@@ -81,76 +67,43 @@ const writeCache = (file, data) => {
|
|
|
81
67
|
};
|
|
82
68
|
|
|
83
69
|
const isFresh = (cache, now, interval = CHECK_INTERVAL_MS) => {
|
|
84
|
-
if (!cache ||
|
|
85
|
-
if (now < cache.checkedAt) return false;
|
|
70
|
+
if (!cache.checkedAt || now < cache.checkedAt) return false;
|
|
86
71
|
return now - cache.checkedAt < interval;
|
|
87
72
|
};
|
|
88
73
|
|
|
89
74
|
const listVersions = (data) => {
|
|
90
75
|
if (!data || typeof data !== 'object') return [];
|
|
91
76
|
const names = [];
|
|
92
|
-
|
|
93
|
-
if (isHashObject(map)) {
|
|
94
|
-
for (const name of Object.keys(map)) names.push(name);
|
|
95
|
-
}
|
|
77
|
+
if (isHashObject(data.versions)) names.push(...Object.keys(data.versions));
|
|
96
78
|
const tags = data['dist-tags'];
|
|
97
79
|
if (tags && typeof tags.latest === 'string') names.push(tags.latest);
|
|
98
80
|
if (typeof data.version === 'string') names.push(data.version);
|
|
99
|
-
|
|
100
|
-
for (const name of names) {
|
|
101
|
-
if (!RELEASE.test(name)) continue;
|
|
102
|
-
if (listed.includes(name)) continue;
|
|
103
|
-
listed.push(name);
|
|
104
|
-
}
|
|
105
|
-
return listed;
|
|
81
|
+
return [...new Set(names)].filter((name) => RELEASE.test(name));
|
|
106
82
|
};
|
|
107
83
|
|
|
108
|
-
const
|
|
84
|
+
const newer = (best, name) => {
|
|
109
85
|
if (!best) return name;
|
|
110
|
-
|
|
111
|
-
if (!prev || cmpVersion(ver, prev) > 0) return name;
|
|
112
|
-
return best;
|
|
86
|
+
return cmpVersion(parseVersion(name), parseVersion(best)) > 0 ? name : best;
|
|
113
87
|
};
|
|
114
88
|
|
|
115
89
|
const pickTargets = (current, names) => {
|
|
116
90
|
const from = parseVersion(current);
|
|
117
|
-
if (!from) return
|
|
91
|
+
if (!from) return NO_TARGETS;
|
|
118
92
|
let compatible = '';
|
|
119
93
|
let major = '';
|
|
120
94
|
for (const name of names) {
|
|
121
95
|
const ver = parseVersion(name);
|
|
122
|
-
if (!ver) continue;
|
|
123
96
|
if (cmpVersion(ver, from) <= 0) continue;
|
|
124
|
-
if (ver.major === from.major)
|
|
125
|
-
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (ver.major > from.major) major = pickNewerName(major, name, ver);
|
|
97
|
+
if (ver.major === from.major) compatible = newer(compatible, name);
|
|
98
|
+
else if (ver.major > from.major) major = newer(major, name);
|
|
129
99
|
}
|
|
130
100
|
return { compatible, major };
|
|
131
101
|
};
|
|
132
102
|
|
|
133
|
-
const
|
|
134
|
-
const from = parseVersion(current);
|
|
135
|
-
const to = parseVersion(latest);
|
|
136
|
-
if (!from || !to || cmpVersion(to, from) <= 0) {
|
|
137
|
-
return { compatible: '', major: '' };
|
|
138
|
-
}
|
|
139
|
-
if (to.major > from.major) return { compatible: '', major: latest };
|
|
140
|
-
return { compatible: latest, major: '' };
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const planUpdate = (current, extra = {}) => {
|
|
103
|
+
const planUpdate = (current, targets) => {
|
|
144
104
|
const from = parseVersion(current);
|
|
145
105
|
if (!from) return { action: 'none' };
|
|
146
|
-
|
|
147
|
-
let major = extra.major ?? '';
|
|
148
|
-
const skipped = extra.skipped ?? extra.handled ?? '';
|
|
149
|
-
if (!compatible && !major && extra.latest) {
|
|
150
|
-
const classified = classifyLatest(current, extra.latest);
|
|
151
|
-
compatible = classified.compatible;
|
|
152
|
-
major = classified.major;
|
|
153
|
-
}
|
|
106
|
+
const { compatible, major, skipped } = targets;
|
|
154
107
|
const compatVer = parseVersion(compatible);
|
|
155
108
|
if (compatVer && compatVer.major === from.major) {
|
|
156
109
|
if (cmpVersion(compatVer, from) > 0) {
|
|
@@ -164,53 +117,40 @@ const planUpdate = (current, extra = {}) => {
|
|
|
164
117
|
return { action: 'confirm', current, latest: major };
|
|
165
118
|
};
|
|
166
119
|
|
|
167
|
-
const isEnabled = (options
|
|
168
|
-
if (options.enabled ===
|
|
169
|
-
|
|
170
|
-
const pkgDir = options.pkgDir ?? PKG_DIR;
|
|
171
|
-
return isNpmInstall(pkgDir);
|
|
120
|
+
const isEnabled = (options) => {
|
|
121
|
+
if (typeof options.enabled === 'boolean') return options.enabled;
|
|
122
|
+
return isNpmInstall(options.pkgDir ?? PKG_DIR);
|
|
172
123
|
};
|
|
173
124
|
|
|
174
|
-
const fetchTargets = async (current, options
|
|
175
|
-
const empty = { compatible: '', major: '' };
|
|
125
|
+
const fetchTargets = async (current, options) => {
|
|
176
126
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
177
|
-
if (typeof fetchImpl !== 'function') return
|
|
178
|
-
const
|
|
179
|
-
const url = `${REGISTRY}/${encoded}`;
|
|
127
|
+
if (typeof fetchImpl !== 'function') return NO_TARGETS;
|
|
128
|
+
const url = `${REGISTRY}/${encodeURIComponent(PKG_NAME)}`;
|
|
180
129
|
const headers = { accept: 'application/json' };
|
|
181
130
|
const signal = options.signal ?? AbortSignal.timeout(FETCH_MS);
|
|
182
131
|
const response = await fetchImpl(url, { headers, signal });
|
|
183
|
-
if (!response || !response.ok) return
|
|
132
|
+
if (!response || !response.ok) return NO_TARGETS;
|
|
184
133
|
const data = await response.json();
|
|
185
134
|
return pickTargets(current, listVersions(data));
|
|
186
135
|
};
|
|
187
136
|
|
|
188
|
-
const npmInstall = async (spec, extra = {}) => {
|
|
189
|
-
const timeout = extra.timeout ?? INSTALL_MS;
|
|
190
|
-
const result = await runProc(
|
|
191
|
-
npmBin(),
|
|
192
|
-
['install', '-g', spec],
|
|
193
|
-
npmOpts({ timeout }),
|
|
194
|
-
);
|
|
195
|
-
if (result.error) throw result.error;
|
|
196
|
-
if (result.status !== 0) {
|
|
197
|
-
throw new Error(oneLine(result.stderr, 'npm install failed'));
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
137
|
const installUpdate = async (version, options = {}) => {
|
|
202
138
|
if (typeof options.install === 'function') {
|
|
203
139
|
await options.install(version);
|
|
204
140
|
return;
|
|
205
141
|
}
|
|
206
|
-
const
|
|
207
|
-
|
|
142
|
+
const timeout = options.timeout ?? INSTALL_MS;
|
|
143
|
+
const args = ['install', '-g', `${PKG_NAME}@${version}`];
|
|
144
|
+
const result = await runProc(npmBin(), args, npmOpts({ timeout }));
|
|
145
|
+
if (result.error) throw result.error;
|
|
146
|
+
if (result.status !== 0) {
|
|
147
|
+
throw new Error(oneLine(result.stderr, 'npm install failed'));
|
|
148
|
+
}
|
|
208
149
|
};
|
|
209
150
|
|
|
210
151
|
const markSkipped = (version, options = {}) => {
|
|
211
152
|
const file = options.cacheFile ?? cacheFile(options.env);
|
|
212
|
-
|
|
213
|
-
writeCache(file, { ...cache, skipped: version });
|
|
153
|
+
writeCache(file, { ...readCache(file), skipped: version });
|
|
214
154
|
};
|
|
215
155
|
|
|
216
156
|
const checkUpdate = async (options = {}) => {
|
|
@@ -218,39 +158,28 @@ const checkUpdate = async (options = {}) => {
|
|
|
218
158
|
const now = Date.now();
|
|
219
159
|
const current = options.current ?? PKG_VERSION;
|
|
220
160
|
const file = options.cacheFile ?? cacheFile(options.env);
|
|
221
|
-
const interval = options.interval ?? CHECK_INTERVAL_MS;
|
|
222
161
|
const cache = readCache(file);
|
|
223
|
-
if (isFresh(cache, now, interval)) {
|
|
162
|
+
if (isFresh(cache, now, options.interval)) {
|
|
224
163
|
const plan = planUpdate(current, cache);
|
|
225
|
-
|
|
226
|
-
return plan;
|
|
164
|
+
return plan.action === 'install' ? { action: 'none' } : plan;
|
|
227
165
|
}
|
|
228
|
-
let targets =
|
|
166
|
+
let targets = NO_TARGETS;
|
|
229
167
|
try {
|
|
230
168
|
targets = await fetchTargets(current, options);
|
|
231
169
|
} catch {
|
|
232
170
|
// ignore registry errors
|
|
233
171
|
}
|
|
234
|
-
const next = {
|
|
235
|
-
...cache,
|
|
236
|
-
checkedAt: now,
|
|
237
|
-
compatible: targets.compatible,
|
|
238
|
-
major: targets.major,
|
|
239
|
-
};
|
|
240
172
|
if (!targets.compatible && !targets.major) {
|
|
241
173
|
writeCache(file, { ...cache, checkedAt: now });
|
|
242
174
|
return { action: 'none' };
|
|
243
175
|
}
|
|
176
|
+
const next = { ...cache, checkedAt: now, ...targets };
|
|
244
177
|
writeCache(file, next);
|
|
245
178
|
return planUpdate(current, next);
|
|
246
179
|
};
|
|
247
180
|
|
|
248
181
|
module.exports = {
|
|
249
182
|
CHECK_INTERVAL_MS,
|
|
250
|
-
PKG_NAME,
|
|
251
|
-
PKG_VERSION,
|
|
252
|
-
parseVersion,
|
|
253
|
-
cmpVersion,
|
|
254
183
|
isNpmInstall,
|
|
255
184
|
cacheFile,
|
|
256
185
|
readCache,
|
package/lib/utilities.js
CHANGED
|
@@ -9,16 +9,12 @@ const IS_WIN = process.platform === 'win32';
|
|
|
9
9
|
const DEFAULT_ENCODING = 'utf8';
|
|
10
10
|
const DEFAULT_MAX_BUFFER = 32 * 1024 * 1024;
|
|
11
11
|
|
|
12
|
-
const UNIX_CLIP = [
|
|
13
|
-
{ cmd: 'wl-copy', args: [] },
|
|
14
|
-
{ cmd: 'xclip', args: ['-selection', 'clipboard'] },
|
|
15
|
-
{ cmd: 'xsel', args: ['--clipboard', '--input'] },
|
|
16
|
-
];
|
|
17
|
-
|
|
18
12
|
const parseVersion = (text) => {
|
|
19
13
|
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(`${text ?? ''}`);
|
|
20
14
|
if (!match) return null;
|
|
21
|
-
const
|
|
15
|
+
const major = Number(match[1]);
|
|
16
|
+
const minor = Number(match[2]);
|
|
17
|
+
const patch = Number(match[3]);
|
|
22
18
|
return { major, minor, patch };
|
|
23
19
|
};
|
|
24
20
|
|
|
@@ -30,20 +26,30 @@ const cmpVersion = (left, right) =>
|
|
|
30
26
|
const oneLine = (text, fallback = '') =>
|
|
31
27
|
split(`${text ?? ''}`.trim(), '\n')[0] || fallback;
|
|
32
28
|
|
|
33
|
-
const
|
|
34
|
-
|
|
29
|
+
const asText = (value) => (typeof value === 'string' ? value : '');
|
|
30
|
+
|
|
31
|
+
const unquote = (value) => {
|
|
32
|
+
const text = `${value ?? ''}`;
|
|
33
|
+
if (text.length < 2) return text;
|
|
34
|
+
const open = text[0];
|
|
35
|
+
const quoted = open === text.at(-1) && (open === '"' || open === '\x27');
|
|
36
|
+
return quoted ? text.slice(1, -1) : text;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const pad2 = (n) => `${n}`.padStart(2, '0');
|
|
40
|
+
|
|
41
|
+
const dateStamp = (date) => {
|
|
42
|
+
const month = pad2(date.getMonth() + 1);
|
|
43
|
+
return `${date.getFullYear()}-${month}-${pad2(date.getDate())}`;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const npmBin = () => (IS_WIN ? 'npm.cmd' : 'npm');
|
|
35
47
|
|
|
36
48
|
const spawnBase = (extra = {}) => ({ windowsHide: true, ...extra });
|
|
37
49
|
|
|
38
50
|
const npmOpts = (extra = {}, platform = process.platform) =>
|
|
39
51
|
spawnBase({ shell: platform === 'win32', ...extra });
|
|
40
52
|
|
|
41
|
-
const clipTools = (platform = process.platform) => {
|
|
42
|
-
if (platform === 'win32') return [{ cmd: 'clip', args: [] }];
|
|
43
|
-
if (platform === 'darwin') return [{ cmd: 'pbcopy', args: [] }];
|
|
44
|
-
return UNIX_CLIP;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
53
|
const listen = (emitter, event, handler) => {
|
|
48
54
|
if (!emitter || typeof emitter.on !== 'function') return () => {};
|
|
49
55
|
emitter.on(event, handler);
|
|
@@ -74,69 +80,36 @@ const watchResize = (stdout, proc, onResize, signal) => {
|
|
|
74
80
|
return stop;
|
|
75
81
|
};
|
|
76
82
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
error.code = 'ABORT';
|
|
80
|
-
return error;
|
|
81
|
-
};
|
|
83
|
+
const codeError = (message, code) =>
|
|
84
|
+
Object.assign(new Error(message), { code });
|
|
82
85
|
|
|
83
|
-
const
|
|
86
|
+
const abortError = () => codeError('aborted', 'ABORT');
|
|
84
87
|
|
|
85
|
-
const
|
|
86
|
-
if (value === undefined || value === null) return '';
|
|
87
|
-
if (typeof value === 'string') return value;
|
|
88
|
-
return `${value}`;
|
|
89
|
-
};
|
|
88
|
+
const isAbort = (error) => !!(error && error.code === 'ABORT');
|
|
90
89
|
|
|
91
|
-
const procResult = (values = {}) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
};
|
|
90
|
+
const procResult = (values = {}) => ({
|
|
91
|
+
status: values.status ?? null,
|
|
92
|
+
stdout: `${values.stdout ?? ''}`,
|
|
93
|
+
stderr: `${values.stderr ?? ''}`,
|
|
94
|
+
error: values.error ?? null,
|
|
95
|
+
});
|
|
98
96
|
|
|
99
|
-
const
|
|
100
|
-
const encoding = options.encoding ?? DEFAULT_ENCODING;
|
|
101
|
-
const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
|
|
97
|
+
const runProcSync = (cmd, args, options = {}) => {
|
|
102
98
|
const opts = spawnBase({
|
|
103
99
|
cwd: options.cwd,
|
|
104
100
|
env: options.env,
|
|
105
|
-
encoding,
|
|
101
|
+
encoding: options.encoding ?? DEFAULT_ENCODING,
|
|
106
102
|
input: options.input,
|
|
107
|
-
maxBuffer,
|
|
103
|
+
maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
108
104
|
timeout: options.timeout,
|
|
109
105
|
});
|
|
110
106
|
if (options.shell) opts.shell = true;
|
|
111
|
-
return opts;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const runProcSync = (cmd, args, options = {}) => {
|
|
115
|
-
const result = spawnSync(cmd, args, spawnOptions(options));
|
|
116
|
-
return procResult(result);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const attachOutput = (stream, encoding, onChunk) => {
|
|
120
|
-
if (!stream) return;
|
|
121
|
-
stream.setEncoding(encoding);
|
|
122
|
-
stream.on('data', onChunk);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const writeInput = (child, input) => {
|
|
126
|
-
if (!child.stdin) return;
|
|
127
|
-
try {
|
|
128
|
-
if (input) child.stdin.end(input);
|
|
129
|
-
else child.stdin.end();
|
|
130
|
-
} catch {
|
|
131
|
-
// closed
|
|
132
|
-
}
|
|
107
|
+
return procResult(spawnSync(cmd, args, opts));
|
|
133
108
|
};
|
|
134
109
|
|
|
135
110
|
const listenChild = (child, options, resolve) => {
|
|
136
111
|
const encoding = options.encoding ?? DEFAULT_ENCODING;
|
|
137
112
|
const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
|
|
138
|
-
const timeout = options.timeout;
|
|
139
|
-
const input = options.input;
|
|
140
113
|
const signal = options.signal;
|
|
141
114
|
let stdout = '';
|
|
142
115
|
let stderr = '';
|
|
@@ -146,46 +119,49 @@ const listenChild = (child, options, resolve) => {
|
|
|
146
119
|
const finish = (result) => {
|
|
147
120
|
if (settled) return;
|
|
148
121
|
settled = true;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (signal && onAbort) {
|
|
152
|
-
signal.removeEventListener('abort', onAbort);
|
|
153
|
-
}
|
|
154
|
-
onAbort = null;
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
if (signal) signal.removeEventListener('abort', onAbort);
|
|
155
124
|
resolve(result);
|
|
156
125
|
};
|
|
157
|
-
|
|
126
|
+
const fail = (error) => {
|
|
158
127
|
child.kill();
|
|
159
|
-
finish(procResult({ stdout, stderr, error
|
|
128
|
+
finish(procResult({ stdout, stderr, error }));
|
|
160
129
|
};
|
|
161
|
-
|
|
162
|
-
|
|
130
|
+
onAbort = () => fail(abortError());
|
|
131
|
+
const append = (chunk, isOut) => {
|
|
132
|
+
if (isOut) stdout += chunk;
|
|
163
133
|
else stderr += chunk;
|
|
164
|
-
if (stdout.length + stderr.length
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
error.code = 'ENOBUFS';
|
|
168
|
-
finish(procResult({ stdout, stderr, error }));
|
|
134
|
+
if (stdout.length + stderr.length > maxBuffer) {
|
|
135
|
+
fail(codeError('maxBuffer exceeded', 'ENOBUFS'));
|
|
136
|
+
}
|
|
169
137
|
};
|
|
170
138
|
if (signal) signal.addEventListener('abort', onAbort);
|
|
171
|
-
if (timeout) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const error = new Error('timed out');
|
|
175
|
-
error.code = 'ETIMEDOUT';
|
|
176
|
-
finish(procResult({ stdout, stderr, error }));
|
|
177
|
-
}, timeout);
|
|
139
|
+
if (options.timeout) {
|
|
140
|
+
const onTimeout = () => fail(codeError('timed out', 'ETIMEDOUT'));
|
|
141
|
+
timer = setTimeout(onTimeout, options.timeout);
|
|
178
142
|
}
|
|
179
143
|
if (signal && signal.aborted) onAbort();
|
|
180
|
-
|
|
181
|
-
|
|
144
|
+
if (child.stdout) {
|
|
145
|
+
child.stdout.setEncoding(encoding);
|
|
146
|
+
child.stdout.on('data', (chunk) => append(chunk, true));
|
|
147
|
+
}
|
|
148
|
+
if (child.stderr) {
|
|
149
|
+
child.stderr.setEncoding(encoding);
|
|
150
|
+
child.stderr.on('data', (chunk) => append(chunk, false));
|
|
151
|
+
}
|
|
182
152
|
child.on('error', (error) => {
|
|
183
153
|
finish(procResult({ stdout, stderr, error }));
|
|
184
154
|
});
|
|
185
155
|
child.on('close', (status) => {
|
|
186
156
|
finish(procResult({ status, stdout, stderr }));
|
|
187
157
|
});
|
|
188
|
-
|
|
158
|
+
if (!child.stdin) return;
|
|
159
|
+
try {
|
|
160
|
+
if (options.input) child.stdin.end(options.input);
|
|
161
|
+
else child.stdin.end();
|
|
162
|
+
} catch {
|
|
163
|
+
// closed
|
|
164
|
+
}
|
|
189
165
|
};
|
|
190
166
|
|
|
191
167
|
const runProc = (cmd, args, options = {}) =>
|
|
@@ -214,15 +190,17 @@ module.exports = {
|
|
|
214
190
|
parseVersion,
|
|
215
191
|
cmpVersion,
|
|
216
192
|
oneLine,
|
|
193
|
+
asText,
|
|
194
|
+
unquote,
|
|
195
|
+
pad2,
|
|
196
|
+
dateStamp,
|
|
217
197
|
npmBin,
|
|
218
198
|
spawnBase,
|
|
219
199
|
npmOpts,
|
|
220
|
-
clipTools,
|
|
221
200
|
listen,
|
|
222
201
|
watchResize,
|
|
223
202
|
abortError,
|
|
224
203
|
isAbort,
|
|
225
|
-
procResult,
|
|
226
204
|
runProcSync,
|
|
227
205
|
runProc,
|
|
228
206
|
};
|
package/lib/wrap.js
CHANGED
|
@@ -12,53 +12,37 @@ const wrapPlain = (text, width) => {
|
|
|
12
12
|
let col = 0;
|
|
13
13
|
let lastBreak = -1;
|
|
14
14
|
let i = 0;
|
|
15
|
-
const slice = (from, to) => {
|
|
16
|
-
let out = '';
|
|
17
|
-
for (let k = from; k < to; k++) out += parts[k];
|
|
18
|
-
return out;
|
|
19
|
-
};
|
|
20
|
-
const flush = (end) => {
|
|
21
|
-
rows.push(slice(start, end));
|
|
22
|
-
start = end;
|
|
23
|
-
col = 0;
|
|
24
|
-
lastBreak = -1;
|
|
25
|
-
};
|
|
26
15
|
while (i < parts.length) {
|
|
27
16
|
const ch = parts[i];
|
|
28
17
|
const w = graphemeWidth(ch, col);
|
|
29
18
|
if (col + w > width && col > 0) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
const end = lastBreak >= start ? lastBreak + 1 : i;
|
|
20
|
+
rows.push(parts.slice(start, end).join(''));
|
|
21
|
+
start = end;
|
|
22
|
+
i = end;
|
|
23
|
+
col = 0;
|
|
24
|
+
lastBreak = -1;
|
|
36
25
|
continue;
|
|
37
26
|
}
|
|
38
27
|
col += w;
|
|
39
28
|
if (ch === ' ' || ch === '\t') lastBreak = i;
|
|
40
29
|
i += 1;
|
|
41
30
|
}
|
|
42
|
-
rows.push(slice(start
|
|
31
|
+
rows.push(parts.slice(start).join(''));
|
|
43
32
|
return rows;
|
|
44
33
|
};
|
|
45
34
|
|
|
46
35
|
const wrapDoc = (text, width) => {
|
|
47
36
|
const rows = [];
|
|
48
|
-
const parts = text.split('\n');
|
|
49
37
|
let offset = 0;
|
|
50
|
-
for (
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
rows.push({ start: offset + used, text: row });
|
|
56
|
-
used += row.length;
|
|
38
|
+
for (const part of text.split('\n')) {
|
|
39
|
+
let start = offset;
|
|
40
|
+
for (const row of wrapPlain(part, width)) {
|
|
41
|
+
rows.push({ start, text: row });
|
|
42
|
+
start += row.length;
|
|
57
43
|
}
|
|
58
|
-
offset += part.length;
|
|
59
|
-
if (p < parts.length - 1) offset += 1;
|
|
44
|
+
offset += part.length + 1;
|
|
60
45
|
}
|
|
61
|
-
if (!rows.length) rows.push({ start: 0, text: '' });
|
|
62
46
|
return rows;
|
|
63
47
|
};
|
|
64
48
|
|
|
@@ -69,9 +53,8 @@ const cursorInWrap = (text, cursor, width) => {
|
|
|
69
53
|
const rows = wrapDoc(text, width);
|
|
70
54
|
const clamped = Math.max(0, Math.min(cursor, text.length));
|
|
71
55
|
let row = rows.length - 1;
|
|
72
|
-
for (let i = 0; i < rows.length; i++) {
|
|
73
|
-
|
|
74
|
-
if (clamped < next) {
|
|
56
|
+
for (let i = 0; i < rows.length - 1; i++) {
|
|
57
|
+
if (clamped < rows[i + 1].start) {
|
|
75
58
|
row = i;
|
|
76
59
|
break;
|
|
77
60
|
}
|
|
@@ -81,16 +64,25 @@ const cursorInWrap = (text, cursor, width) => {
|
|
|
81
64
|
return { row, col: visibleWidth(line.text.slice(0, take)) };
|
|
82
65
|
};
|
|
83
66
|
|
|
84
|
-
const
|
|
67
|
+
const colIndex = (text, col) => {
|
|
85
68
|
let used = 0;
|
|
86
|
-
let
|
|
87
|
-
for (const ch of graphemes(
|
|
69
|
+
let index = 0;
|
|
70
|
+
for (const ch of graphemes(text)) {
|
|
88
71
|
const w = graphemeWidth(ch, used);
|
|
89
72
|
if (used + w > col) break;
|
|
90
73
|
used += w;
|
|
91
|
-
|
|
74
|
+
index += ch.length;
|
|
92
75
|
}
|
|
93
|
-
|
|
76
|
+
return index;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const indexAtRow = (start, text, col) => {
|
|
80
|
+
if (col <= 0) return start;
|
|
81
|
+
return start + colIndex(`${text ?? ''}`, col);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const indexAtWrapCol = (row, col, limit) => {
|
|
85
|
+
const pos = row.start + colIndex(row.text, col);
|
|
94
86
|
if (pos < limit) return pos;
|
|
95
87
|
if (pos <= row.start) return row.start;
|
|
96
88
|
const parts = graphemes(row.text);
|
|
@@ -100,15 +92,11 @@ const indexAtWrapCol = (row, col, limit) => {
|
|
|
100
92
|
const wrapMove = (text, cursor, width, delta, wantCol) => {
|
|
101
93
|
const rows = wrapDoc(text, width);
|
|
102
94
|
const pos = cursorInWrap(text, cursor, width);
|
|
103
|
-
|
|
104
|
-
if (next < 0) next = 0;
|
|
105
|
-
if (next >= rows.length) next = rows.length - 1;
|
|
95
|
+
const next = Math.min(Math.max(pos.row + delta, 0), rows.length - 1);
|
|
106
96
|
const col = wantCol ?? pos.col;
|
|
107
97
|
if (next === pos.row) return { cursor, col };
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
if (next + 1 < rows.length) limit = rows[next + 1].start;
|
|
111
|
-
return { cursor: indexAtWrapCol(row, col, limit), col };
|
|
98
|
+
const limit = next + 1 < rows.length ? rows[next + 1].start : text.length + 1;
|
|
99
|
+
return { cursor: indexAtWrapCol(rows[next], col, limit), col };
|
|
112
100
|
};
|
|
113
101
|
|
|
114
102
|
module.exports = {
|
|
@@ -116,5 +104,6 @@ module.exports = {
|
|
|
116
104
|
wrapDoc,
|
|
117
105
|
wrapMultiline,
|
|
118
106
|
cursorInWrap,
|
|
107
|
+
indexAtRow,
|
|
119
108
|
wrapMove,
|
|
120
109
|
};
|