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
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { inject, provide } from '../../framework/index.js'
|
|
2
|
-
import { GrandchildComponent } from './GrandchildComponent.js'
|
|
3
|
-
|
|
4
|
-
// ChildComponent - injects values from parent (ParentComponent)
|
|
5
|
-
export const ChildComponent = defineComponent({
|
|
6
|
-
components: { GrandchildComponent },
|
|
7
|
-
setup() {
|
|
8
|
-
// Inject values provided by the parent
|
|
9
|
-
const count = inject('count')
|
|
10
|
-
const message = inject('message')
|
|
11
|
-
|
|
12
|
-
// These are NOT provided by parent, so
|
|
13
|
-
const nonExistent = inject('nonExistent')
|
|
14
|
-
|
|
15
|
-
function updateMessage(): void {
|
|
16
|
-
const msg = message as any
|
|
17
|
-
if (msg) {
|
|
18
|
-
msg('Message updated by Child!')
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
count,
|
|
24
|
-
message,
|
|
25
|
-
nonExistent,
|
|
26
|
-
updateMessage,
|
|
27
|
-
template: `
|
|
28
|
-
<div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
29
|
-
<h3 style="color: #2196F3;">Child Component (direct child of Parent)</h3>
|
|
30
|
-
<p>I'm injecting these values from my parent:</p>
|
|
31
|
-
<ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
|
|
32
|
-
<li><strong>count:</strong> {{ count }}</li>
|
|
33
|
-
<li><strong>message:</strong> {{ message }}</li>
|
|
34
|
-
<li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
|
|
35
|
-
</ul>
|
|
36
|
-
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
37
|
-
Update Message
|
|
38
|
-
</button>
|
|
39
|
-
|
|
40
|
-
<div style="margin-top: 15px;">
|
|
41
|
-
{{ GrandchildComponent }}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
`
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
})
|
|
1
|
+
import { inject, provide } from '../../framework/index.js'
|
|
2
|
+
import { GrandchildComponent } from './GrandchildComponent.js'
|
|
3
|
+
|
|
4
|
+
// ChildComponent - injects values from parent (ParentComponent)
|
|
5
|
+
export const ChildComponent = defineComponent({
|
|
6
|
+
components: { GrandchildComponent },
|
|
7
|
+
setup() {
|
|
8
|
+
// Inject values provided by the parent
|
|
9
|
+
const count = inject('count')
|
|
10
|
+
const message = inject('message')
|
|
11
|
+
|
|
12
|
+
// These are NOT provided by parent, so we use optional: true to avoid throwing NullInjectorError
|
|
13
|
+
const nonExistent = inject('nonExistent', { optional: true })
|
|
14
|
+
|
|
15
|
+
function updateMessage(): void {
|
|
16
|
+
const msg = message as any
|
|
17
|
+
if (msg) {
|
|
18
|
+
msg('Message updated by Child!')
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
count,
|
|
24
|
+
message,
|
|
25
|
+
nonExistent,
|
|
26
|
+
updateMessage,
|
|
27
|
+
template: `
|
|
28
|
+
<div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
29
|
+
<h3 style="color: #2196F3;">Child Component (direct child of Parent)</h3>
|
|
30
|
+
<p>I'm injecting these values from my parent:</p>
|
|
31
|
+
<ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
|
|
32
|
+
<li><strong>count:</strong> {{ count }}</li>
|
|
33
|
+
<li><strong>message:</strong> {{ message }}</li>
|
|
34
|
+
<li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
|
|
35
|
+
</ul>
|
|
36
|
+
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
37
|
+
Update Message
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
<div style="margin-top: 15px;">
|
|
41
|
+
{{ GrandchildComponent }}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject } from '../../framework/index.js'
|
|
2
|
+
import { LoggerService, APP_CONFIG } from '../services.js'
|
|
2
3
|
|
|
3
4
|
// GrandchildComponent - injects values from grandparent (ParentComponent)
|
|
4
5
|
// This demonstrates that inject() searches the entire ancestor chain
|
|
@@ -10,6 +11,11 @@ export const GrandchildComponent = defineComponent({
|
|
|
10
11
|
const userData = inject('userData')
|
|
11
12
|
const appVersion = inject('appVersion')
|
|
12
13
|
|
|
14
|
+
const logger = inject(LoggerService)
|
|
15
|
+
const config = inject(APP_CONFIG)
|
|
16
|
+
|
|
17
|
+
logger.log('GrandchildComponent initialized!')
|
|
18
|
+
|
|
13
19
|
function updateMessage() {
|
|
14
20
|
// message is a signal, so we can update it from grandchild!
|
|
15
21
|
message('Updated from Grandchild!')
|
|
@@ -20,6 +26,8 @@ export const GrandchildComponent = defineComponent({
|
|
|
20
26
|
message,
|
|
21
27
|
userData,
|
|
22
28
|
appVersion,
|
|
29
|
+
logger,
|
|
30
|
+
config,
|
|
23
31
|
updateMessage,
|
|
24
32
|
template: `
|
|
25
33
|
<div style="padding: 15px; border: 2px solid #9C27B0; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
@@ -30,7 +38,18 @@ export const GrandchildComponent = defineComponent({
|
|
|
30
38
|
<li><strong>message:</strong> {{ message }} <em>(from ParentComponent)</em></li>
|
|
31
39
|
<li><strong>userData:</strong> {{ userData.name }}, {{ userData.age }} <em>(from ParentComponent)</em></li>
|
|
32
40
|
<li><strong>appVersion:</strong> {{ appVersion }} <em>(from ParentComponent)</em></li>
|
|
41
|
+
<li><strong>Logger ID:</strong> {{ logger.id }} <em>(Singleton!)</em></li>
|
|
42
|
+
<li><strong>App Title:</strong> {{ config.title }} <em>(Global Config)</em></li>
|
|
33
43
|
</ul>
|
|
44
|
+
|
|
45
|
+
<div style="margin-top: 15px; padding: 10px; background: #2a2a2a; border-radius: 4px;">
|
|
46
|
+
<h5 style="margin-bottom: 5px; color: #34d399;">Service Logs:</h5>
|
|
47
|
+
<ul style="font-size: 0.8rem; list-style: none;">
|
|
48
|
+
<li f-for="log in logger.logs" style="border-bottom: 1px solid #444; padding: 2px 0;">
|
|
49
|
+
<span style="color: #888;">[{{ log.time }}]</span> {{ log.message }}
|
|
50
|
+
</li>
|
|
51
|
+
</ul>
|
|
52
|
+
</div>
|
|
34
53
|
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #9C27B0; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
35
54
|
Update Message from Grandchild
|
|
36
55
|
</button>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { provide } from '../../framework/index.js'
|
|
1
|
+
import { provide, inject } from '../../framework/index.js'
|
|
2
|
+
import { LoggerService } from '../services.js'
|
|
2
3
|
import { ChildComponent } from './ChildComponent.js'
|
|
3
4
|
|
|
4
5
|
// ParentComponent - provides values to child components
|
|
@@ -9,6 +10,9 @@ export const ParentComponent = defineComponent({
|
|
|
9
10
|
const message = signal('Hello from Parent!')
|
|
10
11
|
const userData = { name: 'John', age: 30 }
|
|
11
12
|
|
|
13
|
+
const logger = inject(LoggerService)
|
|
14
|
+
logger.log('ParentComponent initialized!')
|
|
15
|
+
|
|
12
16
|
// Provide values to descendant components
|
|
13
17
|
provide('count', count)
|
|
14
18
|
provide('message', message)
|
package/app/routes/About.js
CHANGED
|
@@ -14,7 +14,7 @@ export const About = defineComponent({
|
|
|
14
14
|
<div class="page">
|
|
15
15
|
<h1>About</h1>
|
|
16
16
|
<p>Framework custom — SPA prototipe</p>
|
|
17
|
-
<p><strong>Features:</strong> signals, defineComponent,
|
|
17
|
+
<p><strong>Features:</strong> signals, defineComponent, History API router, lifecycle hooks.</p>
|
|
18
18
|
<p class="status">{{ loaded }}</p>
|
|
19
19
|
</div>
|
|
20
20
|
`
|
package/app/routes/About.ts
CHANGED
|
@@ -20,7 +20,7 @@ export const About = defineComponent({
|
|
|
20
20
|
<div class="page">
|
|
21
21
|
<h1>About</h1>
|
|
22
22
|
<p>Framework custom — prototip SPA.</p>
|
|
23
|
-
<p><strong>Features:</strong> signals, defineComponent,
|
|
23
|
+
<p><strong>Features:</strong> signals, defineComponent, History API router, lifecycle hooks.</p>
|
|
24
24
|
<p class="status">{{ loaded }}</p>
|
|
25
25
|
</div>
|
|
26
26
|
`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { currentRoute, navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
// Demo: Multiple paths for the same component
|
|
4
|
+
// Navigating between /login and /register will NOT cause re-render
|
|
5
|
+
export const AuthRoutes = defineComponent({
|
|
6
|
+
setup() {
|
|
7
|
+
const path = currentRoute
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
path,
|
|
11
|
+
navigate,
|
|
12
|
+
template: `
|
|
13
|
+
<div class="page">
|
|
14
|
+
<h1>🔐 Auth Page (Multiple Paths Demo)</h1>
|
|
15
|
+
<div class="demo-card">
|
|
16
|
+
<p>Current path: <code>{{ path }}</code></p>
|
|
17
|
+
<p style="color: #888; margin-top: 10px;">
|
|
18
|
+
This component handles multiple paths: <code>/login</code> and <code>/register</code>
|
|
19
|
+
</p>
|
|
20
|
+
<p style="color: #34d399;">
|
|
21
|
+
✅ The component is NOT re-rendered when switching between these paths!
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="demo-card">
|
|
26
|
+
<h2>Switch Paths (No Re-render)</h2>
|
|
27
|
+
<div style="display: flex; gap: 10px;">
|
|
28
|
+
<button @click="navigate('/login')" class="cmp-btn">Go to /login</button>
|
|
29
|
+
<button @click="navigate('/register')" class="cmp-btn" style="border-color: #8b8bff; color: #8b8bff;">Go to /register</button>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="demo-card">
|
|
34
|
+
<h2>Test Re-render</h2>
|
|
35
|
+
<button @click="navigate('/about')" class="cmp-btn" style="border-color: #f59e0b; color: #f59e0b;">Go to /about (causes re-render)</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
`
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
package/app/routes/Home.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Counter } from '../components/Counter.js'
|
|
1
2
|
import { UserForm } from '../components/UserForm.js'
|
|
2
3
|
import { CounterSection } from '../components/CounterSection.js'
|
|
3
4
|
import { Card } from '../components/Card.js'
|
|
@@ -98,6 +99,14 @@ export const Home = defineComponent({
|
|
|
98
99
|
<p>This card uses only the default slot - no named slots provided.</p>
|
|
99
100
|
{{ /Card }}
|
|
100
101
|
|
|
102
|
+
<!-- 🧪 INTERPOLATION TEST -->
|
|
103
|
+
<h3 style="margin-top: 40px; margin-bottom: 20px; color: #e83e8c;">🔤 INTERPOLATION TEST</h3>
|
|
104
|
+
<div style="background: #2a2a2a; padding: 15px; border-radius: 8px; border: 1px solid #444;">
|
|
105
|
+
<p><strong>Escaped Braces:</strong> \\{{ this is not evaluated }}</p>
|
|
106
|
+
<p><strong>String Literal with Braces:</strong> {{ "Look, {{ braces }} inside a string!" }}</p>
|
|
107
|
+
<p><strong>Dynamic + Escaped:</strong> {{ isActive ? 'Currently: \\{{ Active }}' : 'Currently: \\{{ Inactive }}' }}</p>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
101
110
|
<!-- 🧪 PROVIDE / INJECT TEST -->
|
|
102
111
|
<h3 style="margin-top: 40px; margin-bottom: 20px; color: #4CAF50;">🔗 PROVIDE / INJECT TEST</h3>
|
|
103
112
|
{{ ParentComponent }}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const NotFound = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
return {
|
|
6
|
+
navigate,
|
|
7
|
+
template: `
|
|
8
|
+
<div class="page">
|
|
9
|
+
<h1 style="color: #ff6b6b;">404 - Page Not Found</h1>
|
|
10
|
+
<p>The requested route does not exist.</p>
|
|
11
|
+
<button @click="navigate('/')" class="primary-btn">Go Home</button>
|
|
12
|
+
</div>
|
|
13
|
+
`
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const NotFound = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
return {
|
|
6
|
+
navigate,
|
|
7
|
+
template: `
|
|
8
|
+
<div class="page">
|
|
9
|
+
<h1 style="color: #ff6b6b;">404 - Page Not Found</h1>
|
|
10
|
+
<p>The requested route does not exist.</p>
|
|
11
|
+
<button @click="navigate('/')" class="primary-btn">Go Home</button>
|
|
12
|
+
</div>
|
|
13
|
+
`
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { currentRoute, navigate, routeParams, routeQuery, computed } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const RouterExample = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const path = currentRoute
|
|
6
|
+
const params = routeParams
|
|
7
|
+
const query = routeQuery
|
|
8
|
+
const paramsJson = computed(() => JSON.stringify(params()))
|
|
9
|
+
const queryJson = computed(() => JSON.stringify(query()))
|
|
10
|
+
|
|
11
|
+
const updateQuery = () => {
|
|
12
|
+
const now = new Date().toLocaleTimeString()
|
|
13
|
+
navigate(`${path()}?t=${encodeURIComponent(now)}&user=guest`)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
path,
|
|
18
|
+
paramsJson,
|
|
19
|
+
queryJson,
|
|
20
|
+
navigate,
|
|
21
|
+
updateQuery,
|
|
22
|
+
template: `
|
|
23
|
+
<div class="page">
|
|
24
|
+
<h1>🔗 Router API Demo</h1>
|
|
25
|
+
|
|
26
|
+
<div class="demo-card">
|
|
27
|
+
<h2>currentRoute Signal</h2>
|
|
28
|
+
<code style="color: #8b8bff; font-size: 1.5rem;">{{ path }}</code>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="demo-card">
|
|
32
|
+
<h2>routeQuery Signal (Query Params)</h2>
|
|
33
|
+
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
34
|
+
<a href="/router?search=framework&page=1" f-link class="cmp-btn" style="text-decoration: none;">?search=framework&page=1</a>
|
|
35
|
+
<a href="/router?filter=active&sort=desc" f-link class="cmp-btn" style="text-decoration: none;">?filter=active&sort=desc</a>
|
|
36
|
+
<button @click="updateQuery" class="cmp-btn">Update Time via Query</button>
|
|
37
|
+
</div>
|
|
38
|
+
<code style="color: #f59e0b; font-size: 1.2rem;">{{ queryJson }}</code>
|
|
39
|
+
<p style="margin-top: 10px; color: #888; font-size: 0.9rem;">
|
|
40
|
+
Query parameters are automatically parsed into the routeQuery signal.
|
|
41
|
+
</p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="demo-card">
|
|
45
|
+
<h2>routeParams Signal (Dynamic Routes)</h2>
|
|
46
|
+
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
47
|
+
<a href="/users/123" f-link class="cmp-btn" style="text-decoration: none;">User 123</a>
|
|
48
|
+
<a href="/users/456" f-link class="cmp-btn" style="text-decoration: none;">User 456</a>
|
|
49
|
+
<a href="/posts/tech/my-post" f-link class="cmp-btn" style="text-decoration: none;">Post Route</a>
|
|
50
|
+
</div>
|
|
51
|
+
<code style="color: #34d399; font-size: 1.2rem;">{{ paramsJson }}</code>
|
|
52
|
+
<p style="margin-top: 10px; color: #888; font-size: 0.9rem;">
|
|
53
|
+
Dynamic routes use :param syntax (e.g., /users/:id or /posts/:category/:slug)
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="demo-card">
|
|
58
|
+
<h2>navigate() Test</h2>
|
|
59
|
+
<div style="display: flex; gap: 10px;">
|
|
60
|
+
<button @click="navigate('/')" class="cmp-btn">Home</button>
|
|
61
|
+
<button @click="navigate('/about')" class="cmp-btn">About</button>
|
|
62
|
+
<button @click="navigate('/404')" class="cmp-btn" style="border-color: #ff6b6b; color: #ff6b6b;">404 Page</button>
|
|
63
|
+
<button @click="navigate('/wildcard-test-123')" class="cmp-btn" style="border-color: #f59e0b; color: #f59e0b;">Wildcard</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="demo-card">
|
|
68
|
+
<h2>f-link Test</h2>
|
|
69
|
+
<div style="display: flex; gap: 10px;">
|
|
70
|
+
<a href="/" f-link class="cmp-btn" style="text-decoration: none;">Home</a>
|
|
71
|
+
<a href="/about" f-link class="cmp-btn" style="text-decoration: none;">About</a>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
`
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { currentRoute, navigate, routeParams, computed } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const RouterExample = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const path = currentRoute
|
|
6
|
+
const params = routeParams
|
|
7
|
+
const paramsJson = computed(() => JSON.stringify(params()))
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
path,
|
|
11
|
+
paramsJson,
|
|
12
|
+
navigate,
|
|
13
|
+
template: `
|
|
14
|
+
<div class="page">
|
|
15
|
+
<h1>🔗 Router API Demo</h1>
|
|
16
|
+
|
|
17
|
+
<div class="demo-card">
|
|
18
|
+
<h2>currentRoute Signal</h2>
|
|
19
|
+
<code style="color: #8b8bff; font-size: 1.5rem;">{{ path }}</code>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="demo-card">
|
|
23
|
+
<h2>routeParams Signal (Dynamic Routes)</h2>
|
|
24
|
+
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
25
|
+
<a href="/users/123" f-link class="cmp-btn" style="text-decoration: none;">User 123</a>
|
|
26
|
+
<a href="/users/456" f-link class="cmp-btn" style="text-decoration: none;">User 456</a>
|
|
27
|
+
<a href="/posts/tech/my-post" f-link class="cmp-btn" style="text-decoration: none;">Post Route</a>
|
|
28
|
+
</div>
|
|
29
|
+
<code style="color: #34d399; font-size: 1.2rem;">{{ paramsJson }}</code>
|
|
30
|
+
<p style="margin-top: 10px; color: #888; font-size: 0.9rem;">
|
|
31
|
+
Dynamic routes use :param syntax (e.g., /users/:id or /posts/:category/:slug)
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="demo-card">
|
|
36
|
+
<h2>navigate() Test</h2>
|
|
37
|
+
<div style="display: flex; gap: 10px;">
|
|
38
|
+
<button @click="navigate('/')" class="cmp-btn">Home</button>
|
|
39
|
+
<button @click="navigate('/about')" class="cmp-btn">About</button>
|
|
40
|
+
<button @click="navigate('/wildcard-test-123')" class="cmp-btn" style="border-color: #f59e0b; color: #f59e0b;">Wildcard</button>
|
|
41
|
+
<button @click="navigate('/nonexistent')" class="cmp-btn" style="border-color: #ff6b6b; color: #ff6b6b;">404 Test</button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="demo-card">
|
|
46
|
+
<h2>f-link Test</h2>
|
|
47
|
+
<div style="display: flex; gap: 10px;">
|
|
48
|
+
<a href="/" f-link class="cmp-btn" style="text-decoration: none;">Home</a>
|
|
49
|
+
<a href="/about" f-link class="cmp-btn" style="text-decoration: none;">About</a>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
`
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineComponent, signal, resource } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export default defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const userId = signal(1)
|
|
6
|
+
|
|
7
|
+
const [user] = resource(userId, async (id) => {
|
|
8
|
+
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
|
|
9
|
+
if (!response.ok) throw new Error('User not found')
|
|
10
|
+
return response.json()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
userId,
|
|
15
|
+
user,
|
|
16
|
+
next: () => userId(userId() + 1),
|
|
17
|
+
prev: () => userId() > 1 && userId(userId() - 1),
|
|
18
|
+
template: `
|
|
19
|
+
<div class="p-4">
|
|
20
|
+
<h1 class="text-2xl font-bold mb-4">Resource Demo</h1>
|
|
21
|
+
|
|
22
|
+
<div class="flex gap-2 mb-4">
|
|
23
|
+
<button @click="prev" class="px-4 py-2 bg-blue-500 text-white rounded">Prev User</button>
|
|
24
|
+
<button @click="next" class="px-4 py-2 bg-blue-500 text-white rounded">Next User</button>
|
|
25
|
+
<span class="py-2">User ID: {{ userId }}</span>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div f-if="user.loading()">
|
|
29
|
+
<p class="text-gray-500 italic">Loading user data...</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div f-if="user.error()">
|
|
33
|
+
<p class="text-red-500">Error: {{ user.error().message }}</p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div f-if="user() && !user.loading()">
|
|
37
|
+
<div class="card border p-4 rounded shadow-lg bg-white">
|
|
38
|
+
<h2 class="text-xl font-semibold">{{ user().name }}</h2>
|
|
39
|
+
<p class="text-gray-600">Email: {{ user().email }}</p>
|
|
40
|
+
<p class="text-gray-600">Company: {{ user().company.name }}</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { currentRoute, navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const WildcardDemo = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const path = currentRoute
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
path,
|
|
9
|
+
navigate,
|
|
10
|
+
template: `
|
|
11
|
+
<div class="page">
|
|
12
|
+
<h1 style="color: #f59e0b;">🎯 Wildcard Match!</h1>
|
|
13
|
+
<p>This route matched via wildcard <code>path: '*'</code></p>
|
|
14
|
+
|
|
15
|
+
<div class="demo-card">
|
|
16
|
+
<h2>Current Path</h2>
|
|
17
|
+
<code style="color: #f59e0b; font-size: 1.5rem;">{{ path }}</code>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="demo-card">
|
|
21
|
+
<h2>Test Wildcard</h2>
|
|
22
|
+
<p>Any path works here:</p>
|
|
23
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
|
24
|
+
<button @click="navigate('/random-123')" class="cmp-btn">/random-123</button>
|
|
25
|
+
<button @click="navigate('/test/abc')" class="cmp-btn">/test/abc</button>
|
|
26
|
+
<button @click="navigate('/deep/nested/path')" class="cmp-btn">/deep/nested/path</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<button @click="navigate('/')" class="primary-btn">Back Home</button>
|
|
31
|
+
</div>
|
|
32
|
+
`
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { currentRoute, navigate } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export const WildcardDemo = defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const path = currentRoute
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
path,
|
|
9
|
+
navigate,
|
|
10
|
+
template: `
|
|
11
|
+
<div class="page">
|
|
12
|
+
<h1 style="color: #f59e0b;">🎯 Wildcard Match!</h1>
|
|
13
|
+
<p>This route matched via wildcard <code>path: '*'</code></p>
|
|
14
|
+
|
|
15
|
+
<div class="demo-card">
|
|
16
|
+
<h2>Current Path</h2>
|
|
17
|
+
<code style="color: #f59e0b; font-size: 1.5rem;">{{ path }}</code>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="demo-card">
|
|
21
|
+
<h2>Test Wildcard</h2>
|
|
22
|
+
<p>Any path works here:</p>
|
|
23
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
|
24
|
+
<button @click="navigate('/random-123')" class="cmp-btn">/random-123</button>
|
|
25
|
+
<button @click="navigate('/test/abc')" class="cmp-btn">/test/abc</button>
|
|
26
|
+
<button @click="navigate('/deep/nested/path')" class="cmp-btn">/deep/nested/path</button>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<button @click="navigate('/')" class="primary-btn">Back Home</button>
|
|
31
|
+
</div>
|
|
32
|
+
`
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
package/app/services.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { InjectionToken, signal } from '../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export class LoggerService {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.id = Math.random().toString(36).substr(2, 9);
|
|
6
|
+
this.logs = signal([]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
log(message) {
|
|
10
|
+
console.log(`[Logger ${this.id}] ${message}`);
|
|
11
|
+
this.logs([...this.logs(), { time: new Date().toLocaleTimeString(), message }]);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const APP_CONFIG = new InjectionToken('app-config');
|
package/auto-imports.d.ts
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
// biome-ignore lint: disable
|
|
7
7
|
export {}
|
|
8
8
|
declare global {
|
|
9
|
+
const MutationType: typeof import('fusee-framework').MutationType
|
|
9
10
|
const batch: typeof import('fusee-framework').batch
|
|
11
|
+
const clearStores: typeof import('fusee-framework').clearStores
|
|
10
12
|
const computed: typeof import('fusee-framework').computed
|
|
11
13
|
const defineComponent: typeof import('fusee-framework').defineComponent
|
|
12
14
|
const defineStore: typeof import('fusee-framework').defineStore
|
|
@@ -17,10 +19,17 @@ declare global {
|
|
|
17
19
|
const inspect: typeof import('fusee-framework').inspect
|
|
18
20
|
const mountOutlet: typeof import('fusee-framework').mountOutlet
|
|
19
21
|
const mountTemplate: typeof import('fusee-framework').mountTemplate
|
|
22
|
+
const onCleanup: typeof import('fusee-framework').onCleanup
|
|
20
23
|
const onMount: typeof import('fusee-framework').onMount
|
|
21
24
|
const onUnmount: typeof import('fusee-framework').onUnmount
|
|
22
25
|
const provide: typeof import('fusee-framework').provide
|
|
26
|
+
const registerStorePlugin: typeof import('fusee-framework').registerStorePlugin
|
|
27
|
+
const resetStore: typeof import('fusee-framework').resetStore
|
|
23
28
|
const signal: typeof import('fusee-framework').signal
|
|
29
|
+
const storeToGetters: typeof import('fusee-framework').storeToGetters
|
|
30
|
+
const storeToRefs: typeof import('fusee-framework').storeToRefs
|
|
31
|
+
const storeToState: typeof import('fusee-framework').storeToState
|
|
24
32
|
const untrack: typeof import('fusee-framework').untrack
|
|
33
|
+
const useNestedStore: typeof import('fusee-framework').useNestedStore
|
|
25
34
|
const watch: typeof import('fusee-framework').watch
|
|
26
35
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
function getBinaryName() {
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
const arch = process.arch;
|
|
12
|
+
|
|
13
|
+
if (platform === 'win32') return 'create-fusee-win.exe';
|
|
14
|
+
if (platform === 'darwin') {
|
|
15
|
+
return arch === 'arm64' ? 'create-fusee-mac-arm64' : 'create-fusee-mac-intel';
|
|
16
|
+
}
|
|
17
|
+
if (platform === 'linux') return 'create-fusee-linux';
|
|
18
|
+
|
|
19
|
+
return 'create-fusee-linux'; // Default fallback
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const binName = getBinaryName();
|
|
23
|
+
const binPath = path.join(__dirname, binName);
|
|
24
|
+
|
|
25
|
+
// Fallback for local development if specific named binary isn't there
|
|
26
|
+
const finalPath = fs.existsSync(binPath) ? binPath : path.join(__dirname, 'create-fusee.exe');
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(finalPath)) {
|
|
29
|
+
console.error(`\n❌ Error: Fusée binary not found for your platform (${process.platform})`);
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const args = process.argv.slice(2);
|
|
34
|
+
const child = spawn(finalPath, args, { stdio: 'inherit' });
|
|
35
|
+
|
|
36
|
+
child.on('exit', (code) => {
|
|
37
|
+
process.exit(code);
|
|
38
|
+
});
|