solid-tag-runtime 0.0.2 → 0.0.4
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 +203 -24
- package/README.md +112 -75
- package/html.d.ts +92 -5
- package/package.json +1 -1
- package/src/html.js +619 -149
package/ARCHITECTURE.md
CHANGED
|
@@ -195,50 +195,209 @@ This permits ordinary JavaScript and JSX modules to coexist in one graph.
|
|
|
195
195
|
|
|
196
196
|
## 8. HTML/browser adapter
|
|
197
197
|
|
|
198
|
-
`
|
|
198
|
+
`solid-tag-runtime/html` is an opt-in browser-facing adapter over the core module runtime.
|
|
199
|
+
|
|
200
|
+
`0.0.3` made the adapter runtime-aware rather than treating document scripts as a single global pool.
|
|
199
201
|
|
|
200
202
|
Important architectural boundary:
|
|
201
203
|
|
|
202
204
|
```text
|
|
203
205
|
DOM / HTML document
|
|
204
206
|
↓
|
|
205
|
-
|
|
207
|
+
HTML runtime controller
|
|
206
208
|
↓
|
|
207
209
|
runtime.define(...)
|
|
208
210
|
↓
|
|
209
211
|
core module graph
|
|
210
212
|
```
|
|
211
213
|
|
|
212
|
-
The core runtime remains DOM-independent. It does not scan documents,
|
|
214
|
+
The core runtime remains DOM-independent. It does not scan documents, own HTML elements, run `MutationObserver`, or know about HTML attributes.
|
|
213
215
|
|
|
214
216
|
### Public HTML API
|
|
215
217
|
|
|
218
|
+
The preferred API for non-trivial usage is a persistent controller:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const html = createHTMLRuntime(runtime, {
|
|
222
|
+
scope: "preview",
|
|
223
|
+
root: document,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await html.register();
|
|
227
|
+
await html.observe({ registerExisting: false });
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The controller exposes:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
html.register(options?);
|
|
234
|
+
html.observe(options?);
|
|
235
|
+
html.flush(options?);
|
|
236
|
+
html.disconnect();
|
|
237
|
+
|
|
238
|
+
html.defineElement(element, options?);
|
|
239
|
+
html.registerElement(element, options?);
|
|
240
|
+
html.append(element, options?);
|
|
241
|
+
html.addModule(options);
|
|
242
|
+
|
|
243
|
+
html.owns(element);
|
|
244
|
+
html.getModuleId(element);
|
|
245
|
+
html.getElement(moduleId);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The original convenience functions remain supported and are implemented on top of a cached default controller for each runtime:
|
|
249
|
+
|
|
216
250
|
```ts
|
|
217
251
|
await registerHTML(runtime, options?);
|
|
218
252
|
await defineScript(runtime, element, options?);
|
|
219
253
|
const observer = await observeHTML(runtime, options?);
|
|
220
254
|
```
|
|
221
255
|
|
|
222
|
-
|
|
256
|
+
This keeps the simple single-runtime API while giving multi-runtime applications an explicit ownership object.
|
|
257
|
+
|
|
258
|
+
### Element ownership model
|
|
259
|
+
|
|
260
|
+
A module-level `WeakMap` is the authoritative in-memory ownership registry for runtime script elements.
|
|
261
|
+
|
|
262
|
+
Conceptually:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
WeakMap<HTMLScriptElement, ElementRecord>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
An element record stores at least:
|
|
269
|
+
|
|
270
|
+
```text
|
|
271
|
+
owner controller
|
|
272
|
+
module ID
|
|
273
|
+
format
|
|
274
|
+
entry flag
|
|
275
|
+
source URL
|
|
276
|
+
registration state
|
|
277
|
+
pending registration promise
|
|
278
|
+
result/error
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
This registry is shared by all HTML runtime controllers created by this package instance.
|
|
282
|
+
|
|
283
|
+
Important invariants:
|
|
284
|
+
|
|
285
|
+
1. A runtime script element has at most one HTML-runtime owner.
|
|
286
|
+
2. Ownership is claimed synchronously before asynchronous fetch/compile work.
|
|
287
|
+
3. `append()` claims ownership before inserting the node into the DOM.
|
|
288
|
+
4. Observer discovery, scans, explicit registration, and append/add helpers all use the same ownership record.
|
|
289
|
+
5. Re-registering an element through the same controller is idempotent and waits on the same pending registration work.
|
|
290
|
+
6. A different controller attempting to claim an owned element receives `HTMLModuleOwnershipError`.
|
|
291
|
+
7. The observer must never redefine a module already being handled through `append()` or `registerElement()`.
|
|
292
|
+
8. Distinct HTML elements cannot silently claim the same module ID in one HTML controller.
|
|
293
|
+
9. Existing non-HTML runtime modules are not silently replaced by HTML registration.
|
|
294
|
+
10. DOM removal does not implicitly delete a runtime module.
|
|
295
|
+
|
|
296
|
+
A `WeakSet` alone is insufficient because the adapter needs to distinguish ownership, pending work, success, and failure. The shared `WeakMap` also avoids retaining detached elements after all external references disappear.
|
|
297
|
+
|
|
298
|
+
### Controller scope and multiple runtimes
|
|
299
|
+
|
|
300
|
+
A controller may declare a scope:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
const previewHTML = createHTMLRuntime(previewRuntime, {
|
|
304
|
+
scope: "preview",
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Owned elements are marked declaratively:
|
|
309
|
+
|
|
310
|
+
```html
|
|
311
|
+
<script
|
|
312
|
+
type="solid-jsx"
|
|
313
|
+
data-solid-runtime="preview"
|
|
314
|
+
module="/App.jsx">
|
|
315
|
+
</script>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
`data-solid-runtime` is useful for declarative selection and DevTools visibility. It is not the authoritative ownership record; the `WeakMap` points to the actual controller object and prevents two controllers with the same string scope from owning the same element.
|
|
319
|
+
|
|
320
|
+
A scoped controller accepts matching scripts. It may also claim unscoped scripts when `acceptUnscoped` is enabled (default `true`). Setting `acceptUnscoped: false` is recommended when multiple scoped runtimes observe the same DOM root.
|
|
321
|
+
|
|
322
|
+
An unscoped controller does not silently claim a script carrying `data-solid-runtime` for another runtime.
|
|
323
|
+
|
|
324
|
+
### User-created script elements
|
|
325
|
+
|
|
326
|
+
Applications may create the element themselves and explicitly associate it with one runtime:
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
const script = document.createElement("script");
|
|
330
|
+
script.type = "solid-jsx";
|
|
331
|
+
script.setAttribute("module", "/Preview.jsx");
|
|
332
|
+
script.textContent = source;
|
|
333
|
+
|
|
334
|
+
await previewHTML.append(script);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`append()` performs this ordering:
|
|
223
338
|
|
|
224
|
-
|
|
339
|
+
```text
|
|
340
|
+
claim element
|
|
341
|
+
↓
|
|
342
|
+
stamp data-solid-runtime (when scoped)
|
|
343
|
+
↓
|
|
344
|
+
append to DOM
|
|
345
|
+
↓
|
|
346
|
+
load/define module directly
|
|
347
|
+
↓
|
|
348
|
+
observer later sees mutation and skips duplicate work
|
|
349
|
+
```
|
|
225
350
|
|
|
226
|
-
|
|
351
|
+
This is deliberately not implemented as “append and wait for MutationObserver”. Explicit APIs are deterministic even when observation is disabled.
|
|
227
352
|
|
|
228
|
-
|
|
353
|
+
For an element already in the DOM:
|
|
229
354
|
|
|
230
355
|
```ts
|
|
231
|
-
|
|
232
|
-
observer.connected;
|
|
233
|
-
await observer.flush();
|
|
234
|
-
observer.disconnect();
|
|
356
|
+
await previewHTML.registerElement(script);
|
|
235
357
|
```
|
|
236
358
|
|
|
237
|
-
|
|
359
|
+
### Controller-created script elements
|
|
360
|
+
|
|
361
|
+
`addModule()` creates and owns the script element for the caller:
|
|
238
362
|
|
|
239
|
-
|
|
363
|
+
```ts
|
|
364
|
+
await previewHTML.addModule({
|
|
365
|
+
id: "/dynamic/Greeting.jsx",
|
|
366
|
+
source: `
|
|
367
|
+
export function Greeting(props) {
|
|
368
|
+
return <p>Hello {props.name}</p>;
|
|
369
|
+
}
|
|
370
|
+
`,
|
|
371
|
+
});
|
|
372
|
+
```
|
|
240
373
|
|
|
241
|
-
|
|
374
|
+
It uses the same claim-before-append pipeline as `append()`.
|
|
375
|
+
|
|
376
|
+
### One-shot registration and live observation
|
|
377
|
+
|
|
378
|
+
`register()` / `registerHTML()` perform one-shot discovery.
|
|
379
|
+
|
|
380
|
+
`observe()` / `observeHTML()` use `MutationObserver` for newly added scripts. Existing scripts are registered by default; `registerExisting: false` supports the pattern where a one-shot registration already ran.
|
|
381
|
+
|
|
382
|
+
Every discovered batch preserves the graph invariant:
|
|
383
|
+
|
|
384
|
+
```text
|
|
385
|
+
discover complete batch
|
|
386
|
+
↓
|
|
387
|
+
claim applicable elements
|
|
388
|
+
↓
|
|
389
|
+
load all source
|
|
390
|
+
↓
|
|
391
|
+
define all modules
|
|
392
|
+
↓
|
|
393
|
+
execute newly discovered entries
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
An entry may therefore import a dependency that appears later in the same discovery batch.
|
|
397
|
+
|
|
398
|
+
`flush()` waits for queued observer work and scans again, providing a deterministic synchronization point after external DOM mutations.
|
|
399
|
+
|
|
400
|
+
Observation remains addition-only: changing attributes/text of an already owned script or removing it does not implicitly update/remove the runtime module. Module replacement remains explicit through the core runtime update/invalidation APIs.
|
|
242
401
|
|
|
243
402
|
### Supported script declarations
|
|
244
403
|
|
|
@@ -278,9 +437,7 @@ Example:
|
|
|
278
437
|
|
|
279
438
|
The source may move without changing imports of `/ui/Button.jsx`.
|
|
280
439
|
|
|
281
|
-
If a `src` script omits `module`, its resolved source URL becomes the module ID.
|
|
282
|
-
|
|
283
|
-
Inline non-entry scripts require an explicit `module` identity. Inline entries may omit it and receive an anonymous runtime ID.
|
|
440
|
+
If a `src` script omits `module`, its resolved source URL becomes the module ID. Inline non-entry scripts require an explicit `module` identity. Inline entries may omit it and receive an anonymous runtime ID.
|
|
284
441
|
|
|
285
442
|
### Entry execution
|
|
286
443
|
|
|
@@ -290,13 +447,7 @@ Inline non-entry scripts require an explicit `module` identity. Inline entries m
|
|
|
290
447
|
<script type="solid-jsx" module="/App.jsx" entry>...</script>
|
|
291
448
|
```
|
|
292
449
|
|
|
293
|
-
After all discovered modules are defined, the adapter
|
|
294
|
-
|
|
295
|
-
```ts
|
|
296
|
-
await runtime.import("/App.jsx");
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
The entry module is expected to perform whatever side effect starts the application, such as calling `render()`.
|
|
450
|
+
After all newly discovered modules in the batch are defined, the adapter imports each entry unless `executeEntries: false` is supplied.
|
|
300
451
|
|
|
301
452
|
### Explicitly not HTML-as-JSX
|
|
302
453
|
|
|
@@ -737,3 +888,31 @@ Decision: add `observeHTML()` to the existing `solid-tag-runtime/html` subpath i
|
|
|
737
888
|
|
|
738
889
|
Reason: applications that dynamically insert runtime modules should not need to manually rescan the document, while keeping DOM observation separate from the core runtime module engine. Addition-only semantics avoid silently redefining modules because of text/attribute churn or trying to infer deletion semantics before the runtime has an explicit module-removal API.
|
|
739
890
|
|
|
891
|
+
### 2026-10-02 — Persistent HTML runtime controllers own DOM/module bindings
|
|
892
|
+
|
|
893
|
+
Decision: `0.0.3` added `createHTMLRuntime(runtime, options)` as the preferred advanced HTML API while keeping `registerHTML()`, `observeHTML()`, and `defineScript()` as compatibility conveniences.
|
|
894
|
+
|
|
895
|
+
Reason: once multiple runtimes, explicit append/register helpers, and observers coexist, ownership must be represented by a persistent object instead of implicit document-wide behavior.
|
|
896
|
+
|
|
897
|
+
### 2026-10-02 — Runtime script ownership is coordinated through a shared WeakMap
|
|
898
|
+
|
|
899
|
+
Decision: all HTML ingestion paths use one module-level `WeakMap` from script element to ownership/registration record. Per-controller module-ID maps supplement element ownership.
|
|
900
|
+
|
|
901
|
+
Reason: a simple observer-local `WeakSet` cannot prevent races between manual registration and MutationObserver, distinguish different owners, retain a pending registration promise, or reject cross-runtime claims deterministically.
|
|
902
|
+
|
|
903
|
+
### 2026-10-02 — Explicit append claims before DOM insertion
|
|
904
|
+
|
|
905
|
+
Decision: `html.append(element)` and `html.addModule()` claim ownership before mutating the DOM and register directly rather than relying on the observer callback.
|
|
906
|
+
|
|
907
|
+
Reason: ownership-before-insertion guarantees the observer can only observe already-owned work, eliminating duplicate compilation and making explicit APIs deterministic even when no observer is active.
|
|
908
|
+
|
|
909
|
+
### 2026-10-02 — HTML runtime scope is declarative metadata, not ownership authority
|
|
910
|
+
|
|
911
|
+
Decision: scoped controllers stamp/use `data-solid-runtime="<scope>"` for discovery and debugging, while the in-memory WeakMap remains authoritative.
|
|
912
|
+
|
|
913
|
+
Reason: attributes are useful across declarative HTML and DevTools but cannot uniquely identify a controller instance or safely coordinate concurrent registration work.
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
### 0.0.4 — typing/test/documentation hardening
|
|
917
|
+
|
|
918
|
+
Decision: publish the next package as `0.0.4` because `0.0.3` has already been published. `0.0.4` preserves the `0.0.3` runtime/HTML semantics while shipping the DOM-compatible HTML adapter declaration fixes, broader regression coverage, and updated feature documentation. No module-resolution, ownership, execution-backend, or cache-invalidation semantics change in this release.
|
package/README.md
CHANGED
|
@@ -100,18 +100,48 @@ The runtime compiles and links the dependency graph automatically.
|
|
|
100
100
|
|
|
101
101
|
## Declarative HTML modules
|
|
102
102
|
|
|
103
|
-
`
|
|
103
|
+
`solid-tag-runtime/html` is the browser/DOM adapter. The core runtime remains DOM-independent.
|
|
104
|
+
|
|
105
|
+
For simple single-runtime use, the original helpers remain available:
|
|
104
106
|
|
|
105
107
|
```ts
|
|
106
|
-
import {
|
|
108
|
+
import {
|
|
109
|
+
registerHTML,
|
|
110
|
+
observeHTML,
|
|
111
|
+
} from "solid-tag-runtime/html";
|
|
107
112
|
|
|
108
113
|
await registerHTML(runtime);
|
|
109
114
|
```
|
|
110
115
|
|
|
111
|
-
|
|
116
|
+
For applications that may have multiple runtimes or that create runtime scripts programmatically, `0.0.3` added a persistent HTML runtime controller:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { createHTMLRuntime } from "solid-tag-runtime/html";
|
|
120
|
+
|
|
121
|
+
const html = createHTMLRuntime(runtime, {
|
|
122
|
+
scope: "main",
|
|
123
|
+
root: document,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await html.register();
|
|
127
|
+
await html.observe({ registerExisting: false });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
A scope is written to owned script elements as:
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<script data-solid-runtime="main" ...></script>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
When multiple runtimes observe the same document, give each one a unique scope and normally set `acceptUnscoped: false`.
|
|
137
|
+
|
|
138
|
+
### Declarative modules
|
|
112
139
|
|
|
113
140
|
```html
|
|
114
|
-
<script
|
|
141
|
+
<script
|
|
142
|
+
type="solid-jsx"
|
|
143
|
+
data-solid-runtime="main"
|
|
144
|
+
module="/ui/Button.jsx">
|
|
115
145
|
export function Button(props) {
|
|
116
146
|
return (
|
|
117
147
|
<button onClick={props.onClick}>
|
|
@@ -120,126 +150,133 @@ The adapter recognizes runtime script modules such as:
|
|
|
120
150
|
);
|
|
121
151
|
}
|
|
122
152
|
</script>
|
|
153
|
+
```
|
|
123
154
|
|
|
124
|
-
|
|
125
|
-
import { render } from "@solidjs/web";
|
|
126
|
-
import { Button } from "./ui/Button.jsx";
|
|
127
|
-
|
|
128
|
-
function App() {
|
|
129
|
-
return <Button>Hello from runtime JSX</Button>;
|
|
130
|
-
}
|
|
155
|
+
Runtime modules import one another normally:
|
|
131
156
|
|
|
132
|
-
|
|
133
|
-
|
|
157
|
+
```tsx
|
|
158
|
+
import { Button } from "./ui/Button.jsx";
|
|
134
159
|
```
|
|
135
160
|
|
|
136
|
-
All
|
|
161
|
+
All newly discovered scripts in one batch are defined before any newly discovered `entry` module executes, so HTML document order does not require dependencies to appear first.
|
|
137
162
|
|
|
138
|
-
External source
|
|
163
|
+
### External source
|
|
139
164
|
|
|
140
165
|
```html
|
|
141
166
|
<script
|
|
142
167
|
type="solid-jsx"
|
|
168
|
+
data-solid-runtime="main"
|
|
143
169
|
module="/ui/Button.jsx"
|
|
144
170
|
src="./components/Button.jsx">
|
|
145
171
|
</script>
|
|
146
|
-
|
|
147
|
-
<script
|
|
148
|
-
type="solid-jsx"
|
|
149
|
-
module="/App.jsx"
|
|
150
|
-
src="./App.jsx"
|
|
151
|
-
entry>
|
|
152
|
-
</script>
|
|
153
172
|
```
|
|
154
173
|
|
|
155
|
-
`src` answers **where
|
|
174
|
+
`src` answers **where source is loaded from**. `module` answers **what identity the source has in the runtime graph**.
|
|
156
175
|
|
|
157
|
-
|
|
176
|
+
### User-created script elements
|
|
158
177
|
|
|
159
|
-
|
|
178
|
+
If application code creates a script itself, use the controller to associate it with the correct runtime:
|
|
160
179
|
|
|
161
|
-
|
|
180
|
+
```ts
|
|
181
|
+
const script = document.createElement("script");
|
|
182
|
+
script.type = "solid-jsx";
|
|
183
|
+
script.setAttribute("module", "/dynamic/Greeting.jsx");
|
|
184
|
+
script.textContent = `
|
|
185
|
+
export function Greeting(props) {
|
|
186
|
+
return <p>Hello {props.name}</p>;
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
162
189
|
|
|
163
|
-
|
|
164
|
-
solid-jsx / text/solid-jsx → JSX transformation
|
|
165
|
-
solid-js / text/solid-js → plain JavaScript module
|
|
166
|
-
solid-module → infer from module/src extension
|
|
190
|
+
await html.append(script);
|
|
167
191
|
```
|
|
168
192
|
|
|
169
|
-
|
|
193
|
+
`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.
|
|
170
194
|
|
|
171
|
-
|
|
195
|
+
For an element that is already in the DOM:
|
|
172
196
|
|
|
173
197
|
```ts
|
|
174
|
-
|
|
175
|
-
executeEntries: false,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
console.log(registration.modules);
|
|
179
|
-
console.log(registration.entries);
|
|
180
|
-
|
|
181
|
-
await runtime.import(registration.entries[0]);
|
|
198
|
+
await html.registerElement(script);
|
|
182
199
|
```
|
|
183
200
|
|
|
184
|
-
|
|
201
|
+
Same-controller repeated registration is idempotent. Trying to register an element already owned by another HTML controller throws `HTMLModuleOwnershipError`.
|
|
185
202
|
|
|
186
|
-
|
|
187
|
-
import { defineScript } from "solid-tag-runtime/html";
|
|
203
|
+
### Let the controller create the script
|
|
188
204
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
```ts
|
|
206
|
+
await html.addModule({
|
|
207
|
+
id: "/dynamic/Badge.jsx",
|
|
208
|
+
source: `
|
|
209
|
+
export function Badge(props) {
|
|
210
|
+
return <span>{props.children}</span>;
|
|
211
|
+
}
|
|
212
|
+
`,
|
|
213
|
+
});
|
|
195
214
|
```
|
|
196
215
|
|
|
197
|
-
|
|
216
|
+
`addModule()` creates the `<script>` element, associates it with the controller, appends it, and registers it.
|
|
198
217
|
|
|
199
|
-
|
|
218
|
+
### Observe scripts added by other code
|
|
200
219
|
|
|
201
220
|
```ts
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const observer = await observeHTML(runtime, {
|
|
221
|
+
await html.observe({
|
|
222
|
+
registerExisting: false,
|
|
205
223
|
onError(error) {
|
|
206
|
-
console.error(
|
|
224
|
+
console.error(error);
|
|
207
225
|
},
|
|
208
226
|
});
|
|
209
227
|
|
|
210
|
-
//
|
|
228
|
+
// Some unrelated code mutates the DOM directly.
|
|
211
229
|
const script = document.createElement("script");
|
|
212
230
|
script.type = "solid-jsx";
|
|
213
|
-
script.setAttribute("
|
|
214
|
-
script.
|
|
215
|
-
|
|
216
|
-
return <p>Hello {props.name}</p>;
|
|
217
|
-
}
|
|
218
|
-
`;
|
|
231
|
+
script.setAttribute("data-solid-runtime", "main");
|
|
232
|
+
script.setAttribute("module", "/observed/Thing.jsx");
|
|
233
|
+
script.textContent = `export const value = 42;`;
|
|
219
234
|
document.body.append(script);
|
|
220
235
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
236
|
+
await html.flush();
|
|
237
|
+
const module = await runtime.import("/observed/Thing.jsx");
|
|
238
|
+
```
|
|
224
239
|
|
|
225
|
-
|
|
240
|
+
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.
|
|
226
241
|
|
|
227
|
-
|
|
228
|
-
|
|
242
|
+
### Ownership and deduplication
|
|
243
|
+
|
|
244
|
+
All HTML controllers share an internal `WeakMap` of script element → ownership/registration record.
|
|
245
|
+
|
|
246
|
+
That controller model gives these guarantees:
|
|
229
247
|
|
|
230
|
-
|
|
248
|
+
- one script element has at most one HTML-runtime owner;
|
|
249
|
+
- manual registration and observer registration cannot compile the same element twice;
|
|
250
|
+
- the same controller can register the same element repeatedly without redefining it;
|
|
251
|
+
- two distinct elements cannot silently define the same HTML-owned module ID;
|
|
252
|
+
- an HTML script cannot silently replace a module already defined outside that HTML controller;
|
|
253
|
+
- `data-solid-runtime` is declarative metadata while the in-memory owner record is authoritative.
|
|
254
|
+
|
|
255
|
+
The controller also exposes lightweight introspection:
|
|
231
256
|
|
|
232
257
|
```ts
|
|
233
|
-
|
|
258
|
+
html.owns(script);
|
|
259
|
+
html.getModuleId(script);
|
|
260
|
+
html.getElement("/dynamic/Greeting.jsx");
|
|
261
|
+
```
|
|
234
262
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
263
|
+
### Script formats
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
solid-jsx / text/solid-jsx → JSX transformation
|
|
267
|
+
solid-js / text/solid-js → plain JavaScript module
|
|
268
|
+
solid-module → infer from module/src extension
|
|
238
269
|
```
|
|
239
270
|
|
|
240
|
-
|
|
271
|
+
`language="js"` or `language="jsx"` may override inference.
|
|
272
|
+
|
|
273
|
+
### Addition-only observation
|
|
274
|
+
|
|
275
|
+
Observation remains addition-only in `0.0.4`.
|
|
276
|
+
|
|
277
|
+
Changing the source/attributes of an already owned script or removing it from the DOM does not implicitly update/delete the corresponding runtime module. Use `runtime.update()` / `runtime.invalidate()` for explicit module lifecycle changes.
|
|
241
278
|
|
|
242
|
-
The HTML adapter does **not** reinterpret
|
|
279
|
+
The HTML adapter does **not** reinterpret arbitrary document HTML as JSX and does not currently assign component semantics to `<template>`.
|
|
243
280
|
|
|
244
281
|
## `toModule()`
|
|
245
282
|
|
package/html.d.ts
CHANGED
|
@@ -4,12 +4,28 @@ import type { ModuleFormat, ModuleNamespaceLike, SolidTagRuntime } from "./index
|
|
|
4
4
|
export interface HTMLModuleScriptElement {
|
|
5
5
|
textContent?: string | null;
|
|
6
6
|
baseURI?: string;
|
|
7
|
+
ownerDocument?: HTMLDocumentLike | null;
|
|
7
8
|
getAttribute(name: string): string | null;
|
|
8
9
|
hasAttribute?(name: string): boolean;
|
|
10
|
+
setAttribute?(name: string, value: string): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface HTMLAppendTarget {
|
|
14
|
+
append?(...nodes: any[]): void;
|
|
15
|
+
appendChild?(element: any): unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface HTMLDocumentLike extends HTMLModuleRoot, HTMLAppendTarget {
|
|
19
|
+
body?: HTMLAppendTarget;
|
|
20
|
+
documentElement?: HTMLAppendTarget;
|
|
21
|
+
createElement?(tagName: string): any;
|
|
9
22
|
}
|
|
10
23
|
|
|
11
24
|
export interface HTMLModuleRoot {
|
|
12
25
|
baseURI?: string;
|
|
26
|
+
ownerDocument?: HTMLDocumentLike | null;
|
|
27
|
+
body?: HTMLAppendTarget;
|
|
28
|
+
documentElement?: HTMLAppendTarget;
|
|
13
29
|
querySelectorAll(selector: string): Iterable<HTMLModuleScriptElement> | ArrayLike<HTMLModuleScriptElement>;
|
|
14
30
|
}
|
|
15
31
|
|
|
@@ -44,6 +60,10 @@ export interface DefineScriptOptions {
|
|
|
44
60
|
baseUrl?: string;
|
|
45
61
|
format?: ModuleFormat | "javascript";
|
|
46
62
|
fetch?: HTMLFetch;
|
|
63
|
+
/** Declarative ownership scope stored in data-solid-runtime. */
|
|
64
|
+
scope?: string;
|
|
65
|
+
/** Whether an HTML runtime may claim scripts without data-solid-runtime. Default: true. */
|
|
66
|
+
acceptUnscoped?: boolean;
|
|
47
67
|
}
|
|
48
68
|
|
|
49
69
|
export interface HTMLModuleDefinition {
|
|
@@ -80,22 +100,89 @@ export interface ObserveHTMLOptions extends RegisterHTMLOptions {
|
|
|
80
100
|
onError?: (error: unknown) => void;
|
|
81
101
|
}
|
|
82
102
|
|
|
83
|
-
export interface
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
export interface AddHTMLModuleOptions extends DefineScriptOptions {
|
|
104
|
+
id?: string;
|
|
105
|
+
module?: string;
|
|
106
|
+
source?: string;
|
|
107
|
+
src?: string;
|
|
108
|
+
entry?: boolean;
|
|
109
|
+
type?: string;
|
|
110
|
+
language?: string;
|
|
111
|
+
executeEntries?: boolean;
|
|
112
|
+
appendTo?: HTMLAppendTarget;
|
|
113
|
+
document?: HTMLDocumentLike;
|
|
114
|
+
createElement?: (tagName: string) => HTMLModuleScriptElement;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface HTMLRuntimeOptions extends ObserveHTMLOptions {
|
|
118
|
+
appendTo?: HTMLAppendTarget;
|
|
119
|
+
createElement?: (tagName: string) => HTMLModuleScriptElement;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface HTMLRuntimeController {
|
|
123
|
+
readonly runtime: SolidTagRuntime;
|
|
124
|
+
readonly scope?: string;
|
|
86
125
|
readonly connected: boolean;
|
|
87
|
-
/**
|
|
88
|
-
|
|
126
|
+
/** Result of the observer's initial scan. */
|
|
127
|
+
readonly initial: RegisterHTMLResult;
|
|
128
|
+
|
|
129
|
+
/** One-shot scan of matching scripts under the configured/root override. */
|
|
130
|
+
register(options?: RegisterHTMLOptions): Promise<RegisterHTMLResult>;
|
|
131
|
+
|
|
132
|
+
/** Start live addition observation. Returns this controller. */
|
|
133
|
+
observe(options?: ObserveHTMLOptions): Promise<HTMLRuntimeController>;
|
|
134
|
+
|
|
135
|
+
/** Wait for queued observer work and scan for newly discoverable scripts. */
|
|
136
|
+
flush(options?: RegisterHTMLOptions): Promise<RegisterHTMLResult>;
|
|
89
137
|
disconnect(): void;
|
|
138
|
+
|
|
139
|
+
/** Define one element without auto-running an entry. */
|
|
140
|
+
defineElement(
|
|
141
|
+
element: HTMLModuleScriptElement,
|
|
142
|
+
options?: DefineScriptOptions,
|
|
143
|
+
): Promise<HTMLModuleDefinition>;
|
|
144
|
+
|
|
145
|
+
/** Explicitly claim/register an existing element for this runtime. */
|
|
146
|
+
registerElement(
|
|
147
|
+
element: HTMLModuleScriptElement,
|
|
148
|
+
options?: RegisterHTMLOptions,
|
|
149
|
+
): Promise<HTMLModuleDefinition>;
|
|
150
|
+
|
|
151
|
+
/** Claim first, append a user-created element, then register it directly. */
|
|
152
|
+
append(
|
|
153
|
+
element: HTMLModuleScriptElement,
|
|
154
|
+
options?: RegisterHTMLOptions & { appendTo?: HTMLAppendTarget },
|
|
155
|
+
): Promise<HTMLModuleDefinition>;
|
|
156
|
+
|
|
157
|
+
/** Create a runtime script element and add it through the controller. */
|
|
158
|
+
addModule(options: AddHTMLModuleOptions): Promise<HTMLModuleDefinition>;
|
|
159
|
+
|
|
160
|
+
owns(element: HTMLModuleScriptElement): boolean;
|
|
161
|
+
getModuleId(element: HTMLModuleScriptElement): string | undefined;
|
|
162
|
+
getElement(id: string): HTMLModuleScriptElement | undefined;
|
|
90
163
|
}
|
|
91
164
|
|
|
165
|
+
/** Legacy observer shape is now the full HTML runtime controller. */
|
|
166
|
+
export type HTMLObserverController = HTMLRuntimeController;
|
|
167
|
+
|
|
92
168
|
export declare class HTMLModuleError extends SolidTagRuntimeError {
|
|
93
169
|
element?: HTMLModuleScriptElement;
|
|
94
170
|
moduleId?: string;
|
|
95
171
|
sourceUrl?: string;
|
|
96
172
|
}
|
|
97
173
|
|
|
174
|
+
export declare class HTMLModuleOwnershipError extends HTMLModuleError {
|
|
175
|
+
ownerScope?: string;
|
|
176
|
+
requestedScope?: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
98
179
|
export declare const htmlModuleSelector: string;
|
|
180
|
+
export declare const htmlRuntimeScopeAttribute: "data-solid-runtime";
|
|
181
|
+
|
|
182
|
+
export declare function createHTMLRuntime(
|
|
183
|
+
runtime: SolidTagRuntime,
|
|
184
|
+
options?: HTMLRuntimeOptions,
|
|
185
|
+
): HTMLRuntimeController;
|
|
99
186
|
|
|
100
187
|
export declare function defineScript(
|
|
101
188
|
runtime: SolidTagRuntime,
|