@lytjs/core-vnode 6.0.0

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/dist/index.mjs ADDED
@@ -0,0 +1,577 @@
1
+ import { createVNode, cloneVNode, EMPTY_OBJ } from '@lytjs/vdom';
2
+ export { Comment, Fragment, Text, cloneVNode, createVNode, mergeProps } from '@lytjs/vdom';
3
+ import { createDOMRenderer } from '@lytjs/renderer';
4
+ import { error } from '@lytjs/common-error';
5
+ import { defineComponent as defineComponent$1, getCurrentInstance, createComponentInstance, setupComponent, createComponentPublicInstance, onBeforeUnmount, createAppContext as createAppContext$1, callUnmountedHook } from '@lytjs/component';
6
+ export { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onUnmounted, onUpdated } from '@lytjs/component';
7
+ import { shallowRef, ref, computed } from '@lytjs/reactivity';
8
+ export { computed, effect, reactive, ref, watch, watchEffect } from '@lytjs/reactivity';
9
+ export { nextTick } from '@lytjs/common-scheduler';
10
+ export { compile } from '@lytjs/compiler';
11
+
12
+ // src/create-app.ts
13
+ function createAppContext() {
14
+ return {
15
+ ...createAppContext$1(),
16
+ config: {
17
+ performance: false,
18
+ globalProperties: {}
19
+ },
20
+ renderer: null,
21
+ _vnode: null,
22
+ _container: null,
23
+ _instance: null
24
+ };
25
+ }
26
+ function createContextConfig(context) {
27
+ return new Proxy({}, {
28
+ get(_, key) {
29
+ if (key === "globalProperties") {
30
+ return context.config.globalProperties;
31
+ }
32
+ return context.config[key];
33
+ },
34
+ set(_, key, value) {
35
+ if (key === "globalProperties") {
36
+ context.config.globalProperties = value;
37
+ return true;
38
+ }
39
+ context.config[key] = value;
40
+ return true;
41
+ },
42
+ ownKeys() {
43
+ return Reflect.ownKeys(context.config);
44
+ },
45
+ has(_, key) {
46
+ return key in context.config;
47
+ },
48
+ getOwnPropertyDescriptor(_, key) {
49
+ return Object.getOwnPropertyDescriptor(context.config, key);
50
+ }
51
+ });
52
+ }
53
+ function createApp(rootComponent, rootProps = null, _options) {
54
+ const context = createAppContext();
55
+ const installedPlugins = /* @__PURE__ */ new Set();
56
+ let _isUnmounted = false;
57
+ let _isMounted = false;
58
+ const app = {
59
+ config: createContextConfig(context),
60
+ use(plugin, ...options) {
61
+ if (installedPlugins.has(plugin)) return app;
62
+ try {
63
+ if (typeof plugin === "function") {
64
+ plugin(app, ...options);
65
+ } else {
66
+ plugin.install(app, ...options);
67
+ }
68
+ installedPlugins.add(plugin);
69
+ } catch (err) {
70
+ error(
71
+ `Plugin failed to install: ${typeof plugin === "function" ? plugin.name || "anonymous function" : plugin.install?.name || "plugin"}: ${err}`
72
+ );
73
+ throw err;
74
+ }
75
+ return app;
76
+ },
77
+ mount(rootContainer) {
78
+ if (_isUnmounted) {
79
+ throw new Error(
80
+ `[LytJS] App has been unmounted and cannot be remounted. Create a new app instance instead.`
81
+ );
82
+ }
83
+ if (!rootComponent) {
84
+ return null;
85
+ }
86
+ if (context._container) {
87
+ throw new Error(
88
+ `[LytJS] App is already mounted. Call app.unmount() first before mounting again.`
89
+ );
90
+ }
91
+ const container = typeof rootContainer === "string" ? document.querySelector(rootContainer) : rootContainer;
92
+ if (!container) {
93
+ throw new Error(
94
+ `[LytJS] Failed to mount app: cannot find element matching selector "${rootContainer}". Make sure the target element exists in the DOM before calling app.mount().`
95
+ );
96
+ }
97
+ context._container = container;
98
+ try {
99
+ const rootVNode = createVNode(rootComponent, rootProps);
100
+ const instance = createComponentInstance(rootVNode, null);
101
+ instance.appContext = context;
102
+ if (context.provides) {
103
+ const rootProvides = instance.provides;
104
+ for (const key in context.provides) {
105
+ if (!(key in rootProvides)) {
106
+ rootProvides[key] = context.provides[key];
107
+ }
108
+ }
109
+ }
110
+ setupComponent(instance);
111
+ rootVNode.component = instance;
112
+ context._instance = instance;
113
+ const renderer = createDOMRenderer();
114
+ context.renderer = renderer;
115
+ context._vnode = rootVNode;
116
+ renderer.mount(rootVNode, container);
117
+ const publicInstance = createComponentPublicInstance(instance);
118
+ _isMounted = true;
119
+ return publicInstance;
120
+ } catch (err) {
121
+ if (app.errorHandler) {
122
+ app.errorHandler(err, null, "mount");
123
+ } else {
124
+ error(`[LytJS] Failed to mount app: ${err instanceof Error ? err.message : String(err)}`);
125
+ }
126
+ throw err;
127
+ }
128
+ },
129
+ unmount() {
130
+ if (!context.renderer || !context._vnode) return;
131
+ const instance = context._instance;
132
+ if (instance) {
133
+ callUnmountedHook(instance);
134
+ }
135
+ context.renderer.unmount(context._vnode);
136
+ context._vnode = null;
137
+ _isUnmounted = true;
138
+ _isMounted = false;
139
+ context._container = null;
140
+ for (const plugin of installedPlugins) {
141
+ if (typeof plugin !== "function" && plugin != null && typeof plugin.cleanup === "function") {
142
+ try {
143
+ const cleanup = plugin.cleanup;
144
+ if (typeof cleanup === "function") cleanup();
145
+ } catch (err) {
146
+ error(
147
+ `Plugin cleanup failed: ${typeof plugin === "object" && plugin !== null && "name" in plugin ? plugin.name : "unknown"}: ${err}`
148
+ );
149
+ }
150
+ }
151
+ }
152
+ installedPlugins.clear();
153
+ context.mixins.length = 0;
154
+ context.components = {};
155
+ context.directives = {};
156
+ context.provides = {};
157
+ context.config.globalProperties = {};
158
+ context._instance = null;
159
+ context.renderer = null;
160
+ },
161
+ provide(key, value) {
162
+ context.provides[key] = value;
163
+ return app;
164
+ },
165
+ inject(key) {
166
+ return context.provides[key];
167
+ },
168
+ component(name, component) {
169
+ context.components[name] = component;
170
+ return app;
171
+ },
172
+ directive(name, directive) {
173
+ context.directives[name] = directive;
174
+ return app;
175
+ },
176
+ mixin(mixin) {
177
+ context.mixins.push(mixin);
178
+ return app;
179
+ }
180
+ };
181
+ return app;
182
+ }
183
+ function h(type, props, ...children) {
184
+ if (props == null) {
185
+ props = EMPTY_OBJ;
186
+ }
187
+ const flatChildren = children.length > 1 ? children : children[0];
188
+ return createVNode(type, props, flatChildren);
189
+ }
190
+ var DEFAULT_ON_ERROR_TIMEOUT = 3e4;
191
+ var defineComponent = defineComponent$1;
192
+ function defineAsyncComponent(source) {
193
+ if (typeof source === "function") {
194
+ source = { loader: source };
195
+ }
196
+ const { loader, loadingComponent, errorComponent, delay = 200, timeout, onError } = source;
197
+ const loadedComponent = shallowRef(void 0);
198
+ const error2 = ref(void 0);
199
+ const loading = ref(false);
200
+ let retries = 0;
201
+ let showLoading = false;
202
+ let delayTimer = null;
203
+ let loadingPromise = null;
204
+ const MAX_RETRIES = 3;
205
+ const retry = () => {
206
+ if (retries >= MAX_RETRIES) {
207
+ error2.value = new Error(
208
+ `[lytjs/core-vnode] AsyncComponent: max retries (${MAX_RETRIES}) exceeded.`
209
+ );
210
+ return;
211
+ }
212
+ retries++;
213
+ loadedComponent.value = void 0;
214
+ error2.value = void 0;
215
+ loadingPromise = null;
216
+ startTimeout();
217
+ return load();
218
+ };
219
+ const load = () => {
220
+ if (loadingPromise) return loadingPromise;
221
+ loading.value = true;
222
+ showLoading = false;
223
+ if (delayTimer) clearTimeout(delayTimer);
224
+ delayTimer = setTimeout(() => {
225
+ showLoading = true;
226
+ }, delay);
227
+ return loadingPromise = loader().then((comp2) => {
228
+ loadedComponent.value = comp2;
229
+ error2.value = void 0;
230
+ loading.value = false;
231
+ loadingPromise = null;
232
+ if (delayTimer) {
233
+ clearTimeout(delayTimer);
234
+ delayTimer = null;
235
+ }
236
+ showLoading = false;
237
+ if (timeoutId !== null) {
238
+ clearTimeout(timeoutId);
239
+ timeoutId = null;
240
+ }
241
+ return comp2;
242
+ }).catch((err) => {
243
+ error2.value = err;
244
+ loading.value = false;
245
+ if (onError) {
246
+ return new Promise((resolve, reject) => {
247
+ let settled = false;
248
+ const ON_ERROR_TIMEOUT = DEFAULT_ON_ERROR_TIMEOUT;
249
+ const timer = setTimeout(() => {
250
+ if (!settled) {
251
+ settled = true;
252
+ reject(
253
+ new Error(
254
+ `[lytjs/core-vnode] AsyncComponent: onError callback did not call retry() or reject() within ${ON_ERROR_TIMEOUT / 1e3}s.`
255
+ )
256
+ );
257
+ }
258
+ }, ON_ERROR_TIMEOUT);
259
+ onError(
260
+ err,
261
+ () => {
262
+ if (!settled) {
263
+ settled = true;
264
+ clearTimeout(timer);
265
+ const result = retry();
266
+ if (result) resolve(result);
267
+ }
268
+ },
269
+ (reason) => {
270
+ if (!settled) {
271
+ settled = true;
272
+ clearTimeout(timer);
273
+ reject(reason ?? err);
274
+ }
275
+ },
276
+ retries
277
+ );
278
+ });
279
+ }
280
+ return Promise.reject(err);
281
+ });
282
+ };
283
+ let timeoutId = null;
284
+ const startTimeout = () => {
285
+ if (timeout == null) return;
286
+ if (timeoutId !== null) {
287
+ clearTimeout(timeoutId);
288
+ }
289
+ timeoutId = setTimeout(() => {
290
+ if (!loadedComponent.value && !error2.value) {
291
+ error2.value = new Error(`[lytjs/core-vnode] AsyncComponent timed out after ${timeout}ms.`);
292
+ loading.value = false;
293
+ }
294
+ }, timeout);
295
+ };
296
+ startTimeout();
297
+ const comp = {
298
+ name: "AsyncComponent",
299
+ setup() {
300
+ const instance = {
301
+ resolved: loadedComponent,
302
+ loading,
303
+ error: error2,
304
+ delay,
305
+ timeout
306
+ };
307
+ onBeforeUnmount(() => {
308
+ if (timeoutId !== null) {
309
+ clearTimeout(timeoutId);
310
+ timeoutId = null;
311
+ }
312
+ if (delayTimer !== null) {
313
+ clearTimeout(delayTimer);
314
+ delayTimer = null;
315
+ }
316
+ });
317
+ if (!loadedComponent.value && !error2.value && !loading.value) {
318
+ load();
319
+ }
320
+ return instance;
321
+ },
322
+ render() {
323
+ if (loadedComponent.value) {
324
+ return h(loadedComponent.value);
325
+ }
326
+ if (error2.value && errorComponent) {
327
+ return h(errorComponent);
328
+ }
329
+ if (showLoading && loadingComponent) {
330
+ return h(loadingComponent);
331
+ }
332
+ return null;
333
+ }
334
+ };
335
+ return comp;
336
+ }
337
+ function resolveComponent(name) {
338
+ const instance = getCurrentInstance();
339
+ if (!instance) {
340
+ return void 0;
341
+ }
342
+ const components = instance.type?.components;
343
+ if (components && components[name]) {
344
+ return components[name];
345
+ }
346
+ const globalComponents = instance.appContext?.components;
347
+ if (globalComponents && globalComponents[name]) {
348
+ return globalComponents[name];
349
+ }
350
+ return void 0;
351
+ }
352
+ function resolveDirective(name) {
353
+ const instance = getCurrentInstance();
354
+ if (!instance) {
355
+ return void 0;
356
+ }
357
+ const directives = instance.type?.directives;
358
+ if (directives && directives[name]) {
359
+ return directives[name];
360
+ }
361
+ const globalDirectives = instance.appContext?.directives;
362
+ if (globalDirectives && globalDirectives[name]) {
363
+ return globalDirectives[name];
364
+ }
365
+ return void 0;
366
+ }
367
+ function withDirectives(vnode, directives) {
368
+ const dirVNode = cloneVNode(vnode);
369
+ dirVNode._directives = directives.map(([dir, value, arg, modifiers]) => ({
370
+ dir,
371
+ value,
372
+ arg,
373
+ modifiers: modifiers || {}
374
+ }));
375
+ return dirVNode;
376
+ }
377
+ function withMemo(memo, render, cache, index) {
378
+ if (index < 0 || index >= cache.length) {
379
+ return render();
380
+ }
381
+ const cached = cache[index];
382
+ if (cached && isMemoSame(cached.memo, memo)) {
383
+ return cached.result;
384
+ }
385
+ const result = render();
386
+ cache[index] = { memo, result };
387
+ return result;
388
+ }
389
+ function isMemoSame(prev, next) {
390
+ if (prev.length !== next.length) return false;
391
+ for (let i = 0; i < prev.length; i++) {
392
+ if (prev[i] !== next[i]) return false;
393
+ }
394
+ return true;
395
+ }
396
+ function useSlots() {
397
+ const instance = getCurrentInstance();
398
+ if (!instance) {
399
+ return {};
400
+ }
401
+ return instance.slots || {};
402
+ }
403
+ function useAttrs() {
404
+ const instance = getCurrentInstance();
405
+ if (!instance) {
406
+ return {};
407
+ }
408
+ return instance.attrs || {};
409
+ }
410
+ function useModel(props, key) {
411
+ const instance = getCurrentInstance();
412
+ if (!instance) {
413
+ return computed({
414
+ get() {
415
+ return void 0;
416
+ },
417
+ set() {
418
+ }
419
+ });
420
+ }
421
+ return computed({
422
+ get() {
423
+ return props[key];
424
+ },
425
+ set(newValue) {
426
+ instance.emit(`update:${key}`, newValue);
427
+ }
428
+ });
429
+ }
430
+ var _currentShadowRoot = null;
431
+ var _currentHost = null;
432
+ var _currentSlotCallback = null;
433
+ function extractObservedAttributes(propsOptions) {
434
+ const attributes = [];
435
+ for (const key in propsOptions) {
436
+ const attrName = key.replace(/([A-Z])/g, "-$1").toLowerCase();
437
+ attributes.push(attrName);
438
+ }
439
+ return attributes;
440
+ }
441
+ function attrToProp(attrName) {
442
+ return attrName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
443
+ }
444
+ function deserializeValue(value, propOptions) {
445
+ if (value === null) return void 0;
446
+ const options = propOptions;
447
+ if (options === true || options === void 0) {
448
+ return value;
449
+ }
450
+ const type = options.type;
451
+ if (type === Number) {
452
+ const num = Number(value);
453
+ return isNaN(num) ? value : num;
454
+ }
455
+ if (type === Boolean) {
456
+ return value !== "false" && value !== "0" && value !== "";
457
+ }
458
+ return value;
459
+ }
460
+ function defineCustomElement(componentOptions, options) {
461
+ const useShadowDOM = options?.shadowRoot !== false;
462
+ const tagName = options?.name || (componentOptions.name ? componentOptions.name.replace(/([A-Z])/g, "-$1").toLowerCase() : "lyt-component");
463
+ const css = options?.css || "";
464
+ const propsOptions = componentOptions.props || {};
465
+ const observedAttributes = extractObservedAttributes(propsOptions);
466
+ class LytCustomElement extends HTMLElement {
467
+ constructor() {
468
+ super();
469
+ this._instance = null;
470
+ this._renderer = null;
471
+ this._vnode = null;
472
+ this._slotObserver = null;
473
+ if (useShadowDOM) {
474
+ this._root = this.attachShadow({ mode: "open" });
475
+ if (css) {
476
+ const style = document.createElement("style");
477
+ style.textContent = css;
478
+ this._root.appendChild(style);
479
+ }
480
+ } else {
481
+ this._root = this;
482
+ }
483
+ this._appContext = createAppContext$1();
484
+ }
485
+ static get observedAttributes() {
486
+ return observedAttributes;
487
+ }
488
+ connectedCallback() {
489
+ _currentShadowRoot = useShadowDOM ? this._root : null;
490
+ _currentHost = this;
491
+ try {
492
+ const props = {};
493
+ for (const attr of observedAttributes) {
494
+ const propKey = attrToProp(attr);
495
+ const value = this.getAttribute(attr);
496
+ props[propKey] = deserializeValue(value, propsOptions[propKey]);
497
+ }
498
+ const vnode = createVNode(componentOptions, props);
499
+ this._vnode = vnode;
500
+ const instance = createComponentInstance(vnode, null);
501
+ instance.appContext = this._appContext;
502
+ this._instance = instance;
503
+ setupComponent(instance);
504
+ const renderer = createDOMRenderer();
505
+ this._renderer = renderer;
506
+ renderer.mount(vnode, this._root);
507
+ this._setupSlotObserver();
508
+ } finally {
509
+ _currentShadowRoot = null;
510
+ _currentHost = null;
511
+ }
512
+ }
513
+ disconnectedCallback() {
514
+ if (this._instance) {
515
+ callUnmountedHook(this._instance);
516
+ }
517
+ if (this._renderer && this._vnode) {
518
+ this._renderer.unmount(this._vnode);
519
+ }
520
+ if (this._slotObserver) {
521
+ this._slotObserver.disconnect();
522
+ this._slotObserver = null;
523
+ }
524
+ this._instance = null;
525
+ this._renderer = null;
526
+ this._vnode = null;
527
+ }
528
+ attributeChangedCallback(name, _oldValue, newValue) {
529
+ if (!this._instance || !this._vnode) return;
530
+ const propKey = attrToProp(name);
531
+ const deserializedValue = deserializeValue(newValue, propsOptions[propKey]);
532
+ this._instance.props[propKey] = deserializedValue;
533
+ if (this._renderer && this._vnode) {
534
+ this._vnode.props[propKey] = deserializedValue;
535
+ if (this._renderer.render && this._vnode) {
536
+ this._renderer.render(this._vnode, this._root);
537
+ }
538
+ }
539
+ }
540
+ _setupSlotObserver() {
541
+ this._slotObserver = new MutationObserver(() => {
542
+ if (_currentSlotCallback) {
543
+ _currentSlotCallback();
544
+ }
545
+ });
546
+ this._slotObserver.observe(this, {
547
+ childList: true,
548
+ subtree: true,
549
+ characterData: true
550
+ });
551
+ }
552
+ }
553
+ if (!customElements.get(tagName)) {
554
+ customElements.define(tagName, LytCustomElement);
555
+ }
556
+ return LytCustomElement;
557
+ }
558
+ function useShadowRoot() {
559
+ return _currentShadowRoot;
560
+ }
561
+ function useHost() {
562
+ return _currentHost;
563
+ }
564
+ function useWebComponentSlots(onChange) {
565
+ _currentSlotCallback = onChange;
566
+ }
567
+ function injectChildStyles(styles) {
568
+ const shadowRoot = _currentShadowRoot;
569
+ if (!shadowRoot) {
570
+ return;
571
+ }
572
+ const style = document.createElement("style");
573
+ style.textContent = styles;
574
+ shadowRoot.appendChild(style);
575
+ }
576
+
577
+ export { createApp, h as createElement, defineAsyncComponent, defineComponent, defineCustomElement, h, injectChildStyles, resolveComponent, resolveDirective, useAttrs, useHost, useModel, useShadowRoot, useSlots, useWebComponentSlots, withDirectives, withMemo };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@lytjs/core-vnode",
3
+ "version": "6.0.0",
4
+ "description": "Lyt.js Core - VNode rendering mode only",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": false,
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch"
23
+ },
24
+ "dependencies": {
25
+ "@lytjs/component": "^6.0.0",
26
+ "@lytjs/reactivity": "^6.0.0",
27
+ "@lytjs/vdom": "^6.0.0",
28
+ "@lytjs/compiler": "^6.0.0",
29
+ "@lytjs/renderer": "^6.0.0",
30
+ "@lytjs/common-scheduler": "^6.0.0",
31
+ "@lytjs/common-error": "^6.0.0",
32
+ "@lytjs/shared-types": "^6.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "tsup": "^8.0.0",
36
+ "typescript": "^5.4.0"
37
+ },
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://gitee.com/lytjs/lytjs.git",
42
+ "directory": "packages/core-vnode"
43
+ },
44
+ "keywords": [
45
+ "lytjs",
46
+ "core",
47
+ "vnode",
48
+ "createApp",
49
+ "h",
50
+ "defineComponent"
51
+ ]
52
+ }