fusee-framework 1.5.3 → 1.7.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 (115) hide show
  1. package/README.md +30 -13
  2. package/app/benchmark/benchmark.js +633 -0
  3. package/app/benchmark/index.html +102 -0
  4. package/app/benchmark/styles.css +249 -0
  5. package/app/components/Counter.js +17 -7
  6. package/app/components/Counter.ts +1 -1
  7. package/app/components/CounterSection.js +3 -4
  8. package/app/main.js +29 -62
  9. package/app/main.ts +72 -63
  10. package/app/pages/404.js +16 -0
  11. package/app/pages/[...all].js +35 -0
  12. package/app/pages/[...all].ts +35 -0
  13. package/app/pages/_layout.js +26 -0
  14. package/app/pages/_layout.ts +26 -0
  15. package/app/pages/about.js +23 -0
  16. package/app/pages/about.ts +29 -0
  17. package/app/pages/composables.js +158 -0
  18. package/app/pages/composables.ts +158 -0
  19. package/app/pages/dynamic.js +40 -0
  20. package/app/pages/dynamic.ts +40 -0
  21. package/app/pages/index.js +117 -0
  22. package/app/pages/index.ts +78 -0
  23. package/app/pages/login.ts +21 -0
  24. package/app/pages/objects/[id].js +56 -0
  25. package/app/pages/objects/[id].ts +56 -0
  26. package/app/pages/posts/[category]/[slug].js +56 -0
  27. package/app/pages/posts/[category]/[slug].ts +56 -0
  28. package/app/pages/router-example.js +56 -0
  29. package/app/pages/router-example.ts +56 -0
  30. package/app/pages/test-for.js +65 -0
  31. package/app/pages/test-for.ts +80 -0
  32. package/app/pages/users/[id].js +24 -0
  33. package/app/pages/users/[id].ts +46 -0
  34. package/app/pages/users/index.js +22 -0
  35. package/app/pages/users/index.ts +29 -0
  36. package/app/pages/users.js +43 -0
  37. package/app/pages/users.ts +78 -0
  38. package/app/routes/About.js +1 -1
  39. package/app/routes/About.ts +1 -1
  40. package/app/routes/AuthRoutes.ts +41 -0
  41. package/app/routes/Home.js +9 -0
  42. package/app/routes/NotFound.js +16 -0
  43. package/app/routes/NotFound.ts +16 -0
  44. package/app/routes/RouterExample.js +56 -0
  45. package/app/routes/RouterExample.ts +56 -0
  46. package/app/routes/WildcardDemo.js +35 -0
  47. package/app/routes/WildcardDemo.ts +35 -0
  48. package/auto-imports.d.ts +1 -0
  49. package/bin/create-fusee-linux +0 -0
  50. package/bin/create-fusee-mac-arm64 +0 -0
  51. package/bin/create-fusee-mac-intel +0 -0
  52. package/bin/create-fusee-win.exe +0 -0
  53. package/bin/create-fusee.exe +0 -0
  54. package/bin/index.js +38 -0
  55. package/create-fusee-go/cmd/generate.go +72 -0
  56. package/create-fusee-go/cmd/init.go +38 -0
  57. package/create-fusee-go/cmd/root.go +121 -0
  58. package/create-fusee-go/go.mod +10 -0
  59. package/create-fusee-go/go.sum +10 -0
  60. package/create-fusee-go/internal/assets/assets.go +78 -0
  61. package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +9 -0
  62. package/create-fusee-go/internal/assets/embed/framework/core/compiler.js +253 -0
  63. package/create-fusee-go/internal/assets/embed/framework/core/component.js +266 -0
  64. package/create-fusee-go/internal/assets/embed/framework/core/composable.js +27 -0
  65. package/create-fusee-go/internal/assets/embed/framework/core/directives.js +875 -0
  66. package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +153 -0
  67. package/create-fusee-go/internal/assets/embed/framework/core/event-delegation.js +191 -0
  68. package/create-fusee-go/internal/assets/embed/framework/core/signal.js +412 -0
  69. package/create-fusee-go/internal/assets/embed/framework/core/store.js +81 -0
  70. package/create-fusee-go/internal/assets/embed/framework/index.d.ts +3 -0
  71. package/create-fusee-go/internal/assets/embed/framework/index.js +10 -0
  72. package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +177 -0
  73. package/create-fusee-go/internal/assets/embed/framework/router/router.js +462 -0
  74. package/create-fusee-go/internal/assets/embed/framework/types/compiler.d.ts +15 -0
  75. package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +88 -0
  76. package/create-fusee-go/internal/assets/embed/framework/types/composable.d.ts +14 -0
  77. package/create-fusee-go/internal/assets/embed/framework/types/directives.d.ts +55 -0
  78. package/create-fusee-go/internal/assets/embed/framework/types/evaluator.d.ts +13 -0
  79. package/create-fusee-go/internal/assets/embed/framework/types/event-delegation.d.ts +32 -0
  80. package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +22 -0
  81. package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +32 -0
  82. package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +79 -0
  83. package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +8 -0
  84. package/create-fusee-go/internal/assets/templates/Counter.tmpl +26 -0
  85. package/create-fusee-go/internal/assets/templates/component.tmpl +11 -0
  86. package/create-fusee-go/internal/assets/templates/components/Loading.tmpl +24 -0
  87. package/create-fusee-go/internal/assets/templates/index.html.tmpl +15 -0
  88. package/create-fusee-go/internal/assets/templates/main.tmpl +14 -0
  89. package/create-fusee-go/internal/assets/templates/package.json.tmpl +14 -0
  90. package/create-fusee-go/internal/assets/templates/page.tmpl +12 -0
  91. package/create-fusee-go/internal/assets/templates/pages/about.tmpl +20 -0
  92. package/create-fusee-go/internal/assets/templates/pages/index.tmpl +22 -0
  93. package/create-fusee-go/internal/assets/templates/pages/layout.tmpl +18 -0
  94. package/create-fusee-go/internal/assets/templates/tsconfig.json.tmpl +14 -0
  95. package/create-fusee-go/internal/assets/templates/vite.config.js.tmpl +18 -0
  96. package/create-fusee-go/main.go +7 -0
  97. package/framework/auto-import-preset.js +1 -1
  98. package/framework/core/compiler.js +252 -249
  99. package/framework/core/component.js +265 -260
  100. package/framework/core/directives.js +875 -632
  101. package/framework/core/evaluator.js +153 -70
  102. package/framework/core/event-delegation.js +191 -0
  103. package/framework/core/signal.js +412 -404
  104. package/framework/index.js +10 -11
  105. package/framework/router/file-router.js +177 -0
  106. package/framework/router/router.js +462 -80
  107. package/framework/types/component.d.ts +0 -4
  108. package/framework/types/event-delegation.d.ts +32 -0
  109. package/framework/types/index.d.ts +3 -0
  110. package/framework/types/router.d.ts +32 -15
  111. package/framework/types/signal.d.ts +1 -0
  112. package/index.html +638 -563
  113. package/package.json +5 -3
  114. package/tsconfig.json +1 -0
  115. /package/{framework → create-fusee-go/internal/assets/embed/framework}/bin/create-fusee.js +0 -0
