Skip to content

Commit 7bd358c

Browse files
authored
Merge pull request #12874 from CesiumGS/gallery-build-path-fix
Sandcastle gallery build path fix
2 parents b2ba066 + d8dbe05 commit 7bd358c

File tree

4 files changed

+111
-53
lines changed

4 files changed

+111
-53
lines changed

packages/sandcastle/sandcastle.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const config = {
33
sourceUrl: "https://github.com/CesiumGS/cesium/blob/main/packages/sandcastle",
44
publicDir: "./public",
55
gallery: {
6-
files: ["gallery/**/*"],
6+
files: ["gallery"],
77
searchOptions: {
88
excerptLength: 10,
99
ranking: {

packages/sandcastle/scripts/buildGallery.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import createGalleryRecord from "./createGalleryRecord.js";
1515
const __dirname = dirname(fileURLToPath(import.meta.url));
1616
const defaultRootDirectory = join(__dirname, "..");
1717
const defaultPublicDirectory = "./public";
18-
const defaultGalleryFiles = ["gallery/**/*"];
18+
const defaultGalleryFiles = ["gallery"];
1919
const defaultThumbnailPath = "images/placeholder-thumbnail.jpg";
2020
const requiredMetadataKeys = ["title", "description"];
2121
const galleryItemConfig = /sandcastle\.(yml|yaml)/;
@@ -118,7 +118,7 @@ export async function buildGalleryList(options = {}) {
118118
};
119119

120120
const galleryFiles = await globby(
121-
galleryFilesPattern.map((pattern) => join(rootDirectory, pattern)),
121+
galleryFilesPattern.map((pattern) => join(rootDirectory, pattern, "**/*")),
122122
);
123123
const yamlFiles = galleryFiles.filter((path) =>
124124
basename(path).match(galleryItemConfig),
@@ -261,7 +261,7 @@ export async function buildGalleryList(options = {}) {
261261
// regardless of if titles match the directory names
262262
output.entries.sort((a, b) => a.title.localeCompare(b.title));
263263

264-
const outputDirectory = join(publicDirectory, "gallery");
264+
const outputDirectory = join(rootDirectory, publicDirectory, "gallery");
265265
await rimraf(outputDirectory);
266266
await mkdir(outputDirectory, { recursive: true });
267267

scripts/build.js

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import child_process from "child_process";
2-
import { existsSync, readFileSync, statSync } from "fs";
3-
import { readFile, writeFile } from "fs/promises";
4-
import { EOL } from "os";
5-
import path from "path";
6-
import { createRequire } from "module";
7-
import { finished } from "stream/promises";
1+
import child_process from "node:child_process";
2+
import { existsSync, statSync } from "node:fs";
3+
import { readFile, writeFile } from "node:fs/promises";
4+
import { EOL } from "node:os";
5+
import path from "node:path";
6+
import { finished } from "node:stream/promises";
7+
import { fileURLToPath } from "node:url";
88

99
import esbuild from "esbuild";
1010
import { globby } from "globby";
@@ -18,21 +18,23 @@ import { mkdirp } from "mkdirp";
1818
// This should match the scope of the dependencies of the root level package.json.
1919
const scope = "cesium";
2020

21-
const require = createRequire(import.meta.url);
22-
const packageJson = require("../package.json");
23-
let version = packageJson.version;
24-
if (/\.0$/.test(version)) {
25-
version = version.substring(0, version.length - 2);
21+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
22+
const projectRoot = path.join(__dirname, "..");
23+
const packageJsonPath = path.join(projectRoot, "package.json");
24+
25+
async function getVersion() {
26+
const data = await readFile(packageJsonPath, "utf8");
27+
const { version } = JSON.parse(data);
28+
return version;
2629
}
2730

28-
const copyrightHeaderTemplate = readFileSync(
29-
path.join("Source", "copyrightHeader.js"),
30-
"utf8",
31-
);
32-
const combinedCopyrightHeader = copyrightHeaderTemplate.replace(
33-
"${version}",
34-
version,
35-
);
31+
async function getCopyrightHeader() {
32+
const copyrightHeaderTemplate = await readFile(
33+
path.join("Source", "copyrightHeader.js"),
34+
"utf8",
35+
);
36+
return copyrightHeaderTemplate.replace("${version}", await getVersion());
37+
}
3638

3739
function escapeCharacters(token) {
3840
return token.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
@@ -164,7 +166,7 @@ export async function bundleCesiumJs(options) {
164166
buildConfig.plugins = options.removePragmas ? [stripPragmaPlugin] : undefined;
165167
buildConfig.write = options.write;
166168
buildConfig.banner = {
167-
js: combinedCopyrightHeader,
169+
js: await getCopyrightHeader(),
168170
};
169171
// print errors immediately, and collect warnings so we can filter out known ones
170172
buildConfig.logLevel = "info";
@@ -288,6 +290,7 @@ function generateDeclaration(workspace, file) {
288290
* @returns {Buffer} contents
289291
*/
290292
export async function createCesiumJs() {
293+
const version = await getVersion();
291294
let contents = `export const VERSION = '${version}';\n`;
292295

293296
// Iterate over each workspace and generate declarations for each file.
@@ -314,6 +317,7 @@ const workspaceSpecFiles = {
314317
* @returns {Buffer} contents
315318
*/
316319
export async function createCombinedSpecList() {
320+
const version = await getVersion();
317321
let contents = `export const VERSION = '${version}';\n`;
318322

319323
for (const workspace of Object.keys(workspaceSpecFiles)) {
@@ -387,7 +391,7 @@ export async function bundleWorkers(options) {
387391
workerConfig.format = "esm";
388392
workerConfig.splitting = true;
389393
workerConfig.banner = {
390-
js: combinedCopyrightHeader,
394+
js: await getCopyrightHeader(),
391395
};
392396
workerConfig.entryPoints = workers;
393397
workerConfig.outdir = path.join(options.path, "Workers");
@@ -606,17 +610,31 @@ const externalResolvePlugin = {
606610
};
607611

608612
/**
609-
* Creates a template html file in the Sandcastle app listing the gallery of demos
610-
* @param {boolean} [noDevelopmentGallery=false] true if the development gallery should not be included in the list
611-
* @returns {Promise<any>}
613+
* Parses Sandcastle config file and returns its values.
614+
* @returns {Promise<Record<string,any>>} A promise that resolves to the config values.
612615
*/
613-
export async function createGalleryList(noDevelopmentGallery) {
614-
const configPath = path.join(
615-
import.meta.url,
616-
"../../packages/sandcastle/sandcastle.config.js",
617-
);
618-
const config = await import(configPath);
619-
const { root: rootDirectory, gallery, sourceUrl } = config.default;
616+
export async function getSandcastleConfig() {
617+
const configPath = "packages/sandcastle/sandcastle.config.js";
618+
const configImportPath = path.join(projectRoot, configPath);
619+
const config = await import(configImportPath);
620+
const options = config.default;
621+
return {
622+
...options,
623+
configPath,
624+
};
625+
}
626+
627+
/**
628+
* Indexes Sandcastle gallery files and writes gallery files to the configured Sandcastle output directory.
629+
* @param {boolean} [includeDevelopment=true] true if gallery items flagged as development should be included.
630+
* @returns {Promise<void>} A promise that resolves once the gallery files have been indexed and written.
631+
*/
632+
export async function buildSandcastleGallery(includeDevelopment) {
633+
const { configPath, root, gallery, sourceUrl } = await getSandcastleConfig();
634+
635+
// Use an absolute path to avoid any descrepency between working directories
636+
// All other directories will be relative to the specified root directory
637+
const rootDirectory = path.join(path.dirname(configPath), root);
620638

621639
// Paths are specified relative to the config file
622640
const {
@@ -627,10 +645,12 @@ export async function createGalleryList(noDevelopmentGallery) {
627645
metadata,
628646
} = gallery ?? {};
629647

630-
// Import asynchronously for now while this script is excluded from the release zip
631-
const { buildGalleryList } = await import(
632-
"../packages/sandcastle/scripts/buildGallery.js"
648+
// Import asynchronously, for now, because this following script is not included in the release zip; However, this script will not be run from the release zip
649+
const buildGalleryScriptPath = path.join(
650+
__dirname,
651+
"../packages/sandcastle/scripts/buildGallery.js",
633652
);
653+
const { buildGalleryList } = await import(buildGalleryScriptPath);
634654

635655
await buildGalleryList({
636656
rootDirectory,
@@ -641,8 +661,17 @@ export async function createGalleryList(noDevelopmentGallery) {
641661
searchOptions,
642662
defaultFilters,
643663
metadata,
644-
includeDevelopment: !noDevelopmentGallery,
664+
includeDevelopment,
645665
});
666+
}
667+
668+
/**
669+
* Creates a template html file in the Sandcastle app listing the gallery of demos
670+
* @param {boolean} [noDevelopmentGallery=false] true if the development gallery should not be included in the list
671+
* @returns {Promise<any>}
672+
*/
673+
export async function createGalleryList(noDevelopmentGallery) {
674+
await buildSandcastleGallery(!noDevelopmentGallery);
646675

647676
const demoObjects = [];
648677
const demoJSONs = [];
@@ -655,7 +684,8 @@ export async function createGalleryList(noDevelopmentGallery) {
655684

656685
// In CI, the version is set to something like '1.43.0-branch-name-buildNumber'
657686
// We need to extract just the Major.Minor version
658-
const majorMinor = packageJson.version.match(/^(.*)\.(.*)\./);
687+
const version = await getVersion();
688+
const majorMinor = version.match(/^(.*)\.(.*)\./);
659689
const major = majorMinor[1];
660690
const minor = Number(majorMinor[2]) - 1; // We want the last release, not current release
661691
const tagVersion = `${major}.${minor}`;
@@ -887,6 +917,7 @@ export async function bundleTestWorkers(options) {
887917
* @returns
888918
*/
889919
export async function createIndexJs(workspace) {
920+
const version = await getVersion();
890921
let contents = `globalThis.CESIUM_VERSION = "${version}";\n`;
891922

892923
// Iterate over all provided source files for the workspace and export the assignment based on file name.

server.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
glslToJavaScript,
1919
createIndexJs,
2020
buildCesium,
21+
getSandcastleConfig,
22+
buildSandcastleGallery,
2123
} from "./scripts/build.js";
2224

2325
const argv = yargs(process.argv)
@@ -80,6 +82,22 @@ async function generateDevelopmentBuild() {
8082
return contexts;
8183
}
8284

85+
// Delay execution of the callback until a short time has elapsed since it was last invoked, preventing
86+
// calls to the same function in quick succession from triggering multiple builds.
87+
const throttleDelay = 500;
88+
const throttle = (callback) => {
89+
let timeout;
90+
return () =>
91+
new Promise((resolve) => {
92+
if (timeout) {
93+
clearTimeout(timeout);
94+
}
95+
timeout = setTimeout(() => {
96+
resolve(callback());
97+
}, throttleDelay);
98+
});
99+
};
100+
83101
(async function () {
84102
const gzipHeader = Buffer.from("1F8B08", "hex");
85103
const production = argv.production;
@@ -267,21 +285,30 @@ async function generateDevelopmentBuild() {
267285
specsCache.clear();
268286
});
269287

270-
const galleryDirectory = "packages/sandcastle/gallery";
271-
const galleryWatcher = chokidar.watch([galleryDirectory], {
272-
ignored: (file, stats) =>
273-
!!stats?.isFile() && !file.endsWith(".yml") && !file.endsWith(".yaml"),
274-
ignoreInitial: true,
275-
});
276288
if (!production) {
277-
const { buildGalleryList } = await import(
278-
"./packages/sandcastle/scripts/buildGallery.js"
289+
const { configPath, root, gallery } = await getSandcastleConfig();
290+
const baseDirectory = path.relative(root, path.dirname(configPath));
291+
const galleryFiles = gallery.files.map((pattern) =>
292+
path.join(baseDirectory, pattern),
279293
);
280-
galleryWatcher.on("all", async (event) => {
281-
if (event === "add" || event === "change" || event === "unlink") {
282-
await buildGalleryList(galleryDirectory);
283-
}
294+
const galleryWatcher = chokidar.watch(galleryFiles, {
295+
ignoreInitial: true,
284296
});
297+
298+
galleryWatcher.on(
299+
"all",
300+
throttle(async () => {
301+
const startTime = performance.now();
302+
try {
303+
await buildSandcastleGallery();
304+
console.log(
305+
`Gallery built in ${formatTimeSinceInSeconds(startTime)} seconds.`,
306+
);
307+
} catch (e) {
308+
console.error(e);
309+
}
310+
}),
311+
);
285312
}
286313

287314
// Rebuild jsHintOptions as needed and serve as-is

0 commit comments

Comments
 (0)