solid-tag-runtime 0.0.6 → 0.0.8
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 +167 -3
- package/README.md +233 -3
- package/html.d.ts +79 -1
- package/index.d.ts +71 -0
- package/package.json +1 -1
- package/src/events.js +63 -0
- package/src/html.js +420 -23
- package/src/runtime.js +392 -78
package/ARCHITECTURE.md
CHANGED
|
@@ -38,9 +38,11 @@ The runtime should allow applications to:
|
|
|
38
38
|
5. expose host components, signals, callbacks, services, and data as modules
|
|
39
39
|
6. reuse compiled/evaluated modules through caching
|
|
40
40
|
7. invalidate and rebuild a module graph when source changes
|
|
41
|
-
8.
|
|
42
|
-
9.
|
|
43
|
-
10.
|
|
41
|
+
8. explicitly remove and clear module definitions
|
|
42
|
+
9. observe runtime lifecycle transitions for tooling/tracing
|
|
43
|
+
10. inspect dependencies and compiled source
|
|
44
|
+
11. support normal JavaScript modules in the same graph
|
|
45
|
+
12. keep the JSX compiler replaceable behind a narrow adapter
|
|
44
46
|
|
|
45
47
|
## 3. Current non-goals
|
|
46
48
|
|
|
@@ -99,6 +101,7 @@ const runtime = createRuntime(options);
|
|
|
99
101
|
|
|
100
102
|
```ts
|
|
101
103
|
runtime.define(id, source, options?);
|
|
104
|
+
runtime.defineMany(definitions);
|
|
102
105
|
runtime.update(id, source, options?);
|
|
103
106
|
```
|
|
104
107
|
|
|
@@ -136,9 +139,18 @@ runtime.getModuleInfo(id);
|
|
|
136
139
|
|
|
137
140
|
```ts
|
|
138
141
|
runtime.invalidate(id);
|
|
142
|
+
runtime.remove(id, options?);
|
|
143
|
+
runtime.clear(options?);
|
|
144
|
+
|
|
145
|
+
runtime.subscribe(listener);
|
|
146
|
+
runtime.subscribe(type, listener);
|
|
147
|
+
runtime.subscribe(types, listener);
|
|
148
|
+
|
|
139
149
|
runtime.dispose();
|
|
140
150
|
```
|
|
141
151
|
|
|
152
|
+
Lifecycle subscriptions are synchronous observational notifications. They do not intercept or modify runtime behavior. Resolution behavior remains customizable through `resolve()`, compilation through the compiler adapter, and executable-module creation through `ModuleUrlBackend`.
|
|
153
|
+
|
|
142
154
|
## 6. Module kinds
|
|
143
155
|
|
|
144
156
|
The runtime currently has three module record kinds.
|
|
@@ -230,6 +242,10 @@ await html.observe({ registerExisting: false });
|
|
|
230
242
|
The controller exposes:
|
|
231
243
|
|
|
232
244
|
```ts
|
|
245
|
+
html.subscribe(listener);
|
|
246
|
+
html.subscribe(type, listener);
|
|
247
|
+
html.subscribe(types, listener);
|
|
248
|
+
|
|
233
249
|
html.register(options?);
|
|
234
250
|
html.observe(options?);
|
|
235
251
|
html.flush(options?);
|
|
@@ -980,3 +996,151 @@ Reason: applications can reparent an existing observed root without intervention
|
|
|
980
996
|
Invariant: a root change must not create a second observer, lose ownership records, or race pending scans from the old root. Pending observer work is drained before rebinding. A live root change registers existing scripts in the new root by default; a disconnected root change does not perform discovery unless requested.
|
|
981
997
|
|
|
982
998
|
Regression coverage: the suite verifies same-node reparenting, live root rebinding, skipping existing scripts, disconnected root changes, `moveTo()` synchronization of root/append target, and independent append-target changes.
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
### 0.0.7 — explicit module lifecycle, batching, and HTML-owned replacement
|
|
1002
|
+
|
|
1003
|
+
Decision: add `defineMany()`, `remove()`, `clear()`, and `subscribe()` to the core runtime, and `updateElement()` / `removeElement()` to `solid-tag-runtime/html`.
|
|
1004
|
+
|
|
1005
|
+
Reason: long-lived runtime environments need an explicit lifecycle beyond define/update/invalidate. Editors, previews, CMSs, and plugin hosts must be able to install related modules together, remove stale definitions, reset runtime state while preserving host modules, trace module transitions, and keep HTML ownership synchronized with explicit module replacement/removal.
|
|
1006
|
+
|
|
1007
|
+
Core lifecycle semantics:
|
|
1008
|
+
|
|
1009
|
+
- `defineMany()` validates the definition list before applying it and rejects duplicate IDs inside one batch. No module evaluation happens during definition, so the full batch is available before an import starts linking. Lifecycle events produced while applying the batch are buffered and published only after every definition has been installed, preventing a subscriber from observing or importing a partially installed batch.
|
|
1010
|
+
- `remove(id)` deletes one module record, revokes generated URLs, removes host-registry state when applicable, and invalidates transitive dependents by default.
|
|
1011
|
+
- `clear()` removes source/URL modules while preserving host modules by default; `preserveHostModules:false` removes every record.
|
|
1012
|
+
- `subscribe(listener)` emits synchronous lifecycle events but isolates subscriber exceptions from runtime execution.
|
|
1013
|
+
- public `resolve()` remains the canonical tooling hook for asking how a specifier resolves without evaluating it.
|
|
1014
|
+
|
|
1015
|
+
Lifecycle event contract:
|
|
1016
|
+
|
|
1017
|
+
```text
|
|
1018
|
+
module-defined
|
|
1019
|
+
module-invalidated
|
|
1020
|
+
module-compiled
|
|
1021
|
+
module-evaluating
|
|
1022
|
+
module-evaluated
|
|
1023
|
+
module-removed
|
|
1024
|
+
module-error
|
|
1025
|
+
runtime-cleared
|
|
1026
|
+
runtime-disposed
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
HTML lifecycle semantics:
|
|
1030
|
+
|
|
1031
|
+
- `updateElement(element)` only operates on an element already owned by that controller. It re-reads/fetches source and calls `runtime.update()` while preserving logical module identity. Changing `module` identity requires explicit remove + register.
|
|
1032
|
+
- `removeElement(element)` releases the shared WeakMap ownership and per-controller binding. By default it also removes the runtime module and DOM node. `removeModule` and `removeFromDOM` can be controlled independently.
|
|
1033
|
+
- DOM removal by itself still does not imply module removal. Module lifecycle remains explicit.
|
|
1034
|
+
- If ownership is released while the element remains under an active observed root, the controller marks that element ignored so the observer does not immediately reclaim it. An explicit `registerElement()` may claim it again.
|
|
1035
|
+
|
|
1036
|
+
Invariants:
|
|
1037
|
+
|
|
1038
|
+
1. Removing a module must revoke its executable URL/namespace cache and clean outbound graph edges.
|
|
1039
|
+
2. Dependents are invalidated before dependency removal unless explicitly disabled.
|
|
1040
|
+
3. `defineMany()` must not publish lifecycle events until the whole batch has been installed.
|
|
1041
|
+
4. `clear()` must not silently discard host environment modules under its default configuration.
|
|
1042
|
+
5. Lifecycle listeners are observational; listener failures cannot alter runtime execution.
|
|
1043
|
+
6. HTML element ownership and runtime module existence are related but separate lifecycles.
|
|
1044
|
+
7. `updateElement()` never changes the module identity associated with an owned element.
|
|
1045
|
+
8. `removeElement()` is the canonical operation for releasing shared WeakMap ownership.
|
|
1046
|
+
|
|
1047
|
+
Regression coverage: package tests cover batch definition and duplicate rejection, dependency removal/invalidation, clear-with-host-preservation, lifecycle event delivery/unsubscribe, owned element updates, identity-change rejection, ownership release, DOM removal, and preserving a runtime module while releasing its HTML element.
|
|
1048
|
+
|
|
1049
|
+
### 0.0.8 — selective lifecycle subscriptions and HTML observability
|
|
1050
|
+
|
|
1051
|
+
Decision: expand lifecycle observation into typed, independently removable subscriptions on both the core runtime and the HTML adapter. Keep lifecycle subscriptions observational and separate from behavioral hooks/interceptors.
|
|
1052
|
+
|
|
1053
|
+
Core subscription forms:
|
|
1054
|
+
|
|
1055
|
+
```ts
|
|
1056
|
+
runtime.subscribe(listener);
|
|
1057
|
+
runtime.subscribe("module-error", listener);
|
|
1058
|
+
runtime.subscribe(["module-evaluating", "module-evaluated"], listener);
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
HTML controllers expose the same shape:
|
|
1062
|
+
|
|
1063
|
+
```ts
|
|
1064
|
+
html.subscribe(listener);
|
|
1065
|
+
html.subscribe("element-registered", listener);
|
|
1066
|
+
html.subscribe(["observer-batch", "html-error"], listener);
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
Each call creates an independent subscription and returns its own disposer. Disposing one subscription never affects another registration of the same callback or subscriptions to other event groups. Subscriber exceptions are reported but cannot alter runtime/HTML operations.
|
|
1070
|
+
|
|
1071
|
+
Core event additions:
|
|
1072
|
+
|
|
1073
|
+
```text
|
|
1074
|
+
module-updated
|
|
1075
|
+
modules-defined
|
|
1076
|
+
module-resolving
|
|
1077
|
+
module-resolved
|
|
1078
|
+
module-linking
|
|
1079
|
+
module-linked
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
The evaluation pipeline is now observed in semantic order:
|
|
1083
|
+
|
|
1084
|
+
```text
|
|
1085
|
+
module-linking
|
|
1086
|
+
↓
|
|
1087
|
+
module-resolving / module-resolved
|
|
1088
|
+
↓
|
|
1089
|
+
module-linked
|
|
1090
|
+
↓
|
|
1091
|
+
module-compiled
|
|
1092
|
+
↓
|
|
1093
|
+
module-evaluating
|
|
1094
|
+
↓
|
|
1095
|
+
module-evaluated
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
`module-resolved` exposes the logical resolution result and resolution kind, while `module-linked` exposes source specifier → resolved module-ID dependencies without exposing Blob/data URL implementation details. `modules-defined` marks the completion of a `defineMany()` batch after buffered per-module events become observable. `module-updated` supplements the backward-compatible `module-defined { operation: "update" }` event with an explicit semantic update notification.
|
|
1099
|
+
|
|
1100
|
+
`module-error` remains one event with a structured pipeline phase instead of creating separate error event types. Supported phases are `resolve`, `analyze`, `compile`, `link`, `url-create`, `evaluate`, and `host-bridge`.
|
|
1101
|
+
|
|
1102
|
+
HTML controller event additions:
|
|
1103
|
+
|
|
1104
|
+
```text
|
|
1105
|
+
element-discovered
|
|
1106
|
+
element-claimed
|
|
1107
|
+
element-loading
|
|
1108
|
+
element-loaded
|
|
1109
|
+
element-registered
|
|
1110
|
+
element-updating
|
|
1111
|
+
element-updated
|
|
1112
|
+
element-removing
|
|
1113
|
+
element-released
|
|
1114
|
+
element-removed
|
|
1115
|
+
observer-connected
|
|
1116
|
+
observer-disconnected
|
|
1117
|
+
observer-batch
|
|
1118
|
+
root-changing
|
|
1119
|
+
root-changed
|
|
1120
|
+
append-target-changed
|
|
1121
|
+
entry-executing
|
|
1122
|
+
entry-executed
|
|
1123
|
+
html-error
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
HTML ownership events include an origin describing the ingestion path (`initial-scan`, `observer`, `register-element`, `append`, `add-module`, etc.). The observer emits summarized `observer-batch` events instead of exposing raw `MutationRecord` values. Root/append-target events belong only to the HTML controller and are deliberately not mixed into core runtime events.
|
|
1127
|
+
|
|
1128
|
+
Architectural boundary:
|
|
1129
|
+
|
|
1130
|
+
```text
|
|
1131
|
+
Observation
|
|
1132
|
+
runtime.subscribe()
|
|
1133
|
+
html.subscribe()
|
|
1134
|
+
|
|
1135
|
+
Behavior customization
|
|
1136
|
+
resolve()
|
|
1137
|
+
compiler adapter
|
|
1138
|
+
ModuleUrlBackend
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
Lifecycle listeners are not interceptors: they cannot cancel evaluation, rewrite source, change resolution, or alter cache behavior. If source/evaluation interception is needed later, it must be designed as a separate hook contract with explicit ordering, async, cancellation, and caching semantics.
|
|
1142
|
+
|
|
1143
|
+
Implementation note: core and HTML event streams share a small internal dispatcher so all/selective subscription behavior, independent disposers, and listener-error isolation have identical semantics while keeping separate event unions.
|
|
1144
|
+
|
|
1145
|
+
Regression coverage: package tests cover catch-all, single-type, and multi-type subscriptions; independent unsubscribe; listener exception isolation; update/batch events; resolution/link/evaluation ordering and payloads; structured resolution errors; HTML ownership origins; observer connect/disconnect/batch events; root and append-target events; external source loading; entry execution; update/removal events; HTML error reporting; and HTML subscriber exception isolation.
|
|
1146
|
+
|
package/README.md
CHANGED
|
@@ -102,6 +102,17 @@ The runtime compiles and links the dependency graph automatically.
|
|
|
102
102
|
|
|
103
103
|
`solid-tag-runtime/html` is the browser/DOM adapter. The core runtime remains DOM-independent.
|
|
104
104
|
|
|
105
|
+
Browser import maps must map the subpath explicitly as well as the package root:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"imports": {
|
|
110
|
+
"solid-tag-runtime": "https://esm.sh/solid-tag-runtime@0.0.8",
|
|
111
|
+
"solid-tag-runtime/html": "https://esm.sh/solid-tag-runtime@0.0.8/html"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
105
116
|
For simple single-runtime use, the original helpers remain available:
|
|
106
117
|
|
|
107
118
|
```ts
|
|
@@ -334,9 +345,9 @@ The `root` may be a `Document`, normal `Element`, `DocumentFragment`, or `Shadow
|
|
|
334
345
|
|
|
335
346
|
### Addition-only observation
|
|
336
347
|
|
|
337
|
-
Observation remains addition-
|
|
348
|
+
Observation remains addition-oriented.
|
|
338
349
|
|
|
339
|
-
Changing the source/attributes of an already owned script or removing it from the DOM does not implicitly update/delete the corresponding runtime module.
|
|
350
|
+
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.
|
|
340
351
|
|
|
341
352
|
The HTML adapter does **not** reinterpret arbitrary document HTML as JSX and does not currently assign component semantics to `<template>`.
|
|
342
353
|
|
|
@@ -510,6 +521,223 @@ Updating a module invalidates its compiled URL and its dependent runtime modules
|
|
|
510
521
|
|
|
511
522
|
Existing references to an older module namespace/component are not mutated. Applications that implement live editing should import the updated entry module again.
|
|
512
523
|
|
|
524
|
+
### Define several source modules together
|
|
525
|
+
|
|
526
|
+
```ts
|
|
527
|
+
runtime.defineMany([
|
|
528
|
+
{
|
|
529
|
+
id: "/ui/Button.jsx",
|
|
530
|
+
source: buttonSource,
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
id: "/App.jsx",
|
|
534
|
+
source: appSource,
|
|
535
|
+
},
|
|
536
|
+
]);
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
`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.
|
|
540
|
+
|
|
541
|
+
### Remove modules
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
runtime.remove("/ui/Button.jsx");
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Removal invalidates transitive dependents by default. A later import of a dependent will fail resolution until the missing module is defined again.
|
|
548
|
+
|
|
549
|
+
If you intentionally want to leave currently linked dependents untouched:
|
|
550
|
+
|
|
551
|
+
```ts
|
|
552
|
+
runtime.remove("/ui/Button.jsx", {
|
|
553
|
+
invalidateDependents: false,
|
|
554
|
+
});
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Clear a runtime
|
|
558
|
+
|
|
559
|
+
```ts
|
|
560
|
+
runtime.clear();
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
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.
|
|
564
|
+
|
|
565
|
+
```ts
|
|
566
|
+
runtime.clear({
|
|
567
|
+
preserveHostModules: false,
|
|
568
|
+
});
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
removes everything. The returned array contains the IDs that were removed.
|
|
572
|
+
|
|
573
|
+
### Lifecycle events
|
|
574
|
+
|
|
575
|
+
`0.0.8` expands lifecycle subscriptions into a typed event stream with independent join/leave semantics.
|
|
576
|
+
|
|
577
|
+
Subscribe to everything:
|
|
578
|
+
|
|
579
|
+
```ts
|
|
580
|
+
const unsubscribe = runtime.subscribe(event => {
|
|
581
|
+
console.log(event.type, event);
|
|
582
|
+
});
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Subscribe to one event type:
|
|
586
|
+
|
|
587
|
+
```ts
|
|
588
|
+
const leaveErrors = runtime.subscribe(
|
|
589
|
+
"module-error",
|
|
590
|
+
event => {
|
|
591
|
+
console.error(event.phase, event.error);
|
|
592
|
+
},
|
|
593
|
+
);
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Subscribe to several event types:
|
|
597
|
+
|
|
598
|
+
```ts
|
|
599
|
+
const leaveEvaluation = runtime.subscribe(
|
|
600
|
+
["module-evaluating", "module-evaluated"],
|
|
601
|
+
event => {
|
|
602
|
+
console.log(event.type, event.id);
|
|
603
|
+
},
|
|
604
|
+
);
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
Each call owns an independent subscription:
|
|
608
|
+
|
|
609
|
+
```ts
|
|
610
|
+
leaveEvaluation();
|
|
611
|
+
// the error subscription remains active
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Core events include:
|
|
615
|
+
|
|
616
|
+
```text
|
|
617
|
+
module-defined
|
|
618
|
+
module-updated
|
|
619
|
+
modules-defined
|
|
620
|
+
|
|
621
|
+
module-resolving
|
|
622
|
+
module-resolved
|
|
623
|
+
|
|
624
|
+
module-linking
|
|
625
|
+
module-linked
|
|
626
|
+
|
|
627
|
+
module-invalidated
|
|
628
|
+
module-compiled
|
|
629
|
+
module-evaluating
|
|
630
|
+
module-evaluated
|
|
631
|
+
|
|
632
|
+
module-removed
|
|
633
|
+
module-error
|
|
634
|
+
|
|
635
|
+
runtime-cleared
|
|
636
|
+
runtime-disposed
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
`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.
|
|
640
|
+
|
|
641
|
+
`module-error.phase` is one of the runtime pipeline phases such as `resolve`, `analyze`, `compile`, `link`, `url-create`, `evaluate`, or `host-bridge`.
|
|
642
|
+
|
|
643
|
+
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.
|
|
644
|
+
|
|
645
|
+
### HTML lifecycle events
|
|
646
|
+
|
|
647
|
+
The HTML controller has its own event stream because DOM ownership/observation is intentionally separate from the core module engine:
|
|
648
|
+
|
|
649
|
+
```ts
|
|
650
|
+
const leaveHTML = html.subscribe(event => {
|
|
651
|
+
console.log(event.type, event);
|
|
652
|
+
});
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
The same selective forms are supported:
|
|
656
|
+
|
|
657
|
+
```ts
|
|
658
|
+
const leaveOwnership = html.subscribe(
|
|
659
|
+
["element-claimed", "element-registered", "element-removed"],
|
|
660
|
+
event => {
|
|
661
|
+
console.log(event.type, event.moduleId);
|
|
662
|
+
},
|
|
663
|
+
);
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
HTML events include:
|
|
667
|
+
|
|
668
|
+
```text
|
|
669
|
+
element-discovered
|
|
670
|
+
element-claimed
|
|
671
|
+
element-loading
|
|
672
|
+
element-loaded
|
|
673
|
+
element-registered
|
|
674
|
+
|
|
675
|
+
element-updating
|
|
676
|
+
element-updated
|
|
677
|
+
element-removing
|
|
678
|
+
element-released
|
|
679
|
+
element-removed
|
|
680
|
+
|
|
681
|
+
observer-connected
|
|
682
|
+
observer-disconnected
|
|
683
|
+
observer-batch
|
|
684
|
+
|
|
685
|
+
root-changing
|
|
686
|
+
root-changed
|
|
687
|
+
append-target-changed
|
|
688
|
+
|
|
689
|
+
entry-executing
|
|
690
|
+
entry-executed
|
|
691
|
+
|
|
692
|
+
html-error
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
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.
|
|
696
|
+
|
|
697
|
+
`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()`.
|
|
698
|
+
|
|
699
|
+
Like core runtime subscribers, HTML subscribers are observational and exception-isolated.
|
|
700
|
+
|
|
701
|
+
## HTML-owned module updates and removal
|
|
702
|
+
|
|
703
|
+
When a script element is already owned by an HTML runtime controller, update the runtime module from the current element contents with:
|
|
704
|
+
|
|
705
|
+
```ts
|
|
706
|
+
script.textContent = `
|
|
707
|
+
export function Card() {
|
|
708
|
+
return <div>updated</div>;
|
|
709
|
+
}
|
|
710
|
+
`;
|
|
711
|
+
|
|
712
|
+
await html.updateElement(script);
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
`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.
|
|
716
|
+
|
|
717
|
+
Release an owned element explicitly with:
|
|
718
|
+
|
|
719
|
+
```ts
|
|
720
|
+
await html.removeElement(script);
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
By default this:
|
|
724
|
+
|
|
725
|
+
1. removes the runtime module,
|
|
726
|
+
2. invalidates its dependents,
|
|
727
|
+
3. releases HTML ownership, and
|
|
728
|
+
4. removes the script element from the DOM.
|
|
729
|
+
|
|
730
|
+
The two lifecycles can be controlled independently:
|
|
731
|
+
|
|
732
|
+
```ts
|
|
733
|
+
await html.removeElement(script, {
|
|
734
|
+
removeModule: false,
|
|
735
|
+
removeFromDOM: true,
|
|
736
|
+
});
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
This is intentionally explicit: ordinary DOM removal does not silently delete a runtime module.
|
|
740
|
+
|
|
513
741
|
## Custom resolution
|
|
514
742
|
|
|
515
743
|
```ts
|
|
@@ -548,10 +776,12 @@ Set it to `false` when all module dependencies must be explicitly registered wit
|
|
|
548
776
|
|
|
549
777
|
```ts
|
|
550
778
|
runtime.invalidate("/App.jsx");
|
|
779
|
+
runtime.remove("/Unused.jsx");
|
|
780
|
+
runtime.clear();
|
|
551
781
|
runtime.dispose();
|
|
552
782
|
```
|
|
553
783
|
|
|
554
|
-
`
|
|
784
|
+
`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.
|
|
555
785
|
|
|
556
786
|
## Current limitations
|
|
557
787
|
|
package/html.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export interface HTMLModuleScriptElement {
|
|
|
5
5
|
textContent?: string | null;
|
|
6
6
|
baseURI?: string;
|
|
7
7
|
ownerDocument?: HTMLDocumentLike | null;
|
|
8
|
-
parentNode?: unknown;
|
|
8
|
+
parentNode?: { removeChild?(element: any): unknown } | null;
|
|
9
|
+
remove?(): void;
|
|
9
10
|
getAttribute(name: string): string | null;
|
|
10
11
|
hasAttribute?(name: string): boolean;
|
|
11
12
|
setAttribute?(name: string, value: string): void;
|
|
@@ -76,6 +77,22 @@ export interface HTMLModuleDefinition {
|
|
|
76
77
|
element: HTMLModuleScriptElement;
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
export interface RemoveHTMLElementOptions {
|
|
81
|
+
/** Remove the runtime module as well as releasing HTML ownership. Default: true. */
|
|
82
|
+
removeModule?: boolean;
|
|
83
|
+
/** Remove the script element from the DOM. Default: true. */
|
|
84
|
+
removeFromDOM?: boolean;
|
|
85
|
+
/** Invalidate runtime dependents when removing the module. Default: true. */
|
|
86
|
+
invalidateDependents?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RemoveHTMLElementResult {
|
|
90
|
+
id: string;
|
|
91
|
+
element: HTMLModuleScriptElement;
|
|
92
|
+
moduleRemoved: boolean;
|
|
93
|
+
elementRemoved: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
export interface RegisterHTMLOptions extends DefineScriptOptions {
|
|
80
97
|
selector?: string;
|
|
81
98
|
executeEntries?: boolean;
|
|
@@ -90,6 +107,45 @@ export interface RegisterHTMLResult {
|
|
|
90
107
|
}>;
|
|
91
108
|
}
|
|
92
109
|
|
|
110
|
+
export type HTMLRegistrationOrigin =
|
|
111
|
+
| "initial-scan"
|
|
112
|
+
| "observer"
|
|
113
|
+
| "observer-initial"
|
|
114
|
+
| "observer-root-change"
|
|
115
|
+
| "root-change-scan"
|
|
116
|
+
| "define-element"
|
|
117
|
+
| "register-element"
|
|
118
|
+
| "append"
|
|
119
|
+
| "add-module"
|
|
120
|
+
| "update-element"
|
|
121
|
+
| "remove-element"
|
|
122
|
+
| string;
|
|
123
|
+
|
|
124
|
+
export type HTMLRuntimeEvent =
|
|
125
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-discovered"; element: HTMLModuleScriptElement; moduleId?: string; origin: HTMLRegistrationOrigin; accepted: boolean }
|
|
126
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-claimed"; element: HTMLModuleScriptElement; moduleId: string; format: ModuleFormat; entry: boolean; sourceUrl?: string; origin: HTMLRegistrationOrigin }
|
|
127
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-loading"; element: HTMLModuleScriptElement; moduleId: string; sourceUrl: string; origin: HTMLRegistrationOrigin }
|
|
128
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-loaded"; element: HTMLModuleScriptElement; moduleId: string; sourceUrl: string; origin: HTMLRegistrationOrigin; bytes: number; durationMs: number }
|
|
129
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-registered"; element: HTMLModuleScriptElement; moduleId: string; definition: HTMLModuleDefinition; origin: HTMLRegistrationOrigin }
|
|
130
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-updating"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin }
|
|
131
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-updated"; element: HTMLModuleScriptElement; moduleId: string; definition: HTMLModuleDefinition; origin: HTMLRegistrationOrigin }
|
|
132
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-removing"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin; removeModule: boolean; removeFromDOM: boolean }
|
|
133
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-released"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin }
|
|
134
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-removed"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin; moduleRemoved: boolean; elementRemoved: boolean }
|
|
135
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "observer-connected"; root: HTMLModuleRoot; subtree: boolean; registerExisting: boolean; reason: string }
|
|
136
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "observer-disconnected"; root?: HTMLModuleRoot; reason: string }
|
|
137
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "observer-batch"; root: HTMLModuleRoot; origin: HTMLRegistrationOrigin; discovered: number; matched: number; claimed: number; ignored: number; modules: string[] }
|
|
138
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "root-changing"; previousRoot?: HTMLModuleRoot; root: HTMLModuleRoot; connected: boolean; registerExisting: boolean }
|
|
139
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "root-changed"; previousRoot?: HTMLModuleRoot; root: HTMLModuleRoot; connected: boolean; registerExisting: boolean }
|
|
140
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "append-target-changed"; previousTarget?: HTMLAppendTarget; target: HTMLAppendTarget }
|
|
141
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "entry-executing"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin }
|
|
142
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "entry-executed"; element: HTMLModuleScriptElement; moduleId: string; namespace: ModuleNamespaceLike; origin: HTMLRegistrationOrigin }
|
|
143
|
+
| { htmlRuntimeId: string; scope?: string; timestamp: number; type: "html-error"; operation: "discover" | "claim" | "fetch" | "register" | "update" | "remove" | "entry" | "root-change" | "observer" | string; error: unknown; element?: HTMLModuleScriptElement; moduleId?: string; origin?: HTMLRegistrationOrigin };
|
|
144
|
+
|
|
145
|
+
export type HTMLRuntimeEventType = HTMLRuntimeEvent["type"];
|
|
146
|
+
export type HTMLRuntimeEventOfType<T extends HTMLRuntimeEventType> =
|
|
147
|
+
Extract<HTMLRuntimeEvent, { type: T }>;
|
|
148
|
+
|
|
93
149
|
export interface ObserveHTMLOptions extends RegisterHTMLOptions {
|
|
94
150
|
/** Register scripts already present when observation starts. Default: true. */
|
|
95
151
|
registerExisting?: boolean;
|
|
@@ -141,6 +197,16 @@ export interface HTMLRuntimeController {
|
|
|
141
197
|
/** Result of the observer's initial scan. */
|
|
142
198
|
readonly initial: RegisterHTMLResult;
|
|
143
199
|
|
|
200
|
+
subscribe(listener: (event: HTMLRuntimeEvent) => void): () => boolean;
|
|
201
|
+
subscribe<T extends HTMLRuntimeEventType>(
|
|
202
|
+
type: T,
|
|
203
|
+
listener: (event: HTMLRuntimeEventOfType<T>) => void,
|
|
204
|
+
): () => boolean;
|
|
205
|
+
subscribe<const T extends readonly HTMLRuntimeEventType[]>(
|
|
206
|
+
types: T,
|
|
207
|
+
listener: (event: HTMLRuntimeEventOfType<T[number]>) => void,
|
|
208
|
+
): () => boolean;
|
|
209
|
+
|
|
144
210
|
/** One-shot scan of matching scripts under the configured/root override. */
|
|
145
211
|
register(options?: RegisterHTMLOptions): Promise<RegisterHTMLResult>;
|
|
146
212
|
|
|
@@ -181,6 +247,18 @@ export interface HTMLRuntimeController {
|
|
|
181
247
|
options?: RegisterHTMLOptions,
|
|
182
248
|
): Promise<HTMLModuleDefinition>;
|
|
183
249
|
|
|
250
|
+
/** Re-read and replace source for an element already owned by this controller. */
|
|
251
|
+
updateElement(
|
|
252
|
+
element: HTMLModuleScriptElement,
|
|
253
|
+
options?: RegisterHTMLOptions,
|
|
254
|
+
): Promise<HTMLModuleDefinition>;
|
|
255
|
+
|
|
256
|
+
/** Release an owned element and optionally remove its module and DOM node. */
|
|
257
|
+
removeElement(
|
|
258
|
+
element: HTMLModuleScriptElement,
|
|
259
|
+
options?: RemoveHTMLElementOptions,
|
|
260
|
+
): Promise<RemoveHTMLElementResult>;
|
|
261
|
+
|
|
184
262
|
/** Claim first, append a user-created element, then register it directly. */
|
|
185
263
|
append(
|
|
186
264
|
element: HTMLModuleScriptElement,
|
package/index.d.ts
CHANGED
|
@@ -76,8 +76,68 @@ export interface CompileResult {
|
|
|
76
76
|
diagnostics: unknown[];
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
export interface SourceModuleDefinition extends DefineOptions {
|
|
80
|
+
id: string;
|
|
81
|
+
source: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface RemoveModuleOptions {
|
|
85
|
+
/** Invalidate transitive dependents before removal. Default: true. */
|
|
86
|
+
invalidateDependents?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ClearRuntimeOptions {
|
|
90
|
+
/** Keep host namespace modules registered with defineModule(). Default: true. */
|
|
91
|
+
preserveHostModules?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type ModuleResolutionKind =
|
|
95
|
+
| "custom"
|
|
96
|
+
| "registered"
|
|
97
|
+
| "url"
|
|
98
|
+
| "absolute"
|
|
99
|
+
| "relative"
|
|
100
|
+
| "native";
|
|
101
|
+
|
|
102
|
+
export type ModuleErrorPhase =
|
|
103
|
+
| "resolve"
|
|
104
|
+
| "analyze"
|
|
105
|
+
| "compile"
|
|
106
|
+
| "link"
|
|
107
|
+
| "url-create"
|
|
108
|
+
| "evaluate"
|
|
109
|
+
| "host-bridge";
|
|
110
|
+
|
|
111
|
+
export interface LinkedDependency {
|
|
112
|
+
specifier: string;
|
|
113
|
+
resolvedId: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type RuntimeLifecycleEvent =
|
|
117
|
+
| { runtimeId: string; timestamp: number; type: "module-defined"; id: string; kind: ModuleInfo["kind"]; version: number; operation: "define" | "defineMany" | "update" | "defineModule" | "defineUrl"; replaced: boolean; module: ModuleInfo }
|
|
118
|
+
| { runtimeId: string; timestamp: number; type: "module-updated"; id: string; kind: ModuleInfo["kind"]; previousVersion: number; version: number; format?: ModuleFormat; module: ModuleInfo }
|
|
119
|
+
| { runtimeId: string; timestamp: number; type: "modules-defined"; ids: string[]; modules: ModuleInfo[] }
|
|
120
|
+
| { runtimeId: string; timestamp: number; type: "module-resolving"; specifier: string; importer: string | null }
|
|
121
|
+
| { runtimeId: string; timestamp: number; type: "module-resolved"; specifier: string; importer: string | null; resolvedId: string; resolutionKind: ModuleResolutionKind }
|
|
122
|
+
| { runtimeId: string; timestamp: number; type: "module-linking"; id: string; kind: ModuleInfo["kind"]; version: number }
|
|
123
|
+
| { runtimeId: string; timestamp: number; type: "module-linked"; id: string; kind: ModuleInfo["kind"]; version: number; dependencies: LinkedDependency[]; module: ModuleInfo }
|
|
124
|
+
| { runtimeId: string; timestamp: number; type: "module-invalidated"; id: string; kind: ModuleInfo["kind"]; version: number; reason: string }
|
|
125
|
+
| { runtimeId: string; timestamp: number; type: "module-compiled"; id: string; kind: ModuleInfo["kind"]; version: number; module: ModuleInfo }
|
|
126
|
+
| { runtimeId: string; timestamp: number; type: "module-evaluating"; id: string; kind: ModuleInfo["kind"]; version: number }
|
|
127
|
+
| { runtimeId: string; timestamp: number; type: "module-evaluated"; id: string; kind: ModuleInfo["kind"]; version: number; module: ModuleInfo }
|
|
128
|
+
| { runtimeId: string; timestamp: number; type: "module-removed"; id: string; kind: ModuleInfo["kind"]; version: number; invalidateDependents: boolean }
|
|
129
|
+
| { runtimeId: string; timestamp: number; type: "module-error"; id?: string; kind?: ModuleInfo["kind"]; version?: number; phase: ModuleErrorPhase; error: unknown }
|
|
130
|
+
| { runtimeId: string; timestamp: number; type: "runtime-cleared"; ids: string[]; preserveHostModules: boolean }
|
|
131
|
+
| { runtimeId: string; timestamp: number; type: "runtime-disposed" };
|
|
132
|
+
|
|
133
|
+
export type RuntimeLifecycleEventType = RuntimeLifecycleEvent["type"];
|
|
134
|
+
export type RuntimeLifecycleEventOfType<T extends RuntimeLifecycleEventType> =
|
|
135
|
+
Extract<RuntimeLifecycleEvent, { type: T }>;
|
|
136
|
+
|
|
137
|
+
|
|
79
138
|
export interface SolidTagRuntime {
|
|
80
139
|
define(id: string, source: string, options?: DefineOptions): string;
|
|
140
|
+
defineMany(definitions: SourceModuleDefinition[]): string[];
|
|
81
141
|
update(id: string, source: string, options?: DefineOptions): string;
|
|
82
142
|
defineModule(id: string, namespace: ModuleNamespaceLike | Function): string;
|
|
83
143
|
defineUrl(id: string, url: string): string;
|
|
@@ -87,6 +147,17 @@ export interface SolidTagRuntime {
|
|
|
87
147
|
toComponent<T extends Function = Function>(source: string, options?: ToComponentOptions): Promise<T>;
|
|
88
148
|
has(id: string): boolean;
|
|
89
149
|
invalidate(id: string, options?: { dependents?: boolean }): string[];
|
|
150
|
+
remove(id: string, options?: RemoveModuleOptions): boolean;
|
|
151
|
+
clear(options?: ClearRuntimeOptions): string[];
|
|
152
|
+
subscribe(listener: (event: RuntimeLifecycleEvent) => void): () => boolean;
|
|
153
|
+
subscribe<T extends RuntimeLifecycleEventType>(
|
|
154
|
+
type: T,
|
|
155
|
+
listener: (event: RuntimeLifecycleEventOfType<T>) => void,
|
|
156
|
+
): () => boolean;
|
|
157
|
+
subscribe<const T extends readonly RuntimeLifecycleEventType[]>(
|
|
158
|
+
types: T,
|
|
159
|
+
listener: (event: RuntimeLifecycleEventOfType<T[number]>) => void,
|
|
160
|
+
): () => boolean;
|
|
90
161
|
modules(): ModuleInfo[];
|
|
91
162
|
dependencies(id: string): string[];
|
|
92
163
|
dependents(id: string): string[];
|