Skip to content

Commit 1f8ac89

Browse files
committed
feat(WASM): allow data-url to point to VTK package registry tar.gz file
1 parent 8a69653 commit 1f8ac89

File tree

5 files changed

+85
-21
lines changed

5 files changed

+85
-21
lines changed

docs/guide/js/plain.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ In this example we tag the script to autoload WASM and create a global vtk names
3434
<<< ../../public/demo/example.js
3535
:::
3636

37+
In this example we tag the script to autoload WASM directly from the VTK repository's package registry and create a global vtk namespace. You can customize the wasm architecture and version by changing the data-url.
38+
39+
::: code-group
40+
<<< ../../public/demo/plain-javascript-annotation-wasm-registry.html
41+
<<< ../../public/demo/example.js
42+
:::
43+
3744
## Configuration options
3845

3946
The method `createNamespace(url, config)` takes two arguments. The first one is used to specify the base directory where the wasm file from VTK will be find. When the module is loaded, the __url__ parameter could be skipped. For the __config__ it is aimed to tune how you would like your WASM environement to behave. The following sections cover the various options and what it means.
@@ -50,4 +57,4 @@ The method `createNamespace(url, config)` takes two arguments. The first one is
5057
- WebGPU only works with the asynchronous implementation of method execution.
5158
- This require WebAssembly JavaScript Promise Integration (JSPI) support in your browser
5259

53-
For the __annotation__ usecase you can add `data-config="{'rendering': 'webgpu'}"` attribute in your HTML to adjust the config setting.
60+
For the __annotation__ usecase you can add `data-config="{'rendering': 'webgpu'}"` attribute in your HTML to adjust the config setting.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<html>
2+
<head>
3+
<script
4+
src="https://unpkg.com/@kitware/vtk-wasm/vtk-umd.js"
5+
id="vtk-wasm"
6+
data-url="https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-wasm32-emscripten/9.5.20250913/vtk-9.5.20250913-wasm32-emscripten.tar.gz"
7+
></script>
8+
<script src="example.js"></script>
9+
</head>
10+
<body>
11+
<canvas id="vtk-wasm-window"></canvas>
12+
<script>
13+
vtkReady.then((vtk) => {
14+
buildWASMScene(vtk); // Also available on window.vtk
15+
});
16+
</script>
17+
</body>
18+
</html>

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@
2525
"semantic-release": "semantic-release"
2626
},
2727
"dependencies": {
28+
"js-untar": "^2.0.0",
2829
"jszip": "3.10.1"
2930
},
3031
"devDependencies": {
32+
"conventional-changelog-conventionalcommits": "9.1.0",
3133
"eslint": "^9.22.0",
3234
"globals": "^16.0.0",
3335
"prettier": "3.5.3",
3436
"rimraf": "^6.0.1",
3537
"semantic-release": "24.2.7",
36-
"conventional-changelog-conventionalcommits": "9.1.0",
3738
"vite": "^6.2.4",
3839
"vitepress": "^1.6.3"
3940
},

src/wasmLoader.js

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import untar from "js-untar";
2+
13
const LOADED_URLS = [];
24
const PROMISES = {};
5+
let WASM_FILE_OBJECT = null;
36

47
/**
58
* Create a future that returns
@@ -33,18 +36,22 @@ function isSameConfig(a, b) {
3336
}
3437

3538
export function generateWasmConfig(config) {
36-
if (config?.rendering === "webgpu") {
37-
console.log("WASM use WebGPU");
38-
return {
39-
preRun: [
40-
function (module) {
41-
module.ENV.VTK_GRAPHICS_BACKEND = "WEBGPU";
42-
},
43-
],
44-
};
39+
return {
40+
locateFile: (fileName) => {
41+
if (WASM_FILE_OBJECT && fileName == WASM_FILE_OBJECT.name) {
42+
return URL.createObjectURL(WASM_FILE_OBJECT);
43+
}
44+
return new URL(fileName, import.meta.url).href;
45+
},
46+
onRuntimeInitialized: (module) => {
47+
if (WASM_FILE_OBJECT) {
48+
URL.revokeObjectURL(WASM_FILE_OBJECT);
49+
}
50+
},
51+
preRun: config?.rendering === "webgpu" ? [(module) => {
52+
module.ENV.VTK_GRAPHICS_BACKEND = "WEBGPU";
53+
}] : [],
4554
}
46-
console.log("WASM use WebGL2");
47-
return {};
4855
}
4956

5057
/**
@@ -148,14 +155,34 @@ export class VtkWASMLoader {
148155
let jsModuleURL = null;
149156

150157
// Try newest version first
151-
url = `${wasmBaseURL}/${wasmBaseName}WebAssembly${this.config?.exec === "async" ? "Async" : ""}.mjs`;
152-
const newModuleResponse = await fetch(url);
153-
if (newModuleResponse.ok) {
154-
// In docker we serve the index.html when file don't exist
155-
const content = await newModuleResponse.text();
156-
if (content[0] !== "<") {
157-
// Not html content
158-
jsModuleURL = url;
158+
let newModuleResponse = null;
159+
if (wasmBaseURL.startsWith("http") && wasmBaseURL.endsWith(".gz")) {
160+
// Absolute URL pointing to a GZIP file.
161+
newModuleResponse = await fetch(wasmBaseURL);
162+
const ds = new DecompressionStream("gzip");
163+
const decompressedStream = newModuleResponse.body.pipeThrough(ds);
164+
const blob = await new Response(decompressedStream).blob();
165+
const arrayBuffer = await blob.arrayBuffer();
166+
const files = await untar(arrayBuffer);
167+
files.forEach((file) => {
168+
file.name = file.name.replace("./", "");
169+
if (file.name === `${wasmBaseName}WebAssembly${this.config?.exec === "async" ? "Async" : ""}.mjs`) {
170+
jsModuleURL = URL.createObjectURL(new File([file.buffer], file.name, { type: "text/javascript" }));
171+
} else if (file.name === `${wasmBaseName}WebAssembly${this.config?.exec === "async" ? "Async" : ""}.wasm`) {
172+
// Create a file URL for the wasm file so it can be loaded
173+
WASM_FILE_OBJECT = new File([file.buffer], file.name, { type: "application/wasm" });
174+
}
175+
});
176+
} else {
177+
url = `${wasmBaseURL}/${wasmBaseName}WebAssembly${this.config?.exec === "async" ? "Async" : ""}.mjs`;
178+
newModuleResponse = await fetch(url);
179+
if (newModuleResponse.ok) {
180+
// In docker we serve the index.html when file don't exist
181+
const content = await newModuleResponse.text();
182+
if (content[0] !== "<") {
183+
// Not html content
184+
jsModuleURL = url;
185+
}
159186
}
160187
}
161188

@@ -181,6 +208,10 @@ export class VtkWASMLoader {
181208
// Load JS
182209
console.log("WASM use", jsModuleURL);
183210
await loadScriptAsModule(jsModuleURL);
211+
// Cleanup object URL corresponding to the JavaScript module.
212+
if (jsModuleURL.startsWith("blob:")) {
213+
URL.revokeObjectURL(jsModuleURL);
214+
}
184215
}
185216

186217
// Load WASM

0 commit comments

Comments
 (0)