Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/runtime/api/html_rewriter.bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const HTMLRewriter = b.class({
onDocument: { ...chainable(b.self), args: { handlers: b.any }, capability: "js", throws: true },
transform: {
args: { input: b.any },
returns: b.any.returning("string", "object"),
returns: b.any.returning("string", "Response", "ArrayBuffer"),
Comment thread
robobun marked this conversation as resolved.
Resolved
capability: "js",
throws: true,
passThis: true,
Expand Down
9 changes: 8 additions & 1 deletion src/runtime/node/util/parse_args.bind.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import * as b from "../../../codegen/bindgen-lib.ts";
import * as table from "../../../codegen/builtins/lib.ts";

export default b.module({ rustPath: "crate::node::util::parse_args" });

export const parseArgs = b.fn({ args: { config: b.any }, returns: b.any, capability: "js", throws: true });
/** New objects and lists, `values` without a prototype. An element of `args` and a `default` come back as the program handed them in, of whatever class. */
export const parseArgs = b.fn({
args: { config: b.any },
returns: b.any.returning(table.anyMadeOf("Object", "Array").spec),
capability: "js",
throws: true,
});
3 changes: 2 additions & 1 deletion test/aot/builtins/differential/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export type WorkerNote = [entry: string, mode: CallMode] | { notStarted: string
// `failure`: the test itself threw, which says nothing about the built-in.
export type WorkerReport = CallReport & { entry: string; failure?: string };

const [requestPath, reportsPath] = process.env.AOT_BUILTINS_WORKER_FILES?.split("\n") ?? process.argv.slice(2);
// The two paths are taken out of process.argv: a built-in that reads it would be handed them, and util.parseArgs refuses them as positionals before it answers.
const [requestPath, reportsPath] = process.env.AOT_BUILTINS_WORKER_FILES?.split("\n") ?? process.argv.splice(2);
const { tablePath, calls }: WorkerRequest = JSON.parse(readFileSync(requestPath, "utf8"));

process.on("unhandledRejection", () => {});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { EventEmitter, getEventListeners } from "node:events";
import { closeSync, openSync } from "node:fs";
import { join, posix, win32 } from "node:path";
import { domainToASCII, domainToUnicode } from "node:url";
import { MIMEParams, MIMEType, parseEnv } from "node:util";
import { MIMEParams, MIMEType, parseArgs, parseEnv } from "node:util";
import { describe, test } from "../supported";
import { never } from "./differential/fixtures";
import { Log } from "./differential/log";
Expand Down Expand Up @@ -644,6 +644,86 @@ const cases: Case[] = [
typesIn: result,
see: () => [{ value: parseEnv("FIRST=1\nSECOND=two\n7=seven") }, { value: parseEnv("") }],
},
// parse_args.rs: an object that holds `values` without a prototype, the list `positionals` and, where they are asked for, the list `tokens` of objects.
// An element of `args` and a `default` come back as the operand held them, so an object of a class of the program may lie in `positionals`, in `tokens` and in `values`.
{
row: "native:Bindgen:parse_args.bind.ts:parseArgs",
mode: "Call",
place: "the result",
typesIn: result,
see: () => [
{
value: parseArgs({
args: ["--name", "made-for-this-test", "-v", "first", "--no-color", "--", "last"],
options: {
name: { type: "string" },
verbose: { type: "boolean", short: "v" },
color: { type: "boolean" },
listed: { type: "string", multiple: true, default: new (class Words extends Array<string> {})("p", "q") },
plain: { type: "string", default: "kept" },
},
allowPositionals: true,
allowNegative: true,
tokens: true,
}),
},
{
value: parseArgs({
args: ["--many", "1", "--many", "2"],
options: { many: { type: "string", multiple: true } },
}),
},
{ value: parseArgs({ args: [] }) },
{
value: parseArgs({
args: [
new (class Path {
toString() {
return "a/b";
}
})() as unknown as string,
"--",
5 as unknown as string,
],
allowPositionals: true,
tokens: true,
}),
},
],
},
// html_rewriter.rs transform(): a string for a string, a new ArrayBuffer for a buffer or a view of any kind, a new Response for a Response.
{
row: "HTMLRewriter.prototype.transform",
mode: "Call",
place: "the result",
typesIn: result,
see: () => {
const rewriter = new HTMLRewriter().on("p", {
element(element) {
element.setAttribute("seen", "yes");
},
});
const text = "<p>made for this test</p>";
const bytes = () => new TextEncoder().encode(text);
const shared = new SharedArrayBuffer(text.length);
new Uint8Array(shared).set(bytes());
return [
text,
"",
bytes().buffer,
new ArrayBuffer(0),
shared,
new Uint8Array(shared),
bytes(),
new DataView(bytes().buffer),
new Float64Array(8),
Buffer.from(text),
new Response(text),
new (class Answer extends Response {})(text),
new Response(new Blob([text])),
].map(input => ({ value: rewriter.transform(input as Response) }));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
},
},
{
row: "native:NewCppFunction:JSEventTargetNode.cpp:jsFunctionNodeEventsGetEventListeners",
mode: "Call",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// util.parseArgs hands back an element of `args` and a `default` as the program made them: the same object, of whatever class, with its members.
import { parseArgs } from "node:util";
class Path {
toString() {
return "a/b";
}
parts() {
return `${this}`.split("/");
}
}
class Words extends Array {
shout() {
return this.map(word => word.toUpperCase());
}
}
const said = [];
for (let turn = 0; turn < 30; turn++) {
said.length = 0;
const path = new Path();
const url = new URL("http://host/a?b=1");
const bytes = new Uint8Array([1, 2, 3]);
const words = Words.of("p", "q");
const operands = [path, url, bytes, turn];
const { values, positionals, tokens } = parseArgs({
args: [path, "--name", url, "--", bytes, turn],
options: { name: { type: "string" }, listed: { type: "string", multiple: true, default: words } },
strict: false,
allowPositionals: true,
tokens: true,
});
said.push(
positionals[0] === path,
positionals[0].parts().join(),
values.name === url,
values.name.searchParams.get("b"),
);
said.push(positionals[1] === bytes, positionals[1].byteLength, typeof positionals[2], positionals[2] === turn);
said.push(
values.listed === words,
values.listed.shout().join(),
Object.getPrototypeOf(values) === null,
Array.isArray(positionals),
);
said.push(tokens.map(token => `${token.kind}:${token.index}:${operands.indexOf(token.value)}`).join(" "));
}
console.log(said.join(" "));
123 changes: 123 additions & 0 deletions test/aot/trimmed-is-whole/cases/classes-that-a-call-may-return.js.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,126 @@ import { createHistogram } from "node:perf_hooks";
const histogram = createHistogram();
histogram.record(5);
console.log(histogram.count, histogram.max);
//// the-text-that-a-rewriter-of-HTML-answers
//@ removes Bun.build
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
const rewriter = new HTMLRewriter().on("p", {
element(element) {
element.setAttribute("seen", "yes");
},
});
console.log(rewriter.transform("<p>text</p>"), rewriter.transform("") === "");
//// the-bytes-that-a-rewriter-of-HTML-answers
//@ removes Bun.build
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
const rewriter = new HTMLRewriter().on("p", {
element(element) {
element.setAttribute("seen", "yes");
},
});
const bytes = () => new TextEncoder().encode("<p>bytes</p>");
const shared = new SharedArrayBuffer(12);
new Uint8Array(shared).set(bytes());
const decoder = new TextDecoder();
for (const input of [bytes().buffer, bytes(), new DataView(bytes().buffer), shared, new Uint8Array(shared)]) {
const answer = rewriter.transform(input);
console.log(answer.byteLength, decoder.decode(answer));
}
//// the-response-that-a-rewriter-of-HTML-answers
//@ removes Bun.build
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
const rewriter = new HTMLRewriter().on("p", {
element(element) {
element.setAttribute("seen", "yes");
},
});
const response = rewriter.transform(new Response("<p>response</p>", { status: 201 }));
console.log(typeof response, response.status, await response.text());
const ofBlob = rewriter.transform(new Response(new Blob(["<p>blob</p>"]), { status: 202 }));
console.log(ofBlob.status, await ofBlob.text());
//// the-response-of-a-file-that-a-rewriter-of-HTML-answers
//@ file cwd/page.html "<p>from a file</p>\n"
//@ removes Bun.build
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
const rewriter = new HTMLRewriter().on("p", {
element(element) {
element.setAttribute("seen", "yes");
},
});
const read = () => rewriter.transform(new Response(Bun.file("page.html")));
const decoder = new TextDecoder();
console.log(await read().text());
const blob = await read().blob();
console.log(decoder.decode(await read().arrayBuffer()), blob.size, await blob.text(), decoder.decode(await read().bytes()));
//// what-a-rewriter-of-HTML-refuses
//@ removes Bun.build
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
const rewriter = new HTMLRewriter();
for (const refused of [new Blob(["<p>blob</p>"]), 5, undefined]) {
try {
rewriter.transform(refused);
} catch (error) {
console.log(error.name, error.message);
}
}
//// the-arguments-that-node-util-parses
//@ removes Bun.build
//@ removes Bun.serve
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
import { parseArgs } from "node:util";
class Words extends Array {}
const words = Words.of("p", "q");
const { values, positionals, tokens } = parseArgs({
args: ["--name", "x", "-v", "first", "--no-color", "--", "last"],
options: {
name: { type: "string" },
verbose: { type: "boolean", short: "v" },
color: { type: "boolean" },
listed: { type: "string", multiple: true, default: words },
plain: { type: "string", default: "kept" },
},
allowPositionals: true,
allowNegative: true,
tokens: true,
});
console.log(Object.getPrototypeOf(values), Object.keys(values).join(), "toString" in values);
console.log(values.name.toUpperCase(), values.verbose, values.color, values.plain, values.listed === words, values.listed instanceof Words);
console.log(positionals.join(), tokens.map(token => `${token.kind}:${token.name ?? token.value}:${token.index}`).join(" "));
const repeated = parseArgs({ args: ["--many", "1", "--many", "2"], options: { many: { type: "string", multiple: true } } });
console.log(repeated.values.many.map(Number).join(), repeated.tokens);
class Path {
toString() {
return "a/b";
}
parts() {
return `${this}`.split("/");
}
}
const path = new Path();
const through = parseArgs({ args: [path, "--", 5], allowPositionals: true, tokens: true });
console.log(through.positionals[0] === path, through.positionals[0].parts().join(), through.tokens[0].value.parts().length, typeof through.positionals[1]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
try {
parseArgs({ args: ["--nope"], options: {} });
} catch (error) {
console.log(error.code);
}
//// what-node-util-prints-of-an-object
//@ removes Bun.build
//@ removes Bun.serve
//@ removes bun:test
//@ removes Bun.Cookie
//@ removes SubtleCrypto
import { inspect, promisify } from "node:util";
console.log(inspect({ a: 1, list: [1, "two"] }), typeof promisify);
Loading