jac-client 0.2.0__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/docs/README.md +659 -0
- jac_client/docs/advanced-state.md +1266 -0
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/guide-example/intro.md +117 -0
- jac_client/docs/guide-example/step-01-setup.md +260 -0
- jac_client/docs/guide-example/step-02-components.md +416 -0
- jac_client/docs/guide-example/step-03-styling.md +478 -0
- jac_client/docs/guide-example/step-04-todo-ui.md +477 -0
- jac_client/docs/guide-example/step-05-local-state.md +530 -0
- jac_client/docs/guide-example/step-06-events.md +750 -0
- jac_client/docs/guide-example/step-07-effects.md +469 -0
- jac_client/docs/guide-example/step-08-walkers.md +534 -0
- jac_client/docs/guide-example/step-09-authentication.md +586 -0
- jac_client/docs/guide-example/step-10-routing.md +540 -0
- jac_client/docs/guide-example/step-11-final.md +964 -0
- jac_client/docs/imports.md +1142 -0
- jac_client/docs/lifecycle-hooks.md +774 -0
- jac_client/docs/routing.md +660 -0
- jac_client/examples/basic/.babelrc +9 -0
- jac_client/examples/basic/README.md +16 -0
- jac_client/examples/basic/app.jac +16 -0
- jac_client/examples/basic/package.json +27 -0
- jac_client/examples/basic/vite.config.js +28 -0
- jac_client/examples/basic-auth/.babelrc +9 -0
- jac_client/examples/basic-auth/README.md +16 -0
- jac_client/examples/basic-auth/app.jac +308 -0
- jac_client/examples/basic-auth/package.json +27 -0
- jac_client/examples/basic-auth/vite.config.js +28 -0
- jac_client/examples/basic-auth-with-router/.babelrc +9 -0
- jac_client/examples/basic-auth-with-router/README.md +60 -0
- jac_client/examples/basic-auth-with-router/app.jac +464 -0
- jac_client/examples/basic-auth-with-router/package.json +28 -0
- jac_client/examples/basic-auth-with-router/vite.config.js +28 -0
- jac_client/examples/basic-full-stack/.babelrc +9 -0
- jac_client/examples/basic-full-stack/README.md +18 -0
- jac_client/examples/basic-full-stack/app.jac +320 -0
- jac_client/examples/basic-full-stack/package.json +28 -0
- jac_client/examples/basic-full-stack/vite.config.js +28 -0
- jac_client/examples/full-stack-with-auth/.babelrc +9 -0
- jac_client/examples/full-stack-with-auth/README.md +16 -0
- jac_client/examples/full-stack-with-auth/app.jac +735 -0
- jac_client/examples/full-stack-with-auth/package.json +28 -0
- jac_client/examples/full-stack-with-auth/vite.config.js +30 -0
- jac_client/examples/little-x/app.jac +615 -0
- jac_client/examples/little-x/package.json +23 -0
- jac_client/examples/little-x/submit-button.jac +8 -0
- jac_client/examples/with-router/.babelrc +9 -0
- jac_client/examples/with-router/README.md +17 -0
- jac_client/examples/with-router/app.jac +323 -0
- jac_client/examples/with-router/package.json +28 -0
- jac_client/examples/with-router/vite.config.js +28 -0
- jac_client/plugin/cli.py +239 -0
- jac_client/plugin/client.py +89 -0
- jac_client/plugin/client_runtime.jac +234 -0
- jac_client/plugin/vite_client_bundle.py +355 -0
- jac_client/tests/__init__.py +2 -0
- jac_client/tests/fixtures/basic-app/app.jac +18 -0
- jac_client/tests/fixtures/client_app_with_antd/app.jac +28 -0
- jac_client/tests/fixtures/js_import/app.jac +30 -0
- jac_client/tests/fixtures/js_import/utils.js +22 -0
- jac_client/tests/fixtures/package-lock.json +329 -0
- jac_client/tests/fixtures/package.json +11 -0
- jac_client/tests/fixtures/relative_import/app.jac +13 -0
- jac_client/tests/fixtures/relative_import/button.jac +6 -0
- jac_client/tests/fixtures/spawn_test/app.jac +133 -0
- jac_client/tests/fixtures/test_fragments_spread/app.jac +53 -0
- jac_client/tests/test_cl.py +476 -0
- jac_client/tests/test_create_jac_app.py +139 -0
- jac_client-0.2.0.dist-info/METADATA +182 -0
- jac_client-0.2.0.dist-info/RECORD +72 -0
- jac_client-0.2.0.dist-info/WHEEL +4 -0
- jac_client-0.2.0.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# React Router HashRouter Example
|
|
2
|
+
cl import from react { useState, useEffect }
|
|
3
|
+
cl import from "@jac-client/utils" {
|
|
4
|
+
Router,
|
|
5
|
+
Routes,
|
|
6
|
+
Route,
|
|
7
|
+
Link,
|
|
8
|
+
useNavigate,
|
|
9
|
+
useLocation,
|
|
10
|
+
useParams
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
cl {
|
|
14
|
+
# Home Page Component
|
|
15
|
+
def Home() -> any {
|
|
16
|
+
location = useLocation();
|
|
17
|
+
navigate = useNavigate();
|
|
18
|
+
|
|
19
|
+
def goToAbout(e: any) -> None {
|
|
20
|
+
navigate("/about");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return <div>
|
|
24
|
+
<h1>
|
|
25
|
+
🏠 Home Page
|
|
26
|
+
</h1>
|
|
27
|
+
<p>
|
|
28
|
+
Welcome to the home page!
|
|
29
|
+
</p>
|
|
30
|
+
<p>
|
|
31
|
+
This example uses React Router's
|
|
32
|
+
<strong>
|
|
33
|
+
HashRouter
|
|
34
|
+
</strong>
|
|
35
|
+
for client-side routing.
|
|
36
|
+
</p>
|
|
37
|
+
<p>
|
|
38
|
+
Current path:
|
|
39
|
+
<code>
|
|
40
|
+
{location.pathname}
|
|
41
|
+
</code>
|
|
42
|
+
</p>
|
|
43
|
+
<button
|
|
44
|
+
onClick={goToAbout}
|
|
45
|
+
>
|
|
46
|
+
Go to About →
|
|
47
|
+
</button>
|
|
48
|
+
</div>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# About Page Component
|
|
52
|
+
def About() -> any {
|
|
53
|
+
location = useLocation();
|
|
54
|
+
|
|
55
|
+
return <div>
|
|
56
|
+
<h1>
|
|
57
|
+
ℹ️ About Page
|
|
58
|
+
</h1>
|
|
59
|
+
<p>
|
|
60
|
+
This is the about page.
|
|
61
|
+
</p>
|
|
62
|
+
<p>
|
|
63
|
+
Learn more about our application here.
|
|
64
|
+
</p>
|
|
65
|
+
<p>
|
|
66
|
+
Current path:
|
|
67
|
+
<code>
|
|
68
|
+
{location.pathname}
|
|
69
|
+
</code>
|
|
70
|
+
</p>
|
|
71
|
+
<div
|
|
72
|
+
style={{"marginTop": "1rem"}}
|
|
73
|
+
>
|
|
74
|
+
<Link to="/about/team">
|
|
75
|
+
View Team →
|
|
76
|
+
</Link>
|
|
77
|
+
</div>
|
|
78
|
+
</div>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Team Page Component (nested route example)
|
|
82
|
+
def Team() -> any {
|
|
83
|
+
return <div>
|
|
84
|
+
<h1>
|
|
85
|
+
👥 Our Team
|
|
86
|
+
</h1>
|
|
87
|
+
<p>
|
|
88
|
+
Meet the amazing team behind this project!
|
|
89
|
+
</p>
|
|
90
|
+
<Link to="/about">
|
|
91
|
+
← Back to About
|
|
92
|
+
</Link>
|
|
93
|
+
</div>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# User Profile Component (with URL parameters)
|
|
97
|
+
def UserProfile() -> any {
|
|
98
|
+
params = useParams();
|
|
99
|
+
userId = params.id if params.id else "Unknown";
|
|
100
|
+
|
|
101
|
+
return <div>
|
|
102
|
+
<h1>
|
|
103
|
+
👤 User Profile
|
|
104
|
+
</h1>
|
|
105
|
+
<p>
|
|
106
|
+
Viewing profile for user:
|
|
107
|
+
<strong>
|
|
108
|
+
{userId}
|
|
109
|
+
</strong>
|
|
110
|
+
</p>
|
|
111
|
+
<p>
|
|
112
|
+
This demonstrates URL parameters using
|
|
113
|
+
<code>
|
|
114
|
+
/user/:id
|
|
115
|
+
</code>
|
|
116
|
+
</p>
|
|
117
|
+
<Link to="/">
|
|
118
|
+
← Back to Home
|
|
119
|
+
</Link>
|
|
120
|
+
</div>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Contact Page Component
|
|
124
|
+
def Contact() -> any {
|
|
125
|
+
[submitted, setSubmitted] = useState(False);
|
|
126
|
+
|
|
127
|
+
def handleSubmit(e: any) -> None {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
setSubmitted(True);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
def resetForm(e: any) -> None {
|
|
133
|
+
setSubmitted(False);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if submitted {
|
|
137
|
+
return <div>
|
|
138
|
+
<h1>
|
|
139
|
+
📧 Contact Page
|
|
140
|
+
</h1>
|
|
141
|
+
<div
|
|
142
|
+
style={{"color": "green"}}
|
|
143
|
+
>
|
|
144
|
+
<p>
|
|
145
|
+
✓ Thank you! Your message has been sent.
|
|
146
|
+
</p>
|
|
147
|
+
<button
|
|
148
|
+
onClick={resetForm}
|
|
149
|
+
>
|
|
150
|
+
Send another
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return <div>
|
|
157
|
+
<h1>
|
|
158
|
+
📧 Contact Page
|
|
159
|
+
</h1>
|
|
160
|
+
<p>
|
|
161
|
+
Get in touch with us!
|
|
162
|
+
</p>
|
|
163
|
+
<form
|
|
164
|
+
onSubmit={handleSubmit}
|
|
165
|
+
>
|
|
166
|
+
<input
|
|
167
|
+
type="text"
|
|
168
|
+
placeholder="Your name"
|
|
169
|
+
style={{"margin": "0.5rem 0", "display": "block"}}
|
|
170
|
+
/>
|
|
171
|
+
<input
|
|
172
|
+
type="email"
|
|
173
|
+
placeholder="Your email"
|
|
174
|
+
style={{"margin": "0.5rem 0", "display": "block"}}
|
|
175
|
+
/>
|
|
176
|
+
<textarea
|
|
177
|
+
placeholder="Your message"
|
|
178
|
+
style={{"margin": "0.5rem 0", "display": "block"}}
|
|
179
|
+
></textarea>
|
|
180
|
+
<button type="submit">
|
|
181
|
+
Send Message
|
|
182
|
+
</button>
|
|
183
|
+
</form>
|
|
184
|
+
</div>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# 404 Not Found Component
|
|
188
|
+
def NotFound() -> any {
|
|
189
|
+
location = useLocation();
|
|
190
|
+
navigate = useNavigate();
|
|
191
|
+
|
|
192
|
+
def goHome(e: any) -> None {
|
|
193
|
+
navigate("/");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return <div
|
|
197
|
+
style={{"textAlign": "center", "padding": "2rem"}}
|
|
198
|
+
>
|
|
199
|
+
<h1>
|
|
200
|
+
🔍 404 - Page Not Found
|
|
201
|
+
</h1>
|
|
202
|
+
<p>
|
|
203
|
+
The page
|
|
204
|
+
<code>
|
|
205
|
+
{location.pathname}
|
|
206
|
+
</code>
|
|
207
|
+
does not exist.
|
|
208
|
+
</p>
|
|
209
|
+
<button
|
|
210
|
+
onClick={goHome}
|
|
211
|
+
>
|
|
212
|
+
Go Home
|
|
213
|
+
</button>
|
|
214
|
+
</div>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# Navigation Component with active link styling
|
|
218
|
+
def Navigation() -> any {
|
|
219
|
+
location = useLocation();
|
|
220
|
+
|
|
221
|
+
def linkStyle(path: str) -> dict {
|
|
222
|
+
isActive = location.pathname == path;
|
|
223
|
+
return {
|
|
224
|
+
"padding": "0.5rem 1rem",
|
|
225
|
+
"textDecoration": "none",
|
|
226
|
+
"color": "#0066cc" if isActive else "#333",
|
|
227
|
+
"fontWeight": "bold" if isActive else "normal",
|
|
228
|
+
"backgroundColor": "#e3f2fd" if isActive else "transparent",
|
|
229
|
+
"borderRadius": "4px",
|
|
230
|
+
"display": "inline-block"
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return <nav
|
|
235
|
+
style={{
|
|
236
|
+
"padding": "1rem",
|
|
237
|
+
"backgroundColor": "#f5f5f5",
|
|
238
|
+
"marginBottom": "2rem",
|
|
239
|
+
"boxShadow": "0 2px 4px rgba(0,0,0,0.1)"
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
<div
|
|
243
|
+
style={{
|
|
244
|
+
"maxWidth": "1200px",
|
|
245
|
+
"margin": "0 auto",
|
|
246
|
+
"display": "flex",
|
|
247
|
+
"gap": "1rem",
|
|
248
|
+
"alignItems": "center"
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
<Link
|
|
252
|
+
to="/"
|
|
253
|
+
style={linkStyle("/")}
|
|
254
|
+
>
|
|
255
|
+
Home
|
|
256
|
+
</Link>
|
|
257
|
+
<Link
|
|
258
|
+
to="/about"
|
|
259
|
+
style={linkStyle("/about")}
|
|
260
|
+
>
|
|
261
|
+
About
|
|
262
|
+
</Link>
|
|
263
|
+
<Link
|
|
264
|
+
to="/contact"
|
|
265
|
+
style={linkStyle("/contact")}
|
|
266
|
+
>
|
|
267
|
+
Contact
|
|
268
|
+
</Link>
|
|
269
|
+
<Link
|
|
270
|
+
to="/user/123"
|
|
271
|
+
style={linkStyle("/user/123")}
|
|
272
|
+
>
|
|
273
|
+
Profile Demo
|
|
274
|
+
</Link>
|
|
275
|
+
</div>
|
|
276
|
+
</nav>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
# Main App Component with React Router HashRouter
|
|
280
|
+
def app() -> any {
|
|
281
|
+
return <Router>
|
|
282
|
+
<div
|
|
283
|
+
style={{"fontFamily": "system-ui, -apple-system, sans-serif"}}
|
|
284
|
+
>
|
|
285
|
+
<Navigation />
|
|
286
|
+
<div
|
|
287
|
+
style={{
|
|
288
|
+
"maxWidth": "1200px",
|
|
289
|
+
"margin": "0 auto",
|
|
290
|
+
"padding": "0 1rem"
|
|
291
|
+
}}
|
|
292
|
+
>
|
|
293
|
+
<Routes>
|
|
294
|
+
<Route
|
|
295
|
+
path="/"
|
|
296
|
+
element={<Home />}
|
|
297
|
+
/>
|
|
298
|
+
<Route
|
|
299
|
+
path="/about"
|
|
300
|
+
element={<About />}
|
|
301
|
+
/>
|
|
302
|
+
<Route
|
|
303
|
+
path="/about/team"
|
|
304
|
+
element={<Team />}
|
|
305
|
+
/>
|
|
306
|
+
<Route
|
|
307
|
+
path="/contact"
|
|
308
|
+
element={<Contact />}
|
|
309
|
+
/>
|
|
310
|
+
<Route
|
|
311
|
+
path="/user/:id"
|
|
312
|
+
element={<UserProfile />}
|
|
313
|
+
/>
|
|
314
|
+
<Route
|
|
315
|
+
path="*"
|
|
316
|
+
element={<NotFound />}
|
|
317
|
+
/>
|
|
318
|
+
</Routes>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
</Router>;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "npm run compile && vite build",
|
|
7
|
+
"dev": "vite dev",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"compile": "babel src --out-dir build --extensions \".jsx,.js\" --out-file-extension .js"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"description": "Jac application: my-app",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"vite": "^6.4.1",
|
|
18
|
+
"@babel/cli": "^7.28.3",
|
|
19
|
+
"@babel/core": "^7.28.5",
|
|
20
|
+
"@babel/preset-env": "^7.28.5",
|
|
21
|
+
"@babel/preset-react": "^7.28.5"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"react": "^19.2.0",
|
|
25
|
+
"react-dom": "^19.2.0",
|
|
26
|
+
"react-router-dom": "^6.30.1"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
root: ".", // base folder
|
|
10
|
+
build: {
|
|
11
|
+
rollupOptions: {
|
|
12
|
+
input: "build/main.js", // your compiled entry file
|
|
13
|
+
output: {
|
|
14
|
+
entryFileNames: "client.[hash].js", // name of the final js file
|
|
15
|
+
assetFileNames: "[name].[ext]",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
outDir: "dist", // final bundled output
|
|
19
|
+
emptyOutDir: true,
|
|
20
|
+
},
|
|
21
|
+
publicDir: false,
|
|
22
|
+
resolve: {
|
|
23
|
+
alias: {
|
|
24
|
+
"@jac-client/utils": path.resolve(__dirname, "src/client_runtime.js"),
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
jac_client/plugin/cli.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""Command line interface tool for the Jac Client."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from jaclang.cli.cmdreg import cmd_registry
|
|
10
|
+
from jaclang.runtimelib.machine import hookimpl
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class JacCmd:
|
|
14
|
+
"""Jac CLI."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
@hookimpl
|
|
18
|
+
def create_cmd() -> None:
|
|
19
|
+
"""Create Jac CLI cmds."""
|
|
20
|
+
|
|
21
|
+
@cmd_registry.register
|
|
22
|
+
def create_jac_app(name: str) -> None:
|
|
23
|
+
"""Create a new Jac application with npm and Vite setup.
|
|
24
|
+
|
|
25
|
+
Bootstraps a new Jac project by creating a temporary directory, initializing
|
|
26
|
+
npm, installing Vite, and setting up the basic project structure.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
name: Name of the project to create
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
jac create_jac_app my-app
|
|
33
|
+
jac create_jac_app my-jac-project
|
|
34
|
+
"""
|
|
35
|
+
if not name:
|
|
36
|
+
print(
|
|
37
|
+
"Error: Project name is required. Use --name=your-project-name",
|
|
38
|
+
file=sys.stderr,
|
|
39
|
+
)
|
|
40
|
+
exit(1)
|
|
41
|
+
|
|
42
|
+
# Validate project name (basic npm package name validation)
|
|
43
|
+
if not re.match(r"^[a-zA-Z0-9_-]+$", name):
|
|
44
|
+
print(
|
|
45
|
+
"Error: Project name must contain only letters, numbers, hyphens, and underscores",
|
|
46
|
+
file=sys.stderr,
|
|
47
|
+
)
|
|
48
|
+
exit(1)
|
|
49
|
+
|
|
50
|
+
print(f"Creating new Jac application: {name}")
|
|
51
|
+
|
|
52
|
+
# Create project directory in current working directory
|
|
53
|
+
project_path = os.path.join(os.getcwd(), name)
|
|
54
|
+
|
|
55
|
+
if os.path.exists(project_path):
|
|
56
|
+
print(
|
|
57
|
+
f"Error: Directory '{name}' already exists in current location",
|
|
58
|
+
file=sys.stderr,
|
|
59
|
+
)
|
|
60
|
+
exit(1)
|
|
61
|
+
|
|
62
|
+
os.makedirs(project_path, exist_ok=True)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
# Change to project directory
|
|
66
|
+
original_cwd = os.getcwd()
|
|
67
|
+
os.chdir(project_path)
|
|
68
|
+
|
|
69
|
+
# Initialize npm package
|
|
70
|
+
print("Initializing npm package...")
|
|
71
|
+
npm_init_cmd = ["npm", "init", "-y"]
|
|
72
|
+
subprocess.run(npm_init_cmd, capture_output=True, text=True, check=True)
|
|
73
|
+
|
|
74
|
+
# Read the generated package.json
|
|
75
|
+
package_json_path = os.path.join(project_path, "package.json")
|
|
76
|
+
with open(package_json_path, "r") as f:
|
|
77
|
+
package_data = json.load(f)
|
|
78
|
+
|
|
79
|
+
# create temp folder
|
|
80
|
+
src_folder = os.path.join(project_path, "src")
|
|
81
|
+
os.makedirs(src_folder, exist_ok=True)
|
|
82
|
+
|
|
83
|
+
# create build folder
|
|
84
|
+
build_folder = os.path.join(project_path, "build")
|
|
85
|
+
os.makedirs(build_folder, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
# Update package.json with Jac-specific configuration
|
|
88
|
+
package_data.update(
|
|
89
|
+
{
|
|
90
|
+
"name": name,
|
|
91
|
+
"description": f"Jac application: {name}",
|
|
92
|
+
"type": "module",
|
|
93
|
+
"scripts": {
|
|
94
|
+
"build": "npm run compile && vite build",
|
|
95
|
+
"dev": "vite dev",
|
|
96
|
+
"preview": "vite preview",
|
|
97
|
+
"compile": 'babel src --out-dir build --extensions ".jsx,.js" --out-file-extension .js',
|
|
98
|
+
},
|
|
99
|
+
"devDependencies": {
|
|
100
|
+
"vite": "^6.4.1",
|
|
101
|
+
"@babel/cli": "^7.28.3",
|
|
102
|
+
"@babel/core": "^7.28.5",
|
|
103
|
+
"@babel/preset-env": "^7.28.5",
|
|
104
|
+
"@babel/preset-react": "^7.28.5",
|
|
105
|
+
},
|
|
106
|
+
"dependencies": {
|
|
107
|
+
"react": "^19.2.0",
|
|
108
|
+
"react-dom": "^19.2.0",
|
|
109
|
+
"react-router-dom": "^6.30.1",
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Write updated package.json
|
|
115
|
+
with open(package_json_path, "w") as f:
|
|
116
|
+
json.dump(package_data, f, indent=2)
|
|
117
|
+
|
|
118
|
+
print("Installing Vite...")
|
|
119
|
+
# Install Vite
|
|
120
|
+
npm_install_cmd = ["npm", "install"]
|
|
121
|
+
subprocess.run(
|
|
122
|
+
npm_install_cmd, capture_output=True, text=True, check=True
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Create basic project structure
|
|
126
|
+
print("Setting up project structure...")
|
|
127
|
+
|
|
128
|
+
# Create a basic Jac file
|
|
129
|
+
main_jac_content = """
|
|
130
|
+
# Pages
|
|
131
|
+
cl import from react {useState, useEffect}
|
|
132
|
+
cl {
|
|
133
|
+
def app() -> any {
|
|
134
|
+
let [count, setCount] = useState(0);
|
|
135
|
+
useEffect(lambda -> None {
|
|
136
|
+
console.log("Count: ", count);
|
|
137
|
+
}, [count]);
|
|
138
|
+
return <div>
|
|
139
|
+
<h1>Hello, World!</h1>
|
|
140
|
+
<p>Count: {count}</p>
|
|
141
|
+
<button onClick={lambda e: any -> None {setCount(count + 1);}}>Increment</button>
|
|
142
|
+
</div>;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
with open(os.path.join(project_path, "app.jac"), "w") as f:
|
|
148
|
+
f.write(main_jac_content)
|
|
149
|
+
|
|
150
|
+
# create .babelrc file
|
|
151
|
+
babel_config_content = """
|
|
152
|
+
{
|
|
153
|
+
"presets": [[
|
|
154
|
+
"@babel/preset-env",
|
|
155
|
+
{
|
|
156
|
+
"modules": false
|
|
157
|
+
}
|
|
158
|
+
], "@babel/preset-react"]
|
|
159
|
+
}
|
|
160
|
+
"""
|
|
161
|
+
with open(os.path.join(project_path, ".babelrc"), "w") as f:
|
|
162
|
+
f.write(babel_config_content)
|
|
163
|
+
|
|
164
|
+
# create vite.config.js file
|
|
165
|
+
vite_config_content = """
|
|
166
|
+
import { defineConfig } from "vite";
|
|
167
|
+
import path from "path";
|
|
168
|
+
import { fileURLToPath } from "url";
|
|
169
|
+
|
|
170
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
171
|
+
|
|
172
|
+
export default defineConfig({
|
|
173
|
+
root: ".", // base folder
|
|
174
|
+
build: {
|
|
175
|
+
rollupOptions: {
|
|
176
|
+
input: "build/main.js", // your compiled entry file
|
|
177
|
+
output: {
|
|
178
|
+
entryFileNames: "client.[hash].js", // name of the final js file
|
|
179
|
+
assetFileNames: "[name].[ext]",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
outDir: "dist", // final bundled output
|
|
183
|
+
emptyOutDir: true,
|
|
184
|
+
},
|
|
185
|
+
publicDir: false,
|
|
186
|
+
resolve: {
|
|
187
|
+
alias: {
|
|
188
|
+
"@jac-client/utils": path.resolve(__dirname, "src/client_runtime.js"),
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
"""
|
|
194
|
+
with open(os.path.join(project_path, "vite.config.js"), "w") as f:
|
|
195
|
+
f.write(vite_config_content)
|
|
196
|
+
|
|
197
|
+
# Create README.md
|
|
198
|
+
readme_content = f"""# {name}
|
|
199
|
+
|
|
200
|
+
## Running Jac Code
|
|
201
|
+
|
|
202
|
+
make sure node modules are installed:
|
|
203
|
+
```bash
|
|
204
|
+
npm install
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
To run your Jac code, use the Jac CLI:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
jac serve app.jac
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Happy coding with Jac!
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
with open(os.path.join(project_path, "README.md"), "w") as f:
|
|
217
|
+
f.write(readme_content)
|
|
218
|
+
|
|
219
|
+
# Return to original directory
|
|
220
|
+
os.chdir(original_cwd)
|
|
221
|
+
|
|
222
|
+
print(f"✅ Successfully created Jac application '{name}'!")
|
|
223
|
+
print(f"📁 Project location: {os.path.abspath(project_path)}")
|
|
224
|
+
print("\nNext steps:")
|
|
225
|
+
print(f" cd {name}")
|
|
226
|
+
print(" jac serve app.jac")
|
|
227
|
+
|
|
228
|
+
except subprocess.CalledProcessError as e:
|
|
229
|
+
# Return to original directory on error
|
|
230
|
+
os.chdir(original_cwd)
|
|
231
|
+
print(f"Error running npm command: {e}", file=sys.stderr)
|
|
232
|
+
print(f"Command output: {e.stdout}", file=sys.stderr)
|
|
233
|
+
print(f"Command error: {e.stderr}", file=sys.stderr)
|
|
234
|
+
exit(1)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
# Return to original directory on error
|
|
237
|
+
os.chdir(original_cwd)
|
|
238
|
+
print(f"Error creating project: {e}", file=sys.stderr)
|
|
239
|
+
exit(1)
|