solid-tag-runtime 0.0.8 → 0.0.11
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 +92 -1
- package/README.md +187 -5
- package/html.d.ts +120 -65
- package/package.json +1 -1
- package/src/html.js +872 -65
- package/src/render.js +205 -0
- package/src/runtime.js +8 -0
package/src/render.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { SolidTagRuntimeError } from "./errors.js";
|
|
2
|
+
|
|
3
|
+
export class HTMLRenderError extends SolidTagRuntimeError {
|
|
4
|
+
constructor(message, options = {}) {
|
|
5
|
+
super(message, options);
|
|
6
|
+
this.name = "HTMLRenderError";
|
|
7
|
+
this.moduleId = options.moduleId;
|
|
8
|
+
this.component = options.component;
|
|
9
|
+
this.element = options.element;
|
|
10
|
+
this.cause = options.cause ?? this.cause;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function resolveRenderComponent(namespace, componentName, context = {}) {
|
|
15
|
+
const exportName = componentName || "default";
|
|
16
|
+
if (!namespace || !(exportName in namespace)) {
|
|
17
|
+
const suffix = componentName
|
|
18
|
+
? `Module does not export component ${JSON.stringify(componentName)}.`
|
|
19
|
+
: `Render declaration expected default export, but the module does not export \"default\". Use component=\"Name\" to render a named export.`;
|
|
20
|
+
throw new HTMLRenderError(suffix, {
|
|
21
|
+
moduleId: context.moduleId,
|
|
22
|
+
component: componentName,
|
|
23
|
+
element: context.element,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const Component = namespace[exportName];
|
|
28
|
+
if (typeof Component !== "function") {
|
|
29
|
+
throw new HTMLRenderError(
|
|
30
|
+
`Export ${JSON.stringify(exportName)} from ${JSON.stringify(context.moduleId ?? "module")} is not callable and cannot be rendered as a Solid component.`,
|
|
31
|
+
{
|
|
32
|
+
moduleId: context.moduleId,
|
|
33
|
+
component: componentName,
|
|
34
|
+
element: context.element,
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { Component, exportName };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createDefaultHTMLRenderAdapter(runtime) {
|
|
43
|
+
let modulesPromise;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
async mountContainer({ target, Component, props }) {
|
|
47
|
+
const { Solid, Web } = await getSolidModules();
|
|
48
|
+
if (typeof Web.render !== "function") {
|
|
49
|
+
throw new HTMLRenderError("@solidjs/web does not provide render().");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const reactive = createReactiveProps(Solid, props);
|
|
53
|
+
const createComponent = getCreateComponent(Solid, Web);
|
|
54
|
+
const dispose = Web.render(
|
|
55
|
+
() => createComponent(Component, reactive.props),
|
|
56
|
+
target,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
kind: "container",
|
|
61
|
+
target,
|
|
62
|
+
updateProps: reactive.update,
|
|
63
|
+
dispose: once(() => {
|
|
64
|
+
if (typeof dispose === "function") dispose();
|
|
65
|
+
}),
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async mountRange({ parent, before, startMarker, endMarker, Component, props }) {
|
|
70
|
+
const { Solid, Web } = await getSolidModules();
|
|
71
|
+
if (typeof Solid.createRoot !== "function") {
|
|
72
|
+
throw new HTMLRenderError("solid-js does not provide createRoot().");
|
|
73
|
+
}
|
|
74
|
+
if (typeof Web.insert !== "function") {
|
|
75
|
+
throw new HTMLRenderError("@solidjs/web does not provide insert() required for in-place rendering.");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const reactive = createReactiveProps(Solid, props);
|
|
79
|
+
const createComponent = getCreateComponent(Solid, Web);
|
|
80
|
+
const disposeRoot = Solid.createRoot(dispose => {
|
|
81
|
+
Web.insert(
|
|
82
|
+
parent,
|
|
83
|
+
() => createComponent(Component, reactive.props),
|
|
84
|
+
before,
|
|
85
|
+
);
|
|
86
|
+
return dispose;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
kind: "range",
|
|
91
|
+
parent,
|
|
92
|
+
startMarker,
|
|
93
|
+
endMarker,
|
|
94
|
+
updateProps: reactive.update,
|
|
95
|
+
dispose: once(() => {
|
|
96
|
+
if (typeof disposeRoot === "function") disposeRoot();
|
|
97
|
+
clearBetween(startMarker, endMarker);
|
|
98
|
+
}),
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function getSolidModules() {
|
|
104
|
+
if (!modulesPromise) {
|
|
105
|
+
modulesPromise = Promise.all([
|
|
106
|
+
runtime.import("solid-js"),
|
|
107
|
+
importSolidWeb(runtime),
|
|
108
|
+
]).then(([Solid, Web]) => ({ Solid, Web }));
|
|
109
|
+
}
|
|
110
|
+
return modulesPromise;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function importSolidWeb(runtime) {
|
|
115
|
+
try {
|
|
116
|
+
return await runtime.import("@solidjs/web");
|
|
117
|
+
} catch (primaryError) {
|
|
118
|
+
try {
|
|
119
|
+
return await runtime.import("solid-js/web");
|
|
120
|
+
} catch {
|
|
121
|
+
throw primaryError;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getCreateComponent(Solid, Web) {
|
|
127
|
+
const createComponent = Solid?.createComponent ?? Web?.createComponent;
|
|
128
|
+
if (typeof createComponent === "function") return createComponent;
|
|
129
|
+
return (Component, props) => Component(props);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function createReactiveProps(Solid, initialProps = {}) {
|
|
133
|
+
if (typeof Solid?.createSignal !== "function") {
|
|
134
|
+
let current = initialProps;
|
|
135
|
+
return {
|
|
136
|
+
props: new Proxy(Object.create(null), {
|
|
137
|
+
get(_target, key) {
|
|
138
|
+
return current[key];
|
|
139
|
+
},
|
|
140
|
+
has(_target, key) {
|
|
141
|
+
return key in current;
|
|
142
|
+
},
|
|
143
|
+
ownKeys() {
|
|
144
|
+
return Reflect.ownKeys(current);
|
|
145
|
+
},
|
|
146
|
+
getOwnPropertyDescriptor(_target, key) {
|
|
147
|
+
if (!(key in current)) return undefined;
|
|
148
|
+
return { configurable: true, enumerable: true, value: current[key] };
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
151
|
+
update(next) {
|
|
152
|
+
current = next ?? {};
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const [revision, setRevision] = Solid.createSignal(0);
|
|
158
|
+
let current = initialProps ?? {};
|
|
159
|
+
const props = new Proxy(Object.create(null), {
|
|
160
|
+
get(_target, key) {
|
|
161
|
+
revision();
|
|
162
|
+
return current[key];
|
|
163
|
+
},
|
|
164
|
+
has(_target, key) {
|
|
165
|
+
revision();
|
|
166
|
+
return key in current;
|
|
167
|
+
},
|
|
168
|
+
ownKeys() {
|
|
169
|
+
revision();
|
|
170
|
+
return Reflect.ownKeys(current);
|
|
171
|
+
},
|
|
172
|
+
getOwnPropertyDescriptor(_target, key) {
|
|
173
|
+
revision();
|
|
174
|
+
if (!(key in current)) return undefined;
|
|
175
|
+
return { configurable: true, enumerable: true, value: current[key] };
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
props,
|
|
181
|
+
update(next) {
|
|
182
|
+
current = next ?? {};
|
|
183
|
+
setRevision(value => value + 1);
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function clearBetween(start, end) {
|
|
189
|
+
if (!start || !end) return;
|
|
190
|
+
let node = start.nextSibling;
|
|
191
|
+
while (node && node !== end) {
|
|
192
|
+
const next = node.nextSibling;
|
|
193
|
+
node.parentNode?.removeChild?.(node);
|
|
194
|
+
node = next;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function once(fn) {
|
|
199
|
+
let called = false;
|
|
200
|
+
return () => {
|
|
201
|
+
if (called) return;
|
|
202
|
+
called = true;
|
|
203
|
+
fn?.();
|
|
204
|
+
};
|
|
205
|
+
}
|
package/src/runtime.js
CHANGED
|
@@ -245,6 +245,14 @@ export function createRuntime(options = {}) {
|
|
|
245
245
|
const record = records.get(resolved);
|
|
246
246
|
|
|
247
247
|
if (!record) {
|
|
248
|
+
// Top-level virtual path imports follow the same rule as static imports:
|
|
249
|
+
// unresolved relative/absolute virtual IDs are runtime resolution errors,
|
|
250
|
+
// not native browser fetches. Native fallback is reserved for absolute
|
|
251
|
+
// URLs and unresolved bare specifiers when allowNativeImports is enabled.
|
|
252
|
+
const raw = String(id);
|
|
253
|
+
if (isRelativeSpecifier(raw) || raw.startsWith("/")) {
|
|
254
|
+
throw new ModuleResolutionError(id, null);
|
|
255
|
+
}
|
|
248
256
|
if (!allowNativeImports && !isAbsoluteUrl(resolved)) {
|
|
249
257
|
throw new ModuleResolutionError(id, null);
|
|
250
258
|
}
|