xlvane 1.0.1 → 1.1.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/README.md +8 -7
- package/dist/cli.js +474 -51
- package/dist/index.d.ts +10 -10
- package/dist/index.js +3 -3
- package/dist/{node-files-uazELnqk.js → node-files-CsQVpKgP.js} +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +3 -3
- package/dist/{options-B7UhudO_.js → options-Ba6r4zP1.js} +399 -85
- package/dist/{rows-Cwte1t8m.js → rows-Biu_ddwz.js} +280 -89
- package/dist/{stream-write-Q-sef804.d.ts → stream-write-CaGFWPhB.d.ts} +45 -12
- package/dist/{stream-write-CTgWff2P.js → stream-write-CtWyTd44.js} +32 -10
- package/docs/cli-json/inspect.schema.json +8 -0
- package/docs/errors.md +18 -1
- package/docs/exceljs-compat.md +3 -3
- package/docs/inspect-json.md +2 -0
- package/llms-full.txt +41 -13
- package/package.json +1 -1
- package/skills/xlvane/SKILL.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { a as appendRows, c as fromCSV, d as fromJSON, f as pickSheet, h as toJSON, l as inferValue, n as insertRows, o as editCells, p as readTable, s as diff, t as deleteRows, u as toCSV } from "./rows-
|
|
4
|
-
import { i as readInput } from "./node-files-
|
|
2
|
+
import { On as XlvError, Vt as suggest, _ as isoDate, b as readWorkbook, dn as parseRange, g as inspect } from "./options-Ba6r4zP1.js";
|
|
3
|
+
import { a as appendRows, c as fromCSV, d as fromJSON, f as pickSheet, h as toJSON, l as inferValue, n as insertRows, o as editCells, p as readTable, s as diff, t as deleteRows, u as toCSV } from "./rows-Biu_ddwz.js";
|
|
4
|
+
import { i as readInput } from "./node-files-CsQVpKgP.js";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import { chmod, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { chmod, lstat, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
7
7
|
import process from "node:process";
|
|
8
|
-
import {
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
9
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
10
|
+
// src/cli/node-util.ts
|
|
11
|
+
/** The `node:util` module. */
|
|
12
|
+
const nodeUtil = createRequire(import.meta.url)("node:util");
|
|
10
13
|
// src/cli/args.ts
|
|
11
14
|
/** Options every command accepts. */
|
|
12
15
|
const GLOBAL_OPTIONS = {
|
|
@@ -64,7 +67,7 @@ function parseCommandArgs(spec, args) {
|
|
|
64
67
|
...o.short ? { short: o.short } : {},
|
|
65
68
|
...o.multiple ? { multiple: true } : {}
|
|
66
69
|
};
|
|
67
|
-
const { tokens } = parseArgs({
|
|
70
|
+
const { tokens } = nodeUtil.parseArgs({
|
|
68
71
|
args: [...args],
|
|
69
72
|
options: config,
|
|
70
73
|
strict: false,
|
|
@@ -166,6 +169,259 @@ function headerOption(args, command) {
|
|
|
166
169
|
option: "--header"
|
|
167
170
|
});
|
|
168
171
|
}
|
|
172
|
+
// src/cli/east-asian-width.ts
|
|
173
|
+
/**
|
|
174
|
+
* Code points whose East_Asian_Width is Wide (W) or Fullwidth (F), including the unassigned code
|
|
175
|
+
* points that default to W, as sorted pairs of first and last code point. Unicode 17.0.0.
|
|
176
|
+
*/
|
|
177
|
+
const EAST_ASIAN_WIDE = [
|
|
178
|
+
4352,
|
|
179
|
+
4447,
|
|
180
|
+
8986,
|
|
181
|
+
8987,
|
|
182
|
+
9001,
|
|
183
|
+
9002,
|
|
184
|
+
9193,
|
|
185
|
+
9196,
|
|
186
|
+
9200,
|
|
187
|
+
9200,
|
|
188
|
+
9203,
|
|
189
|
+
9203,
|
|
190
|
+
9725,
|
|
191
|
+
9726,
|
|
192
|
+
9748,
|
|
193
|
+
9749,
|
|
194
|
+
9776,
|
|
195
|
+
9783,
|
|
196
|
+
9800,
|
|
197
|
+
9811,
|
|
198
|
+
9855,
|
|
199
|
+
9855,
|
|
200
|
+
9866,
|
|
201
|
+
9871,
|
|
202
|
+
9875,
|
|
203
|
+
9875,
|
|
204
|
+
9889,
|
|
205
|
+
9889,
|
|
206
|
+
9898,
|
|
207
|
+
9899,
|
|
208
|
+
9917,
|
|
209
|
+
9918,
|
|
210
|
+
9924,
|
|
211
|
+
9925,
|
|
212
|
+
9934,
|
|
213
|
+
9934,
|
|
214
|
+
9940,
|
|
215
|
+
9940,
|
|
216
|
+
9962,
|
|
217
|
+
9962,
|
|
218
|
+
9970,
|
|
219
|
+
9971,
|
|
220
|
+
9973,
|
|
221
|
+
9973,
|
|
222
|
+
9978,
|
|
223
|
+
9978,
|
|
224
|
+
9981,
|
|
225
|
+
9981,
|
|
226
|
+
9989,
|
|
227
|
+
9989,
|
|
228
|
+
9994,
|
|
229
|
+
9995,
|
|
230
|
+
10024,
|
|
231
|
+
10024,
|
|
232
|
+
10060,
|
|
233
|
+
10060,
|
|
234
|
+
10062,
|
|
235
|
+
10062,
|
|
236
|
+
10067,
|
|
237
|
+
10069,
|
|
238
|
+
10071,
|
|
239
|
+
10071,
|
|
240
|
+
10133,
|
|
241
|
+
10135,
|
|
242
|
+
10160,
|
|
243
|
+
10160,
|
|
244
|
+
10175,
|
|
245
|
+
10175,
|
|
246
|
+
11035,
|
|
247
|
+
11036,
|
|
248
|
+
11088,
|
|
249
|
+
11088,
|
|
250
|
+
11093,
|
|
251
|
+
11093,
|
|
252
|
+
11904,
|
|
253
|
+
11929,
|
|
254
|
+
11931,
|
|
255
|
+
12019,
|
|
256
|
+
12032,
|
|
257
|
+
12245,
|
|
258
|
+
12272,
|
|
259
|
+
12350,
|
|
260
|
+
12353,
|
|
261
|
+
12438,
|
|
262
|
+
12441,
|
|
263
|
+
12543,
|
|
264
|
+
12549,
|
|
265
|
+
12591,
|
|
266
|
+
12593,
|
|
267
|
+
12686,
|
|
268
|
+
12688,
|
|
269
|
+
12773,
|
|
270
|
+
12783,
|
|
271
|
+
12830,
|
|
272
|
+
12832,
|
|
273
|
+
12871,
|
|
274
|
+
12880,
|
|
275
|
+
42124,
|
|
276
|
+
42128,
|
|
277
|
+
42182,
|
|
278
|
+
43360,
|
|
279
|
+
43388,
|
|
280
|
+
44032,
|
|
281
|
+
55203,
|
|
282
|
+
63744,
|
|
283
|
+
64255,
|
|
284
|
+
65040,
|
|
285
|
+
65049,
|
|
286
|
+
65072,
|
|
287
|
+
65106,
|
|
288
|
+
65108,
|
|
289
|
+
65126,
|
|
290
|
+
65128,
|
|
291
|
+
65131,
|
|
292
|
+
65281,
|
|
293
|
+
65376,
|
|
294
|
+
65504,
|
|
295
|
+
65510,
|
|
296
|
+
94176,
|
|
297
|
+
94180,
|
|
298
|
+
94192,
|
|
299
|
+
94198,
|
|
300
|
+
94208,
|
|
301
|
+
101589,
|
|
302
|
+
101631,
|
|
303
|
+
101662,
|
|
304
|
+
101760,
|
|
305
|
+
101874,
|
|
306
|
+
110576,
|
|
307
|
+
110579,
|
|
308
|
+
110581,
|
|
309
|
+
110587,
|
|
310
|
+
110589,
|
|
311
|
+
110590,
|
|
312
|
+
110592,
|
|
313
|
+
110882,
|
|
314
|
+
110898,
|
|
315
|
+
110898,
|
|
316
|
+
110928,
|
|
317
|
+
110930,
|
|
318
|
+
110933,
|
|
319
|
+
110933,
|
|
320
|
+
110948,
|
|
321
|
+
110951,
|
|
322
|
+
110960,
|
|
323
|
+
111355,
|
|
324
|
+
119552,
|
|
325
|
+
119638,
|
|
326
|
+
119648,
|
|
327
|
+
119670,
|
|
328
|
+
126980,
|
|
329
|
+
126980,
|
|
330
|
+
127183,
|
|
331
|
+
127183,
|
|
332
|
+
127374,
|
|
333
|
+
127374,
|
|
334
|
+
127377,
|
|
335
|
+
127386,
|
|
336
|
+
127488,
|
|
337
|
+
127490,
|
|
338
|
+
127504,
|
|
339
|
+
127547,
|
|
340
|
+
127552,
|
|
341
|
+
127560,
|
|
342
|
+
127568,
|
|
343
|
+
127569,
|
|
344
|
+
127584,
|
|
345
|
+
127589,
|
|
346
|
+
127744,
|
|
347
|
+
127776,
|
|
348
|
+
127789,
|
|
349
|
+
127797,
|
|
350
|
+
127799,
|
|
351
|
+
127868,
|
|
352
|
+
127870,
|
|
353
|
+
127891,
|
|
354
|
+
127904,
|
|
355
|
+
127946,
|
|
356
|
+
127951,
|
|
357
|
+
127955,
|
|
358
|
+
127968,
|
|
359
|
+
127984,
|
|
360
|
+
127988,
|
|
361
|
+
127988,
|
|
362
|
+
127992,
|
|
363
|
+
128062,
|
|
364
|
+
128064,
|
|
365
|
+
128064,
|
|
366
|
+
128066,
|
|
367
|
+
128252,
|
|
368
|
+
128255,
|
|
369
|
+
128317,
|
|
370
|
+
128331,
|
|
371
|
+
128334,
|
|
372
|
+
128336,
|
|
373
|
+
128359,
|
|
374
|
+
128378,
|
|
375
|
+
128378,
|
|
376
|
+
128405,
|
|
377
|
+
128406,
|
|
378
|
+
128420,
|
|
379
|
+
128420,
|
|
380
|
+
128507,
|
|
381
|
+
128591,
|
|
382
|
+
128640,
|
|
383
|
+
128709,
|
|
384
|
+
128716,
|
|
385
|
+
128716,
|
|
386
|
+
128720,
|
|
387
|
+
128722,
|
|
388
|
+
128725,
|
|
389
|
+
128728,
|
|
390
|
+
128732,
|
|
391
|
+
128735,
|
|
392
|
+
128747,
|
|
393
|
+
128748,
|
|
394
|
+
128756,
|
|
395
|
+
128764,
|
|
396
|
+
128992,
|
|
397
|
+
129003,
|
|
398
|
+
129008,
|
|
399
|
+
129008,
|
|
400
|
+
129292,
|
|
401
|
+
129338,
|
|
402
|
+
129340,
|
|
403
|
+
129349,
|
|
404
|
+
129351,
|
|
405
|
+
129535,
|
|
406
|
+
129648,
|
|
407
|
+
129660,
|
|
408
|
+
129664,
|
|
409
|
+
129674,
|
|
410
|
+
129678,
|
|
411
|
+
129734,
|
|
412
|
+
129736,
|
|
413
|
+
129736,
|
|
414
|
+
129741,
|
|
415
|
+
129756,
|
|
416
|
+
129759,
|
|
417
|
+
129770,
|
|
418
|
+
129775,
|
|
419
|
+
129784,
|
|
420
|
+
131072,
|
|
421
|
+
196605,
|
|
422
|
+
196608,
|
|
423
|
+
262141
|
|
424
|
+
];
|
|
169
425
|
// src/cli/output.ts
|
|
170
426
|
/**
|
|
171
427
|
* Whether to colour a stream. `util.styleText` respects NO_COLOR, NODE_DISABLE_COLORS,
|
|
@@ -207,8 +463,15 @@ var Output = class {
|
|
|
207
463
|
this.color = color;
|
|
208
464
|
this.width = width;
|
|
209
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Colour codes are written with {@link STYLE_MARK} in place of ESC, and an ESC already in the
|
|
468
|
+
* text (from a file) is escaped, so that the final filter ({@link terminalSafe}) can tell the
|
|
469
|
+
* CLI's own colour codes from the same bytes in a sheet name or cell (D-H01).
|
|
470
|
+
*/
|
|
210
471
|
style(format, text) {
|
|
211
|
-
|
|
472
|
+
if (!this.color || text === "") return text;
|
|
473
|
+
const safe = text.includes("\x1B") ? text.split("\x1B").join("\\x1b") : text;
|
|
474
|
+
return nodeUtil.styleText(format, safe, { validateStream: false }).split("\x1B").join(STYLE_MARK);
|
|
212
475
|
}
|
|
213
476
|
bold(text) {
|
|
214
477
|
return this.style("bold", text);
|
|
@@ -230,11 +493,11 @@ var Output = class {
|
|
|
230
493
|
}
|
|
231
494
|
/**
|
|
232
495
|
* An aligned table. Columns are cut with "…" to fit `width`; the widest columns shrink first.
|
|
233
|
-
*
|
|
496
|
+
* Widths are display columns ({@link displayWidth}), so CJK text and emoji stay aligned.
|
|
234
497
|
*/
|
|
235
498
|
table(headers, rows, indent = "", align = []) {
|
|
236
499
|
const count = headers.length;
|
|
237
|
-
const widths = headers.map((h, i) => Math.max(h
|
|
500
|
+
const widths = headers.map((h, i) => Math.max(displayWidth(oneLine(h)), ...rows.map((r) => displayWidth(oneLine(r[i] ?? "")))));
|
|
238
501
|
const gap = 2;
|
|
239
502
|
const available = Math.max(count * 4, this.width - indent.length - gap * (count - 1));
|
|
240
503
|
let total = widths.reduce((a, b) => a + b, 0);
|
|
@@ -248,7 +511,8 @@ var Output = class {
|
|
|
248
511
|
const line = (cells, head) => indent + cells.map((c, i) => {
|
|
249
512
|
const w = widths[i] ?? 0;
|
|
250
513
|
const text = fit(oneLine(c), w);
|
|
251
|
-
const
|
|
514
|
+
const pad = " ".repeat(Math.max(0, w - displayWidth(text)));
|
|
515
|
+
const padded = align[i] === "right" && !head ? pad + text : i === count - 1 ? text : text + pad;
|
|
252
516
|
return head ? this.bold(padded) : padded;
|
|
253
517
|
}).join(" ".repeat(gap)).trimEnd();
|
|
254
518
|
return [line(headers, true), ...rows.map((r) => line(r, false))].join("\n");
|
|
@@ -262,7 +526,8 @@ function oneLine(text) {
|
|
|
262
526
|
* which includes the 8-bit CSI U+009B) written as `\x1b`, and bidirectional formatting characters
|
|
263
527
|
* written as `\u202e`, so text from a file, such as a cell
|
|
264
528
|
* holding `_x001B_]0;title_x0007_`, cannot move the cursor, change the window title or hide
|
|
265
|
-
* output (docs/decisions.md D-R02). Used for all human-readable output; `--json`
|
|
529
|
+
* output (docs/decisions.md D-R02). Used for all human-readable output; `--json` writes the same
|
|
530
|
+
* characters as `\uXXXX` escapes instead (D-H01).
|
|
266
531
|
*/
|
|
267
532
|
function escapeControls(text) {
|
|
268
533
|
return text.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g, (c) => `\\x${c.charCodeAt(0).toString(16).padStart(2, "0")}`).replace(BIDI, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
@@ -274,34 +539,110 @@ function escapeControls(text) {
|
|
|
274
539
|
* `\u202e` in human-readable output, like other control characters (D-R08).
|
|
275
540
|
*/
|
|
276
541
|
const BIDI = /[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
277
|
-
/**
|
|
278
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Stands for ESC in the colour codes `Output` writes until {@link terminalSafe} turns it back:
|
|
544
|
+
* NUL and 16 random hexadecimal digits, new for each process, so text from a file cannot contain
|
|
545
|
+
* it by chance or design. Before 1.1.1 the filter kept every ESC sequence that looked like one of
|
|
546
|
+
* the CLI's colours, including ones from a sheet name (D-H01).
|
|
547
|
+
*/
|
|
548
|
+
const STYLE_MARK = `\u0000${Array.from(globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8)), (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
549
|
+
/** The colour sequences `Output` writes (styleText: bold, dim, red, green, yellow, cyan and their resets), after the mark. */
|
|
550
|
+
const OWN_STYLE = /^\[(?:1|2|22|31|32|33|36|39)m/;
|
|
551
|
+
const MARKED_STYLE = new RegExp(`${STYLE_MARK}\\[[0-9;]*m`, "g");
|
|
279
552
|
/**
|
|
280
553
|
* Final filter for human-readable output: escapes every control character (see
|
|
281
|
-
* {@link escapeControls}) except line feeds
|
|
282
|
-
*
|
|
554
|
+
* {@link escapeControls}) except line feeds and tabs. When `keepStyles` is true, the colour codes
|
|
555
|
+
* that `Output` wrote (marked with {@link STYLE_MARK}) are turned into ESC sequences; any ESC in
|
|
556
|
+
* the text itself is escaped. Applied to everything the CLI prints for people.
|
|
283
557
|
*/
|
|
284
558
|
function terminalSafe(text, keepStyles) {
|
|
559
|
+
if (!text.includes(STYLE_MARK)) return controlsSafe(text);
|
|
560
|
+
const parts = text.split(STYLE_MARK);
|
|
561
|
+
let result = controlsSafe(parts[0] ?? "");
|
|
562
|
+
for (const part of parts.slice(1)) {
|
|
563
|
+
const own = OWN_STYLE.exec(part)?.[0];
|
|
564
|
+
if (own === void 0) result += controlsSafe(part);
|
|
565
|
+
else result += (keepStyles ? `\u001b${own}` : "") + controlsSafe(part.slice(own.length));
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
/** Escapes the control characters of text (see {@link escapeControls}) except line feeds and tabs. */
|
|
570
|
+
function controlsSafe(text) {
|
|
285
571
|
let result = "";
|
|
286
572
|
let start = 0;
|
|
287
573
|
for (let i = 0; i < text.length; i++) {
|
|
288
574
|
const code = text.charCodeAt(i);
|
|
289
575
|
if (!(code < 32 && code !== 10 && code !== 9 || code >= 127 && code <= 159 || code === 1564 || code === 8206 || code === 8207 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297)) continue;
|
|
290
|
-
if (keepStyles && code === 27) {
|
|
291
|
-
OWN_STYLES.lastIndex = i;
|
|
292
|
-
const m = OWN_STYLES.exec(text);
|
|
293
|
-
if (m !== null) {
|
|
294
|
-
i += m[0].length - 1;
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
576
|
result += text.slice(start, i) + escapeControls(text[i]);
|
|
299
577
|
start = i + 1;
|
|
300
578
|
}
|
|
301
579
|
return start === 0 ? text : result + text.slice(start);
|
|
302
580
|
}
|
|
581
|
+
/** Cuts text to `width` display columns with "…", between grapheme clusters. */
|
|
303
582
|
function fit(text, width) {
|
|
304
|
-
|
|
583
|
+
if (displayWidth(text) <= width) return text;
|
|
584
|
+
let result = "";
|
|
585
|
+
let used = 0;
|
|
586
|
+
for (const part of graphemes(text)) {
|
|
587
|
+
const w = displayWidth(part);
|
|
588
|
+
if (used + w > width - 1) break;
|
|
589
|
+
result += part;
|
|
590
|
+
used += w;
|
|
591
|
+
}
|
|
592
|
+
return `${result}…`;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Grapheme clusters of a text (Intl.Segmenter), or its code points when the Node.js build has no
|
|
596
|
+
* Intl support, so that cutting never splits a surrogate pair or a letter from its accent.
|
|
597
|
+
*/
|
|
598
|
+
function graphemes(text) {
|
|
599
|
+
if (segmenter === void 0) return Array.from(text);
|
|
600
|
+
return Array.from(segmenter.segment(text), (s) => s.segment);
|
|
601
|
+
}
|
|
602
|
+
const segmenter = typeof Intl === "object" && typeof Intl.Segmenter === "function" ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : void 0;
|
|
603
|
+
/**
|
|
604
|
+
* The number of terminal columns a text takes, counted as Node.js counts them for its own output
|
|
605
|
+
* (src/node_i18n.cc GetColumnWidth, v22.23.4; F-UNICODE-01): East Asian Wide and Fullwidth
|
|
606
|
+
* characters (Unicode East_Asian_Width W and F) and emoji with Emoji_Presentation take two
|
|
607
|
+
* columns; control and format characters (except the soft hyphen), nonspacing and enclosing marks
|
|
608
|
+
* and emoji modifiers that are not Wide take none; every other character, including East Asian
|
|
609
|
+
* Ambiguous ones, takes one. Each code point of an emoji sequence is counted, as Node.js does by
|
|
610
|
+
* default, so a terminal that draws the sequence as one picture shows it narrower. Colour codes
|
|
611
|
+
* (`ESC [ … m`, as `Output` writes them) take no columns, as Node.js strips them first.
|
|
612
|
+
*/
|
|
613
|
+
function displayWidth(text) {
|
|
614
|
+
if (text.includes("\x1B")) text = text.replace(/\u001b\[[0-9;]*m/g, "");
|
|
615
|
+
if (text.includes(STYLE_MARK)) text = text.replace(MARKED_STYLE, "");
|
|
616
|
+
let width = 0;
|
|
617
|
+
for (let i = 0; i < text.length; i++) {
|
|
618
|
+
const code = text.charCodeAt(i);
|
|
619
|
+
if (code >= 32 && code < 127) width++;
|
|
620
|
+
else {
|
|
621
|
+
for (const ch of text.slice(i)) width += codePointWidth(ch.codePointAt(0) ?? 0, ch);
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return width;
|
|
626
|
+
}
|
|
627
|
+
function codePointWidth(code, ch) {
|
|
628
|
+
if (isEastAsianWide(code) || EMOJI_PRESENTATION.test(ch)) return 2;
|
|
629
|
+
if (code !== 173 && ZERO_WIDTH.test(ch)) return 0;
|
|
630
|
+
return 1;
|
|
631
|
+
}
|
|
632
|
+
const EMOJI_PRESENTATION = /^\p{Emoji_Presentation}$/u;
|
|
633
|
+
const ZERO_WIDTH = /^[\p{Cc}\p{Cf}\p{Me}\p{Mn}\p{Emoji_Modifier}]$/u;
|
|
634
|
+
/** Binary search in the sorted [first, last, first, last, …] ranges of EAST_ASIAN_WIDE. */
|
|
635
|
+
function isEastAsianWide(code) {
|
|
636
|
+
if (code < 4352) return false;
|
|
637
|
+
let lo = 0;
|
|
638
|
+
let hi = EAST_ASIAN_WIDE.length / 2 - 1;
|
|
639
|
+
while (lo <= hi) {
|
|
640
|
+
const mid = lo + hi >> 1;
|
|
641
|
+
if (code < EAST_ASIAN_WIDE[mid * 2]) hi = mid - 1;
|
|
642
|
+
else if (code > EAST_ASIAN_WIDE[mid * 2 + 1]) lo = mid + 1;
|
|
643
|
+
else return true;
|
|
644
|
+
}
|
|
645
|
+
return false;
|
|
305
646
|
}
|
|
306
647
|
/** Quotes an argument for a POSIX shell when needed, so "Try:" commands can be copied. */
|
|
307
648
|
function quoteArg(arg) {
|
|
@@ -333,12 +674,12 @@ function humanSize(bytes) {
|
|
|
333
674
|
if (bytes < 1e6) return `${(bytes / 1e3).toFixed(1)} kB`;
|
|
334
675
|
return `${(bytes / 1e6).toFixed(1)} MB`;
|
|
335
676
|
}
|
|
336
|
-
/** Wraps a paragraph at `width` columns; lines after the first start with `indent`. */
|
|
677
|
+
/** Wraps a paragraph at `width` display columns; lines after the first start with `indent`. */
|
|
337
678
|
function wrap(text, width, indent = "", firstIndent = indent) {
|
|
338
679
|
const lines = [];
|
|
339
680
|
let line = "";
|
|
340
681
|
let prefix = firstIndent;
|
|
341
|
-
for (const word of text.split(/\s+/).filter((w) => w !== "")) if (line !== "" && prefix
|
|
682
|
+
for (const word of text.split(/\s+/).filter((w) => w !== "")) if (line !== "" && displayWidth(prefix) + displayWidth(line) + 1 + displayWidth(word) > width) {
|
|
342
683
|
lines.push(prefix + line);
|
|
343
684
|
prefix = indent;
|
|
344
685
|
line = word;
|
|
@@ -1015,7 +1356,7 @@ const DEFAULT_LIMIT$1 = 50;
|
|
|
1015
1356
|
function showCell(c) {
|
|
1016
1357
|
if (c === null) return "(empty)";
|
|
1017
1358
|
const value = c.value === null ? "" : typeof c.value === "string" ? JSON.stringify(c.value) : String(c.value);
|
|
1018
|
-
return c.formula !== null ? `=${c.formula}${value !== "" ? ` (${value})` : ""}` : value === "" ? "(no value)" : value;
|
|
1359
|
+
return c.formula !== null ? `=${escapeControls(c.formula)}${value !== "" ? ` (${value})` : ""}` : value === "" ? "(no value)" : value;
|
|
1019
1360
|
}
|
|
1020
1361
|
function kindLabel(out, kind) {
|
|
1021
1362
|
const text = kind.padEnd(7);
|
|
@@ -1074,14 +1415,14 @@ const diffCommand = {
|
|
|
1074
1415
|
result: r,
|
|
1075
1416
|
exitCode: r.equal ? 0 : 1,
|
|
1076
1417
|
render: (out) => {
|
|
1077
|
-
if (r.equal) return `${out.green("No differences")} between ${a} and ${b}.`;
|
|
1418
|
+
if (r.equal) return `${out.green("No differences")} between ${escapeControls(a)} and ${escapeControls(b)}.`;
|
|
1078
1419
|
const s = r.summary;
|
|
1079
|
-
const lines = [`${out.bold(`${a} → ${b}`)}: ${plural(s.cellsChanged, "cell")} changed, ${String(s.cellsAdded)} added, ${String(s.cellsRemoved)} removed` + (s.sheetsAdded + s.sheetsRemoved > 0 ? `; sheets added: ${String(s.sheetsAdded)}, removed: ${String(s.sheetsRemoved)}` : "")];
|
|
1080
|
-
for (const name of r.sheetsAdded) lines.push(`${out.green("added sheet")} "${name}"`);
|
|
1081
|
-
for (const name of r.sheetsRemoved) lines.push(`${out.red("removed sheet")} "${name}"`);
|
|
1420
|
+
const lines = [`${out.bold(`${escapeControls(a)} → ${escapeControls(b)}`)}: ${plural(s.cellsChanged, "cell")} changed, ${String(s.cellsAdded)} added, ${String(s.cellsRemoved)} removed` + (s.sheetsAdded + s.sheetsRemoved > 0 ? `; sheets added: ${String(s.sheetsAdded)}, removed: ${String(s.sheetsRemoved)}` : "")];
|
|
1421
|
+
for (const name of r.sheetsAdded) lines.push(`${out.green("added sheet")} "${escapeControls(name)}"`);
|
|
1422
|
+
for (const name of r.sheetsRemoved) lines.push(`${out.red("removed sheet")} "${escapeControls(name)}"`);
|
|
1082
1423
|
for (const sheet of r.sheets) {
|
|
1083
1424
|
if (sheet.cells.length === 0 && !sheet.state) continue;
|
|
1084
|
-
lines.push("", out.bold(`Sheet "${sheet.name}"`));
|
|
1425
|
+
lines.push("", out.bold(`Sheet "${escapeControls(sheet.name)}"`));
|
|
1085
1426
|
if (sheet.state) lines.push(` ${out.yellow("changed")} visibility: ${sheet.state.before} → ${sheet.state.after}`);
|
|
1086
1427
|
for (const c of sheet.cells.slice(0, limit)) {
|
|
1087
1428
|
const style = c.kind === "changed" && showCell(c.before) === showCell(c.after) ? " (formatting)" : "";
|
|
@@ -1438,17 +1779,17 @@ const inspectCommand = {
|
|
|
1438
1779
|
}
|
|
1439
1780
|
};
|
|
1440
1781
|
function renderReport(out, file, r, verbose) {
|
|
1441
|
-
const lines = [`${out.bold(file)}: ${r.format}, ${plural(r.sheetCount, "sheet")}, date system ${r.dateSystem}${r.conformance === "strict" ? ", Strict conformance" : ""}`];
|
|
1782
|
+
const lines = [`${out.bold(escapeControls(file))}: ${r.format}, ${plural(r.sheetCount, "sheet")}, date system ${r.dateSystem}${r.conformance === "strict" ? ", Strict conformance" : ""}`];
|
|
1442
1783
|
if (r.hasMacros) lines.push("Contains macros (not run).");
|
|
1443
1784
|
if (r.externalLinkCount > 0) lines.push(`Links to ${plural(r.externalLinkCount, "other workbook")} (not followed).`);
|
|
1444
1785
|
const props = Object.entries(r.properties);
|
|
1445
1786
|
if (props.length > 0) {
|
|
1446
1787
|
const keyWidth = Math.max(...props.map(([k]) => k.length));
|
|
1447
|
-
lines.push(out.bold("Properties:"), ...props.map(([k, v]) => ` ${k.padEnd(keyWidth)} ${v}`));
|
|
1788
|
+
lines.push(out.bold("Properties:"), ...props.map(([k, v]) => ` ${k.padEnd(keyWidth)} ${escapeControls(String(v))}`));
|
|
1448
1789
|
}
|
|
1449
1790
|
const names = verbose ? r.definedNames : r.definedNames.filter((n) => !(n.hidden && n.name.startsWith("_xlnm.")));
|
|
1450
1791
|
if (names.length > 0) {
|
|
1451
|
-
const shown = names.slice(0, 5).map((n) => `${n.name}${n.sheet === null ? "" : ` (sheet "${n.sheet}")`}${n.hidden ? " (hidden)" : ""} =${n.refersTo}`);
|
|
1792
|
+
const shown = names.slice(0, 5).map((n) => `${escapeControls(n.name)}${n.sheet === null ? "" : ` (sheet "${escapeControls(n.sheet)}")`}${n.hidden ? " (hidden)" : ""} =${escapeControls(n.refersTo)}`);
|
|
1452
1793
|
const more = names.length > 5 || r.definedNamesTruncated ? `, … (${r.definedNamesTruncated ? "more than " : ""}${String(names.length)} in total; see --json)` : "";
|
|
1453
1794
|
lines.push(`${out.bold("Defined names:")} ${shown.join(", ")}${more}`);
|
|
1454
1795
|
}
|
|
@@ -1456,16 +1797,16 @@ function renderReport(out, file, r, verbose) {
|
|
|
1456
1797
|
lines.push("");
|
|
1457
1798
|
const state = s.state === "visible" ? "" : ` (${s.state})`;
|
|
1458
1799
|
if (s.kind !== "worksheet") {
|
|
1459
|
-
lines.push(`${out.bold(`Sheet "${s.name}"`)}${state}: ${s.kind}, no cells`);
|
|
1800
|
+
lines.push(`${out.bold(`Sheet "${escapeControls(s.name)}"`)}${state}: ${s.kind}, no cells`);
|
|
1460
1801
|
continue;
|
|
1461
1802
|
}
|
|
1462
1803
|
if (s.usedRange === null) {
|
|
1463
|
-
lines.push(`${out.bold(`Sheet "${s.name}"`)}${state}: empty`);
|
|
1804
|
+
lines.push(`${out.bold(`Sheet "${escapeControls(s.name)}"`)}${state}: empty`);
|
|
1464
1805
|
continue;
|
|
1465
1806
|
}
|
|
1466
|
-
lines.push(`${out.bold(`Sheet "${s.name}"`)}${state}: ${s.usedRange}, ${plural(s.rowCount, "row")} × ${plural(s.columnCount, "column")}, ${plural(s.cellCount, "cell")}, ${plural(s.formulaCount, "formula")}`);
|
|
1807
|
+
lines.push(`${out.bold(`Sheet "${escapeControls(s.name)}"`)}${state}: ${s.usedRange}, ${plural(s.rowCount, "row")} × ${plural(s.columnCount, "column")}, ${plural(s.cellCount, "cell")}, ${plural(s.formulaCount, "formula")}`);
|
|
1467
1808
|
lines.push(s.headerRow ? ` Header row: ${String(s.headerRow.row)} (${s.headerRow.confidence} confidence)` : " Header row: none detected");
|
|
1468
|
-
if (s.mergedRangeCount > 0) lines.push(` Merged: ${s.mergedRanges.slice(0, 10).join(", ")}${s.mergedRangeCount > 10 ? `, … (${String(s.mergedRangeCount)} in total)` : ""}`);
|
|
1809
|
+
if (s.mergedRangeCount > 0) lines.push(` Merged: ${escapeControls(s.mergedRanges.slice(0, 10).join(", "))}${s.mergedRangeCount > 10 ? `, … (${String(s.mergedRangeCount)} in total)` : ""}`);
|
|
1469
1810
|
if (s.columns.length > 0) {
|
|
1470
1811
|
lines.push(out.table([
|
|
1471
1812
|
"Column",
|
|
@@ -1480,23 +1821,23 @@ function renderReport(out, file, r, verbose) {
|
|
|
1480
1821
|
]), " "));
|
|
1481
1822
|
if (s.columnsTruncated) lines.push(` … ${plural(s.columnCount - s.columns.length, "more column")}; use --max-columns ${String(s.columnCount)} to see them`);
|
|
1482
1823
|
}
|
|
1483
|
-
if (s.formulas.length > 0) lines.push(` Formulas: ${s.formulas.slice(0, 3).map((f) => `${f.cell} =${f.formula ?? "(shared)"}`).join(", ")}`);
|
|
1824
|
+
if (s.formulas.length > 0) lines.push(` Formulas: ${s.formulas.slice(0, 3).map((f) => `${f.cell} =${escapeControls(f.formula ?? "(shared)")}`).join(", ")}`);
|
|
1484
1825
|
if (s.richTextCellCount > 0) lines.push(` Rich text: ${plural(s.richTextCellCount, "cell")} (${s.richTextCells.slice(0, 5).join(", ")}${s.richTextCellCount > 5 ? ", …" : ""})`);
|
|
1485
1826
|
if (s.styledColumns.length > 0) lines.push(` Column styles: ${s.styledColumns.slice(0, 10).join(", ")}${s.styledColumns.length > 10 ? ", …" : ""}`);
|
|
1486
1827
|
if (s.hyperlinkCount > 0) {
|
|
1487
|
-
const shown = s.hyperlinks.slice(0, 3).map((l) => `${l.ref} → ${l.target ?? ""}${l.location === null ? "" : `${l.target === null ? "" : "#"}${l.location}`}`);
|
|
1828
|
+
const shown = s.hyperlinks.slice(0, 3).map((l) => `${l.ref} → ${escapeControls(l.target ?? "")}${l.location === null ? "" : `${l.target === null ? "" : "#"}${escapeControls(l.location)}`}`);
|
|
1488
1829
|
lines.push(` Hyperlinks: ${plural(s.hyperlinkCount, "link")} (${shown.join(", ")}${s.hyperlinkCount > 3 ? ", …" : ""})`);
|
|
1489
1830
|
}
|
|
1490
1831
|
if (s.imageCount > 0) lines.push(` Images: ${plural(s.imageCount, "picture")} (${s.images.slice(0, 5).map((i) => `${i.cell ?? "absolute"} ${i.format ?? "other"}`).join(", ")}${s.imageCount > 5 ? ", …" : ""})`);
|
|
1491
1832
|
if (s.noteCount > 0) lines.push(` Notes: ${plural(s.noteCount, "note")} (${s.notes.slice(0, 5).join(", ")}${s.noteCount > 5 ? ", …" : ""})`);
|
|
1492
|
-
if (s.tables.length > 0) lines.push(` Tables: ${s.tables.slice(0, 5).map((
|
|
1833
|
+
if (s.tables.length > 0) lines.push(` Tables: ${s.tables.slice(0, 5).map((table) => `${escapeControls(table.name)} ${escapeControls(table.ref)}`).join(", ")}${s.tables.length > 5 ? ", …" : ""}`);
|
|
1493
1834
|
if (s.dataValidationCount > 0 || s.conditionalFormatCount > 0) {
|
|
1494
1835
|
const parts = [s.dataValidationCount > 0 ? plural(s.dataValidationCount, "data validation") : null, s.conditionalFormatCount > 0 ? plural(s.conditionalFormatCount, "conditional format rule") : null];
|
|
1495
1836
|
lines.push(` Rules: ${parts.filter((p) => p !== null).join(", ")}`);
|
|
1496
1837
|
}
|
|
1497
1838
|
if (s.protected) lines.push(" Protected: yes");
|
|
1498
1839
|
if (s.printArea !== null || s.pageBreakCount > 0) {
|
|
1499
|
-
const parts = [s.printArea === null ? null : `print area ${s.printArea.join(", ")}`, s.pageBreakCount > 0 ? plural(s.pageBreakCount, "page break") : null];
|
|
1840
|
+
const parts = [s.printArea === null ? null : `print area ${escapeControls(s.printArea.join(", "))}`, s.pageBreakCount > 0 ? plural(s.pageBreakCount, "page break") : null];
|
|
1500
1841
|
lines.push(` Printing: ${parts.filter((p) => p !== null).join(", ")}`);
|
|
1501
1842
|
}
|
|
1502
1843
|
}
|
|
@@ -1547,10 +1888,11 @@ async function sourceFiles(root) {
|
|
|
1547
1888
|
const path = join(dir, name);
|
|
1548
1889
|
let info;
|
|
1549
1890
|
try {
|
|
1550
|
-
info = await
|
|
1891
|
+
info = await lstat(path);
|
|
1551
1892
|
} catch {
|
|
1552
1893
|
continue;
|
|
1553
1894
|
}
|
|
1895
|
+
if (info.isSymbolicLink()) continue;
|
|
1554
1896
|
if (info.isDirectory()) await walk(path);
|
|
1555
1897
|
else if (info.isFile() && SOURCE.test(name) && info.size <= MAX_FILE_BYTES) files.push(path);
|
|
1556
1898
|
}
|
|
@@ -1579,6 +1921,75 @@ function scanSource(file, text) {
|
|
|
1579
1921
|
});
|
|
1580
1922
|
return findings;
|
|
1581
1923
|
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Offsets `[start, end)` of the string value (with its quotes) at `path` in valid JSON text, the
|
|
1926
|
+
* one JSON.parse reads when keys repeat, or `null`. Keys are compared after decoding their
|
|
1927
|
+
* escapes. Values off the path are skipped without recursion.
|
|
1928
|
+
*/
|
|
1929
|
+
function stringValueSpan(text, path) {
|
|
1930
|
+
let i = 0;
|
|
1931
|
+
const space = () => {
|
|
1932
|
+
while (i < text.length && " \r\n".includes(text[i])) i++;
|
|
1933
|
+
};
|
|
1934
|
+
/** Skips a string starting at i (on its opening quote); returns its raw text. */
|
|
1935
|
+
const string = () => {
|
|
1936
|
+
const start = i++;
|
|
1937
|
+
while (text[i] !== "\"") i += text[i] === "\\" ? 2 : 1;
|
|
1938
|
+
i++;
|
|
1939
|
+
return text.slice(start, i);
|
|
1940
|
+
};
|
|
1941
|
+
/** Skips any value starting at i, iteratively. */
|
|
1942
|
+
const skip = () => {
|
|
1943
|
+
let depth = 0;
|
|
1944
|
+
do {
|
|
1945
|
+
const c = text[i];
|
|
1946
|
+
if (c === "\"") string();
|
|
1947
|
+
else {
|
|
1948
|
+
if (c === "{" || c === "[") depth++;
|
|
1949
|
+
else if (c === "}" || c === "]") depth--;
|
|
1950
|
+
i++;
|
|
1951
|
+
if (depth === 0 && c !== "{" && c !== "[") while (i < text.length && !",]} \r\n".includes(text[i])) i++;
|
|
1952
|
+
}
|
|
1953
|
+
} while (depth > 0 && i < text.length);
|
|
1954
|
+
};
|
|
1955
|
+
const value = (level) => {
|
|
1956
|
+
space();
|
|
1957
|
+
if (level === path.length) {
|
|
1958
|
+
if (text[i] !== "\"") {
|
|
1959
|
+
skip();
|
|
1960
|
+
return null;
|
|
1961
|
+
}
|
|
1962
|
+
const start = i;
|
|
1963
|
+
string();
|
|
1964
|
+
return [start, i];
|
|
1965
|
+
}
|
|
1966
|
+
if (text[i] !== "{") {
|
|
1967
|
+
skip();
|
|
1968
|
+
return null;
|
|
1969
|
+
}
|
|
1970
|
+
i++;
|
|
1971
|
+
let found = null;
|
|
1972
|
+
space();
|
|
1973
|
+
if (text[i] === "}") {
|
|
1974
|
+
i++;
|
|
1975
|
+
return null;
|
|
1976
|
+
}
|
|
1977
|
+
for (;;) {
|
|
1978
|
+
space();
|
|
1979
|
+
const key = JSON.parse(string());
|
|
1980
|
+
space();
|
|
1981
|
+
i++;
|
|
1982
|
+
if (key === path[level]) found = value(level + 1);
|
|
1983
|
+
else {
|
|
1984
|
+
space();
|
|
1985
|
+
skip();
|
|
1986
|
+
}
|
|
1987
|
+
space();
|
|
1988
|
+
if (text[i++] === "}") return found;
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
return value(0);
|
|
1992
|
+
}
|
|
1582
1993
|
function jsonError(path, cause) {
|
|
1583
1994
|
return new XlvError("XLV_E_JSON_INVALID", `"${path}" is not a valid package.json.`, {
|
|
1584
1995
|
hint: "Fix the JSON syntax of package.json and run the command again.",
|
|
@@ -1626,11 +2037,14 @@ async function run$1(ctx) {
|
|
|
1626
2037
|
}
|
|
1627
2038
|
let write = null;
|
|
1628
2039
|
if (changes.length > 0 && flag(ctx.args, "force")) {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
2040
|
+
const spans = changes.map((c) => stringValueSpan(text, [c.section, "exceljs"])).filter((s) => s !== null);
|
|
2041
|
+
let updated = "";
|
|
2042
|
+
let at = 0;
|
|
2043
|
+
for (const [start, end] of spans.sort((a, b) => a[0] - b[0])) {
|
|
2044
|
+
updated += `${text.slice(at, start)}"${ALIAS}"`;
|
|
2045
|
+
at = end;
|
|
1633
2046
|
}
|
|
2047
|
+
updated += text.slice(at);
|
|
1634
2048
|
write = await writeOutput(ctx, pkgPath, updated, {
|
|
1635
2049
|
force: true,
|
|
1636
2050
|
dryRun: flag(ctx.args, "dry-run")
|
|
@@ -2031,14 +2445,14 @@ async function run(argv, io) {
|
|
|
2031
2445
|
const err = new Output(useColor(io.env, io.stderrIsTTY), io.stderrIsTTY ? width : 100);
|
|
2032
2446
|
const stdout = (text) => io.stdout(terminalSafe(text, out.color));
|
|
2033
2447
|
const stderr = (text) => io.stderr(terminalSafe(text, err.color));
|
|
2034
|
-
const toJson = (value) => `${io.stdoutIsTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value)}\n`;
|
|
2448
|
+
const toJson = (value) => `${jsonSafe(io.stdoutIsTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value))}\n`;
|
|
2035
2449
|
const json = optionArgs(argv).includes("--json");
|
|
2036
2450
|
const [first, ...rest] = argv;
|
|
2037
2451
|
let spec;
|
|
2038
2452
|
const state = { lastFile: null };
|
|
2039
2453
|
try {
|
|
2040
2454
|
if (first === void 0 || first === "--json" && rest.length === 0) {
|
|
2041
|
-
|
|
2455
|
+
stdout(welcome(out, io.version, COMMANDS));
|
|
2042
2456
|
return EXIT.ok;
|
|
2043
2457
|
}
|
|
2044
2458
|
if (first === "--version" || first === "-v") {
|
|
@@ -2048,7 +2462,7 @@ async function run(argv, io) {
|
|
|
2048
2462
|
if (first === "help" || first === "--help" || first === "-h") {
|
|
2049
2463
|
const topic = rest.find((a) => !a.startsWith("-"));
|
|
2050
2464
|
if (topic === void 0) {
|
|
2051
|
-
|
|
2465
|
+
stdout(welcome(out, io.version, COMMANDS));
|
|
2052
2466
|
return EXIT.ok;
|
|
2053
2467
|
}
|
|
2054
2468
|
stdout(commandHelp(out, findCommand(topic)));
|
|
@@ -2223,6 +2637,15 @@ function formatError(err, error, tryCommand, verbose = false) {
|
|
|
2223
2637
|
""
|
|
2224
2638
|
].join("\n");
|
|
2225
2639
|
}
|
|
2640
|
+
/**
|
|
2641
|
+
* JSON text with DEL, the C1 controls (which include the 8-bit CSI U+009B), the bidirectional
|
|
2642
|
+
* formatting characters and U+2028/U+2029 written as `\uXXXX` escapes. JSON.stringify already
|
|
2643
|
+
* escapes C0 controls; these characters can occur only inside strings, so the escaped text is
|
|
2644
|
+
* valid JSON with the same value, and a terminal showing it does not act on them (D-H01).
|
|
2645
|
+
*/
|
|
2646
|
+
function jsonSafe(json) {
|
|
2647
|
+
return json.replace(/[\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
2648
|
+
}
|
|
2226
2649
|
// src/cli/bin.ts
|
|
2227
2650
|
function version() {
|
|
2228
2651
|
try {
|