Skip to content
Draft
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
3 changes: 2 additions & 1 deletion scripts/build-docs-gallery-nav.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// transform, re-pointed at this repo's own docs.json.
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
getCatalogTab,
readCatalogGalleryData,
Expand Down Expand Up @@ -43,5 +44,5 @@ export function buildNav(docs) {
console.log(`Rebuilt Catalog nav: ${groups.length} groups, ${data.items.length} items.`);
}

if (import.meta.url === `file://${process.argv[1]}`)
if (process.argv[1] === fileURLToPath(import.meta.url))
buildNav(resolveDocsRoot(process.argv[2]).docs);
3 changes: 2 additions & 1 deletion scripts/check-comment-citations.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import ts from "typescript";

const SCANNABLE = new Set([".ts", ".tsx", ".mjs", ".js", ".css", ".py", ".md"]);
Expand Down Expand Up @@ -998,7 +999,7 @@ function proveGateCanFail() {
process.exit(2);
}

if (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const root = process.cwd();
proveGateCanFail();
const explicit = process.argv.slice(2);
Expand Down
2 changes: 1 addition & 1 deletion scripts/check-docs-snippet-motion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export function auditSnippets(dir = SNIPPETS_DIR) {
.filter((finding) => finding.problems.length > 0);
}

if (import.meta.url === `file://${process.argv[1]}`) {
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const failures = auditSnippets();
if (failures.length > 0) {
console.error("Docs snippets that autoplay must honour prefers-reduced-motion:\n");
Expand Down
3 changes: 2 additions & 1 deletion scripts/check-no-main-deletions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const BASE_FLAG = "--base";

Expand Down Expand Up @@ -1984,4 +1985,4 @@ function main() {
process.exit(1);
}

if (import.meta.url === `file://${process.argv[1]}`) main();
if (process.argv[1] === fileURLToPath(import.meta.url)) main();
3 changes: 2 additions & 1 deletion scripts/comment-ratchet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { extname } from "node:path";
import { fileURLToPath } from "node:url";
import { commentBlocks, generatedPaths } from "./check-comment-citations.mjs";

const MAX_BLOCK_LINES = 12;
Expand Down Expand Up @@ -118,7 +119,7 @@ function packageShare(pkg, isNew, generated) {
return share(own.lines > 0 ? own : packageTotal("packages", isNew, generated));
}

if (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const ref = git(["merge-base", process.env.COMMENT_CHECK_BASE ?? "origin/main", "HEAD"]).trim();
const generated = generatedPaths(
process.cwd(),
Expand Down
184 changes: 181 additions & 3 deletions scripts/file-url-paths.test.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { execFileSync, spawnSync } from "node:child_process";
import {
copyFileSync,
mkdirSync,
mkdtempSync,
readFileSync,
realpathSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { it } from "node:test";
import { pathToFileURL } from "node:url";

const ROOT = join(import.meta.dirname, "..");

Expand All @@ -19,3 +30,170 @@ it("package code turns file URLs into paths with fileURLToPath, not .pathname",
);
assert.deepEqual(offenders, []);
});

const ENTRYPOINTS = [
"check-comment-citations.mjs",
"check-docs-snippet-motion.mjs",
"check-no-main-deletions.mjs",
"comment-ratchet.mjs",
"validate-release-channel.mjs",
"build-docs-gallery-nav.mjs",
"generate-media-use-svg-sanitize.mjs",
];

function checkout(t) {
const root = realpathSync(mkdtempSync(join(tmpdir(), "hyperframes entrypoints %#-")));
t.after(() => rmSync(root, { recursive: true, force: true }));
const write = (file, source) => {
mkdirSync(dirname(join(root, file)), { recursive: true });
writeFileSync(join(root, file), source);
};
for (const file of [
...ENTRYPOINTS.map((name) => `scripts/${name}`),
"scripts/docs-catalog-shared.mjs",
"scripts/gate-probes/broken-citation.md",
"packages/core/src/figma/sanitizeSvg.ts",
]) {
mkdirSync(dirname(join(root, file)), { recursive: true });
copyFileSync(join(ROOT, file), join(root, file));
}
symlinkSync(join(ROOT, "node_modules"), join(root, "node_modules"), "junction");
symlinkSync(
join(ROOT, "packages/core/node_modules"),
join(root, "packages/core/node_modules"),
"junction",
);
const git = (...args) => execFileSync("git", args, { cwd: root, encoding: "utf8" });
const run = (name, args = [], env = {}) =>
spawnSync(process.execPath, [join(root, "scripts", name), ...args], {
cwd: root,
encoding: "utf8",
env: { ...process.env, ...env },
});
const commit = () =>
git(
"-c",
"user.name=Test",
"-c",
"user.email=test@example.com",
"commit",
"--no-gpg-sign",
"-qm",
"fixture",
);
return { root, write, git, commit, run };
}

it("runs release validation from a path needing URL encoding", (t) => {
const { run } = checkout(t);
const env = {
EVENT_NAME: "pull_request",
VERSION: "0.4.24",
DIST_TAG: "alpha",
PR_HEAD_REF: "release/v0.4.24",
};
const invalid = run("validate-release-channel.mjs", [], env);
assert.equal(invalid.status, 1, invalid.stderr);
assert.match(invalid.stderr, /must publish with npm dist-tag "latest"/);
const valid = run("validate-release-channel.mjs", [], { ...env, DIST_TAG: "latest" });
assert.equal(valid.status, 0, valid.stderr);
assert.match(valid.stdout, /Release channel validated/);
});

it("checks autoplaying docs snippets from a path needing URL encoding", (t) => {
const { write, run } = checkout(t);
write("docs/snippets/grid.jsx", "export const Grid = () => <video autoPlay />;\n");
const result = run("check-docs-snippet-motion.mjs");
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /grid\.jsx.*Grid/);
assert.match(result.stderr, /prefers-reduced-motion/);
});

it("reports deleted files from a path needing URL encoding", (t) => {
const { root, write, git, commit, run } = checkout(t);
git("init", "-q");
write("removed.txt", "keep this file\n");
git("add", "removed.txt");
commit();
const base = git("rev-parse", "HEAD").trim();
rmSync(join(root, "removed.txt"));
git("add", "removed.txt");
commit();
const result = run("check-no-main-deletions.mjs", ["--base", base]);
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /This branch deletes 1 files/);
assert.match(result.stderr, /removed\.txt/);
});

it("checks comment citations from a path needing URL encoding", (t) => {
const { write, git, run } = checkout(t);
git("init", "-q");
write("bad.ts", "// See `missing-source.ts`.\nexport const value = 1;\n");
git("add", "bad.ts", "scripts/gate-probes/broken-citation.md");
const result = run("check-comment-citations.mjs", ["bad.ts"]);
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /comments FAILED/);
assert.match(result.stderr, /missing-source\.ts/);
});

it("checks comment share from a path needing URL encoding", (t) => {
const { write, git, commit, run } = checkout(t);
git("init", "-q");
const file = "packages/example/src/value.ts";
write(file, "export const value = 1;\n");
git("add", file);
commit();
write(file, "// A comment above the value.\nexport const value = 1;\n");
const result = run("comment-ratchet.mjs", [], { COMMENT_CHECK_BASE: "HEAD" });
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, /RAISED comment share/);
assert.match(result.stderr, /packages\/example\/src\/value\.ts/);
});

