solid-tag-runtime 0.0.12 → 0.0.14
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/ARCHITECTURE.md +243 -39
- package/README.md +122 -885
- package/docs/README.md +26 -0
- package/docs/api/compile-cache.md +103 -0
- package/docs/api/html.md +89 -0
- package/docs/api/runtime.md +87 -0
- package/docs/api/solid.md +71 -0
- package/docs/compile-cache.md +271 -0
- package/docs/getting-started.md +112 -0
- package/docs/html-runtime.md +120 -0
- package/docs/lifecycle-events.md +77 -0
- package/docs/modules.md +93 -0
- package/docs/rendering.md +71 -0
- package/docs/solid-render.md +111 -0
- package/docs/solid-runtime-setup.md +181 -0
- package/docs/wrapperless-delegation.md +81 -0
- package/examples/basic.js +41 -0
- package/examples/compile-cache.js +35 -0
- package/examples/main.tsx +318 -0
- package/examples/render.html +79 -0
- package/examples/solid-runtime.js +24 -0
- package/index.d.ts +183 -27
- package/package.json +11 -3
- package/solid.d.ts +77 -0
- package/src/compile-cache.js +340 -0
- package/src/compiler.js +86 -38
- package/src/html/delegated-events.js +69 -0
- package/src/html/delegation-host.js +29 -0
- package/src/index.js +6 -0
- package/src/render.js +4 -0
- package/src/runtime.js +656 -74
- package/src/solid/import-map.js +29 -0
- package/src/solid/index.js +67 -0
- package/src/solid/integration.js +30 -0
- package/src/solid/packages.js +24 -0
- package/src/solid/providers/esm-sh.js +81 -0
- package/src/solid/providers/index.js +22 -0
- package/src/solid/providers/jsdelivr.js +45 -0
- package/src/solid/resolve.js +287 -0
package/README.md
CHANGED
|
@@ -1,45 +1,63 @@
|
|
|
1
1
|
# solid-tag-runtime
|
|
2
2
|
|
|
3
|
-
`solid-tag-runtime` is a
|
|
4
|
-
|
|
5
|
-
It builds on [`solid-tag`](https://www.npmjs.com/package/solid-tag):
|
|
3
|
+
`solid-tag-runtime` is a runtime module system for dynamically defined Solid JSX and JavaScript modules.
|
|
6
4
|
|
|
7
5
|
```text
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
solid-tag
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
solid-tag-runtime
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
source module
|
|
7
|
+
↓
|
|
8
|
+
solid-tag compiler
|
|
9
|
+
↓
|
|
10
|
+
pre-link compiled artifact
|
|
11
|
+
↓
|
|
12
|
+
solid-tag-runtime linker
|
|
13
|
+
↓
|
|
14
|
+
host Solid runtime
|
|
17
15
|
```
|
|
18
16
|
|
|
19
|
-
The package is
|
|
17
|
+
The core package is module-first and DOM-independent. Browser discovery, ownership, declarative rendering, and `<solid-render>` live in `solid-tag-runtime/html`. Optional Solid setup/provider helpers live in `solid-tag-runtime/solid`.
|
|
20
18
|
|
|
21
|
-
##
|
|
19
|
+
## Install
|
|
22
20
|
|
|
23
21
|
```bash
|
|
24
22
|
npm install solid-tag-runtime solid-tag
|
|
25
23
|
```
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
## Easiest Solid setup
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
`0.0.14` adds an opt-in high-level Solid integration:
|
|
30
28
|
|
|
31
29
|
```ts
|
|
32
|
-
import {
|
|
30
|
+
import { createSolidRuntime } from "solid-tag-runtime/solid";
|
|
33
31
|
|
|
34
|
-
const runtime =
|
|
32
|
+
const runtime = await createSolidRuntime({
|
|
33
|
+
modules: {
|
|
34
|
+
"@app/state": state,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
35
37
|
```
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
The helper resolves the standard family:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
solid-js
|
|
43
|
+
@solidjs/web
|
|
44
|
+
@solidjs/html
|
|
45
|
+
@solidjs/signals
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Existing browser/bundler resolution and import-map mappings are preferred. Missing packages may be filled through an inferred/configured provider. Existing mappings are never rewritten, and user-provided modules override automatic defaults.
|
|
49
|
+
|
|
50
|
+
`createSolidRuntime()` also installs a **lazy** Solid DOM integration used by wrapperless `<script render>` mounts. It does not establish a document render/delegation host until a wrapperless render actually needs one.
|
|
51
|
+
|
|
52
|
+
See [Solid runtime setup](./docs/solid-runtime-setup.md).
|
|
53
|
+
|
|
54
|
+
## Low-level setup remains unchanged
|
|
38
55
|
|
|
39
56
|
```ts
|
|
40
57
|
import * as Solid from "solid-js";
|
|
41
58
|
import * as SolidWeb from "@solidjs/web";
|
|
42
59
|
import html from "@solidjs/html";
|
|
60
|
+
import { createRuntime } from "solid-tag-runtime";
|
|
43
61
|
|
|
44
62
|
const runtime = createRuntime({
|
|
45
63
|
modules: {
|
|
@@ -50,119 +68,58 @@ const runtime = createRuntime({
|
|
|
50
68
|
});
|
|
51
69
|
```
|
|
52
70
|
|
|
53
|
-
|
|
71
|
+
`createRuntime()` remains synchronous and does not know about CDNs, browser import maps, Solid version inference, or document event delegation.
|
|
54
72
|
|
|
55
|
-
##
|
|
73
|
+
## Composable Solid module loader
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
runtime.define(
|
|
59
|
-
"/ui/Button.jsx",
|
|
60
|
-
`
|
|
61
|
-
export function Button(props) {
|
|
62
|
-
return (
|
|
63
|
-
<button onClick={props.onClick}>
|
|
64
|
-
{props.children}
|
|
65
|
-
</button>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
`,
|
|
69
|
-
);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
A second module can import the first one normally:
|
|
75
|
+
If you want provider-aware setup but still want to call `createRuntime()` yourself:
|
|
73
76
|
|
|
74
77
|
```ts
|
|
75
|
-
runtime
|
|
76
|
-
|
|
77
|
-
`
|
|
78
|
-
import { createSignal } from "solid-js";
|
|
79
|
-
import { Button } from "../ui/Button.jsx";
|
|
80
|
-
|
|
81
|
-
export function Counter() {
|
|
82
|
-
const [count, setCount] = createSignal(0);
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<Button onClick={() => setCount(value => value + 1)}>
|
|
86
|
-
Count: {count()}
|
|
87
|
-
</Button>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
`,
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const counterModule = await runtime.import("/features/Counter.jsx");
|
|
94
|
-
|
|
95
|
-
counterModule.Counter;
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
The runtime compiles and links the dependency graph automatically.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## Declarative HTML modules
|
|
102
|
-
|
|
103
|
-
`solid-tag-runtime/html` is the browser/DOM adapter. The core runtime remains DOM-independent.
|
|
78
|
+
import { loadSolidModules } from "solid-tag-runtime/solid";
|
|
79
|
+
import { createRuntime } from "solid-tag-runtime";
|
|
104
80
|
|
|
105
|
-
|
|
81
|
+
const solidModules = await loadSolidModules({
|
|
82
|
+
source: "auto",
|
|
83
|
+
fallbackProvider: "esm.sh",
|
|
84
|
+
});
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
86
|
+
const runtime = createRuntime({
|
|
87
|
+
modules: {
|
|
88
|
+
...solidModules,
|
|
89
|
+
"@app/state": state,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
114
92
|
```
|
|
115
93
|
|
|
116
|
-
|
|
94
|
+
Provider logic is isolated from the core runtime. `loadSolidModules()` reads existing mappings/resolution but never mutates import maps. If a host resolves only part of the Solid family but its identity is opaque, automatic CDN mixing is rejected rather than risking a second Solid runtime.
|
|
95
|
+
|
|
96
|
+
## Define runtime modules
|
|
117
97
|
|
|
118
98
|
```ts
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
99
|
+
runtime.define("/Greeting.jsx", `
|
|
100
|
+
export default function Greeting() {
|
|
101
|
+
return <p>Hello from runtime JSX</p>;
|
|
102
|
+
}
|
|
103
|
+
`);
|
|
123
104
|
|
|
124
|
-
await
|
|
105
|
+
const module = await runtime.import("/Greeting.jsx");
|
|
106
|
+
module.default;
|
|
125
107
|
```
|
|
126
108
|
|
|
127
|
-
|
|
109
|
+
## Declarative browser modules
|
|
128
110
|
|
|
129
111
|
```ts
|
|
130
112
|
import { createHTMLRuntime } from "solid-tag-runtime/html";
|
|
131
113
|
|
|
132
|
-
const
|
|
114
|
+
const htmlRuntime = createHTMLRuntime(runtime, {
|
|
133
115
|
scope: "main",
|
|
134
116
|
root: document,
|
|
135
117
|
});
|
|
136
118
|
|
|
137
|
-
await
|
|
138
|
-
await html.observe({ registerExisting: false });
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
## Render runtime UI
|
|
143
|
-
|
|
144
|
-
`solid-tag-runtime/html` can now mount component exports as an HTML-adapter concern while the core runtime remains DOM-independent.
|
|
145
|
-
|
|
146
|
-
There are two complementary forms:
|
|
147
|
-
|
|
148
|
-
```text
|
|
149
|
-
<script module>
|
|
150
|
-
define a runtime module
|
|
151
|
-
|
|
152
|
-
<script module render>
|
|
153
|
-
define a module + mount one instance
|
|
154
|
-
|
|
155
|
-
<solid-render module>
|
|
156
|
-
reference an existing module + mount one instance
|
|
119
|
+
await htmlRuntime.register();
|
|
157
120
|
```
|
|
158
121
|
|
|
159
|
-
### Define and render in one declaration
|
|
160
|
-
|
|
161
|
-
Render the default export into a normal Solid container:
|
|
162
|
-
|
|
163
122
|
```html
|
|
164
|
-
<div id="app"></div>
|
|
165
|
-
|
|
166
123
|
<script
|
|
167
124
|
type="solid-jsx"
|
|
168
125
|
data-solid-runtime="main"
|
|
@@ -170,824 +127,104 @@ Render the default export into a normal Solid container:
|
|
|
170
127
|
render="#app"
|
|
171
128
|
>
|
|
172
129
|
export default function App() {
|
|
173
|
-
return <h1>Hello
|
|
130
|
+
return <h1>Hello</h1>;
|
|
174
131
|
}
|
|
175
132
|
</script>
|
|
176
133
|
```
|
|
177
134
|
|
|
178
|
-
`render`
|
|
135
|
+
Bare `render` mounts in place without adding a wrapper:
|
|
179
136
|
|
|
180
137
|
```html
|
|
181
|
-
<script
|
|
182
|
-
type="solid-jsx"
|
|
183
|
-
module="/widgets.jsx"
|
|
184
|
-
render="#app"
|
|
185
|
-
component="Counter"
|
|
186
|
-
>
|
|
187
|
-
export function Counter() {
|
|
188
|
-
return <button>Counter</button>;
|
|
189
|
-
}
|
|
190
|
-
</script>
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Bare `render` mounts at the declaration's exact sibling position without adding a wrapper:
|
|
194
|
-
|
|
195
|
-
```html
|
|
196
|
-
<p>Before</p>
|
|
197
|
-
|
|
198
138
|
<script type="solid-jsx" module="/Message.jsx" render>
|
|
199
139
|
export default function Message() {
|
|
200
|
-
return <
|
|
140
|
+
return <button onClick={() => console.log("clicked")}>Click</button>;
|
|
201
141
|
}
|
|
202
142
|
</script>
|
|
203
|
-
|
|
204
|
-
<p>After</p>
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
The adapter replaces the declaration with an owned start/end marker range and inserts the component between the markers. The module definition and mount lifecycle remain separate: removing the declaration through `html.removeElement()` disposes the Solid owner while module removal follows the existing lifecycle options.
|
|
208
|
-
|
|
209
|
-
`entry` and `render` are intentionally mutually exclusive on one declaration. Use separate declarations when a side-effect entry and a mounted component are both needed.
|
|
210
|
-
|
|
211
|
-
All matching modules in one scan/observer batch are defined before any `entry` or `render` action executes, so a render declaration may import a dependency declared later in the same batch.
|
|
212
|
-
|
|
213
|
-
### Reuse an existing module with `<solid-render>`
|
|
214
|
-
|
|
215
|
-
`<solid-render>` references an already-defined module and remains as the light-DOM mount container:
|
|
216
|
-
|
|
217
|
-
```html
|
|
218
|
-
<solid-render
|
|
219
|
-
data-solid-runtime="main"
|
|
220
|
-
module="/Counter.jsx"
|
|
221
|
-
></solid-render>
|
|
222
143
|
```
|
|
223
144
|
|
|
224
|
-
|
|
145
|
+
When the runtime was created with `createSolidRuntime()`, wrapperless mounting lazily ensures Solid's document-level delegated-event infrastructure while keeping every declaration in its own independently disposable `createRoot() + insert()` root.
|
|
225
146
|
|
|
226
|
-
|
|
227
|
-
<solid-render
|
|
228
|
-
data-solid-runtime="main"
|
|
229
|
-
module="/widgets.jsx"
|
|
230
|
-
component="Counter"
|
|
231
|
-
></solid-render>
|
|
232
|
-
```
|
|
147
|
+
See [Wrapperless delegation](./docs/wrapperless-delegation.md).
|
|
233
148
|
|
|
234
|
-
|
|
149
|
+
## Reusable `<solid-render>` instances
|
|
235
150
|
|
|
236
151
|
```html
|
|
237
|
-
<solid-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
prop:module="billing"
|
|
241
|
-
prop:compact
|
|
242
|
-
></solid-render>
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Declarative values are conservative:
|
|
246
|
-
|
|
247
|
-
```text
|
|
248
|
-
prop:name="Alice" → "Alice"
|
|
249
|
-
prop:compact → true
|
|
250
|
-
prop:count="42" → "42"
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Kebab-case prop names normalize to camelCase (`prop:user-id` → `userId`). Arbitrary JavaScript references use the `.props` property:
|
|
254
|
-
|
|
255
|
-
```ts
|
|
256
|
-
const renderer = document.querySelector("solid-render");
|
|
257
|
-
|
|
258
|
-
renderer.props = {
|
|
259
|
-
user,
|
|
260
|
-
onSave,
|
|
261
|
-
service,
|
|
262
|
-
};
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Programmatic props override `prop:*` attributes. Prop changes update the existing component instance reactively and preserve local Solid state. Changes to `module`, `component`, or `data-solid-runtime` dispose the current component and mount a new identity.
|
|
266
|
-
|
|
267
|
-
Initial light-DOM children become `props.children`:
|
|
268
|
-
|
|
269
|
-
```html
|
|
270
|
-
<solid-render module="/Card.jsx" prop:title="Profile">
|
|
271
|
-
<p>Hello from HTML.</p>
|
|
272
|
-
<solid-render module="/SaveButton.jsx"></solid-render>
|
|
273
|
-
</solid-render>
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
The first implementation deliberately has no named-slot system and no Shadow DOM. Nested `<solid-render>` elements work through normal custom-element connection when captured children are instantiated.
|
|
277
|
-
|
|
278
|
-
The HTML adapter registers the custom element automatically **after the initial declarative script batch is installed** by `register()` / `registerHTML()` or the initial `observe()` / `observeHTML()` pass. `createHTMLRuntime()` intentionally does not upgrade existing `<solid-render>` elements immediately, because doing so could make them import modules before preceding `<script module>` declarations have been registered. Most applications therefore do not need to call a registration helper directly.
|
|
279
|
-
|
|
280
|
-
For custom registries, tests, or explicit registration, the helper remains available:
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
import { registerSolidRenderElement } from "solid-tag-runtime/html";
|
|
284
|
-
|
|
285
|
-
registerSolidRenderElement();
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
Multiple `<solid-render>` elements may share one cached runtime module namespace while each mounted component owns independent Solid state. Async module changes are generation-guarded so a stale import can never replace a newer `module`/`component`/runtime selection.
|
|
289
|
-
|
|
290
|
-
`<solid-render>` uses the same runtime-routing rules as declarative scripts. An explicit `data-solid-runtime` selects a controller with that scope **whose root contains the element**. Without an explicit scope, any containing controller that accepts unscoped declarations (`acceptUnscoped: true`) may handle the element. If more than one equally specific controller can handle it, add `data-solid-runtime` to disambiguate. Controllers outside the element's DOM root are never selected as a fallback.
|
|
291
|
-
|
|
292
|
-
Both of these reference the same registered virtual module when used with the correct runtime:
|
|
293
|
-
|
|
294
|
-
```html
|
|
295
|
-
<solid-render module="/ui/Button.jsx"></solid-render>
|
|
296
|
-
<solid-render module="./ui/Button.jsx"></solid-render>
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
Top-level relative module references are normalized as virtual paths; they are not browser URL fetches.
|
|
300
|
-
|
|
301
|
-
A document may safely declare a module and immediately reference it later in the same HTML before calling `register()`:
|
|
302
|
-
|
|
303
|
-
```html
|
|
304
|
-
<script type="solid-jsx" module="/ui/Button.jsx">
|
|
305
|
-
export default function Button() {
|
|
306
|
-
return <button>Ready</button>;
|
|
307
|
-
}
|
|
308
|
-
</script>
|
|
309
|
-
|
|
310
|
-
<solid-render module="/ui/Button.jsx"></solid-render>
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
The adapter installs the declaration batch before existing `solid-render` instances mount. If the custom element class was already registered by another runtime, unresolved instances are retried when the matching module becomes available. A transient startup ordering race therefore does not surface as a `ModuleResolutionError`. If the registration/observer queue settles and the referenced virtual module is still absent, the normal `solid-render` error is reported; the retry behavior does not hide genuine missing-module mistakes.
|
|
314
|
-
|
|
315
|
-
A scope is written to owned script elements as:
|
|
316
|
-
|
|
317
|
-
```html
|
|
318
|
-
<script data-solid-runtime="main" ...></script>
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
When multiple runtimes observe the same document, give each one a unique scope and normally set `acceptUnscoped: false`.
|
|
322
|
-
|
|
323
|
-
A scoped declaration that only defines a module may be ignored by non-matching controllers without noise. A scoped declaration with active `render` semantics is different: rendering requests immediate UI work. If no registered HTML runtime with that scope can handle the declaration's DOM root, the adapter emits one deduplicated console warning and an `html-warning` lifecycle event with `code: "unresolved-runtime-scope"`. `executeRenders: false` suppresses this diagnostic because rendering was explicitly disabled.
|
|
324
|
-
|
|
325
|
-
```ts
|
|
326
|
-
html.subscribe("html-warning", event => {
|
|
327
|
-
if (event.code === "unresolved-runtime-scope") {
|
|
328
|
-
console.warn(event.requestedScope, event.moduleId);
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
`<solid-render>` keeps stronger semantics: when it actively connects and cannot resolve its selected runtime, that is a render error rather than only a warning.
|
|
334
|
-
|
|
335
|
-
### Declarative modules
|
|
336
|
-
|
|
337
|
-
```html
|
|
338
|
-
<script
|
|
339
|
-
type="solid-jsx"
|
|
340
|
-
data-solid-runtime="main"
|
|
341
|
-
module="/ui/Button.jsx">
|
|
342
|
-
export function Button(props) {
|
|
343
|
-
return (
|
|
344
|
-
<button onClick={props.onClick}>
|
|
345
|
-
{props.children}
|
|
346
|
-
</button>
|
|
347
|
-
);
|
|
152
|
+
<script type="solid-jsx" module="/Counter.jsx">
|
|
153
|
+
export default function Counter() {
|
|
154
|
+
return <button>Counter</button>;
|
|
348
155
|
}
|
|
349
156
|
</script>
|
|
350
|
-
```
|
|
351
157
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
```tsx
|
|
355
|
-
import { Button } from "./ui/Button.jsx";
|
|
158
|
+
<solid-render module="/Counter.jsx"></solid-render>
|
|
159
|
+
<solid-render module="/Counter.jsx"></solid-render>
|
|
356
160
|
```
|
|
357
161
|
|
|
358
|
-
|
|
162
|
+
**In HTML, always use the explicit closing tag.** Do not write `<solid-render ... />`; custom elements are not HTML void elements and the self-closing slash is ignored by the HTML parser.
|
|
359
163
|
|
|
360
|
-
|
|
164
|
+
## Persistent compile cache
|
|
361
165
|
|
|
362
|
-
|
|
363
|
-
<script
|
|
364
|
-
type="solid-jsx"
|
|
365
|
-
data-solid-runtime="main"
|
|
366
|
-
module="/ui/Button.jsx"
|
|
367
|
-
src="./components/Button.jsx">
|
|
368
|
-
</script>
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
`src` answers **where source is loaded from**. `module` answers **what identity the source has in the runtime graph**.
|
|
372
|
-
|
|
373
|
-
### User-created script elements
|
|
374
|
-
|
|
375
|
-
If application code creates a script itself, use the controller to associate it with the correct runtime:
|
|
166
|
+
The opt-in compile cache persists **pre-link compiler artifacts**. Runtime-specific linked URLs and evaluated module namespaces are never persisted.
|
|
376
167
|
|
|
377
168
|
```ts
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
export function Greeting(props) {
|
|
383
|
-
return <p>Hello {props.name}</p>;
|
|
384
|
-
}
|
|
385
|
-
`;
|
|
386
|
-
|
|
387
|
-
await html.append(script);
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
`append()` claims the element **before** inserting it in the DOM, stamps the controller scope, then registers it directly. If a `MutationObserver` is active, the later mutation callback sees that the element is already owned and does not compile it twice.
|
|
391
|
-
|
|
392
|
-
For an element that is already in the DOM:
|
|
393
|
-
|
|
394
|
-
```ts
|
|
395
|
-
await html.registerElement(script);
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
Same-controller repeated registration is idempotent. Trying to register an element already owned by another HTML controller throws `HTMLModuleOwnershipError`.
|
|
399
|
-
|
|
400
|
-
### Let the controller create the script
|
|
401
|
-
|
|
402
|
-
```ts
|
|
403
|
-
await html.addModule({
|
|
404
|
-
id: "/dynamic/Badge.jsx",
|
|
405
|
-
source: `
|
|
406
|
-
export function Badge(props) {
|
|
407
|
-
return <span>{props.children}</span>;
|
|
408
|
-
}
|
|
409
|
-
`,
|
|
410
|
-
});
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
`addModule()` creates the `<script>` element, associates it with the controller, appends it, and registers it.
|
|
414
|
-
|
|
415
|
-
### Observe scripts added by other code
|
|
169
|
+
import {
|
|
170
|
+
createRuntime,
|
|
171
|
+
createIndexedDBCompileCache,
|
|
172
|
+
} from "solid-tag-runtime";
|
|
416
173
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
174
|
+
const runtime = createRuntime({
|
|
175
|
+
compileCache: {
|
|
176
|
+
store: createIndexedDBCompileCache({
|
|
177
|
+
database: "my-app-runtime",
|
|
178
|
+
}),
|
|
179
|
+
namespace: "main",
|
|
180
|
+
version: "1",
|
|
422
181
|
},
|
|
423
182
|
});
|
|
424
|
-
|
|
425
|
-
// Some unrelated code mutates the DOM directly.
|
|
426
|
-
const script = document.createElement("script");
|
|
427
|
-
script.type = "solid-jsx";
|
|
428
|
-
script.setAttribute("data-solid-runtime", "main");
|
|
429
|
-
script.setAttribute("module", "/observed/Thing.jsx");
|
|
430
|
-
script.textContent = `export const value = 42;`;
|
|
431
|
-
document.body.append(script);
|
|
432
|
-
|
|
433
|
-
await html.flush();
|
|
434
|
-
const module = await runtime.import("/observed/Thing.jsx");
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
The observer is for DOM changes that happen **outside** the controller API. Prefer `html.append()` / `html.addModule()` when your code controls insertion because those operations are deterministic and do not depend on observer timing.
|
|
438
|
-
|
|
439
|
-
### Ownership and deduplication
|
|
440
|
-
|
|
441
|
-
All HTML controllers share an internal `WeakMap` of script element → ownership/registration record.
|
|
442
|
-
|
|
443
|
-
That controller model gives these guarantees:
|
|
444
|
-
|
|
445
|
-
- one script element has at most one HTML-runtime owner;
|
|
446
|
-
- manual registration and observer registration cannot compile the same element twice;
|
|
447
|
-
- the same controller can register the same element repeatedly without redefining it;
|
|
448
|
-
- two distinct elements cannot silently define the same HTML-owned module ID;
|
|
449
|
-
- an HTML script cannot silently replace a module already defined outside that HTML controller;
|
|
450
|
-
- `data-solid-runtime` is declarative metadata while the in-memory owner record is authoritative.
|
|
451
|
-
|
|
452
|
-
The controller also exposes lightweight introspection:
|
|
453
|
-
|
|
454
|
-
```ts
|
|
455
|
-
html.owns(script);
|
|
456
|
-
html.getModuleId(script);
|
|
457
|
-
html.getElement("/dynamic/Greeting.jsx");
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### Script formats
|
|
461
|
-
|
|
462
|
-
```text
|
|
463
|
-
solid-jsx / text/solid-jsx → JSX transformation
|
|
464
|
-
solid-js / text/solid-js → plain JavaScript module
|
|
465
|
-
solid-module → infer from module/src extension
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
`language="js"` or `language="jsx"` may override inference.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
### Change the observed root
|
|
472
|
-
|
|
473
|
-
The HTML controller can move to a different discovery root without creating a new runtime:
|
|
474
|
-
|
|
475
|
-
```ts
|
|
476
|
-
const first = document.querySelector("#first-runtime")!;
|
|
477
|
-
const second = document.querySelector("#second-runtime")!;
|
|
478
|
-
|
|
479
|
-
const html = createHTMLRuntime(runtime, {
|
|
480
|
-
root: first,
|
|
481
|
-
appendTo: first,
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
await html.observe({ registerExisting: false });
|
|
485
|
-
|
|
486
|
-
// Rebind a live observer to a different node. Because the observer is already
|
|
487
|
-
// connected, matching scripts already in the new root are registered by default.
|
|
488
|
-
await html.setRoot(second);
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
If the controller is disconnected, `setRoot()` only changes configuration unless `registerExisting: true` is passed:
|
|
492
|
-
|
|
493
|
-
```ts
|
|
494
|
-
await html.setRoot(second, {
|
|
495
|
-
registerExisting: true,
|
|
496
|
-
});
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
Use `registerExisting: false` when switching a live observer but intentionally ignoring scripts already present in the new root.
|
|
500
|
-
|
|
501
|
-
Moving the **same root node** elsewhere in the DOM does not require `setRoot()`. `MutationObserver` remains attached to that node object even when it is reparented.
|
|
502
|
-
|
|
503
|
-
### Move root and append target together
|
|
504
|
-
|
|
505
|
-
When the runtime module area itself moves to another container, `moveTo()` changes both boundaries:
|
|
506
|
-
|
|
507
|
-
```ts
|
|
508
|
-
await html.moveTo(nextContainer, {
|
|
509
|
-
registerExisting: true,
|
|
510
|
-
});
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
For an `Element`, `DocumentFragment`, or `ShadowRoot`, future `append()` / `addModule()` calls insert into that root. For a `Document`, the controller chooses the document body (or document element fallback) as the append target.
|
|
514
|
-
|
|
515
|
-
### Change only the append target
|
|
516
|
-
|
|
517
|
-
Observation and insertion can have different boundaries:
|
|
518
|
-
|
|
519
|
-
```ts
|
|
520
|
-
html.setAppendTarget(document.head);
|
|
521
183
|
```
|
|
522
184
|
|
|
523
|
-
|
|
185
|
+
See [Persistent compile cache](./docs/compile-cache.md).
|
|
524
186
|
|
|
525
|
-
|
|
526
|
-
html.root;
|
|
527
|
-
html.appendTarget;
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
The `root` may be a `Document`, normal `Element`, `DocumentFragment`, or `ShadowRoot` (which is a `DocumentFragment`). A controller observes the selected root directly rather than observing the whole document and filtering mutations afterward.
|
|
531
|
-
|
|
532
|
-
### Addition-only observation
|
|
533
|
-
|
|
534
|
-
Observation remains addition-oriented.
|
|
535
|
-
|
|
536
|
-
Changing the source/attributes of an already owned script or removing it from the DOM does not implicitly update/delete the corresponding runtime module. In `0.0.7`, use `html.updateElement()` and `html.removeElement()` when the lifecycle should follow an owned script explicitly, or use the core `runtime.update()` / `runtime.remove()` APIs directly.
|
|
537
|
-
|
|
538
|
-
The HTML adapter does **not** reinterpret arbitrary document HTML as JSX and does not currently assign component semantics to `<template>`.
|
|
539
|
-
|
|
540
|
-
## `toModule()`
|
|
541
|
-
|
|
542
|
-
For one-off source strings:
|
|
543
|
-
|
|
544
|
-
```ts
|
|
545
|
-
const module = await runtime.toModule(`
|
|
546
|
-
export function MyComponent(props) {
|
|
547
|
-
return <>{props.message} {props.name}</>;
|
|
548
|
-
}
|
|
549
|
-
`);
|
|
187
|
+
## Documentation
|
|
550
188
|
|
|
551
|
-
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
`toModule()` creates an anonymous runtime module, compiles it with `solid-tag`, evaluates it as a real ES module, and returns the module namespace.
|
|
189
|
+
Detailed documentation lives under [`docs/`](./docs/README.md):
|
|
555
190
|
|
|
556
|
-
|
|
191
|
+
- [Getting started](./docs/getting-started.md)
|
|
192
|
+
- [Solid runtime setup and providers](./docs/solid-runtime-setup.md)
|
|
193
|
+
- [Runtime modules and resolution](./docs/modules.md)
|
|
194
|
+
- [HTML runtime and ownership](./docs/html-runtime.md)
|
|
195
|
+
- [Declarative rendering](./docs/rendering.md)
|
|
196
|
+
- [Wrapperless delegation](./docs/wrapperless-delegation.md)
|
|
197
|
+
- [`<solid-render>`](./docs/solid-render.md)
|
|
198
|
+
- [Lifecycle events](./docs/lifecycle-events.md)
|
|
199
|
+
- [Persistent compile cache](./docs/compile-cache.md)
|
|
200
|
+
- [Core runtime API](./docs/api/runtime.md)
|
|
201
|
+
- [Solid integration API](./docs/api/solid.md)
|
|
202
|
+
- [HTML runtime API](./docs/api/html.md)
|
|
203
|
+
- [Compile-cache API](./docs/api/compile-cache.md)
|
|
557
204
|
|
|
558
|
-
|
|
205
|
+
For engineering invariants and design decisions, see [`ARCHITECTURE.md`](./ARCHITECTURE.md).
|
|
559
206
|
|
|
560
|
-
|
|
207
|
+
## Browser import maps
|
|
561
208
|
|
|
562
|
-
|
|
563
|
-
const MyComponent = await runtime.toComponent(
|
|
564
|
-
`
|
|
565
|
-
export function MyComponent(props) {
|
|
566
|
-
return <div>{props.message}</div>;
|
|
567
|
-
}
|
|
568
|
-
`,
|
|
569
|
-
{ exportName: "MyComponent" },
|
|
570
|
-
);
|
|
571
|
-
```
|
|
209
|
+
When loading from an import map, map every used package subpath to the same release:
|
|
572
210
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"imports": {
|
|
214
|
+
"solid-tag-runtime": "https://esm.sh/solid-tag-runtime@0.0.14",
|
|
215
|
+
"solid-tag-runtime/html": "https://esm.sh/solid-tag-runtime@0.0.14/html",
|
|
216
|
+
"solid-tag-runtime/solid": "https://esm.sh/solid-tag-runtime@0.0.14/solid"
|
|
579
217
|
}
|
|
580
|
-
`);
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
## Inject application values as modules
|
|
584
|
-
|
|
585
|
-
The runtime deliberately does not rewrite unresolved identifiers into a magical scope object.
|
|
586
|
-
|
|
587
|
-
Instead, application scope can be expressed as a normal module:
|
|
588
|
-
|
|
589
|
-
```ts
|
|
590
|
-
const [count, setCount] = createSignal(0);
|
|
591
|
-
|
|
592
|
-
runtime.defineModule("@app/state", {
|
|
593
|
-
count,
|
|
594
|
-
setCount,
|
|
595
|
-
});
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
Runtime source imports those references normally:
|
|
599
|
-
|
|
600
|
-
```tsx
|
|
601
|
-
import { count, setCount } from "@app/state";
|
|
602
|
-
|
|
603
|
-
export function Counter() {
|
|
604
|
-
return (
|
|
605
|
-
<button onClick={() => setCount(value => value + 1)}>
|
|
606
|
-
{count()}
|
|
607
|
-
</button>
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
This works for:
|
|
613
|
-
|
|
614
|
-
- Solid signals and setters
|
|
615
|
-
- stores
|
|
616
|
-
- components
|
|
617
|
-
- callbacks
|
|
618
|
-
- services
|
|
619
|
-
- data objects
|
|
620
|
-
- application utilities
|
|
621
|
-
|
|
622
|
-
The values cross the boundary as actual JavaScript references; they are not serialized.
|
|
623
|
-
|
|
624
|
-
## Expose existing components
|
|
625
|
-
|
|
626
|
-
```ts
|
|
627
|
-
runtime.defineModule("@app/components", {
|
|
628
|
-
Button,
|
|
629
|
-
Dialog,
|
|
630
|
-
Card,
|
|
631
|
-
});
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
Then runtime JSX can use them like any other module:
|
|
635
|
-
|
|
636
|
-
```tsx
|
|
637
|
-
import { Button } from "@app/components";
|
|
638
|
-
|
|
639
|
-
export function SaveButton() {
|
|
640
|
-
return <Button>Save</Button>;
|
|
641
218
|
}
|
|
642
219
|
```
|
|
643
220
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
A package or external library can be mapped to a native ESM URL:
|
|
647
|
-
|
|
648
|
-
```ts
|
|
649
|
-
runtime.defineUrl(
|
|
650
|
-
"some-library",
|
|
651
|
-
"https://esm.sh/some-library@1.2.3",
|
|
652
|
-
);
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
Then dynamic modules can write:
|
|
656
|
-
|
|
657
|
-
```js
|
|
658
|
-
import something from "some-library";
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
## Plain JavaScript modules
|
|
662
|
-
|
|
663
|
-
JSX transformation can be disabled per module:
|
|
664
|
-
|
|
665
|
-
```ts
|
|
666
|
-
runtime.define(
|
|
667
|
-
"/config.js",
|
|
668
|
-
`export const value = 42;`,
|
|
669
|
-
{ format: "js" },
|
|
670
|
-
);
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
This is useful when the same runtime graph contains both JSX-backed modules and ordinary JavaScript modules.
|
|
674
|
-
|
|
675
|
-
## Compilation without execution
|
|
676
|
-
|
|
677
|
-
```ts
|
|
678
|
-
runtime.define("/App.jsx", source);
|
|
679
|
-
|
|
680
|
-
const compiled = await runtime.compile("/App.jsx");
|
|
681
|
-
|
|
682
|
-
console.log(compiled.code);
|
|
683
|
-
console.log(compiled.dependencies);
|
|
684
|
-
console.log(compiled.diagnostics);
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
This is useful for editors, debugging tools, and inspecting the exact `html\`\`` output generated by `solid-tag`.
|
|
688
|
-
|
|
689
|
-
## Module graph introspection
|
|
690
|
-
|
|
691
|
-
```ts
|
|
692
|
-
runtime.modules();
|
|
693
|
-
runtime.dependencies("/App.jsx");
|
|
694
|
-
runtime.dependents("/ui/Button.jsx");
|
|
695
|
-
runtime.getModuleInfo("/App.jsx");
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
## Updating modules
|
|
699
|
-
|
|
700
|
-
```ts
|
|
701
|
-
runtime.update("/ui/Button.jsx", newButtonSource);
|
|
702
|
-
|
|
703
|
-
const app = await runtime.import("/App.jsx");
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
Updating a module invalidates its compiled URL and its dependent runtime modules. Re-importing produces a newly linked graph.
|
|
707
|
-
|
|
708
|
-
Existing references to an older module namespace/component are not mutated. Applications that implement live editing should import the updated entry module again.
|
|
709
|
-
|
|
710
|
-
### Define several source modules together
|
|
711
|
-
|
|
712
|
-
```ts
|
|
713
|
-
runtime.defineMany([
|
|
714
|
-
{
|
|
715
|
-
id: "/ui/Button.jsx",
|
|
716
|
-
source: buttonSource,
|
|
717
|
-
},
|
|
718
|
-
{
|
|
719
|
-
id: "/App.jsx",
|
|
720
|
-
source: appSource,
|
|
721
|
-
},
|
|
722
|
-
]);
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
`defineMany()` validates the whole definition list before applying it and rejects duplicate module IDs inside the batch. It is useful when a group of modules should become available before anything is imported. Lifecycle notifications from the batch are published only after every definition has been installed.
|
|
726
|
-
|
|
727
|
-
### Remove modules
|
|
728
|
-
|
|
729
|
-
```ts
|
|
730
|
-
runtime.remove("/ui/Button.jsx");
|
|
731
|
-
```
|
|
732
|
-
|
|
733
|
-
Removal invalidates transitive dependents by default. A later import of a dependent will fail resolution until the missing module is defined again.
|
|
734
|
-
|
|
735
|
-
If you intentionally want to leave currently linked dependents untouched:
|
|
736
|
-
|
|
737
|
-
```ts
|
|
738
|
-
runtime.remove("/ui/Button.jsx", {
|
|
739
|
-
invalidateDependents: false,
|
|
740
|
-
});
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
### Clear a runtime
|
|
744
|
-
|
|
745
|
-
```ts
|
|
746
|
-
runtime.clear();
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
By default `clear()` removes source and URL modules while preserving host modules registered with `defineModule()`. This is convenient for editor/preview resets where the host environment should remain installed.
|
|
750
|
-
|
|
751
|
-
```ts
|
|
752
|
-
runtime.clear({
|
|
753
|
-
preserveHostModules: false,
|
|
754
|
-
});
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
removes everything. The returned array contains the IDs that were removed.
|
|
758
|
-
|
|
759
|
-
### Lifecycle events
|
|
760
|
-
|
|
761
|
-
`0.0.8` expands lifecycle subscriptions into a typed event stream with independent join/leave semantics.
|
|
762
|
-
|
|
763
|
-
Subscribe to everything:
|
|
764
|
-
|
|
765
|
-
```ts
|
|
766
|
-
const unsubscribe = runtime.subscribe(event => {
|
|
767
|
-
console.log(event.type, event);
|
|
768
|
-
});
|
|
769
|
-
```
|
|
770
|
-
|
|
771
|
-
Subscribe to one event type:
|
|
772
|
-
|
|
773
|
-
```ts
|
|
774
|
-
const leaveErrors = runtime.subscribe(
|
|
775
|
-
"module-error",
|
|
776
|
-
event => {
|
|
777
|
-
console.error(event.phase, event.error);
|
|
778
|
-
},
|
|
779
|
-
);
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
Subscribe to several event types:
|
|
783
|
-
|
|
784
|
-
```ts
|
|
785
|
-
const leaveEvaluation = runtime.subscribe(
|
|
786
|
-
["module-evaluating", "module-evaluated"],
|
|
787
|
-
event => {
|
|
788
|
-
console.log(event.type, event.id);
|
|
789
|
-
},
|
|
790
|
-
);
|
|
791
|
-
```
|
|
792
|
-
|
|
793
|
-
Each call owns an independent subscription:
|
|
794
|
-
|
|
795
|
-
```ts
|
|
796
|
-
leaveEvaluation();
|
|
797
|
-
// the error subscription remains active
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
Core events include:
|
|
801
|
-
|
|
802
|
-
```text
|
|
803
|
-
module-defined
|
|
804
|
-
module-updated
|
|
805
|
-
modules-defined
|
|
806
|
-
|
|
807
|
-
module-resolving
|
|
808
|
-
module-resolved
|
|
809
|
-
|
|
810
|
-
module-linking
|
|
811
|
-
module-linked
|
|
812
|
-
|
|
813
|
-
module-invalidated
|
|
814
|
-
module-compiled
|
|
815
|
-
module-evaluating
|
|
816
|
-
module-evaluated
|
|
817
|
-
|
|
818
|
-
module-removed
|
|
819
|
-
module-error
|
|
820
|
-
|
|
821
|
-
runtime-cleared
|
|
822
|
-
runtime-disposed
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
`module-resolved` reports how a specifier was resolved (`relative`, `registered`, `custom`, `native`, and so on). `module-linked` exposes the logical dependency mapping used to link a source module. `modules-defined` marks the completion of a `defineMany()` batch after all definitions have been installed.
|
|
826
|
-
|
|
827
|
-
`module-error.phase` is one of the runtime pipeline phases such as `resolve`, `analyze`, `compile`, `link`, `url-create`, `evaluate`, or `host-bridge`.
|
|
828
|
-
|
|
829
|
-
Subscribers are observational only: they cannot cancel or modify runtime operations, and subscriber exceptions are isolated from runtime execution. Behavioral extension remains separate through the resolver, compiler adapter, and module URL backend.
|
|
830
|
-
|
|
831
|
-
### HTML lifecycle events
|
|
832
|
-
|
|
833
|
-
The HTML controller has its own event stream because DOM ownership/observation is intentionally separate from the core module engine:
|
|
834
|
-
|
|
835
|
-
```ts
|
|
836
|
-
const leaveHTML = html.subscribe(event => {
|
|
837
|
-
console.log(event.type, event);
|
|
838
|
-
});
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
The same selective forms are supported:
|
|
842
|
-
|
|
843
|
-
```ts
|
|
844
|
-
const leaveOwnership = html.subscribe(
|
|
845
|
-
["element-claimed", "element-registered", "element-removed"],
|
|
846
|
-
event => {
|
|
847
|
-
console.log(event.type, event.moduleId);
|
|
848
|
-
},
|
|
849
|
-
);
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
HTML events include:
|
|
853
|
-
|
|
854
|
-
```text
|
|
855
|
-
element-discovered
|
|
856
|
-
element-claimed
|
|
857
|
-
element-loading
|
|
858
|
-
element-loaded
|
|
859
|
-
element-registered
|
|
860
|
-
|
|
861
|
-
element-updating
|
|
862
|
-
element-updated
|
|
863
|
-
element-removing
|
|
864
|
-
element-released
|
|
865
|
-
element-removed
|
|
866
|
-
|
|
867
|
-
observer-connected
|
|
868
|
-
observer-disconnected
|
|
869
|
-
observer-batch
|
|
870
|
-
|
|
871
|
-
root-changing
|
|
872
|
-
root-changed
|
|
873
|
-
append-target-changed
|
|
874
|
-
|
|
875
|
-
entry-executing
|
|
876
|
-
entry-executed
|
|
877
|
-
|
|
878
|
-
render-mounting
|
|
879
|
-
render-mounted
|
|
880
|
-
render-disposing
|
|
881
|
-
render-disposed
|
|
882
|
-
|
|
883
|
-
html-warning
|
|
884
|
-
html-error
|
|
885
|
-
```
|
|
886
|
-
|
|
887
|
-
Ownership-related events include an `origin` describing how the element entered the controller (`observer`, `append`, `register-element`, `add-module`, and related internal scan origins). This is useful for tracing observer/manual-registration races and proving that an explicit `append()` was not processed a second time by the observer.
|
|
888
|
-
|
|
889
|
-
`html-warning` currently reports non-fatal adapter diagnostics. `unresolved-runtime-scope` is emitted once per render declaration/scope when `data-solid-runtime` requests immediate rendering but no registered controller can handle that scoped declaration. The event includes `requestedScope`, `registeredScopes`, `moduleId` when available, and whether a matching scope exists outside the declaration's root.
|
|
890
|
-
|
|
891
|
-
`observer-batch` summarizes a DOM discovery pass instead of exposing noisy raw `MutationRecord` objects. `element-loading` / `element-loaded` are emitted for `src`-backed modules, and entry events distinguish evaluation caused by an HTML `entry` declaration from an ordinary `runtime.import()`. Render events cover both `<script render>` and `<solid-render>`; `source` identifies `script-render` versus `solid-render`, and `mode` identifies selector, in-place, or container mounting.
|
|
892
|
-
|
|
893
|
-
Like core runtime subscribers, HTML subscribers are observational and exception-isolated.
|
|
894
|
-
|
|
895
|
-
## HTML-owned module updates and removal
|
|
896
|
-
|
|
897
|
-
When a script element is already owned by an HTML runtime controller, update the runtime module from the current element contents with:
|
|
898
|
-
|
|
899
|
-
```ts
|
|
900
|
-
script.textContent = `
|
|
901
|
-
export function Card() {
|
|
902
|
-
return <div>updated</div>;
|
|
903
|
-
}
|
|
904
|
-
`;
|
|
905
|
-
|
|
906
|
-
await html.updateElement(script);
|
|
907
|
-
```
|
|
908
|
-
|
|
909
|
-
`updateElement()` keeps the existing logical module ID and calls `runtime.update()` underneath. Changing the element's logical `module` identity is intentionally rejected; remove and register it again instead.
|
|
910
|
-
|
|
911
|
-
Release an owned element explicitly with:
|
|
912
|
-
|
|
913
|
-
```ts
|
|
914
|
-
await html.removeElement(script);
|
|
915
|
-
```
|
|
916
|
-
|
|
917
|
-
By default this:
|
|
918
|
-
|
|
919
|
-
1. removes the runtime module,
|
|
920
|
-
2. invalidates its dependents,
|
|
921
|
-
3. releases HTML ownership, and
|
|
922
|
-
4. removes the script element from the DOM.
|
|
923
|
-
|
|
924
|
-
The two lifecycles can be controlled independently:
|
|
925
|
-
|
|
926
|
-
```ts
|
|
927
|
-
await html.removeElement(script, {
|
|
928
|
-
removeModule: false,
|
|
929
|
-
removeFromDOM: true,
|
|
930
|
-
});
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
This is intentionally explicit: ordinary DOM removal does not silently delete a runtime module.
|
|
934
|
-
|
|
935
|
-
## Custom resolution
|
|
936
|
-
|
|
937
|
-
```ts
|
|
938
|
-
const runtime = createRuntime({
|
|
939
|
-
resolve(specifier, importer) {
|
|
940
|
-
if (specifier.startsWith("@ui/")) {
|
|
941
|
-
return `/ui/${specifier.slice(4)}`;
|
|
942
|
-
}
|
|
943
|
-
},
|
|
944
|
-
});
|
|
945
|
-
```
|
|
946
|
-
|
|
947
|
-
Default runtime resolution supports:
|
|
948
|
-
|
|
949
|
-
- exact virtual module IDs
|
|
950
|
-
- relative virtual imports
|
|
951
|
-
- absolute virtual paths
|
|
952
|
-
- registered URL modules
|
|
953
|
-
- native/bare imports when `allowNativeImports` is enabled
|
|
954
|
-
|
|
955
|
-
## Native imports
|
|
956
|
-
|
|
957
|
-
`allowNativeImports` defaults to `true`.
|
|
958
|
-
|
|
959
|
-
This means an unresolved **bare** import can be left for the browser's native module resolver/import map:
|
|
960
|
-
|
|
961
|
-
```ts
|
|
962
|
-
const runtime = createRuntime({
|
|
963
|
-
allowNativeImports: true,
|
|
964
|
-
});
|
|
965
|
-
```
|
|
966
|
-
|
|
967
|
-
Relative and absolute virtual paths are different. If `/ui/Button.jsx` or `./ui/Button.jsx` does not resolve to a registered runtime module, `runtime.import()` throws `ModuleResolutionError`; it does not turn the path into a browser/native network import. Use `defineUrl()` or an actual absolute URL when native URL loading is intended.
|
|
968
|
-
|
|
969
|
-
Set `allowNativeImports` to `false` when even unresolved bare imports must be explicitly registered with the runtime.
|
|
970
|
-
|
|
971
|
-
## Runtime lifecycle
|
|
972
|
-
|
|
973
|
-
```ts
|
|
974
|
-
runtime.invalidate("/App.jsx");
|
|
975
|
-
runtime.remove("/Unused.jsx");
|
|
976
|
-
runtime.clear();
|
|
977
|
-
runtime.dispose();
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
`invalidate()` forces fresh compilation/evaluation without deleting the module definition. `remove()` deletes one definition, `clear()` resets a group of definitions, and `dispose()` permanently tears down the runtime instance.
|
|
221
|
+
The Solid helper does **not** inject or mutate import maps.
|
|
981
222
|
|
|
982
223
|
## Current limitations
|
|
983
224
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
-
|
|
987
|
-
-
|
|
988
|
-
-
|
|
989
|
-
-
|
|
990
|
-
- TypeScript/TSX parsing depends on what the underlying `solid-tag` compiler supports
|
|
991
|
-
- this package is **not a security sandbox**; loaded source executes with the privileges of the page
|
|
992
|
-
|
|
993
|
-
See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for the detailed design and decision log.
|
|
225
|
+
- runtime-defined source-module cycles are not supported yet
|
|
226
|
+
- generated browser modules require a compatible CSP for the selected module URL backend
|
|
227
|
+
- persistent compile caching is an optimization, not a security sandbox
|
|
228
|
+
- already-held namespace/component references are not mutated by module updates
|
|
229
|
+
- TypeScript/TSX support depends on the configured compiler
|
|
230
|
+
- the built-in provider layer currently supports esm.sh and jsDelivr; unsafe mixed-provider cases fail rather than silently loading a second Solid runtime
|