jsx-framework-test-pb 0.1.3 → 0.1.6

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.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { render, unmount } from './runtime';
2
+ export { renderToString, hydrate } from './ssr';
2
3
  export { Fragment } from './jsx-runtime';
3
4
  export { isElement } from './utils';
4
- export { State, computed, effect } from './reactivity';
5
+ export { state, computed, effect } from './reactivity';
5
6
  export type { StateObject } from './reactivity';
6
7
  export type { Element, ElementProps, ElementChild, DevElement, FC, HTMLAttributes, CSSProperties } from './types';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { render, unmount } from './runtime';
2
+ export { renderToString, hydrate } from './ssr';
2
3
  export { Fragment } from './jsx-runtime';
3
4
  export { isElement } from './utils';
4
- export { State, computed, effect } from './reactivity';
5
+ export { state, computed, effect } from './reactivity';
@@ -1,2 +1,3 @@
1
- import type { Effect } from './state';
1
+ import type { Effect, StateObject } from './types';
2
2
  export declare function effect(fn: Effect): void;
3
+ export declare function computed<T>(fn: () => T): StateObject<T>;
@@ -1,4 +1,4 @@
1
- import { setActiveEffect } from './state';
1
+ import { setActiveEffect, state } from './state';
2
2
  export function effect(fn) {
3
3
  const runner = () => {
4
4
  setActiveEffect(runner);
@@ -7,3 +7,8 @@ export function effect(fn) {
7
7
  };
8
8
  runner();
9
9
  }
10
+ export function computed(fn) {
11
+ const s = state(undefined);
12
+ effect(() => { s.val = fn(); });
13
+ return s;
14
+ }
@@ -1,3 +1,3 @@
1
- export { State, computed, isState, setActiveEffect } from './state';
2
- export { effect } from './effect';
3
- export type { Effect, StateObject } from './state';
1
+ export type { Effect, StateObject } from './types';
2
+ export { state, isState } from './state';
3
+ export { effect, computed } from './effect';
@@ -1,2 +1,2 @@
1
- export { State, computed, isState, setActiveEffect } from './state';
2
- export { effect } from './effect';
1
+ export { state, isState } from './state';
2
+ export { effect, computed } from './effect';
@@ -1,8 +1,5 @@
1
- export type Effect = () => void;
2
- export interface StateObject<T> {
3
- val: T;
4
- }
5
- export declare function State<T>(initial: T): StateObject<T>;
1
+ import type { Effect, StateObject } from './types';
6
2
  export declare function setActiveEffect(effect: Effect | null): void;
7
- export declare function isState(value: any): value is StateObject<any>;
8
- export declare function computed<T>(fn: () => T): StateObject<T>;
3
+ export declare function getActiveEffect(): Effect | null;
4
+ export declare function state<T>(initial: T): StateObject<T>;
5
+ export declare function isState(value: unknown): value is StateObject<unknown>;
@@ -1,12 +1,17 @@
1
1
  let activeEffect = null;
2
- export function State(initial) {
2
+ export function setActiveEffect(effect) {
3
+ activeEffect = effect;
4
+ }
5
+ export function getActiveEffect() {
6
+ return activeEffect;
7
+ }
8
+ export function state(initial) {
3
9
  let value = initial;
4
10
  const subscribers = new Set();
5
11
  return {
6
12
  get val() {
7
- if (activeEffect) {
13
+ if (activeEffect)
8
14
  subscribers.add(activeEffect);
9
- }
10
15
  return value;
11
16
  },
12
17
  set val(next) {
@@ -17,19 +22,6 @@ export function State(initial) {
17
22
  }
18
23
  };
19
24
  }
20
- export function setActiveEffect(effect) {
21
- activeEffect = effect;
22
- }
23
25
  export function isState(value) {
24
- return value && typeof value === 'object' && 'val' in value;
25
- }
26
- export function computed(fn) {
27
- const state = State(undefined);
28
- const runner = () => {
29
- setActiveEffect(runner);
30
- state.val = fn();
31
- setActiveEffect(null);
32
- };
33
- runner();
34
- return state;
26
+ return value !== null && typeof value === 'object' && 'val' in value;
35
27
  }
@@ -0,0 +1,4 @@
1
+ export type Effect = () => void;
2
+ export interface StateObject<T> {
3
+ val: T;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- export { mountComponent, unmountComponent } from './component';
2
- export { createElement } from './createElement';
1
+ export type { FiberNode } from './types';
3
2
  export { reconcile } from './reconciler';
4
3
  export { render, unmount } from './render';
4
+ export { createElement } from './createElement';
@@ -1,4 +1,3 @@
1
- export { mountComponent, unmountComponent } from './component';
2
- export { createElement } from './createElement';
3
1
  export { reconcile } from './reconciler';
4
2
  export { render, unmount } from './render';
3
+ export { createElement } from './createElement';
@@ -1,9 +1,2 @@
1
- interface FiberNode {
2
- element: any;
3
- dom: Node | null;
4
- parent: FiberNode | null;
5
- key: string | null;
6
- index: number;
7
- }
1
+ import type { FiberNode } from "./types";
8
2
  export declare function reconcile(parent: HTMLElement, newElements: any[] | any, oldFibers?: FiberNode[]): FiberNode[];
9
- export {};
@@ -1,5 +1,6 @@
1
1
  import { createElement } from "./createElement";
2
2
  import { isElement } from "../utils";
3
+ import { effect } from "../reactivity";
3
4
  function canReuse(oldElement, newElement) {
4
5
  if (typeof oldElement !== typeof newElement)
5
6
  return false;
@@ -13,23 +14,29 @@ function updateElement(dom, element) {
13
14
  return;
14
15
  const { props } = element;
15
16
  Object.keys(props).forEach((key) => {
16
- if (key === "children") {
17
+ if (key === "children" || key === "key")
17
18
  return;
18
- }
19
- else if (key.startsWith("on") && typeof props[key] === "function") {
19
+ if (key.startsWith("on"))
20
20
  return;
21
- }
22
- else if (key === "className") {
23
- dom.setAttribute("class", props[key]);
21
+ if (key === "className") {
22
+ setAttr(dom, "class", props[key]);
24
23
  }
25
24
  else if (key === "style" && typeof props[key] === "object") {
26
25
  Object.assign(dom.style, props[key]);
27
26
  }
28
- else if (key !== "key" && props[key] != null) {
29
- dom.setAttribute(key, String(props[key]));
27
+ else if (props[key] != null) {
28
+ setAttr(dom, key, props[key]);
30
29
  }
31
30
  });
32
31
  }
32
+ function setAttr(el, name, value) {
33
+ if (typeof value === "function") {
34
+ effect(() => el.setAttribute(name, String(value())));
35
+ }
36
+ else {
37
+ el.setAttribute(name, String(value));
38
+ }
39
+ }
33
40
  function getInsertionPoint(parent, targetIndex) {
34
41
  return parent.childNodes.item(targetIndex) ?? null;
35
42
  }
@@ -60,13 +67,7 @@ export function reconcile(parent, newElements, oldFibers = []) {
60
67
  }
61
68
  let fiber;
62
69
  if (oldFiber && canReuse(oldFiber.element, element)) {
63
- fiber = {
64
- element,
65
- dom: oldFiber.dom,
66
- parent: null,
67
- key,
68
- index,
69
- };
70
+ fiber = { element, dom: oldFiber.dom, key, index };
70
71
  if (fiber.dom)
71
72
  reusedOldDoms.add(fiber.dom);
72
73
  if (oldFiber.dom && isElement(element)) {
@@ -75,14 +76,8 @@ export function reconcile(parent, newElements, oldFibers = []) {
75
76
  }
76
77
  else {
77
78
  const dom = createElement(element);
78
- fiber = {
79
- element,
80
- dom,
81
- parent: null,
82
- key,
83
- index,
84
- };
85
- if (oldFiber?.dom && oldFiber.dom.parentNode === parent) {
79
+ fiber = { element, dom, key, index };
80
+ if (oldFiber?.dom?.parentNode === parent) {
86
81
  parent.removeChild(oldFiber.dom);
87
82
  }
88
83
  if (dom) {
@@ -99,7 +94,7 @@ export function reconcile(parent, newElements, oldFibers = []) {
99
94
  newFibers.push(fiber);
100
95
  });
101
96
  [...oldFibersByKey.values(), ...oldFibersByIndex.values()].forEach((fiber) => {
102
- if (fiber.dom && fiber.dom.parentNode === parent && !reusedOldDoms.has(fiber.dom)) {
97
+ if (fiber.dom?.parentNode === parent && !reusedOldDoms.has(fiber.dom)) {
103
98
  parent.removeChild(fiber.dom);
104
99
  }
105
100
  });
@@ -1,12 +1,24 @@
1
- import { mountComponent } from './component';
1
+ import { reconcile } from './reconciler';
2
+ import { isElement } from '../utils';
3
+ const instances = new WeakMap();
2
4
  export function render(element, container) {
3
5
  if (!container)
4
6
  throw new Error('Container element is required');
5
- container.replaceChildren();
6
- mountComponent(element, container);
7
+ const oldFibers = instances.get(container) ?? [];
8
+ let children;
9
+ if (isElement(element) && typeof element.type === 'function') {
10
+ const result = element.type(element.props);
11
+ children = Array.isArray(result) ? result : [result];
12
+ }
13
+ else {
14
+ children = [element];
15
+ }
16
+ const newFibers = reconcile(container, children, oldFibers);
17
+ instances.set(container, newFibers);
7
18
  }
8
19
  export function unmount(container) {
9
20
  if (!container)
10
21
  return;
11
22
  container.replaceChildren();
23
+ instances.delete(container);
12
24
  }
@@ -0,0 +1,6 @@
1
+ export interface FiberNode {
2
+ element: any;
3
+ dom: Node | null;
4
+ key: string | null;
5
+ index: number;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function hydrate(element: any, container: Document | HTMLElement): void;
@@ -0,0 +1,45 @@
1
+ import { isElement } from '../utils';
2
+ const instances = new WeakMap();
3
+ export function hydrate(element, container) {
4
+ const target = container === document ? document.documentElement : container;
5
+ if (!target) {
6
+ throw new Error('Hydration target not found');
7
+ }
8
+ const fibers = hydrateNode(element, target);
9
+ instances.set(target, fibers);
10
+ }
11
+ function hydrateNode(element, dom) {
12
+ if (!isElement(element)) {
13
+ return [];
14
+ }
15
+ const { type, props } = element;
16
+ if (typeof type === 'function') {
17
+ const result = type(props);
18
+ return hydrateNode(result, dom);
19
+ }
20
+ if (typeof type === 'string' && dom instanceof HTMLElement) {
21
+ Object.keys(props).forEach(key => {
22
+ if (key.startsWith('on') && typeof props[key] === 'function') {
23
+ const eventName = key.substring(2).toLowerCase();
24
+ dom.addEventListener(eventName, props[key]);
25
+ }
26
+ });
27
+ const children = Array.isArray(props.children)
28
+ ? props.children
29
+ : props.children
30
+ ? [props.children]
31
+ : [];
32
+ const childNodes = Array.from(dom.childNodes).filter(node => node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE);
33
+ children.forEach((child, i) => {
34
+ if (childNodes[i]) {
35
+ hydrateNode(child, childNodes[i]);
36
+ }
37
+ });
38
+ }
39
+ return [{
40
+ element,
41
+ dom,
42
+ key: element.key ?? null,
43
+ index: 0
44
+ }];
45
+ }
@@ -0,0 +1,2 @@
1
+ export { renderToString } from './renderToString';
2
+ export { hydrate } from './hydrate';
@@ -0,0 +1,2 @@
1
+ export { renderToString } from './renderToString';
2
+ export { hydrate } from './hydrate';
@@ -0,0 +1 @@
1
+ export declare function renderToString(element: any): string;
@@ -0,0 +1,69 @@
1
+ import { isElement, isValidChild, FRAGMENT_TYPE } from '../utils';
2
+ const VOID_ELEMENTS = new Set([
3
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
4
+ 'link', 'meta', 'param', 'source', 'track', 'wbr'
5
+ ]);
6
+ export function renderToString(element) {
7
+ if (!isValidChild(element)) {
8
+ return '';
9
+ }
10
+ if (typeof element === 'string' || typeof element === 'number') {
11
+ return escapeHtml(String(element));
12
+ }
13
+ if (typeof element === 'function') {
14
+ return escapeHtml(String(element()));
15
+ }
16
+ if (Array.isArray(element)) {
17
+ return element.map(renderToString).join('');
18
+ }
19
+ if (!isElement(element)) {
20
+ return '';
21
+ }
22
+ const { type, props } = element;
23
+ if (type === FRAGMENT_TYPE) {
24
+ return renderToString(props.children);
25
+ }
26
+ if (typeof type === 'function') {
27
+ const result = type(props);
28
+ return renderToString(result);
29
+ }
30
+ if (typeof type === 'string') {
31
+ const attrs = propsToAttributes(props);
32
+ if (VOID_ELEMENTS.has(type)) {
33
+ return `<${type}${attrs}>`;
34
+ }
35
+ const children = renderToString(props.children);
36
+ return `<${type}${attrs}>${children}</${type}>`;
37
+ }
38
+ return '';
39
+ }
40
+ function propsToAttributes(props) {
41
+ if (!props)
42
+ return '';
43
+ const attrs = Object.entries(props)
44
+ .filter(([key, value]) => {
45
+ if (key === 'children' || key === 'key')
46
+ return false;
47
+ if (key.startsWith('on'))
48
+ return false;
49
+ if (value == null || value === false)
50
+ return false;
51
+ return true;
52
+ })
53
+ .map(([key, value]) => {
54
+ const attrName = key === 'className' ? 'class' : key;
55
+ if (value === true)
56
+ return attrName;
57
+ const attrValue = typeof value === 'function' ? value() : value;
58
+ return `${attrName}="${escapeHtml(String(attrValue))}"`;
59
+ })
60
+ .join(' ');
61
+ return attrs ? ` ${attrs}` : '';
62
+ }
63
+ function escapeHtml(str) {
64
+ return str
65
+ .replace(/&/g, '&amp;')
66
+ .replace(/</g, '&lt;')
67
+ .replace(/>/g, '&gt;')
68
+ .replace(/"/g, '&quot;');
69
+ }
package/dist/types.d.ts CHANGED
@@ -226,10 +226,34 @@ export interface SVGAttributes extends HTMLAttributes<SVGElement> {
226
226
  strokeLinecap?: 'butt' | 'round' | 'square';
227
227
  strokeLinejoin?: 'miter' | 'round' | 'bevel';
228
228
  }
229
+ export interface MetaHTMLAttributes extends HTMLAttributes<HTMLMetaElement> {
230
+ charSet?: string;
231
+ content?: string;
232
+ httpEquiv?: string;
233
+ name?: string;
234
+ }
235
+ export interface LinkHTMLAttributes extends HTMLAttributes<HTMLLinkElement> {
236
+ href?: string;
237
+ rel?: string;
238
+ type?: string;
239
+ media?: string;
240
+ crossOrigin?: 'anonymous' | 'use-credentials';
241
+ }
242
+ export interface BaseHTMLAttributes extends HTMLAttributes<HTMLBaseElement> {
243
+ href?: string;
244
+ target?: string;
245
+ }
229
246
  declare global {
230
247
  namespace JSX {
231
248
  type Element = import('./types').Element;
232
249
  interface IntrinsicElements {
250
+ html: HTMLAttributes<HTMLHtmlElement>;
251
+ head: HTMLAttributes<HTMLHeadElement>;
252
+ body: HTMLAttributes<HTMLBodyElement>;
253
+ title: HTMLAttributes<HTMLTitleElement>;
254
+ meta: MetaHTMLAttributes;
255
+ link: LinkHTMLAttributes;
256
+ base: BaseHTMLAttributes;
233
257
  a: AnchorHTMLAttributes;
234
258
  abbr: HTMLAttributes<HTMLElement>;
235
259
  address: HTMLAttributes<HTMLElement>;
@@ -1,2 +1,2 @@
1
- export const ELEMENT_TYPE = Symbol.for('your.framework.element');
2
- export const FRAGMENT_TYPE = Symbol.for('your.framework.fragment');
1
+ export const ELEMENT_TYPE = Symbol.for('uraniyum.element');
2
+ export const FRAGMENT_TYPE = Symbol.for('uraniyum.fragment');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsx-framework-test-pb",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -17,7 +17,10 @@
17
17
  "./jsx-dev-runtime": {
18
18
  "types": "./dist/jsx-dev-runtime.d.ts",
19
19
  "default": "./dist/jsx-dev-runtime.js"
20
- }
20
+ },
21
+ "./utils/*": "./dist/utils/*",
22
+ "./runtime/*": "./dist/runtime/*",
23
+ "./ssr/*": "./dist/ssr/*"
21
24
  },
22
25
  "scripts": {
23
26
  "build": "tsc",
@@ -1,11 +0,0 @@
1
- interface ComponentInstance {
2
- element: any;
3
- container: HTMLElement;
4
- fibers: any[];
5
- props: any;
6
- state: any;
7
- setState: (updater: any) => void;
8
- }
9
- export declare function mountComponent(element: any, container: HTMLElement): ComponentInstance;
10
- export declare function unmountComponent(container: HTMLElement): void;
11
- export {};
@@ -1,39 +0,0 @@
1
- import { reconcile } from './reconciler';
2
- import { isElement } from '../utils';
3
- const instances = new WeakMap();
4
- export function mountComponent(element, container) {
5
- const instance = {
6
- element,
7
- container,
8
- fibers: [],
9
- props: isElement(element) ? element.props : {},
10
- state: {},
11
- setState: (updater) => {
12
- if (typeof updater === 'function') {
13
- instance.state = updater(instance.state);
14
- }
15
- else {
16
- instance.state = { ...instance.state, ...updater };
17
- }
18
- updateComponent(instance);
19
- }
20
- };
21
- instances.set(container, instance);
22
- updateComponent(instance);
23
- return instance;
24
- }
25
- function updateComponent(instance) {
26
- const { element, container } = instance;
27
- let children;
28
- if (isElement(element) && typeof element.type === 'function') {
29
- const result = element.type(element.props);
30
- children = Array.isArray(result) ? result : [result];
31
- }
32
- else {
33
- children = [element];
34
- }
35
- instance.fibers = reconcile(container, children, instance.fibers);
36
- }
37
- export function unmountComponent(container) {
38
- instances.delete(container);
39
- }