sinho 0.1.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.
Files changed (117) hide show
  1. package/.github/workflows/ci.yml +24 -0
  2. package/.github/workflows/deploy-docs.yml +47 -0
  3. package/.prettierrc +3 -0
  4. package/LICENSE.md +21 -0
  5. package/README.md +33 -0
  6. package/ci/check-size.js +8 -0
  7. package/dist/array_mutation.d.ts +16 -0
  8. package/dist/array_mutation.js +75 -0
  9. package/dist/array_mutation.js.map +1 -0
  10. package/dist/bundle.d.ts +1126 -0
  11. package/dist/bundle.js +1074 -0
  12. package/dist/bundle.min.js +1 -0
  13. package/dist/component.d.ts +253 -0
  14. package/dist/component.js +256 -0
  15. package/dist/component.js.map +1 -0
  16. package/dist/context.d.ts +21 -0
  17. package/dist/context.js +34 -0
  18. package/dist/context.js.map +1 -0
  19. package/dist/create_element.d.ts +43 -0
  20. package/dist/create_element.js +43 -0
  21. package/dist/create_element.js.map +1 -0
  22. package/dist/dom.d.ts +602 -0
  23. package/dist/dom.js +97 -0
  24. package/dist/dom.js.map +1 -0
  25. package/dist/intrinsic/ClassComponent.d.ts +2 -0
  26. package/dist/intrinsic/ClassComponent.js +10 -0
  27. package/dist/intrinsic/ClassComponent.js.map +1 -0
  28. package/dist/intrinsic/Dynamic.d.ts +33 -0
  29. package/dist/intrinsic/Dynamic.js +53 -0
  30. package/dist/intrinsic/Dynamic.js.map +1 -0
  31. package/dist/intrinsic/ErrorBoundary.d.ts +14 -0
  32. package/dist/intrinsic/ErrorBoundary.js +36 -0
  33. package/dist/intrinsic/ErrorBoundary.js.map +1 -0
  34. package/dist/intrinsic/For.d.ts +10 -0
  35. package/dist/intrinsic/For.js +81 -0
  36. package/dist/intrinsic/For.js.map +1 -0
  37. package/dist/intrinsic/Fragment.d.ts +23 -0
  38. package/dist/intrinsic/Fragment.js +28 -0
  39. package/dist/intrinsic/Fragment.js.map +1 -0
  40. package/dist/intrinsic/If.d.ts +24 -0
  41. package/dist/intrinsic/If.js +47 -0
  42. package/dist/intrinsic/If.js.map +1 -0
  43. package/dist/intrinsic/Portal.d.ts +6 -0
  44. package/dist/intrinsic/Portal.js +15 -0
  45. package/dist/intrinsic/Portal.js.map +1 -0
  46. package/dist/intrinsic/Style.d.ts +7 -0
  47. package/dist/intrinsic/Style.js +70 -0
  48. package/dist/intrinsic/Style.js.map +1 -0
  49. package/dist/intrinsic/TagComponent.d.ts +4 -0
  50. package/dist/intrinsic/TagComponent.js +67 -0
  51. package/dist/intrinsic/TagComponent.js.map +1 -0
  52. package/dist/intrinsic/Text.d.ts +6 -0
  53. package/dist/intrinsic/Text.js +16 -0
  54. package/dist/intrinsic/Text.js.map +1 -0
  55. package/dist/intrinsic/mod.d.ts +5 -0
  56. package/dist/intrinsic/mod.js +6 -0
  57. package/dist/intrinsic/mod.js.map +1 -0
  58. package/dist/jsx-runtime/mod.d.ts +23 -0
  59. package/dist/jsx-runtime/mod.js +11 -0
  60. package/dist/jsx-runtime/mod.js.map +1 -0
  61. package/dist/mod.d.ts +8 -0
  62. package/dist/mod.js +7 -0
  63. package/dist/mod.js.map +1 -0
  64. package/dist/renderer.d.ts +13 -0
  65. package/dist/renderer.js +25 -0
  66. package/dist/renderer.js.map +1 -0
  67. package/dist/scope.d.ts +138 -0
  68. package/dist/scope.js +228 -0
  69. package/dist/scope.js.map +1 -0
  70. package/dist/template.d.ts +10 -0
  71. package/dist/template.js +7 -0
  72. package/dist/template.js.map +1 -0
  73. package/dist/utils.d.ts +6 -0
  74. package/dist/utils.js +13 -0
  75. package/dist/utils.js.map +1 -0
  76. package/package.json +71 -0
  77. package/src/array_mutation.ts +118 -0
  78. package/src/component.ts +624 -0
  79. package/src/context.ts +70 -0
  80. package/src/create_element.ts +89 -0
  81. package/src/dom.ts +819 -0
  82. package/src/intrinsic/ClassComponent.ts +17 -0
  83. package/src/intrinsic/For.ts +122 -0
  84. package/src/intrinsic/Fragment.ts +38 -0
  85. package/src/intrinsic/If.ts +73 -0
  86. package/src/intrinsic/Portal.ts +25 -0
  87. package/src/intrinsic/Style.ts +120 -0
  88. package/src/intrinsic/TagComponent.ts +102 -0
  89. package/src/intrinsic/Text.ts +24 -0
  90. package/src/intrinsic/mod.ts +5 -0
  91. package/src/jsx-runtime/mod.ts +41 -0
  92. package/src/mod.ts +37 -0
  93. package/src/renderer.ts +45 -0
  94. package/src/scope.ts +404 -0
  95. package/src/template.ts +16 -0
  96. package/src/utils.ts +29 -0
  97. package/terser.config.json +16 -0
  98. package/tsconfig.json +18 -0
  99. package/web/README.md +41 -0
  100. package/web/babel.config.js +3 -0
  101. package/web/dist/shingo.min.d.ts +1131 -0
  102. package/web/dist/shingo.min.js +1 -0
  103. package/web/docusaurus.config.ts +151 -0
  104. package/web/package-lock.json +14850 -0
  105. package/web/package.json +54 -0
  106. package/web/sidebars.ts +31 -0
  107. package/web/src/components/monacoEditor.tsx +72 -0
  108. package/web/src/components/playground.tsx +89 -0
  109. package/web/src/components/playgroundComponent.tsx +168 -0
  110. package/web/src/css/custom.css +37 -0
  111. package/web/src/pages/index.module.css +31 -0
  112. package/web/src/pages/index.tsx +73 -0
  113. package/web/src/pages/playground.tsx +64 -0
  114. package/web/static/.nojekyll +0 -0
  115. package/web/static/dist/bundle.d.ts +1126 -0
  116. package/web/static/dist/bundle.min.js +1 -0
  117. package/web/tsconfig.json +8 -0
