mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Print an error's non-enumerable cause and an AggregateError's errors in its checked-dynamic rendering
An error rendered through its checked-dynamic node (a runtime error class,
or an error held as a subclass's `cause`) prints a non-enumerable `cause`
as `[cause]: value` and an AggregateError's `errors` as `[errors]: [...]`,
after its enumerable properties, as Node's formatError does. Before, a
subclass's cause that was an Error constructed with a `cause` option
printed without it: `{ cause: [Error: inner] }` where Node prints
`{ cause: [Error: inner] { [cause]: 'deep' } }`.
This commit is contained in:
@@ -1070,6 +1070,16 @@ static ScrDyn *insp_assertion_ellipsis(ScrDyn *value) {
|
||||
* scr_insp_error: the stackless bracket form `[Name: message]`, then the
|
||||
* remaining enumerable own properties (code, errno, syscall, ...) in
|
||||
* Node's `{ ... }` tail. */
|
||||
/* An own non-enumerable entry of the error encoding, NULL when absent or
|
||||
* enumerable (an enumerable one prints with the other own keys). */
|
||||
static const ScrDynEntry *insp_error_hidden_entry(const ScrDyn *d, const char *key, size_t len) {
|
||||
for (size_t i = 0; i < d->v.obj.len; i++) {
|
||||
const ScrDynEntry *ent = &d->v.obj.entries[i];
|
||||
if (ent->key_len == len && memcmp(ent->key, key, len) == 0) return ent->enumerable ? NULL : ent;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) {
|
||||
/* An error already on the traversal stack (a spawnSync error's own
|
||||
* `error`): Node's [Circular *N], and <ref *N> on the outer rendering. */
|
||||
@@ -1090,6 +1100,12 @@ static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) {
|
||||
continue;
|
||||
extra++;
|
||||
}
|
||||
/* formatError shows a non-enumerable `cause`, and an AggregateError's
|
||||
* `errors` array, after the enumerable keys: `[cause]`, `[errors]`. */
|
||||
const ScrDynEntry *cause = insp_error_hidden_entry(d, "cause", 5);
|
||||
const ScrDynEntry *errors = insp_error_hidden_entry(d, "errors", 6);
|
||||
if (errors && errors->value->kind != SCR_DYN_ARR) errors = NULL;
|
||||
extra += (cause != NULL) + (errors != NULL);
|
||||
InspBuf base = {0};
|
||||
ib_char(&base, '[');
|
||||
if (name != NULL) ib_str(&base, name->v.str);
|
||||
@@ -1132,6 +1148,20 @@ static ScrStr *scr_insp_dyn_error(ScrDyn *d, double recurse, double depth) {
|
||||
scr_insp_entry(entry, false);
|
||||
scr_str_release(entry);
|
||||
}
|
||||
const ScrDynEntry *hidden[] = {cause, errors};
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
if (!hidden[i]) continue;
|
||||
ScrStr *val = scr_insp_dyn(hidden[i]->value, recurse + 1, value_depth);
|
||||
InspBuf eb = {0};
|
||||
ib_char(&eb, '[');
|
||||
ib_bytes(&eb, hidden[i]->key, hidden[i]->key_len);
|
||||
ib_cstr(&eb, "]: ");
|
||||
ib_bytes(&eb, val->data, val->len);
|
||||
scr_str_release(val);
|
||||
ScrStr *entry = ib_take(&eb);
|
||||
scr_insp_entry(entry, false);
|
||||
scr_str_release(entry);
|
||||
}
|
||||
ScrStr *b0 = scr_str_new("{", 1);
|
||||
ScrStr *b1 = scr_str_new("}", 1);
|
||||
ScrStr *body = scr_insp_end(base_str, b0, b1, recurse + 1, false, false);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// (`[Bailable [Error]: m]`), followed by the own properties — on one line
|
||||
// or broken across lines by the same width rule as any object, and as
|
||||
// `[Name]` beyond the depth budget. A `cause` follows the fields: `cause:`
|
||||
// when assigned, `[cause]:` from the constructor's options. (The stack is
|
||||
// cleared: compiled errors carry none.)
|
||||
// when assigned, `[cause]:` from the constructor's options, and a cause
|
||||
// prints its own cause. (The stack is cleared: compiled errors carry none.)
|
||||
import * as util from "node:util";
|
||||
class Bailable extends Error {
|
||||
bail = false;
|
||||
@@ -48,3 +48,8 @@ show(clean(new Retried("with an undefined cause")));
|
||||
const assigned = clean(new Bailable("assigned"));
|
||||
assigned.cause = { code: 1 };
|
||||
show(assigned);
|
||||
const root = new Error("root");
|
||||
root.stack = "";
|
||||
const middle: Error = new TypeError("middle", { cause: root });
|
||||
middle.stack = "";
|
||||
show(clean(new Retried("nested causes", middle)));
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
// `[FetchFailedError: m]` when the constructor name already contains the
|
||||
// error's name; runtime error classes keep their own header. Own `cause`
|
||||
// properties (declared, assigned in the constructor or later) and a
|
||||
// declared `name` follow Node's rendering, and beyond the depth limit an
|
||||
// instance with own properties renders as `[Constructor]`. (The stack is
|
||||
// cleared: compiled errors carry none.)
|
||||
// declared `name` follow Node's rendering, as does a cause's own
|
||||
// `[cause]` and an AggregateError cause's `[errors]`, and beyond the depth
|
||||
// limit an instance with own properties renders as `[Constructor]`. (The
|
||||
// stack is cleared: compiled errors carry none.)
|
||||
import * as util from "node:util";
|
||||
|
||||
class Plain extends Error {}
|
||||
@@ -64,3 +65,16 @@ const multiline = clean(new Plain("first\nsecond"));
|
||||
console.log(util.inspect({ multiline }));
|
||||
const deep = [clean(new Assigned("beyond depth")), clean(new Assigned()), clean(new Plain())];
|
||||
for (const error of deep) console.log(util.inspect({ a: { b: error } }, { depth: 1 }));
|
||||
const inner = new Error("inner", { cause: "deep" });
|
||||
inner.stack = "";
|
||||
console.log(util.inspect(inner));
|
||||
const wrapped = clean(new Plain("outer"));
|
||||
wrapped.cause = inner;
|
||||
console.log(util.inspect(wrapped));
|
||||
const first = new Error("first");
|
||||
first.stack = "";
|
||||
const aggregate: Error = new AggregateError([first], "all failed", { cause: "why" });
|
||||
aggregate.stack = "";
|
||||
const holder = clean(new Plain("holder"));
|
||||
holder.cause = aggregate;
|
||||
console.log(util.inspect(holder, { depth: 4 }));
|
||||
|
||||
Reference in New Issue
Block a user