/** @vitest-environment node */ import { mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; import { build } from "vite"; async function listFiles(directory: string): Promise { const entries = await readdir(directory, { withFileTypes: true }); const files = await Promise.all( entries.map(async (entry) => { const path = join(directory, entry.name); return entry.isDirectory() ? listFiles(path) : [path]; }), ); return files.flat(); } describe("production model format main-bundle boundary", () => { it("keeps schema, limits, handler metadata, and public format imports decoder-free", async () => { const formatDirectory = dirname(fileURLToPath(import.meta.url)); const modelImportDirectory = resolve(formatDirectory, ".."); const packageRoot = resolve(formatDirectory, "../../.."); const outputRoot = await mkdtemp( join(tmpdir(), "toolcraft-model-main-vite-"), ); const outputDirectory = join(outputRoot, "dist"); const entryPath = join(outputRoot, "entry.mjs"); try { await writeFile( entryPath, [ `import { defineToolcraft } from ${JSON.stringify( resolve(packageRoot, "src/schema/define-toolcraft.ts"), )};`, `import { normalizeToolcraftModelImportLimits } from ${JSON.stringify( resolve(modelImportDirectory, "model-import-limits.ts"), )};`, `import { createToolcraftModelSourceAssetHandler } from ${JSON.stringify( resolve(modelImportDirectory, "model-source-asset-handler.ts"), )};`, `import { TOOLCRAFT_MODEL_FILE_DROP_ACCEPT } from ${JSON.stringify( resolve(formatDirectory, "index.ts"), )};`, `import { TOOLCRAFT_ADVERTISED_MODEL_FORMATS as barrelFormats } from ${JSON.stringify( resolve(modelImportDirectory, "index.ts"), )};`, `import { createToolcraftLazyThreeModelRenderBinding, ToolcraftRoot } from ${JSON.stringify( resolve(packageRoot, "src/react/index.ts"), )};`, "globalThis.__toolcraftModelMainBundleSmoke = {", " barrelFormats,", " createToolcraftLazyThreeModelRenderBinding,", " createToolcraftModelSourceAssetHandler,", " defineToolcraft,", " normalizeToolcraftModelImportLimits,", " ToolcraftRoot,", " accept: TOOLCRAFT_MODEL_FILE_DROP_ACCEPT,", "};", ].join("\n"), ); await build({ build: { emptyOutDir: true, minify: false, outDir: outputDirectory, rollupOptions: { input: entryPath }, }, configFile: false, logLevel: "silent", root: packageRoot, }); const emittedFiles = await listFiles(outputDirectory); const javaScriptFiles = emittedFiles.filter((path) => path.endsWith(".js")); const sources = await Promise.all( javaScriptFiles.map((path) => readFile(path, "utf8")), ); const entrySource = sources.find((source) => source.includes("__toolcraftModelMainBundleSmoke"), ); expect(entrySource, emittedFiles.join("\n")).toBeDefined(); expect(entrySource).toContain("TOOLCRAFT_MODEL_FILE_DROP_ACCEPT"); expect(entrySource).not.toContain("data:application/wasm;base64"); expect(entrySource).not.toContain("bundled-draco-decoder-unavailable"); expect(entrySource).not.toContain("createDecoderModule"); expect(entrySource).not.toContain("MeshoptDecoder"); expect(entrySource).not.toContain("ALL_EXTENSIONS"); expect(entrySource).not.toContain("draco_decoder_gltf.wasm"); expect(entrySource).not.toContain("new WebIO"); expect(entrySource).not.toContain("class WebGLRenderer"); const threeAdapterDeclaration = /function createToolcraftThreeModelRenderAdapter\(/u; expect(entrySource).not.toMatch(threeAdapterDeclaration); expect( sources.some( (source) => source !== entrySource && threeAdapterDeclaration.test(source), ), ).toBe(true); expect(javaScriptFiles.length).toBeGreaterThan(1); expect(entrySource).not.toContain( "TOOLCRAFT_GLTF_MODEL_FORMAT_ADAPTERS", ); expect(entrySource).not.toContain("toolcraftGlbModelFormatAdapter"); expect(entrySource).not.toContain("class OBJLoader"); expect(entrySource).not.toContain("class PLYLoader"); expect(entrySource).not.toContain("class STLLoader"); expect(entrySource).not.toContain("class FBXLoader"); expect(entrySource).not.toContain("toolcraftObjModelFormatAdapter"); expect(entrySource).not.toContain("toolcraftFbxModelFormatAdapter"); } finally { await rm(outputRoot, { force: true, recursive: true }); } }, 30_000); });