@@ -0,0 +1,17 @@
1
+ import type { DomProps } from "../dom.js";
2
+ import { useRenderer } from "../renderer.js";
3
+ import { createTemplate } from "../template.js";
4
+ import { hydrateElement } from "./TagComponent.js";
5
+
6
+ export const ClassComponent = <T extends HTMLElement>(
7
+ type: new () => T,
8
+ props: DomProps<T> & Record<string, unknown>,
9
+ ) =>
10
+ createTemplate(() => {
11
+ const node = useRenderer()._node(() => new type());
12
+ customElements.upgrade(node);
13
+
14
+ hydrateElement(node, false, props);
15
+
16
+ return [node];
17
+ });
@@ -0,0 +1,122 @@
1
+ import { useArrayMutation } from "../array_mutation.js";
2
+ import {
3
+ MaybeSignal,
4
+ Signal,
5
+ SignalLike,
6
+ useEffect,
7
+ useSignal,
8
+ useSubscope,
9
+ } from "../scope.js";
10
+ import { useRenderer } from "../renderer.js";
11
+ import { createTemplate, Template } from "../template.js";
12
+
13
+ interface KeyMeta {
14
+ _subnodes: Node[];
15
+ _destroy: () => void;
16
+ }
17
+
18
+ /**
19
+ * `For` is a component that can be used to render a list of items.
20
+ */
21
+ export const For = <T>(props: {
22
+ each?: MaybeSignal<readonly T[]>;
23
+ key?: (item: T, index: number) => string | number;
24
+ children?: (
25
+ item: Signal<T>,
26
+ index: Signal<number>,
27
+ arr: SignalLike<readonly T[]>,
28
+ ) => Template;
29
+ }): Template =>
30
+ createTemplate(() => {
31
+ const renderer = useRenderer();
32
+ const items = MaybeSignal.upgrade(props.each ?? []);
33
+ const anchor = renderer._node(() => document.createComment(""));
34
+ const keyFn = props.key ?? ((_, i) => i);
35
+ const nodes: Node[] = [anchor];
36
+ const keyMap = new Map<unknown, KeyMeta>();
37
+ const mutationResult = useArrayMutation(items, keyFn);
38
+
39
+ const lookForAnchor = (index: number): Node => {
40
+ for (let i = index - 1; i >= 0; i--) {
41
+ const key = keyFn(items()[index - 1], index - 1);
42
+ const nodes = keyMap.get(key)?._subnodes ?? [];
43
+
44
+ if (nodes.length > 0) {
45
+ return nodes[nodes.length - 1];
46
+ }
47
+ }
48
+
49
+ return anchor;
50
+ };
51
+
52
+ useEffect(() => {
53
+ for (const mutation of mutationResult()._mutations) {
54
+ if (mutation._type == "r") {
55
+ const { _subnodes = [], _destroy } = keyMap.get(mutation._key) ?? {};
56
+ _destroy?.();
57
+
58
+ const index = nodes.indexOf(_subnodes[0]);
59
+ if (index > 0) {
60
+ nodes.splice(index, _subnodes.length);
61
+ }
62
+
63
+ _subnodes.forEach((node) => node.parentNode?.removeChild(node));
64
+ keyMap.delete(mutation._key);
65
+ } else if (mutation._type == "a") {
66
+ let _subnodes: Node[] = [];
67
+
68
+ const [, destroy] = useSubscope(() => {
69
+ const [index, setIndex] = useSignal(mutation._index);
70
+ const [item, setItem] = useSignal(items()[mutation._index]);
71
+
72
+ useEffect(() => {
73
+ if (0 <= index() && index() < items().length) {
74
+ setItem(() => items()[index()]);
75
+ }
76
+ });
77
+
78
+ useEffect(() => {
79
+ const index = mutationResult()._map.get(mutation._key);
80
+
81
+ if (index != null) {
82
+ setIndex(index);
83
+ }
84
+ });
85
+
86
+ _subnodes = props.children?.(item, index, items).build() ?? [];
87
+
88
+ const itemAnchor = lookForAnchor(mutation._index);
89
+ const anchorIndex = nodes.indexOf(itemAnchor);
90
+ if (anchorIndex >= 0) {
91
+ nodes.splice(anchorIndex + 1, 0, ..._subnodes);
92
+ }
93
+
94
+ _subnodes.forEach((node) =>
95
+ itemAnchor.parentNode?.insertBefore(node, itemAnchor.nextSibling),
96
+ );
97
+ });
98
+
99
+ keyMap.set(mutation._key, { _subnodes, _destroy: destroy });
100
+ } else if (mutation._type == "m") {
101
+ const { _subnodes = [] } = keyMap.get(mutation._key) ?? {};
102
+
103
+ const index = nodes.indexOf(_subnodes[0]);
104
+ if (index >= 0) {
105
+ nodes.splice(index, _subnodes.length);
106
+ }
107
+
108
+ const itemAnchor = lookForAnchor(mutation._to);
109
+ const anchorIndex = nodes.indexOf(itemAnchor);
110
+ if (anchorIndex >= 0) {
111
+ nodes.splice(anchorIndex + 1, 0, ..._subnodes);
112
+ }
113
+
114
+ _subnodes.forEach((node) =>
115
+ itemAnchor.parentNode?.insertBefore(node, itemAnchor.nextSibling),
116
+ );
117
+ }
118
+ }
119
+ }, [mutationResult]);
120
+
121
+ return nodes;
122
+ });
@@ -0,0 +1,38 @@
1
+ import { Text } from "./Text.js";
2
+ import { FunctionalComponent } from "../component.js";
3
+ import { createTemplate, Template } from "../template.js";
4
+ import type { MaybeSignal } from "../scope.js";
5
+
6
+ export type Children =
7
+ | Template
8
+ | MaybeSignal<string | number | null | undefined>
9
+ | Children[];
10
+
11
+ /**
12
+ * Fragment is a component that can be used to wrap multiple children without
13
+ * introducing an extra DOM element.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * render() {
18
+ * return (
19
+ * <>
20
+ * <h1>Hello World</h1>
21
+ * <p>This is a paragraph.</p>
22
+ * </>
23
+ * );
24
+ * }
25
+ * ```
26
+ */
27
+ export const Fragment: FunctionalComponent<{
28
+ children?: Children;
29
+ }> = ({ children }) =>
30
+ createTemplate(() => {
31
+ return !Array.isArray(children)
32
+ ? children == null
33
+ ? []
34
+ : typeof children == "object"
35
+ ? children
36
+ : Text({ text: children })
37
+ : children.flatMap((children) => Fragment({ children }).build());
38
+ });
@@ -0,0 +1,73 @@
1
+ import type { FunctionalComponent } from "../component.js";
2
+ import { createTemplate, type Template } from "../template.js";
3
+ import { MaybeSignal, useEffect, useMemo, useSubscope } from "../scope.js";
4
+ import { runWithRenderer, useRenderer } from "../renderer.js";
5
+ import { Children, Fragment } from "./Fragment.js";
6
+
7
+ /**
8
+ * `If` is a component that can be used to render conditionally.
9
+ */
10
+ export const If: FunctionalComponent<{
11
+ condition?: MaybeSignal<boolean>;
12
+ children?: Children;
13
+ }> = (props) => {
14
+ const renderer = useRenderer();
15
+ renderer._ifConditions = [];
16
+
17
+ return ElseIf({ condition: props.condition, children: props.children });
18
+ };
19
+
20
+ /**
21
+ * `ElseIf` serves as an `else if` block for {@link If}. It can also be chained
22
+ * multiple times.
23
+ */
24
+ export const ElseIf: FunctionalComponent<{
25
+ condition?: MaybeSignal<boolean>;
26
+ children?: Children;
27
+ }> = (props) => {
28
+ const renderer = useRenderer();
29
+ const conditions = renderer._ifConditions;
30
+ const condition = useMemo(
31
+ () =>
32
+ conditions.every((condition) => !condition()) &&
33
+ MaybeSignal.get<boolean | undefined>(props.condition),
34
+ );
35
+
36
+ renderer._ifConditions = [...conditions, condition];
37
+
38
+ return runWithRenderer({ _ifConditions: [] }, () =>
39
+ createTemplate(() => {
40
+ const anchor = renderer._node(() => document.createComment(""));
41
+ const nodes: Node[] = [anchor];
42
+ const template = useMemo(() =>
43
+ condition() ? Fragment({ children: props.children }) : null,
44
+ );
45
+
46
+ let subnodes: Node[] = [];
47
+
48
+ useEffect(() => {
49
+ subnodes.forEach((node) => node.parentNode?.removeChild(node));
50
+ nodes.length = 1;
51
+
52
+ const [, destroy] = useSubscope(() => {
53
+ subnodes = template()?.build() ?? [];
54
+ anchor.after(...subnodes);
55
+ nodes.push(...subnodes);
56
+ });
57
+
58
+ return destroy;
59
+ }, [template]);
60
+
61
+ return nodes;
62
+ }),
63
+ );
64
+ };
65
+
66
+ /**
67
+ * `Else` indicates the `else` block for {@link If} and {@link ElseIf}.
68
+ */
69
+ export const Else: FunctionalComponent<{ children?: Children }> = ({
70
+ children,
71
+ }) => {
72
+ return ElseIf({ condition: true, children });
73
+ };
@@ -0,0 +1,25 @@
1
+ import { FunctionalComponent } from "../component.js";
2
+ import { runWithRenderer } from "../renderer.js";
3
+ import { useEffect } from "../scope.js";
4
+ import { createTemplate } from "../template.js";
5
+ import { Children, Fragment } from "./Fragment.js";
6
+
7
+ export const Portal: FunctionalComponent<{
8
+ mount: Node;
9
+ children?: Children;
10
+ }> = ({ mount, children }) =>
11
+ createTemplate(() =>
12
+ runWithRenderer({ _nodes: undefined }, () => {
13
+ const nodes = Fragment({ children }).build();
14
+
15
+ useEffect(() => {
16
+ nodes.forEach((node) => mount.appendChild(node));
17
+
18
+ return () => {
19
+ nodes.forEach((node) => node.parentNode?.removeChild(node));
20
+ };
21
+ });
22
+
23
+ return [];
24
+ }),
25
+ );
@@ -0,0 +1,120 @@
1
+ import { createElement } from "../create_element.js";
2
+ import { FunctionalComponent } from "../component.js";
3
+ import { Text } from "./Text.js";
4
+ import { MaybeSignal, useEffect } from "../scope.js";
5
+ import { Fragment } from "./Fragment.js";
6
+ import { useRenderer } from "../renderer.js";
7
+ import { Portal } from "./Portal.js";
8
+
9
+ type StyleSheetRegistry = Map<
10
+ string,
11
+ {
12
+ _sheet: CSSStyleSheet;
13
+ _refs: number;
14
+ }
15
+ >;
16
+
17
+ const styleSheetRegistrySym = Symbol("styleSheetRegistry");
18
+ const globalStyleSheetRegistry: StyleSheetRegistry = new Map();
19
+
20
+ const getLocalStyleSheetRegistry = <T>(
21
+ obj: T & { [styleSheetRegistrySym]?: StyleSheetRegistry },
22
+ ): StyleSheetRegistry => (obj[styleSheetRegistrySym] ??= new Map());
23
+
24
+ const useStyleSheet = (
25
+ registry: StyleSheetRegistry,
26
+ css: string,
27
+ cleanup: () => void,
28
+ ): CSSStyleSheet => {
29
+ if (!globalStyleSheetRegistry.has(css)) {
30
+ const sheet = new CSSStyleSheet();
31
+ sheet.replaceSync(css);
32
+ globalStyleSheetRegistry.set(css, {
33
+ _sheet: sheet,
34
+ _refs: 0,
35
+ });
36
+ }
37
+
38
+ const globalEntry = globalStyleSheetRegistry.get(css)!;
39
+ globalEntry._refs++;
40
+
41
+ if (!registry.has(css)) {
42
+ registry.set(css, {
43
+ _sheet: globalEntry._sheet,
44
+ _refs: 0,
45
+ });
46
+ }
47
+
48
+ const entry = registry.get(css)!;
49
+ entry._refs++;
50
+
51
+ useEffect(() => () => {
52
+ if (!--entry._refs) {
53
+ registry.delete(css);
54
+ cleanup();
55
+ }
56
+
57
+ if (!--globalEntry._refs) {
58
+ globalStyleSheetRegistry.delete(css);
59
+ }
60
+ });
61
+
62
+ return entry._sheet;
63
+ };
64
+
65
+ export const Style: FunctionalComponent<{
66
+ light?: boolean;
67
+ children?: MaybeSignal<string>;
68
+ }> = (props) => {
69
+ const css = props.children;
70
+
71
+ if (typeof css == "function") {
72
+ // Dynamic CSS will be inserted into the DOM as a <style> element.
73
+
74
+ const styleEl = createElement(
75
+ "style",
76
+ {},
77
+ Text({
78
+ text: css,
79
+ marker: false,
80
+ }),
81
+ );
82
+
83
+ return props.light
84
+ ? Portal({ mount: document.head, children: styleEl })
85
+ : styleEl;
86
+ }
87
+
88
+ // Static CSS will be inserted as an adopted stylesheet and cached.
89
+
90
+ if (css) {
91
+ const renderer = useRenderer();
92
+ const styleRoot = props.light
93
+ ? document
94
+ : renderer._component?.shadowRoot ?? document;
95
+ const registry = getLocalStyleSheetRegistry(styleRoot);
96
+
97
+ const sheet = useStyleSheet(registry, css, () => {
98
+ styleRoot.adoptedStyleSheets = styleRoot.adoptedStyleSheets.filter(
99
+ (s) => s != sheet,
100
+ );
101
+ });
102
+
103
+ styleRoot.adoptedStyleSheets.push(sheet);
104
+ }
105
+
106
+ return Fragment({});
107
+ };
108
+
109
+ export const css = (
110
+ strings: TemplateStringsArray,
111
+ ...values: MaybeSignal<string | number>[]
112
+ ): MaybeSignal<string> => {
113
+ const result = () =>
114
+ strings.reduce(
115
+ (acc, string, i) => acc + string + (MaybeSignal.get(values[i]) ?? ""),
116
+ "",
117
+ );
118
+
119
+ return values.some((value) => typeof value == "function") ? result : result();
120
+ };
@@ -0,0 +1,102 @@
1
+ import { DomProps, setAttr, setStyle } from "../dom.js";
2
+ import { jsxPropNameToEventName } from "../utils.js";
3
+ import { MaybeSignal, useBatch, useEffect, useScope } from "../scope.js";
4
+ import { Fragment } from "./Fragment.js";
5
+ import { runWithRenderer, useRenderer } from "../renderer.js";
6
+ import { createTemplate, Template } from "../template.js";
7
+
8
+ export const hydrateElement = <E extends HTMLElement | SVGElement>(
9
+ node: E,
10
+ svg: boolean,
11
+ props: DomProps<any>,
12
+ heuristic?: boolean,
13
+ ): E => {
14
+ const { ref, style, children, dangerouslySetInnerHTML, ...attrs } = props;
15
+
16
+ for (const name in style ?? {}) {
17
+ const value = style![name];
18
+ const signal = value as MaybeSignal<string | number | null | undefined>;
19
+
20
+ useEffect(() => {
21
+ setStyle(node, name, MaybeSignal.get(signal));
22
+ });
23
+ }
24
+
25
+ for (const name in attrs) {
26
+ const value = attrs[name as keyof typeof attrs];
27
+
28
+ if (name.startsWith("on")) {
29
+ // Register event
30
+
31
+ const s = useScope();
32
+ const listener = (evt: Event) => {
33
+ s._run(() => useBatch(() => (value as (evt: Event) => void)(evt)));
34
+ };
35
+
36
+ const eventName = jsxPropNameToEventName(name as `on${string}`);
37
+
38
+ useEffect(() => {
39
+ node.addEventListener(eventName, listener);
40
+ return () => node.removeEventListener(eventName, listener);
41
+ });
42
+ } else {
43
+ // Set attribute
44
+
45
+ useEffect(() => {
46
+ setAttr(node, name, MaybeSignal.get(value), heuristic);
47
+ });
48
+ }
49
+ }
50
+
51
+ if (dangerouslySetInnerHTML) {
52
+ useEffect(() => {
53
+ const html = MaybeSignal.get(dangerouslySetInnerHTML).__html;
54
+
55
+ if (node.innerHTML != html) {
56
+ node.innerHTML = html;
57
+ }
58
+ });
59
+ }
60
+
61
+ if (ref) {
62
+ useEffect(() => {
63
+ ref.set(node);
64
+ return () => ref.set(undefined);
65
+ });
66
+ }
67
+
68
+ if (props.children != null) {
69
+ node.append(
70
+ ...runWithRenderer(
71
+ {
72
+ _svg: svg,
73
+ _nodes: node.childNodes.values(),
74
+ },
75
+ () => Fragment({ children: props.children }).build(),
76
+ ),
77
+ );
78
+ }
79
+
80
+ return node;
81
+ };
82
+
83
+ export const TagComponent = (
84
+ tagName: string,
85
+ props: DomProps<any> = {},
86
+ ): Template =>
87
+ createTemplate(() => {
88
+ const renderer = useRenderer();
89
+ const svg = tagName == "svg" ? true : !!renderer._svg;
90
+ const node = hydrateElement(
91
+ renderer._node(() =>
92
+ !svg
93
+ ? document.createElement(tagName)
94
+ : document.createElementNS("http://www.w3.org/2000/svg", tagName),
95
+ ),
96
+ svg,
97
+ props,
98
+ true,
99
+ );
100
+
101
+ return [node];
102
+ });
@@ -0,0 +1,24 @@
1
+ import { MaybeSignal, useEffect, useMemo } from "../scope.js";
2
+ import { FunctionalComponent } from "../component.js";
3
+ import { useRenderer } from "../renderer.js";
4
+ import { createTemplate } from "../template.js";
5
+
6
+ export const Text: FunctionalComponent<{
7
+ text?: MaybeSignal<string | number | undefined | null>;
8
+ marker?: boolean;
9
+ }> = ({ text, marker }) =>
10
+ createTemplate(() => {
11
+ const renderer = useRenderer();
12
+ const anchor = marker && renderer._node(() => document.createComment(""));
13
+ const node = renderer._node(() => document.createTextNode(""));
14
+
15
+ useEffect(() => {
16
+ const textContent = "" + (MaybeSignal.get(text) ?? "");
17
+
18
+ if (node.textContent != textContent) {
19
+ node.textContent = textContent;
20
+ }
21
+ });
22
+
23
+ return anchor ? [anchor, node] : [node];
24
+ });
@@ -0,0 +1,5 @@
1
+ export { For } from "./For.js";
2
+ export { type Children, Fragment } from "./Fragment.js";
3
+ export { If, ElseIf, Else } from "./If.js";
4
+ export { Portal } from "./Portal.js";
5
+ export { Style, css } from "./Style.js";
@@ -0,0 +1,41 @@
1
+ import { createElement } from "../create_element.js";
2
+ import type { Template } from "../template.js";
3
+ import type { DomEventProps, DomIntrinsicElements, DomProps } from "../dom.js";
4
+ import type { jsxPropsSym } from "../component.js";
5
+
6
+ /** @ignore */
7
+ export const jsx = (
8
+ type: any,
9
+ props?: object & { key?: unknown },
10
+ key?: unknown,
11
+ ): Template => {
12
+ if (props && key != null) {
13
+ props.key = key;
14
+ }
15
+
16
+ return createElement(type, props);
17
+ };
18
+
19
+ /** @ignore */
20
+ export namespace JSX {
21
+ export type Element = Template;
22
+
23
+ export type ElementClass = Omit<HTMLElement, typeof jsxPropsSym>;
24
+
25
+ export interface ElementAttributesProperty {
26
+ [jsxPropsSym]: {};
27
+ }
28
+
29
+ export interface ElementChildrenAttribute {
30
+ children: {};
31
+ }
32
+
33
+ export type IntrinsicElements = DomIntrinsicElements;
34
+
35
+ export interface IntrinsicClassAttributes<T>
36
+ extends DomProps<T>,
37
+ DomEventProps<T> {}
38
+ }
39
+
40
+ export { Fragment } from "../intrinsic/Fragment.js";
41
+ export { jsx as jsxDEV, jsx as jsxs };
package/src/mod.ts ADDED
@@ -0,0 +1,37 @@
1
+ export {
2
+ type AttributeOptions,
3
+ Component,
4
+ type ComponentConstructor,
5
+ type ComponentOptions,
6
+ defineComponents,
7
+ event,
8
+ type EventConstructor,
9
+ type FunctionalComponent,
10
+ isComponent,
11
+ type Metadata,
12
+ prop,
13
+ type PropOptions,
14
+ useMountEffect as useEffect,
15
+ } from "./component.js";
16
+ export { type Context, createContext, useContext } from "./context.js";
17
+ export { createElement, h } from "./create_element.js";
18
+ export { DangerousHtml, Styles } from "./dom.js";
19
+ export { type Template } from "./template.js";
20
+ export {
21
+ type Cleanup,
22
+ MaybeSignal,
23
+ type SetSignalOptions,
24
+ type Signal,
25
+ type SignalLike,
26
+ type SignalSetter,
27
+ type SubscopeOptions,
28
+ type RefSignal,
29
+ type RefSignalSetter,
30
+ useBatch,
31
+ useMemo,
32
+ useSignal,
33
+ useRef,
34
+ } from "./scope.js";
35
+
36
+ export * from "./intrinsic/mod.js";
37
+ export * from "./jsx-runtime/mod.js";
@@ -0,0 +1,45 @@
1
+ import { Component } from "./component.js";
2
+ import { Signal, useEffect, useScope, useSubscope } from "./scope.js";
3
+
4
+ interface Renderer {
5
+ _component?: Component;
6
+ _svg?: boolean;
7
+ _nodes?: IterableIterator<Node>;
8
+ _ifConditions: Signal<boolean | undefined>[];
9
+
10
+ _node<N extends Node>(fallback: () => N): N;
11
+ }
12
+
13
+ type RendererOverrides = Partial<Omit<Renderer, "_node">>;
14
+
15
+ const createRenderer = (override: RendererOverrides = {}): Renderer => ({
16
+ _ifConditions: [],
17
+ _node<N extends Node>(fallback: () => N): N {
18
+ return (this._nodes?.next().value as N | undefined) ?? fallback();
19
+ },
20
+ ...override,
21
+ });
22
+
23
+ export const useRenderer = () => {
24
+ const scope = useScope<{ _renderer?: Renderer }>();
25
+ return (scope._details._renderer ??= createRenderer());
26
+ };
27
+
28
+ export const runWithRenderer = <T>(
29
+ override: RendererOverrides,
30
+ fn: () => T,
31
+ ): T => {
32
+ const currRenderer = useRenderer();
33
+ const _renderer = createRenderer({
34
+ ...currRenderer,
35
+ ...override,
36
+ });
37
+
38
+ const [result, destroy] = useSubscope(fn, {
39
+ details: { _renderer },
40
+ });
41
+
42
+ useEffect(() => destroy);
43
+
44
+ return result;
45
+ };