jac-client 0.2.13__py3-none-any.whl → 0.2.14__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.
Files changed (41) hide show
  1. jac_client/examples/all-in-one/components/Header.jac +1 -1
  2. jac_client/examples/all-in-one/components/ProfitOverview.jac +1 -1
  3. jac_client/examples/all-in-one/components/Summary.jac +1 -1
  4. jac_client/examples/all-in-one/components/TransactionList.jac +2 -2
  5. jac_client/examples/all-in-one/components/navigation.jac +3 -9
  6. jac_client/examples/all-in-one/context/BudgetContext.jac +1 -1
  7. jac_client/examples/all-in-one/main.jac +5 -386
  8. jac_client/examples/all-in-one/pages/(auth)/index.jac +299 -0
  9. jac_client/examples/all-in-one/pages/{nestedDemo.jac → (auth)/nested.jac} +3 -13
  10. jac_client/examples/all-in-one/pages/{loginPage.jac → (public)/login.jac} +1 -1
  11. jac_client/examples/all-in-one/pages/{signupPage.jac → (public)/signup.jac} +1 -1
  12. jac_client/examples/all-in-one/pages/{notFound.jac → [...notFound].jac} +2 -1
  13. jac_client/examples/all-in-one/pages/budget.jac +11 -0
  14. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +1 -1
  15. jac_client/examples/all-in-one/pages/features.jac +8 -0
  16. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +7 -7
  17. jac_client/examples/all-in-one/pages/{LandingPage.jac → landing.jac} +4 -9
  18. jac_client/examples/all-in-one/pages/layout.jac +20 -0
  19. jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +1 -1
  20. jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +1 -1
  21. jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +1 -1
  22. jac_client/plugin/client_runtime.cl.jac +4 -2
  23. jac_client/plugin/impl/client_runtime.impl.jac +12 -1
  24. jac_client/plugin/plugin_config.jac +4 -11
  25. jac_client/plugin/src/compiler.jac +15 -1
  26. jac_client/plugin/src/impl/compiler.impl.jac +216 -23
  27. jac_client/plugin/src/impl/package_installer.impl.jac +3 -2
  28. jac_client/plugin/src/impl/route_scanner.impl.jac +201 -0
  29. jac_client/plugin/src/impl/vite_bundler.impl.jac +15 -11
  30. jac_client/plugin/src/route_scanner.jac +44 -0
  31. jac_client/plugin/utils/impl/bun_installer.impl.jac +16 -19
  32. jac_client/plugin/utils/impl/client_deps.impl.jac +12 -16
  33. jac_client/templates/fullstack.jacpack +3 -2
  34. jac_client/tests/test_e2e.py +19 -28
  35. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/METADATA +2 -2
  36. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/RECORD +39 -35
  37. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +0 -140
  38. jac_client/examples/all-in-one/pages/FeaturesTest.jac +0 -157
  39. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/WHEEL +0 -0
  40. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/entry_points.txt +0 -0
  41. {jac_client-0.2.13.dist-info → jac_client-0.2.14.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 &lt;img&gt; 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
- # Nested folder imports (same pattern as nested-basic/)
2
- cl import from ..components.nestedDemo.CustomButton.tsx {
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 NestedImportsDemo -> any {
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={{
@@ -1,7 +1,7 @@
1
1
  cl import from "@jac/runtime" { Link, useNavigate, jacLogin }
2
2
 
3
3
  cl {
4
- def:pub LoginPage -> any {
4
+ def:pub page -> JsxElement {
5
5
  has username: str = "",
6
6
  password: str = "",
7
7
  error: str = "";
@@ -1,7 +1,7 @@
1
1
  cl import from "@jac/runtime" { Link, useNavigate, jacSignup }
2
2
 
3
3
  cl {
4
- def:pub SignupPage -> any {
4
+ def:pub page -> JsxElement {
5
5
  has username: str = "",
6
6
  password: str = "",
7
7
  error: str = "";
@@ -1,7 +1,8 @@
1
1
  cl import from "@jac/runtime" { Link }
2
2
  cl import "..styles/notFoundPage/NotFoundstyle.css";
3
+
3
4
  cl {
4
- def:pub NotFound -> any {
5
+ def:pub page -> JsxElement {
5
6
  return
6
7
  <div className="not-found-container">
7
8
  <h1>
@@ -0,0 +1,11 @@
1
+ cl {
2
+ import from .budget_planner_ui { BudgetPlanner as BudgetPlannerUI }
3
+ import from ..context.BudgetContext { BudgetProvider }
4
+
5
+ def:pub page -> JsxElement {
6
+ return
7
+ <BudgetProvider>
8
+ <BudgetPlannerUI />
9
+ </BudgetProvider>;
10
+ }
11
+ }
@@ -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 -> any {
15
+ cl def:pub BudgetPlanner -> JsxElement {
16
16
  [filter, setFilter] = useState("ALL");
17
17
  budget = useBudgetContext();
18
18
 
@@ -0,0 +1,8 @@
1
+ cl {
2
+ import from .features_test_ui { FeaturesTest as FeaturesTestUI }
3
+
4
+ def:pub page -> JsxElement {
5
+ return
6
+ <FeaturesTestUI />;
7
+ }
8
+ }
@@ -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 .FeaturesTest {
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
- ) -> any {
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
- ) -> any {
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
- ) -> any {
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 -> any {
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 -> any { return
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 -> any { return
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
- # Landing Page - Ledgerly Brand
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 LandingPage -> any {
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="/app">
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="/app">
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
+ }
@@ -1,7 +1,7 @@
1
1
  # Root Level Button
2
2
  cl import from antd { Button }
3
3
 
4
- cl def:pub ButtonRoot -> any {
4
+ cl def:pub ButtonRoot -> JsxElement {
5
5
  return
6
6
  <Button type="primary" size="large">
7
7
  Root Level Button
@@ -2,7 +2,7 @@
2
2
  cl import from antd { Button }
3
3
  cl import from ..ButtonRoot { ButtonRoot }
4
4
 
5
- cl def:pub ButtonSecondL -> any {
5
+ cl def:pub ButtonSecondL -> JsxElement {
6
6
  return
7
7
  <div>
8
8
  <Button type="default" size="large">
@@ -3,7 +3,7 @@ cl import from antd { Button }
3
3
  cl import from ...ButtonRoot { ButtonRoot }
4
4
  cl import from ..ButtonSecondL { ButtonSecondL }
5
5
 
6
- cl def:pub ButtonThirdL -> any {
6
+ cl def:pub ButtonThirdL -> JsxElement {
7
7
  return
8
8
  <div>
9
9
  <Button type="dashed" size="large">
@@ -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 = []) -> 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 = []) -> 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
- print(
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
- print(
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
- print(
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]];