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,193 @@
1
+ import from jaclang.project.config { get_config }
2
+
3
+ """Send static file response (images, fonts, etc.)."""
4
+ impl JacClient.send_static_file(
5
+ handler: BaseHTTPRequestHandler, file_path: Path, content_type: (str | None) = None
6
+ ) -> None {
7
+ import from jaclang.runtimelib.server { ResponseBuilder }
8
+ if (not file_path.exists() or not file_path.is_file()) {
9
+ ResponseBuilder.send_json(handler, 404, {'error': 'File not found'});
10
+ return;
11
+ }
12
+ try {
13
+ file_content = file_path.read_bytes();
14
+ if (content_type is None) {
15
+ (content_type, _) = mimetypes.guess_type(str(file_path));
16
+ if (content_type is None) {
17
+ content_type = 'application/octet-stream';
18
+ }
19
+ }
20
+ handler.send_response(200);
21
+ handler.send_header('Content-Type', content_type);
22
+ handler.send_header('Content-Length', str(len(file_content)));
23
+ handler.send_header('Cache-Control', 'public, max-age=3600');
24
+ ResponseBuilder._add_cors_headers(handler);
25
+ ResponseBuilder._add_custom_headers(handler);
26
+ handler.end_headers();
27
+ handler.wfile.write(file_content);
28
+ } except Exception as exc {
29
+ ResponseBuilder.send_json(handler, 500, {'error': str(exc)});
30
+ }
31
+ }
32
+
33
+ """Build a client bundle for the supplied module."""
34
+ impl JacClient.build_client_bundle(
35
+ module: types.ModuleType, force: bool = False
36
+ ) -> ClientBundle {
37
+ builder = JacClient.get_client_bundle_builder();
38
+ return builder.build(module, force=force);
39
+ }
40
+
41
+ """Get the client bundle builder instance."""
42
+ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
43
+ import from jaclang.project.config { find_project_root }
44
+ # Find project root by looking for jac.toml (base_path_dir might be src/ for entry files)
45
+ base_path_dir = Path(Jac.base_path_dir);
46
+ project_root_result = find_project_root(base_path_dir);
47
+ if project_root_result {
48
+ (base_path, _) = project_root_result;
49
+ } else {
50
+ # Fallback to base_path_dir if no project root found
51
+ base_path = base_path_dir;
52
+ }
53
+ # Use ViteBundler to get the client directory
54
+ bundler = ViteBundler(base_path);
55
+ client_dir = bundler._get_client_dir();
56
+ # package.json should only exist in .jac/client/configs/
57
+ generated_package_json = client_dir / 'configs' / 'package.json';
58
+ # Generate package.json if it doesn't exist
59
+ if not generated_package_json.exists() {
60
+ generated_path = bundler.create_package_json();
61
+ # Verify the file was created and resolve to absolute path
62
+ if not generated_path.exists() {
63
+ raise ClientBundleError(
64
+ f'Failed to generate package.json at {generated_path}'
65
+ ) ;
66
+ }
67
+ package_json_path = generated_path.resolve();
68
+ } else {
69
+ package_json_path = generated_package_json.resolve();
70
+ }
71
+ # Final verification that package.json exists before creating builder
72
+ if not package_json_path.exists() {
73
+ raise ClientBundleError(
74
+ f'package.json not found at {package_json_path}. Expected at {generated_package_json}'
75
+ ) ;
76
+ }
77
+ output_dir = client_dir / 'dist';
78
+ runtime_path = Path(__file__).with_name('client_runtime.cl.jac');
79
+ return ViteClientBundleBuilder(
80
+ runtime_path=runtime_path,
81
+ vite_package_json=package_json_path,
82
+ vite_output_dir=output_dir,
83
+ vite_minify=False
84
+ );
85
+ }
86
+
87
+ """Render HTML page for client function using the Vite bundle."""
88
+ impl JacClient.render_page(
89
+ introspector: ModuleIntrospector,
90
+ function_name: str,
91
+ args: dict[(str, Any)],
92
+ username: str
93
+ ) -> dict[str, Any] {
94
+ introspector.load();
95
+ available_exports = (
96
+ set(introspector._client_manifest.get('exports', []))
97
+ or set(introspector.get_client_functions().keys())
98
+ );
99
+ if (function_name not in available_exports) {
100
+ raise ValueError(f"Client function '{function_name}' not found") ;
101
+ }
102
+ bundle_hash = introspector.ensure_bundle();
103
+ import from jaclang.project.config { find_project_root, get_config }
104
+ # Find project root by looking for jac.toml (base_path_dir might be src/ for entry files)
105
+ base_path_dir = Path(Jac.base_path_dir);
106
+ project_root_result = find_project_root(base_path_dir);
107
+ if project_root_result {
108
+ (base_path, _) = project_root_result;
109
+ } else {
110
+ # Fallback to base_path_dir if no project root found
111
+ base_path = base_path_dir;
112
+ }
113
+ # Get client directory from config or use default
114
+ config = get_config();
115
+ if config is not None {
116
+ dist_dir = config.get_client_dir() / 'dist';
117
+ } else {
118
+ dist_dir = base_path / '.jac' / 'client' / 'dist';
119
+ }
120
+ css_link = '';
121
+ css_file = dist_dir / 'styles.css';
122
+ if css_file.exists() {
123
+ css_hash = hashlib.sha256(css_file.read_bytes()).hexdigest()[:8];
124
+ css_link = f'<link rel="stylesheet" href="/static/styles.css?hash={css_hash}"/>';
125
+ }
126
+ # Get meta data from config
127
+ client_cfg = config.get_plugin_config("client") if config else None;
128
+ meta_data = client_cfg.get("app_meta_data", {}) if client_cfg else {};
129
+ charset = meta_data.get("charset", "UTF-8");
130
+ title = meta_data.get("title", function_name);
131
+ viewport = meta_data.get("viewport", "width=device-width, initial-scale=1");
132
+ description = meta_data.get("description", None);
133
+ robots = meta_data.get("robots", "index, follow");
134
+ canonical = meta_data.get("canonical", None);
135
+ og_type = meta_data.get("og_type", "website");
136
+ og_title = meta_data.get("og_title", title);
137
+ og_description = meta_data.get("og_description", None);
138
+ og_url = meta_data.get("og_url", None);
139
+ og_image = meta_data.get("og_image", None);
140
+ theme_color = meta_data.get("theme_color", "#ffffff");
141
+ icon = meta_data.get("icon", None);
142
+ # Build head content from TOML metadata
143
+ head_content = f'<meta charset="{html.escape(charset)}"/>\n <meta name="viewport" content="{html.escape(
144
+ viewport
145
+ )}"/>\n <title>{html.escape(title)}</title>';
146
+ head_content += f'\n <meta name="robots" content="{html.escape(robots)}"/>';
147
+ head_content += f'\n <meta name="theme-color" content="{html.escape(
148
+ theme_color
149
+ )}"/>';
150
+ head_content += f'\n <meta property="og:type" content="{html.escape(
151
+ og_type
152
+ )}"/>';
153
+ head_content += f'\n <meta property="og:title" content="{html.escape(
154
+ og_title
155
+ )}"/>';
156
+ if description {
157
+ head_content += f'\n <meta name="description" content="{html.escape(
158
+ description
159
+ )}"/>';
160
+ }
161
+ if canonical {
162
+ head_content += f'\n <link rel="canonical" href="{html.escape(
163
+ canonical
164
+ )}"/>';
165
+ }
166
+ if icon {
167
+ head_content += f'\n <link rel="icon" href="{html.escape(icon)}"/>';
168
+ }
169
+ if og_url {
170
+ head_content += f'\n <meta property="og:url" content="{html.escape(
171
+ og_url
172
+ )}"/>';
173
+ }
174
+ if og_image {
175
+ head_content += f'\n <meta property="og:image" content="{html.escape(
176
+ og_image
177
+ )}"/>';
178
+ }
179
+ if og_description {
180
+ head_content += f'\n <meta property="og:description" content="{html.escape(
181
+ og_description
182
+ )}"/>';
183
+ }
184
+ if css_link {
185
+ head_content += f"\n {css_link}";
186
+ }
187
+ page = f'<!DOCTYPE html><html lang="en"><head>{head_content}</head><body><div id="root"></div><script src="/static/client.js?hash={bundle_hash}" defer></script></body></html>';
188
+ return {
189
+ 'html': page,
190
+ 'bundle_hash': bundle_hash,
191
+ 'bundle_code': introspector._bundle.code
192
+ };
193
+ }
@@ -0,0 +1,195 @@
1
+ impl __jacJsx(tag: any, props: dict = {}, children: any = []) -> any {
2
+ if tag == None {
3
+ tag = React.Fragment;
4
+ }
5
+ childrenArray = [];
6
+ if children != None {
7
+ if Array.isArray(children) {
8
+ childrenArray = children;
9
+ } else {
10
+ childrenArray = [children];
11
+ }
12
+ }
13
+ reactChildren = [];
14
+ for child in childrenArray {
15
+ if child != None {
16
+ reactChildren.push(child);
17
+ }
18
+ }
19
+ if reactChildren.length > 0 {
20
+ args = [tag, props];
21
+ for child in reactChildren {
22
+ args.push(child);
23
+ }
24
+ return React.createElement.apply(React, args);
25
+ } else {
26
+ return React.createElement(tag, props);
27
+ }
28
+ }
29
+
30
+ impl useRouter -> dict {
31
+ navigate = reactRouterUseNavigate();
32
+ location = reactRouterUseLocation();
33
+ params = reactRouterUseParams();
34
+ return {
35
+ "navigate": navigate,
36
+ "location": location,
37
+ "params": params,
38
+ "pathname": location.pathname,
39
+ "search": location.search,
40
+ "hash": location.hash
41
+ };
42
+ }
43
+
44
+ impl navigate(path: str) -> None {
45
+ window.location.hash = "#" + path;
46
+ }
47
+
48
+ impl __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any {
49
+ token = __getLocalStorage("jac_token");
50
+ url = f"/walker/{left}";
51
+ if right != "" {
52
+ url = f"/walker/{left}/{right}";
53
+ }
54
+ response = await fetch(
55
+ url,
56
+ {
57
+ "method": "POST",
58
+ "accept": "application/json",
59
+ "headers": {
60
+ "Content-Type": "application/json",
61
+ "Authorization": f"Bearer {token}" if token else ""
62
+ },
63
+ "body": JSON.stringify(fields)
64
+ }
65
+ );
66
+ if not response.ok {
67
+ error_text = await response.json();
68
+ walker_name = f"{left}/{right}" if right else left;
69
+ raise Exception(f"Walker {walker_name} failed: {error_text}") ;
70
+ }
71
+ payload = await response.json();
72
+ return payload["data"] if payload["data"] else {};
73
+ }
74
+
75
+ impl jacSpawn(left: str, right: str = "", fields: dict = {}) -> any {
76
+ return __jacSpawn(left, right, fields);
77
+ }
78
+
79
+ impl __jacCallFunction(function_name: str, args: dict = {}) -> any {
80
+ token = __getLocalStorage("jac_token");
81
+ response = await fetch(
82
+ f"/function/{function_name}",
83
+ {
84
+ "method": "POST",
85
+ "headers": {
86
+ "Content-Type": "application/json",
87
+ "Authorization": f"Bearer {token}" if token else ""
88
+ },
89
+ "body": JSON.stringify({"args": args})
90
+ }
91
+ );
92
+ payload = await response.json();
93
+ if not payload["ok"] {
94
+ error_msg = payload["error"] if payload["error"] else "Unknown error";
95
+ raise Exception(f"Function {function_name} failed: {error_msg}") ;
96
+ }
97
+ result = None;
98
+ try {
99
+ if payload["data"] and payload["data"]["result"] {
100
+ result = payload["data"]["result"];
101
+ }
102
+ } except Exception { }
103
+ return result;
104
+ }
105
+
106
+ impl jacSignup(username: str, password: str) -> dict {
107
+ response = await fetch(
108
+ "/user/register",
109
+ {
110
+ "method": "POST",
111
+ "headers": {"Content-Type": "application/json"},
112
+ "body": JSON.stringify({"username": username, "password": password})
113
+ }
114
+ );
115
+ if response.ok {
116
+ data = JSON.parse(await response.text());
117
+ token = None;
118
+ if data["data"] and data["data"]["token"] {
119
+ token = data["data"]["token"];
120
+ }
121
+ if token {
122
+ __setLocalStorage("jac_token", token);
123
+ return {"success": True, "token": token, "username": username};
124
+ }
125
+ return {"success": False, "error": "No token received"};
126
+ } else {
127
+ error_text = await response.text();
128
+ try {
129
+ error_data = JSON.parse(error_text);
130
+ return {
131
+ "success": False,
132
+ "error": error_data["error"]
133
+ if error_data["error"] != None
134
+ else "Signup failed"
135
+ };
136
+ } except Exception {
137
+ return {"success": False, "error": error_text};
138
+ }
139
+ }
140
+ }
141
+
142
+ impl jacLogin(username: str, password: str) -> bool {
143
+ response = await fetch(
144
+ "/user/login",
145
+ {
146
+ "method": "POST",
147
+ "headers": {"Content-Type": "application/json"},
148
+ "body": JSON.stringify({"username": username, "password": password})
149
+ }
150
+ );
151
+ if response.ok {
152
+ data = JSON.parse(await response.text());
153
+ console.log("data", data);
154
+ token = None;
155
+ try {
156
+ if data["data"] and data["data"]["token"] {
157
+ token = data["data"]["token"];
158
+ }
159
+ } except Exception { }
160
+ console.log("token", token);
161
+ if token {
162
+ __setLocalStorage("jac_token", token);
163
+ return True;
164
+ }
165
+ }
166
+ return False;
167
+ }
168
+
169
+ impl jacLogout -> None {
170
+ __removeLocalStorage("jac_token");
171
+ }
172
+
173
+ impl jacIsLoggedIn -> bool {
174
+ token = __getLocalStorage("jac_token");
175
+ return token != None and token != "";
176
+ }
177
+
178
+ impl __getLocalStorage(key: str) -> str {
179
+ storage = globalThis.localStorage;
180
+ return storage.getItem(key) if storage else "";
181
+ }
182
+
183
+ impl __setLocalStorage(key: str, value: str) -> None {
184
+ storage = globalThis.localStorage;
185
+ if storage {
186
+ storage.setItem(key, value);
187
+ }
188
+ }
189
+
190
+ impl __removeLocalStorage(key: str) -> None {
191
+ storage = globalThis.localStorage;
192
+ if storage {
193
+ storage.removeItem(key);
194
+ }
195
+ }
@@ -0,0 +1,72 @@
1
+ """Clean up the compiled directory and its contents."""
2
+
3
+ impl ViteClientBundleBuilder.cleanup_temp_dir(self: ViteClientBundleBuilder) -> None {
4
+ if (not self.vite_package_json or not self.vite_package_json.exists()) {
5
+ return;
6
+ }
7
+ project_dir = self.vite_package_json.parent;
8
+ temp_dir = project_dir / 'compiled';
9
+ if temp_dir.exists() {
10
+ with contextlib.suppress(OSError, shutil.Error) {
11
+ shutil.rmtree(temp_dir);
12
+ }
13
+ }
14
+ }
15
+
16
+ """Override to use Vite bundling instead of simple concatenation."""
17
+ impl ViteClientBundleBuilder._compile_bundle(
18
+ self: ViteClientBundleBuilder, module: ModuleType, module_path: Path
19
+ ) -> ClientBundle {
20
+ compiler = self._get_compiler();
21
+ (bundle_code, bundle_hash, client_exports, client_globals) = compiler.compile_and_bundle(
22
+ module, module_path
23
+ );
24
+ return ClientBundle(
25
+ module_name=module.__name__,
26
+ code=bundle_code,
27
+ client_functions=client_exports,
28
+ client_globals=client_globals,
29
+ hash=bundle_hash
30
+ );
31
+ }
32
+
33
+ """Get or create the Vite compiler instance."""
34
+ impl ViteClientBundleBuilder._get_compiler(
35
+ self: ViteClientBundleBuilder
36
+ ) -> ViteCompiler {
37
+ if (self._compiler is None) {
38
+ if (not self.vite_package_json or not self.vite_package_json.exists()) {
39
+ raise ClientBundleError(
40
+ 'Vite package.json not found. Set vite_package_json when using ViteClientBundleBuilder'
41
+ ) ;
42
+ }
43
+ compile_to_js_func: Callable[([Path], tuple[(str, (ModuleType | None))])] = cast(
44
+ Callable[([Path], tuple[(str, (ModuleType | None))])], self._compile_to_js
45
+ );
46
+ self._compiler = ViteCompiler(
47
+ vite_package_json=self.vite_package_json,
48
+ vite_output_dir=self.vite_output_dir,
49
+ vite_minify=self.vite_minify,
50
+ runtime_path=self.runtime_path,
51
+ compile_to_js_func=compile_to_js_func,
52
+ extract_exports_func=self._extract_client_exports,
53
+ extract_globals_func=self._extract_client_globals
54
+ );
55
+ }
56
+ return self._compiler;
57
+ }
58
+
59
+ """Initialize the Vite-enhanced bundle builder."""
60
+ impl ViteClientBundleBuilder.init(
61
+ self: ViteClientBundleBuilder,
62
+ runtime_path: (Path | None) = None,
63
+ vite_output_dir: (Path | None) = None,
64
+ vite_package_json: (Path | None) = None,
65
+ vite_minify: bool = False
66
+ ) -> None {
67
+ super.init(runtime_path);
68
+ self.vite_output_dir = vite_output_dir;
69
+ self.vite_package_json = vite_package_json;
70
+ self.vite_minify = vite_minify;
71
+ self._compiler: (ViteCompiler | None) = None;
72
+ }
@@ -0,0 +1,195 @@
1
+ """Plugin configuration hooks for jac-client.
2
+
3
+ This module implements JacPluginConfig hooks to integrate with core's
4
+ configuration system, registering:
5
+ - Plugin metadata (name, version)
6
+ - Config schema for [plugins.client] section
7
+ - npm dependency type for [dependencies.npm] section
8
+ """
9
+
10
+ import subprocess;
11
+ import sys;
12
+ import from pathlib { Path }
13
+ import from typing { Any }
14
+ import from jaclang.pycore.runtime { hookimpl }
15
+ import from jaclang.project.config { JacConfig }
16
+
17
+ """Plugin configuration hooks for jac-client."""
18
+ class JacClientPluginConfig {
19
+ """Return plugin metadata."""
20
+ @hookimpl
21
+ static def get_plugin_metadata -> dict[str, Any] {
22
+ return {
23
+ "name": "client",
24
+ "version": "1.0.0",
25
+ "description": "Jac client-side rendering with Vite bundling"
26
+ };
27
+ }
28
+
29
+ """Return the plugin's configuration schema for [plugins.client]."""
30
+ @hookimpl
31
+ static def get_config_schema -> dict[str, Any] {
32
+ return {
33
+ "section": "client",
34
+ "options": {
35
+ "vite": {
36
+ "type": "dict",
37
+ "default": {},
38
+ "description": "Vite bundler configuration",
39
+ "nested": {
40
+ "plugins": {
41
+ "type": "list",
42
+ "default": [],
43
+ "description": "Vite plugins to include"
44
+ },
45
+ "lib_imports": {
46
+ "type": "list",
47
+ "default": [],
48
+ "description": "Library imports for vite.config.js"
49
+ },
50
+ "build": {
51
+ "type": "dict",
52
+ "default": {},
53
+ "description": "Vite build options"
54
+ },
55
+ "server": {
56
+ "type": "dict",
57
+ "default": {},
58
+ "description": "Vite dev server options"
59
+ },
60
+ "resolve": {
61
+ "type": "dict",
62
+ "default": {},
63
+ "description": "Vite resolve options"
64
+ }
65
+ }
66
+ },
67
+ "ts": {
68
+ "type": "dict",
69
+ "default": {},
70
+ "description": "TypeScript configuration overrides"
71
+ }
72
+ }
73
+ };
74
+ }
75
+
76
+ """Register npm as a dependency type for [dependencies.npm]."""
77
+ @hookimpl
78
+ static def register_dependency_type -> dict[str, Any] {
79
+ return {
80
+ "name": "npm",
81
+ "dev_name": "npm.dev",
82
+ "cli_flag": "--cl",
83
+ "install_dir": ".jac/client/configs",
84
+ "install_handler": _npm_install_handler,
85
+ "install_all_handler": _npm_install_all_handler,
86
+ "remove_handler": _npm_remove_handler
87
+ };
88
+ }
89
+ }
90
+
91
+ """Install an npm package."""
92
+ def _npm_install_handler(
93
+ config: JacConfig,
94
+ package_name: str,
95
+ version: str | None = None,
96
+ is_dev: bool = False
97
+ ) -> None {
98
+ import from jac_client.plugin.src.vite_bundler { ViteBundler }
99
+
100
+ if config.project_root is None {
101
+ raise RuntimeError("No project root found") ;
102
+ }
103
+
104
+ project_dir = config.project_root;
105
+
106
+ # Add to config (core JacConfig handles this)
107
+ package_version = version or "latest";
108
+ config.add_dependency(package_name, package_version, dev=is_dev, dep_type="npm");
109
+ config.save();
110
+
111
+ # Generate package.json and run npm install
112
+ _regenerate_and_install(project_dir);
113
+ }
114
+
115
+ """Install all npm packages from jac.toml."""
116
+ def _npm_install_all_handler(config: JacConfig) -> None {
117
+ if config.project_root is None {
118
+ raise RuntimeError("No project root found") ;
119
+ }
120
+
121
+ _regenerate_and_install(config.project_root);
122
+ }
123
+
124
+ """Remove an npm package."""
125
+ def _npm_remove_handler(
126
+ config: JacConfig, package_name: str, is_dev: bool = False
127
+ ) -> None {
128
+ if config.project_root is None {
129
+ raise RuntimeError("No project root found") ;
130
+ }
131
+
132
+ project_dir = config.project_root;
133
+
134
+ # Remove from config (core JacConfig handles this)
135
+ result = config.remove_dependency(package_name, dev=is_dev, dep_type="npm");
136
+ if not result {
137
+ deps_key = "dev-dependencies" if is_dev else "dependencies";
138
+ raise RuntimeError(f'Package "{package_name}" not found in {deps_key}') ;
139
+ }
140
+
141
+ config.save();
142
+
143
+ # Regenerate package.json and run npm install
144
+ _regenerate_and_install(project_dir);
145
+ }
146
+
147
+ """Regenerate package.json from jac.toml and run npm install."""
148
+ def _regenerate_and_install(project_dir: Path) -> None {
149
+ import from jac_client.plugin.src.vite_bundler { ViteBundler }
150
+ import shutil;
151
+
152
+ bundler = ViteBundler(project_dir);
153
+ bundler.create_package_json();
154
+
155
+ # Install to .jac/client/ directory where babel processor expects it
156
+ client_dir = bundler._get_client_dir();
157
+ client_dir.mkdir(parents=True, exist_ok=True);
158
+
159
+ # Copy package.json to .jac/client/ for npm install
160
+ configs_package_json = client_dir / 'configs' / 'package.json';
161
+ build_package_json = client_dir / 'package.json';
162
+ if configs_package_json.exists() {
163
+ shutil.copy2(configs_package_json, build_package_json);
164
+ }
165
+
166
+ try {
167
+ subprocess.run(
168
+ ["npm", "install"],
169
+ cwd=client_dir,
170
+ check=True,
171
+ capture_output=True,
172
+ text=True
173
+ );
174
+ } except subprocess.CalledProcessError as e {
175
+ raise RuntimeError(f"Failed to install npm packages: {e.stderr}") from e ;
176
+ } except FileNotFoundError {
177
+ raise RuntimeError(
178
+ "npm command not found. Ensure Node.js and npm are installed."
179
+ ) from None ;
180
+ } finally {
181
+ # Clean up temporary package.json
182
+ if build_package_json.exists() {
183
+ build_package_json.unlink();
184
+ }
185
+ # Move package-lock.json to configs/ if it exists
186
+ build_package_lock = client_dir / 'package-lock.json';
187
+ if build_package_lock.exists() {
188
+ configs_package_lock = client_dir / 'configs' / 'package-lock.json';
189
+ if configs_package_lock.exists() {
190
+ configs_package_lock.unlink();
191
+ }
192
+ build_package_lock.rename(configs_package_lock);
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,20 @@
1
+ """Vite client bundle processing modules."""
2
+ import from jac_client.plugin.src.asset_processor { AssetProcessor }
3
+ import from jac_client.plugin.src.babel_processor { BabelProcessor }
4
+ import from jac_client.plugin.src.compiler { ViteCompiler }
5
+ import from jac_client.plugin.src.config_loader { JacClientConfig }
6
+ import from jac_client.plugin.src.import_processor { ImportProcessor }
7
+ import from jac_client.plugin.src.jac_to_js { JacToJSCompiler }
8
+ import from jac_client.plugin.src.package_installer { PackageInstaller }
9
+ import from jac_client.plugin.src.vite_bundler { ViteBundler }
10
+
11
+ glob __all__ = [
12
+ 'AssetProcessor',
13
+ 'BabelProcessor',
14
+ 'ViteCompiler',
15
+ 'JacClientConfig',
16
+ 'ImportProcessor',
17
+ 'JacToJSCompiler',
18
+ 'ViteBundler',
19
+ 'PackageInstaller'
20
+ ];