it("generates catalog navigation from a path needing URL encoding", (t) => {
const { root, write, run } = checkout(t);
write("docs/docs.json", JSON.stringify({ navigation: { tabs: [{ tab: "Catalog" }] } }));
write(
"docs/snippets/catalog-gallery-data.mdx",
`export const catalogGalleryData = ${JSON.stringify({
groups: [{ id: "titles", label: "Titles" }],
items: [{ group: "titles", section: "Titles", href: "/catalog/title" }],
})};\n`,
);
const result = run("build-docs-gallery-nav.mjs");
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /Rebuilt Catalog nav: 1 groups, 1 items/);
const config = JSON.parse(readFileSync(join(root, "docs/docs.json"), "utf8"));
assert.deepEqual(config.navigation.tabs[0].groups[0].pages[1].pages, ["catalog/title"]);
});

it("generates the SVG sanitizer from a path needing URL encoding", (t) => {
const { root, write, run } = checkout(t);
const output = "packages/cli/src/media-use/lib/svg-sanitize.mjs";
write(output, "stale output\n");
const result = run("generate-media-use-svg-sanitize.mjs");
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /wrote .*svg-sanitize\.mjs/);
const generated = readFileSync(join(root, output), "utf8");
assert.match(generated, /GENERATED/);
assert.match(generated, /export.*sanitizeSvg/);
});

for (const name of ENTRYPOINTS) {
it(`does not run ${name} when imported`, (t) => {
const { root } = checkout(t);
const url = pathToFileURL(join(root, "scripts", name)).href;
const result = spawnSync(
process.execPath,
[
"--input-type=module",
"--eval",
`await import(${JSON.stringify(url)}); console.log("imported");`,
],
{ cwd: root, encoding: "utf8" },
);
assert.equal(result.status, 0, result.stderr);
assert.equal(result.stdout, "imported\n");
assert.equal(result.stderr, "");
});
}
6 changes: 4 additions & 2 deletions scripts/generate-media-use-svg-sanitize.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const ENTRY = resolve(here, "../packages/core/src/figma/sanitizeSvg.ts");
const OUTFILE = resolve(here, "../packages/cli/src/media-use/lib/svg-sanitize.mjs");
// scripts/ has no node_modules of its own (bun's per-consumer linking); reach into the one
// package that already depends on esbuild rather than adding a root-level dependency.
const { build } = await import(resolve(here, "../packages/core/node_modules/esbuild/lib/main.js"));
const { build } = await import(
new URL("../packages/core/node_modules/esbuild/lib/main.js", import.meta.url).href
);

const HEADER = `// GENERATED — do not hand-edit. Source: packages/core/src/figma/sanitizeSvg.ts, bundled by
// scripts/generate-media-use-svg-sanitize.mjs so media-use's zero-install \`node --test\` lane can
Expand All @@ -31,7 +33,7 @@ export async function generate() {
return HEADER + result.outputFiles[0].text;
}

if (import.meta.url === `file://${process.argv[1]}`) {
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const code = await generate();
writeFileSync(OUTFILE, code);
console.log(`wrote ${OUTFILE} (${code.length} bytes)`);
Expand Down
3 changes: 2 additions & 1 deletion scripts/validate-release-channel.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const VERSION_RE = /^\d+\.\d+\.\d+(?:-([0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*)?$/;
const PRERELEASE_BRANCH_RE = /^origin\/(next|alpha|beta|rc|canary|prerelease\/.+)$/;
Expand Down Expand Up @@ -129,6 +130,6 @@ function main() {
reportValidation(input, validateReleaseChannel(input));
}

if (import.meta.url === `file://${process.argv[1]}`) {
if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}