jac-client 0.2.3__py3-none-any.whl → 0.2.5__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 (202) hide show
  1. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  2. jac_client/examples/all-in-one/src/app.jac +841 -0
  3. jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
  4. jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
  5. jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
  6. jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
  7. jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +3 -3
  8. jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
  9. jac_client/examples/basic-auth/src/app.jac +377 -0
  10. jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
  11. jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
  12. jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
  13. jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
  14. jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
  15. jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
  16. jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
  17. jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
  18. jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
  19. jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
  20. jac_client/examples/little-x/src/submit-button.jac +16 -0
  21. jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
  22. jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
  23. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
  24. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
  25. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
  26. jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
  27. jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
  28. jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
  29. jac_client/examples/ts-support/src/app.jac +35 -0
  30. jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
  31. jac_client/plugin/cli.jac +547 -0
  32. jac_client/plugin/client.jac +52 -0
  33. jac_client/plugin/client_runtime.cl.jac +38 -0
  34. jac_client/plugin/impl/client.impl.jac +134 -0
  35. jac_client/plugin/impl/client_runtime.impl.jac +177 -0
  36. jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
  37. jac_client/plugin/plugin_config.jac +195 -0
  38. jac_client/plugin/src/__init__.jac +20 -0
  39. jac_client/plugin/src/asset_processor.jac +33 -0
  40. jac_client/plugin/src/babel_processor.jac +18 -0
  41. jac_client/plugin/src/compiler.jac +66 -0
  42. jac_client/plugin/src/config_loader.jac +32 -0
  43. jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
  44. jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
  45. jac_client/plugin/src/impl/compiler.impl.jac +251 -0
  46. jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
  47. jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
  48. jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
  49. jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
  50. jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
  51. jac_client/plugin/src/import_processor.jac +19 -0
  52. jac_client/plugin/src/jac_to_js.jac +35 -0
  53. jac_client/plugin/src/package_installer.jac +26 -0
  54. jac_client/plugin/src/vite_bundler.jac +36 -0
  55. jac_client/plugin/vite_client_bundle.jac +31 -0
  56. jac_client/tests/conftest.py +281 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +2 -2
  58. jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
  59. jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
  60. jac_client/tests/fixtures/js_import/app.jac +5 -5
  61. jac_client/tests/fixtures/spawn_test/app.jac +7 -7
  62. jac_client/tests/fixtures/with-ts/app.jac +35 -0
  63. jac_client/tests/test_cli.py +755 -0
  64. jac_client/tests/test_it.py +347 -67
  65. {jac_client-0.2.3.dist-info → jac_client-0.2.5.dist-info}/METADATA +28 -30
  66. jac_client-0.2.5.dist-info/RECORD +74 -0
  67. {jac_client-0.2.3.dist-info → jac_client-0.2.5.dist-info}/WHEEL +2 -1
  68. jac_client-0.2.5.dist-info/entry_points.txt +4 -0
  69. jac_client-0.2.5.dist-info/top_level.txt +1 -0
  70. jac_client/docs/README.md +0 -689
  71. jac_client/docs/advanced-state.md +0 -1265
  72. jac_client/docs/asset-serving/intro.md +0 -209
  73. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  74. jac_client/docs/assets/pipe_line.png +0 -0
  75. jac_client/docs/file-system/app.jac.md +0 -121
  76. jac_client/docs/file-system/backend-frontend.md +0 -217
  77. jac_client/docs/file-system/intro.md +0 -72
  78. jac_client/docs/file-system/nested-imports.md +0 -348
  79. jac_client/docs/guide-example/intro.md +0 -115
  80. jac_client/docs/guide-example/step-01-setup.md +0 -270
  81. jac_client/docs/guide-example/step-02-components.md +0 -416
  82. jac_client/docs/guide-example/step-03-styling.md +0 -478
  83. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  84. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  85. jac_client/docs/guide-example/step-06-events.md +0 -749
  86. jac_client/docs/guide-example/step-07-effects.md +0 -468
  87. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  88. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  89. jac_client/docs/guide-example/step-10-routing.md +0 -539
  90. jac_client/docs/guide-example/step-11-final.md +0 -963
  91. jac_client/docs/imports.md +0 -1141
  92. jac_client/docs/lifecycle-hooks.md +0 -773
  93. jac_client/docs/routing.md +0 -659
  94. jac_client/docs/styling/intro.md +0 -249
  95. jac_client/docs/styling/js-styling.md +0 -367
  96. jac_client/docs/styling/material-ui.md +0 -341
  97. jac_client/docs/styling/pure-css.md +0 -299
  98. jac_client/docs/styling/sass.md +0 -403
  99. jac_client/docs/styling/styled-components.md +0 -395
  100. jac_client/docs/styling/tailwind.md +0 -298
  101. jac_client/examples/all-in-one/.babelrc +0 -9
  102. jac_client/examples/all-in-one/README.md +0 -16
  103. jac_client/examples/all-in-one/app.jac +0 -426
  104. jac_client/examples/all-in-one/assets/burger.png +0 -0
  105. jac_client/examples/all-in-one/package.json +0 -29
  106. jac_client/examples/all-in-one/styles.css +0 -26
  107. jac_client/examples/all-in-one/vite.config.js +0 -28
  108. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  109. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  110. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  111. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  112. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  113. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  114. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  115. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  116. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  117. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  118. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  119. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  120. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  121. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  122. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  123. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  124. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  125. jac_client/examples/basic/.babelrc +0 -9
  126. jac_client/examples/basic/README.md +0 -16
  127. jac_client/examples/basic/package.json +0 -27
  128. jac_client/examples/basic/vite.config.js +0 -27
  129. jac_client/examples/basic-auth/.babelrc +0 -9
  130. jac_client/examples/basic-auth/README.md +0 -16
  131. jac_client/examples/basic-auth/app.jac +0 -308
  132. jac_client/examples/basic-auth/package.json +0 -27
  133. jac_client/examples/basic-auth/vite.config.js +0 -27
  134. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  135. jac_client/examples/basic-auth-with-router/README.md +0 -60
  136. jac_client/examples/basic-auth-with-router/package.json +0 -28
  137. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  138. jac_client/examples/basic-full-stack/.babelrc +0 -9
  139. jac_client/examples/basic-full-stack/README.md +0 -18
  140. jac_client/examples/basic-full-stack/package.json +0 -28
  141. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  142. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  143. jac_client/examples/css-styling/js-styling/README.md +0 -183
  144. jac_client/examples/css-styling/js-styling/package.json +0 -28
  145. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  146. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  147. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  148. jac_client/examples/css-styling/material-ui/README.md +0 -16
  149. jac_client/examples/css-styling/material-ui/package.json +0 -32
  150. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  151. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  152. jac_client/examples/css-styling/pure-css/README.md +0 -16
  153. jac_client/examples/css-styling/pure-css/package.json +0 -28
  154. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  155. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  156. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  157. jac_client/examples/css-styling/sass-example/README.md +0 -16
  158. jac_client/examples/css-styling/sass-example/package.json +0 -29
  159. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  160. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  161. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  162. jac_client/examples/css-styling/styled-components/README.md +0 -16
  163. jac_client/examples/css-styling/styled-components/package.json +0 -29
  164. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  165. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  166. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  167. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  168. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  169. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  170. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  171. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  172. jac_client/examples/full-stack-with-auth/README.md +0 -16
  173. jac_client/examples/full-stack-with-auth/package.json +0 -28
  174. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  175. jac_client/examples/little-x/package.json +0 -23
  176. jac_client/examples/little-x/submit-button.jac +0 -8
  177. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  178. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  179. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  180. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  181. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  182. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  183. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  184. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  185. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  186. jac_client/examples/with-router/.babelrc +0 -9
  187. jac_client/examples/with-router/README.md +0 -17
  188. jac_client/examples/with-router/package.json +0 -28
  189. jac_client/examples/with-router/vite.config.js +0 -27
  190. jac_client/plugin/cli.py +0 -244
  191. jac_client/plugin/client.py +0 -152
  192. jac_client/plugin/client_runtime.jac +0 -234
  193. jac_client/plugin/vite_client_bundle.py +0 -503
  194. jac_client/tests/fixtures/js_import/utils.js +0 -21
  195. jac_client/tests/fixtures/package-lock.json +0 -329
  196. jac_client/tests/fixtures/package.json +0 -11
  197. jac_client/tests/test_asset_examples.py +0 -322
  198. jac_client/tests/test_cl.py +0 -530
  199. jac_client/tests/test_create_jac_app.py +0 -131
  200. jac_client/tests/test_nested_file.py +0 -374
  201. jac_client-0.2.3.dist-info/RECORD +0 -171
  202. jac_client-0.2.3.dist-info/entry_points.txt +0 -4
