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