Files
finos--perspective/docs/build.mjs
T
2026-07-13 12:23:53 +08:00

158 lines
6.2 KiB
JavaScript

// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
import puppeteer from "puppeteer";
import * as fs from "node:fs";
import * as cp from "node:child_process";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import EXAMPLES from "./src/data/features.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// features.js uses CJS exports.default, import it dynamically
// const EXAMPLES = (await import("./src/data/features.ts")).default;
const perspective = import(
"@perspective-dev/client/dist/esm/perspective.node.js"
);
const DEFAULT_VIEWPORT = {
width: 400,
height: 300,
};
async function run_with_theme(page, is_dark = false, order) {
await page.goto("http://localhost:8080/");
await page.setContent(template(is_dark));
await page.setViewport(DEFAULT_VIEWPORT);
await page.evaluate(async () => {
while (!window.__TEST_PERSPECTIVE_READY__) {
await new Promise((resolve) => setTimeout(resolve, 10));
}
});
await page.evaluate(async function () {
const viewer = document.querySelector("perspective-viewer");
await viewer.flush();
await viewer.toggleConfig();
});
for (const idx in EXAMPLES) {
const { config, viewport } = EXAMPLES[idx];
await page.setViewport(viewport || DEFAULT_VIEWPORT);
const new_config = Object.assign(
{
plugin: "Datagrid",
group_by: [],
expressions: {},
split_by: [],
sort: [],
aggregates: {},
},
config,
);
console.log(JSON.stringify(new_config));
await page.evaluate(async (config) => {
const viewer = document.querySelector("perspective-viewer");
await viewer.reset();
await viewer.restore(config);
}, new_config);
const screenshot = await page.screenshot({
captureBeyondViewport: false,
fullPage: true,
});
const name = `static/features/feature_${idx}${
is_dark ? "_dark" : ""
}.png`;
fs.writeFileSync(name, screenshot);
cp.execSync(`convert ${name} -resize 200x150 ${name}`);
}
const suffix = is_dark ? "_dark" : "";
const montage_files = order.map(
(idx) => `static/features/feature_${idx}${suffix}.png`,
);
cp.execSync(
`montage -mode concatenate -background none -tile 5x ${montage_files.join(
" ",
)} static/features/montage${is_dark ? "_dark" : "_light"}.png`,
);
}
async function run() {
if (
!fs.existsSync("static/features") ||
fs.readdirSync("static/features").length === 0
) {
console.log("Generating feature screenshots!");
fs.mkdirSync(path.join(__dirname, "static/features"), {
recursive: true,
});
const x = await perspective;
const server = new x.WebSocketServer({
assets: [
path.join(__dirname, "."),
path.join(__dirname, "../node_modules"),
],
});
const indices = Array.from({ length: EXAMPLES.length }, (_, i) => i);
for (let i = indices.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[indices[i], indices[j]] = [indices[j], indices[i]];
}
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await run_with_theme(page, false, indices);
await run_with_theme(page, true, indices);
await page.close();
await browser.close();
await server.close();
fs.writeFileSync(
path.join(__dirname, "static/features/montage_map.json"),
JSON.stringify({
tile_width: 200,
tile_height: 150,
columns: 5,
order: indices,
}),
);
}
if (!fs.existsSync("static/blocks")) {
fs.mkdirSync("static/blocks");
}
const { dist_examples } = await import("../examples/blocks/index.mjs");
await dist_examples(`${__dirname}/static/blocks`);
}
function template(is_dark) {
return fs
.readFileSync(path.join(__dirname, "template.html"))
.toString()
.replace("/css/pro.css", is_dark ? "/css/pro-dark.css" : "/css/pro.css")
.trim();
}
run();