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.
Files changed (72) hide show
  1. jac_client/docs/README.md +659 -0
  2. jac_client/docs/advanced-state.md +1266 -0
  3. jac_client/docs/assets/pipe_line.png +0 -0
  4. jac_client/docs/guide-example/intro.md +117 -0
  5. jac_client/docs/guide-example/step-01-setup.md +260 -0
  6. jac_client/docs/guide-example/step-02-components.md +416 -0
  7. jac_client/docs/guide-example/step-03-styling.md +478 -0
  8. jac_client/docs/guide-example/step-04-todo-ui.md +477 -0
  9. jac_client/docs/guide-example/step-05-local-state.md +530 -0
  10. jac_client/docs/guide-example/step-06-events.md +750 -0
  11. jac_client/docs/guide-example/step-07-effects.md +469 -0
  12. jac_client/docs/guide-example/step-08-walkers.md +534 -0
  13. jac_client/docs/guide-example/step-09-authentication.md +586 -0
  14. jac_client/docs/guide-example/step-10-routing.md +540 -0
  15. jac_client/docs/guide-example/step-11-final.md +964 -0
  16. jac_client/docs/imports.md +1142 -0
  17. jac_client/docs/lifecycle-hooks.md +774 -0
  18. jac_client/docs/routing.md +660 -0
  19. jac_client/examples/basic/.babelrc +9 -0
  20. jac_client/examples/basic/README.md +16 -0
  21. jac_client/examples/basic/app.jac +16 -0
  22. jac_client/examples/basic/package.json +27 -0
  23. jac_client/examples/basic/vite.config.js +28 -0
  24. jac_client/examples/basic-auth/.babelrc +9 -0
  25. jac_client/examples/basic-auth/README.md +16 -0
  26. jac_client/examples/basic-auth/app.jac +308 -0
  27. jac_client/examples/basic-auth/package.json +27 -0
  28. jac_client/examples/basic-auth/vite.config.js +28 -0
  29. jac_client/examples/basic-auth-with-router/.babelrc +9 -0
  30. jac_client/examples/basic-auth-with-router/README.md +60 -0
  31. jac_client/examples/basic-auth-with-router/app.jac +464 -0
  32. jac_client/examples/basic-auth-with-router/package.json +28 -0
  33. jac_client/examples/basic-auth-with-router/vite.config.js +28 -0
  34. jac_client/examples/basic-full-stack/.babelrc +9 -0
  35. jac_client/examples/basic-full-stack/README.md +18 -0
  36. jac_client/examples/basic-full-stack/app.jac +320 -0
  37. jac_client/examples/basic-full-stack/package.json +28 -0
  38. jac_client/examples/basic-full-stack/vite.config.js +28 -0
  39. jac_client/examples/full-stack-with-auth/.babelrc +9 -0
  40. jac_client/examples/full-stack-with-auth/README.md +16 -0
  41. jac_client/examples/full-stack-with-auth/app.jac +735 -0
  42. jac_client/examples/full-stack-with-auth/package.json +28 -0
  43. jac_client/examples/full-stack-with-auth/vite.config.js +30 -0
  44. jac_client/examples/little-x/app.jac +615 -0
  45. jac_client/examples/little-x/package.json +23 -0
  46. jac_client/examples/little-x/submit-button.jac +8 -0
  47. jac_client/examples/with-router/.babelrc +9 -0
  48. jac_client/examples/with-router/README.md +17 -0
  49. jac_client/examples/with-router/app.jac +323 -0
  50. jac_client/examples/with-router/package.json +28 -0
  51. jac_client/examples/with-router/vite.config.js +28 -0
  52. jac_client/plugin/cli.py +239 -0
  53. jac_client/plugin/client.py +89 -0
  54. jac_client/plugin/client_runtime.jac +234 -0
  55. jac_client/plugin/vite_client_bundle.py +355 -0
  56. jac_client/tests/__init__.py +2 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +18 -0
  58. jac_client/tests/fixtures/client_app_with_antd/app.jac +28 -0
  59. jac_client/tests/fixtures/js_import/app.jac +30 -0
  60. jac_client/tests/fixtures/js_import/utils.js +22 -0
  61. jac_client/tests/fixtures/package-lock.json +329 -0
  62. jac_client/tests/fixtures/package.json +11 -0
  63. jac_client/tests/fixtures/relative_import/app.jac +13 -0
  64. jac_client/tests/fixtures/relative_import/button.jac +6 -0
  65. jac_client/tests/fixtures/spawn_test/app.jac +133 -0
  66. jac_client/tests/fixtures/test_fragments_spread/app.jac +53 -0
  67. jac_client/tests/test_cl.py +476 -0
  68. jac_client/tests/test_create_jac_app.py +139 -0
  69. jac_client-0.2.0.dist-info/METADATA +182 -0
  70. jac_client-0.2.0.dist-info/RECORD +72 -0
  71. jac_client-0.2.0.dist-info/WHEEL +4 -0
  72. jac_client-0.2.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,9 @@
1
+
2
+ {
3
+ "presets": [[
4
+ "@babel/preset-env",
5
+ {
6
+ "modules": false
7
+ }
8
+ ], "@babel/preset-react"]
9
+ }
@@ -0,0 +1,17 @@
1
+ # my-app
2
+
3
+ make sure node modules are installed:
4
+ ```bash
5
+ npm install
6
+ ```
7
+
8
+
9
+ ## Running Jac Code
10
+
11
+ To run your Jac code, use the Jac CLI:
12
+
13
+ ```bash
14
+ jac serve app.jac
15
+ ```
16
+
17
+ Happy coding with Jac!
@@ -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
+
@@ -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)