@needle-tools/three-test-matrix
v0.2.0
Published
Shared Three.js version matrix and model cache helpers for browser compatibility tests.
Readme
@needle-tools/three-test-matrix
Shared infrastructure for browser compatibility tests that need to run code against several Three.js versions and renderer modes.
This is an ESM package for Node.js 18 and newer.
The package handles the repetitive parts of a Three.js matrix harness:
- resolve and cache Three.js npm tarballs
- include the current npm
threelatest by default - create WebGL/WebGPU import maps for cached Three.js packages
- generate one browser page per Three.js version and renderer mode
- generate custom matrix pages across extra axes such as fixtures or engine versions
- resolve and cache optional Needle Engine npm package versions
- bundle worker test entries against a specific cached Three.js package
- cache glTF/GLB roots plus referenced buffers, textures, and progressive LOD files
- serve raw filesystem cache paths through Vite
- render a Markdown compatibility table from a JSON artifact
Assertions, fixtures, and expected support policy stay in the project using the harness.
Install
npm install --save-dev @needle-tools/three-test-matrixMatrix Setup
Use prepareThreeMatrix when you want the standard cache, runtime, and page-generation flow:
import path from "node:path";
import {
prepareThreeMatrix,
rendererModes,
} from "@needle-tools/three-test-matrix";
const projectRoot = process.cwd();
const matrix = await prepareThreeMatrix({
cwd: projectRoot,
cacheRoot: path.join(projectRoot, ".cache", "three-test-matrix"),
pagesRoot: path.join(projectRoot, ".cache", "three-pages"),
rendererModes,
createPage({ runtime, rendererMode }) {
return renderCompatibilityPage({ runtime, rendererMode });
},
});
console.log(matrix.versions);
console.log(matrix.pagesManifest.pages.length);prepareThreeMatrix accepts explicit options for the common control points:
versions: exact Three.js package versions to testfromRevision: latest patch version for every Three.js minor from that revision onwarddefaultVersions: fallback versions when neitherversionsnorfromRevisionis providedincludeLatest: append the current npmthreelatest, enabled by defaultcacheRoot: shared cache locationthreeCacheRoot: override only the Three.js package cache locationrendererModes: renderer modes to generaterefresh: re-download cached filesincludeLocalRuntime: include the locally installedthreepackage, enabled by default
The lower-level helpers remain exported for custom harnesses: resolveRequestedVersions, cacheThreeVersions, createLocalThreeRuntime, createCachedThreeRuntime, createThreeImportMap, writeThreeMatrixPages, writeMatrixPages, resolveRendererModes, buildWorkerBundle, cacheModelGraph, and renderRendererMatrixMarkdown.
Cache Location
If cacheRoot is not provided, the default cache is shared on the machine:
$(npm config get cache)/_three-test-matrixScripts can expose their own command-line flags with parseMatrixArgs:
import {
parseMatrixArgs,
prepareThreeMatrix,
} from "@needle-tools/three-test-matrix";
const args = parseMatrixArgs(process.argv.slice(2));
await prepareThreeMatrix({
cacheRoot: args.cacheRoot,
versions: args.versions,
fromRevision: args.fromRevision,
refresh: args.refresh,
createPage,
pagesRoot,
});Then run the script with explicit flags:
node test/cache-three-pages.mjs --cache-root .cache/three-test-matrix --versions 0.169.0,0.184.0 --refreshRenderer Modes
The built-in renderer modes are:
webglwebgpu-force-webgl2webgpu
createThreeImportMap(runtime, rendererMode) maps three, three/addons/, three/examples/jsm/, three/webgpu, and three/nodes to the selected cached package.
Scripts that expose renderer filtering can use resolveRendererModes:
import { resolveRendererModes } from "@needle-tools/three-test-matrix";
const rendererModes = resolveRendererModes(process.argv.slice(2), {
envValue: process.env.THREE_MATRIX_RENDERER_MODES,
});Then run a subset:
node test/cache-pages.mjs --renderer-modes webgl,webgpuCustom Axes
Use writeMatrixPages when a project needs additional dimensions, such as USD fixtures or feature modes:
import { writeMatrixPages } from "@needle-tools/three-test-matrix";
const manifest = await writeMatrixPages({
pagesRoot,
clean: true,
axes: [{
name: "runtime",
values: runtimes,
pathPart: runtime => runtime.id,
idPart: runtime => runtime.id,
}, {
name: "rendererMode",
values: rendererModes,
}, {
name: "fixture",
values: fixtures,
pathPart: fixture => fixture.name,
idPart: fixture => fixture.name,
}],
createPage({ runtime, rendererMode, fixture }) {
return renderCompatibilityPage({ runtime, rendererMode, fixture });
},
createEntry({ runtime, rendererMode, fixture, id, pagePath }) {
return {
id,
version: runtime.id,
rendererMode,
fixtureName: fixture.name,
fixtureUrl: fixture.url,
pagePath,
};
},
});
console.log(manifest.pages.length);writeThreeMatrixPages is a convenience wrapper around this generic writer for the common runtime x rendererMode case.
Needle Engine
Needle Engine can be added as an optional package matrix. The default resolver selects the latest stable 4.x, latest stable 5.0.x, latest stable 5.1.x, and the latest stable patch for later minor or major releases as they appear on npm.
Needle Engine has two supported runtime shapes:
dist: the documented import-map shape.@needle-tools/enginepoints at the cached package'sdist/needle-engine.min.js,threepoints at that same package'sdist/three.min.js, andthree/addons/plusthree/examples/jsm/point at the matching@needle-tools/threepackage declared by that Engine version.module: the package-module shape. Dependencies are installed in the cache and@needle-tools/enginepoints at a browser ESM bundle built from the package export entry, withthreekept external so it still comes from the selected Engine runtime mapping. Addon/example prefixes also point at the same installed@needle-tools/threetree.
Use both when you want the same browser suite to run against both loading modes.
The plural helpers default to both shapes; pass runtimeShapes: ["dist"] or ["module"] to narrow the matrix.
import path from "node:path";
import {
cacheNeedleEngineVersions,
createCachedNeedleEngineRuntimes,
createNeedleEngineImportMap,
createThreeImportMap,
resolveNeedleEngineVersions,
} from "@needle-tools/three-test-matrix";
const engineCacheRoot = path.join(cacheRoot, "needle-engine-versions");
const engineVersions = resolveNeedleEngineVersions();
await cacheNeedleEngineVersions({
cacheRoot: engineCacheRoot,
versions: engineVersions,
runtimeShapes: ["dist", "module"],
refresh,
});
const engineRuntimes = await createCachedNeedleEngineRuntimes({
cacheRoot: engineCacheRoot,
versions: engineVersions,
runtimeShapes: ["dist", "module"],
});
function renderPage({ threeRuntime, engineRuntime, rendererMode }) {
const threeImportMap = createThreeImportMap(threeRuntime, rendererMode);
const importMap = createNeedleEngineImportMap(engineRuntime, {
baseImportMap: threeImportMap,
});
return `<!doctype html>
<script type="importmap">${JSON.stringify(importMap)}</script>
<script type="module" src="/src/my-engine-suite.js"></script>`;
}createNeedleEngineImportMap lets the selected Engine runtime's own three, three/addons/, and three/examples/jsm/ mappings win while keeping supplemental imports from the base map, such as project-specific packages. Runtime IDs include the shape suffix, for example @needle-tools/[email protected]:dist and @needle-tools/[email protected]:module.
When serving generated pages through Vite, include the Needle Engine cache root in rawFsServePlugin({ cacheRoots }).
For browser runners, the package exports explicit defaults for WebGPU-capable matrix runs:
import { webgpuDefaults } from "@needle-tools/three-test-matrix";
export default defineConfig({
use: {
...webgpuDefaults.playwright,
baseURL: "http://127.0.0.1:5199",
},
});Vitest browser projects can use the Vitest-shaped defaults:
import webgpuBrowserDefaults from "@needle-tools/three-test-matrix/vitest";
export default defineConfig({
test: {
browser: {
enabled: true,
...webgpuBrowserDefaults,
},
},
});The package includes a headed Chrome renderer self-test:
npm run test:browserThat test creates real Three.js renderers for every default milestone. It asserts webgpu-force-webgl2 initializes on a WebGL backend and webgpu initializes on a WebGPU backend when the browser and Three.js version support it.
Examples
Minimal starter harnesses live in examples/:
examples/playwright: generates one page per Three.js version and renderer mode, then runs the project suite with Playwright.examples/vitest-browser: generates the same matrix pages from Vitest global setup, then loads each page from a Vitest Browser test.
Both examples keep project assertions in browser-side modules that import three normally. The generated matrix pages provide the selected Three.js import map for each case.
Vite
Generated pages often point at files in the shared cache. Add the raw filesystem plugin to serve those files through Vite:
import { defineConfig } from "vite";
import { rawFsServePlugin } from "@needle-tools/three-test-matrix/vite";
export default defineConfig({
plugins: [
rawFsServePlugin({
cacheRoots: [".cache/three-test-matrix"],
headers: {
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Opener-Policy": "same-origin",
},
}),
],
});Publishing
Types are generated from the JavaScript sources and JSDoc comments:
npm run buildCheck the package contents before publishing:
npm run publish:dryprepack runs the type build automatically, so npm publish includes fresh dist/*.d.ts output.
Run the package type-shape check with the ESM-only ATTW profile:
npm run test:attw