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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Solid runtime setup and provider resolution
|
|
2
|
+
|
|
3
|
+
`solid-tag-runtime/solid` is an **optional additive integration layer**. The core `createRuntime()` API remains synchronous and provider-agnostic.
|
|
4
|
+
|
|
5
|
+
## Standard Solid family
|
|
6
|
+
|
|
7
|
+
The helper treats these packages as one coordinated runtime family:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
solid-js
|
|
11
|
+
@solidjs/web
|
|
12
|
+
@solidjs/html
|
|
13
|
+
@solidjs/signals
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The family is resolved coherently rather than as four unrelated imports.
|
|
17
|
+
|
|
18
|
+
## High-level setup
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createSolidRuntime } from "solid-tag-runtime/solid";
|
|
22
|
+
|
|
23
|
+
const runtime = await createSolidRuntime({
|
|
24
|
+
modules: {
|
|
25
|
+
"@app/state": state,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`createSolidRuntime()`:
|
|
31
|
+
|
|
32
|
+
1. resolves/imports the standard Solid family;
|
|
33
|
+
2. merges those namespaces with `modules`;
|
|
34
|
+
3. gives caller-supplied modules precedence;
|
|
35
|
+
4. calls the normal runtime engine;
|
|
36
|
+
5. installs a lazy Solid integration capability used by wrapperless DOM rendering.
|
|
37
|
+
|
|
38
|
+
The returned value is the normal `SolidTagRuntime` API.
|
|
39
|
+
|
|
40
|
+
## Low-level setup
|
|
41
|
+
|
|
42
|
+
Nothing changes for applications that already own their exact Solid namespaces:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import * as Solid from "solid-js";
|
|
46
|
+
import * as SolidWeb from "@solidjs/web";
|
|
47
|
+
import html from "@solidjs/html";
|
|
48
|
+
import { createRuntime } from "solid-tag-runtime";
|
|
49
|
+
|
|
50
|
+
const runtime = createRuntime({
|
|
51
|
+
modules: {
|
|
52
|
+
"solid-js": Solid,
|
|
53
|
+
"@solidjs/web": SolidWeb,
|
|
54
|
+
"@solidjs/html": { default: html },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This path performs no provider detection and installs no implicit DOM delegation behavior.
|
|
60
|
+
|
|
61
|
+
## Composable loader
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { loadSolidModules } from "solid-tag-runtime/solid";
|
|
65
|
+
|
|
66
|
+
const solidModules = await loadSolidModules();
|
|
67
|
+
|
|
68
|
+
const runtime = createRuntime({
|
|
69
|
+
modules: {
|
|
70
|
+
...solidModules,
|
|
71
|
+
"@app/state": state,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The returned object is directly compatible with the existing `modules` option.
|
|
77
|
+
|
|
78
|
+
## Resolution precedence
|
|
79
|
+
|
|
80
|
+
For the standard family the loader uses this precedence:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
1. already supplied namespace
|
|
84
|
+
2. explicit specifier override
|
|
85
|
+
3. existing browser import-map/effective host resolution
|
|
86
|
+
4. provider inferred from an existing Solid-family mapping
|
|
87
|
+
5. provider inferred weakly from other page mappings
|
|
88
|
+
6. configured fallback provider
|
|
89
|
+
7. actionable failure
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Existing mappings are authoritative. The helper never silently replaces them.
|
|
93
|
+
|
|
94
|
+
## Source modes
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
await loadSolidModules({ source: "auto" });
|
|
98
|
+
await loadSolidModules({ source: "host" });
|
|
99
|
+
await loadSolidModules({ source: "esm.sh" });
|
|
100
|
+
await loadSolidModules({ source: "jsdelivr" });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `auto`
|
|
104
|
+
|
|
105
|
+
Use host/browser resolution first. Fill missing packages only when a coherent provider path can be determined.
|
|
106
|
+
|
|
107
|
+
If only part of the Solid family resolves from an opaque host/bundler identity and the loader cannot identify the provider/version behind that runtime, `auto` fails instead of filling the rest from a CDN. This prevents a hidden second Solid runtime. Supply the missing namespaces explicitly or provide a coherent mapping/provider configuration.
|
|
108
|
+
|
|
109
|
+
### `host`
|
|
110
|
+
|
|
111
|
+
Use only already-resolvable host/bundler/browser modules. No CDN fallback is allowed.
|
|
112
|
+
|
|
113
|
+
### `esm.sh`
|
|
114
|
+
|
|
115
|
+
Use explicit/existing mappings where present and generate missing package URLs through the esm.sh adapter.
|
|
116
|
+
|
|
117
|
+
### `jsdelivr`
|
|
118
|
+
|
|
119
|
+
Use explicit/existing mappings where present and generate missing package URLs through the jsDelivr adapter. Cases that cannot safely preserve a pre-existing Solid identity fail rather than mixing providers silently.
|
|
120
|
+
|
|
121
|
+
## Tested fallback version
|
|
122
|
+
|
|
123
|
+
When provider fallback is needed and there is no versioned Solid anchor, this release uses a documented tested family version rather than CDN `latest`:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { TESTED_SOLID_VERSION } from "solid-tag-runtime/solid";
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
For `0.0.14`, the tested fallback line is Solid `2.0.0-rc.13`.
|
|
130
|
+
|
|
131
|
+
If an existing Solid mapping is explicitly versioned, that version becomes the family anchor for generated siblings. An explicitly unversioned existing mapping remains unversioned; the loader does not pretend it is pinned.
|
|
132
|
+
|
|
133
|
+
## esm.sh identity handling
|
|
134
|
+
|
|
135
|
+
When an authoritative page mapping exists, generated esm.sh siblings externalize that dependency so the browser resolves it through the existing mapping.
|
|
136
|
+
|
|
137
|
+
When there is no existing core anchor, generated sibling package URLs pin coordinated Solid dependencies through provider-specific `deps` parameters. Solid 2's family direction is modeled with `@solidjs/signals` as the reactive core consumed by `solid-js`, then `@solidjs/web`, then `@solidjs/html`; the generated URLs pin/externalize that family closure rather than treating `@solidjs/signals` as depending back on `solid-js`.
|
|
138
|
+
|
|
139
|
+
Provider query details remain isolated in the provider adapter; the core runtime does not know they exist.
|
|
140
|
+
|
|
141
|
+
## Import maps are read-only
|
|
142
|
+
|
|
143
|
+
The helpers may inspect import-map script contents and use the browser's effective resolver, but they never insert or rewrite import maps.
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
loadSolidModules()
|
|
147
|
+
createSolidRuntime()
|
|
148
|
+
↓
|
|
149
|
+
read/detect only
|
|
150
|
+
↓
|
|
151
|
+
no global import-map mutation
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
If an application wants to install an import map, it should do so before module evaluation using application-owned tooling.
|
|
155
|
+
|
|
156
|
+
## User overrides
|
|
157
|
+
|
|
158
|
+
Caller modules win:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const runtime = await createSolidRuntime({
|
|
162
|
+
modules: {
|
|
163
|
+
"solid-js": CustomSolid,
|
|
164
|
+
"@app/state": state,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This is the escape hatch for custom builds, embedded runtimes, tests, and applications that already control dependency identity.
|
|
170
|
+
|
|
171
|
+
## Wrapperless delegation capability
|
|
172
|
+
|
|
173
|
+
`createSolidRuntime()` installs one private capability used by the HTML adapter:
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
ensureDelegation(document)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Normal users do not call it. It is invoked lazily only when wrapperless `<script render>` mounting requires Solid's delegated-event infrastructure.
|
|
180
|
+
|
|
181
|
+
See [Wrapperless delegation](./wrapperless-delegation.md).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Wrapperless Solid 2 event delegation
|
|
2
|
+
|
|
3
|
+
Bare declarative rendering uses marker ranges instead of a container element:
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<script type="solid-jsx" render>
|
|
7
|
+
export default function App() {
|
|
8
|
+
return <button onClick={() => console.log("clicked")}>Click</button>;
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Each declaration remains an **independent Solid reactive root**.
|
|
14
|
+
|
|
15
|
+
## Ownership model
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
Document
|
|
19
|
+
│
|
|
20
|
+
│ lazily, once per Document × @solidjs/web namespace:
|
|
21
|
+
├── delegation host
|
|
22
|
+
│
|
|
23
|
+
├── wrapperless declaration A
|
|
24
|
+
│ └── createRoot A + insert(range A)
|
|
25
|
+
│
|
|
26
|
+
└── wrapperless declaration B
|
|
27
|
+
└── createRoot B + insert(range B)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The delegation host exists only for DOM/event infrastructure. It is not the reactive parent of the wrapperless roots.
|
|
31
|
+
|
|
32
|
+
Disposing A does not dispose B and does not tear down the document host.
|
|
33
|
+
|
|
34
|
+
## When it is initialized
|
|
35
|
+
|
|
36
|
+
The high-level factory:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
await createSolidRuntime(...)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
installs the capability but performs no DOM work immediately.
|
|
43
|
+
|
|
44
|
+
The first wrapperless range mount does:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
ensure document delegation host
|
|
48
|
+
ensure delegated event types
|
|
49
|
+
create independent root
|
|
50
|
+
insert component into marker range
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
If the application never performs wrapperless rendering, no document delegation host is created by this feature.
|
|
54
|
+
|
|
55
|
+
## Selector rendering is unchanged
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<script type="solid-jsx" render="#app">
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
continues to use normal Solid container rendering.
|
|
62
|
+
|
|
63
|
+
## `<solid-render>` is unchanged
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<solid-render module="/Counter.jsx"></solid-render>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
continues to use its own persistent light-DOM container. This feature does not change its ownership model.
|
|
70
|
+
|
|
71
|
+
## Low-level `createRuntime()`
|
|
72
|
+
|
|
73
|
+
The core runtime deliberately does not install Solid-specific DOM behavior.
|
|
74
|
+
|
|
75
|
+
If wrapperless delegated-event setup should be automatic, use `createSolidRuntime()` from `solid-tag-runtime/solid`.
|
|
76
|
+
|
|
77
|
+
Advanced low-level users remain free to supply their own render adapter or host DOM setup.
|
|
78
|
+
|
|
79
|
+
## Delegated events
|
|
80
|
+
|
|
81
|
+
The integration prefers the renderer's exported delegated-event set when available and otherwise uses the standard Solid/dom-expressions delegated UI event family. Registration is idempotent per renderer/document pair.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createRuntime } from "solid-tag-runtime";
|
|
2
|
+
import * as Solid from "solid-js";
|
|
3
|
+
import * as SolidWeb from "@solidjs/web";
|
|
4
|
+
import html from "@solidjs/html";
|
|
5
|
+
|
|
6
|
+
const runtime = createRuntime({
|
|
7
|
+
modules: {
|
|
8
|
+
"solid-js": Solid,
|
|
9
|
+
"@solidjs/web": SolidWeb,
|
|
10
|
+
"@solidjs/html": { default: html },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
runtime.define(
|
|
15
|
+
"/Button.jsx",
|
|
16
|
+
`
|
|
17
|
+
export function Button(props) {
|
|
18
|
+
return <button onClick={props.onClick}>{props.children}</button>;
|
|
19
|
+
}
|
|
20
|
+
`,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
runtime.define(
|
|
24
|
+
"/Counter.jsx",
|
|
25
|
+
`
|
|
26
|
+
import { createSignal } from "solid-js";
|
|
27
|
+
import { Button } from "./Button.jsx";
|
|
28
|
+
|
|
29
|
+
export function Counter() {
|
|
30
|
+
const [count, setCount] = createSignal(0);
|
|
31
|
+
return (
|
|
32
|
+
<Button onClick={() => setCount(value => value + 1)}>
|
|
33
|
+
Count: {count()}
|
|
34
|
+
</Button>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const { Counter } = await runtime.import("/Counter.jsx");
|
|
41
|
+
console.log(Counter);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRuntime,
|
|
3
|
+
createIndexedDBCompileCache,
|
|
4
|
+
} from "solid-tag-runtime";
|
|
5
|
+
|
|
6
|
+
const store = createIndexedDBCompileCache({
|
|
7
|
+
database: "my-app-runtime",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function createAppRuntime() {
|
|
11
|
+
return createRuntime({
|
|
12
|
+
compileCache: {
|
|
13
|
+
store,
|
|
14
|
+
namespace: "main",
|
|
15
|
+
version: "1",
|
|
16
|
+
maxEntries: 500,
|
|
17
|
+
maxBytes: 50 * 1024 * 1024,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// First runtime: compile miss + persistent write.
|
|
23
|
+
const first = createAppRuntime();
|
|
24
|
+
first.define("/config.js", `export const value = 42;`, { format: "js" });
|
|
25
|
+
await first.import("/config.js");
|
|
26
|
+
console.log("first", first.getModuleInfo("/config.js")?.compileCache);
|
|
27
|
+
first.dispose();
|
|
28
|
+
|
|
29
|
+
// New runtime, same source and cache namespace/version: compile hit.
|
|
30
|
+
// Linking, module URL creation, and native evaluation are still fresh.
|
|
31
|
+
const second = createAppRuntime();
|
|
32
|
+
second.define("/config.js", `export const value = 42;`, { format: "js" });
|
|
33
|
+
await second.import("/config.js");
|
|
34
|
+
console.log("second", second.getModuleInfo("/config.js")?.compileCache);
|
|
35
|
+
second.dispose();
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { render } from "@solidjs/web";
|
|
2
|
+
import * as Solid from "solid-js";
|
|
3
|
+
import * as SolidWeb from "@solidjs/web";
|
|
4
|
+
import html from "@solidjs/html";
|
|
5
|
+
import { createRuntime } from "solid-tag-runtime";
|
|
6
|
+
import { createHTMLRuntime } from "solid-tag-runtime/html";
|
|
7
|
+
|
|
8
|
+
// A tiny type helper for module exports returned by solid-tag-runtime.
|
|
9
|
+
type Component<P = Record<string, unknown>> = (props: P) => any;
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
const root = document.getElementById("root");
|
|
13
|
+
if (!root) throw new Error("Missing #root element");
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// 1. Host application state
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// These signals are created by the Solid runtime already bundled with the app.
|
|
19
|
+
// Runtime-created modules receive the exact same signal/accessor references.
|
|
20
|
+
|
|
21
|
+
const [hostCount, setHostCount] = Solid.createSignal(100);
|
|
22
|
+
const [hostMessage, setHostMessage] = Solid.createSignal(
|
|
23
|
+
"Hello from the host application",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// 2. Create the runtime and reuse this application's Solid modules
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// This is important: dynamic components do NOT load another Solid runtime.
|
|
30
|
+
|
|
31
|
+
const runtime = createRuntime({
|
|
32
|
+
modules: {
|
|
33
|
+
"solid-js": Solid,
|
|
34
|
+
"@solidjs/web": SolidWeb,
|
|
35
|
+
"@solidjs/html": { default: html },
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// 3. Expose host-owned state/services as a normal runtime module
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Runtime source can now use:
|
|
43
|
+
//
|
|
44
|
+
// import { hostCount, setHostCount } from "@app/state";
|
|
45
|
+
//
|
|
46
|
+
// No serialization or global-variable rewriting is involved.
|
|
47
|
+
|
|
48
|
+
runtime.defineModule("@app/state", {
|
|
49
|
+
hostCount,
|
|
50
|
+
setHostCount,
|
|
51
|
+
hostMessage,
|
|
52
|
+
setHostMessage,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// 4. Observe runtime module scripts added to the document
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// We already own registration in this demo, so only watch scripts added from
|
|
59
|
+
// this point forward. Each observed batch is fully defined before any entry
|
|
60
|
+
// in that batch executes.
|
|
61
|
+
|
|
62
|
+
const htmlRuntime = createHTMLRuntime(runtime, {
|
|
63
|
+
scope: "main",
|
|
64
|
+
root: document,
|
|
65
|
+
appendTo: document.body,
|
|
66
|
+
acceptUnscoped: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await htmlRuntime.observe({
|
|
70
|
+
registerExisting: false,
|
|
71
|
+
onError(error) {
|
|
72
|
+
console.error("Observed runtime module failed", error);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// This script is inert to the browser because its type is solid-jsx, but the
|
|
77
|
+
// solid-tag-runtime HTML adapter discovers it through MutationObserver.
|
|
78
|
+
const observedModuleScript = document.createElement("script");
|
|
79
|
+
observedModuleScript.type = "solid-jsx";
|
|
80
|
+
observedModuleScript.setAttribute("data-solid-runtime", "main");
|
|
81
|
+
observedModuleScript.setAttribute("module", "/observed/Badge.jsx");
|
|
82
|
+
observedModuleScript.textContent = `
|
|
83
|
+
export function ObservedBadge(props) {
|
|
84
|
+
return (
|
|
85
|
+
<small
|
|
86
|
+
style={{
|
|
87
|
+
display: "inline-block",
|
|
88
|
+
padding: "4px 8px",
|
|
89
|
+
border: "1px dashed #777",
|
|
90
|
+
"border-radius": "999px"
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
Observed module: {props.children}
|
|
94
|
+
</small>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
document.body.append(observedModuleScript);
|
|
99
|
+
|
|
100
|
+
// MutationObserver is automatic; flush() is useful when the application wants
|
|
101
|
+
// to explicitly wait until discovered modules are ready before importing them.
|
|
102
|
+
await htmlRuntime.flush();
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// 5. Define a reusable runtime JSX module
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
runtime.define(
|
|
109
|
+
"/ui/Button.jsx",
|
|
110
|
+
`
|
|
111
|
+
export function Button(props) {
|
|
112
|
+
return (
|
|
113
|
+
<button
|
|
114
|
+
type="button"
|
|
115
|
+
onClick={props.onClick}
|
|
116
|
+
style={{
|
|
117
|
+
padding: "8px 12px",
|
|
118
|
+
border: "1px solid #777",
|
|
119
|
+
"border-radius": "6px",
|
|
120
|
+
cursor: "pointer"
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
{props.children}
|
|
124
|
+
</button>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
`,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// 6. Define another runtime module that imports Button + host state + Solid
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
runtime.define(
|
|
135
|
+
"/features/Counter.jsx",
|
|
136
|
+
`
|
|
137
|
+
import { createSignal } from "solid-js";
|
|
138
|
+
import { Button } from "../ui/Button.jsx";
|
|
139
|
+
import { hostCount, setHostCount } from "@app/state";
|
|
140
|
+
|
|
141
|
+
export function Counter() {
|
|
142
|
+
const [localCount, setLocalCount] = createSignal(0);
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<section
|
|
146
|
+
style={{
|
|
147
|
+
display: "grid",
|
|
148
|
+
gap: "10px",
|
|
149
|
+
padding: "16px",
|
|
150
|
+
border: "1px solid #555",
|
|
151
|
+
"border-radius": "8px"
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
<strong>Runtime Counter module</strong>
|
|
155
|
+
|
|
156
|
+
<div>Local runtime signal: {localCount()}</div>
|
|
157
|
+
<div>Host application signal: {hostCount()}</div>
|
|
158
|
+
|
|
159
|
+
<div style={{ display: "flex", gap: "8px", "flex-wrap": "wrap" }}>
|
|
160
|
+
<Button onClick={() => setLocalCount(value => value + 1)}>
|
|
161
|
+
Increment local
|
|
162
|
+
</Button>
|
|
163
|
+
|
|
164
|
+
<Button onClick={() => setHostCount(value => value + 1)}>
|
|
165
|
+
Increment host
|
|
166
|
+
</Button>
|
|
167
|
+
</div>
|
|
168
|
+
</section>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
`,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// 7. toModule(): create an anonymous runtime module and get its exports
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
const greetingModule = await runtime.toModule(`
|
|
179
|
+
export function Greeting(props) {
|
|
180
|
+
return (
|
|
181
|
+
<p style={{ margin: 0, opacity: 0.8 }}>
|
|
182
|
+
Runtime module says: Hello {props.name}!
|
|
183
|
+
</p>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
`);
|
|
187
|
+
|
|
188
|
+
// A compiled module namespace can itself be exposed as a named host module.
|
|
189
|
+
// That lets other runtime modules import it normally.
|
|
190
|
+
runtime.defineModule("@runtime/greeting", greetingModule);
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// 8. Define the runtime application module
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// It imports another runtime JSX module, an anonymous module namespace that we
|
|
196
|
+
// registered above, and host state.
|
|
197
|
+
|
|
198
|
+
runtime.define(
|
|
199
|
+
"/App.jsx",
|
|
200
|
+
`
|
|
201
|
+
import { Counter } from "./features/Counter.jsx";
|
|
202
|
+
import { Greeting } from "@runtime/greeting";
|
|
203
|
+
import { ObservedBadge } from "./observed/Badge.jsx";
|
|
204
|
+
import { hostMessage, setHostMessage } from "@app/state";
|
|
205
|
+
|
|
206
|
+
export function App(props) {
|
|
207
|
+
return (
|
|
208
|
+
<main
|
|
209
|
+
style={{
|
|
210
|
+
display: "grid",
|
|
211
|
+
gap: "16px",
|
|
212
|
+
width: "min(680px, calc(100% - 32px))",
|
|
213
|
+
margin: "40px auto",
|
|
214
|
+
"font-family": "system-ui, sans-serif"
|
|
215
|
+
}}
|
|
216
|
+
>
|
|
217
|
+
<h1 style={{ margin: 0 }}>{props.title}</h1>
|
|
218
|
+
|
|
219
|
+
<Greeting name={props.name} />
|
|
220
|
+
|
|
221
|
+
<ObservedBadge>
|
|
222
|
+
discovered through MutationObserver
|
|
223
|
+
</ObservedBadge>
|
|
224
|
+
|
|
225
|
+
<p style={{ margin: 0 }}>
|
|
226
|
+
Host message: {hostMessage()}
|
|
227
|
+
</p>
|
|
228
|
+
|
|
229
|
+
<button
|
|
230
|
+
type="button"
|
|
231
|
+
onClick={() =>
|
|
232
|
+
setHostMessage("The runtime App changed host-owned state")
|
|
233
|
+
}
|
|
234
|
+
>
|
|
235
|
+
Change host message from runtime code
|
|
236
|
+
</button>
|
|
237
|
+
|
|
238
|
+
<Counter />
|
|
239
|
+
</main>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
`,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// 9. Import the runtime module and use its exported component in host JSX
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
const appModule = await runtime.import("/App.jsx");
|
|
250
|
+
const RuntimeApp = appModule.App as Component<{
|
|
251
|
+
title: string;
|
|
252
|
+
name: string;
|
|
253
|
+
}>;
|
|
254
|
+
|
|
255
|
+
const disposeRender = render(
|
|
256
|
+
() => (
|
|
257
|
+
<RuntimeApp
|
|
258
|
+
title="solid-tag-runtime demo"
|
|
259
|
+
name="Solid"
|
|
260
|
+
/>
|
|
261
|
+
),
|
|
262
|
+
root,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// 10. Runtime module graph introspection
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
console.table(runtime.modules());
|
|
270
|
+
console.log("/App.jsx dependencies:", runtime.dependencies("/App.jsx"));
|
|
271
|
+
console.log(
|
|
272
|
+
"/features/Counter.jsx dependencies:",
|
|
273
|
+
runtime.dependencies("/features/Counter.jsx"),
|
|
274
|
+
);
|
|
275
|
+
console.log(
|
|
276
|
+
"/ui/Button.jsx dependents:",
|
|
277
|
+
runtime.dependents("/ui/Button.jsx"),
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// You can inspect the transformed module source too.
|
|
281
|
+
const compiledCounter = await runtime.compile("/features/Counter.jsx");
|
|
282
|
+
console.log("Compiled Counter module:\n", compiledCounter.code);
|
|
283
|
+
|
|
284
|
+
// Optional demo/debug handle from the browser console.
|
|
285
|
+
Object.assign(globalThis, {
|
|
286
|
+
solidTagDemo: {
|
|
287
|
+
runtime,
|
|
288
|
+
hostCount,
|
|
289
|
+
setHostCount,
|
|
290
|
+
hostMessage,
|
|
291
|
+
setHostMessage,
|
|
292
|
+
htmlRuntime,
|
|
293
|
+
dispose() {
|
|
294
|
+
htmlRuntime.disconnect();
|
|
295
|
+
observedModuleScript.remove();
|
|
296
|
+
disposeRender();
|
|
297
|
+
runtime.dispose();
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
main().catch(error => {
|
|
304
|
+
console.error("solid-tag-runtime demo failed", error);
|
|
305
|
+
|
|
306
|
+
const root = document.getElementById("root");
|
|
307
|
+
if (root) {
|
|
308
|
+
const pre = document.createElement("pre");
|
|
309
|
+
pre.style.whiteSpace = "pre-wrap";
|
|
310
|
+
pre.style.color = "crimson";
|
|
311
|
+
pre.textContent =
|
|
312
|
+
error instanceof Error
|
|
313
|
+
? error.stack ?? error.message
|
|
314
|
+
: String(error);
|
|
315
|
+
|
|
316
|
+
root.replaceChildren(pre);
|
|
317
|
+
}
|
|
318
|
+
});
|