Skip to content

Commit 9375caf

Browse files
authored
fix: bind TSL preview runtime inside generated module (#66)
1 parent 73d69a0 commit 9375caf

2 files changed

Lines changed: 57 additions & 23 deletions

File tree

packages/schema/src/index.test.ts

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
44
import { dirname, join } from "node:path";
55
import { fileURLToPath } from "node:url";
66
import { shaderManifestSchema, validateShaderManifestFile } from "./index.ts";
7+
import { buildTslPreviewModule } from "./tsl-preview-module.ts";
78

89
const fixtureManifestPath = fileURLToPath(
910
new URL("../../../shaders/gradient-radial/shader.json", import.meta.url),
@@ -12,25 +13,30 @@ const sourcedFixtureManifestPath = fileURLToPath(
1213
new URL("../../../shaders/vignette-postprocess/shader.json", import.meta.url),
1314
);
1415

15-
function runTest(name: string, callback: () => void) {
16-
try {
17-
callback();
18-
console.log(`ok ${name}`);
19-
} catch (error) {
20-
console.error(`not ok ${name}`);
21-
throw error;
16+
function runTest(name: string, callback: () => void | Promise<void>) {
17+
const result = callback();
18+
if (result instanceof Promise) {
19+
return result.then(
20+
() => console.log(`ok ${name}`),
21+
(error) => {
22+
console.error(`not ok ${name}`);
23+
throw error;
24+
},
25+
);
2226
}
27+
28+
console.log(`ok ${name}`);
2329
}
2430

25-
runTest("validates the seed shader manifest", () => {
31+
await runTest("validates the seed shader manifest", () => {
2632
const manifest = validateShaderManifestFile(fixtureManifestPath);
2733

2834
assert.equal(manifest.name, "gradient-radial");
2935
assert.equal(manifest.recipes.length, 2);
3036
assert.ok(manifest.compatibility.environments.includes("three"));
3137
});
3238

33-
runTest("validates an adapted upstream shader manifest", () => {
39+
await runTest("validates an adapted upstream shader manifest", () => {
3440
const manifest = validateShaderManifestFile(sourcedFixtureManifestPath);
3541

3642
assert.equal(manifest.name, "vignette-postprocess");
@@ -42,7 +48,7 @@ runTest("validates an adapted upstream shader manifest", () => {
4248
);
4349
});
4450

45-
runTest("rejects invalid uniform defaults", () => {
51+
await runTest("rejects invalid uniform defaults", () => {
4652
const manifest = JSON.parse(readFileSync(fixtureManifestPath, "utf8")) as Record<string, unknown>;
4753
const uniforms = manifest.uniforms as Array<Record<string, unknown>>;
4854

@@ -53,7 +59,7 @@ runTest("rejects invalid uniform defaults", () => {
5359
assert.equal(result.success, false);
5460
});
5561

56-
runTest("rejects adapted manifests without exact provenance", () => {
62+
await runTest("rejects adapted manifests without exact provenance", () => {
5763
const manifest = JSON.parse(readFileSync(fixtureManifestPath, "utf8")) as Record<string, unknown>;
5864

5965
manifest.provenance = {
@@ -69,7 +75,7 @@ runTest("rejects adapted manifests without exact provenance", () => {
6975
assert.equal(result.success, false);
7076
});
7177

72-
runTest("rejects missing referenced files", () => {
78+
await runTest("rejects missing referenced files", () => {
7379
const tempDirectory = mkdtempSync(join(tmpdir(), "shaderbase-schema-"));
7480
const shaderDirectory = join(tempDirectory, "gradient-radial");
7581
const recipeDirectory = join(shaderDirectory, "recipes");
@@ -104,7 +110,7 @@ runTest("rejects missing referenced files", () => {
104110
}
105111
});
106112

107-
runTest("defaults language to glsl when missing", () => {
113+
await runTest("defaults language to glsl when missing", () => {
108114
const manifest = JSON.parse(readFileSync(fixtureManifestPath, "utf8")) as Record<string, unknown>;
109115
delete manifest.language;
110116

@@ -115,7 +121,7 @@ runTest("defaults language to glsl when missing", () => {
115121
}
116122
});
117123

118-
runTest("validates a TSL manifest", () => {
124+
await runTest("validates a TSL manifest", () => {
119125
const manifest = JSON.parse(readFileSync(fixtureManifestPath, "utf8")) as Record<string, unknown>;
120126
manifest.language = "tsl";
121127
manifest.tslEntry = "source.ts";
@@ -133,7 +139,7 @@ runTest("validates a TSL manifest", () => {
133139
}
134140
});
135141

136-
runTest("rejects TSL manifest without tslEntry", () => {
142+
await runTest("rejects TSL manifest without tslEntry", () => {
137143
const manifest = JSON.parse(readFileSync(fixtureManifestPath, "utf8")) as Record<string, unknown>;
138144
manifest.language = "tsl";
139145
delete manifest.files;
@@ -142,4 +148,34 @@ runTest("rejects TSL manifest without tslEntry", () => {
142148
assert.equal(result.success, false);
143149
});
144150

151+
await runTest("buildTslPreviewModule binds runtime imports inside createPreview", async () => {
152+
const previewModule = buildTslPreviewModule(`
153+
import { color } from 'three/tsl';
154+
import { NodeMaterial } from 'three/webgpu';
155+
156+
export function createMaterial() {
157+
const material = new NodeMaterial();
158+
material.colorNode = color(0xff0000);
159+
return material;
160+
}
161+
`);
162+
163+
const module = await import(`data:text/javascript,${encodeURIComponent(previewModule)}`);
164+
165+
class FakeNodeMaterial {
166+
colorNode?: number;
167+
}
168+
169+
const preview = module.createPreview({
170+
THREE: { NodeMaterial: FakeNodeMaterial },
171+
TSL: { color: (value: number) => value },
172+
width: 512,
173+
height: 512,
174+
pipeline: "surface",
175+
});
176+
177+
assert.ok(preview.material instanceof FakeNodeMaterial);
178+
assert.equal((preview.material as FakeNodeMaterial).colorNode, 0xff0000);
179+
});
180+
145181
console.log("schema tests passed");

packages/schema/src/tsl-preview-module.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,17 @@ function normalizeTslSource(sourceCode: string) {
8181
export function buildTslPreviewModule(sourceCode: string) {
8282
const { normalizedSource, tslBindings, webgpuBindings } = normalizeTslSource(sourceCode)
8383

84-
const moduleBody = [
84+
const createPreviewBody = [
85+
'const TSL = runtime.TSL;',
86+
'const THREE = runtime.THREE;',
8587
buildDestructureLine('TSL', tslBindings),
8688
buildDestructureLine('THREE', webgpuBindings),
8789
normalizedSource.trim(),
88-
`
89-
export function createPreview(runtime) {
90-
const material = createMaterial();
91-
return { material };
92-
}
93-
`.trim(),
90+
'const material = createMaterial();',
91+
'return { material };',
9492
]
9593
.filter(Boolean)
9694
.join('\n\n')
9795

98-
return `${moduleBody}\n`
96+
return `export function createPreview(runtime) {\n${createPreviewBody}\n}\n`
9997
}

0 commit comments

Comments
 (0)