fusee-framework 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -123
- package/app/benchmark/benchmark.js +633 -0
- package/app/benchmark/index.html +102 -0
- package/app/benchmark/styles.css +249 -0
- package/app/components/Counter.js +17 -7
- package/app/components/CounterSection.js +3 -4
- package/app/main.js +50 -62
- package/app/main.ts +78 -63
- package/app/pages/404.js +16 -0
- package/app/pages/[...all].js +35 -0
- package/app/pages/[...all].ts +35 -0
- package/app/pages/_layout.js +53 -0
- package/app/pages/_layout.ts +26 -0
- package/app/pages/about.js +23 -0
- package/app/pages/about.ts +29 -0
- package/app/pages/async-work.js +65 -0
- package/app/pages/composables.js +158 -0
- package/app/pages/composables.ts +158 -0
- package/app/pages/dynamic.js +40 -0
- package/app/pages/dynamic.ts +40 -0
- package/app/pages/index.js +137 -0
- package/app/pages/index.ts +87 -0
- package/app/pages/login.ts +21 -0
- package/app/pages/objects/[id].js +56 -0
- package/app/pages/objects/[id].ts +56 -0
- package/app/pages/posts/[category]/[slug].js +56 -0
- package/app/pages/posts/[category]/[slug].ts +56 -0
- package/app/pages/router-example.js +79 -0
- package/app/pages/router-example.ts +56 -0
- package/app/pages/scroll-demo.ts +102 -0
- package/app/pages/test-for.js +65 -0
- package/app/pages/test-for.ts +80 -0
- package/app/pages/users/[id].js +24 -0
- package/app/pages/users/[id].ts +46 -0
- package/app/pages/users/index.js +22 -0
- package/app/pages/users/index.ts +29 -0
- package/app/pages/users.js +43 -0
- package/app/pages/users.ts +78 -0
- package/app/providers/ChildComponent.js +8 -2
- package/app/providers/ChildComponent.ts +47 -47
- package/app/providers/GrandchildComponent.js +19 -0
- package/app/providers/ParentComponent.js +5 -1
- package/app/routes/About.js +1 -1
- package/app/routes/About.ts +1 -1
- package/app/routes/AuthRoutes.ts +41 -0
- package/app/routes/Home.js +9 -0
- package/app/routes/NotFound.js +16 -0
- package/app/routes/NotFound.ts +16 -0
- package/app/routes/RouterExample.js +78 -0
- package/app/routes/RouterExample.ts +56 -0
- package/app/routes/TestResource.js +47 -0
- package/app/routes/WildcardDemo.js +35 -0
- package/app/routes/WildcardDemo.ts +35 -0
- package/app/services.js +15 -0
- package/auto-imports.d.ts +9 -0
- package/bin/create-fusee-linux +0 -0
- package/bin/create-fusee-mac-arm64 +0 -0
- package/bin/create-fusee-mac-intel +0 -0
- package/bin/create-fusee-win.exe +0 -0
- package/bin/create-fusee.exe +0 -0
- package/bin/index.js +38 -0
- package/create-fusee-go/cmd/generate.go +72 -0
- package/create-fusee-go/cmd/init.go +38 -0
- package/create-fusee-go/cmd/root.go +121 -0
- package/create-fusee-go/go.mod +10 -0
- package/create-fusee-go/go.sum +10 -0
- package/create-fusee-go/internal/assets/assets.go +78 -0
- package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +9 -0
- package/create-fusee-go/internal/assets/embed/framework/core/compiler.js +253 -0
- package/create-fusee-go/internal/assets/embed/framework/core/component.js +266 -0
- package/create-fusee-go/internal/assets/embed/framework/core/composable.js +27 -0
- package/create-fusee-go/internal/assets/embed/framework/core/directives.js +875 -0
- package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +153 -0
- package/create-fusee-go/internal/assets/embed/framework/core/event-delegation.js +191 -0
- package/create-fusee-go/internal/assets/embed/framework/core/signal.js +412 -0
- package/create-fusee-go/internal/assets/embed/framework/core/store.js +81 -0
- package/create-fusee-go/internal/assets/embed/framework/index.d.ts +3 -0
- package/create-fusee-go/internal/assets/embed/framework/index.js +10 -0
- package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +177 -0
- package/create-fusee-go/internal/assets/embed/framework/router/router.js +462 -0
- package/create-fusee-go/internal/assets/embed/framework/types/compiler.d.ts +15 -0
- package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +88 -0
- package/create-fusee-go/internal/assets/embed/framework/types/composable.d.ts +14 -0
- package/create-fusee-go/internal/assets/embed/framework/types/directives.d.ts +55 -0
- package/create-fusee-go/internal/assets/embed/framework/types/evaluator.d.ts +13 -0
- package/create-fusee-go/internal/assets/embed/framework/types/event-delegation.d.ts +32 -0
- package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +22 -0
- package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +32 -0
- package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +79 -0
- package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +8 -0
- package/create-fusee-go/internal/assets/templates/Counter.tmpl +26 -0
- package/create-fusee-go/internal/assets/templates/component.tmpl +11 -0
- package/create-fusee-go/internal/assets/templates/components/Loading.tmpl +24 -0
- package/create-fusee-go/internal/assets/templates/index.html.tmpl +15 -0
- package/create-fusee-go/internal/assets/templates/main.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/package.json.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/page.tmpl +12 -0
- package/create-fusee-go/internal/assets/templates/pages/about.tmpl +20 -0
- package/create-fusee-go/internal/assets/templates/pages/index.tmpl +22 -0
- package/create-fusee-go/internal/assets/templates/pages/layout.tmpl +18 -0
- package/create-fusee-go/internal/assets/templates/tsconfig.json.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/vite.config.js.tmpl +18 -0
- package/create-fusee-go/main.go +7 -0
- package/framework/auto-import-preset.js +13 -9
- package/framework/core/compiler.js +252 -249
- package/framework/core/component.js +279 -260
- package/framework/core/di.js +187 -0
- package/framework/core/directives.js +819 -786
- package/framework/core/evaluator.js +153 -70
- package/framework/core/event-delegation.js +33 -14
- package/framework/core/reconcile.js +151 -0
- package/framework/core/signal.js +679 -403
- package/framework/core/store.js +322 -81
- package/framework/index.js +12 -11
- package/framework/router/file-router.js +177 -0
- package/framework/router/router.js +554 -80
- package/framework/types/component.d.ts +89 -88
- package/framework/types/di.d.ts +43 -0
- package/framework/types/event-delegation.d.ts +0 -58
- package/framework/types/index.d.ts +32 -22
- package/framework/types/reconcile.d.ts +7 -0
- package/framework/types/router.d.ts +42 -15
- package/framework/types/signal.d.ts +147 -78
- package/framework/types/store.d.ts +66 -8
- package/index.html +638 -626
- package/package.json +5 -3
- package/tsconfig.json +1 -0
- package/vite.config.js +7 -1
- /package/{framework → create-fusee-go/internal/assets/embed/framework}/bin/create-fusee.js +0 -0
package/framework/core/store.js
CHANGED
|
@@ -1,81 +1,322 @@
|
|
|
1
|
-
import { getCurrentInstance, setCurrentInstance } from './component.js'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
import { getCurrentInstance, setCurrentInstance } from './component.js'
|
|
2
|
+
import { batch, effect } from './signal.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const storesRegistry = new Map()
|
|
6
|
+
const storePlugins = []
|
|
7
|
+
const initializationStack = new Set()
|
|
8
|
+
|
|
9
|
+
export const MutationType = {
|
|
10
|
+
DIRECT: 'direct',
|
|
11
|
+
PATCH_OBJECT: 'patch object',
|
|
12
|
+
PATCH_FUNCTION: 'patch function'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function registerStorePlugin(plugin) {
|
|
16
|
+
if (typeof plugin === 'function') {
|
|
17
|
+
storePlugins.push(plugin)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useNestedStore(useStoreFn) {
|
|
22
|
+
if (typeof useStoreFn !== 'function' || !useStoreFn.$id) {
|
|
23
|
+
console.error('[framework] useNestedStore requires a valid store hook (defineStore result)')
|
|
24
|
+
throw new Error('Invalid store hook provided to useNestedStore')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const targetId = useStoreFn.$id
|
|
28
|
+
|
|
29
|
+
if (initializationStack.has(targetId)) {
|
|
30
|
+
const chain = Array.from(initializationStack).concat(targetId).join(' -> ')
|
|
31
|
+
console.error(`[framework] Circular dependency detected: ${chain}`)
|
|
32
|
+
throw new Error(`Circular store dependency detected: ${chain}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return useStoreFn()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isDev = typeof import.meta.env !== 'undefined' ? !!import.meta.env.DEV : true
|
|
39
|
+
if (isDev && typeof window !== 'undefined') {
|
|
40
|
+
window.__FRAMEWORK_STORES__ = storesRegistry
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function defineStore(id, setup) {
|
|
44
|
+
if (typeof id !== 'string' || !id.trim()) {
|
|
45
|
+
console.error('[framework] defineStore requires a valid string ID.')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function useStore() {
|
|
49
|
+
let store = storesRegistry.get(id)
|
|
50
|
+
|
|
51
|
+
if (!store) {
|
|
52
|
+
const prevInstance = getCurrentInstance()
|
|
53
|
+
|
|
54
|
+
setCurrentInstance(null)
|
|
55
|
+
initializationStack.add(id)
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
store = setup()
|
|
59
|
+
|
|
60
|
+
if (store instanceof Promise) {
|
|
61
|
+
console.error(`[framework] Store "${id}" cannot be async.`)
|
|
62
|
+
throw new Error(`Async store "${id}" is not supported.`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error(`[framework] Error initializing store "${id}":`, err)
|
|
67
|
+
throw err
|
|
68
|
+
} finally {
|
|
69
|
+
initializationStack.delete(id)
|
|
70
|
+
setCurrentInstance(prevInstance)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const initialState = {}
|
|
74
|
+
const signalKeys = []
|
|
75
|
+
const getterKeys = []
|
|
76
|
+
for (const key in store) {
|
|
77
|
+
if (typeof store[key] === 'function' && store[key].isSignal) {
|
|
78
|
+
if (store[key].readonly) {
|
|
79
|
+
getterKeys.push(key)
|
|
80
|
+
} else {
|
|
81
|
+
initialState[key] = store[key]()
|
|
82
|
+
signalKeys.push(key)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Subscription management
|
|
88
|
+
const subscriptions = new Map()
|
|
89
|
+
let subscriptionId = 0
|
|
90
|
+
let currentMutation = null
|
|
91
|
+
|
|
92
|
+
function notifySubscribers() {
|
|
93
|
+
if (!currentMutation) return
|
|
94
|
+
const mutation = currentMutation
|
|
95
|
+
currentMutation = null
|
|
96
|
+
|
|
97
|
+
const state = {}
|
|
98
|
+
for (const key of signalKeys) {
|
|
99
|
+
state[key] = store[key]()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const [subId, sub] of subscriptions) {
|
|
103
|
+
if (sub.flush === 'sync') {
|
|
104
|
+
sub.callback(mutation, state)
|
|
105
|
+
} else {
|
|
106
|
+
// Default: post (after all batched updates)
|
|
107
|
+
queueMicrotask(() => {
|
|
108
|
+
if (subscriptions.has(subId)) {
|
|
109
|
+
sub.callback(mutation, state)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function wrapSignal(key, originalSignal) {
|
|
117
|
+
const wrapped = function(newValue) {
|
|
118
|
+
if (arguments.length === 0) {
|
|
119
|
+
return originalSignal()
|
|
120
|
+
} else {
|
|
121
|
+
const result = originalSignal(newValue)
|
|
122
|
+
if (!currentMutation) {
|
|
123
|
+
currentMutation = {
|
|
124
|
+
type: MutationType.DIRECT,
|
|
125
|
+
storeId: id,
|
|
126
|
+
payload: { [key]: newValue }
|
|
127
|
+
}
|
|
128
|
+
notifySubscribers()
|
|
129
|
+
}
|
|
130
|
+
return result
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
wrapped.isSignal = true
|
|
134
|
+
for (const method of ['map', 'filter', 'slice', 'concat', 'flat', 'flatMap',
|
|
135
|
+
'find', 'findLast', 'findIndex', 'findLastIndex',
|
|
136
|
+
'indexOf', 'lastIndexOf', 'includes', 'every', 'some',
|
|
137
|
+
'reduce', 'at', 'join', 'push', 'pop', 'shift', 'unshift',
|
|
138
|
+
'splice', 'remove', 'clear', 'sort', 'reverse']) {
|
|
139
|
+
if (originalSignal[method]) {
|
|
140
|
+
wrapped[method] = originalSignal[method]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return wrapped
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function wrapGetter(key, originalGetter) {
|
|
147
|
+
const wrapped = function(newValue) {
|
|
148
|
+
if (arguments.length === 0) {
|
|
149
|
+
return originalGetter()
|
|
150
|
+
} else {
|
|
151
|
+
console.warn(`[framework] Getter "${key}" is read-only`)
|
|
152
|
+
return originalGetter()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
wrapped.isSignal = true
|
|
156
|
+
wrapped.readonly = true
|
|
157
|
+
for (const method of ['map', 'filter', 'slice', 'concat', 'flat', 'flatMap',
|
|
158
|
+
'find', 'findLast', 'findIndex', 'findLastIndex',
|
|
159
|
+
'indexOf', 'lastIndexOf', 'includes', 'every', 'some',
|
|
160
|
+
'reduce', 'at', 'join']) {
|
|
161
|
+
if (originalGetter[method]) {
|
|
162
|
+
wrapped[method] = originalGetter[method]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return wrapped
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const wrappedSignals = {}
|
|
169
|
+
for (const key of signalKeys) {
|
|
170
|
+
wrappedSignals[key] = wrapSignal(key, store[key])
|
|
171
|
+
store[key] = wrappedSignals[key]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const key of getterKeys) {
|
|
175
|
+
store[key] = wrapGetter(key, store[key])
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
Object.defineProperties(store, {
|
|
179
|
+
id: {
|
|
180
|
+
value: id,
|
|
181
|
+
enumerable: false
|
|
182
|
+
},
|
|
183
|
+
type: {
|
|
184
|
+
value: 'store',
|
|
185
|
+
enumerable: false
|
|
186
|
+
},
|
|
187
|
+
patch: {
|
|
188
|
+
value: (arg) => {
|
|
189
|
+
const isObjectPatch = arg && typeof arg === 'object'
|
|
190
|
+
currentMutation = {
|
|
191
|
+
type: isObjectPatch ? MutationType.PATCH_OBJECT : MutationType.PATCH_FUNCTION,
|
|
192
|
+
storeId: id,
|
|
193
|
+
payload: isObjectPatch ? { ...arg } : undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
batch(() => {
|
|
197
|
+
if (typeof arg === 'function') {
|
|
198
|
+
arg(store)
|
|
199
|
+
} else if (isObjectPatch) {
|
|
200
|
+
for (const key in arg) {
|
|
201
|
+
if (typeof store[key] === 'function' && store[key].isSignal && !store[key].readonly) {
|
|
202
|
+
const originalSignal = wrappedSignals[key]
|
|
203
|
+
originalSignal(arg[key])
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
notifySubscribers()
|
|
210
|
+
},
|
|
211
|
+
enumerable: false
|
|
212
|
+
},
|
|
213
|
+
reset: {
|
|
214
|
+
value: () => {
|
|
215
|
+
currentMutation = {
|
|
216
|
+
type: MutationType.PATCH_FUNCTION,
|
|
217
|
+
storeId: id,
|
|
218
|
+
payload: undefined
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
batch(() => {
|
|
222
|
+
for (const key in initialState) {
|
|
223
|
+
const originalSignal = wrappedSignals[key]
|
|
224
|
+
originalSignal(initialState[key])
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
notifySubscribers()
|
|
229
|
+
},
|
|
230
|
+
enumerable: false
|
|
231
|
+
},
|
|
232
|
+
subscribe: {
|
|
233
|
+
value: (callback, options = {}) => {
|
|
234
|
+
if (typeof callback !== 'function') {
|
|
235
|
+
console.warn('[framework] subscribe callback must be a function')
|
|
236
|
+
return () => {}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const subId = ++subscriptionId
|
|
240
|
+
const sub = {
|
|
241
|
+
callback,
|
|
242
|
+
flush: options.flush || 'post',
|
|
243
|
+
detached: options.detached || false
|
|
244
|
+
}
|
|
245
|
+
subscriptions.set(subId, sub)
|
|
246
|
+
|
|
247
|
+
if (!sub.detached) {
|
|
248
|
+
const instance = getCurrentInstance()
|
|
249
|
+
if (instance && instance._unmountHooks) {
|
|
250
|
+
instance._unmountHooks.push(() => {
|
|
251
|
+
subscriptions.delete(subId)
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return () => {
|
|
257
|
+
subscriptions.delete(subId)
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
enumerable: false
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
for (const plugin of storePlugins) {
|
|
265
|
+
try {
|
|
266
|
+
plugin(store, id)
|
|
267
|
+
} catch (e) {
|
|
268
|
+
console.warn(`[framework] Store plugin error for "${id}":`, e)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
storesRegistry.set(id, store)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return store
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
useStore.$id = id
|
|
279
|
+
|
|
280
|
+
return useStore
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function resetStore(id) {
|
|
284
|
+
storesRegistry.delete(id)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function clearStores() {
|
|
288
|
+
storesRegistry.clear()
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function storeToRefs(store) {
|
|
292
|
+
const refs = {}
|
|
293
|
+
for (const key in store) {
|
|
294
|
+
const value = store[key]
|
|
295
|
+
if (typeof value === 'function' && value.isSignal) {
|
|
296
|
+
refs[key] = value
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return refs
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function storeToState(store) {
|
|
303
|
+
const state = {}
|
|
304
|
+
for (const key in store) {
|
|
305
|
+
const value = store[key]
|
|
306
|
+
if (typeof value === 'function' && value.isSignal && !value.readonly) {
|
|
307
|
+
state[key] = value
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return state
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function storeToGetters(store) {
|
|
314
|
+
const getters = {}
|
|
315
|
+
for (const key in store) {
|
|
316
|
+
const value = store[key]
|
|
317
|
+
if (typeof value === 'function' && value.isSignal && value.readonly) {
|
|
318
|
+
getters[key] = value
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return getters
|
|
322
|
+
}
|
package/framework/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
// ─── Framework Public API ─────────────────────────────────────────────────────
|
|
2
|
-
export { signal, computed, effect, batch, untrack, inspect, watch } from './core/signal.js'
|
|
3
|
-
export { defineComponent, defineAsyncComponent, onMount, onUnmount, parseSlots, provide, inject, getCurrentInstance } from './core/component.js'
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
export
|
|
1
|
+
// ─── Framework Public API ─────────────────────────────────────────────────────
|
|
2
|
+
export { signal, computed, effect, batch, untrack, inspect, watch, onCleanup, resource, createSuspense, scheduleAsyncJob } from './core/signal.js'
|
|
3
|
+
export { defineComponent, defineAsyncComponent, onMount, onUnmount, parseSlots, provide, inject, getCurrentInstance } from './core/component.js'
|
|
4
|
+
export { InjectionToken, provideGlobal } from './core/di.js'
|
|
5
|
+
export { createRouter, navigate, mountOutlet, currentRoute, routeParams, routeQuery, matchedRoutes } from './router/router.js'
|
|
6
|
+
export { generateRoutes } from './router/file-router.js'
|
|
7
|
+
export { mountTemplate } from './core/compiler.js'
|
|
8
|
+
export { defineComposable, assertSetupContext } from './core/composable.js'
|
|
9
|
+
export { defineStore, resetStore, clearStores, registerStorePlugin, storeToRefs, storeToState, storeToGetters, useNestedStore, MutationType } from './core/store.js'
|
|
10
|
+
export { directive } from './core/directives.js'
|
|
11
|
+
export { reconcile } from './core/reconcile.js'
|
|
12
|
+
export const emit = () => console.warn('[framework] emit() can only be used inside component setup()')
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { defineAsyncComponent } from '../core/component.js';
|
|
2
|
+
|
|
3
|
+
export function generateRoutes(globResults, options = {}) {
|
|
4
|
+
const entries = _parseGlobEntries(globResults, options);
|
|
5
|
+
const tree = _buildRouteTree(entries, false);
|
|
6
|
+
_sortRoutesRecursive(tree);
|
|
7
|
+
return tree;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function _parseGlobEntries(globResults, options) {
|
|
11
|
+
const entries = [];
|
|
12
|
+
|
|
13
|
+
for (const filePath of Object.keys(globResults)) {
|
|
14
|
+
const loader = globResults[filePath];
|
|
15
|
+
|
|
16
|
+
let cleanPath = filePath
|
|
17
|
+
.replace(/^\.?\/?app\/pages\//, '')
|
|
18
|
+
.replace(/^\.?\/?pages\//, '')
|
|
19
|
+
.replace(/\.[jt]sx?$/, '');
|
|
20
|
+
|
|
21
|
+
const parts = cleanPath.split('/');
|
|
22
|
+
const rawFileName = parts.pop();
|
|
23
|
+
const rawDirSegments = parts;
|
|
24
|
+
|
|
25
|
+
const dirSegments = rawDirSegments.map(_transformSegment);
|
|
26
|
+
const fileName = _transformSegment(rawFileName);
|
|
27
|
+
|
|
28
|
+
const isIndex = rawFileName === 'index';
|
|
29
|
+
const isCatchAll = /^\[\.\.\./.test(rawFileName);
|
|
30
|
+
|
|
31
|
+
entries.push({
|
|
32
|
+
filePath,
|
|
33
|
+
loader,
|
|
34
|
+
dirSegments,
|
|
35
|
+
fileName,
|
|
36
|
+
isIndex,
|
|
37
|
+
isCatchAll,
|
|
38
|
+
component: defineAsyncComponent({
|
|
39
|
+
loader,
|
|
40
|
+
loadingComponent: options.loadingComponent
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return entries;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _transformSegment(seg) {
|
|
49
|
+
return seg
|
|
50
|
+
.replace(/\[\.\.\.([^\]]+)\]/g, '*')
|
|
51
|
+
.replace(/\[([^\]]+)\]/g, ':$1');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function _buildRouteTree(entries, isChild) {
|
|
55
|
+
const filesHere = new Map();
|
|
56
|
+
const grouped = new Map();
|
|
57
|
+
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (entry.dirSegments.length === 0) {
|
|
60
|
+
filesHere.set(entry.fileName, entry);
|
|
61
|
+
} else {
|
|
62
|
+
const firstDir = entry.dirSegments[0];
|
|
63
|
+
if (!grouped.has(firstDir)) grouped.set(firstDir, []);
|
|
64
|
+
grouped.get(firstDir).push(entry);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let rootLayoutEntry = null;
|
|
69
|
+
if (filesHere.has('_layout')) {
|
|
70
|
+
rootLayoutEntry = filesHere.get('_layout');
|
|
71
|
+
filesHere.delete('_layout');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const routes = [];
|
|
75
|
+
const usedAsLayout = new Set();
|
|
76
|
+
|
|
77
|
+
for (const [dirName, dirEntries] of grouped) {
|
|
78
|
+
const layoutEntry = filesHere.get(dirName);
|
|
79
|
+
|
|
80
|
+
if (layoutEntry) {
|
|
81
|
+
usedAsLayout.add(dirName);
|
|
82
|
+
|
|
83
|
+
const childEntries = dirEntries.map(e => ({
|
|
84
|
+
...e,
|
|
85
|
+
dirSegments: e.dirSegments.slice(1)
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
const children = _buildRouteTree(childEntries, true);
|
|
89
|
+
|
|
90
|
+
routes.push({
|
|
91
|
+
path: isChild ? dirName : '/' + dirName,
|
|
92
|
+
component: layoutEntry.component,
|
|
93
|
+
children
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
for (const entry of dirEntries) {
|
|
97
|
+
const segments = [...entry.dirSegments];
|
|
98
|
+
if (!entry.isIndex) segments.push(entry.fileName);
|
|
99
|
+
|
|
100
|
+
const routePath = isChild
|
|
101
|
+
? segments.join('/')
|
|
102
|
+
: '/' + segments.join('/');
|
|
103
|
+
|
|
104
|
+
routes.push({
|
|
105
|
+
path: routePath,
|
|
106
|
+
component: entry.component
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const [name, entry] of filesHere) {
|
|
113
|
+
if (usedAsLayout.has(name)) continue;
|
|
114
|
+
|
|
115
|
+
if (entry.isIndex) {
|
|
116
|
+
routes.push({ path: isChild ? '' : '/', component: entry.component });
|
|
117
|
+
} else if (entry.isCatchAll || name === '*') {
|
|
118
|
+
routes.push({ path: '*', component: entry.component });
|
|
119
|
+
} else {
|
|
120
|
+
routes.push({
|
|
121
|
+
path: isChild ? name : '/' + name,
|
|
122
|
+
component: entry.component
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (rootLayoutEntry) {
|
|
128
|
+
return [{
|
|
129
|
+
path: '',
|
|
130
|
+
component: rootLayoutEntry.component,
|
|
131
|
+
children: routes
|
|
132
|
+
}];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return routes;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function _sortRoutesRecursive(routes) {
|
|
139
|
+
routes.sort(_compareRoutes);
|
|
140
|
+
for (const route of routes) {
|
|
141
|
+
if (route.children) _sortRoutesRecursive(route.children);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function _getPathArray(path) {
|
|
146
|
+
return Array.isArray(path) ? path : [path];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function _compareRoutes(a, b) {
|
|
150
|
+
const aPaths = _getPathArray(a.path);
|
|
151
|
+
const bPaths = _getPathArray(b.path);
|
|
152
|
+
|
|
153
|
+
const aPath = aPaths[0];
|
|
154
|
+
const bPath = bPaths[0];
|
|
155
|
+
|
|
156
|
+
if (aPath === '*' && bPath !== '*') return 1;
|
|
157
|
+
if (aPath !== '*' && bPath === '*') return -1;
|
|
158
|
+
|
|
159
|
+
const aWild = aPath.endsWith('/*');
|
|
160
|
+
const bWild = bPath.endsWith('/*');
|
|
161
|
+
if (aWild && !bWild) return 1;
|
|
162
|
+
if (!aWild && bWild) return -1;
|
|
163
|
+
|
|
164
|
+
const aDynamic = aPath.includes(':');
|
|
165
|
+
const bDynamic = bPath.includes(':');
|
|
166
|
+
if (aDynamic && !bDynamic) return 1;
|
|
167
|
+
if (!aDynamic && bDynamic) return -1;
|
|
168
|
+
|
|
169
|
+
if (a.children && !b.children) return -1;
|
|
170
|
+
if (!a.children && b.children) return 1;
|
|
171
|
+
|
|
172
|
+
const aSegs = aPath.split('/').filter(Boolean).length;
|
|
173
|
+
const bSegs = bPath.split('/').filter(Boolean).length;
|
|
174
|
+
if (aSegs !== bSegs) return bSegs - aSegs;
|
|
175
|
+
|
|
176
|
+
return aPath.localeCompare(bPath);
|
|
177
|
+
}
|