From 7c868a36659db94ff06eb738a0a6db853cfa2848 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:20:45 -0400 Subject: [PATCH] gallery conversion script --- packages/sandcastle/package.json | 2 + packages/sandcastle/scripts/buildGallery.js | 2 +- packages/sandcastle/scripts/convertGallery.js | 122 ++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 packages/sandcastle/scripts/convertGallery.js diff --git a/packages/sandcastle/package.json b/packages/sandcastle/package.json index db721b3ebc..67a82f1975 100644 --- a/packages/sandcastle/package.json +++ b/packages/sandcastle/package.json @@ -32,6 +32,8 @@ "@vitejs/plugin-react": "^4.3.4", "globals": "^15.15.0", "globby": "^14.1.0", + "jsdom": "^26.1.0", + "slugify": "^1.6.6", "typescript": "~5.7.2", "vite": "^6.2.0", "vite-plugin-static-copy": "^2.3.1", diff --git a/packages/sandcastle/scripts/buildGallery.js b/packages/sandcastle/scripts/buildGallery.js index b75dcb19e4..0bbdf9dbb8 100644 --- a/packages/sandcastle/scripts/buildGallery.js +++ b/packages/sandcastle/scripts/buildGallery.js @@ -75,7 +75,7 @@ export function buildGalleryList(galleryDirectory, includeDevelopment = true) { // Validate metadata if ( - check(!/^[a-zA-Z0-9-]+$/.test(slug), `"${slug}" is not a valid slug`) || + check(!/^[a-zA-Z0-9-.]+$/.test(slug), `"${slug}" is not a valid slug`) || check(!title, `${slug} - Missing title`) || check(!description, `${slug} - Missing description`) ) { diff --git a/packages/sandcastle/scripts/convertGallery.js b/packages/sandcastle/scripts/convertGallery.js new file mode 100644 index 0000000000..88206c168c --- /dev/null +++ b/packages/sandcastle/scripts/convertGallery.js @@ -0,0 +1,122 @@ +import { + copyFileSync, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, +} from "fs"; +import { basename, dirname, join } from "path"; +import { fileURLToPath } from "url"; +import { JSDOM } from "jsdom"; +import { stringify } from "yaml"; +import slugify from "slugify"; +import * as prettier from "prettier"; +import * as babelPlugin from "prettier/plugins/babel"; +import * as estreePlugin from "prettier/plugins/estree"; +import * as htmlPlugin from "prettier/plugins/html"; +import { globbySync } from "globby"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const inputDirectory = join(__dirname, "../../../Apps/Sandcastle/gallery"); +const outputDirectory = join(__dirname, "../gallery"); + +async function convertSandcastle(path, developerOnly = false) { + const htmlFile = readFileSync(path, "utf-8"); + const { document } = new JSDOM(htmlFile, { + url: "https://example.com", + }).window; + + const name = basename(path, ".html"); + let slug = slugify(name, { + lower: true, + }).replaceAll(/[\(\)]/g, ""); + + const thumbnailPath = path.replace(".html", ".jpg"); + + console.log(`processing ${developerOnly ? "dev" : ""}`, slug); + + let title = document.title; + if (title === "Cesium Demo") { + title = name; + } + const labelsMeta = document.querySelector( + "meta[name=cesium-sandcastle-labels]", + )?.content; + const description = document.querySelector("meta[name=description]")?.content; + + const labels = labelsMeta.split(",").map((s) => s.trim()); + if (developerOnly && !labels.includes("Development")) { + labels.push("Development"); + } + + const scriptElem = document.querySelector("#cesium_sandcastle_script"); + let script = scriptElem?.textContent?.match( + /\/\/Sandcastle_Begin((?:.|\n)*)\/\/Sandcastle_End/m, + )?.[1]; + + if (script?.includes("Sandcastle")) { + script = `import Sandcastle from "Sandcastle";\n${script}`; + } + script = `import * as Cesium from "cesium";\n${script}`; + const formattedScript = await prettier.format(script, { + parser: "babel", + // @ts-expect-error the estree plugin has no type https://github.com/prettier/prettier/issues/16501 + plugins: [babelPlugin, estreePlugin], + }); + + document.body.removeChild(scriptElem); + const html = document.body.innerHTML; + const formattedHtml = await prettier.format(html, { + parser: "html", + plugins: [htmlPlugin], + }); + + if (developerOnly) { + // force a dev suffix so names are deterministic so this script can be run multiple times + slug += "-dev"; + title += " - Dev"; + } + + const metadata = { + legacyId: (developerOnly ? "development/" : "") + basename(path), + title, + description, + labels, + thumbnail: existsSync(thumbnailPath) ? "thumbnail.jpg" : undefined, + development: developerOnly ? true : undefined, + }; + + const dir = join(outputDirectory, slug); + mkdirSync(dir, { recursive: true }); + const yaml = stringify(metadata, { + lineWidth: 0, + }); + writeFileSync(join(dir, "sandcastle.yaml"), yaml); + if (script) { + writeFileSync(join(dir, "main.js"), formattedScript); + } + if (html) { + writeFileSync(join(dir, "index.html"), formattedHtml); + } + if (existsSync(thumbnailPath)) { + copyFileSync(thumbnailPath, join(dir, "thumbnail.jpg")); + } +} + +async function main() { + const htmlFiles = globbySync([`${inputDirectory}/*.html`]); + + for (const file of htmlFiles) { + await convertSandcastle(file, false); + } + + const developerHtmlFiles = globbySync([ + `${inputDirectory}/development/*.html`, + ]); + for (const file of developerHtmlFiles) { + await convertSandcastle(file, true); + } +} + +main();