mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
- Enforce oxlint and oxfmt for owned JavaScript and TypeScript with independent CI checks. - Preserve compiler import boundaries and protected fixtures while cleaning up lint findings. - Standardize source formatting and validate import, fixture, and embedded-source protections.
137 lines
4.6 KiB
JavaScript
137 lines
4.6 KiB
JavaScript
/* Measure AST child access independently of parser/checker IPC. Every sample
|
|
* starts with a fresh adapter and checks child identities against the wire. */
|
|
import assert from "node:assert/strict";
|
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
import { parseArgs } from "node:util";
|
|
import { AstFile } from "../packages/compiler/dist/frontend/ts7/ast-node.js";
|
|
import { AstWireFile } from "../packages/compiler/dist/frontend/ts7/ast-wire.js";
|
|
import {
|
|
astChildNames,
|
|
KIND_NODE_LIST,
|
|
} from "../packages/compiler/dist/frontend/ts7/ast-schema.generated.js";
|
|
import { Ts7RpcClient } from "../packages/compiler/dist/frontend/ts7/rpc-client.js";
|
|
import { spawnTs7Wire } from "../packages/compiler/dist/frontend/ts7/rpc-process.js";
|
|
import { ts7Executable } from "../packages/compiler/dist/frontend/ts7/rpc-api.js";
|
|
|
|
const { values } = parseArgs({
|
|
options: {
|
|
iterations: { type: "string", default: "5" },
|
|
repeats: { type: "string", default: "16" },
|
|
functions: { type: "string", default: "2000" },
|
|
source: { type: "string" },
|
|
},
|
|
});
|
|
const iterations = Number(values.iterations);
|
|
const repeats = Number(values.repeats);
|
|
const functions = Number(values.functions);
|
|
for (const [name, value, maximum] of [
|
|
["iterations", iterations, 100],
|
|
["repeats", repeats, 1000],
|
|
["functions", functions, 100000],
|
|
]) {
|
|
assert.ok(
|
|
Number.isInteger(value) && value >= 1 && value <= maximum,
|
|
`--${name} must be an integer between 1 and ${maximum}`,
|
|
);
|
|
}
|
|
const directory = await mkdtemp(join(tmpdir(), "scriptc-bench-ast-"));
|
|
let bytes;
|
|
try {
|
|
const source = values.source
|
|
? await readFile(resolve(values.source), "utf8")
|
|
: Array.from(
|
|
{ length: functions },
|
|
(_, i) =>
|
|
`export function fn${i}<T>(value: T, amount = ${i}): T { if (amount > 0) return value; return value; }`,
|
|
).join("\n");
|
|
const file = join(directory, "main.ts");
|
|
const config = join(directory, "tsconfig.json");
|
|
await writeFile(file, source);
|
|
await writeFile(
|
|
config,
|
|
JSON.stringify({ compilerOptions: { types: [], target: "esnext" }, files: [file] }),
|
|
);
|
|
const rpc = new Ts7RpcClient(spawnTs7Wire(ts7Executable(), ["--api", "--cwd", directory]));
|
|
try {
|
|
rpc.requestText("initialize", "null");
|
|
const snapshot = JSON.parse(
|
|
rpc.requestText("updateSnapshot", JSON.stringify({ openProjects: [config] })),
|
|
);
|
|
bytes = rpc.requestBytes(
|
|
"getSourceFile",
|
|
Buffer.from(
|
|
JSON.stringify({ snapshot: snapshot.snapshot, project: snapshot.projects[0].id, file }),
|
|
),
|
|
);
|
|
} finally {
|
|
rpc.close();
|
|
}
|
|
} finally {
|
|
await rm(directory, { recursive: true, force: true });
|
|
}
|
|
const wire = new AstWireFile(bytes);
|
|
const queries = [];
|
|
for (let index = 1; index < wire.nodeCount; index++) {
|
|
const kind = wire.kind(index);
|
|
if (kind === KIND_NODE_LIST) continue;
|
|
for (const name of astChildNames(kind).split(",").filter(Boolean)) {
|
|
const expected = wire.namedChild(index, name);
|
|
queries.push({
|
|
index,
|
|
name,
|
|
expected,
|
|
list: expected !== 0 && wire.kind(expected) === KIND_NODE_LIST,
|
|
});
|
|
}
|
|
}
|
|
const samples = [];
|
|
for (let iteration = -2; iteration < iterations; iteration++) {
|
|
const file = new AstFile(bytes);
|
|
const nodes = queries.map(({ index }) => file.node(index));
|
|
const answers = new Uint32Array(queries.length);
|
|
const start = performance.now();
|
|
for (let repeat = 0; repeat < repeats; repeat++) {
|
|
for (let i = 0; i < queries.length; i++) {
|
|
const query = queries[i];
|
|
if (query.list) {
|
|
const list = nodes[i].childList(query.name);
|
|
answers[i] = list?.length ?? 0;
|
|
} else {
|
|
answers[i] = nodes[i].childNode(query.name)?.index ?? 0;
|
|
}
|
|
}
|
|
}
|
|
const ms = performance.now() - start;
|
|
for (let i = 0; i < queries.length; i++) {
|
|
const query = queries[i];
|
|
if (query.list) {
|
|
const expected = wire.list(query.expected);
|
|
assert.equal(answers[i], expected.length);
|
|
assert.deepEqual(
|
|
nodes[i].childList(query.name).map((node) => node.index),
|
|
expected,
|
|
);
|
|
} else assert.equal(answers[i], query.expected);
|
|
}
|
|
if (iteration >= 0) samples.push(ms);
|
|
}
|
|
const sorted = [...samples].sort((a, b) => a - b);
|
|
const middle = Math.floor(sorted.length / 2);
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
node: process.version,
|
|
source: values.source ?? "generated",
|
|
nodes: wire.nodeCount,
|
|
queries: queries.length,
|
|
repeats,
|
|
median_ms: sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2,
|
|
samples_ms: samples,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|