@fluixi/core 1.0.0-alpha.100

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 (253) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +334 -0
  3. package/dist/cdn/core.global.js +2 -0
  4. package/dist/cli/generate.cjs +5639 -0
  5. package/dist/cli/generate.d.ts +7 -0
  6. package/dist/cli/generate.d.ts.map +1 -0
  7. package/dist/cli/generate.js +2580 -0
  8. package/dist/cli/generate.mjs +5644 -0
  9. package/dist/cli/index.cjs +5639 -0
  10. package/dist/cli/index.d.ts +7 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +2580 -0
  13. package/dist/cli/index.mjs +5644 -0
  14. package/dist/cli/run.cjs +64 -0
  15. package/dist/cli/run.d.ts +3 -0
  16. package/dist/cli/run.d.ts.map +1 -0
  17. package/dist/cli/run.js +62 -0
  18. package/dist/cli/run.mjs +40 -0
  19. package/dist/fluixi.d.ts +5 -0
  20. package/dist/src/cdn/chunk-RKBFEFZP.mjs +987 -0
  21. package/dist/src/cdn/core.cjs +1592 -0
  22. package/dist/src/cdn/core.d.ts +16 -0
  23. package/dist/src/cdn/core.d.ts.map +1 -0
  24. package/dist/src/cdn/core.js +15 -0
  25. package/dist/src/cdn/core.mjs +714 -0
  26. package/dist/src/cdn/router-V2THK27H.mjs +74 -0
  27. package/dist/src/index.cjs +1711 -0
  28. package/dist/src/index.d.ts +4 -0
  29. package/dist/src/index.d.ts.map +1 -0
  30. package/dist/src/index.js +11 -0
  31. package/dist/src/index.mjs +1833 -0
  32. package/dist/src/jsx-dev-runtime.cjs +26 -0
  33. package/dist/src/jsx-dev-runtime.d.ts +2 -0
  34. package/dist/src/jsx-dev-runtime.d.ts.map +1 -0
  35. package/dist/src/jsx-dev-runtime.js +1 -0
  36. package/dist/src/jsx-dev-runtime.mjs +24 -0
  37. package/dist/src/jsx-runtime.cjs +26 -0
  38. package/dist/src/jsx-runtime.d.ts +7 -0
  39. package/dist/src/jsx-runtime.d.ts.map +1 -0
  40. package/dist/src/jsx-runtime.js +8 -0
  41. package/dist/src/jsx-runtime.mjs +24 -0
  42. package/dist/src/lib/client/chunk-FDOPDB6Z.mjs +53 -0
  43. package/dist/src/lib/client/index.cjs +1210 -0
  44. package/dist/src/lib/client/index.d.ts +51 -0
  45. package/dist/src/lib/client/index.d.ts.map +1 -0
  46. package/dist/src/lib/client/index.js +174 -0
  47. package/dist/src/lib/client/index.mjs +293 -0
  48. package/dist/src/lib/client/router-4PTBPZLQ.mjs +891 -0
  49. package/dist/src/lib/context/context.cjs +81 -0
  50. package/dist/src/lib/context/context.d.ts +73 -0
  51. package/dist/src/lib/context/context.d.ts.map +1 -0
  52. package/dist/src/lib/context/context.js +93 -0
  53. package/dist/src/lib/context/context.mjs +64 -0
  54. package/dist/src/lib/context/index.cjs +83 -0
  55. package/dist/src/lib/context/index.d.ts +3 -0
  56. package/dist/src/lib/context/index.d.ts.map +1 -0
  57. package/dist/src/lib/context/index.js +1 -0
  58. package/dist/src/lib/context/index.mjs +64 -0
  59. package/dist/src/lib/control-flow.cjs +35 -0
  60. package/dist/src/lib/control-flow.d.ts +4 -0
  61. package/dist/src/lib/control-flow.d.ts.map +1 -0
  62. package/dist/src/lib/control-flow.js +1 -0
  63. package/dist/src/lib/control-flow.mjs +25 -0
  64. package/dist/src/lib/core.cjs +1710 -0
  65. package/dist/src/lib/core.d.ts +91 -0
  66. package/dist/src/lib/core.d.ts.map +1 -0
  67. package/dist/src/lib/core.js +158 -0
  68. package/dist/src/lib/core.mjs +1833 -0
  69. package/dist/src/lib/env/index.cjs +44 -0
  70. package/dist/src/lib/env/index.d.ts +26 -0
  71. package/dist/src/lib/env/index.d.ts.map +1 -0
  72. package/dist/src/lib/env/index.js +31 -0
  73. package/dist/src/lib/env/index.mjs +23 -0
  74. package/dist/src/lib/i18n/index.cjs +123 -0
  75. package/dist/src/lib/i18n/index.d.ts +110 -0
  76. package/dist/src/lib/i18n/index.d.ts.map +1 -0
  77. package/dist/src/lib/i18n/index.js +164 -0
  78. package/dist/src/lib/i18n/index.mjs +103 -0
  79. package/dist/src/lib/index.cjs +598 -0
  80. package/dist/src/lib/index.d.ts +81 -0
  81. package/dist/src/lib/index.d.ts.map +1 -0
  82. package/dist/src/lib/index.js +130 -0
  83. package/dist/src/lib/index.mjs +688 -0
  84. package/dist/src/lib/isomorphic.cjs +90 -0
  85. package/dist/src/lib/isomorphic.d.ts +114 -0
  86. package/dist/src/lib/isomorphic.d.ts.map +1 -0
  87. package/dist/src/lib/isomorphic.js +132 -0
  88. package/dist/src/lib/isomorphic.mjs +69 -0
  89. package/dist/src/lib/jsx-runtime/index.cjs +21 -0
  90. package/dist/src/lib/jsx-runtime/index.d.ts +13 -0
  91. package/dist/src/lib/jsx-runtime/index.d.ts.map +1 -0
  92. package/dist/src/lib/jsx-runtime/index.js +13 -0
  93. package/dist/src/lib/jsx-runtime/index.mjs +4 -0
  94. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +21 -0
  95. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts +2 -0
  96. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.d.ts.map +1 -0
  97. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.js +1 -0
  98. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +4 -0
  99. package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +302 -0
  100. package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +35 -0
  101. package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -0
  102. package/dist/src/lib/plugins/fluixi-routes-plugin.js +118 -0
  103. package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +271 -0
  104. package/dist/src/lib/plugins/index.cjs +311 -0
  105. package/dist/src/lib/plugins/index.d.ts +6 -0
  106. package/dist/src/lib/plugins/index.d.ts.map +1 -0
  107. package/dist/src/lib/plugins/index.js +7 -0
  108. package/dist/src/lib/plugins/index.mjs +279 -0
  109. package/dist/src/lib/plugins/route-codegen.cjs +73 -0
  110. package/dist/src/lib/plugins/route-codegen.d.ts +12 -0
  111. package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -0
  112. package/dist/src/lib/plugins/route-codegen.js +80 -0
  113. package/dist/src/lib/plugins/route-codegen.mjs +52 -0
  114. package/dist/src/lib/plugins/route-scanner.cjs +189 -0
  115. package/dist/src/lib/plugins/route-scanner.d.ts +38 -0
  116. package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -0
  117. package/dist/src/lib/plugins/route-scanner.js +215 -0
  118. package/dist/src/lib/plugins/route-scanner.mjs +158 -0
  119. package/dist/src/lib/plugins/vite.cjs +27 -0
  120. package/dist/src/lib/plugins/vite.d.ts +3 -0
  121. package/dist/src/lib/plugins/vite.d.ts.map +1 -0
  122. package/dist/src/lib/plugins/vite.js +1 -0
  123. package/dist/src/lib/plugins/vite.mjs +6 -0
  124. package/dist/src/lib/render/await.cjs +38 -0
  125. package/dist/src/lib/render/await.d.ts +26 -0
  126. package/dist/src/lib/render/await.d.ts.map +1 -0
  127. package/dist/src/lib/render/await.js +45 -0
  128. package/dist/src/lib/render/await.mjs +17 -0
  129. package/dist/src/lib/render/component.cjs +221 -0
  130. package/dist/src/lib/render/component.d.ts +296 -0
  131. package/dist/src/lib/render/component.d.ts.map +1 -0
  132. package/dist/src/lib/render/component.js +514 -0
  133. package/dist/src/lib/render/component.mjs +214 -0
  134. package/dist/src/lib/render/deferred.cjs +175 -0
  135. package/dist/src/lib/render/deferred.d.ts +36 -0
  136. package/dist/src/lib/render/deferred.d.ts.map +1 -0
  137. package/dist/src/lib/render/deferred.js +141 -0
  138. package/dist/src/lib/render/deferred.mjs +158 -0
  139. package/dist/src/lib/render/index.cjs +573 -0
  140. package/dist/src/lib/render/index.d.ts +98 -0
  141. package/dist/src/lib/render/index.d.ts.map +1 -0
  142. package/dist/src/lib/render/index.js +180 -0
  143. package/dist/src/lib/render/index.mjs +590 -0
  144. package/dist/src/lib/render/lazy.cjs +67 -0
  145. package/dist/src/lib/render/lazy.d.ts +49 -0
  146. package/dist/src/lib/render/lazy.d.ts.map +1 -0
  147. package/dist/src/lib/render/lazy.js +105 -0
  148. package/dist/src/lib/render/lazy.mjs +50 -0
  149. package/dist/src/lib/render/suspense.cjs +141 -0
  150. package/dist/src/lib/render/suspense.d.ts +65 -0
  151. package/dist/src/lib/render/suspense.d.ts.map +1 -0
  152. package/dist/src/lib/render/suspense.js +196 -0
  153. package/dist/src/lib/render/suspense.mjs +136 -0
  154. package/dist/src/lib/render/versions.cjs +35 -0
  155. package/dist/src/lib/render/versions.d.ts +2 -0
  156. package/dist/src/lib/render/versions.d.ts.map +1 -0
  157. package/dist/src/lib/render/versions.js +21 -0
  158. package/dist/src/lib/render/versions.mjs +14 -0
  159. package/dist/src/lib/router/data.cjs +228 -0
  160. package/dist/src/lib/router/data.d.ts +142 -0
  161. package/dist/src/lib/router/data.d.ts.map +1 -0
  162. package/dist/src/lib/router/data.js +258 -0
  163. package/dist/src/lib/router/data.mjs +214 -0
  164. package/dist/src/lib/router/index.cjs +921 -0
  165. package/dist/src/lib/router/index.d.ts +274 -0
  166. package/dist/src/lib/router/index.d.ts.map +1 -0
  167. package/dist/src/lib/router/index.js +904 -0
  168. package/dist/src/lib/router/index.mjs +938 -0
  169. package/dist/src/lib/router/scroll.cjs +79 -0
  170. package/dist/src/lib/router/scroll.d.ts +19 -0
  171. package/dist/src/lib/router/scroll.d.ts.map +1 -0
  172. package/dist/src/lib/router/scroll.js +68 -0
  173. package/dist/src/lib/router/scroll.mjs +58 -0
  174. package/dist/src/lib/router/transition.cjs +75 -0
  175. package/dist/src/lib/router/transition.d.ts +81 -0
  176. package/dist/src/lib/router/transition.d.ts.map +1 -0
  177. package/dist/src/lib/router/transition.js +136 -0
  178. package/dist/src/lib/router/transition.mjs +54 -0
  179. package/dist/src/lib/server/hydration.cjs +169 -0
  180. package/dist/src/lib/server/hydration.d.ts +198 -0
  181. package/dist/src/lib/server/hydration.d.ts.map +1 -0
  182. package/dist/src/lib/server/hydration.js +345 -0
  183. package/dist/src/lib/server/hydration.mjs +148 -0
  184. package/dist/src/lib/server/index.cjs +406 -0
  185. package/dist/src/lib/server/index.d.ts +115 -0
  186. package/dist/src/lib/server/index.d.ts.map +1 -0
  187. package/dist/src/lib/server/index.js +164 -0
  188. package/dist/src/lib/server/index.mjs +384 -0
  189. package/dist/src/lib/server/reactive.cjs +197 -0
  190. package/dist/src/lib/server/reactive.d.ts +239 -0
  191. package/dist/src/lib/server/reactive.d.ts.map +1 -0
  192. package/dist/src/lib/server/reactive.js +413 -0
  193. package/dist/src/lib/server/reactive.mjs +176 -0
  194. package/dist/src/lib/storage/cache.cjs +51 -0
  195. package/dist/src/lib/storage/cache.d.ts +18 -0
  196. package/dist/src/lib/storage/cache.d.ts.map +1 -0
  197. package/dist/src/lib/storage/cache.js +42 -0
  198. package/dist/src/lib/storage/cache.mjs +30 -0
  199. package/dist/src/lib/storage/index.cjs +246 -0
  200. package/dist/src/lib/storage/index.d.ts +83 -0
  201. package/dist/src/lib/storage/index.d.ts.map +1 -0
  202. package/dist/src/lib/storage/index.js +179 -0
  203. package/dist/src/lib/storage/index.mjs +231 -0
  204. package/dist/src/lib/storage/persisted.cjs +91 -0
  205. package/dist/src/lib/storage/persisted.d.ts +53 -0
  206. package/dist/src/lib/storage/persisted.d.ts.map +1 -0
  207. package/dist/src/lib/storage/persisted.js +75 -0
  208. package/dist/src/lib/storage/persisted.mjs +70 -0
  209. package/dist/src/lib/theme/color-scheme.cjs +281 -0
  210. package/dist/src/lib/theme/color-scheme.d.ts +61 -0
  211. package/dist/src/lib/theme/color-scheme.d.ts.map +1 -0
  212. package/dist/src/lib/theme/color-scheme.js +120 -0
  213. package/dist/src/lib/theme/color-scheme.mjs +265 -0
  214. package/dist/src/lib/theme/index.cjs +267 -0
  215. package/dist/src/lib/theme/index.d.ts +78 -0
  216. package/dist/src/lib/theme/index.d.ts.map +1 -0
  217. package/dist/src/lib/theme/index.js +135 -0
  218. package/dist/src/lib/theme/index.mjs +251 -0
  219. package/dist/src/lib/theme/provider.cjs +346 -0
  220. package/dist/src/lib/theme/provider.d.ts +34 -0
  221. package/dist/src/lib/theme/provider.d.ts.map +1 -0
  222. package/dist/src/lib/theme/provider.js +60 -0
  223. package/dist/src/lib/theme/provider.mjs +332 -0
  224. package/dist/src/lib/utils/index.cjs +209 -0
  225. package/dist/src/lib/utils/index.d.ts +286 -0
  226. package/dist/src/lib/utils/index.d.ts.map +1 -0
  227. package/dist/src/lib/utils/index.js +434 -0
  228. package/dist/src/lib/utils/index.mjs +188 -0
  229. package/dist/src/routes.cjs +37 -0
  230. package/dist/src/routes.d.ts +22 -0
  231. package/dist/src/routes.d.ts.map +1 -0
  232. package/dist/src/routes.js +20 -0
  233. package/dist/src/routes.mjs +17 -0
  234. package/dist/src/server.cjs +405 -0
  235. package/dist/src/server.d.ts +63 -0
  236. package/dist/src/server.d.ts.map +1 -0
  237. package/dist/src/server.js +62 -0
  238. package/dist/src/server.mjs +382 -0
  239. package/dist/src/utils.cjs +21 -0
  240. package/dist/src/utils.d.ts +10 -0
  241. package/dist/src/utils.d.ts.map +1 -0
  242. package/dist/src/utils.js +9 -0
  243. package/dist/src/utils.mjs +4 -0
  244. package/dist/src/version.generated.cjs +26 -0
  245. package/dist/src/version.generated.d.ts +3 -0
  246. package/dist/src/version.generated.d.ts.map +1 -0
  247. package/dist/src/version.generated.js +3 -0
  248. package/dist/src/version.generated.mjs +5 -0
  249. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  250. package/package.json +164 -0
  251. package/src/index.ts +19 -0
  252. package/types/fluixi.d.ts +5 -0
  253. package/types/vinxi.d.ts +50 -0
