burgee 0.6.0 → 0.7.0

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.
@@ -1,221 +1,37 @@
1
+ import { strip, width, wrap } from "linegauge";
2
+ import { host } from "../runtime.js";
1
3
  /**
2
- * cliui 9 — the column layout yargs' usage renders through — with the string-width,
3
- * strip-ansi and wrap-ansi it depends on, ported for `burgee/yargs`. The wrapping
4
+ * cliui 9 — the column layout yargs' usage renders through — with the wrap-ansi it depends
5
+ * on, ported for `burgee/yargs`. Width and escape-stripping come from linegauge. The wrapping
4
6
  * arithmetic is byte-for-byte the upstream's: yargs' usage tests compare whole help
5
7
  * screens.
6
8
  */
7
- const ANSI_PATTERN = '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))';
8
- export function stripAnsi(str) {
9
- return typeof str === 'string' ? str.replace(new RegExp(ANSI_PATTERN, 'g'), '') : str;
10
- }
11
- const segmenter = new Intl.Segmenter();
12
- const EMOJI = new RegExp('^\\p{RGI_Emoji}$', 'v');
13
- function isWide(code) {
14
- return ((code >= 0x1100 && code <= 0x115f) ||
15
- code === 0x2329 ||
16
- code === 0x232a ||
17
- (code >= 0x2e80 && code <= 0x303e) ||
18
- (code >= 0x3041 && code <= 0x33ff) ||
19
- (code >= 0x3400 && code <= 0x4dbf) ||
20
- (code >= 0x4e00 && code <= 0x9fff) ||
21
- (code >= 0xa000 && code <= 0xa4cf) ||
22
- (code >= 0xa960 && code <= 0xa97f) ||
23
- (code >= 0xac00 && code <= 0xd7a3) ||
24
- (code >= 0xf900 && code <= 0xfaff) ||
25
- (code >= 0xfe10 && code <= 0xfe19) ||
26
- (code >= 0xfe30 && code <= 0xfe6f) ||
27
- (code >= 0xff00 && code <= 0xff60) ||
28
- (code >= 0xffe0 && code <= 0xffe6) ||
29
- (code >= 0x1f300 && code <= 0x1f64f) ||
30
- (code >= 0x1f900 && code <= 0x1f9ff) ||
31
- (code >= 0x20000 && code <= 0x3fffd));
32
- }
33
- export function stringWidth(input) {
34
- if (typeof input !== 'string' || input.length === 0)
35
- return 0;
36
- const string = stripAnsi(input);
37
- if (string.length === 0)
38
- return 0;
39
- let width = 0;
40
- for (const { segment } of segmenter.segment(string)) {
41
- const code = segment.codePointAt(0);
42
- if (code <= 0x1f || (code >= 0x7f && code <= 0x9f))
43
- continue;
44
- if ((code >= 0x200b && code <= 0x200f) || code === 0xfeff)
45
- continue;
46
- if (code >= 0x300 && code <= 0x36f)
47
- continue;
48
- if (EMOJI.test(segment)) {
49
- width += 2;
50
- continue;
51
- }
52
- width += isWide(code) ? 2 : 1;
53
- }
54
- return width;
55
- }
56
- /** ansi-styles' open→close map, the part wrap-ansi reads. */
57
- function closeCode(code) {
58
- if (code === 1 || code === 2)
59
- return 22;
60
- if (code === 3)
61
- return 23;
62
- if (code === 4)
63
- return 24;
64
- if (code === 7)
65
- return 27;
66
- if (code === 8)
67
- return 28;
68
- if (code === 9)
69
- return 29;
70
- if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97))
71
- return 39;
72
- if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107))
73
- return 49;
74
- return undefined;
75
- }
76
- const ESC = '\u001B';
77
- const ESCAPES = new Set([ESC, '\u009B']);
78
- const END_CODE = 39;
79
- const ANSI_ESCAPE_BELL = '\u0007';
80
- const ANSI_CSI = '[';
81
- const ANSI_OSC = ']';
82
- const ANSI_SGR_TERMINATOR = 'm';
83
- const ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
84
- const wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
85
- const wrapAnsiHyperlink = (uri) => `${ESC}${ANSI_ESCAPE_LINK}${uri}${ANSI_ESCAPE_BELL}`;
86
- const wordLengths = (string) => string.split(' ').map((character) => stringWidth(character));
87
- function wrapWord(rows, word, columns) {
88
- const characters = [...word];
89
- let isInsideEscape = false;
90
- let isInsideLinkEscape = false;
91
- let visible = stringWidth(stripAnsi(rows.at(-1)));
92
- for (const [index, character] of characters.entries()) {
93
- const characterLength = stringWidth(character);
94
- if (visible + characterLength <= columns)
95
- rows[rows.length - 1] += character;
96
- else {
97
- rows.push(character);
98
- visible = 0;
99
- }
100
- if (ESCAPES.has(character)) {
101
- isInsideEscape = true;
102
- isInsideLinkEscape = characters.slice(index + 1).join('').startsWith(ANSI_ESCAPE_LINK);
103
- }
104
- if (isInsideEscape) {
105
- if (isInsideLinkEscape) {
106
- if (character === ANSI_ESCAPE_BELL) {
107
- isInsideEscape = false;
108
- isInsideLinkEscape = false;
109
- }
110
- }
111
- else if (character === ANSI_SGR_TERMINATOR)
112
- isInsideEscape = false;
113
- continue;
114
- }
115
- visible += characterLength;
116
- if (visible === columns && index < characters.length - 1) {
117
- rows.push('');
118
- visible = 0;
119
- }
120
- }
121
- if (!visible && rows.at(-1).length > 0 && rows.length > 1)
122
- rows[rows.length - 2] += rows.pop();
123
- }
124
- function stringVisibleTrimSpacesRight(string) {
125
- const words = string.split(' ');
126
- let last = words.length;
127
- while (last > 0) {
128
- if (stringWidth(words[last - 1]) > 0)
129
- break;
130
- last--;
131
- }
132
- if (last === words.length)
133
- return string;
134
- return words.slice(0, last).join(' ') + words.slice(last).join('');
135
- }
136
- function execWrap(string, columns, options = {}) {
137
- if (options.trim !== false && string.trim() === '')
138
- return '';
139
- let returnValue = '';
140
- let escapeCode;
141
- let escapeUrl;
142
- const lengths = wordLengths(string);
143
- let rows = [''];
144
- for (const [index, word] of string.split(' ').entries()) {
145
- if (options.trim !== false)
146
- rows[rows.length - 1] = rows.at(-1).trimStart();
147
- let rowLength = stringWidth(rows.at(-1));
148
- if (index !== 0) {
149
- if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
150
- rows.push('');
151
- rowLength = 0;
152
- }
153
- if (rowLength > 0 || options.trim === false) {
154
- rows[rows.length - 1] += ' ';
155
- rowLength++;
156
- }
157
- }
158
- const length = lengths[index];
159
- if (options.hard && length > columns) {
160
- const remainingColumns = columns - rowLength;
161
- const breaksStartingThisLine = 1 + Math.floor((length - remainingColumns - 1) / columns);
162
- const breaksStartingNextLine = Math.floor((length - 1) / columns);
163
- if (breaksStartingNextLine < breaksStartingThisLine)
164
- rows.push('');
165
- wrapWord(rows, word, columns);
166
- continue;
167
- }
168
- if (rowLength + length > columns && rowLength > 0 && length > 0) {
169
- if (options.wordWrap === false && rowLength < columns) {
170
- wrapWord(rows, word, columns);
171
- continue;
172
- }
173
- rows.push('');
174
- }
175
- if (rowLength + length > columns && options.wordWrap === false) {
176
- wrapWord(rows, word, columns);
177
- continue;
178
- }
179
- rows[rows.length - 1] += word;
180
- }
181
- if (options.trim !== false)
182
- rows = rows.map(stringVisibleTrimSpacesRight);
183
- const pre = [...rows.join('\n')];
184
- for (const [index, character] of pre.entries()) {
185
- returnValue += character;
186
- if (ESCAPES.has(character)) {
187
- const found = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`).exec(pre.slice(index).join(''));
188
- const groups = found?.groups ?? {};
189
- if (groups.code !== undefined) {
190
- const code = Number.parseFloat(groups.code);
191
- escapeCode = code === END_CODE ? undefined : code;
192
- }
193
- else if (groups.uri !== undefined)
194
- escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
195
- }
196
- const code = closeCode(Number(escapeCode));
197
- if (pre[index + 1] === '\n') {
198
- if (escapeUrl)
199
- returnValue += wrapAnsiHyperlink('');
200
- if (escapeCode && code)
201
- returnValue += wrapAnsiCode(code);
202
- }
203
- else if (character === '\n') {
204
- if (escapeCode && code)
205
- returnValue += wrapAnsiCode(escapeCode);
206
- if (escapeUrl)
207
- returnValue += wrapAnsiHyperlink(escapeUrl);
208
- }
209
- }
210
- return returnValue;
211
- }
9
+ /*
10
+ * `width` and `strip` are linegauge's — measuring text and removing escapes is the layer
11
+ * below this one, and this file had its own copies of both. They were not merely duplicated,
12
+ * they were wrong: the ITU T.416 sub-parameter form `ESC[38:2::255:0:0m`, which chalk emits
13
+ * for truecolor, left `:2::255:0:0m` behind and measured a 13-column string as 25. linegauge
14
+ * fixed that in its own `strip`; burgee inherited nothing because it was not asking.
15
+ *
16
+ * The names stay, because `usage.ts` and `shim.ts` import them and the upstream port reads
17
+ * the way cliui reads.
18
+ */
19
+ export const stripAnsi = (str) => typeof str === "string" ? strip(str) : str;
20
+ export const stringWidth = (input) => typeof input === "string" ? width(input) : 0;
21
+ /*
22
+ * The wrap is linegauge's too. This file carried a wrap-ansi port — ansi-styles' open→close
23
+ * map, an escape-state machine over every character, hyperlink re-opening — to wrap a line
24
+ * without breaking the styling across it. That is the same job `linegauge/wrap` exists for
25
+ * and grades against wrap-ansi, so burgee kept a second implementation of a thing the layer
26
+ * below already owned.
27
+ *
28
+ * Checked before swapping, because yargs' usage tests compare whole help screens: the two
29
+ * agreed on all ten shapes probed — plain prose at several widths, an unbreakable word,
30
+ * SGR-coloured text, single characters, CJK, an embedded newline, leading and trailing
31
+ * spaces, empty, and a string exactly the column width.
32
+ */
212
33
  export function wrapAnsi(string, columns, options) {
213
- return String(string)
214
- .normalize()
215
- .replace(/\r\n/g, '\n')
216
- .split('\n')
217
- .map((line) => execWrap(line, columns, options))
218
- .join('\n');
34
+ return wrap(String(string).normalize().replace(/\r\n/g, "\n"), columns, options);
219
35
  }
220
36
  const align = { right: alignRight, center: alignCenter };
221
37
  const top = 0;
@@ -240,21 +56,24 @@ export class UI {
240
56
  }
241
57
  div(...args) {
242
58
  if (args.length === 0)
243
- this.div('');
244
- if (this.wrap && this.shouldApplyLayoutDSL(...args) && typeof args[0] === 'string')
59
+ this.div("");
60
+ if (this.wrap &&
61
+ this.shouldApplyLayoutDSL(...args) &&
62
+ typeof args[0] === "string")
245
63
  return this.applyLayoutDSL(args[0]);
246
- const cols = args.map((arg) => (typeof arg === 'string' ? this.colFromString(arg) : arg));
64
+ const cols = args.map((arg) => typeof arg === "string" ? this.colFromString(arg) : arg);
247
65
  this.rows.push(cols);
248
66
  return cols;
249
67
  }
250
68
  shouldApplyLayoutDSL(...args) {
251
- return args.length === 1 && typeof args[0] === 'string' && /[\t\n]/.test(args[0]);
69
+ return (args.length === 1 && typeof args[0] === "string" && /[\t\n]/.test(args[0]));
252
70
  }
253
71
  applyLayoutDSL(str) {
254
- const rows = str.split('\n').map((row) => row.split('\t'));
72
+ const rows = str.split("\n").map((row) => row.split("\t"));
255
73
  let leftColumnWidth = 0;
256
74
  rows.forEach((columns) => {
257
- if (columns.length > 1 && stringWidth(columns[0]) > leftColumnWidth) {
75
+ if (columns.length > 1 &&
76
+ stringWidth(columns[0]) > leftColumnWidth) {
258
77
  leftColumnWidth = Math.min(Math.floor(this.width * 0.5), stringWidth(columns[0]));
259
78
  }
260
79
  });
@@ -271,8 +90,20 @@ export class UI {
271
90
  return { text, padding: this.measurePadding(text) };
272
91
  }
273
92
  measurePadding(str) {
93
+ // An unanchored `\s*$` was the upstream spelling, and it is quadratic: the engine
94
+ // retries at every position, so a cell of 50,000 spaces then an `x` costs 1,346 ms
95
+ // here — CodeQL alert 35, "polynomial regular expression used on uncontrolled data",
96
+ // raised once `stripAnsi` became library input. Spaces, not the tabs the alert names:
97
+ // a tab routes into `applyLayoutDSL`, which splits on it long before this runs.
98
+ // `trim{Start,End}` remove exactly the set `\s` matches (WhiteSpace + LineTerminator)
99
+ // and are linear, so this is the same measurement without the backtracking.
274
100
  const noAnsi = stripAnsi(str);
275
- return [0, /\s*$/.exec(noAnsi)[0].length, 0, /^\s*/.exec(noAnsi)[0].length];
101
+ return [
102
+ 0,
103
+ noAnsi.length - noAnsi.trimEnd().length,
104
+ 0,
105
+ noAnsi.length - noAnsi.trimStart().length,
106
+ ];
276
107
  }
277
108
  toString() {
278
109
  const lines = [];
@@ -282,36 +113,36 @@ export class UI {
282
113
  return lines
283
114
  .filter((line) => !line.hidden)
284
115
  .map((line) => line.text)
285
- .join('\n');
116
+ .join("\n");
286
117
  }
287
118
  rowToString(row, lines) {
288
119
  this.rasterize(row).forEach((rrow, r) => {
289
- let str = '';
120
+ let str = "";
290
121
  rrow.forEach((col, c) => {
291
122
  const column = row[c];
292
123
  const { width } = column;
293
124
  const wrapWidth = this.negatePadding(column);
294
125
  let ts = col;
295
126
  if (wrapWidth > stringWidth(col))
296
- ts += ' '.repeat(wrapWidth - stringWidth(col));
297
- if (column.align && column.align !== 'left' && this.wrap) {
127
+ ts += " ".repeat(wrapWidth - stringWidth(col));
128
+ if (column.align && column.align !== "left" && this.wrap) {
298
129
  const fn = align[column.align];
299
130
  ts = fn(ts, wrapWidth);
300
131
  if (stringWidth(ts) < wrapWidth)
301
- ts += ' '.repeat((width || 0) - stringWidth(ts) - 1);
132
+ ts += " ".repeat((width || 0) - stringWidth(ts) - 1);
302
133
  }
303
134
  const padding = column.padding || [0, 0, 0, 0];
304
135
  if (padding[left])
305
- str += ' '.repeat(padding[left]);
306
- str += addBorder(column, ts, '| ');
136
+ str += " ".repeat(padding[left]);
137
+ str += addBorder(column, ts, "| ");
307
138
  str += ts;
308
- str += addBorder(column, ts, ' |');
139
+ str += addBorder(column, ts, " |");
309
140
  if (padding[right])
310
- str += ' '.repeat(padding[right]);
141
+ str += " ".repeat(padding[right]);
311
142
  if (r === 0 && lines.length > 0)
312
143
  str = this.renderInline(str, lines.at(-1));
313
144
  });
314
- lines.push({ text: str.replace(/ +$/, ''), span: row.span });
145
+ lines.push({ text: trimTrailingSpaces(str), span: row.span });
315
146
  });
316
147
  return lines;
317
148
  }
@@ -329,7 +160,9 @@ export class UI {
329
160
  if (leadingWhitespace < targetTextWidth)
330
161
  return source;
331
162
  previousLine.hidden = true;
332
- return target.trimEnd() + ' '.repeat(leadingWhitespace - targetTextWidth) + source.trimStart();
163
+ return (target.trimEnd() +
164
+ " ".repeat(leadingWhitespace - targetTextWidth) +
165
+ source.trimStart());
333
166
  }
334
167
  rasterize(row) {
335
168
  const rrows = [];
@@ -338,16 +171,18 @@ export class UI {
338
171
  row.forEach((col, c) => {
339
172
  col.width = widths[c];
340
173
  if (this.wrap)
341
- wrapped = wrapAnsi(col.text, this.negatePadding(col), { hard: true }).split('\n');
174
+ wrapped = wrapAnsi(col.text, this.negatePadding(col), {
175
+ hard: true,
176
+ }).split("\n");
342
177
  else
343
- wrapped = col.text.split('\n');
178
+ wrapped = col.text.split("\n");
344
179
  if (col.border) {
345
- wrapped.unshift(`.${'-'.repeat(this.negatePadding(col) + 2)}.`);
346
- wrapped.push(`'${'-'.repeat(this.negatePadding(col) + 2)}'`);
180
+ wrapped.unshift(`.${"-".repeat(this.negatePadding(col) + 2)}.`);
181
+ wrapped.push(`'${"-".repeat(this.negatePadding(col) + 2)}'`);
347
182
  }
348
183
  if (col.padding) {
349
- wrapped.unshift(...new Array(col.padding[top] || 0).fill(''));
350
- wrapped.push(...new Array(col.padding[bottom] || 0).fill(''));
184
+ wrapped.unshift(...new Array(col.padding[top] || 0).fill(""));
185
+ wrapped.push(...new Array(col.padding[bottom] || 0).fill(""));
351
186
  }
352
187
  wrapped.forEach((str, r) => {
353
188
  if (!rrows[r])
@@ -355,7 +190,7 @@ export class UI {
355
190
  const rrow = rrows[r];
356
191
  for (let i = 0; i < c; i++) {
357
192
  if (rrow[i] === undefined)
358
- rrow.push('');
193
+ rrow.push("");
359
194
  }
360
195
  rrow.push(str);
361
196
  });
@@ -384,18 +219,39 @@ export class UI {
384
219
  return undefined;
385
220
  });
386
221
  const unsetWidth = unset ? Math.floor(remainingWidth / unset) : 0;
387
- return widths.map((w, i) => (w === undefined ? Math.max(unsetWidth, minWidth(row[i])) : w));
222
+ return widths.map((w, i) => w === undefined ? Math.max(unsetWidth, minWidth(row[i])) : w);
388
223
  }
389
224
  }
225
+ /*
226
+ * `str.replace(/ +$/, "")` was the upstream spelling, and it is the same quadratic shape
227
+ * `measurePadding` had: the start is unanchored, so the engine retries the match at every
228
+ * position in the run of spaces and each attempt walks to the end before failing on the
229
+ * character that is not the end. A row built from a cell of 50,000 spaces then an `x` is
230
+ * 100,001 characters of which the first 50,000 are the left padding, and trimming it cost
231
+ * 1,049 ms of `toString()`'s 1,223 ms. Doubling the cell quadrupled it.
232
+ *
233
+ * `trimEnd()` is linear but not the same function — it also removes tabs, newlines and the
234
+ * rest of `\s`. A trailing tab does reach here: with `wrap: false`, `rasterize` only splits
235
+ * the cell on newlines, so nothing expands the tab and nothing routes the string through
236
+ * `applyLayoutDSL` (that check is behind `this.wrap`). `cliui({ wrap: false }).div('a\t')`
237
+ * renders `'a\t'` today and would render `'a'` under `trimEnd`, and the yargs usage tests
238
+ * compare whole help screens. This removes exactly U+0020, exactly as the regex did.
239
+ */
240
+ function trimTrailingSpaces(str) {
241
+ let end = str.length;
242
+ while (end > 0 && str[end - 1] === " ")
243
+ end--;
244
+ return end === str.length ? str : str.slice(0, end);
245
+ }
390
246
  function addBorder(col, ts, style) {
391
247
  if (col.border) {
392
248
  if (/[.']-+[.']/.test(ts))
393
- return '';
249
+ return "";
394
250
  if (ts.trim().length !== 0)
395
251
  return style;
396
- return ' ';
252
+ return " ";
397
253
  }
398
- return '';
254
+ return "";
399
255
  }
400
256
  function minWidth(col) {
401
257
  const padding = col.padding || [];
@@ -405,15 +261,15 @@ function minWidth(col) {
405
261
  return min;
406
262
  }
407
263
  function getWindowWidth() {
408
- if (typeof process === 'object' && process.stdout && process.stdout.columns)
409
- return process.stdout.columns;
410
- return 80;
264
+ // `host.columns` carries the upstream's guard on the process global itself, so a bundle
265
+ // that has no process at all still falls back to 80 rather than throwing.
266
+ return host.columns || 80;
411
267
  }
412
268
  function alignRight(str, width) {
413
269
  str = str.trim();
414
270
  const strWidth = stringWidth(str);
415
271
  if (strWidth < width)
416
- return ' '.repeat(width - strWidth) + str;
272
+ return " ".repeat(width - strWidth) + str;
417
273
  return str;
418
274
  }
419
275
  function alignCenter(str, width) {
@@ -421,7 +277,7 @@ function alignCenter(str, width) {
421
277
  const strWidth = stringWidth(str);
422
278
  if (strWidth >= width)
423
279
  return str;
424
- return ' '.repeat((width - strWidth) >> 1) + str;
280
+ return " ".repeat((width - strWidth) >> 1) + str;
425
281
  }
426
282
  export function cliui(opts) {
427
283
  return new UI({ width: opts?.width || getWindowWidth(), wrap: opts?.wrap });
@@ -5,7 +5,7 @@
5
5
  * `getInternalMethods()` seam are the upstream's — that is what makes a user's
6
6
  * existing program run unchanged (J2).
7
7
  */
8
- import { type Effects, Manifest, type Plugin } from '../manifest.js';
8
+ import { type DeclaredEffects, Manifest, type Plugin } from '../manifest.js';
9
9
  import { type CommandInstance } from './command.js';
10
10
  import { type CompletionFunction } from './completion.js';
11
11
  import { type Middleware } from './middleware.js';
@@ -179,7 +179,7 @@ export declare class YargsInstance {
179
179
  */
180
180
  get manifest(): Manifest;
181
181
  /** burgee: declare what the command does to the world (N6); what exposes it as an MCP tool (N2). */
182
- effects(value: Effects): this;
182
+ effects(value: DeclaredEffects): this;
183
183
  /** Additive: plugins yargs never had. `preRun`/`postRun` fire around every handler. */
184
184
  use(plugin: Plugin): this;
185
185
  /**
@@ -9,7 +9,7 @@ var _a;
9
9
  import { ExitCode } from '../exit-code.js';
10
10
  import { Manifest } from '../manifest.js';
11
11
  import { serveMcp } from '../mcp.js';
12
- import { schemaOf } from '../schema.js';
12
+ import { machineJson, schemaOf } from '../schema.js';
13
13
  import { tokenizeArgString } from '../yargs-parser.js';
14
14
  import { projectManifest, render } from './burgee.js';
15
15
  import { command as Command, isCommandBuilderCallback } from './command.js';
@@ -1315,7 +1315,12 @@ export class YargsInstance {
1315
1315
  });
1316
1316
  }
1317
1317
  if (head.includes('--schema') && !this.#declares('schema')) {
1318
- this.#logger.log(JSON.stringify(schemaOf(this.manifest), null, 2));
1318
+ // R1, through the same seam the engine and the commander façade use. Hand-rolling
1319
+ // `JSON.stringify(…, null, 2)` here made this façade the one front-end that could not
1320
+ // see `--format=json-pretty`, and emitted a different document from the other two for
1321
+ // the same CLI. `--schema` is burgee's surface, not yargs', so it answers to burgee's
1322
+ // byte discipline.
1323
+ this.#logger.log(machineJson(schemaOf(this.manifest), head));
1319
1324
  this.exit(0);
1320
1325
  return true;
1321
1326
  }
@@ -9,6 +9,7 @@ import { createRequire } from 'node:module';
9
9
  import { basename, dirname, extname, join, relative, resolve } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import { inspect } from 'node:util';
12
+ import { host } from '../runtime.js';
12
13
  import { Parser } from '../yargs-parser.js';
13
14
  import { cliui, stringWidth } from './cliui.js';
14
15
  import { getProcessArgvBin } from './utils.js';
@@ -71,24 +72,30 @@ export const shim = {
71
72
  assert: { notStrictEqual, strictEqual },
72
73
  cliui,
73
74
  findUp,
74
- getEnv: (key) => process.env[key],
75
+ getEnv: (key) => host.env[key],
75
76
  inspect,
76
77
  getProcessArgvBin,
77
- mainFilename: mainFilename || process.cwd(),
78
+ mainFilename: mainFilename || host.cwd(),
78
79
  Parser,
79
80
  path: { basename, dirname, extname, relative, resolve, join },
80
81
  process: {
81
- argv: () => process.argv,
82
- cwd: process.cwd,
83
- emitWarning: (warning, type) => process.emitWarning(warning, type),
84
- execPath: () => process.execPath,
82
+ argv: () => host.argv,
83
+ cwd: () => host.cwd(),
84
+ emitWarning: (warning, type) => {
85
+ host.emitWarning(warning, type);
86
+ },
87
+ execPath: () => host.execPath,
85
88
  exit: (code) => {
86
- process.exit(code);
89
+ host.exit(code);
90
+ },
91
+ nextTick: (fn, ...args) => {
92
+ host.nextTick(fn, ...args);
87
93
  },
88
- nextTick: process.nextTick,
89
- stdColumns: typeof process.stdout.columns !== 'undefined' ? process.stdout.columns : null,
90
- stdin: () => process.stdin,
91
- stdout: () => process.stdout,
94
+ // Captured, as it was before the seam: `stdColumns` is a value in `PlatformShim`, not
95
+ // a thunk, so yargs reads whatever this object was built with. Unchanged on purpose.
96
+ stdColumns: typeof host.stdout.columns !== 'undefined' ? host.stdout.columns : null,
97
+ stdin: () => host.stdin,
98
+ stdout: () => host.stdout,
92
99
  },
93
100
  readFileSync,
94
101
  readdirSync,
@@ -7,6 +7,7 @@
7
7
  import { readFileSync } from 'node:fs';
8
8
  import { createRequire } from 'node:module';
9
9
  import { dirname, resolve } from 'node:path';
10
+ import { host } from '../runtime.js';
10
11
  export class YError extends Error {
11
12
  constructor(msg) {
12
13
  super(msg || 'yargs error');
@@ -139,7 +140,7 @@ export function maybeAsyncResult(getResult, resultHandler, errorHandler = (err)
139
140
  export function setBlocking(blocking) {
140
141
  if (typeof process === 'undefined')
141
142
  return;
142
- [process.stdout, process.stderr].forEach((_stream) => {
143
+ [host.stdout, host.stderr].forEach((_stream) => {
143
144
  const stream = _stream;
144
145
  if (stream._handle && stream.isTTY && typeof stream._handle.setBlocking === 'function')
145
146
  stream._handle.setBlocking(blocking);
@@ -151,16 +152,16 @@ function getProcessArgvBinIndex() {
151
152
  return 1;
152
153
  }
153
154
  function isBundledElectronApp() {
154
- return isElectronApp() && !process.defaultApp;
155
+ return isElectronApp() && !host.defaultApp;
155
156
  }
156
157
  function isElectronApp() {
157
- return !!process.versions.electron;
158
+ return !!host.versions['electron'];
158
159
  }
159
160
  export function hideBin(argv) {
160
161
  return argv.slice(getProcessArgvBinIndex() + 1);
161
162
  }
162
163
  export function getProcessArgvBin() {
163
- return process.argv[getProcessArgvBinIndex()];
164
+ return host.argv[getProcessArgvBinIndex()];
164
165
  }
165
166
  const nodeRequire = createRequire(import.meta.url);
166
167
  let previouslyVisitedConfigs = [];
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { normalize, resolve } from 'node:path';
4
4
  import { format } from 'node:util';
5
+ import { host } from './runtime.js';
5
6
  export function camelCase(str) {
6
7
  const isCamelCase = str !== str.toLowerCase() && str !== str.toUpperCase();
7
8
  if (!isCamelCase)
@@ -902,11 +903,10 @@ function sanitizeKey(key) {
902
903
  function stripQuotes(val) {
903
904
  return typeof val === 'string' && (val[0] === "'" || val[0] === '"') && val.at(-1) === val[0] ? val.substring(1, val.length - 1) : val;
904
905
  }
905
- const env = process.env;
906
906
  const nodeRequire = createRequire(import.meta.url);
907
907
  const parser = new YargsParser({
908
- cwd: process.cwd,
909
- env: () => env,
908
+ cwd: () => host.cwd(),
909
+ env: () => host.env,
910
910
  format,
911
911
  normalize,
912
912
  resolve,