mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-23 06:10:02 +08:00
235 lines
9.4 KiB
JavaScript
235 lines
9.4 KiB
JavaScript
// The island's node:util shim, differentially against Node: every line
|
|
// must match real Node byte-for-byte. Exercises inspect (quoting, depth,
|
|
// break-length wrapping, array grouping, circular refs, Map/Set, classes,
|
|
// custom inspect, promise state via the host peek), format's specifiers,
|
|
// promisify/callbackify round trips, inherits, isDeepStrictEqual,
|
|
// parseArgs, stripVTControlCharacters/styleText (piped: both lanes
|
|
// uncolored), util.types, and the util/types module identity.
|
|
import util, {
|
|
inspect, format, promisify, callbackify, inherits, isDeepStrictEqual,
|
|
parseArgs, stripVTControlCharacters, styleText, types,
|
|
} from "node:util";
|
|
import { isNativeError } from "node:util/types";
|
|
|
|
class Foo {
|
|
constructor() {
|
|
this.a = 1;
|
|
this.b = { c: 2 };
|
|
}
|
|
}
|
|
|
|
export async function report() {
|
|
const lines = [];
|
|
const show = (label, v, opts) => {
|
|
lines.push(label + " " + (opts === undefined ? inspect(v) : inspect(v, opts)));
|
|
};
|
|
|
|
// primitives and strings
|
|
show("undef:", undefined);
|
|
show("null:", null);
|
|
show("neg0:", -0);
|
|
show("nan:", NaN);
|
|
show("exp:", 1e21);
|
|
show("bigint:", 123n);
|
|
show("str:", "plain");
|
|
show("squote:", "it's");
|
|
show("dquote:", 'say "hi"');
|
|
show("bothq:", "it's \"quoted\"");
|
|
show("tick:", "a`b'c\"d");
|
|
show("ctrl:", "a\nb\tc\x01d\x7f");
|
|
show("uni:", "héllo ☃");
|
|
show("sym:", Symbol("desc"));
|
|
|
|
// functions and classes
|
|
show("fn:", function named() {});
|
|
show("anon:", function () {});
|
|
show("arrow:", () => {});
|
|
show("asyncfn:", async function af() {});
|
|
show("genfn:", function* gf() {});
|
|
show("cls:", class Klass {});
|
|
show("clsext:", class Sub extends Foo {});
|
|
const fnProps = function withProps() {};
|
|
fnProps.attached = 42;
|
|
show("fnprops:", fnProps);
|
|
|
|
// arrays
|
|
show("arr:", [1, 2, 3]);
|
|
show("arrmix:", [1, "two", null, undefined, true]);
|
|
show("arrnest:", [[1, [2, [3, [4]]]]]);
|
|
// eslint-disable-next-line no-sparse-arrays
|
|
show("holes:", [1, , , 4]);
|
|
show("arrlong:", Array.from({ length: 150 }, (_, i) => i));
|
|
show("arr30:", new Array(30).fill(7));
|
|
show("arrstr:", [
|
|
"some fairly long string here", "another fairly long string here",
|
|
"and a third fairly long string", "plus the fourth one for measure",
|
|
]);
|
|
show("arrx:", Object.assign([1, 2], { extra: "x" }));
|
|
|
|
// objects
|
|
show("obj:", { a: 1, b: "two" });
|
|
show("keys:", { "a-b": 1, "0x": 2, valid_$: 3, "": 4 });
|
|
show("nest4:", { a: { b: { c: { d: { e: 5 } } } } });
|
|
show("depth0:", { a: { b: 1 } }, { depth: 0 });
|
|
show("depthnull:", { a: { b: { c: { d: {} } } } }, { depth: null });
|
|
show("break40:", {
|
|
alpha: "one", beta: "two", gamma: "three", delta: "four",
|
|
}, { breakLength: 40 });
|
|
show("sorted:", { b: 1, a: 2 }, { sorted: true });
|
|
show("compactoff:", { a: 1, b: 2 }, { compact: false });
|
|
show("maxarr:", [1, 2, 3, 4, 5], { maxArrayLength: 2 });
|
|
show("maxstr:", "abcdef", { maxStringLength: 3 });
|
|
const nullProto = Object.create(null);
|
|
nullProto.x = 1;
|
|
show("nullproto:", nullProto);
|
|
show("symkey:", { plain: 2, [Symbol("k")]: 1 });
|
|
const getterObj = {};
|
|
Object.defineProperty(getterObj, "g", { get: () => 1, enumerable: true });
|
|
Object.defineProperty(getterObj, "gs", { get: () => 1, set: () => {}, enumerable: true });
|
|
show("getters:", getterObj);
|
|
show("inst:", new Foo());
|
|
|
|
// builtin composites
|
|
show("map0:", new Map());
|
|
show("map:", new Map([["a", 1], ["b", { x: 2 }]]));
|
|
show("set:", new Set([1, "two", { three: 3 }]));
|
|
show("date:", new Date(86400000));
|
|
show("baddate:", new Date(NaN));
|
|
show("re:", /a+b/gi);
|
|
// A stackless Error shape: engine stack strings never byte-match V8's,
|
|
// so the differential case pins the no-stack rendering (real errors
|
|
// carry engine-format stacks — a documented divergence).
|
|
const errNoStack = Object.create(Error.prototype);
|
|
errNoStack.message = "boom";
|
|
show("err:", errNoStack);
|
|
show("boxed:", new String("boxed"));
|
|
show("u8:", new Uint8Array([1, 2, 3]));
|
|
show("i32:", new Int32Array([-1, 0, 1]));
|
|
show("ab:", new Uint8Array([0x61, 0x62]).buffer);
|
|
|
|
// circular
|
|
const circ = { name: "x" };
|
|
circ.self = circ;
|
|
show("circ:", circ);
|
|
const circArr = [1];
|
|
circArr.push(circArr);
|
|
show("circarr:", circArr);
|
|
|
|
// custom inspect
|
|
const custom = {
|
|
[inspect.custom](depth) {
|
|
return "custom<" + depth + ">";
|
|
},
|
|
};
|
|
show("custom:", custom);
|
|
|
|
// promise state (the host peek; Node reads engine internals the same way)
|
|
show("pending:", new Promise(() => {}));
|
|
show("resolved:", Promise.resolve(42));
|
|
const rejected = Promise.reject("nope");
|
|
show("rejected:", rejected);
|
|
rejected.catch(() => {});
|
|
|
|
// format
|
|
lines.push("f1 " + format("%s|%d|%i|%f|%j", "s", "42.5", "42.9", "42.9x", { a: [1] }));
|
|
lines.push("f2 " + format("%s", { a: { b: { c: 1 } } }));
|
|
lines.push("f3 " + format("%d", { a: 1 }));
|
|
lines.push("f4 " + format("%o", { a: 1 }));
|
|
lines.push("f5 " + format("%O", { a: { b: { c: 1 } } }));
|
|
lines.push("f6 " + format("100%% %s", "sure"));
|
|
lines.push("f7 " + format("%cstyled", "color: red", "after"));
|
|
lines.push("f8 " + format("%s %s %s", "one"));
|
|
lines.push("f9 " + format("%s", "one", "two", { three: 3 }));
|
|
lines.push("f10 " + format(1, "two", { three: 3 }));
|
|
lines.push("f11 " + format("%d|%s", -0, -0));
|
|
lines.push("f12 " + format("%d", 7n));
|
|
|
|
// promisify / callbackify
|
|
const padd = promisify((a, b, cb) => cb(null, a + b));
|
|
lines.push("promisify " + (await padd(1, 2)));
|
|
const pfail = promisify((cb) => cb(new Error("no")));
|
|
lines.push("promisify-err " + (await pfail().catch((e) => "caught:" + e.message)));
|
|
const customAsync = () => {};
|
|
customAsync[promisify.custom] = async () => "custom-path";
|
|
lines.push("promisify-custom " + (await promisify(customAsync)()));
|
|
const doubled = callbackify(async (x) => x * 2);
|
|
lines.push("callbackify " + (await new Promise((res) => doubled(21, (err, v) => res(err + ":" + v)))));
|
|
|
|
// inherits
|
|
function A() {}
|
|
function B() {}
|
|
inherits(B, A);
|
|
lines.push("inherits " + (Object.getPrototypeOf(B.prototype) === A.prototype) + " " + (B.super_ === A));
|
|
|
|
// isDeepStrictEqual
|
|
const dse = [
|
|
[1, 1], [0, -0], [NaN, NaN],
|
|
[{ a: 1 }, { a: 1 }], [{ a: 1 }, { a: 2 }], [{ a: 1 }, { a: 1, b: undefined }],
|
|
[[1, 2], [2, 1]],
|
|
[new Map([[{ x: 1 }, "a"]]), new Map([[{ x: 1 }, "a"]])],
|
|
[new Set([{ x: 1 }]), new Set([{ x: 1 }])],
|
|
[new Date(0), new Date(0)], [/a/g, /a/i],
|
|
[new Uint8Array([1]), new Int8Array([1])],
|
|
[new Foo(), new Foo()], [new Foo(), { a: 1, b: { c: 2 } }],
|
|
[Object.create(null), {}],
|
|
[new Error("x"), new Error("x")], [new Error("x"), new Error("y")],
|
|
];
|
|
lines.push("dse " + dse.map(([a, b]) => (isDeepStrictEqual(a, b) ? 1 : 0)).join(""));
|
|
const ca = { v: 1 };
|
|
ca.self = ca;
|
|
const cb = { v: 1 };
|
|
cb.self = cb;
|
|
lines.push("dse-circ " + isDeepStrictEqual(ca, cb));
|
|
|
|
// parseArgs
|
|
const pa = (cfg) => {
|
|
try {
|
|
return JSON.stringify(parseArgs(cfg));
|
|
} catch (e) {
|
|
return "threw:" + e.code;
|
|
}
|
|
};
|
|
lines.push("pa1 " + pa({ args: ["--foo", "bar"], options: { foo: { type: "string" } } }));
|
|
lines.push("pa2 " + pa({ args: ["--flag", "--foo=inline"], options: { flag: { type: "boolean" }, foo: { type: "string" } } }));
|
|
lines.push("pa3 " + pa({ args: ["-ab", "-fvalue"], options: { alpha: { type: "boolean", short: "a" }, beta: { type: "boolean", short: "b" }, foo: { type: "string", short: "f" } } }));
|
|
lines.push("pa4 " + pa({ args: ["--multi", "a", "--multi", "b", "pos"], options: { multi: { type: "string", multiple: true } }, allowPositionals: true }));
|
|
lines.push("pa5 " + pa({ args: ["--", "--not-an-option"], allowPositionals: true }));
|
|
lines.push("pa6 " + pa({ args: [], options: { d: { type: "string", default: "dv" } } }));
|
|
lines.push("pa7 " + pa({ args: ["--unknown"] }));
|
|
lines.push("pa8 " + pa({ args: ["--foo"], options: { foo: { type: "string" } } }));
|
|
lines.push("pa9 " + pa({ args: ["pos"] }));
|
|
lines.push("pa10 " + pa({ args: ["--flag", "pos"], options: { flag: { type: "boolean" } }, allowPositionals: true, tokens: true }));
|
|
|
|
// strip / styleText (stdout is piped in the harness: both lanes uncolored)
|
|
lines.push("strip " + stripVTControlCharacters("\u001b[31mred\u001b[39m plain"));
|
|
lines.push("style " + styleText("red", "text") + "|" + styleText(["bold", "green"], "text"));
|
|
let styleErr;
|
|
try {
|
|
styleText("nope", "x");
|
|
styleErr = "no";
|
|
} catch (e) {
|
|
styleErr = e.code;
|
|
}
|
|
lines.push("style-bad " + styleErr);
|
|
|
|
// types
|
|
const t = [
|
|
types.isDate(new Date()), types.isDate({}),
|
|
types.isRegExp(/x/), types.isMap(new Map()), types.isSet(new Set()),
|
|
types.isPromise(Promise.resolve()), types.isNativeError(new TypeError("t")),
|
|
types.isNativeError({}), types.isArrayBuffer(new ArrayBuffer(1)),
|
|
types.isDataView(new DataView(new ArrayBuffer(1))),
|
|
types.isTypedArray(new Uint8Array(1)), types.isTypedArray(new DataView(new ArrayBuffer(1))),
|
|
types.isUint8Array(new Uint8Array(1)), types.isUint8Array(new Int8Array(1)),
|
|
types.isGeneratorFunction(function* () {}), types.isAsyncFunction(async () => {}),
|
|
types.isStringObject(new String("s")), types.isBoxedPrimitive(new Number(1)),
|
|
types.isArrayBufferView(new Uint8Array(1)), types.isArrayBufferView([]),
|
|
];
|
|
lines.push("types " + t.map((x) => (x ? 1 : 0)).join(""));
|
|
lines.push("types-module " + (isNativeError === types.isNativeError) + " " + (util.types === types));
|
|
lines.push("legacy " + util.isArray([]) + " " + util.isArray({}) + " " + (typeof util._extend === "function"));
|
|
lines.push("tenc " + (util.TextEncoder === TextEncoder));
|
|
|
|
return lines.join("\n");
|
|
}
|