From 375f4596a274aa7e5dd823e3712860aa29931e30 Mon Sep 17 00:00:00 2001 From: xiaoheiai4719-collab Date: Thu, 22 Oct 2026 13:39:17 +0900 Subject: [PATCH] fix(scripts): run entrypoints from URL-encoded checkout paths --- scripts/build-docs-gallery-nav.mjs | 3 +- scripts/check-comment-citations.mjs | 3 +- scripts/check-docs-snippet-motion.mjs | 2 +- scripts/check-no-main-deletions.mjs | 3 +- scripts/comment-ratchet.mjs | 3 +- scripts/file-url-paths.test.mjs | 184 +++++++++++++++++++- scripts/generate-media-use-svg-sanitize.mjs | 6 +- scripts/validate-release-channel.mjs | 3 +- 8 files changed, 196 insertions(+), 11 deletions(-) diff --git a/scripts/build-docs-gallery-nav.mjs b/scripts/build-docs-gallery-nav.mjs index 3457274ae3f..b0097c87e35 100644 --- a/scripts/build-docs-gallery-nav.mjs +++ b/scripts/build-docs-gallery-nav.mjs @@ -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, @@ -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); diff --git a/scripts/check-comment-citations.mjs b/scripts/check-comment-citations.mjs index 260db990e52..a0267e37735 100644 --- a/scripts/check-comment-citations.mjs +++ b/scripts/check-comment-citations.mjs @@ -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"]); @@ -985,7 +986,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); diff --git a/scripts/check-docs-snippet-motion.mjs b/scripts/check-docs-snippet-motion.mjs index 1a19f3c999f..28079aa5c3e 100644 --- a/scripts/check-docs-snippet-motion.mjs +++ b/scripts/check-docs-snippet-motion.mjs @@ -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"); diff --git a/scripts/check-no-main-deletions.mjs b/scripts/check-no-main-deletions.mjs index 5792c5ea4e3..611411ee869 100644 --- a/scripts/check-no-main-deletions.mjs +++ b/scripts/check-no-main-deletions.mjs @@ -21,6 +21,7 @@ */ import { execFileSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; const BASE_FLAG = "--base"; @@ -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(); diff --git a/scripts/comment-ratchet.mjs b/scripts/comment-ratchet.mjs index c86717fcc46..c4906853be5 100644 --- a/scripts/comment-ratchet.mjs +++ b/scripts/comment-ratchet.mjs @@ -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 } from "./check-comment-citations.mjs"; const MAX_BLOCK_LINES = 12; @@ -118,7 +119,7 @@ function packageShare(pkg, isNew) { return share(own.lines > 0 ? own : packageTotal("packages", isNew)); } -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(); // Against the working tree, where contents are read from; in CI that is HEAD. const changed = changedPaths(git(["diff", "--name-status", "-M", ref, "--", "packages"])) diff --git a/scripts/file-url-paths.test.mjs b/scripts/file-url-paths.test.mjs index 1e74f023673..2789dca083d 100644 --- a/scripts/file-url-paths.test.mjs +++ b/scripts/file-url-paths.test.mjs @@ -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, ".."); @@ -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 = () =>