lunet 0.0.8 → 0.0.10

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/build.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { build, file, write } from "bun";
2
+ import { parse } from "@babel/parser";
3
+ import { generate } from "@babel/generator";
4
+ import { generateDts } from "typeroll";
5
+
6
+ const render1_res = await build({
7
+ entrypoints: [`${import.meta.dir}/src/render/index.ts`],
8
+ throw: true,
9
+ });
10
+
11
+ const render1_code = await render1_res.outputs[0]!.text();
12
+
13
+ const ast = parse(render1_code, {
14
+ sourceType: "module",
15
+ });
16
+
17
+ const export_statement = ast.program.body.at(-1);
18
+ if (!export_statement || export_statement.type !== "ExportNamedDeclaration") {
19
+ throw new Error("Not found export statement on last");
20
+ }
21
+
22
+ const nameset = new Set<string>();
23
+
24
+ for (const spec of export_statement.specifiers) {
25
+ if (
26
+ spec.type !== "ExportSpecifier" ||
27
+ spec.exported.type !== "Identifier" ||
28
+ spec.exported.name !== spec.local.name
29
+ ) {
30
+ throw new Error("AST assertion failed");
31
+ }
32
+ nameset.add(spec.local.name);
33
+ }
34
+
35
+ if (
36
+ !nameset.has("createFragment") ||
37
+ !nameset.has("updateFragment") ||
38
+ !nameset.has("revokeFragment")
39
+ ) {
40
+ throw new Error("AST assertion failed");
41
+ }
42
+
43
+ ast.program.body.pop();
44
+
45
+ const render2_code = generate(ast).code;
46
+
47
+ const entrypoint = `${import.meta.dir}/src/index.ts`;
48
+ const entry_src = await file(entrypoint).text();
49
+
50
+ const cfg = {
51
+ entrypoints: [entrypoint],
52
+ //@ts-ignore
53
+ files: {
54
+ [entrypoint]: entry_src.replace("//@INJECT", render2_code),
55
+ },
56
+ outdir: `${import.meta.dir}/dist/`
57
+ } as {
58
+ entrypoints: [string],
59
+ outdir: string,
60
+ };
61
+
62
+ await build({
63
+ ...cfg,
64
+ format: "esm",
65
+ naming: "index.js",
66
+ });
67
+
68
+ await build({
69
+ ...cfg,
70
+ format: "cjs",
71
+ naming: "index.cjs",
72
+ });
73
+
74
+ const dts_result = await generateDts([
75
+ entrypoint,
76
+ ]);
77
+
78
+ if (dts_result.errors.length) {
79
+ throw dts_result.errors;
80
+ }
81
+
82
+ await write(`${import.meta.dir}/dist/index.d.ts`, dts_result.files[0]!.dts);
package/dist/index.cjs CHANGED
@@ -29,293 +29,310 @@ var __export = (target, all) => {
29
29
  // src/index.ts
30
30
  var exports_src = {};
31
31
  __export(exports_src, {
32
- setBatch: () => setBatch,
33
- render: () => render,
34
32
  h: () => h,
35
33
  fragment: () => fragment,
34
+ createRoot: () => createRoot,
36
35
  createComponent: () => createComponent
37
36
  });
38
37
  module.exports = __toCommonJS(exports_src);
39
38
 
40
39
  // src/jsx/component.ts
41
- var createComponent = (component) => (props) => [component, props];
40
+ var createComponent = (component) => (props) => [component, props, []];
42
41
  // src/jsx/fragment.ts
43
- var fragment = (...children) => [null, {}, ...children];
42
+ var fragment = (...children) => [null, {}, children];
44
43
  // src/jsx/likereact.ts
45
- var h = (type, props, ...children) => typeof type === "string" ? [type, props ?? {}, ...children] : type === fragment ? [null, props ?? {}, ...children] : type(props);
46
- // src/mount/dom/text.ts
47
- var createText = (jsx) => {
48
- const node = new Text(jsx);
49
- return [[0, jsx, node], node];
50
- };
51
- var updateText = (dom, jsx) => {
52
- if (dom[1] !== jsx)
53
- dom[2].textContent = dom[1] = jsx;
54
- };
55
- var revokeText = (dom) => dom[2].remove();
56
- var afterText = (dom, node) => {
57
- dom[2].parentNode?.insertBefore(node, dom[2].nextSibling);
58
- };
59
-
60
- // src/mount/diff.ts
61
- var isCompatibleNode = (before, after) => typeof before === "string" || typeof after === "string" ? typeof before === typeof after : before[0] === after[0] && before[1].key === after[1].key;
62
- var diff = (before_nodes, after_nodes) => {
63
- const patches = [];
64
- const max = Math.max(before_nodes.length, after_nodes.length);
65
- let index = 0;
66
- for (let i = 0;i < max; i++) {
67
- const before = before_nodes[i];
68
- const after = after_nodes[i];
69
- if (!before && after)
70
- patches.push([1, index++, after]);
71
- else if (before && !after)
72
- patches.push([2, index, before]);
73
- else if (before && after)
74
- if (isCompatibleNode(before, after))
75
- patches.push([0, index++, after]);
76
- else {
77
- patches.push([2, index, before]);
44
+ var h = (type, props, ...children) => typeof type === "string" ? [type, props ?? {}, children] : type === fragment ? [null, props ?? {}, children] : type(props);
45
+ // src/index.ts
46
+ var createRoot = (el, options) => {
47
+ var createText = (jsx2) => {
48
+ const node2 = new Text(jsx2);
49
+ return [[0, jsx2, node2], node2];
50
+ };
51
+ var updateText = (dom2, jsx2) => {
52
+ if (dom2[1] !== jsx2)
53
+ dom2[2].textContent = dom2[1] = jsx2;
54
+ };
55
+ var revokeText = (dom2) => dom2[2].remove();
56
+ var afterText = (dom2, node2) => {
57
+ dom2[2].parentNode?.insertBefore(node2, dom2[2].nextSibling);
58
+ };
59
+ var isCompatibleNode = (before, after) => typeof before === "string" || typeof after === "string" ? typeof before === typeof after : before[0] === after[0] && before[1].key === after[1].key;
60
+ var diff = (before_nodes, after_nodes) => {
61
+ const patches = [];
62
+ const max = Math.max(before_nodes.length, after_nodes.length);
63
+ let index = 0;
64
+ for (let i = 0;i < max; i++) {
65
+ const before = before_nodes[i];
66
+ const after = after_nodes[i];
67
+ if (!before && after)
78
68
  patches.push([1, index++, after]);
79
- }
69
+ else if (before && !after)
70
+ patches.push([2, index, before]);
71
+ else if (before && after)
72
+ if (isCompatibleNode(before, after))
73
+ patches.push([0, index++, after]);
74
+ else {
75
+ patches.push([2, index, before]);
76
+ patches.push([1, index++, after]);
77
+ }
78
+ }
79
+ return patches;
80
+ };
81
+ var lifecycle_events = new Set(["beforeMount", "mount", "beforeUpdate", "update", "beforeUnmount", "unmount"]);
82
+ var elementEvents = new WeakMap;
83
+ function handleEvent(ev) {
84
+ return elementEvents.get(this)?.[ev.type]?.call(this, ev);
80
85
  }
81
- return patches;
82
- };
83
-
84
- // src/mount/dom/element.ts
85
- var lifecycle_events = new Set([
86
- "beforeMount",
87
- "mount",
88
- "beforeUpdate",
89
- "update",
90
- "beforeUnmount",
91
- "unmount"
92
- ]);
93
- var elementEvents = new WeakMap;
94
- function handleEvent(ev) {
95
- return elementEvents.get(this)?.[ev.type]?.call(this, ev);
96
- }
97
- var setAttribute = (el, name, value) => {
98
- if (name.startsWith("$")) {
99
- const ev_name = name.substring(1);
100
- if (!lifecycle_events.has(ev_name)) {
101
- const events = elementEvents.get(el);
102
- if (!(ev_name in events))
103
- el.addEventListener(ev_name, handleEvent);
104
- if (typeof value === "function" || value == null)
105
- events[ev_name] = value;
106
- else {
107
- console.error("Invalid event handler: ", value);
108
- events[ev_name] = undefined;
86
+ var setAttribute = (el2, name, value) => {
87
+ if (name.startsWith("$")) {
88
+ const ev_name = name.substring(1);
89
+ if (!lifecycle_events.has(ev_name)) {
90
+ const events = elementEvents.get(el2);
91
+ if (!(ev_name in events))
92
+ el2.addEventListener(ev_name, handleEvent);
93
+ if (typeof value === "function" || value == null)
94
+ events[ev_name] = value;
95
+ else {
96
+ console.error("Invalid event handler: ", value);
97
+ events[ev_name] = undefined;
98
+ }
109
99
  }
100
+ } else
101
+ switch (typeof value) {
102
+ case "string":
103
+ el2.setAttribute(name, value);
104
+ break;
105
+ case "function":
106
+ case "object":
107
+ if (value === null)
108
+ el2.removeAttribute(name);
109
+ else
110
+ console.error(`${typeof value} values cannot mount on attributes.`);
111
+ break;
112
+ default:
113
+ if (value === undefined)
114
+ el2.removeAttribute(name);
115
+ else
116
+ el2.setAttribute(name, String(value));
117
+ break;
118
+ }
119
+ };
120
+ var createElement = (jsx2) => {
121
+ const [tag, props, children] = jsx2;
122
+ props.$beforeMount?.();
123
+ const element = document.createElement(tag);
124
+ elementEvents.set(element, {});
125
+ props.$mount?.call(element, new CustomEvent("mount", {
126
+ detail: element
127
+ }));
128
+ for (const [name, value] of Object.entries(props))
129
+ setAttribute(element, name, value);
130
+ const rendered_children = children.map(createNode);
131
+ element.append(...rendered_children.map((e) => e[1]));
132
+ return [[1, jsx2, element, rendered_children.map((e) => e[0])], element];
133
+ };
134
+ var updateElement = (dom2, jsx2) => {
135
+ const [, [, old_props, old_children], element, rendered_children] = dom2;
136
+ const [, new_props, new_children] = jsx2;
137
+ old_props.$beforeUpdate?.call(element, new CustomEvent("beforeupdate", {
138
+ detail: element
139
+ }));
140
+ const removed_prop_keys = Object.keys(old_props).filter((key) => !(key in new_props));
141
+ for (const [name, value] of Object.entries({
142
+ ...new_props,
143
+ ...Object.fromEntries(removed_prop_keys.map((key) => [key, undefined]))
144
+ }))
145
+ if (old_props[name] !== value)
146
+ setAttribute(element, name, value);
147
+ for (const [type, idx, jsx22] of diff(old_children, new_children)) {
148
+ switch (type) {
149
+ case 0:
150
+ updateNode(rendered_children[idx], jsx22);
151
+ break;
152
+ case 1:
153
+ const [rendered, node2] = createNode(jsx22);
154
+ rendered_children.splice(idx, 0, rendered);
155
+ if (idx === 0) {
156
+ if (element.firstChild) {
157
+ element.firstChild.before(node2);
158
+ } else {
159
+ element.append(node2);
160
+ }
161
+ } else {
162
+ afterNode(rendered_children[idx - 1], node2);
163
+ }
164
+ break;
165
+ case 2:
166
+ revokeNode(rendered_children.splice(idx, 1)[0]);
167
+ break;
168
+ }
169
+ }
170
+ dom2[1] = jsx2;
171
+ new_props.$update?.call(element, new CustomEvent("update", {
172
+ detail: element
173
+ }));
174
+ };
175
+ var revokeElement = (dom2) => {
176
+ const [, [, props], element, rendered_children] = dom2;
177
+ props.$beforeUnmount?.call(element, new CustomEvent("beforeunmount", {
178
+ detail: element
179
+ }));
180
+ for (const child of rendered_children)
181
+ revokeNode(child);
182
+ elementEvents.delete(element);
183
+ element.remove();
184
+ props.$unmount?.();
185
+ };
186
+ var afterElement = (dom2, node2) => {
187
+ dom2[2].after(node2);
188
+ };
189
+ var batch = options?.batch ?? ((cb) => cb());
190
+ var createComponent2 = (jsx2) => {
191
+ const [component, init_props, children] = jsx2;
192
+ let rendered_dom;
193
+ let doc_frag;
194
+ const props = component((jsx22) => {
195
+ if (rendered_dom)
196
+ updateFragment(rendered_dom, [null, {}, [jsx22]]);
197
+ else
198
+ [rendered_dom, doc_frag] = createFragment([null, {}, [jsx22]]);
199
+ }, {
200
+ ...init_props
201
+ });
202
+ if (!doc_frag) {
203
+ console.error("never rendered Initial render.");
204
+ [rendered_dom, doc_frag] = createFragment([null, {}, []]);
205
+ }
206
+ return [[3, [component, props, children], rendered_dom], doc_frag];
207
+ };
208
+ var updateComponent = (dom2, jsx2) => {
209
+ const old_props = dom2[1][1];
210
+ const new_props = jsx2[1];
211
+ batch(() => {
212
+ for (const [key, value] of Object.entries(new_props))
213
+ if (old_props[key] !== value)
214
+ old_props[key] = value;
215
+ });
216
+ };
217
+ var revokeComponent = (dom2) => revokeFragment(dom2[2]);
218
+ var afterComponent = (dom2, node2) => afterFragment(dom2[2], node2);
219
+ var createNode = (jsx2) => {
220
+ if (typeof jsx2 === "string")
221
+ return createText(jsx2);
222
+ if (Array.isArray(jsx2)) {
223
+ const [tag] = jsx2;
224
+ if (typeof tag === "string")
225
+ return createElement(jsx2);
226
+ if (typeof tag === "function")
227
+ return createComponent2(jsx2);
228
+ if (tag === null)
229
+ return createFragment(jsx2);
110
230
  }
111
- } else
112
- switch (typeof value) {
113
- case "string":
114
- el.setAttribute(name, value);
231
+ throw new Error(`Unrecognized JSX node`, {
232
+ cause: jsx2
233
+ });
234
+ };
235
+ var updateNode = (dom2, jsx2) => {
236
+ switch (dom2[0]) {
237
+ case 0:
238
+ updateText(dom2, jsx2);
239
+ break;
240
+ case 1:
241
+ updateElement(dom2, jsx2);
115
242
  break;
116
- case "function":
117
- case "object":
118
- if (value === null)
119
- el.removeAttribute(name);
120
- else
121
- console.error(`${typeof value} values cannot mount on attributes.`);
243
+ case 2:
244
+ updateFragment(dom2, jsx2);
122
245
  break;
123
- default:
124
- if (value === undefined)
125
- el.removeAttribute(name);
126
- else
127
- el.setAttribute(name, String(value));
246
+ case 3:
247
+ updateComponent(dom2, jsx2);
128
248
  break;
129
249
  }
130
- };
131
- var createElement = (jsx) => {
132
- const [tag, props, ...children] = jsx;
133
- props.$beforeMount?.();
134
- const element = document.createElement(tag);
135
- elementEvents.set(element, {});
136
- props.$mount?.call(element, new CustomEvent("mount", { detail: element }));
137
- for (const [name, value] of Object.entries(props))
138
- setAttribute(element, name, value);
139
- const rendered_children = children.map(createNode);
140
- element.append(...rendered_children.map((e) => e[1]));
141
- return [[1, jsx, element, rendered_children.map((e) => e[0])], element];
142
- };
143
- var updateElement = (dom, jsx) => {
144
- const [, [, old_props, ...old_children], element, rendered_children] = dom;
145
- const [, new_props, ...new_children] = jsx;
146
- old_props.$beforeUpdate?.call(element, new CustomEvent("beforeupdate", { detail: element }));
147
- const removed_prop_keys = Object.keys(old_props).filter((key) => !(key in new_props));
148
- for (const [name, value] of Object.entries({
149
- ...new_props,
150
- ...Object.fromEntries(removed_prop_keys.map((key) => [key, undefined]))
151
- }))
152
- if (old_props[name] !== value)
153
- setAttribute(element, name, value);
154
- for (const [type, idx, jsx2] of diff(old_children, new_children)) {
155
- switch (type) {
250
+ };
251
+ var revokeNode = (dom2) => {
252
+ switch (dom2[0]) {
156
253
  case 0:
157
- updateNode(rendered_children[idx], jsx2);
254
+ revokeText(dom2);
158
255
  break;
159
256
  case 1:
160
- const [rendered, node] = createNode(jsx2);
161
- rendered_children.splice(idx, 0, rendered);
162
- if (idx === 0) {
163
- if (element.firstChild) {
164
- element.firstChild.before(node);
165
- } else {
166
- element.append(node);
167
- }
168
- } else {
169
- afterNode(rendered_children[idx - 1], node);
170
- }
257
+ revokeElement(dom2);
171
258
  break;
172
259
  case 2:
173
- revokeNode(rendered_children.splice(idx, 1)[0]);
260
+ revokeFragment(dom2);
261
+ break;
262
+ case 3:
263
+ revokeComponent(dom2);
174
264
  break;
175
265
  }
176
- }
177
- dom[1] = jsx;
178
- new_props.$update?.call(element, new CustomEvent("update", { detail: element }));
179
- };
180
- var revokeElement = (dom) => {
181
- const [, [, props], element, rendered_children] = dom;
182
- props.$beforeUnmount?.call(element, new CustomEvent("beforeunmount", { detail: element }));
183
- for (const child of rendered_children)
184
- revokeNode(child);
185
- elementEvents.delete(element);
186
- element.remove();
187
- props.$unmount?.();
188
- };
189
- var afterElement = (dom, node) => {
190
- dom[2].after(node);
191
- };
192
-
193
- // src/mount/dom/fragment.ts
194
- var createFragment = (jsx) => {
195
- const [, , ...children] = jsx;
196
- const mark = new Comment;
197
- const el = new DocumentFragment;
198
- const rendered_children = children.map(createNode);
199
- el.append(mark, ...rendered_children.map((e) => e[1]));
200
- return [[2, jsx, mark, rendered_children.map((e) => e[0])], el];
201
- };
202
- var updateFragment = (dom, jsx) => {
203
- const [, [, , ...old_children], mark, rendered_children] = dom;
204
- const [, , ...new_children] = jsx;
205
- for (const [type, idx, jsx2] of diff(old_children, new_children)) {
206
- switch (type) {
266
+ };
267
+ var afterNode = (dom2, node2) => {
268
+ switch (dom2[0]) {
207
269
  case 0:
208
- updateNode(rendered_children[idx], jsx2);
270
+ afterText(dom2, node2);
209
271
  break;
210
272
  case 1:
211
- const [rendered, el] = createNode(jsx2);
212
- rendered_children.splice(idx, 0, rendered);
213
- if (idx === 0) {
214
- mark.after(el);
215
- } else {
216
- afterNode(rendered_children[idx - 1], el);
217
- }
273
+ afterElement(dom2, node2);
218
274
  break;
219
275
  case 2:
220
- const [removed] = rendered_children.splice(idx, 1);
221
- revokeNode(removed);
276
+ afterFragment(dom2, node2);
277
+ break;
278
+ case 3:
279
+ afterComponent(dom2, node2);
222
280
  break;
223
281
  }
224
- }
225
- dom[1] = jsx;
226
- };
227
- var revokeFragment = (dom) => {
228
- for (const child of dom[3])
229
- revokeNode(child);
230
- dom[2].remove();
231
- };
232
- var afterFragment = (dom, node) => {
233
- const last_rendered_dom = dom[3].at(-1);
234
- if (last_rendered_dom)
235
- afterNode(last_rendered_dom, node);
236
- else
237
- dom[2].after(node);
238
- };
239
-
240
- // src/batch.ts
241
- var batch = (cb) => cb();
242
- var setBatch = (fn) => {
243
- batch = fn;
244
- };
245
-
246
- // src/mount/dom/component.ts
247
- var createComponent2 = (jsx) => {
248
- const [component, init_props] = jsx;
249
- let rendered_dom;
250
- let doc_frag;
251
- const props = component((jsx2) => {
252
- if (rendered_dom)
253
- updateFragment(rendered_dom, [null, {}, jsx2]);
282
+ };
283
+ var createFragment = (jsx2) => {
284
+ const [, , children] = jsx2;
285
+ const mark = new Comment;
286
+ const el2 = new DocumentFragment;
287
+ const rendered_children = children.map(createNode);
288
+ el2.append(mark, ...rendered_children.map((e) => e[1]));
289
+ return [[2, jsx2, mark, rendered_children.map((e) => e[0])], el2];
290
+ };
291
+ var updateFragment = (dom2, jsx2) => {
292
+ const [, [, , old_children], mark, rendered_children] = dom2;
293
+ const [, , new_children] = jsx2;
294
+ for (const [type, idx, jsx22] of diff(old_children, new_children)) {
295
+ switch (type) {
296
+ case 0:
297
+ updateNode(rendered_children[idx], jsx22);
298
+ break;
299
+ case 1:
300
+ const [rendered, el2] = createNode(jsx22);
301
+ rendered_children.splice(idx, 0, rendered);
302
+ if (idx === 0) {
303
+ mark.after(el2);
304
+ } else {
305
+ afterNode(rendered_children[idx - 1], el2);
306
+ }
307
+ break;
308
+ case 2:
309
+ const [removed] = rendered_children.splice(idx, 1);
310
+ revokeNode(removed);
311
+ break;
312
+ }
313
+ }
314
+ dom2[1] = jsx2;
315
+ };
316
+ var revokeFragment = (dom2) => {
317
+ for (const child of dom2[3])
318
+ revokeNode(child);
319
+ dom2[2].remove();
320
+ };
321
+ var afterFragment = (dom2, node2) => {
322
+ const last_rendered_dom = dom2[3].at(-1);
323
+ if (last_rendered_dom)
324
+ afterNode(last_rendered_dom, node2);
254
325
  else
255
- [rendered_dom, doc_frag] = createFragment([null, {}, jsx2]);
256
- }, { ...init_props });
257
- if (!doc_frag) {
258
- console.error("never rendered Initial render.");
259
- [rendered_dom, doc_frag] = createFragment([null, {}]);
260
- }
261
- return [
262
- [
263
- 3,
264
- [
265
- component,
266
- props
267
- ],
268
- rendered_dom
269
- ],
270
- doc_frag
271
- ];
272
- };
273
- var updateComponent = (dom, jsx) => {
274
- const old_props = dom[1][1];
275
- const new_props = jsx[1];
276
- batch(() => {
277
- for (const [key, value] of Object.entries(new_props))
278
- if (old_props[key] !== value)
279
- old_props[key] = value;
280
- });
281
- };
282
- var revokeComponent = (dom) => revokeFragment(dom[2]);
283
- var afterComponent = (dom, node) => afterFragment(dom[2], node);
284
-
285
- // src/mount/dom/index.ts
286
- var funcMap = [
287
- [updateText, revokeText, afterText],
288
- [updateElement, revokeElement, afterElement],
289
- [updateFragment, revokeFragment, afterFragment],
290
- [updateComponent, revokeComponent, afterComponent]
291
- ];
292
- var createNode = (jsx) => {
293
- if (typeof jsx === "string")
294
- return createText(jsx);
295
- if (Array.isArray(jsx)) {
296
- const [tag] = jsx;
297
- if (typeof tag === "string")
298
- return createElement(jsx);
299
- if (typeof tag === "function")
300
- return createComponent2(jsx);
301
- if (tag === null)
302
- return createFragment(jsx);
303
- }
304
- throw new Error(`Unrecognized JSX node`, { cause: jsx });
305
- };
306
- var updateNode = (dom, jsx) => funcMap[dom[0]][0](dom, jsx);
307
- var revokeNode = (dom) => funcMap[dom[0]][1](dom);
308
- var afterNode = (dom, node) => funcMap[dom[0]][2](dom, node);
309
-
310
- // src/mount/index.ts
311
- var rootMap = new WeakMap;
312
- var render = (el, jsx) => {
313
- if (rootMap.has(el))
314
- revokeNode(rootMap.get(el));
315
- const [root, node] = createNode(jsx);
316
- rootMap.set(el, root);
317
- el.append(node);
326
+ dom2[2].after(node2);
327
+ };
328
+ const [node, dom] = createFragment([null, {}]);
329
+ el.append(dom);
330
+ return {
331
+ render(jsx2) {
332
+ updateFragment(node, [null, {}, jsx2]);
333
+ },
334
+ unmount() {
335
+ revokeFragment(node);
336
+ }
337
+ };
318
338
  };
319
-
320
- //# debugId=A5D082D64B18D38664756E2164756E21
321
- //# sourceMappingURL=index.cjs.map