jac-client 0.2.13__py3-none-any.whl → 0.2.15__py3-none-any.whl
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.
- jac_client/examples/all-in-one/components/Header.jac +1 -1
- jac_client/examples/all-in-one/components/ProfitOverview.jac +1 -1
- jac_client/examples/all-in-one/components/Summary.jac +1 -1
- jac_client/examples/all-in-one/components/TransactionList.jac +2 -2
- jac_client/examples/all-in-one/components/navigation.jac +3 -9
- jac_client/examples/all-in-one/context/BudgetContext.jac +1 -1
- jac_client/examples/all-in-one/main.jac +5 -386
- jac_client/examples/all-in-one/pages/(auth)/index.jac +299 -0
- jac_client/examples/all-in-one/pages/{nestedDemo.jac → (auth)/nested.jac} +3 -13
- jac_client/examples/all-in-one/pages/{loginPage.jac → (public)/login.jac} +1 -1
- jac_client/examples/all-in-one/pages/{signupPage.jac → (public)/signup.jac} +1 -1
- jac_client/examples/all-in-one/pages/{notFound.jac → [...notFound].jac} +2 -1
- jac_client/examples/all-in-one/pages/budget.jac +11 -0
- jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +1 -1
- jac_client/examples/all-in-one/pages/features.jac +8 -0
- jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +7 -7
- jac_client/examples/all-in-one/pages/{LandingPage.jac → landing.jac} +4 -9
- jac_client/examples/all-in-one/pages/layout.jac +20 -0
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +1 -1
- jac_client/plugin/client_runtime.cl.jac +4 -2
- jac_client/plugin/impl/client_runtime.impl.jac +12 -1
- jac_client/plugin/plugin_config.jac +4 -11
- jac_client/plugin/src/compiler.jac +15 -1
- jac_client/plugin/src/impl/compiler.impl.jac +216 -23
- jac_client/plugin/src/impl/package_installer.impl.jac +3 -2
- jac_client/plugin/src/impl/route_scanner.impl.jac +201 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +15 -11
- jac_client/plugin/src/route_scanner.jac +44 -0
- jac_client/plugin/utils/impl/bun_installer.impl.jac +16 -19
- jac_client/plugin/utils/impl/client_deps.impl.jac +12 -16
- jac_client/templates/fullstack.jacpack +3 -2
- jac_client/tests/test_e2e.py +19 -28
- jac_client/tests/test_it.py +247 -0
- {jac_client-0.2.13.dist-info → jac_client-0.2.15.dist-info}/METADATA +2 -2
- {jac_client-0.2.13.dist-info → jac_client-0.2.15.dist-info}/RECORD +40 -36
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +0 -140
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +0 -157
- {jac_client-0.2.13.dist-info → jac_client-0.2.15.dist-info}/WHEEL +0 -0
- {jac_client-0.2.13.dist-info → jac_client-0.2.15.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.13.dist-info → jac_client-0.2.15.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
cl import from react { useEffect, useRef }
|
|
2
|
+
cl import from ...components.Card.tsx { Card }
|
|
3
|
+
sv import from ...main { ping_server, get_server_message, create_todo }
|
|
4
|
+
|
|
5
|
+
cl {
|
|
6
|
+
def:pub page -> JsxElement {
|
|
7
|
+
has count: int = 0,
|
|
8
|
+
pingResult: str = "",
|
|
9
|
+
serverMessage: str = "",
|
|
10
|
+
lastTodoMessage: str = "";
|
|
11
|
+
|
|
12
|
+
can with count entry {
|
|
13
|
+
console.log("Home count changed: ", count);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async def handlePing -> None {
|
|
17
|
+
result = root spawn ping_server();
|
|
18
|
+
if result.reports and result.reports.length > 0 {
|
|
19
|
+
pingResult = result.reports[0][0];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async def loadServerMessage -> None {
|
|
24
|
+
result = root spawn get_server_message();
|
|
25
|
+
if result.reports and result.reports.length > 0 {
|
|
26
|
+
serverMessage = result.reports[0][0];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async def handleCreateSampleTodo -> None {
|
|
31
|
+
result = root spawn create_todo(text="Sample todo from all-in-one app");
|
|
32
|
+
if result.reports and result.reports.length > 0 {
|
|
33
|
+
todo = result.reports[0][0];
|
|
34
|
+
lastTodoMessage = "Created Todo: " + todo.text;
|
|
35
|
+
console.log("Created Todo node:", todo);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async can with entry {
|
|
40
|
+
await loadServerMessage();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
workerRef = useRef(None);
|
|
44
|
+
has message: str = "";
|
|
45
|
+
|
|
46
|
+
useEffect(
|
|
47
|
+
lambda -> None { workerRef.current = Reflect.construct(
|
|
48
|
+
Worker, ["/workers/worker.js"]
|
|
49
|
+
);workerRef.current.onmessage = lambda event: any -> None { console.log(
|
|
50
|
+
"Message received from worker:", event.data
|
|
51
|
+
);message = event.data;};return (
|
|
52
|
+
lambda -> None { workerRef.current.terminate();}
|
|
53
|
+
); },
|
|
54
|
+
[]
|
|
55
|
+
);
|
|
56
|
+
handleClick = lambda -> None { workerRef.current.postMessage("");};
|
|
57
|
+
|
|
58
|
+
return
|
|
59
|
+
<div
|
|
60
|
+
style={{
|
|
61
|
+
"padding": "2rem",
|
|
62
|
+
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<h1>
|
|
66
|
+
🍔 Router + Styling + Assets Demo
|
|
67
|
+
</h1>
|
|
68
|
+
<p>
|
|
69
|
+
This home page combines
|
|
70
|
+
{" "}
|
|
71
|
+
<strong>
|
|
72
|
+
React Router,
|
|
73
|
+
</strong>
|
|
74
|
+
{" "}
|
|
75
|
+
<strong>
|
|
76
|
+
pure CSS styling,
|
|
77
|
+
</strong>
|
|
78
|
+
{" "}
|
|
79
|
+
<strong>
|
|
80
|
+
staticassets
|
|
81
|
+
</strong>
|
|
82
|
+
{" "}
|
|
83
|
+
and
|
|
84
|
+
{" "}
|
|
85
|
+
<strong>
|
|
86
|
+
nested folder imports
|
|
87
|
+
</strong>
|
|
88
|
+
</p>
|
|
89
|
+
<div className="container">
|
|
90
|
+
<h2
|
|
91
|
+
style={{
|
|
92
|
+
"color": "white",
|
|
93
|
+
"textShadow": "2px 2px 4px rgba(0,0,0,0.6)"
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
CSS Background Image
|
|
97
|
+
</h2>
|
|
98
|
+
<p
|
|
99
|
+
style={{
|
|
100
|
+
"color": "white",
|
|
101
|
+
"maxWidth": "480px",
|
|
102
|
+
"textShadow": "1px 1px 3px rgba(0,0,0,0.7)"
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
This section uses the burger image as a background via CSS, just like the
|
|
106
|
+
{" "}
|
|
107
|
+
<code>
|
|
108
|
+
asset-serving/css-with-image
|
|
109
|
+
</code>
|
|
110
|
+
{" "}
|
|
111
|
+
example.
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
<Card
|
|
115
|
+
title="TypeScript Card Component"
|
|
116
|
+
description="This card is built with TypeScript and demonstrates type-safe component usage in Jac"
|
|
117
|
+
variant="highlighted"
|
|
118
|
+
>
|
|
119
|
+
<p style={{"margin": "0.5rem 0", "color": "#374151"}}>
|
|
120
|
+
This is a TypeScript component imported and used in Jac code!
|
|
121
|
+
</p>
|
|
122
|
+
</Card>
|
|
123
|
+
<div className="card">
|
|
124
|
+
<h3>
|
|
125
|
+
Direct <img> asset
|
|
126
|
+
</h3>
|
|
127
|
+
<img
|
|
128
|
+
src="/static/assets/burger.png"
|
|
129
|
+
alt="Burger asset served by Jac"
|
|
130
|
+
className="burgerImage"
|
|
131
|
+
/>
|
|
132
|
+
<p style={{"marginTop": "0.75rem", "color": "#555"}}>
|
|
133
|
+
This image is served from the project
|
|
134
|
+
{" "}
|
|
135
|
+
<code>
|
|
136
|
+
assets/
|
|
137
|
+
</code>
|
|
138
|
+
{" "}
|
|
139
|
+
folder using the
|
|
140
|
+
{" "}
|
|
141
|
+
<code>
|
|
142
|
+
/static/assets/
|
|
143
|
+
</code>
|
|
144
|
+
{" "}
|
|
145
|
+
path.
|
|
146
|
+
</p>
|
|
147
|
+
</div>
|
|
148
|
+
<div style={{"marginTop": "2rem"}}>
|
|
149
|
+
<h3>
|
|
150
|
+
Counter with pure CSS classes
|
|
151
|
+
</h3>
|
|
152
|
+
<p>
|
|
153
|
+
You've clicked the burger
|
|
154
|
+
{" "}
|
|
155
|
+
<strong>
|
|
156
|
+
{count}
|
|
157
|
+
</strong>
|
|
158
|
+
{" "}
|
|
159
|
+
times.
|
|
160
|
+
</p>
|
|
161
|
+
<button
|
|
162
|
+
onClick={lambda e: any -> None { count = count + 1;}}
|
|
163
|
+
style={{
|
|
164
|
+
"padding": "0.6rem 1.4rem",
|
|
165
|
+
"fontSize": "1rem",
|
|
166
|
+
"backgroundColor": "#ff6b35",
|
|
167
|
+
"color": "white",
|
|
168
|
+
"border": "none",
|
|
169
|
+
"borderRadius": "6px",
|
|
170
|
+
"cursor": "pointer",
|
|
171
|
+
"boxShadow": "0 2px 4px rgba(0,0,0,0.2)"
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
Click the Burger! 🍔
|
|
175
|
+
</button>
|
|
176
|
+
</div>
|
|
177
|
+
<div style={{"marginTop": "2rem"}}>
|
|
178
|
+
<h3>
|
|
179
|
+
Backend Walkers
|
|
180
|
+
</h3>
|
|
181
|
+
<p>
|
|
182
|
+
Basic example walkers:
|
|
183
|
+
{" "}
|
|
184
|
+
<code>
|
|
185
|
+
ping_server
|
|
186
|
+
</code>
|
|
187
|
+
{" "}
|
|
188
|
+
and
|
|
189
|
+
{" "}
|
|
190
|
+
<code>
|
|
191
|
+
get_server_message
|
|
192
|
+
</code>
|
|
193
|
+
</p>
|
|
194
|
+
<button
|
|
195
|
+
onClick={lambda e: any -> None { handlePing();}}
|
|
196
|
+
style={{
|
|
197
|
+
"padding": "0.5rem 1.2rem",
|
|
198
|
+
"marginRight": "0.75rem",
|
|
199
|
+
"backgroundColor": "#3b82f6",
|
|
200
|
+
"color": "white",
|
|
201
|
+
"border": "none",
|
|
202
|
+
"borderRadius": "6px",
|
|
203
|
+
"cursor": "pointer"
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
Ping Backend
|
|
207
|
+
</button>
|
|
208
|
+
<button
|
|
209
|
+
onClick={lambda e: any -> None { handleCreateSampleTodo();}}
|
|
210
|
+
style={{
|
|
211
|
+
"padding": "0.5rem 1.2rem",
|
|
212
|
+
"backgroundColor": "#10b981",
|
|
213
|
+
"color": "white",
|
|
214
|
+
"border": "none",
|
|
215
|
+
"borderRadius": "6px",
|
|
216
|
+
"cursor": "pointer"
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
Create Sample Todo
|
|
220
|
+
</button>
|
|
221
|
+
{pingResult
|
|
222
|
+
and (
|
|
223
|
+
<span style={{"marginLeft": "0.5rem", "color": "#374151"}}>
|
|
224
|
+
Result:
|
|
225
|
+
{" "}
|
|
226
|
+
<code>
|
|
227
|
+
{pingResult}
|
|
228
|
+
</code>
|
|
229
|
+
</span>
|
|
230
|
+
)}
|
|
231
|
+
{serverMessage
|
|
232
|
+
and (
|
|
233
|
+
<p style={{"marginTop": "0.75rem", "color": "#374151"}}>
|
|
234
|
+
Message:
|
|
235
|
+
{" "}
|
|
236
|
+
<code>
|
|
237
|
+
{serverMessage}
|
|
238
|
+
</code>
|
|
239
|
+
</p>
|
|
240
|
+
)}
|
|
241
|
+
{lastTodoMessage
|
|
242
|
+
and (
|
|
243
|
+
<p style={{"marginTop": "0.5rem", "color": "#111827"}}>
|
|
244
|
+
{lastTodoMessage}
|
|
245
|
+
</p>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
<div style={{"marginTop": "2rem"}}>
|
|
249
|
+
<h3>
|
|
250
|
+
Web Worker
|
|
251
|
+
</h3>
|
|
252
|
+
<p>
|
|
253
|
+
This demonstrates how to communicate with a
|
|
254
|
+
{" "}
|
|
255
|
+
<strong>
|
|
256
|
+
Python backend worker
|
|
257
|
+
</strong>
|
|
258
|
+
{" "}
|
|
259
|
+
using Web Workers for asynchronous processing.
|
|
260
|
+
</p>
|
|
261
|
+
<p
|
|
262
|
+
style={{
|
|
263
|
+
"fontSize": "0.9rem",
|
|
264
|
+
"color": "#666",
|
|
265
|
+
"marginTop": "0.5rem"
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
File:
|
|
269
|
+
{" "}
|
|
270
|
+
<code>
|
|
271
|
+
worker.py
|
|
272
|
+
</code>
|
|
273
|
+
{" "}
|
|
274
|
+
— Runs in a separate thread to avoid blocking the UI.
|
|
275
|
+
</p>
|
|
276
|
+
<button
|
|
277
|
+
onClick={lambda -> None { handleClick();}}
|
|
278
|
+
style={{
|
|
279
|
+
"padding": "0.5rem 1.2rem",
|
|
280
|
+
"marginRight": "0.75rem",
|
|
281
|
+
"backgroundColor": "#d73bf6ff",
|
|
282
|
+
"color": "white",
|
|
283
|
+
"border": "none",
|
|
284
|
+
"borderRadius": "6px",
|
|
285
|
+
"cursor": "pointer"
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
Call Python Worker
|
|
289
|
+
</button>
|
|
290
|
+
{message
|
|
291
|
+
&& (
|
|
292
|
+
<p style={{marginTop: "1rem", fontWeight: "bold"}}>
|
|
293
|
+
{message}
|
|
294
|
+
</p>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
</div>;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
cl import from
|
|
3
|
-
CustomButton
|
|
4
|
-
}
|
|
5
|
-
cl import from ..button { CustomButtonRoot }
|
|
6
|
-
cl import from "@jac/runtime" { Navigate, jacIsLoggedIn }
|
|
1
|
+
cl import from ...components.nestedDemo.CustomButton.tsx { CustomButton }
|
|
2
|
+
cl import from ...button { CustomButtonRoot }
|
|
7
3
|
|
|
8
4
|
cl {
|
|
9
|
-
def:pub
|
|
10
|
-
# Check if user is logged in, redirect if not
|
|
11
|
-
if not jacIsLoggedIn() {
|
|
12
|
-
return
|
|
13
|
-
<Navigate to="/login" />;
|
|
14
|
-
}
|
|
15
|
-
|
|
5
|
+
def:pub page -> JsxElement {
|
|
16
6
|
return
|
|
17
7
|
<div
|
|
18
8
|
style={{
|
|
@@ -12,7 +12,7 @@ cl import from ..components.CategoryFilter { CategoryFilter }
|
|
|
12
12
|
cl import from ..constants.categories { CATEGORY_COLORS }
|
|
13
13
|
|
|
14
14
|
# cl import from "..components.PieChart.tsx" { PieChart }
|
|
15
|
-
cl def:pub BudgetPlanner ->
|
|
15
|
+
cl def:pub BudgetPlanner -> JsxElement {
|
|
16
16
|
[filter, setFilter] = useState("ALL");
|
|
17
17
|
budget = useBudgetContext();
|
|
18
18
|
|
|
@@ -21,7 +21,7 @@ import from ..utils.helpers {
|
|
|
21
21
|
import from ..utils.formatters { formatCurrency }
|
|
22
22
|
|
|
23
23
|
# Import walkers from server module
|
|
24
|
-
sv import from
|
|
24
|
+
sv import from ..main {
|
|
25
25
|
create_test_data,
|
|
26
26
|
read_test_data,
|
|
27
27
|
update_test_data,
|
|
@@ -38,7 +38,7 @@ sv import from .FeaturesTest {
|
|
|
38
38
|
# Button component demonstrating props - NEW PATTERN: Direct parameters
|
|
39
39
|
def:pub TestButton(
|
|
40
40
|
text: str, onClick: any, variant: str
|
|
41
|
-
) ->
|
|
41
|
+
) -> JsxElement {
|
|
42
42
|
bg_color = "#3b82f6" if variant == "primary" else "#6b7280";
|
|
43
43
|
hover_color = "#2563eb" if variant == "primary" else "#4b5563";
|
|
44
44
|
|
|
@@ -66,7 +66,7 @@ def:pub TestButton(
|
|
|
66
66
|
# # Card component with props - NEW PATTERN: Direct parameters
|
|
67
67
|
def:pub TestCard(
|
|
68
68
|
title: str, children: any, color: str
|
|
69
|
-
) ->
|
|
69
|
+
) -> JsxElement {
|
|
70
70
|
return
|
|
71
71
|
<div
|
|
72
72
|
style={{
|
|
@@ -106,7 +106,7 @@ def:pub TestCard(
|
|
|
106
106
|
# Result display component - NEW PATTERN: Direct parameters
|
|
107
107
|
def:pub ResultDisplay(
|
|
108
108
|
data: any, label: str
|
|
109
|
-
) ->
|
|
109
|
+
) -> JsxElement {
|
|
110
110
|
if not data {
|
|
111
111
|
return
|
|
112
112
|
<div style={{"color": "#9ca3af", "fontStyle": "italic"}}>
|
|
@@ -146,7 +146,7 @@ def:pub ResultDisplay(
|
|
|
146
146
|
# ============================================================================
|
|
147
147
|
# MAIN PAGE COMPONENT
|
|
148
148
|
# ============================================================================
|
|
149
|
-
def:pub FeaturesTest ->
|
|
149
|
+
def:pub FeaturesTest -> JsxElement {
|
|
150
150
|
navigate = useNavigate();
|
|
151
151
|
|
|
152
152
|
# State management
|
|
@@ -408,7 +408,7 @@ def:pub FeaturesTest -> any {
|
|
|
408
408
|
No data yet. Create some!
|
|
409
409
|
</p>}
|
|
410
410
|
{testData.map(
|
|
411
|
-
lambda item: any ->
|
|
411
|
+
lambda item: any -> JsxElement { return
|
|
412
412
|
<div
|
|
413
413
|
key={item._jac_id}
|
|
414
414
|
style={{
|
|
@@ -480,7 +480,7 @@ def:pub FeaturesTest -> any {
|
|
|
480
480
|
}}
|
|
481
481
|
>
|
|
482
482
|
{Object.keys(string_demos).map(
|
|
483
|
-
lambda key: str ->
|
|
483
|
+
lambda key: str -> JsxElement { return
|
|
484
484
|
<div key={key} style={{"marginBottom": "5px"}}>
|
|
485
485
|
<span style={{"fontWeight": "600"}}>
|
|
486
486
|
{key}:
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
# Professional landing page with hero, features, and CTA sections
|
|
3
|
-
# Demonstrates: routing with Link, dark theme design, responsive layout
|
|
4
|
-
cl import from "@jac/runtime" {
|
|
5
|
-
Link,
|
|
6
|
-
}
|
|
1
|
+
cl import from "@jac/runtime" { Link }
|
|
7
2
|
|
|
8
3
|
cl {
|
|
9
|
-
def:pub
|
|
4
|
+
def:pub page -> JsxElement {
|
|
10
5
|
return
|
|
11
6
|
<div className="landing-container">
|
|
12
7
|
<section className="hero-section">
|
|
@@ -29,7 +24,7 @@ cl {
|
|
|
29
24
|
"justifyContent": "center"
|
|
30
25
|
}}
|
|
31
26
|
>
|
|
32
|
-
<Link to="/
|
|
27
|
+
<Link to="/budget">
|
|
33
28
|
<button className="cta-button">
|
|
34
29
|
Let's Plan
|
|
35
30
|
</button>
|
|
@@ -101,7 +96,7 @@ cl {
|
|
|
101
96
|
"justifyContent": "center"
|
|
102
97
|
}}
|
|
103
98
|
>
|
|
104
|
-
<Link to="/
|
|
99
|
+
<Link to="/budget">
|
|
105
100
|
<button className="cta-button">
|
|
106
101
|
Get Started Free
|
|
107
102
|
</button>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
cl import from "@jac/runtime" { Outlet }
|
|
2
|
+
cl import from ..components.navigation { Navigation }
|
|
3
|
+
|
|
4
|
+
cl {
|
|
5
|
+
def:pub layout -> JsxElement {
|
|
6
|
+
return
|
|
7
|
+
<>
|
|
8
|
+
<Navigation />
|
|
9
|
+
<div
|
|
10
|
+
style={{
|
|
11
|
+
"maxWidth": "960px",
|
|
12
|
+
"margin": "0 auto",
|
|
13
|
+
"padding": "0 1rem 3rem 1rem"
|
|
14
|
+
}}
|
|
15
|
+
>
|
|
16
|
+
<Outlet />
|
|
17
|
+
</div>
|
|
18
|
+
</>;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -9,13 +9,14 @@ import from 'react-router-dom' {
|
|
|
9
9
|
Route as ReactRouterRoute,
|
|
10
10
|
Link as ReactRouterLink,
|
|
11
11
|
Navigate as ReactRouterNavigate,
|
|
12
|
+
Outlet as ReactRouterOutlet,
|
|
12
13
|
useNavigate as reactRouterUseNavigate,
|
|
13
14
|
useLocation as reactRouterUseLocation,
|
|
14
15
|
useParams as reactRouterUseParams
|
|
15
16
|
}
|
|
16
17
|
import from 'react-error-boundary' { ErrorBoundary }
|
|
17
18
|
|
|
18
|
-
def:pub __jacJsx(tag: any, props: dict = {}, children: any = []) ->
|
|
19
|
+
def:pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> JsxElement;
|
|
19
20
|
|
|
20
21
|
# React hooks re-exported for auto-injection by `has` variables and `can with entry/exit` effects
|
|
21
22
|
glob:
|
|
@@ -29,6 +30,7 @@ glob:
|
|
|
29
30
|
useNavigate = reactRouterUseNavigate,
|
|
30
31
|
useLocation = reactRouterUseLocation,
|
|
31
32
|
useParams = reactRouterUseParams,
|
|
33
|
+
Outlet = ReactRouterOutlet,
|
|
32
34
|
JacClientErrorBoundary = ErrorBoundary;
|
|
33
35
|
|
|
34
36
|
def:pub useRouter -> dict;
|
|
@@ -44,6 +46,6 @@ def:pub __getApiBaseUrl -> str;
|
|
|
44
46
|
def:pub __getLocalStorage(key: str) -> str;
|
|
45
47
|
def:pub __setLocalStorage(key: str, value: str) -> None;
|
|
46
48
|
def:pub __removeLocalStorage(key: str) -> None;
|
|
49
|
+
def:pub AuthGuard(redirect: str = "/login") -> any;
|
|
47
50
|
def:pub ErrorFallback(error: str, resetErrorBoundary: any) -> any;
|
|
48
51
|
def:pub errorOverlay(filePath: str, errors: str) -> any;
|
|
49
|
-
# React Router components
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
impl __jacJsx(tag: any, props: dict = {}, children: any = []) ->
|
|
1
|
+
impl __jacJsx(tag: any, props: dict = {}, children: any = []) -> JsxElement {
|
|
2
2
|
if tag == None {
|
|
3
3
|
tag = React.Fragment;
|
|
4
4
|
}
|
|
@@ -205,6 +205,17 @@ impl __removeLocalStorage(key: str) -> None {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
"""Auth guard component for file-based routing. Renders child routes if
|
|
209
|
+
authenticated, otherwise redirects to the login path."""
|
|
210
|
+
impl AuthGuard(redirect: str = "/login") -> any {
|
|
211
|
+
if jacIsLoggedIn() {
|
|
212
|
+
return
|
|
213
|
+
<ReactRouterOutlet />;
|
|
214
|
+
}
|
|
215
|
+
return
|
|
216
|
+
<ReactRouterNavigate to={redirect} replace={True} />;
|
|
217
|
+
}
|
|
218
|
+
|
|
208
219
|
impl ErrorFallback(error: str, resetErrorBoundary: any) -> any {
|
|
209
220
|
return
|
|
210
221
|
<div
|
|
@@ -10,7 +10,6 @@ configuration system, registering:
|
|
|
10
10
|
|
|
11
11
|
import os;
|
|
12
12
|
import subprocess;
|
|
13
|
-
import sys;
|
|
14
13
|
import json;
|
|
15
14
|
import from pathlib { Path }
|
|
16
15
|
import from typing { Any }
|
|
@@ -147,9 +146,7 @@ def _load_template(template_name: str) -> dict[str, Any] | None {
|
|
|
147
146
|
|
|
148
147
|
return data;
|
|
149
148
|
} except Exception as e {
|
|
150
|
-
|
|
151
|
-
f"Warning: Could not load {template_name} template: {e}", file=sys.stderr
|
|
152
|
-
);
|
|
149
|
+
console.error(f"Warning: Could not load {template_name} template: {e}");
|
|
153
150
|
return None;
|
|
154
151
|
}
|
|
155
152
|
}
|
|
@@ -191,10 +188,7 @@ def _post_create_client(project_path: Path, project_name: str) -> None {
|
|
|
191
188
|
# Verify jac.toml exists
|
|
192
189
|
toml_path = project_path / "jac.toml";
|
|
193
190
|
if not toml_path.exists() {
|
|
194
|
-
|
|
195
|
-
"Warning: jac.toml not found, skipping package installation",
|
|
196
|
-
file=sys.stderr
|
|
197
|
-
);
|
|
191
|
+
console.error("Warning: jac.toml not found, skipping package installation");
|
|
198
192
|
return;
|
|
199
193
|
}
|
|
200
194
|
|
|
@@ -213,9 +207,8 @@ def _post_create_client(project_path: Path, project_name: str) -> None {
|
|
|
213
207
|
build_package_json = client_dir / 'package.json';
|
|
214
208
|
|
|
215
209
|
if not configs_package_json.exists() {
|
|
216
|
-
|
|
217
|
-
"Warning: package.json was not generated, skipping package installation"
|
|
218
|
-
file=sys.stderr
|
|
210
|
+
console.error(
|
|
211
|
+
"Warning: package.json was not generated, skipping package installation"
|
|
219
212
|
);
|
|
220
213
|
return;
|
|
221
214
|
}
|
|
@@ -8,6 +8,7 @@ import from jaclang.runtimelib.client_bundle { ClientBundleError }
|
|
|
8
8
|
import from .asset_processor { AssetProcessor }
|
|
9
9
|
import from .import_processor { ImportProcessor }
|
|
10
10
|
import from .jac_to_js { JacToJSCompiler }
|
|
11
|
+
import from .route_scanner { RouteScanner, RouteEntry }
|
|
11
12
|
import from .vite_bundler { ViteBundler }
|
|
12
13
|
|
|
13
14
|
with entry {
|
|
@@ -23,12 +24,16 @@ class ViteCompiler {
|
|
|
23
24
|
'Route',
|
|
24
25
|
'Link',
|
|
25
26
|
'Navigate',
|
|
27
|
+
'Outlet',
|
|
26
28
|
'useNavigate',
|
|
27
29
|
'useLocation',
|
|
28
|
-
'useParams'
|
|
30
|
+
'useParams',
|
|
31
|
+
'AuthGuard'
|
|
29
32
|
];
|
|
33
|
+
COMPOUND_EXTENSIONS = ['.cl.jac', '.impl.jac', '.test.jac'];
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
def _jac_path_to_js(self: ViteCompiler, rel_str: str) -> str;
|
|
32
37
|
def init(
|
|
33
38
|
self: ViteCompiler,
|
|
34
39
|
vite_package_json: Path,
|
|
@@ -60,6 +65,15 @@ class ViteCompiler {
|
|
|
60
65
|
|
|
61
66
|
def copy_root_assets(self: ViteCompiler) -> None;
|
|
62
67
|
def create_entry_file(self: ViteCompiler, module_path: Path) -> None;
|
|
68
|
+
def _create_pages_entry_content(self: ViteCompiler, module_path: Path) -> str;
|
|
69
|
+
def _scan_and_compile_pages(
|
|
70
|
+
self: ViteCompiler,
|
|
71
|
+
visited: (set[Path] | None) = None,
|
|
72
|
+
collected_exports: (set[str] | None) = None,
|
|
73
|
+
collected_globals: (dict[(str, Any)] | None) = None
|
|
74
|
+
) -> bool;
|
|
75
|
+
|
|
76
|
+
def _generate_routes_manifest(self: ViteCompiler) -> None;
|
|
63
77
|
def compile_and_bundle(
|
|
64
78
|
self: ViteCompiler, module: ModuleType, module_path: Path
|
|
65
79
|
) -> tuple[str, str, list[str], list[str]];
|