@@ -0,0 +1,462 @@
1
+ import { signal } from '../core/signal.js'
2
+
3
+ export const currentRoute = signal('/')
4
+ export const routeParams = signal({})
5
+ export const matchedRoutes = signal([])
6
+
7
+ let _routes = []
8
+ let _rootOutlet = null
9
+ let _clickHandler = null
10
+ let _activeChain = []
11
+ let _routeCache = new Map()
12
+ let _cacheMaxSize = 100
13
+ let _routerViewTimeout = 10000
14
+
15
+ function _getPath() {
16
+ return window.location.pathname || '/'
17
+ }
18
+
19
+ function _getRoutePaths(routePath) {
20
+ return Array.isArray(routePath) ? routePath : [routePath]
21
+ }
22
+
23
+ function _updateRoute() {
24
+ currentRoute(_getPath())
25
+ }
26
+
27
+ function _matchSegments(routePath, urlSegments) {
28
+ const cleanPath = routePath.startsWith('/') ? routePath.slice(1) : routePath
29
+
30
+ if (cleanPath === '') {
31
+ return { matched: true, consumed: 0, params: {} }
32
+ }
33
+
34
+ if (cleanPath === '*') {
35
+ return { matched: true, consumed: urlSegments.length, params: {} }
36
+ }
37
+
38
+ const routeSegs = cleanPath.split('/').filter(Boolean)
39
+ const hasWildcard = routeSegs[routeSegs.length - 1] === '*'
40
+ const checkSegs = hasWildcard ? routeSegs.slice(0, -1) : routeSegs
41
+
42
+ if (hasWildcard) {
43
+ if (urlSegments.length < checkSegs.length) return null
44
+ }
45
+
46
+ if (urlSegments.length < checkSegs.length) return null
47
+
48
+ const params = {}
49
+ for (let i = 0; i < checkSegs.length; i++) {
50
+ const rSeg = checkSegs[i]
51
+ const uSeg = urlSegments[i]
52
+ if (rSeg.startsWith(':')) {
53
+ params[rSeg.slice(1)] = uSeg
54
+ } else if (rSeg !== uSeg) {
55
+ return null
56
+ }
57
+ }
58
+
59
+ const consumed = hasWildcard ? urlSegments.length : checkSegs.length
60
+ return { matched: true, consumed, params }
61
+ }
62
+
63
+ function _matchRouteTree(routes, urlSegments) {
64
+ for (const route of routes) {
65
+ const paths = _getRoutePaths(route.path)
66
+ for (const path of paths) {
67
+ const result = _matchSegments(path, urlSegments)
68
+ if (!result) continue
69
+
70
+ const remaining = urlSegments.slice(result.consumed)
71
+
72
+ if (route.children && route.children.length > 0) {
73
+ const childChain = _matchRouteTree(route.children, remaining)
74
+ if (childChain) {
75
+ return [
76
+ { route, params: result.params, matchedSegments: urlSegments.slice(0, result.consumed), matchedPath: path },
77
+ ...childChain
78
+ ]
79
+ }
80
+ continue
81
+ }
82
+
83
+ if (remaining.length === 0 || path === '*' || path.endsWith('/*')) {
84
+ return [{ route, params: result.params, matchedSegments: urlSegments.slice(0, result.consumed), matchedPath: path }]
85
+ }
86
+ }
87
+ }
88
+
89
+ return null
90
+ }
91
+
92
+ function _matchSingleFlat(routePath, actualPath) {
93
+ if (routePath === '*') return true
94
+
95
+ const routeParts = routePath.split('/').filter(Boolean)
96
+ const actualParts = actualPath.split('/').filter(Boolean)
97
+
98
+ const hasWildcard = routeParts.length > 0 && routeParts[routeParts.length - 1] === '*'
99
+
100
+ if (hasWildcard) {
101
+ const baseParts = routeParts.slice(0, -1)
102
+ if (actualParts.length < baseParts.length) return false
103
+ return baseParts.every((part, i) => part.startsWith(':') || part === actualParts[i])
104
+ }
105
+
106
+ if (routeParts.length !== actualParts.length) return false
107
+ return routeParts.every((part, i) => part.startsWith(':') || part === actualParts[i])
108
+ }
109
+
110
+ function _matchFlat(routePath, actualPath) {
111
+ const paths = _getRoutePaths(routePath)
112
+ return paths.some(p => _matchSingleFlat(p, actualPath))
113
+ }
114
+
115
+ function _findMatchingPath(routePath, actualPath) {
116
+ const paths = _getRoutePaths(routePath)
117
+ return paths.find(p => _matchSingleFlat(p, actualPath)) || null
118
+ }
119
+
120
+ function _extractParamsFromPath(singlePath, actualPath) {
121
+ const params = {}
122
+ if (singlePath === '*') return params
123
+
124
+ const routeParts = singlePath.split('/').filter(Boolean)
125
+ const actualParts = actualPath.split('/').filter(Boolean)
126
+
127
+ const hasWildcard = routeParts.length > 0 && routeParts[routeParts.length - 1] === '*'
128
+ const checkLen = hasWildcard ? routeParts.length - 1 : routeParts.length
129
+
130
+ for (let i = 0; i < checkLen; i++) {
131
+ if (routeParts[i] && routeParts[i].startsWith(':')) {
132
+ params[routeParts[i].slice(1)] = actualParts[i] || ''
133
+ }
134
+ }
135
+ return params
136
+ }
137
+
138
+ function _extractParamsFlat(routePath, actualPath) {
139
+ const matchingPath = _findMatchingPath(routePath, actualPath)
140
+ if (!matchingPath) return {}
141
+ return _extractParamsFromPath(matchingPath, actualPath)
142
+ }
143
+
144
+ function _findMatchingChain(path) {
145
+ if (_routeCache.has(path)) {
146
+ const cached = _routeCache.get(path)
147
+ _routeCache.delete(path)
148
+ _routeCache.set(path, cached)
149
+ return cached
150
+ }
151
+
152
+ const urlSegments = path.split('/').filter(Boolean)
153
+ const hasNested = _routes.some(r => r.children && r.children.length > 0)
154
+
155
+ let chain = null
156
+
157
+ if (hasNested) {
158
+ chain = _matchRouteTree(_routes, urlSegments)
159
+ if (chain) {
160
+ _cacheResult(path, chain)
161
+ return chain
162
+ }
163
+ }
164
+
165
+ const paths = _routes.filter(r => {
166
+ const routePaths = _getRoutePaths(r.path)
167
+ return !routePaths.includes('*') && !routePaths.some(p => p.endsWith('/*')) && !r.children
168
+ })
169
+ const exactMatch = paths.find(r => _matchFlat(r.path, path))
170
+ if (exactMatch) {
171
+ const matchedPath = _findMatchingPath(exactMatch.path, path)
172
+ chain = [{ route: exactMatch, params: _extractParamsFlat(exactMatch.path, path), matchedSegments: urlSegments, matchedPath }]
173
+ _cacheResult(path, chain)
174
+ return chain
175
+ }
176
+
177
+ const wildcardPaths = _routes.filter(r => {
178
+ const routePaths = _getRoutePaths(r.path)
179
+ return !r.children && routePaths.some(p => p.endsWith('/*'))
180
+ })
181
+ const wildcardMatch = wildcardPaths.find(r => _matchFlat(r.path, path))
182
+ if (wildcardMatch) {
183
+ const matchedPath = _findMatchingPath(wildcardMatch.path, path)
184
+ chain = [{ route: wildcardMatch, params: _extractParamsFlat(wildcardMatch.path, path), matchedSegments: urlSegments, matchedPath }]
185
+ _cacheResult(path, chain)
186
+ return chain
187
+ }
188
+
189
+ const catchAll = _routes.find(r => {
190
+ const routePaths = _getRoutePaths(r.path)
191
+ return routePaths.includes('*')
192
+ })
193
+ if (catchAll) {
194
+ chain = [{ route: catchAll, params: {}, matchedSegments: urlSegments, matchedPath: '*' }]
195
+ _cacheResult(path, chain)
196
+ return chain
197
+ }
198
+
199
+ return null
200
+ }
201
+
202
+ function _cacheResult(path, chain) {
203
+ if (_routeCache.size >= _cacheMaxSize) {
204
+ const firstKey = _routeCache.keys().next().value
205
+ _routeCache.delete(firstKey)
206
+ }
207
+ _routeCache.set(path, chain)
208
+ }
209
+
210
+ function _clearRouteCache() {
211
+ _routeCache.clear()
212
+ }
213
+
214
+ function _unmountFromLevel(level) {
215
+ for (let i = _activeChain.length - 1; i >= level; i--) {
216
+ const entry = _activeChain[i]
217
+ if (entry && entry.instance) {
218
+ try { entry.instance.unmount() } catch { }
219
+ }
220
+ }
221
+ _activeChain.length = level
222
+ }
223
+
224
+ function _getRoutePathCount(route) {
225
+ return Array.isArray(route.path) ? route.path.length : 1
226
+ }
227
+
228
+ function _renderChain(chain) {
229
+ try {
230
+ let reuseUntil = 0
231
+ for (let i = 0; i < Math.min(_activeChain.length, chain.length); i++) {
232
+ const active = _activeChain[i]
233
+ const incoming = chain[i]
234
+
235
+ if (
236
+ active.route === incoming.route &&
237
+ _segmentsEqual(active.matchedSegments, incoming.matchedSegments)
238
+ ) {
239
+ reuseUntil = i + 1
240
+ }
241
+ else if (
242
+ active.route === incoming.route &&
243
+ _getRoutePathCount(active.route) > 1
244
+ ) {
245
+ reuseUntil = i + 1
246
+ }
247
+ else {
248
+ break
249
+ }
250
+ }
251
+
252
+ _unmountFromLevel(reuseUntil)
253
+
254
+ const allParams = {}
255
+ for (const entry of chain) {
256
+ Object.assign(allParams, entry.params)
257
+ }
258
+ routeParams(allParams)
259
+ matchedRoutes(chain.map(e => e.route))
260
+ _renderFromLevel(chain, reuseUntil)
261
+ } catch (error) {
262
+ console.error('[framework] Error rendering route chain:', error)
263
+ _unmountFromLevel(0)
264
+ if (_rootOutlet) {
265
+ _rootOutlet.innerHTML = `<p style="color:red">[framework] Error rendering route: ${error.message}</p>`
266
+ }
267
+ }
268
+ }
269
+
270
+ function _renderFromLevel(chain, level) {
271
+ if (level >= chain.length) return
272
+
273
+ const entry = chain[level]
274
+ const outlet = _getOutletForLevel(level)
275
+
276
+ if (!outlet) {
277
+ _waitForRouterView(level, () => _renderFromLevel(chain, level))
278
+ return
279
+ }
280
+
281
+ outlet.innerHTML = ''
282
+
283
+ const componentFn = entry.route.component
284
+ const instance = componentFn()
285
+ instance.render(outlet)
286
+
287
+ _activeChain[level] = {
288
+ route: entry.route,
289
+ matchedSegments: entry.matchedSegments,
290
+ params: entry.params,
291
+ instance,
292
+ outlet
293
+ }
294
+
295
+ if (level + 1 < chain.length) _scheduleNextLevel(chain, level + 1)
296
+ }
297
+
298
+ function _scheduleNextLevel(chain, level) {
299
+ const outlet = _getOutletForLevel(level)
300
+ if (outlet) {
301
+ _renderFromLevel(chain, level)
302
+ return
303
+ }
304
+
305
+ _waitForRouterView(level, () => _renderFromLevel(chain, level))
306
+ }
307
+
308
+ function _getOutletForLevel(level) {
309
+ if (level === 0) return _rootOutlet
310
+
311
+ const parentEntry = _activeChain[level - 1]
312
+ if (!parentEntry || !parentEntry.outlet) return null
313
+
314
+ return parentEntry.outlet.querySelector('[data-router-view]')
315
+ }
316
+
317
+ function _waitForRouterView(level, callback) {
318
+ const parentEntry = _activeChain[level - 1]
319
+ if (!parentEntry || !parentEntry.outlet) return
320
+
321
+ const parentOutlet = parentEntry.outlet
322
+
323
+ const existing = parentOutlet.querySelector('[data-router-view]')
324
+ if (existing) {
325
+ callback()
326
+ return
327
+ }
328
+
329
+ const observer = new MutationObserver(() => {
330
+ const el = parentOutlet.querySelector('[data-router-view]')
331
+ if (el) {
332
+ observer.disconnect()
333
+ callback()
334
+ }
335
+ })
336
+
337
+ observer.observe(parentOutlet, { childList: true, subtree: true })
338
+
339
+ setTimeout(() => observer.disconnect(), _routerViewTimeout)
340
+ }
341
+
342
+ function _segmentsEqual(a, b) {
343
+ if (a.length !== b.length) return false
344
+ for (let i = 0; i < a.length; i++) {
345
+ if (a[i] !== b[i]) return false
346
+ }
347
+ return true
348
+ }
349
+
350
+ function _resolveRoute() {
351
+ if (!_rootOutlet || _routes.length === 0) return
352
+
353
+ const path = _getPath()
354
+ const chain = _findMatchingChain(path)
355
+
356
+ if (!chain) {
357
+ _unmountFromLevel(0)
358
+ _rootOutlet.innerHTML = `<p style="color:red">[framework] No route matched for "${path}"</p>`
359
+ routeParams({})
360
+ matchedRoutes([])
361
+ return
362
+ }
363
+
364
+ _renderChain(chain)
365
+ }
366
+
367
+ export function navigate(path) {
368
+ if (path === _getPath()) return
369
+ window.history.pushState({}, '', path)
370
+ _updateRoute()
371
+ _resolveRoute()
372
+ }
373
+
374
+ export function mountOutlet(el) {
375
+ _rootOutlet = el
376
+ _resolveRoute()
377
+ }
378
+
379
+ function _isInternalLink(anchor) {
380
+ const href = anchor.getAttribute('href')
381
+ if (!href) return false
382
+ if (href.startsWith('#')) return false
383
+ if (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('//')) {
384
+ const url = new URL(href, window.location.origin)
385
+ return url.origin === window.location.origin
386
+ }
387
+ return href.startsWith('/')
388
+ }
389
+
390
+ function _getAnchorFromEvent(e) {
391
+ if (e.composedPath) {
392
+ return e.composedPath().find(el => el.tagName === 'A')
393
+ }
394
+ return e.target.tagName === 'A' ? e.target : null
395
+ }
396
+
397
+ function _handleLinkClick(e) {
398
+ const anchor = _getAnchorFromEvent(e)
399
+ if (!anchor) return
400
+
401
+ if (anchor.hasAttribute('f-link') || _isInternalLink(anchor)) {
402
+ e.preventDefault()
403
+ const href = anchor.getAttribute('href')
404
+ if (href && href !== _getPath()) {
405
+ navigate(href)
406
+ }
407
+ }
408
+ }
409
+
410
+ function _setupPopstateHandler() {
411
+ const handler = () => {
412
+ _updateRoute()
413
+ _resolveRoute()
414
+ }
415
+ window.addEventListener('popstate', handler)
416
+ return handler
417
+ }
418
+
419
+ function _setupClickHandler() {
420
+ _clickHandler = _handleLinkClick
421
+ document.addEventListener('click', _clickHandler)
422
+ return _clickHandler
423
+ }
424
+
425
+ function _setupInitialRoute() {
426
+ if (document.readyState === 'loading') {
427
+ window.addEventListener('DOMContentLoaded', () => {
428
+ _updateRoute()
429
+ _resolveRoute()
430
+ }, { once: true })
431
+ } else {
432
+ setTimeout(() => {
433
+ if (_activeChain.length === 0) _resolveRoute()
434
+ }, 0)
435
+ }
436
+ }
437
+
438
+ export function createRouter(routes, options = {}) {
439
+ _routes = routes
440
+ _activeChain = []
441
+ _cacheMaxSize = options.cacheSize || 100
442
+ _routerViewTimeout = options.routerViewTimeout || 10000
443
+
444
+ _updateRoute()
445
+
446
+ const popstateHandler = _setupPopstateHandler()
447
+ _setupClickHandler()
448
+ _setupInitialRoute()
449
+
450
+ return {
451
+ navigate,
452
+ destroy() {
453
+ window.removeEventListener('popstate', popstateHandler)
454
+ document.removeEventListener('click', _clickHandler)
455
+ _unmountFromLevel(0)
456
+ _routes = []
457
+ _rootOutlet = null
458
+ _activeChain = []
459
+ _clearRouteCache()
460
+ }
461
+ }
462
+ }
@@ -0,0 +1,15 @@
1
+ import type { ComponentResult, ComponentFactory } from './component'
2
+
3
+ export function mountTemplate(
4
+ template: string,
5
+ container: HTMLElement,
6
+ context: ComponentResult,
7
+ components: Record<string, ComponentFactory>
8
+ ): { effects: (() => void)[] }
9
+
10
+ export function compileNode(
11
+ node: HTMLElement,
12
+ context: ComponentResult,
13
+ components: Record<string, ComponentFactory>,
14
+ effects: (() => void)[]
15
+ ): void
@@ -0,0 +1,88 @@
1
+ export type ComponentProps = Record<string, any>
2
+ export type EmptyProps = Record<string, never>
3
+ export type PropSchema = string[] | Record<string, PropConfig>
4
+
5
+ export type PropConfig = {
6
+ type?: FunctionConstructor | StringConstructor | NumberConstructor | BooleanConstructor
7
+ default?: any | (() => any)
8
+ required?: boolean
9
+ }
10
+
11
+ export type EmitFn = (eventName: string, ...args: any[]) => void
12
+
13
+ export type Slots = Record<string, string>
14
+
15
+ export type SetupContext = {
16
+ emit: EmitFn
17
+ slots: Slots
18
+ }
19
+
20
+ export type ComponentInstance = {
21
+ props: ComponentProps
22
+ _mountHooks: (() => void)[]
23
+ _unmountHooks: (() => void)[]
24
+ _effects: import('./signal').EffectRunner[]
25
+ _element: HTMLElement | null
26
+ _provides: Record<string, any>
27
+ _parent: ComponentInstance | null
28
+ }
29
+
30
+ export type ComponentResult = {
31
+ template: string
32
+ [key: string]: any
33
+ }
34
+
35
+ export type ComponentOptions<TProps = ComponentProps, P = PropSchema> = {
36
+ props?: P
37
+ components?: Record<string, ComponentFactory<any>>
38
+ setup: (props: TProps, ctx: SetupContext) => ComponentResult
39
+ }
40
+
41
+ export type ComponentFactory<TProps = ComponentProps> = (
42
+ props?: TProps,
43
+ options?: { listeners?: Record<string, Function>, slots?: Slots, parent?: ComponentInstance }
44
+ ) => ComponentApi
45
+
46
+ export type ComponentApi = {
47
+ render: (container: HTMLElement) => ComponentInstance
48
+ unmount: () => void
49
+ instance: ComponentInstance
50
+ }
51
+
52
+ export declare function onMount(fn: () => void): void
53
+ export declare function onUnmount(fn: () => void): void
54
+ export declare function parseSlots(slotHTML: string): Slots
55
+ export declare function getCurrentInstance(): ComponentInstance | null
56
+
57
+ export declare function provide<T>(key: string, value: T): void
58
+ export declare function inject<T>(key: string): T | null
59
+
60
+ type InferPropType<T> = T extends StringConstructor ? string :
61
+ T extends NumberConstructor ? number :
62
+ T extends BooleanConstructor ? boolean :
63
+ any;
64
+
65
+ type InferProps<P> = P extends string[]
66
+ ? Record<P[number], any>
67
+ : P extends Record<string, PropConfig>
68
+ ? {
69
+ [K in keyof P]: P[K]['required'] extends true
70
+ ? InferPropType<P[K]['type']>
71
+ : InferPropType<P[K]['type']> | (P[K] extends { default: any } ? never : undefined)
72
+ }
73
+ : Record<string, any>;
74
+
75
+ export declare function defineComponent<P extends PropSchema = any, TProps = InferProps<P>>(
76
+ options: ComponentOptions<TProps, P>
77
+ ): ComponentFactory<TProps>
78
+
79
+ export type AsyncComponentLoader<TProps = ComponentProps> = () => Promise<ComponentFactory<TProps> | { default: ComponentFactory<TProps> } | Record<string, any>>
80
+
81
+ export type AsyncComponentOptions<TProps = ComponentProps> = {
82
+ loader: AsyncComponentLoader<TProps>
83
+ loadingComponent?: ComponentFactory<any>
84
+ }
85
+
86
+ export declare function defineAsyncComponent<TProps = ComponentProps>(
87
+ loaderOrOptions: AsyncComponentLoader<TProps> | AsyncComponentOptions<TProps>
88
+ ): ComponentFactory<TProps>
@@ -0,0 +1,14 @@
1
+ export interface Composable<T = any> {
2
+ (...args: any[]): T
3
+ readonly _isComposable: true
4
+ }
5
+
6
+ declare global {
7
+ interface Composable<T = any> {
8
+ (...args: any[]): T
9
+ readonly _isComposable: true
10
+ }
11
+ }
12
+
13
+ export declare function defineComposable<T extends (...args: any[]) => any>(fn: T): T;
14
+ export declare function assertSetupContext(name?: string): void;
@@ -0,0 +1,55 @@
1
+ import type { ComponentResult, ComponentFactory } from './component'
2
+
3
+ export function processDirectives(
4
+ root: HTMLElement,
5
+ context: ComponentResult,
6
+ components: Record<string, ComponentFactory>,
7
+ effects: (() => void)[]
8
+ ): void
9
+
10
+ export function processRefs(root: HTMLElement, context: ComponentResult): void
11
+
12
+ export function processModel(root: HTMLElement, context: ComponentResult, effects: (() => void)[]): void
13
+
14
+ export function processFor(
15
+ root: HTMLElement,
16
+ context: ComponentResult,
17
+ components: Record<string, ComponentFactory>,
18
+ effects: (() => void)[]
19
+ ): void
20
+
21
+ export function processIf(
22
+ root: HTMLElement,
23
+ context: ComponentResult,
24
+ components: Record<string, ComponentFactory>,
25
+ effects: (() => void)[]
26
+ ): void
27
+
28
+ export function processIs(
29
+ root: HTMLElement,
30
+ context: ComponentResult,
31
+ components: Record<string, ComponentFactory>,
32
+ effects: (() => void)[]
33
+ ): void
34
+
35
+ export function processShow(root: HTMLElement, context: ComponentResult, effects: (() => void)[]): void
36
+ export function processHtml(root: HTMLElement, context: ComponentResult, effects: (() => void)[]): void
37
+ export function processText(root: HTMLElement, context: ComponentResult, effects: (() => void)[]): void
38
+ export function processEvents(root: HTMLElement, context: ComponentResult): void
39
+ export function processClassList(root: HTMLElement, context: ComponentResult, effects: (() => void)[]): void
40
+ export function processCloak(root: HTMLElement): void
41
+
42
+ export interface DirectiveBinding<V = any> {
43
+ value: V
44
+ expression: string
45
+ arg: string | null
46
+ modifiers: string[]
47
+ }
48
+
49
+ export interface DirectiveHooks<V = any> {
50
+ mounted?: (el: HTMLElement, binding: DirectiveBinding<V>) => void
51
+ updated?: (el: HTMLElement, binding: DirectiveBinding<V>) => void
52
+ unmounted?: (el: HTMLElement, binding: DirectiveBinding<V>) => void
53
+ }
54
+
55
+ export function directive<V = any>(name: string, hooks: DirectiveHooks<V>): void
@@ -0,0 +1,13 @@
1
+ import type { ComponentResult } from './component'
2
+
3
+ export type InterpolationPart = {
4
+ type: 'static' | 'dynamic'
5
+ value?: string
6
+ key?: string
7
+ }
8
+
9
+ export const MUSTACHE_RE: RegExp
10
+
11
+ export function evaluateExpression(expr: string, context: Record<string, any>, extraContext?: Record<string, any>): any
12
+ export function parseInterpolation(str: string): InterpolationPart[]
13
+ export function sanitizeAttr(name: string, value: string): string
@@ -0,0 +1,32 @@
1
+ export const DELEGATED_EVENTS: Set<string>
2
+
3
+ export function isDelegatedEvent(eventName: string): boolean
4
+
5
+ export interface DelegatedEventOptions {
6
+ modifiers?: string[]
7
+ context?: Record<string, any>
8
+ expr?: string
9
+ }
10
+
11
+ export function registerDelegatedEvent(
12
+ element: Element,
13
+ eventType: string,
14
+ handler: (event: Event) => void,
15
+ options?: DelegatedEventOptions
16
+ ): () => void
17
+
18
+ export function createEventHandler(
19
+ handler: Function | string,
20
+ modifiers: string[],
21
+ context: Record<string, any>,
22
+ expr: string
23
+ ): (event: Event) => void
24
+
25
+ export interface EventHandlerSyntax {
26
+ delegated: `@${string}`
27
+ native: `on:${string}`
28
+ }
29
+
30
+ declare global {
31
+ const isDelegatedEvent: typeof import('./event-delegation').isDelegatedEvent
32
+ }
@@ -0,0 +1,22 @@
1
+ export * from './signal'
2
+ export * from './component'
3
+ export * from './composable'
4
+ export * from './router'
5
+ export * from './compiler'
6
+ export * from './directives'
7
+ export * from './store'
8
+ export * from './event-delegation'
9
+
10
+ declare global {
11
+ type Signal<T = any> = import('./signal').Signal<T>
12
+ type Computed<T = any> = import('./signal').Computed<T>
13
+ type SignalAccessor<T> = import('./signal').SignalAccessor<T>
14
+ type Composable<T extends (...args: any[]) => any> = import('./composable').Composable<T>
15
+ type DirectiveBinding<V = any> = import('./directives').DirectiveBinding<V>
16
+ type DirectiveHooks<V = any> = import('./directives').DirectiveHooks<V>
17
+ type DelegatedEventOptions = import('./event-delegation').DelegatedEventOptions
18
+ const defineStore: typeof import('./store').defineStore
19
+ const isDelegatedEvent: typeof import('./event-delegation').isDelegatedEvent
20
+ }
21
+
22
+ export as namespace Fusee