@@ -0,0 +1,514 @@
1
+ /**
2
+ * Component creation for Fluixi. Component-creation logic only: context lives in
3
+ * the reactive system, rendering in render/index.ts, and SSR in server/.
4
+ */
5
+ import { isServer } from '@fluixi/dom';
6
+ import { createComponent as domCreateComponent, asComponent, } from '@fluixi/dom';
7
+ import { untrack, onCleanup, getOwner, createEffect, createMemo, createSignal, isSignal, batch, runWithOwner, isOwnerDisposed, } from '@fluixi/reactive/signal';
8
+ const sharedConfig = {
9
+ context: null,
10
+ owner: null,
11
+ };
12
+ /**
13
+ * Get shared reactive config
14
+ * Used for maintaining reactive context across components
15
+ * @internal
16
+ */
17
+ export function getSharedConfig() {
18
+ return sharedConfig;
19
+ }
20
+ /**
21
+ * Set current owner in shared config
22
+ * @internal
23
+ */
24
+ export function setOwner(owner) {
25
+ sharedConfig.owner = owner;
26
+ sharedConfig.context = owner;
27
+ }
28
+ /**
29
+ * Get current owner from shared config
30
+ * @internal
31
+ */
32
+ export function getContextOwner() {
33
+ return sharedConfig.context || sharedConfig.owner;
34
+ }
35
+ // ============================================================================
36
+ // GLOBAL STATE
37
+ // ============================================================================
38
+ /**
39
+ * Development mode flag
40
+ */
41
+ export const DEV = typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
42
+ // ============================================================================
43
+ // OWNER-BASED LIFECYCLE COORDINATION
44
+ // ============================================================================
45
+ // Every component has a reactive Owner (from createRoot).
46
+ // onMount and useEffect attach callbacks to the Owner via a Symbol-keyed
47
+ // property. A single queueMicrotask per Owner flushes them in order:
48
+ // 1. All onMount callbacks run first
49
+ // 2. Then all useEffect callbacks are created as reactive effects
50
+ // This works regardless of whether the component goes through $component,
51
+ // the JSX runtime, or is called directly, as long as there's an Owner.
52
+ const _LIFECYCLE = Symbol.for('fluixi-lifecycle');
53
+ /**
54
+ * Get or create a lifecycle bucket on the current Owner.
55
+ * Returns null if there is no reactive Owner.
56
+ */
57
+ function getOwnerLifecycle() {
58
+ const owner = getOwner();
59
+ if (!owner)
60
+ return null;
61
+ if (!owner[_LIFECYCLE]) {
62
+ const lc = { mounts: [], effects: [], scheduled: false };
63
+ owner[_LIFECYCLE] = lc;
64
+ }
65
+ return owner[_LIFECYCLE];
66
+ }
67
+ /**
68
+ * Schedule a microtask to flush the lifecycle for the current Owner.
69
+ * Only schedules once per Owner: subsequent calls are no-ops.
70
+ */
71
+ function scheduleLifecycleFlush(lc) {
72
+ if (lc.scheduled)
73
+ return;
74
+ // The flush is a microtask, so on the server it lands after renderToString has
75
+ // returned and reaches for a DOM that is not there.
76
+ if (isServer()) {
77
+ lc.mounts = [];
78
+ lc.effects = [];
79
+ return;
80
+ }
81
+ lc.scheduled = true;
82
+ // Capture the owner now; by microtask time it will have been restored.
83
+ const owner = getOwner();
84
+ queueMicrotask(() => {
85
+ // The scope may have been torn down before this microtask ran (e.g. a <Portal>
86
+ // inside a <Show> that closed synchronously). Running mount/effect callbacks on a
87
+ // dead owner would re-attach listeners/effects whose cleanup never fires, a leak.
88
+ // Match Solid: an unmounted component's onMount simply does not run.
89
+ if (isOwnerDisposed(owner)) {
90
+ lc.mounts = [];
91
+ lc.effects = [];
92
+ return;
93
+ }
94
+ runWithOwner(owner, () => {
95
+ // 1. Run ALL onMount callbacks first
96
+ for (const cb of lc.mounts) {
97
+ try {
98
+ const cleanup = cb();
99
+ if (typeof cleanup === 'function') {
100
+ onCleanup(cleanup);
101
+ }
102
+ }
103
+ catch (e) {
104
+ console.error('Error in onMount callback:', e);
105
+ }
106
+ }
107
+ // 2. THEN create effects for useEffect callbacks
108
+ for (const cb of lc.effects) {
109
+ let cleanup;
110
+ createEffect(() => {
111
+ if (cleanup)
112
+ cleanup();
113
+ cleanup = cb();
114
+ });
115
+ onCleanup(() => {
116
+ if (cleanup)
117
+ cleanup();
118
+ });
119
+ }
120
+ // Clear
121
+ lc.mounts = [];
122
+ lc.effects = [];
123
+ });
124
+ });
125
+ }
126
+ /**
127
+ * The runtime creates components itself now, so nothing has to be registered for
128
+ * it to work and this does nothing. It stays because callers exist, and because
129
+ * `registerCreateComponent` is still the way to install a different component
130
+ * model: pass your own implementation there and the runtime uses it instead.
131
+ *
132
+ * Do not register a function that calls the runtime's own `createComponent`: it
133
+ * consults the slot first, so that recurses.
134
+ */
135
+ export function ensureIntegration(_from) { }
136
+ /**
137
+ * Create and execute a component once. Opens an Owner, runs the component untracked,
138
+ * captures its output, keeps children lazy, propagates context (via sharedConfig), and
139
+ * attaches cleanup. Components execute once and never re-execute.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const MyComponent = (props: { name: string }) => {
144
+ * return html`<div>Hello ${props.name}</div>`;
145
+ * };
146
+ *
147
+ * const result = createComponent(MyComponent, { name: 'World' });
148
+ * ```
149
+ */
150
+ export function createComponent(Comp, props) {
151
+ return domCreateComponent(Comp, props);
152
+ }
153
+ const cached = new WeakMap();
154
+ export function $component(value, props) {
155
+ // if(isComponent(value) || isRoutes(value) || isRouter(value)) {
156
+ // // if(!cached.has(value)){
157
+ // // cached.set(value, ()=>untrack(()=>value(props)))
158
+ // // }else{
159
+ // // console.log('skipp track', value)
160
+ // // }
161
+ // // return cached.get(value)()
162
+ // return untrack(() => value(props))
163
+ // }
164
+ const component = (typeof value === 'function')
165
+ ? untrack(() => value(props))
166
+ : value;
167
+ // Only stamp $name and $COMPONENT on functions/objects: primitives (strings, numbers)
168
+ // cannot have properties added in strict mode and would throw TypeError.
169
+ // Also skip stamping reactive values (signals/memos) with $COMPONENT so the DOM
170
+ // runtime doesn't misidentify them as static component thunks and cache them.
171
+ const nameable = component != null && (typeof component === 'function' || typeof component === 'object');
172
+ // As in the dom integration: the name goes on a reactive return value too, since only
173
+ // `asComponent` has to avoid one.
174
+ if (nameable) {
175
+ try {
176
+ component['$name'] = value.name;
177
+ }
178
+ catch { }
179
+ }
180
+ if (nameable && !isSignal(component)) {
181
+ // Always mark with asComponent so isComponent() works reliably in both dev and
182
+ // production browser environments.
183
+ return asComponent(component);
184
+ }
185
+ return component;
186
+ }
187
+ // ============================================================================
188
+ // LIFECYCLE HOOKS
189
+ // ============================================================================
190
+ /**
191
+ * Register a callback to run when component mounts
192
+ *
193
+ * SolidJS-aligned: uses createEffect to schedule the callback.
194
+ * The effect runs once after the reactive graph is established,
195
+ * tied to the current Owner for automatic cleanup.
196
+ *
197
+ * @example
198
+ * ```typescript
199
+ * const MyComponent = () => {
200
+ * onMount(() => {
201
+ * console.log('Component mounted');
202
+ * return () => console.log('Cleanup');
203
+ * });
204
+ *
205
+ * return html`<div>Content</div>`;
206
+ * };
207
+ * ```
208
+ */
209
+ export function onMount(fn, reactive = true) {
210
+ if (isServer())
211
+ return;
212
+ const lc = getOwnerLifecycle();
213
+ if (lc) {
214
+ if (!reactive) {
215
+ if (!lc.scheduled) {
216
+ fn();
217
+ }
218
+ return;
219
+ }
220
+ // Attach to Owner: will run before useEffect callbacks in the flush.
221
+ lc.mounts.push(fn);
222
+ scheduleLifecycleFlush(lc);
223
+ }
224
+ else {
225
+ if (!reactive) {
226
+ fn();
227
+ return;
228
+ }
229
+ // No Owner (rare). Fall back to SolidJS pattern.
230
+ createEffect(() => {
231
+ const cleanup = untrack(fn);
232
+ if (typeof cleanup === 'function') {
233
+ onCleanup(cleanup);
234
+ }
235
+ });
236
+ }
237
+ }
238
+ /**
239
+ * Register a callback to run when component unmounts
240
+ *
241
+ * SolidJS-aligned: directly delegates to onCleanup which registers
242
+ * with the current reactive Owner for automatic disposal.
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * const MyComponent = () => {
247
+ * onUnmount(() => {
248
+ * console.log('Component is unmounting');
249
+ * });
250
+ *
251
+ * return html`<div>Content</div>`;
252
+ * };
253
+ * ```
254
+ */
255
+ export function onUnmount(fn) {
256
+ onCleanup(fn);
257
+ }
258
+ /**
259
+ * Register a cleanup function
260
+ *
261
+ * Component constructs reactive graph including cleanup
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * const MyComponent = () => {
266
+ * const timer = setInterval(() => console.log('tick'), 1000);
267
+ *
268
+ * onCleanup(() => clearInterval(timer));
269
+ *
270
+ * return html`<div>Timer running</div>`;
271
+ * };
272
+ * ```
273
+ */
274
+ export { onCleanup };
275
+ // ============================================================================
276
+ // HOOKS
277
+ // ============================================================================
278
+ /**
279
+ * Create a reactive effect
280
+ *
281
+ * Effects run when their dependencies change
282
+ * On server, effects run once
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * const MyComponent = () => {
287
+ * const [count, setCount] = createSignal(0);
288
+ *
289
+ * useEffect(() => {
290
+ * console.log('Count changed:', count());
291
+ * });
292
+ *
293
+ * return html`<button @click=${() => setCount(count() + 1)}>Click</button>`;
294
+ * };
295
+ * ```
296
+ */
297
+ export function useEffect(callback) {
298
+ const lc = getOwnerLifecycle();
299
+ if (lc) {
300
+ // Attach to the Owner: created as an effect after all onMount callbacks.
301
+ lc.effects.push(callback);
302
+ scheduleLifecycleFlush(lc);
303
+ }
304
+ else {
305
+ // No Owner (rare). Create the effect immediately.
306
+ let cleanup;
307
+ createEffect(() => {
308
+ if (cleanup)
309
+ cleanup();
310
+ cleanup = callback();
311
+ });
312
+ onCleanup(() => {
313
+ if (cleanup)
314
+ cleanup();
315
+ });
316
+ }
317
+ }
318
+ /**
319
+ * Create a memoized computed value
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * const MyComponent = () => {
324
+ * const [count, setCount] = createSignal(0);
325
+ * const doubled = useMemo(() => count() * 2);
326
+ *
327
+ * return html`<div>Doubled: ${doubled()}</div>`;
328
+ * };
329
+ * ```
330
+ */
331
+ export function useMemo(fn) {
332
+ // createMemo is natively reactive and disposed automatically
333
+ // when its Owner is cleaned up. No lifecycle stack needed.
334
+ return createMemo(fn);
335
+ }
336
+ /**
337
+ * Create a ref object
338
+ *
339
+ * Refs persist across component re-renders (though components only run once)
340
+ * Useful for storing DOM references and mutable values
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * const MyComponent = () => {
345
+ * const inputRef = useRef<HTMLInputElement>(null);
346
+ *
347
+ * onMount(() => {
348
+ * inputRef.current?.focus();
349
+ * });
350
+ *
351
+ * return html`<input ref=${inputRef} />`;
352
+ * };
353
+ * ```
354
+ */
355
+ export function useRef(initialValue) {
356
+ const ref = { current: initialValue };
357
+ // Register cleanup to clear ref
358
+ onCleanup(() => {
359
+ ref.current = null;
360
+ });
361
+ return ref;
362
+ }
363
+ /**
364
+ * Create a reducer for state management
365
+ *
366
+ * @example
367
+ * ```typescript
368
+ * type Action = { type: 'increment' } | { type: 'decrement' };
369
+ *
370
+ * const MyComponent = () => {
371
+ * const [state, dispatch] = useReducer(
372
+ * (state: number, action: Action) => {
373
+ * switch (action.type) {
374
+ * case 'increment': return state + 1;
375
+ * case 'decrement': return state - 1;
376
+ * }
377
+ * },
378
+ * 0
379
+ * );
380
+ *
381
+ * return html`
382
+ * <button @click=${() => dispatch({ type: 'increment' })}>
383
+ * Count: ${state()}
384
+ * </button>
385
+ * `;
386
+ * };
387
+ * ```
388
+ */
389
+ export function useReducer(reducer, initialState) {
390
+ const [state, setState] = createSignal(initialState);
391
+ const dispatch = (action) => {
392
+ setState(reducer(state(), action));
393
+ };
394
+ return [state, dispatch];
395
+ }
396
+ /**
397
+ * Attach a listener for as long as the current owner lives.
398
+ *
399
+ * Runs through `useEffect`, so the removal it returns is called on disposal and before
400
+ * any re-run. Nothing has to be undone by hand. `target` may be a ref, read when the
401
+ * listener is attached rather than when this is called, so it works for an element the
402
+ * template has not created yet.
403
+ *
404
+ * @example
405
+ * ```ts
406
+ * useEventListener(window, 'resize', () => setWidth(window.innerWidth));
407
+ * useEventListener(dialogRef, 'keydown', onKey, { capture: true });
408
+ * ```
409
+ */
410
+ export function useEventListener(target, event, handler, options) {
411
+ useEffect(() => {
412
+ // Resolve target from ref or direct value
413
+ const element = target && 'current' in target
414
+ ? target.current
415
+ : target;
416
+ if (!element)
417
+ return;
418
+ // Attach event listener
419
+ element.addEventListener(event, handler, options);
420
+ // Return cleanup function
421
+ return () => {
422
+ element.removeEventListener(event, handler, options);
423
+ };
424
+ });
425
+ }
426
+ /**
427
+ * Create a callback function (memoization optimization)
428
+ *
429
+ * In Fluixi, components only run once, so this is mostly for
430
+ * API compatibility and explicit intent
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * const MyComponent = () => {
435
+ * const handleClick = useCallback(() => {
436
+ * console.log('Clicked');
437
+ * });
438
+ *
439
+ * return html`<button @click=${handleClick}>Click</button>`;
440
+ * };
441
+ * ```
442
+ */
443
+ export function useCallback(callback) {
444
+ // Components execute once, so callback is already stable
445
+ // This is mainly for API compatibility with React-like patterns
446
+ return callback;
447
+ }
448
+ /**
449
+ * Batch update helper
450
+ * Re-exported for convenience
451
+ */
452
+ export { batch };
453
+ /**
454
+ * Create a memo
455
+ * Re-exported for convenience
456
+ */
457
+ export { createMemo };
458
+ /**
459
+ * Create an effect
460
+ * Re-exported for convenience
461
+ */
462
+ export { createEffect };
463
+ /**
464
+ * Get current reactive owner
465
+ * Re-exported for convenience
466
+ */
467
+ export { getOwner };
468
+ /**
469
+ * Run with specific owner
470
+ * Re-exported for convenience
471
+ */
472
+ export { runWithOwner };
473
+ // ============================================================================
474
+ // HELPER FUNCTIONS (INTERNAL)
475
+ // ============================================================================
476
+ /**
477
+ * Helper to check if we're in a component context
478
+ * @internal
479
+ */
480
+ export function hasLifecycleContext() {
481
+ return getOwner() !== null;
482
+ }
483
+ // ============================================================================
484
+ // DEV MODE UTILITIES
485
+ // ============================================================================
486
+ /**
487
+ * Check if in development mode
488
+ *
489
+ * @example
490
+ * ```typescript
491
+ * if (isDev()) {
492
+ * console.log('Running in development mode');
493
+ * }
494
+ * ```
495
+ */
496
+ export function isDev() {
497
+ // return false
498
+ return DEV;
499
+ }
500
+ /**
501
+ * Development-only code execution
502
+ *
503
+ * @example
504
+ * ```typescript
505
+ * devOnly(() => {
506
+ * console.log('This only runs in development');
507
+ * });
508
+ * ```
509
+ */
510
+ export function devOnly(fn) {
511
+ if (DEV) {
512
+ fn();
513
+ }
514
+ }
@@ -0,0 +1,214 @@
1
+ // src/lib/render/component.ts
2
+ import { isServer } from "@fluixi/dom";
3
+ import {
4
+ createComponent as domCreateComponent,
5
+ asComponent
6
+ } from "@fluixi/dom";
7
+ import {
8
+ untrack,
9
+ onCleanup,
10
+ getOwner,
11
+ createEffect,
12
+ createMemo,
13
+ createSignal,
14
+ isSignal,
15
+ batch,
16
+ runWithOwner,
17
+ isOwnerDisposed
18
+ } from "@fluixi/reactive/signal";
19
+ var sharedConfig = {
20
+ context: null,
21
+ owner: null
22
+ };
23
+ function getSharedConfig() {
24
+ return sharedConfig;
25
+ }
26
+ function setOwner(owner) {
27
+ sharedConfig.owner = owner;
28
+ sharedConfig.context = owner;
29
+ }
30
+ function getContextOwner() {
31
+ return sharedConfig.context || sharedConfig.owner;
32
+ }
33
+ var DEV = typeof process !== "undefined" && false;
34
+ var _LIFECYCLE = /* @__PURE__ */ Symbol.for("fluixi-lifecycle");
35
+ function getOwnerLifecycle() {
36
+ const owner = getOwner();
37
+ if (!owner) return null;
38
+ if (!owner[_LIFECYCLE]) {
39
+ const lc = { mounts: [], effects: [], scheduled: false };
40
+ owner[_LIFECYCLE] = lc;
41
+ }
42
+ return owner[_LIFECYCLE];
43
+ }
44
+ function scheduleLifecycleFlush(lc) {
45
+ if (lc.scheduled) return;
46
+ if (isServer()) {
47
+ lc.mounts = [];
48
+ lc.effects = [];
49
+ return;
50
+ }
51
+ lc.scheduled = true;
52
+ const owner = getOwner();
53
+ queueMicrotask(() => {
54
+ if (isOwnerDisposed(owner)) {
55
+ lc.mounts = [];
56
+ lc.effects = [];
57
+ return;
58
+ }
59
+ runWithOwner(owner, () => {
60
+ for (const cb of lc.mounts) {
61
+ try {
62
+ const cleanup = cb();
63
+ if (typeof cleanup === "function") {
64
+ onCleanup(cleanup);
65
+ }
66
+ } catch (e) {
67
+ console.error("Error in onMount callback:", e);
68
+ }
69
+ }
70
+ for (const cb of lc.effects) {
71
+ let cleanup;
72
+ createEffect(() => {
73
+ if (cleanup) cleanup();
74
+ cleanup = cb();
75
+ });
76
+ onCleanup(() => {
77
+ if (cleanup) cleanup();
78
+ });
79
+ }
80
+ lc.mounts = [];
81
+ lc.effects = [];
82
+ });
83
+ });
84
+ }
85
+ function ensureIntegration(_from) {
86
+ }
87
+ function createComponent(Comp, props) {
88
+ return domCreateComponent(Comp, props);
89
+ }
90
+ function $component(value, props) {
91
+ const component = typeof value === "function" ? untrack(() => value(props)) : value;
92
+ const nameable = component != null && (typeof component === "function" || typeof component === "object");
93
+ if (nameable) {
94
+ try {
95
+ component["$name"] = value.name;
96
+ } catch {
97
+ }
98
+ }
99
+ if (nameable && !isSignal(component)) {
100
+ return asComponent(component);
101
+ }
102
+ return component;
103
+ }
104
+ function onMount(fn, reactive = true) {
105
+ if (isServer()) return;
106
+ const lc = getOwnerLifecycle();
107
+ if (lc) {
108
+ if (!reactive) {
109
+ if (!lc.scheduled) {
110
+ fn();
111
+ }
112
+ return;
113
+ }
114
+ lc.mounts.push(fn);
115
+ scheduleLifecycleFlush(lc);
116
+ } else {
117
+ if (!reactive) {
118
+ fn();
119
+ return;
120
+ }
121
+ createEffect(() => {
122
+ const cleanup = untrack(fn);
123
+ if (typeof cleanup === "function") {
124
+ onCleanup(cleanup);
125
+ }
126
+ });
127
+ }
128
+ }
129
+ function onUnmount(fn) {
130
+ onCleanup(fn);
131
+ }
132
+ function useEffect(callback) {
133
+ const lc = getOwnerLifecycle();
134
+ if (lc) {
135
+ lc.effects.push(callback);
136
+ scheduleLifecycleFlush(lc);
137
+ } else {
138
+ let cleanup;
139
+ createEffect(() => {
140
+ if (cleanup) cleanup();
141
+ cleanup = callback();
142
+ });
143
+ onCleanup(() => {
144
+ if (cleanup) cleanup();
145
+ });
146
+ }
147
+ }
148
+ function useMemo(fn) {
149
+ return createMemo(fn);
150
+ }
151
+ function useRef(initialValue) {
152
+ const ref = { current: initialValue };
153
+ onCleanup(() => {
154
+ ref.current = null;
155
+ });
156
+ return ref;
157
+ }
158
+ function useReducer(reducer, initialState) {
159
+ const [state, setState] = createSignal(initialState);
160
+ const dispatch = (action) => {
161
+ setState(reducer(state(), action));
162
+ };
163
+ return [state, dispatch];
164
+ }
165
+ function useEventListener(target, event, handler, options) {
166
+ useEffect(() => {
167
+ const element = target && "current" in target ? target.current : target;
168
+ if (!element) return;
169
+ element.addEventListener(event, handler, options);
170
+ return () => {
171
+ element.removeEventListener(event, handler, options);
172
+ };
173
+ });
174
+ }
175
+ function useCallback(callback) {
176
+ return callback;
177
+ }
178
+ function hasLifecycleContext() {
179
+ return getOwner() !== null;
180
+ }
181
+ function isDev() {
182
+ return DEV;
183
+ }
184
+ function devOnly(fn) {
185
+ if (DEV) {
186
+ fn();
187
+ }
188
+ }
189
+ export {
190
+ $component,
191
+ DEV,
192
+ batch,
193
+ createComponent,
194
+ createEffect,
195
+ createMemo,
196
+ devOnly,
197
+ ensureIntegration,
198
+ getContextOwner,
199
+ getOwner,
200
+ getSharedConfig,
201
+ hasLifecycleContext,
202
+ isDev,
203
+ onCleanup,
204
+ onMount,
205
+ onUnmount,
206
+ runWithOwner,
207
+ setOwner,
208
+ useCallback,
209
+ useEffect,
210
+ useEventListener,
211
+ useMemo,
212
+ useReducer,
213
+ useRef
214
+ };