@@ -0,0 +1,547 @@
1
+ """Command line interface tool for the Jac Client.
2
+
3
+ This module extends the core `create` command to add client-side (frontend)
4
+ project setup via the --cl flag.
5
+ """
6
+
7
+ import os;
8
+ import re;
9
+ import sys;
10
+ import pathlib;
11
+ import subprocess;
12
+ import shutil;
13
+ import from jaclang.cli.cmdreg { cmd_registry, CommandPriority }
14
+ import from jaclang.pycore.runtime { hookimpl }
15
+ import from jaclang.project.config { JacConfig, find_project_root }
16
+
17
+ """Jac CLI extensions for client-side development."""
18
+ class JacCmd {
19
+ """Create Jac CLI cmds."""
20
+ @hookimpl
21
+ static def create_cmd -> None {
22
+ """Override core create command to add --cl flag for client-side setup.""";
23
+
24
+ def create(
25
+ name: str = 'main',
26
+ force: bool = False,
27
+ cl: bool = False,
28
+ skip: bool = False,
29
+ verbose: bool = False
30
+ ) -> None {
31
+ """Initialize a new Jac project.
32
+
33
+ Creates a jac.toml configuration file and basic project structure.
34
+ Use --cl to include client-side (frontend) setup with Vite bundling.
35
+
36
+ Args:
37
+ name: Project name (default: 'main')
38
+ force: Overwrite existing jac.toml if present
39
+ cl: Include client-side (frontend) setup
40
+ skip: Skip installing default packages (only for --cl projects)
41
+ verbose: Show detailed output during package installation
42
+
43
+ Examples:
44
+ jac create
45
+ jac create myapp
46
+ jac create --cl myapp
47
+ jac create --cl --skip myapp
48
+ jac create --cl --verbose myapp
49
+ jac create --force
50
+ """;
51
+ cwd = pathlib.Path(os.getcwd());
52
+
53
+ if cl {
54
+ _create_client_project(cwd, name, force, skip, verbose);
55
+ } else {
56
+ _create_core_project(cwd, name, force);
57
+ }
58
+ }
59
+
60
+ # Register with PLUGIN priority to override core's create command
61
+ cmd_registry.register(
62
+ create, priority=CommandPriority.PLUGIN, source="jac-client"
63
+ );
64
+ }
65
+ }
66
+
67
+ """Create a standard Jac project (core behavior)."""
68
+ def _create_core_project(cwd: pathlib.Path, name: str, force: bool) -> None {
69
+ existing = find_project_root(cwd);
70
+ if existing and not force {
71
+ (project_root, toml_path) = existing;
72
+ print(f"Already in a Jac project: {toml_path}", file=sys.stderr);
73
+ print("Use --force to reinitialize.", file=sys.stderr);
74
+ <>exit(1);
75
+ }
76
+
77
+ project_name = name or cwd.name;
78
+
79
+ toml_path = cwd / "jac.toml";
80
+ if toml_path.exists() and not force {
81
+ print("jac.toml already exists. Use --force to overwrite.", file=sys.stderr);
82
+ <>exit(1);
83
+ }
84
+
85
+ toml_content = JacConfig.create_default_toml(project_name);
86
+ with open(toml_path, "w") as f {
87
+ f.write(toml_content);
88
+ }
89
+ print(f"Created {toml_path}");
90
+
91
+ main_jac = cwd / f"{name}.jac";
92
+ if not main_jac.exists() {
93
+ with open(main_jac, "w") as f {
94
+ f.write(
95
+ f'''"""Main entry point for {project_name}."""
96
+
97
+ with entry {{
98
+ print("Hello from {project_name}!");
99
+ }}
100
+ '''
101
+ );
102
+ }
103
+ print(f"Created {main_jac}");
104
+ }
105
+
106
+ packages_dir = cwd / "packages";
107
+ if not packages_dir.exists() {
108
+ packages_dir.mkdir(parents=True, exist_ok=True);
109
+ print(f"Created {packages_dir}/");
110
+ }
111
+
112
+ _create_gitignore(cwd, client=False);
113
+
114
+ print(f"\nProject '{project_name}' initialized successfully!");
115
+ print("\nNext steps:");
116
+ print(" jac run main.jac # Run the main entry point");
117
+ print(" jac add <package> # Add dependencies");
118
+ print(" jac install # Install all dependencies");
119
+ }
120
+
121
+ """Create a client-side Jac project with organized folder structure."""
122
+ def _create_client_project(
123
+ cwd: pathlib.Path,
124
+ name: str,
125
+ force: bool,
126
+ skip: bool = False,
127
+ verbose: bool = False
128
+ ) -> None {
129
+ project_name = name or cwd.name;
130
+
131
+ if not project_name or project_name == 'main' {
132
+ print(
133
+ "Error: Project name is required for client projects. Use: jac create --cl <name>",
134
+ file=sys.stderr
135
+ );
136
+ <>exit(1);
137
+ }
138
+
139
+ if not re.match('^[a-zA-Z0-9_-]+$', project_name) {
140
+ print(
141
+ "Error: Project name must contain only letters, numbers, hyphens, and underscores",
142
+ file=sys.stderr
143
+ );
144
+ <>exit(1);
145
+ }
146
+
147
+ existing = find_project_root(cwd);
148
+ if existing and not force {
149
+ (project_root, toml_path) = existing;
150
+ print(f"Already in a Jac project: {toml_path}", file=sys.stderr);
151
+ print("Use --force to reinitialize.", file=sys.stderr);
152
+ <>exit(1);
153
+ }
154
+
155
+ project_path: pathlib.Path;
156
+ if name and name != cwd.name {
157
+ project_path = cwd / name;
158
+ if project_path.exists() and not force {
159
+ print(f"Error: Directory '{name}' already exists", file=sys.stderr);
160
+ <>exit(1);
161
+ }
162
+ project_path.mkdir(parents=True, exist_ok=True);
163
+ } else {
164
+ project_path = cwd;
165
+ }
166
+
167
+ print(f"Creating Jac client application: {project_name}");
168
+
169
+ src_dir = project_path / "src";
170
+ src_dir.mkdir(parents=True, exist_ok=True);
171
+
172
+ components_dir = src_dir / "components";
173
+ components_dir.mkdir(parents=True, exist_ok=True);
174
+
175
+ assets_dir = project_path / "assets";
176
+ assets_dir.mkdir(parents=True, exist_ok=True);
177
+
178
+ (project_path / ".client-build").mkdir(parents=True, exist_ok=True);
179
+
180
+ toml_path = project_path / "jac.toml";
181
+ toml_content = f'''[project]
182
+ name = "{project_name}"
183
+ version = "1.0.0"
184
+ description = "Jac client application: {project_name}"
185
+ entry-point = "src/app.jac"
186
+
187
+ [plugins.client]
188
+ # Vite bundler configuration (optional overrides)
189
+ # vite.plugins = []
190
+ # vite.build = {{}}
191
+ ''';
192
+ with open(toml_path, 'w') as f {
193
+ f.write(toml_content);
194
+ }
195
+ print(f"Created {toml_path}");
196
+
197
+ app_jac_content = '''"""Main entry point for the Jac client application."""
198
+
199
+ # Client-side imports
200
+ cl import from react { useState, useEffect }
201
+ cl import from ".components/Button.tsx" { Button }
202
+
203
+ # Client-side component
204
+ cl {
205
+ def:pub app() -> any {
206
+ [count, setCount] = useState(0);
207
+
208
+ useEffect(lambda -> None {
209
+ console.log("Count updated:", count);
210
+ }, [count]);
211
+
212
+ return <div style={{padding: "2rem", fontFamily: "Arial, sans-serif"}}>
213
+ <h1>Hello, World!</h1>
214
+ <p>Count: {count}</p>
215
+ <div style={{display: "flex", gap: "1rem", marginTop: "1rem"}}>
216
+ <Button
217
+ label="Increment"
218
+ onClick={lambda -> None { setCount(count + 1); }}
219
+ variant="primary"
220
+ />
221
+ <Button
222
+ label="Reset"
223
+ onClick={lambda -> None { setCount(0); }}
224
+ variant="secondary"
225
+ />
226
+ </div>
227
+ </div>;
228
+ }
229
+ }
230
+ ''';
231
+ with open(src_dir / "app.jac", 'w') as f {
232
+ f.write(app_jac_content);
233
+ }
234
+ print("Created src/app.jac");
235
+
236
+ button_tsx_content = '''import React from 'react';
237
+
238
+ interface ButtonProps {
239
+ label: string;
240
+ onClick?: () => void;
241
+ variant?: 'primary' | 'secondary';
242
+ disabled?: boolean;
243
+ }
244
+
245
+ export const Button: React.FC<ButtonProps> = ({
246
+ label,
247
+ onClick,
248
+ variant = 'primary',
249
+ disabled = false
250
+ }) => {
251
+ const baseStyles: React.CSSProperties = {
252
+ padding: '0.75rem 1.5rem',
253
+ fontSize: '1rem',
254
+ fontWeight: '600',
255
+ borderRadius: '0.5rem',
256
+ border: 'none',
257
+ cursor: disabled ? 'not-allowed' : 'pointer',
258
+ transition: 'all 0.2s ease',
259
+ };
260
+
261
+ const variantStyles: Record<string, React.CSSProperties> = {
262
+ primary: {
263
+ backgroundColor: disabled ? '#9ca3af' : '#3b82f6',
264
+ color: '#ffffff',
265
+ },
266
+ secondary: {
267
+ backgroundColor: disabled ? '#e5e7eb' : '#6b7280',
268
+ color: '#ffffff',
269
+ },
270
+ };
271
+
272
+ return (
273
+ <button
274
+ style={{ ...baseStyles, ...variantStyles[variant] }}
275
+ onClick={onClick}
276
+ disabled={disabled}
277
+ >
278
+ {label}
279
+ </button>
280
+ );
281
+ };
282
+
283
+ export default Button;
284
+ ''';
285
+ with open(components_dir / "Button.tsx", 'w') as f {
286
+ f.write(button_tsx_content);
287
+ }
288
+ print("Created src/components/Button.tsx");
289
+
290
+ readme_content = f''' # {project_name}
291
+
292
+
293
+ A Jac client-side application with React and TypeScript support.
294
+
295
+ ## Project Structure
296
+
297
+ ```
298
+ {project_name}/
299
+ ├── jac.toml # Project configuration
300
+ ├── src/ # Source files
301
+ │ ├── app.jac # Main application entry
302
+ │ └── components/ # Reusable components
303
+ │ └── Button.tsx # Example TypeScript component
304
+ ├── assets/ # Static assets (images, fonts, etc.)
305
+ └── build/ # Build output (generated)
306
+ ```
307
+
308
+ ## Getting Started
309
+
310
+ Start the development server:
311
+
312
+ ```bash
313
+ jac serve src/app.jac
314
+ ```
315
+
316
+ ## TypeScript Support
317
+
318
+ Create TypeScript components in `src/components/` and import them in your Jac files:
319
+
320
+ ```jac
321
+ cl import from "./components/Button.tsx" {{ Button }}
322
+ ```
323
+
324
+ ## Adding Dependencies
325
+
326
+ Add npm packages with the --cl flag:
327
+
328
+ ```bash
329
+ jac add --cl react-router-dom
330
+ ```
331
+ ''';
332
+ with open(project_path / "README.md", 'w') as f {
333
+ f.write(readme_content);
334
+ }
335
+ print("Created README.md");
336
+
337
+ _create_gitignore(project_path, client=True);
338
+
339
+ # Install default packages unless --skip is specified
340
+ if not skip {
341
+ print("\nInstalling default packages...");
342
+ _install_default_packages(project_path, verbose);
343
+ }
344
+
345
+ print(f"\nProject '{project_name}' created successfully!");
346
+ if name and name != cwd.name {
347
+ print("\nNext steps:");
348
+ print(f" cd {name}");
349
+ print(" jac serve src/app.jac");
350
+ } else {
351
+ print("\nNext steps:");
352
+ print(" jac serve src/app.jac");
353
+ }
354
+ }
355
+
356
+ """Create .gitignore file with appropriate entries."""
357
+ def _create_gitignore(project_path: pathlib.Path, client: bool = False) -> None {
358
+ gitignore_entries = [
359
+ "# Jac project",
360
+ "packages/",
361
+ ".jac_cache/",
362
+ "*.jbc",
363
+ "*.jir",
364
+ "__jaccache__/",
365
+ "",
366
+ "# Python",
367
+ "__pycache__/",
368
+ "*.py[cod]",
369
+ ".venv/",
370
+ "venv/",
371
+ "",
372
+ "# IDE",
373
+ ".idea/",
374
+ ".vscode/",
375
+ "*.swp"
376
+ ];
377
+
378
+ if client {
379
+ gitignore_entries.extend(
380
+ [
381
+ "",
382
+ "# Node.js",
383
+ "node_modules/",
384
+ "",
385
+ "# Jac Client",
386
+ ".client-build/",
387
+ "*.session",
388
+ "*.session.*"
389
+ ]
390
+ );
391
+ }
392
+
393
+ gitignore_path = project_path / ".gitignore";
394
+ if gitignore_path.exists() {
395
+ with open(gitignore_path, "r") as f {
396
+ existing_content = f.read();
397
+ }
398
+ new_entries: list = [];
399
+ for entry in gitignore_entries {
400
+ if entry and entry not in existing_content {
401
+ new_entries.append(entry);
402
+ }
403
+ }
404
+ if new_entries {
405
+ with open(gitignore_path, "a") as f {
406
+ f.write("\n" + "\n".join(new_entries));
407
+ }
408
+ print("Updated .gitignore");
409
+ }
410
+ } else {
411
+ with open(gitignore_path, "w") as f {
412
+ f.write("\n".join(gitignore_entries) + "\n");
413
+ }
414
+ print("Created .gitignore");
415
+ }
416
+ }
417
+
418
+ """Install default npm packages in .client-build directory."""
419
+ def _install_default_packages(
420
+ project_path: pathlib.Path, verbose: bool = False
421
+ ) -> None {
422
+ import from jac_client.plugin.src.vite_bundler { ViteBundler }
423
+
424
+ try {
425
+ # Verify jac.toml exists
426
+ toml_path = project_path / "jac.toml";
427
+ if not toml_path.exists() {
428
+ print(
429
+ "Warning: jac.toml not found, skipping package installation",
430
+ file=sys.stderr
431
+ );
432
+ return;
433
+ }
434
+
435
+ # Get project name from jac.toml or use directory name
436
+ project_name = project_path.name;
437
+
438
+ # Create ViteBundler instance (it will load config internally)
439
+ bundler = ViteBundler(project_path);
440
+
441
+ # Generate package.json with default packages (defaults are added automatically)
442
+ bundler.create_package_json(project_name=project_name);
443
+
444
+ # Ensure .client-build directory exists
445
+ build_dir = project_path / '.client-build';
446
+ build_dir.mkdir(parents=True, exist_ok=True);
447
+
448
+ # Copy package.json to .client-build/ for npm install
449
+ configs_package_json = build_dir / '.jac-client.configs' / 'package.json';
450
+ build_package_json = build_dir / 'package.json';
451
+
452
+ if not configs_package_json.exists() {
453
+ print(
454
+ "Warning: package.json was not generated, skipping package installation",
455
+ file=sys.stderr
456
+ );
457
+ return;
458
+ }
459
+
460
+ # Always copy the generated package.json to .client-build/ for npm install
461
+ shutil.copy2(configs_package_json, build_package_json);
462
+
463
+ # Read package data for verbose output
464
+ import json;
465
+ with open(configs_package_json, 'r') as f {
466
+ pkg_data = json.load(f);
467
+ }
468
+ deps = pkg_data.get('dependencies', {});
469
+ dev_deps = pkg_data.get('devDependencies', {});
470
+
471
+ if verbose {
472
+ # Verbose mode: show detailed package list and stream npm output
473
+ if deps {
474
+ print(" Dependencies:");
475
+ for (name, version) in deps.items() {
476
+ print(f" - {name}@{version}");
477
+ }
478
+ }
479
+ if dev_deps {
480
+ print(" Dev dependencies:");
481
+ for (name, version) in dev_deps.items() {
482
+ print(f" - {name}@{version}");
483
+ }
484
+ }
485
+ print("\nRunning npm install...");
486
+ }
487
+
488
+ # Run npm install in .client-build/ directory
489
+ try {
490
+ if verbose {
491
+ # Stream output for visibility in verbose mode
492
+ subprocess.run(
493
+ ['npm', 'install', '--progress'], cwd=build_dir, check=True
494
+ );
495
+ } else {
496
+ # Quiet mode: capture output
497
+ subprocess.run(
498
+ ['npm', 'install'],
499
+ cwd=build_dir,
500
+ check=True,
501
+ capture_output=True,
502
+ text=True
503
+ );
504
+ }
505
+
506
+ # Move package-lock.json to .jac-client.configs/ if it was created
507
+ build_package_lock = build_dir / 'package-lock.json';
508
+ configs_dir = build_dir / '.jac-client.configs';
509
+ configs_package_lock = configs_dir / 'package-lock.json';
510
+ if build_package_lock.exists() {
511
+ configs_dir.mkdir(parents=True, exist_ok=True);
512
+ if configs_package_lock.exists() {
513
+ configs_package_lock.unlink();
514
+ }
515
+ shutil.move(str(build_package_lock), str(configs_package_lock));
516
+ }
517
+
518
+ print("Default packages installed successfully");
519
+ } except subprocess.CalledProcessError as e {
520
+ if verbose {
521
+ print(
522
+ f"Warning: Failed to install packages (exit code {e.returncode})",
523
+ file=sys.stderr
524
+ );
525
+ } else {
526
+ print(
527
+ f"Warning: Failed to install packages: {e.stderr}", file=sys.stderr
528
+ );
529
+ }
530
+ print("You can install packages later with: jac add --cl", file=sys.stderr);
531
+ } except FileNotFoundError {
532
+ print(
533
+ "Warning: npm command not found. Install Node.js and npm to install packages.",
534
+ file=sys.stderr
535
+ );
536
+ print("You can install packages later with: jac add --cl", file=sys.stderr);
537
+ } finally {
538
+ # Clean up temporary package.json in .client-build/
539
+ if build_package_json.exists() {
540
+ build_package_json.unlink();
541
+ }
542
+ }
543
+ } except Exception as e {
544
+ print(f"Warning: Could not install default packages: {e}", file=sys.stderr);
545
+ print("You can install packages later with: jac add --cl", file=sys.stderr);
546
+ }
547
+ }
@@ -0,0 +1,52 @@
1
+ """Jac Client plugin for client-side rendering."""
2
+ import hashlib;
3
+ import html;
4
+ import mimetypes;
5
+ import types;
6
+ import from http.server { BaseHTTPRequestHandler }
7
+ import from pathlib { Path }
8
+ import from typing { Any, Literal, TypeAlias }
9
+ import from jaclang.runtimelib.client_bundle { ClientBundle, ClientBundleError }
10
+ import from jaclang.runtimelib.runtime { JacRuntime as Jac }
11
+ import from jaclang.runtimelib.runtime { hookimpl }
12
+ import from jaclang.runtimelib.server { ModuleIntrospector }
13
+ import from .vite_client_bundle { ViteClientBundleBuilder }
14
+ import from .src.vite_bundler { ViteBundler }
15
+
16
+ glob JsonValue: TypeAlias = None | str | int | float | bool | list['JsonValue'] | dict[
17
+ (str, 'JsonValue')
18
+ ],
19
+ StatusCode: TypeAlias = Literal[(200, 201, 400, 401, 404, 503)];
20
+
21
+ """Jac Client Module Introspector."""
22
+ class JacClientModuleIntrospector(ModuleIntrospector) {
23
+ def render_page(
24
+ self: JacClientModuleIntrospector,
25
+ function_name: str,
26
+ args: dict[(str, Any)],
27
+ username: str
28
+ ) -> dict[str, Any];
29
+ }
30
+
31
+ """Jac Client."""
32
+ class JacClient {
33
+ @hookimpl
34
+ static def get_client_bundle_builder -> ViteClientBundleBuilder;
35
+
36
+ @hookimpl
37
+ static def build_client_bundle(
38
+ module: types.ModuleType, force: bool = False
39
+ ) -> ClientBundle;
40
+
41
+ @hookimpl
42
+ static def get_module_introspector(
43
+ module_name: str, base_path: (str | None)
44
+ ) -> ModuleIntrospector;
45
+
46
+ @hookimpl
47
+ static def send_static_file(
48
+ handler: BaseHTTPRequestHandler,
49
+ file_path: Path,
50
+ content_type: (str | None) = None
51
+ ) -> None;
52
+ }
@@ -0,0 +1,38 @@
1
+ """Client-side runtime for Jac JSX and walker interactions."""
2
+
3
+ import from 'react' { * as React }
4
+ import from 'react-dom/client' { * as ReactDOM }
5
+ import from 'react-router-dom' {
6
+ HashRouter as ReactRouterHashRouter,
7
+ Routes as ReactRouterRoutes,
8
+ Route as ReactRouterRoute,
9
+ Link as ReactRouterLink,
10
+ Navigate as ReactRouterNavigate,
11
+ useNavigate as reactRouterUseNavigate,
12
+ useLocation as reactRouterUseLocation,
13
+ useParams as reactRouterUseParams
14
+ }
15
+
16
+ def : pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
17
+
18
+ glob : pub Router = ReactRouterHashRouter,
19
+ Routes = ReactRouterRoutes,
20
+ Route = ReactRouterRoute,
21
+ Link = ReactRouterLink,
22
+ Navigate = ReactRouterNavigate,
23
+ useNavigate = reactRouterUseNavigate,
24
+ useLocation = reactRouterUseLocation,
25
+ useParams = reactRouterUseParams;
26
+
27
+ def : pub useRouter -> dict;
28
+ def : pub navigate(path: str) -> None;
29
+ async def : pub __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
30
+ def : pub jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
31
+ async def : pub __jacCallFunction(function_name: str, args: dict = {}) -> any;
32
+ async def : pub jacSignup(username: str, password: str) -> dict;
33
+ async def : pub jacLogin(username: str, password: str) -> bool;
34
+ def : pub jacLogout -> None;
35
+ def : pub jacIsLoggedIn -> bool;
36
+ def : pub __getLocalStorage(key: str) -> str;
37
+ def : pub __setLocalStorage(key: str, value: str) -> None;
38
+ def : pub __removeLocalStorage(key: str) -> None;