@vizhub/runtime 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -128
- package/dist/buildHTML.d.ts +14 -0
- package/dist/buildHTML.js +67 -0
- package/dist/common/packageJson.d.ts +1 -1
- package/dist/common/virtualFileSystem.d.ts +1 -1
- package/dist/determineRuntimeVersion.d.ts +2 -1
- package/dist/index.d.ts +3 -14
- package/dist/index.js +3 -67
- package/dist/test/testInBrowser.d.ts +1 -2
- package/dist/test/testStackTrace.d.ts +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/utils/vizContentToFileCollection.d.ts +1 -2
- package/dist/v2/computeBundleJSV2.d.ts +1 -1
- package/dist/v2/getComputedIndexHtml.d.ts +1 -1
- package/dist/v2/v2Build.d.ts +1 -1
- package/dist/v3/cleanRollupErrorMessage.d.ts +4 -0
- package/dist/v3/cleanRollupErrorMessage.js +9 -0
- package/dist/v3/computeBundleJSV3.d.ts +1 -2
- package/dist/v3/createVizContent.d.ts +1 -2
- package/dist/v3/setupV3Runtime.d.ts +15 -0
- package/dist/v3/setupV3Runtime.js +341 -0
- package/dist/v3/v3Build.d.ts +1 -2
- package/dist/v3/v3Build.js +1 -1
- package/dist/v3/vizLoad.js +1 -1
- package/dist/v4/index.d.ts +1 -1
- package/package.json +12 -10
package/README.md
CHANGED
@@ -9,6 +9,252 @@ A powerful, flexible runtime environment for executing code sandboxes in the bro
|
|
9
9
|
|
10
10
|
`@vizhub/runtime` intelligently detects the appropriate runtime version based on the provided files and generates executable HTML that can be used within an iframe's `srcdoc` attribute. It handles everything from simple HTML/JS/CSS combinations to complex module bundling, dependency resolution, and cross-viz imports.
|
11
11
|
|
12
|
+
## Runtime Versions
|
13
|
+
|
14
|
+
The library automatically detects which runtime version to use based on the files provided:
|
15
|
+
|
16
|
+
- **v1**: When only `index.html` is present
|
17
|
+
- **v2**: When both `index.html` and `index.js` (or `index.jsx`) are present
|
18
|
+
- **v3**: When only `index.js` is present (no `index.html`)
|
19
|
+
- **v4**: When `index.html` contains ES module scripts with import maps
|
20
|
+
|
21
|
+
| Feature | **V1** | **V2** | **V3** | **V4** |
|
22
|
+
| --------------------- | ----------------- | ------------------------------ | --------------------------------- | ---------------------------- |
|
23
|
+
| **When Used** | Only `index.html` | `index.html` + `index.js/.jsx` | Only `index.js` (no `index.html`) | `index.html` with ES modules |
|
24
|
+
| **`index.html`** | User-provided | User-provided | Auto-generated | User-provided |
|
25
|
+
| **ES Modules** | ☐ | ✅ | ✅ | ✅ |
|
26
|
+
| **UMD Libraries** | ✅ | ✅ | ✅ | ☐ |
|
27
|
+
| **ESM Libraries** | ☐ | ☐ | ☐ | ✅ |
|
28
|
+
| **JSX Support** | ☐ | ✅ | ☐ | ✅ |
|
29
|
+
| **Svelte Support** | ☐ | ☐ | ✅ | ☐ |
|
30
|
+
| **Cross-Viz Imports** | ☐ | ☐ | ✅ | ☐ |
|
31
|
+
| **State Management** | ☐ | ☐ | ✅ | ☐ |
|
32
|
+
| **Import from CSV** | ☐ | ☐ | ✅ | ☐ |
|
33
|
+
| **`fetch` proxy** | ✅ | ✅ | ☐ | ✅ |
|
34
|
+
| **Best For** | Simple HTML demos | React with CDN deps | Svelte / reusable D3 | Modern module-based apps |
|
35
|
+
|
36
|
+
## V1 Runtime
|
37
|
+
|
38
|
+
The V1 runtime is the simplest version, designed for basic HTML, CSS, and JavaScript projects. This runtime is automatically selected when your project contains only an `index.html` file.
|
39
|
+
|
40
|
+
### How It Works
|
41
|
+
|
42
|
+
In V1 runtime:
|
43
|
+
|
44
|
+
- Your `index.html` file is executed directly in the browser
|
45
|
+
- You can include inline JavaScript and CSS within your HTML file
|
46
|
+
- The runtime provides fetch request proxying to handle cross-origin requests
|
47
|
+
|
48
|
+
### Example Usage
|
49
|
+
|
50
|
+
As a VizHub user, you simply need to create an `index.html` file containing your entire project:
|
51
|
+
|
52
|
+
**index.html**
|
53
|
+
|
54
|
+
```html
|
55
|
+
<!DOCTYPE html>
|
56
|
+
<html>
|
57
|
+
<head>
|
58
|
+
<style>
|
59
|
+
body {
|
60
|
+
font-family: sans-serif;
|
61
|
+
}
|
62
|
+
</style>
|
63
|
+
</head>
|
64
|
+
<body>
|
65
|
+
<h1>Hello World</h1>
|
66
|
+
<script>
|
67
|
+
console.log("Hello from V1 runtime!");
|
68
|
+
</script>
|
69
|
+
</body>
|
70
|
+
</html>
|
71
|
+
```
|
72
|
+
|
73
|
+
V1 is ideal for simple demonstrations or when you want complete control over your HTML structure.
|
74
|
+
|
75
|
+
## V2 Runtime
|
76
|
+
|
77
|
+
The V2 runtime introduces JavaScript bundling with Rollup, JSX support, and CDN-based dependency resolution. This runtime is automatically selected when your project contains both an `index.html` and an `index.js` (or `index.jsx`) file.
|
78
|
+
|
79
|
+
### How It Works
|
80
|
+
|
81
|
+
In V2 runtime:
|
82
|
+
|
83
|
+
- Your JavaScript files are bundled together using Rollup
|
84
|
+
- Internally, a file named `bundle.js` is created
|
85
|
+
- The `index.html` file references this `bundle.js` file
|
86
|
+
- You can use ES6 modules to import/export code
|
87
|
+
- JSX syntax is supported for React development
|
88
|
+
- Dependencies listed in `package.json` are automatically resolved via CDNs (jsDelivr/unpkg)
|
89
|
+
- The bundled JavaScript is referenced in your HTML file
|
90
|
+
|
91
|
+
### Example Usage
|
92
|
+
|
93
|
+
As a VizHub user, you'll typically create:
|
94
|
+
|
95
|
+
1. An `index.html` file that references a `bundle.js` file
|
96
|
+
2. An `index.js` (or `index.jsx`) file as your entry point
|
97
|
+
3. Additional JavaScript modules as needed
|
98
|
+
4. A `package.json` file to list dependencies
|
99
|
+
|
100
|
+
**index.html**
|
101
|
+
|
102
|
+
```html
|
103
|
+
<!DOCTYPE html>
|
104
|
+
<html>
|
105
|
+
<body>
|
106
|
+
<div id="root"></div>
|
107
|
+
<script src="bundle.js"></script>
|
108
|
+
</body>
|
109
|
+
</html>
|
110
|
+
```
|
111
|
+
|
112
|
+
**index.js**
|
113
|
+
|
114
|
+
```javascript
|
115
|
+
import { render } from "./render";
|
116
|
+
render(document.getElementById("root"));
|
117
|
+
```
|
118
|
+
|
119
|
+
**render.js**
|
120
|
+
|
121
|
+
```javascript
|
122
|
+
export function render(element) {
|
123
|
+
element.innerHTML = "<h1>Hello from V2 runtime!</h1>";
|
124
|
+
}
|
125
|
+
```
|
126
|
+
|
127
|
+
**package.json**
|
128
|
+
|
129
|
+
```json
|
130
|
+
{
|
131
|
+
"dependencies": {
|
132
|
+
"d3": "7.8.5",
|
133
|
+
"react": "18.2.0",
|
134
|
+
"react-dom": "18.2.0"
|
135
|
+
}
|
136
|
+
}
|
137
|
+
```
|
138
|
+
|
139
|
+
V2 is ideal for more complex projects that require modular JavaScript and external dependencies that provide UMD builds. Note that the V2 runtime does not support ESM builds for external dependencies (see V4 if you need this).
|
140
|
+
|
141
|
+
## V3 Runtime
|
142
|
+
|
143
|
+
The V3 runtime provides advanced module bundling with Svelte support and cross-viz imports. This runtime is automatically selected when your project contains an `index.js` file but no `index.html` file.
|
144
|
+
|
145
|
+
### How It Works
|
146
|
+
|
147
|
+
In V3 runtime:
|
148
|
+
|
149
|
+
- Your JavaScript modules are bundled together using Rollup
|
150
|
+
- A default HTML structure is automatically generated
|
151
|
+
- Svelte components are supported
|
152
|
+
- Cross-viz imports allow you to import code from other viz instances
|
153
|
+
- The runtime provides a built-in state management system
|
154
|
+
|
155
|
+
### State Management in V3
|
156
|
+
|
157
|
+
V3 runtime includes a built-in state management system via the [unidirectional-data-flow](https://www.npmjs.com/package/unidirectional-data-flow) package ([GitHub](https://github.com/vizhub-core/unidirectional-data-flow)). This provides React-like state management capabilities with:
|
158
|
+
|
159
|
+
- A `main` entry point
|
160
|
+
- A minimal state management system based on `state` and `setState`
|
161
|
+
- Similar semantics to React's `useState` hook: `const [state, setState] = useState({})`
|
162
|
+
- Automatic re-rendering when state changes
|
163
|
+
|
164
|
+
### The Problem: Re-using D3 Rendering Logic Across Frameworks
|
165
|
+
|
166
|
+
While frameworks like React, Svelte, Vue, and Angular offer state management and DOM manipulation solutions, D3 excels in data transformation and visualization, particularly with axes, transitions, and behaviors (e.g. zoom, drag, and brush). These D3 features require direct access to the DOM, making it challenging to replicate them effectively within frameworks.
|
167
|
+
|
168
|
+
### The Solution: Unidirectional Data Flow
|
169
|
+
|
170
|
+
Unidirectional data flow is a pattern that can be cleanly invoked from multiple frameworks. In this paradigm, a single function is responsible for updating the DOM or rendering visuals based on a single, central state. As the state updates, the function re-renders the visualization in an idempotent manner, meaning it can run multiple times without causing side effects. Here's what the entry point function looks like for a D3-based visualization that uses unidirectional data flow:
|
171
|
+
|
172
|
+
**index.js**
|
173
|
+
|
174
|
+
```js
|
175
|
+
export const main = (container, { state, setState }) => {
|
176
|
+
// Your reusable D3-based rendering logic goes here
|
177
|
+
};
|
178
|
+
```
|
179
|
+
|
180
|
+
- **`container`**: A DOM element where the visualization will be rendered
|
181
|
+
- **`state`**: An object representing the current state of the application, initially empty
|
182
|
+
- **`setState`**: A function that updates the state using immutable update patterns
|
183
|
+
|
184
|
+
Whenever `setState` is invoked, `main` re-executes with the new state, ensuring that the rendering logic is both dynamic and responsive.
|
185
|
+
|
186
|
+
For cross-viz imports, you can reference other vizzes directly:
|
187
|
+
|
188
|
+
**example-with-import.js**
|
189
|
+
|
190
|
+
```javascript
|
191
|
+
// Import from another viz using @username/vizIdOrSlug syntax
|
192
|
+
import { someFunction } from "@username/my-other-viz";
|
193
|
+
```
|
194
|
+
|
195
|
+
V3 is ideal for modern JavaScript applications that benefit from automatic HTML structure generation and built-in state management. Additional features of V3 include:
|
196
|
+
|
197
|
+
- **Cross-Viz Imports**: Import code from other viz instances using `@username/vizIdOrSlug` syntax
|
198
|
+
- **Import from CSV**: Import CSV files directly into your viz
|
199
|
+
|
200
|
+
## V4 Runtime
|
201
|
+
|
202
|
+
The V4 runtime leverages modern ES Modules with import maps for direct browser execution. This runtime is automatically selected when your project's `index.html` contains ES module scripts with import maps.
|
203
|
+
|
204
|
+
### How It Works
|
205
|
+
|
206
|
+
In V4 runtime:
|
207
|
+
|
208
|
+
- Native browser ES modules are used without bundling
|
209
|
+
- Import maps allow you to specify module resolution directly in the browser
|
210
|
+
- Module paths can be aliased for cleaner imports
|
211
|
+
- External dependencies can be loaded directly from CDNs
|
212
|
+
|
213
|
+
### Example Usage
|
214
|
+
|
215
|
+
As a VizHub user, you'll create an `index.html` file with import maps and ES module scripts:
|
216
|
+
|
217
|
+
**index.html**
|
218
|
+
|
219
|
+
```html
|
220
|
+
<!DOCTYPE html>
|
221
|
+
<html>
|
222
|
+
<head>
|
223
|
+
<script type="importmap">
|
224
|
+
{
|
225
|
+
"imports": {
|
226
|
+
"utils": "./utils.js",
|
227
|
+
"d3": "https://cdn.jsdelivr.net/npm/d3@7.8.5/+esm"
|
228
|
+
}
|
229
|
+
}
|
230
|
+
</script>
|
231
|
+
</head>
|
232
|
+
<body>
|
233
|
+
<div id="app"></div>
|
234
|
+
<script type="module" src="index.js"></script>
|
235
|
+
</body>
|
236
|
+
</html>
|
237
|
+
```
|
238
|
+
|
239
|
+
**index.js**
|
240
|
+
|
241
|
+
```javascript
|
242
|
+
import { createApp } from "utils";
|
243
|
+
import * as d3 from "d3";
|
244
|
+
|
245
|
+
createApp(document.getElementById("app"));
|
246
|
+
```
|
247
|
+
|
248
|
+
**utils.js**
|
249
|
+
|
250
|
+
```javascript
|
251
|
+
export function createApp(element) {
|
252
|
+
element.innerHTML = "<h1>Hello from V4 runtime!</h1>";
|
253
|
+
}
|
254
|
+
```
|
255
|
+
|
256
|
+
V4 is ideal for modern browsers with native ES module support and when you want direct control over module resolution.
|
257
|
+
|
12
258
|
## Key Features
|
13
259
|
|
14
260
|
- **Multi-Version Runtime Support**
|
@@ -42,84 +288,87 @@ npm install @vizhub/runtime
|
|
42
288
|
### Basic Usage
|
43
289
|
|
44
290
|
```javascript
|
45
|
-
import { buildHTML } from
|
46
|
-
import { rollup } from
|
291
|
+
import { buildHTML } from "@vizhub/runtime";
|
292
|
+
import { rollup } from "rollup";
|
47
293
|
|
48
294
|
// Simple v1 runtime (HTML only)
|
49
295
|
const html = await buildHTML({
|
50
296
|
files: {
|
51
|
-
|
52
|
-
|
297
|
+
"index.html":
|
298
|
+
"<html><body><h1>Hello World</h1></body></html>",
|
299
|
+
},
|
53
300
|
});
|
54
301
|
|
55
302
|
// v2 runtime with bundling
|
56
303
|
const html = await buildHTML({
|
57
304
|
files: {
|
58
|
-
|
59
|
-
|
60
|
-
|
305
|
+
"index.html":
|
306
|
+
'<html><body><div id="root"></div><script src="bundle.js"></script></body></html>',
|
307
|
+
"index.js":
|
308
|
+
'import { message } from "./message"; console.log(message);',
|
309
|
+
"message.js":
|
310
|
+
'export const message = "Hello, bundled world!";',
|
61
311
|
},
|
62
|
-
rollup
|
312
|
+
rollup,
|
63
313
|
});
|
64
314
|
|
65
315
|
// Use the generated HTML in an iframe
|
66
|
-
const iframe = document.createElement(
|
316
|
+
const iframe = document.createElement("iframe");
|
67
317
|
iframe.srcdoc = html;
|
68
318
|
document.body.appendChild(iframe);
|
69
319
|
```
|
70
320
|
|
71
|
-
### Runtime Versions
|
72
|
-
|
73
|
-
The library automatically detects which runtime version to use based on the files provided:
|
74
|
-
|
75
|
-
- **v1**: When only `index.html` is present
|
76
|
-
- **v2**: When both `index.html` and `index.js` (or `index.jsx`) are present
|
77
|
-
- **v3**: When only `index.js` is present (no `index.html`)
|
78
|
-
- **v4**: When `index.html` contains ES module scripts with import maps
|
79
|
-
|
80
321
|
### Advanced Usage: v3 Runtime with Cross-Viz Imports
|
81
322
|
|
82
323
|
```javascript
|
83
|
-
import {
|
84
|
-
|
85
|
-
|
324
|
+
import {
|
325
|
+
buildHTML,
|
326
|
+
createVizCache,
|
327
|
+
createSlugCache,
|
328
|
+
} from "@vizhub/runtime";
|
329
|
+
import { rollup } from "rollup";
|
330
|
+
import { compile } from "svelte/compiler";
|
86
331
|
|
87
332
|
// Create caches for viz content and slug resolution
|
88
333
|
const vizCache = createVizCache({
|
89
334
|
initialContents: [
|
90
335
|
{
|
91
|
-
id:
|
336
|
+
id: "viz-123",
|
92
337
|
files: {
|
93
|
-
|
94
|
-
|
95
|
-
|
338
|
+
file1: {
|
339
|
+
name: "index.js",
|
340
|
+
text: "export const value = 42;",
|
341
|
+
},
|
342
|
+
},
|
343
|
+
},
|
96
344
|
],
|
97
345
|
handleCacheMiss: async (vizId) => {
|
98
346
|
// Fetch viz content from your backend
|
99
347
|
return await fetchVizContent(vizId);
|
100
|
-
}
|
348
|
+
},
|
101
349
|
});
|
102
350
|
|
103
351
|
const slugCache = createSlugCache({
|
104
352
|
initialMappings: {
|
105
|
-
|
353
|
+
"username/my-viz": "viz-123",
|
106
354
|
},
|
107
355
|
handleCacheMiss: async (slug) => {
|
108
356
|
// Resolve slug to vizId from your backend
|
109
357
|
return await resolveSlug(slug);
|
110
|
-
}
|
358
|
+
},
|
111
359
|
});
|
112
360
|
|
113
361
|
// Build HTML with cross-viz imports
|
114
362
|
const html = await buildHTML({
|
115
363
|
files: {
|
116
|
-
|
364
|
+
"index.js":
|
365
|
+
'import { value } from "@username/my-viz"; console.log(value);',
|
117
366
|
},
|
118
367
|
rollup,
|
119
368
|
vizCache,
|
120
|
-
vizId:
|
369
|
+
vizId: "current-viz-id",
|
121
370
|
slugCache,
|
122
|
-
getSvelteCompiler: async () => compile
|
371
|
+
getSvelteCompiler: async () => compile,
|
123
372
|
});
|
124
373
|
```
|
125
374
|
|
@@ -157,103 +406,6 @@ Creates a cache for slug resolution.
|
|
157
406
|
- **initialMappings**: `Record<string, string>` - Initial slug to vizId mappings
|
158
407
|
- **handleCacheMiss**: `(slug: string) => Promise<string>` - Function to handle cache misses
|
159
408
|
|
160
|
-
## Examples
|
161
|
-
|
162
|
-
### v1: Simple HTML
|
163
|
-
|
164
|
-
```javascript
|
165
|
-
const html = await buildHTML({
|
166
|
-
files: {
|
167
|
-
'index.html': `
|
168
|
-
<!DOCTYPE html>
|
169
|
-
<html>
|
170
|
-
<head>
|
171
|
-
<style>
|
172
|
-
body { font-family: sans-serif; }
|
173
|
-
</style>
|
174
|
-
</head>
|
175
|
-
<body>
|
176
|
-
<h1>Hello World</h1>
|
177
|
-
<script>
|
178
|
-
console.log('Hello from v1 runtime!');
|
179
|
-
</script>
|
180
|
-
</body>
|
181
|
-
</html>
|
182
|
-
`
|
183
|
-
}
|
184
|
-
});
|
185
|
-
```
|
186
|
-
|
187
|
-
### v2: Bundled JavaScript
|
188
|
-
|
189
|
-
```javascript
|
190
|
-
const html = await buildHTML({
|
191
|
-
files: {
|
192
|
-
'index.html': `
|
193
|
-
<!DOCTYPE html>
|
194
|
-
<html>
|
195
|
-
<body>
|
196
|
-
<div id="root"></div>
|
197
|
-
<script src="bundle.js"></script>
|
198
|
-
</body>
|
199
|
-
</html>
|
200
|
-
`,
|
201
|
-
'index.js': `
|
202
|
-
import { render } from './render';
|
203
|
-
render(document.getElementById('root'));
|
204
|
-
`,
|
205
|
-
'render.js': `
|
206
|
-
export function render(element) {
|
207
|
-
element.innerHTML = '<h1>Hello from v2 runtime!</h1>';
|
208
|
-
}
|
209
|
-
`,
|
210
|
-
'package.json': JSON.stringify({
|
211
|
-
dependencies: {
|
212
|
-
'd3': '7.8.5'
|
213
|
-
}
|
214
|
-
})
|
215
|
-
},
|
216
|
-
rollup
|
217
|
-
});
|
218
|
-
```
|
219
|
-
|
220
|
-
### v4: ES Modules with Import Maps
|
221
|
-
|
222
|
-
```javascript
|
223
|
-
const html = await buildHTML({
|
224
|
-
files: {
|
225
|
-
'index.html': `
|
226
|
-
<!DOCTYPE html>
|
227
|
-
<html>
|
228
|
-
<head>
|
229
|
-
<script type="importmap">
|
230
|
-
{
|
231
|
-
"imports": {
|
232
|
-
"utils": "./utils.js"
|
233
|
-
}
|
234
|
-
}
|
235
|
-
</script>
|
236
|
-
</head>
|
237
|
-
<body>
|
238
|
-
<div id="app"></div>
|
239
|
-
<script type="module" src="index.js"></script>
|
240
|
-
</body>
|
241
|
-
</html>
|
242
|
-
`,
|
243
|
-
'index.js': `
|
244
|
-
import { createApp } from 'utils';
|
245
|
-
createApp(document.getElementById('app'));
|
246
|
-
`,
|
247
|
-
'utils.js': `
|
248
|
-
export function createApp(element) {
|
249
|
-
element.innerHTML = '<h1>Hello from v4 runtime!</h1>';
|
250
|
-
}
|
251
|
-
`
|
252
|
-
},
|
253
|
-
rollup
|
254
|
-
});
|
255
|
-
```
|
256
|
-
|
257
409
|
## Development
|
258
410
|
|
259
411
|
### Setup
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import type { RollupBuild, RollupOptions } from "rollup";
|
2
|
+
import { VizCache } from "./v3/vizCache";
|
3
|
+
import { SlugCache } from "./v3/slugCache";
|
4
|
+
import { SvelteCompiler } from "./v3/transformSvelte";
|
5
|
+
import { FileCollection } from "@vizhub/viz-types";
|
6
|
+
export declare const buildHTML: ({ files, rollup, enableSourcemap, vizCache, vizId, slugCache, getSvelteCompiler, }: {
|
7
|
+
files?: FileCollection;
|
8
|
+
rollup?: (options: RollupOptions) => Promise<RollupBuild>;
|
9
|
+
enableSourcemap?: boolean;
|
10
|
+
vizCache?: VizCache;
|
11
|
+
vizId?: string;
|
12
|
+
slugCache?: SlugCache;
|
13
|
+
getSvelteCompiler?: () => Promise<SvelteCompiler>;
|
14
|
+
}) => Promise<string>;
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import { magicSandbox } from "magic-sandbox";
|
2
|
+
import { determineRuntimeVersion } from "./determineRuntimeVersion";
|
3
|
+
import { v2Build } from "./v2";
|
4
|
+
import { v3Build } from "./v3";
|
5
|
+
import { v4Build } from "./v4";
|
6
|
+
import { createVizCache } from "./v3/vizCache";
|
7
|
+
import { createVizContent } from "./v3/createVizContent";
|
8
|
+
import { vizContentToFileCollection } from "./utils/vizContentToFileCollection";
|
9
|
+
const DEBUG = false;
|
10
|
+
export const buildHTML = async ({ files, rollup, enableSourcemap = true, vizCache, vizId, slugCache, getSvelteCompiler, }) => {
|
11
|
+
if (!files && !vizCache) {
|
12
|
+
throw new Error("Either files or vizCache is required");
|
13
|
+
}
|
14
|
+
if (!files && vizCache && !vizId) {
|
15
|
+
throw new Error("vizId is required when using vizCache");
|
16
|
+
}
|
17
|
+
if (!files && vizCache && vizId) {
|
18
|
+
files = vizContentToFileCollection(await vizCache.get(vizId));
|
19
|
+
}
|
20
|
+
if (!files) {
|
21
|
+
throw new Error("Upable to extract viz files");
|
22
|
+
}
|
23
|
+
const version = determineRuntimeVersion(files);
|
24
|
+
DEBUG && console.log("[buildHTML] version:", version);
|
25
|
+
if (version === "v1") {
|
26
|
+
return magicSandbox(files);
|
27
|
+
}
|
28
|
+
if (version === "v2") {
|
29
|
+
if (!rollup) {
|
30
|
+
throw new Error("Rollup is required for v2 runtime");
|
31
|
+
}
|
32
|
+
return magicSandbox(await v2Build({ files, rollup, enableSourcemap }));
|
33
|
+
}
|
34
|
+
if (version === "v3") {
|
35
|
+
if (!rollup) {
|
36
|
+
throw new Error("Rollup is required for v3 runtime");
|
37
|
+
}
|
38
|
+
if (!vizCache && !vizId) {
|
39
|
+
const vizContent = createVizContent(files);
|
40
|
+
vizId = vizContent.id;
|
41
|
+
vizCache = createVizCache({
|
42
|
+
initialContents: [vizContent],
|
43
|
+
handleCacheMiss: async () => {
|
44
|
+
throw new Error("Cache miss handler not implemented");
|
45
|
+
},
|
46
|
+
});
|
47
|
+
}
|
48
|
+
if (!vizCache || !vizId) {
|
49
|
+
throw new Error("vizCache and vizId are required for v3 runtime");
|
50
|
+
}
|
51
|
+
return await v3Build({
|
52
|
+
files,
|
53
|
+
rollup,
|
54
|
+
vizCache,
|
55
|
+
vizId,
|
56
|
+
slugCache,
|
57
|
+
getSvelteCompiler,
|
58
|
+
});
|
59
|
+
}
|
60
|
+
if (version === "v4") {
|
61
|
+
if (!rollup) {
|
62
|
+
throw new Error("Rollup is required for v4 runtime");
|
63
|
+
}
|
64
|
+
return magicSandbox(await v4Build({ files, rollup, enableSourcemap }));
|
65
|
+
}
|
66
|
+
throw new Error(`Unsupported runtime version: ${version}`);
|
67
|
+
};
|
package/dist/index.d.ts
CHANGED
@@ -1,14 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
import { SlugCache } from "./v3/slugCache";
|
5
|
-
import { SvelteCompiler } from "./v3/transformSvelte";
|
6
|
-
export declare const buildHTML: ({ files, rollup, enableSourcemap, vizCache, vizId, slugCache, getSvelteCompiler, }: {
|
7
|
-
files?: FileCollection;
|
8
|
-
rollup?: (options: RollupOptions) => Promise<RollupBuild>;
|
9
|
-
enableSourcemap?: boolean;
|
10
|
-
vizCache?: VizCache;
|
11
|
-
vizId?: string;
|
12
|
-
slugCache?: SlugCache;
|
13
|
-
getSvelteCompiler?: () => Promise<SvelteCompiler>;
|
14
|
-
}) => Promise<string>;
|
1
|
+
export { buildHTML } from "./buildHTML";
|
2
|
+
export { createVizCache, VizCache } from "./v3/vizCache";
|
3
|
+
export { createSlugCache, SlugCache } from "./v3/slugCache";
|
package/dist/index.js
CHANGED
@@ -1,67 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
import { v3Build } from "./v3";
|
5
|
-
import { v4Build } from "./v4";
|
6
|
-
import { createVizCache } from "./v3/vizCache";
|
7
|
-
import { createVizContent } from "./v3/createVizContent";
|
8
|
-
import { vizContentToFileCollection } from "./utils/vizContentToFileCollection";
|
9
|
-
const DEBUG = false;
|
10
|
-
export const buildHTML = async ({ files, rollup, enableSourcemap = true, vizCache, vizId, slugCache, getSvelteCompiler, }) => {
|
11
|
-
if (!files && !vizCache) {
|
12
|
-
throw new Error("Either files or vizCache is required");
|
13
|
-
}
|
14
|
-
if (!files && vizCache && !vizId) {
|
15
|
-
throw new Error("vizId is required when using vizCache");
|
16
|
-
}
|
17
|
-
if (!files && vizCache && vizId) {
|
18
|
-
files = vizContentToFileCollection(await vizCache.get(vizId));
|
19
|
-
}
|
20
|
-
if (!files) {
|
21
|
-
throw new Error("Upable to extract viz files");
|
22
|
-
}
|
23
|
-
const version = determineRuntimeVersion(files);
|
24
|
-
DEBUG && console.log("[buildHTML] version:", version);
|
25
|
-
if (version === "v1") {
|
26
|
-
return magicSandbox(files);
|
27
|
-
}
|
28
|
-
if (version === "v2") {
|
29
|
-
if (!rollup) {
|
30
|
-
throw new Error("Rollup is required for v2 runtime");
|
31
|
-
}
|
32
|
-
return magicSandbox(await v2Build({ files, rollup, enableSourcemap }));
|
33
|
-
}
|
34
|
-
if (version === "v3") {
|
35
|
-
if (!rollup) {
|
36
|
-
throw new Error("Rollup is required for v3 runtime");
|
37
|
-
}
|
38
|
-
if (!vizCache && !vizId) {
|
39
|
-
const vizContent = createVizContent(files);
|
40
|
-
vizId = vizContent.id;
|
41
|
-
vizCache = createVizCache({
|
42
|
-
initialContents: [vizContent],
|
43
|
-
handleCacheMiss: async () => {
|
44
|
-
throw new Error("Cache miss handler not implemented");
|
45
|
-
},
|
46
|
-
});
|
47
|
-
}
|
48
|
-
if (!vizCache || !vizId) {
|
49
|
-
throw new Error("vizCache and vizId are required for v3 runtime");
|
50
|
-
}
|
51
|
-
return await v3Build({
|
52
|
-
files,
|
53
|
-
rollup,
|
54
|
-
vizCache,
|
55
|
-
vizId,
|
56
|
-
slugCache,
|
57
|
-
getSvelteCompiler,
|
58
|
-
});
|
59
|
-
}
|
60
|
-
if (version === "v4") {
|
61
|
-
if (!rollup) {
|
62
|
-
throw new Error("Rollup is required for v4 runtime");
|
63
|
-
}
|
64
|
-
return magicSandbox(await v4Build({ files, rollup, enableSourcemap }));
|
65
|
-
}
|
66
|
-
throw new Error(`Unsupported runtime version: ${version}`);
|
67
|
-
};
|
1
|
+
export { buildHTML } from "./buildHTML";
|
2
|
+
export { createVizCache } from "./v3/vizCache";
|
3
|
+
export { createSlugCache } from "./v3/slugCache";
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import { Browser, Page } from "puppeteer";
|
2
|
-
import { FileCollection } from "magic-sandbox";
|
3
2
|
import { VizCache } from "../v3/vizCache";
|
4
|
-
import { VizId } from "@vizhub/viz-types";
|
3
|
+
import { FileCollection, VizId } from "@vizhub/viz-types";
|
5
4
|
import { SlugCache } from "../v3/slugCache";
|
6
5
|
import { SvelteCompiler } from "../v3/transformSvelte";
|
7
6
|
export declare function testInBrowser({ browser, files, vizCache, slugCache, vizId, expectedLog, evaluateInBrowser, getSvelteCompiler, }: {
|
package/dist/types.d.ts
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
import type { VizContent } from "@vizhub/viz-types";
|
2
|
-
import type { FileCollection } from "magic-sandbox";
|
1
|
+
import type { FileCollection, VizContent } from "@vizhub/viz-types";
|
3
2
|
/**
|
4
3
|
* Converts VizContent format to FileCollection format
|
5
4
|
* VizContent has files as {id: {name, text}} structure
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { FileCollection } from "../types";
|
2
1
|
import type { RollupBuild, RollupOptions } from "rollup";
|
2
|
+
import { FileCollection } from "@vizhub/viz-types";
|
3
3
|
export declare const computeBundleJSV2: ({ files, rollup, enableSourcemap, }: {
|
4
4
|
files: FileCollection;
|
5
5
|
rollup: (options: RollupOptions) => Promise<RollupBuild>;
|
@@ -1,4 +1,4 @@
|
|
1
|
+
import { FileCollection } from "@vizhub/viz-types";
|
1
2
|
import type { JSDOM } from "jsdom";
|
2
|
-
import { FileCollection } from "../types";
|
3
3
|
export declare const setJSDOM: (JSDOMInstance: typeof JSDOM) => void;
|
4
4
|
export declare const getComputedIndexHtml: (files: FileCollection) => string;
|
package/dist/v2/v2Build.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { FileCollection } from "magic-sandbox";
|
2
1
|
import type { RollupBuild, RollupOptions } from "rollup";
|
2
|
+
import { FileCollection } from "@vizhub/viz-types";
|
3
3
|
export declare const v2Build: ({ files, rollup, enableSourcemap, }: {
|
4
4
|
files: FileCollection;
|
5
5
|
rollup: (options: RollupOptions) => Promise<RollupBuild>;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
// We want to remove the vizId from the error message
|
2
|
+
// to make it more user-friendly.
|
3
|
+
// Example error message before and after:
|
4
|
+
// Before: "7f0b69fcb754479699172d1887817027/index.js (14:8): Expected ';', '}' or <eof>"
|
5
|
+
// After: "./index.js (14:8): Expected ';', '}' or <eof>"
|
6
|
+
export const cleanRollupErrorMessage = ({ rawMessage, vizId, }) => {
|
7
|
+
const regex = new RegExp(vizId, "g");
|
8
|
+
return rawMessage?.replace(regex, ".");
|
9
|
+
};
|
@@ -1,7 +1,6 @@
|
|
1
|
-
import { FileCollection } from "../types";
|
2
1
|
import type { RollupBuild, RollupOptions } from "rollup";
|
3
2
|
import { VizCache } from "./vizCache";
|
4
|
-
import { VizId } from "@vizhub/viz-types";
|
3
|
+
import { FileCollection, VizId } from "@vizhub/viz-types";
|
5
4
|
import { SlugCache } from "./slugCache";
|
6
5
|
import { SvelteCompiler } from "./transformSvelte";
|
7
6
|
export declare const computeBundleJSV3: ({ files, rollup, enableSourcemap, vizCache, vizId, slugCache, getSvelteCompiler, }: {
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import { VizContent } from "@vizhub/viz-types";
|
2
|
-
import { FileCollection } from "magic-sandbox";
|
1
|
+
import { FileCollection, VizContent } from "@vizhub/viz-types";
|
3
2
|
/**
|
4
3
|
* Creates a VizContent object with the given files
|
5
4
|
* @param files An object with file names as keys and file content as values
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { VizContent, VizId } from "@vizhub/viz-types";
|
2
|
+
export type V3Runtime = {
|
3
|
+
handleCodeChange: (content: VizContent) => void;
|
4
|
+
invalidateVizCache: (changedVizIds: Array<VizId>) => void;
|
5
|
+
resetSrcdoc: (changedVizIds: Array<VizId>) => void;
|
6
|
+
};
|
7
|
+
export declare const setupV3Runtime: ({ vizId, iframe, setSrcdocErrorMessage, getLatestContent, resolveSlugKey, writeFile, worker, }: {
|
8
|
+
vizId: VizId;
|
9
|
+
iframe: HTMLIFrameElement;
|
10
|
+
setSrcdocErrorMessage: (error: string | null) => void;
|
11
|
+
getLatestContent: (vizId: VizId) => Promise<VizContent>;
|
12
|
+
resolveSlugKey: (slugKey: string) => Promise<VizId>;
|
13
|
+
writeFile: (fileName: string, content: string) => void;
|
14
|
+
worker: Worker;
|
15
|
+
}) => V3Runtime;
|
@@ -0,0 +1,341 @@
|
|
1
|
+
import { parseId } from "./parseId";
|
2
|
+
import { cleanRollupErrorMessage } from "./cleanRollupErrorMessage";
|
3
|
+
import { getFileText } from "@vizhub/viz-utils";
|
4
|
+
// Flag for debugging.
|
5
|
+
const debug = false;
|
6
|
+
// Nothing happening.
|
7
|
+
const IDLE = "IDLE";
|
8
|
+
// An update has been enqueued
|
9
|
+
// via requestAnimationFrame.
|
10
|
+
const ENQUEUED = "ENQUEUED";
|
11
|
+
// An update (build and run) is pending,
|
12
|
+
// and the files have not changed.
|
13
|
+
const PENDING_CLEAN = "PENDING_CLEAN";
|
14
|
+
// An update (build and run) is pending,
|
15
|
+
// and the files have changed
|
16
|
+
// while this run is taking place.
|
17
|
+
const PENDING_DIRTY = "PENDING_DIRTY";
|
18
|
+
export const setupV3Runtime = ({ vizId, iframe, setSrcdocErrorMessage, getLatestContent, resolveSlugKey, writeFile, worker, }) => {
|
19
|
+
// Valid State Transitions:
|
20
|
+
//
|
21
|
+
// * IDLE --> ENQUEUED
|
22
|
+
// When the system is idle and files are changed.
|
23
|
+
//
|
24
|
+
// * ENQUEUED --> PENDING_CLEAN
|
25
|
+
// When the pending changes run.
|
26
|
+
//
|
27
|
+
// * PENDING_CLEAN --> IDLE
|
28
|
+
// When the pending update finishes running
|
29
|
+
// and files were not changed in the mean time.
|
30
|
+
//
|
31
|
+
// * PENDING_CLEAN --> PENDING_DIRTY
|
32
|
+
// When files are changed while an update is pending.
|
33
|
+
//
|
34
|
+
// * PENDING_DIRTY --> ENQUEUED
|
35
|
+
// When the pending update finishes running
|
36
|
+
// and files were changed in the mean time.
|
37
|
+
//
|
38
|
+
// When a build error happens, the state is set to IDLE.
|
39
|
+
// This is to prevent a build error from causing
|
40
|
+
// the whole system to stop working.
|
41
|
+
//
|
42
|
+
// Valid State Transitions (with build errors):
|
43
|
+
// TODO complete this section
|
44
|
+
let state = IDLE;
|
45
|
+
if (debug) {
|
46
|
+
setInterval(() => {
|
47
|
+
console.log("state", state);
|
48
|
+
}, 1000);
|
49
|
+
}
|
50
|
+
// Pending promise resolvers.
|
51
|
+
let pendingBuildPromise = null;
|
52
|
+
let pendingRunPromise = null;
|
53
|
+
// Logic around profiling build times.
|
54
|
+
const profileBuildTimes = debug;
|
55
|
+
let buildTimes = [];
|
56
|
+
const avg = (arr) => arr.reduce((a, b) => a + b, 0) / arr.length;
|
57
|
+
const n = 100;
|
58
|
+
// This runs when the build worker sends a message.
|
59
|
+
worker.addEventListener("message", async ({ data }) => {
|
60
|
+
const message = data;
|
61
|
+
// Handle 'buildResponse' messages.
|
62
|
+
// These are sent by the build worker in response
|
63
|
+
// to a 'buildRequest' message.
|
64
|
+
if (message.type === "buildResponse") {
|
65
|
+
const buildResult = message.buildResult;
|
66
|
+
const error = message.error;
|
67
|
+
if (profileBuildTimes && buildResult) {
|
68
|
+
buildTimes.push(buildResult.time);
|
69
|
+
// Every n times, log the rolling average.
|
70
|
+
if (buildTimes.length % n === 0) {
|
71
|
+
console.log("Average build time: " +
|
72
|
+
avg(buildTimes) +
|
73
|
+
" ms");
|
74
|
+
buildTimes = [];
|
75
|
+
}
|
76
|
+
}
|
77
|
+
// Regardless of whether the build succeeded or failed,
|
78
|
+
// resolve the pending build promise,
|
79
|
+
// so that the system remains responsive.
|
80
|
+
if (pendingBuildPromise) {
|
81
|
+
pendingBuildPromise(buildResult);
|
82
|
+
pendingBuildPromise = null;
|
83
|
+
}
|
84
|
+
if (error) {
|
85
|
+
setSrcdocErrorMessage(cleanRollupErrorMessage({
|
86
|
+
rawMessage: error.message,
|
87
|
+
vizId,
|
88
|
+
}));
|
89
|
+
}
|
90
|
+
}
|
91
|
+
// Handle 'contentRequest' messages.
|
92
|
+
// These are sent by the worker when it needs
|
93
|
+
// to get the content of a file, in order to
|
94
|
+
// populate its VizCache.
|
95
|
+
if (message.type === "contentRequest") {
|
96
|
+
const { vizId } = message;
|
97
|
+
const content = await getLatestContent(vizId);
|
98
|
+
const contentResponseMessage = {
|
99
|
+
type: "contentResponse",
|
100
|
+
vizId: message.vizId,
|
101
|
+
content,
|
102
|
+
};
|
103
|
+
if (debug) {
|
104
|
+
console.log("[v3 runtime] received contentRequest, sending contentResponse", contentResponseMessage);
|
105
|
+
}
|
106
|
+
// Send the content back to the worker.
|
107
|
+
worker.postMessage(contentResponseMessage);
|
108
|
+
}
|
109
|
+
// Handle 'resolveSlugRequest' messages.
|
110
|
+
// These are sent by the worker when it needs
|
111
|
+
// to resolve a slug import to a viz ID.
|
112
|
+
if (message.type === "resolveSlugRequest") {
|
113
|
+
const { slugKey } = message;
|
114
|
+
const resolveSlugResponseMessage = {
|
115
|
+
type: "resolveSlugResponse",
|
116
|
+
slugKey,
|
117
|
+
requestId: message.requestId,
|
118
|
+
vizId: await resolveSlugKey(slugKey),
|
119
|
+
};
|
120
|
+
if (debug) {
|
121
|
+
console.log("[v3 runtime] received resolveSlugRequest, sending resolveSlugResponse", resolveSlugResponseMessage);
|
122
|
+
}
|
123
|
+
// Send the viz ID back to the worker.
|
124
|
+
worker.postMessage(resolveSlugResponseMessage);
|
125
|
+
}
|
126
|
+
// Handle 'invalidateVizCacheResponse' messages.
|
127
|
+
// These are sent by the worker in response to
|
128
|
+
// an 'invalidateVizCacheRequest' message.
|
129
|
+
if (message.type === "invalidateVizCacheResponse") {
|
130
|
+
if (debug) {
|
131
|
+
console.log("[v3 runtime] received invalidateVizCacheResponse", message);
|
132
|
+
}
|
133
|
+
// Leverage existing infra for executing the hot reloading.
|
134
|
+
handleCodeChange();
|
135
|
+
}
|
136
|
+
if (message.type === "resetSrcdocResponse") {
|
137
|
+
const srcdoc = message.srcdoc;
|
138
|
+
const error = message.error;
|
139
|
+
if (error) {
|
140
|
+
setSrcdocErrorMessage(cleanRollupErrorMessage({
|
141
|
+
rawMessage: error.message,
|
142
|
+
vizId,
|
143
|
+
}));
|
144
|
+
}
|
145
|
+
else {
|
146
|
+
setSrcdocErrorMessage(null);
|
147
|
+
// Really reset the srcdoc!
|
148
|
+
// console.log('Really reset the srcdoc!');
|
149
|
+
if (srcdoc) {
|
150
|
+
iframe.srcdoc = srcdoc;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
});
|
155
|
+
// This runs when the IFrame sends a message.
|
156
|
+
window.addEventListener("message", ({ data }) => {
|
157
|
+
// Handle 'runDone' and 'runError' messages.
|
158
|
+
// These happen in response to sending a 'runJS' message.
|
159
|
+
if (data.type === "runDone" ||
|
160
|
+
data.type === "runError") {
|
161
|
+
// console.log('got ' + data.type);
|
162
|
+
if (pendingRunPromise) {
|
163
|
+
// TODO pass errors out for display
|
164
|
+
// pendingRunPromise(data as V3WindowMessage);
|
165
|
+
pendingRunPromise();
|
166
|
+
pendingRunPromise = null;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
if (data.type === "runError") {
|
170
|
+
setSrcdocErrorMessage(data.error.message);
|
171
|
+
}
|
172
|
+
if (data.type === "writeFile") {
|
173
|
+
if (data.fileName && data.content) {
|
174
|
+
writeFile(data.fileName, data.content);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
});
|
178
|
+
const handleCodeChange = () => {
|
179
|
+
if (state === IDLE) {
|
180
|
+
state = ENQUEUED;
|
181
|
+
update();
|
182
|
+
}
|
183
|
+
else if (state === PENDING_CLEAN) {
|
184
|
+
state = PENDING_DIRTY;
|
185
|
+
}
|
186
|
+
};
|
187
|
+
// This runs when one or more imported vizzes are changed.
|
188
|
+
const invalidateVizCache = (changedVizIds) => {
|
189
|
+
// Send a message to the worker to invalidate the cache.
|
190
|
+
const message = {
|
191
|
+
type: "invalidateVizCacheRequest",
|
192
|
+
changedVizIds,
|
193
|
+
};
|
194
|
+
worker.postMessage(message);
|
195
|
+
};
|
196
|
+
const profileHotReloadFPS = true;
|
197
|
+
let updateCount = 0;
|
198
|
+
if (profileHotReloadFPS) {
|
199
|
+
setInterval(() => {
|
200
|
+
if (debug && updateCount > 0) {
|
201
|
+
console.log(updateCount +
|
202
|
+
" hot reload" +
|
203
|
+
(updateCount !== 1 ? "s" : "") +
|
204
|
+
" in the last second");
|
205
|
+
}
|
206
|
+
updateCount = 0;
|
207
|
+
}, 1000);
|
208
|
+
}
|
209
|
+
const build = () => {
|
210
|
+
return new Promise((resolve) => {
|
211
|
+
pendingBuildPromise = resolve;
|
212
|
+
const message = {
|
213
|
+
type: "buildRequest",
|
214
|
+
vizId,
|
215
|
+
enableSourcemap: true,
|
216
|
+
};
|
217
|
+
worker.postMessage(message);
|
218
|
+
});
|
219
|
+
};
|
220
|
+
// Builds and runs the latest files.
|
221
|
+
const update = async () => {
|
222
|
+
state = PENDING_CLEAN;
|
223
|
+
if (debug) {
|
224
|
+
console.log("update: before run");
|
225
|
+
}
|
226
|
+
// Build the code. This may fail and return `undefined`.
|
227
|
+
const buildResult = await build();
|
228
|
+
// If the build was successful, run the code.
|
229
|
+
if (buildResult !== undefined) {
|
230
|
+
await run(buildResult);
|
231
|
+
}
|
232
|
+
if (debug) {
|
233
|
+
console.log("update: after run");
|
234
|
+
}
|
235
|
+
updateCount++;
|
236
|
+
// TypeScript can't comprehend that `state`
|
237
|
+
// may change during the await calls above.
|
238
|
+
// @ts-ignore
|
239
|
+
if (state === PENDING_DIRTY) {
|
240
|
+
requestAnimationFrame(update);
|
241
|
+
state = ENQUEUED;
|
242
|
+
}
|
243
|
+
else {
|
244
|
+
state = IDLE;
|
245
|
+
}
|
246
|
+
};
|
247
|
+
let previousCSSFiles = [];
|
248
|
+
const run = (buildResult) => {
|
249
|
+
return new Promise((resolve) => {
|
250
|
+
const { src, warnings, cssFiles } = buildResult;
|
251
|
+
// Sanity check.
|
252
|
+
// At this point, since there were no errors,
|
253
|
+
// we expect there to be a `src` property.
|
254
|
+
// This should never happen, but log & error just in case!
|
255
|
+
if (src === undefined) {
|
256
|
+
if (debug) {
|
257
|
+
console.log("[v3 runtime] src is undefined, but no errors!");
|
258
|
+
}
|
259
|
+
throw new Error("[v3 runtime] src is undefined, but no errors!");
|
260
|
+
}
|
261
|
+
// Set pendingRunPromise because at this point,
|
262
|
+
// we expect an asynchronous response when the run is done.
|
263
|
+
// The iframe should send either a `runDone` or `runError` message.
|
264
|
+
pendingRunPromise = resolve;
|
265
|
+
// Handle build warnings
|
266
|
+
if (warnings.length > 0) {
|
267
|
+
// TODO: Distinguish between warnings and errors in UI
|
268
|
+
setSrcdocErrorMessage(warnings
|
269
|
+
.map((warning) => warning.message)
|
270
|
+
.join("\n\n"));
|
271
|
+
}
|
272
|
+
else {
|
273
|
+
setSrcdocErrorMessage(null); // Clear error message if no warnings
|
274
|
+
}
|
275
|
+
if (iframe.contentWindow) {
|
276
|
+
// For each cssFiles
|
277
|
+
for (const cssFile of cssFiles) {
|
278
|
+
const { vizId, fileName } = parseId(cssFile);
|
279
|
+
getLatestContent(vizId).then((content) => {
|
280
|
+
const src = getFileText(content, fileName);
|
281
|
+
if (src === null) {
|
282
|
+
// The file doesn't exist.
|
283
|
+
// TODO surface this error to the user
|
284
|
+
// in a nicer way than this.
|
285
|
+
console.warn(`Imported CSS file ${fileName} doesn't exist.`);
|
286
|
+
return;
|
287
|
+
}
|
288
|
+
// TODO only inject CSS if it has changed.
|
289
|
+
const runCSSMessage = {
|
290
|
+
type: "runCSS",
|
291
|
+
id: cssFile,
|
292
|
+
src,
|
293
|
+
};
|
294
|
+
if (debug) {
|
295
|
+
console.log("runCSSMessage", runCSSMessage);
|
296
|
+
}
|
297
|
+
iframe.contentWindow?.postMessage(runCSSMessage, window.location.origin);
|
298
|
+
});
|
299
|
+
}
|
300
|
+
// Detect which CSS files have been removed
|
301
|
+
// and remove them from the iframe.
|
302
|
+
const removedCSSFiles = previousCSSFiles.filter((id) => !cssFiles.includes(id));
|
303
|
+
previousCSSFiles = cssFiles;
|
304
|
+
if (debug) {
|
305
|
+
console.log("removedCSSFiles", removedCSSFiles);
|
306
|
+
}
|
307
|
+
for (const id of removedCSSFiles) {
|
308
|
+
const removeCSSMessage = {
|
309
|
+
type: "runCSS",
|
310
|
+
id,
|
311
|
+
src: "",
|
312
|
+
};
|
313
|
+
iframe.contentWindow?.postMessage(removeCSSMessage, window.location.origin);
|
314
|
+
}
|
315
|
+
// Clear the console before each run.
|
316
|
+
console.clear();
|
317
|
+
const runJSMessage = {
|
318
|
+
type: "runJS",
|
319
|
+
src,
|
320
|
+
};
|
321
|
+
iframe.contentWindow.postMessage(runJSMessage, window.location.origin);
|
322
|
+
}
|
323
|
+
});
|
324
|
+
};
|
325
|
+
const resetSrcdoc = (changedVizIds) => {
|
326
|
+
state = IDLE;
|
327
|
+
pendingBuildPromise = null;
|
328
|
+
pendingRunPromise = null;
|
329
|
+
const message = {
|
330
|
+
type: "resetSrcdocRequest",
|
331
|
+
vizId,
|
332
|
+
changedVizIds,
|
333
|
+
};
|
334
|
+
worker.postMessage(message);
|
335
|
+
};
|
336
|
+
return {
|
337
|
+
handleCodeChange,
|
338
|
+
invalidateVizCache,
|
339
|
+
resetSrcdoc,
|
340
|
+
};
|
341
|
+
};
|
package/dist/v3/v3Build.d.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import { RollupBuild, RollupOptions } from "rollup";
|
2
|
-
import { FileCollection } from "../types";
|
3
2
|
import { VizCache } from "./vizCache";
|
4
|
-
import { VizId } from "@vizhub/viz-types";
|
3
|
+
import { FileCollection, VizId } from "@vizhub/viz-types";
|
5
4
|
import { SlugCache } from "./slugCache";
|
6
5
|
import { SvelteCompiler } from "./transformSvelte";
|
7
6
|
export declare const v3Build: ({ files, rollup, enableSourcemap, vizCache, vizId, slugCache, getSvelteCompiler, }: {
|
package/dist/v3/v3Build.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { computeBundleJSV3 } from "./computeBundleJSV3";
|
2
2
|
import { htmlTemplate } from "./htmlTemplate";
|
3
3
|
import { parseId } from "./parseId";
|
4
|
-
import { getFileText } from "
|
4
|
+
import { getFileText } from "@vizhub/viz-utils";
|
5
5
|
export const v3Build = async ({ files, rollup, enableSourcemap = true, vizCache, vizId, slugCache, getSvelteCompiler, }) => {
|
6
6
|
const { src, cssFiles } = await computeBundleJSV3({
|
7
7
|
files,
|
package/dist/v3/vizLoad.js
CHANGED
package/dist/v4/index.d.ts
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vizhub/runtime",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.0",
|
4
4
|
"description": "Flexible runtime environment for data visualization sandboxes",
|
5
5
|
"type": "module",
|
6
6
|
"main": "./dist/index.js",
|
@@ -20,28 +20,30 @@
|
|
20
20
|
"test": "vitest run",
|
21
21
|
"test:watch": "vitest",
|
22
22
|
"typecheck": "tsc --noEmit",
|
23
|
-
"prettier": "prettier {*.*,**/*.*} --write"
|
23
|
+
"prettier": "prettier {*.*,**/*.*} --write",
|
24
|
+
"upgrade": "ncu -x svelte -u"
|
24
25
|
},
|
25
26
|
"keywords": [],
|
26
27
|
"author": "",
|
27
28
|
"license": "MIT",
|
28
29
|
"devDependencies": {
|
29
30
|
"@types/jsdom": "^21.1.7",
|
30
|
-
"@types/node": "^22.
|
31
|
+
"@types/node": "^22.14.0",
|
31
32
|
"@types/uuid": "^10.0.0",
|
32
33
|
"jsdom": "^26.0.0",
|
34
|
+
"npm-check-updates": "^17.1.16",
|
33
35
|
"prettier": "^3.5.3",
|
34
|
-
"puppeteer": "^24.
|
35
|
-
"rollup": "^4.
|
36
|
+
"puppeteer": "^24.6.0",
|
37
|
+
"rollup": "^4.39.0",
|
36
38
|
"sucrase": "^3.35.0",
|
37
39
|
"svelte": "4.2.9",
|
38
|
-
"typescript": "^5.8.
|
39
|
-
"vitest": "^3.
|
40
|
+
"typescript": "^5.8.3",
|
41
|
+
"vitest": "^3.1.1"
|
40
42
|
},
|
41
43
|
"dependencies": {
|
42
|
-
"@vizhub/viz-types": "^0.0
|
43
|
-
"@vizhub/viz-utils": "^0.0
|
44
|
-
"magic-sandbox": "^2.
|
44
|
+
"@vizhub/viz-types": "^0.1.0",
|
45
|
+
"@vizhub/viz-utils": "^0.1.0",
|
46
|
+
"magic-sandbox": "^2.2.0",
|
45
47
|
"uuid": "^11.1.0"
|
46
48
|
}
|
47
49
|
}
|