mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-22 05:59:31 +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.
138 lines
5.1 KiB
TypeScript
138 lines
5.1 KiB
TypeScript
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { expect, test } from "vitest";
|
|
import { analyze } from "../src/index.js";
|
|
|
|
test.each([
|
|
["assignment", 'let key = Symbol.for("x"); key = Symbol.for("y"); class C { [key] = true; }'],
|
|
[
|
|
"shorthand assignment",
|
|
'var key = Symbol.for("x"); ({ key } = { key: Symbol.for("y") }); class C { [key] = true; }',
|
|
],
|
|
[
|
|
"array assignment",
|
|
'var key = Symbol.for("x"); [key] = [Symbol.for("y")]; class C { [key] = true; }',
|
|
],
|
|
[
|
|
"redeclaration",
|
|
'var key = Symbol.for("x"); var key = Symbol.for("y"); class C { [key] = true; }',
|
|
],
|
|
["early class key", 'class C { [key] = true; } var key = Symbol.for("x");'],
|
|
[
|
|
"early hoisted call",
|
|
'read(); var key = Symbol.for("x"); function read() { return key; } class C { [key] = true; }',
|
|
],
|
|
[
|
|
"early shorthand call",
|
|
'const callbacks = { read }; callbacks.read(); var key = Symbol.for("x"); function read() { return key; } class C { [key] = true; }',
|
|
],
|
|
[
|
|
"for-of assignment",
|
|
'var key = Symbol.for("x"); for (key of [Symbol.for("y")]) {} class C { [key] = true; }',
|
|
],
|
|
[
|
|
"runtime registry name",
|
|
'function text() { return "x"; } const key = Symbol.for(text()); class C { [key] = true; }',
|
|
],
|
|
["local symbol", 'class C { constructor() { const key = Symbol("x"); this[key] = true; } }'],
|
|
[
|
|
"distinct symbol collision",
|
|
'const key = Symbol("x"); const other = Symbol.for("x"); class C { [key] = true; [other] = false; }',
|
|
],
|
|
])("refuses symbol fields with %s", (_name, source) => {
|
|
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-fields-"));
|
|
try {
|
|
const entry = join(dir, "main.cjs");
|
|
writeFileSync(entry, source + "\nnew C();\n");
|
|
const { coverage } = analyze(entry);
|
|
const diagnostics = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])];
|
|
expect(
|
|
diagnostics.some(
|
|
(d) => d.code === "SC1090" && /symbol|computed class fields/i.test(d.message),
|
|
),
|
|
JSON.stringify(coverage, null, 2),
|
|
).toBe(true);
|
|
expect(coverage.stats.statementsIsland).toBe(0);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test.each([
|
|
[
|
|
"constant registry name",
|
|
'const text = "x"; const key = Symbol.for(text); class C { [key] = true; }',
|
|
],
|
|
["uninitialized JavaScript field", 'const key = Symbol.for("x"); class C { [key]; }'],
|
|
])("accepts symbol fields with %s", (_name, source) => {
|
|
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-fields-"));
|
|
try {
|
|
const entry = join(dir, "main.cjs");
|
|
writeFileSync(entry, source + "\nnew C();\n");
|
|
const { coverage } = analyze(entry);
|
|
expect(coverage.preflightFailed).toBe(false);
|
|
expect([...coverage.diagnostics, ...(coverage.runtimeFences ?? [])]).toEqual([]);
|
|
expect(coverage.stats.statementsIsland).toBe(0);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test.each([
|
|
["rest declaration", "const { name, ...copy } = value; console.log(copy);"],
|
|
["rest assignment", "let copy = {}; let name; ({ name, ...copy } = value); console.log(copy);"],
|
|
["Object.assign", "const copy = Object.assign({}, value); console.log(copy);"],
|
|
[
|
|
"Object.assign with several sources",
|
|
"const copy = Object.assign({}, value, { extra: 1 }); console.log(copy);",
|
|
],
|
|
[
|
|
"Object.assign with spread sources",
|
|
"const copy = Object.assign({}, ...[value]); console.log(copy);",
|
|
],
|
|
])("refuses %s copies of symbol fields", (_name, copy) => {
|
|
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-copy-"));
|
|
try {
|
|
const entry = join(dir, "main.cjs");
|
|
writeFileSync(
|
|
entry,
|
|
`const key = Symbol.for("x"); class Base { [key] = true; name = "base"; } class C extends Base {} const value = new C(); ${copy}`,
|
|
);
|
|
const { coverage } = analyze(entry);
|
|
const diagnostics = [...coverage.diagnostics, ...(coverage.runtimeFences ?? [])];
|
|
expect(
|
|
diagnostics.some((d) =>
|
|
/rest bindings|copying class instances|object spread/.test(d.message),
|
|
),
|
|
JSON.stringify(coverage, null, 2),
|
|
).toBe(true);
|
|
expect(coverage.stats.statementsIsland).toBe(0);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test.each([
|
|
["spread", "const copy = { ...value }; console.log(copy[key]);"],
|
|
[
|
|
"computed-key spread",
|
|
'let name = "extra"; const copy = { [name]: 1, ...value }; console.log(copy[key]);',
|
|
],
|
|
])("accepts %s copies of symbol fields", (_name, copy) => {
|
|
const dir = mkdtempSync(join(tmpdir(), "scriptc-symbol-copy-"));
|
|
try {
|
|
const entry = join(dir, "main.cjs");
|
|
writeFileSync(
|
|
entry,
|
|
`const key = Symbol.for("x"); class Base { [key] = true; name = "base"; } class C extends Base {} const value = new C(); ${copy}`,
|
|
);
|
|
const { coverage } = analyze(entry);
|
|
expect(coverage.preflightFailed).toBe(false);
|
|
expect([...coverage.diagnostics, ...(coverage.runtimeFences ?? [])]).toEqual([]);
|
|
expect(coverage.stats.statementsIsland).toBe(0);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|