Files
workinf_Blender_Wasm/web/app/vite.config.ts
mes123456 0fe8d2bb56
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

246 lines
11 KiB
TypeScript

import { defineConfig, type Plugin } from "vite";
import react from "@vitejs/plugin-react";
// @ts-expect-error The runtime is Node; this project intentionally avoids a browser dependency on Node types.
import fs from "node:fs";
// @ts-expect-error The runtime is Node; this project intentionally avoids a browser dependency on Node types.
import path from "node:path";
// @ts-expect-error The runtime is Node; this project intentionally avoids a browser dependency on Node types.
import { fileURLToPath } from "node:url";
const deploymentContract = JSON.parse(
fs.readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs/web/deployment-contract.json"), "utf8"),
) as { responseHeaders: { allResponses: Record<string, string> } };
const isolationHeaders = deploymentContract.responseHeaders.allResponses;
const runtimeEnvironment = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env;
const unisolatedSingleThreadTest = runtimeEnvironment?.WEB_TEST_UNISOLATED_SINGLE_THREAD === "1";
const developmentHeaders = unisolatedSingleThreadTest ? {} : isolationHeaders;
function preserveIsolationHeaders(): Plugin {
const install = (server: { middlewares: { use: (handler: (request: unknown, response: { setHeader: (name: string, value: string) => void }, next: () => void) => void) => void } }) => {
server.middlewares.use((_request, response, next) => {
for (const [name, value] of Object.entries(isolationHeaders)) response.setHeader(name, value);
next();
});
};
return {
name: "preserve-isolation-headers",
configureServer: install,
configurePreviewServer: install,
};
}
function serveEngineVariantModuleImports(): Plugin {
const vendorRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "public/vendor/blender");
const install = (server: { middlewares: { use: (handler: (request: unknown, response: { setHeader: (name: string, value: string | number) => void }, next: () => void) => void) => void } }) => {
server.middlewares.use((request, response, next) => {
const url = new URL((request as { url?: string }).url ?? "/", "http://vite.local");
const match = url.pathname.match(/^\/vendor\/blender\/(single|pthread)\/web_engine\.js$/);
if (!match || !url.searchParams.has("import")) { next(); return; }
const filePath = path.join(vendorRoot, match[1], "web_engine.js");
if (!fs.existsSync(filePath)) { next(); return; }
const stat = fs.statSync(filePath);
response.setHeader("Content-Type", "text/javascript; charset=utf-8");
response.setHeader("Content-Length", stat.size);
for (const [name, value] of Object.entries(developmentHeaders)) response.setHeader(name, value);
fs.createReadStream(filePath).pipe(response as never);
});
};
return {
name: "serve-engine-variant-module-imports",
configureServer: install,
configurePreviewServer: install,
};
}
function localVDBFixture(): Plugin {
const resourceRoot = runtimeEnvironment?.VDB_RESOURCE_ROOT ?? path.join(runtimeEnvironment?.HOME ?? "", "resource-library/blender-web-vdb");
const files = new Map([
["/__vdb_fixture__/manifest", { path: path.join(resourceRoot, "manifests/generated-smoke.nanovdb.json"), type: "application/json" }],
["/__vdb_fixture__/report", { path: path.join(resourceRoot, "reports/generated-smoke-conversion.json"), type: "application/json" }],
["/__vdb_fixture__/bundle", { path: path.join(resourceRoot, "nanovdb/generated-smoke.nvdb"), type: "application/x-nanovdb" }],
["/assets/volumes/generated-smoke.nanovdb.json", { path: path.join(resourceRoot, "manifests/generated-smoke.nanovdb.json"), type: "application/json" }],
["/assets/volumes/generated-smoke.nvdb", { path: path.join(resourceRoot, "nanovdb/generated-smoke.nvdb"), type: "application/x-nanovdb" }],
["/volumes/generated-smoke.nanovdb.json", { path: path.join(resourceRoot, "manifests/generated-smoke.nanovdb.json"), type: "application/json" }],
["/volumes/generated-smoke.nvdb", { path: path.join(resourceRoot, "nanovdb/generated-smoke.nvdb"), type: "application/x-nanovdb" }],
]);
return {
name: "local-vdb-fixture",
configureServer(server) {
server.middlewares.use((request, response, next) => {
const nodeRequest = request as unknown as { url?: string; headers: { range?: string } };
const pathname = nodeRequest.url?.split("?", 1)[0] ?? "";
const fixture = files.get(pathname);
if (!fixture || !fs.existsSync(fixture.path)) { next(); return; }
const stat = fs.statSync(fixture.path);
response.setHeader("Content-Type", fixture.type);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Cache-Control", "no-store");
response.setHeader("ETag", `"vdb-${stat.size}-${Math.trunc(stat.mtimeMs)}"`);
const match = nodeRequest.headers.range?.match(/^bytes=(\d+)-(\d+)$/);
if (match) {
const start = Number(match[1]);
const end = Number(match[2]);
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end >= stat.size) { response.writeHead(416); response.end(); return; }
response.statusCode = 206;
response.setHeader("Content-Range", `bytes ${start}-${end}/${stat.size}`);
response.setHeader("Content-Length", end - start + 1);
fs.createReadStream(fixture.path, { start, end }).pipe(response);
return;
}
response.setHeader("Content-Length", stat.size);
fs.createReadStream(fixture.path).pipe(response);
});
},
};
}
// Materialize requested bytes over HTTP without checking a 64 MiB performance-only fixture into the repository.
const sparseBundleBytes = 64 * 1024 * 1024;
const sparseChunkBytes = 4 * 1024 * 1024;
const sparseStreamBytes = 64 * 1024;
const sparseChunkSha256 = "bb9f8df61474d25e71fa00722318cd387396ca1736605e1248821cc0de3d3af8";
const sparseBundleSha256 = "3b6a07d0d404fab4e23b6d34bc6696a6a312dd92821332385e5af7c01c421351";
const sparseConverterSha256 = "0000000000000000000000000000000000000000000000000000000000000000";
function sparseBundleManifest(): Record<string, unknown> {
return {
schemaVersion: 1,
projectId: "vdb-sparse-64m",
sourcePath: "//volumes/generated-synthetic-sparse.vdb",
sourceSha256: sparseBundleSha256,
conversionRequestSha256: sparseConverterSha256,
bundlePath: "//volumes/generated-sparse-64m.nvdb",
bundleByteLength: sparseBundleBytes,
bundleSha256: sparseBundleSha256,
converter: {
target: "DESKTOP",
blenderVersion: "5.2.0",
openVDBVersion: "13.0.0",
nanoVDBVersion: "32.9.0",
executableSha256: sparseConverterSha256,
},
grids: [{
name: "density",
valueType: "FLOAT16",
gridClass: "FOG_VOLUME",
semantic: "DENSITY",
activeVoxelCount: 0,
segmentByteOffset: 0,
segmentByteLength: sparseBundleBytes,
byteOffset: 0,
byteLength: sparseBundleBytes,
indexBounds: { min: [0, 0, 0], max: [0, 0, 0] },
worldBounds: { min: [0, 0, 0], max: [0, 0, 0] },
voxelSize: [1, 1, 1],
indexToWorld: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
}],
chunks: Array.from({ length: sparseBundleBytes / sparseChunkBytes }, (_, index) => ({
index,
byteOffset: index * sparseChunkBytes,
byteLength: sparseChunkBytes,
sha256: sparseChunkSha256,
})),
material: {
densityGrid: "density",
densityScale: 1,
emissionScale: 0,
temperatureScale: 1,
anisotropy: 0,
interpolation: "NEAREST",
},
gpu: {
representation: "NANOVDB_STORAGE_BUFFER",
byteAlignment: 32,
pageByteLength: 256 * 1024,
maxResidentBytes: 1 * 1024 * 1024,
shaderSemanticVersion: "volume-wgsl-v1",
},
};
}
function serveSparseVDBFixture(): Plugin {
return {
name: "serve-sparse-64m-vdb-fixture",
configureServer(server) {
server.middlewares.use((request, response, next) => {
const nodeRequest = request as unknown as { url?: string; headers: { range?: string } };
const parsed = new URL(nodeRequest.url ?? "/", "http://vite.local");
if (parsed.pathname !== "/__vdb_sparse_64m__/manifest" && parsed.pathname !== "/__vdb_sparse_64m__/bundle") { next(); return; }
const output = response as unknown as {
statusCode: number;
setHeader: (name: string, value: string | number) => void;
writeHead: (status: number) => void;
write: (data: Uint8Array) => boolean;
end: (data?: string) => void;
on: (event: string, listener: () => void) => void;
once: (event: string, listener: () => void) => void;
};
if (parsed.pathname.endsWith("/manifest")) {
const body = JSON.stringify(sparseBundleManifest());
output.statusCode = 200;
output.setHeader("Content-Type", "application/json");
output.setHeader("Cache-Control", "no-store");
output.setHeader("Content-Length", body.length);
output.end(body);
return;
}
const match = nodeRequest.headers.range?.match(/^bytes=(\d+)-(\d+)$/);
const start = match ? Number(match[1]) : 0;
const end = match ? Number(match[2]) : sparseBundleBytes - 1;
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end >= sparseBundleBytes) {
output.statusCode = 416;
output.end();
return;
}
const length = end - start + 1;
const delayMs = Math.min(25, Math.max(0, Number.parseInt(parsed.searchParams.get("delayMs") ?? "0", 10) || 0));
output.statusCode = match ? 206 : 200;
output.setHeader("Content-Type", "application/x-nanovdb");
output.setHeader("Accept-Ranges", "bytes");
output.setHeader("Cache-Control", "no-store");
output.setHeader("ETag", '"vdb-sparse-64m-v1"');
output.setHeader("Content-Length", length);
if (match) output.setHeader("Content-Range", `bytes ${start}-${end}/${sparseBundleBytes}`);
let closed = false;
let written = 0;
const zeroes = new Uint8Array(sparseStreamBytes);
output.on("close", () => { closed = true; });
const pump = (): void => {
if (closed) return;
if (written >= length) { output.end(); return; }
const nextLength = Math.min(sparseStreamBytes, length - written);
const accepted = output.write(nextLength === zeroes.byteLength ? zeroes : zeroes.slice(0, nextLength));
written += nextLength;
const continuePump = (): void => { if (!closed) setTimeout(pump, delayMs); };
if (!accepted) output.once("drain", continuePump);
else continuePump();
};
pump();
});
},
};
}
export default defineConfig({
root: "app",
plugins: [
serveEngineVariantModuleImports(),
...(unisolatedSingleThreadTest ? [] : [preserveIsolationHeaders()]),
localVDBFixture(),
serveSparseVDBFixture(),
react(),
],
server: {
port: 5173,
strictPort: false,
headers: developmentHeaders,
},
preview: {
headers: developmentHeaders,
},
build: {
outDir: "../dist",
emptyOutDir: true,
},
});