jac-client 0.2.3__py3-none-any.whl → 0.2.4__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 (176) hide show
  1. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  2. jac_client/tests/conftest.py +281 -0
  3. jac_client/tests/test_cli.py +755 -0
  4. jac_client/tests/test_it.py +347 -67
  5. {jac_client-0.2.3.dist-info → jac_client-0.2.4.dist-info}/METADATA +28 -30
  6. jac_client-0.2.4.dist-info/RECORD +10 -0
  7. {jac_client-0.2.3.dist-info → jac_client-0.2.4.dist-info}/WHEEL +2 -1
  8. jac_client-0.2.4.dist-info/entry_points.txt +4 -0
  9. jac_client-0.2.4.dist-info/top_level.txt +1 -0
  10. jac_client/docs/README.md +0 -689
  11. jac_client/docs/advanced-state.md +0 -1265
  12. jac_client/docs/asset-serving/intro.md +0 -209
  13. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  14. jac_client/docs/assets/pipe_line.png +0 -0
  15. jac_client/docs/file-system/app.jac.md +0 -121
  16. jac_client/docs/file-system/backend-frontend.md +0 -217
  17. jac_client/docs/file-system/intro.md +0 -72
  18. jac_client/docs/file-system/nested-imports.md +0 -348
  19. jac_client/docs/guide-example/intro.md +0 -115
  20. jac_client/docs/guide-example/step-01-setup.md +0 -270
  21. jac_client/docs/guide-example/step-02-components.md +0 -416
  22. jac_client/docs/guide-example/step-03-styling.md +0 -478
  23. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  24. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  25. jac_client/docs/guide-example/step-06-events.md +0 -749
  26. jac_client/docs/guide-example/step-07-effects.md +0 -468
  27. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  28. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  29. jac_client/docs/guide-example/step-10-routing.md +0 -539
  30. jac_client/docs/guide-example/step-11-final.md +0 -963
  31. jac_client/docs/imports.md +0 -1141
  32. jac_client/docs/lifecycle-hooks.md +0 -773
  33. jac_client/docs/routing.md +0 -659
  34. jac_client/docs/styling/intro.md +0 -249
  35. jac_client/docs/styling/js-styling.md +0 -367
  36. jac_client/docs/styling/material-ui.md +0 -341
  37. jac_client/docs/styling/pure-css.md +0 -299
  38. jac_client/docs/styling/sass.md +0 -403
  39. jac_client/docs/styling/styled-components.md +0 -395
  40. jac_client/docs/styling/tailwind.md +0 -298
  41. jac_client/examples/all-in-one/.babelrc +0 -9
  42. jac_client/examples/all-in-one/README.md +0 -16
  43. jac_client/examples/all-in-one/app.jac +0 -426
  44. jac_client/examples/all-in-one/assets/burger.png +0 -0
  45. jac_client/examples/all-in-one/button.jac +0 -7
  46. jac_client/examples/all-in-one/components/button.jac +0 -7
  47. jac_client/examples/all-in-one/package.json +0 -29
  48. jac_client/examples/all-in-one/styles.css +0 -26
  49. jac_client/examples/all-in-one/vite.config.js +0 -28
  50. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  51. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  52. jac_client/examples/asset-serving/css-with-image/app.jac +0 -88
  53. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  54. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  55. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  56. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  57. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  58. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  59. jac_client/examples/asset-serving/image-asset/app.jac +0 -55
  60. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  61. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  62. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  63. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  64. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  65. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  66. jac_client/examples/asset-serving/import-alias/app.jac +0 -111
  67. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  68. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  69. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  70. jac_client/examples/basic/.babelrc +0 -9
  71. jac_client/examples/basic/README.md +0 -16
  72. jac_client/examples/basic/app.jac +0 -21
  73. jac_client/examples/basic/package.json +0 -27
  74. jac_client/examples/basic/vite.config.js +0 -27
  75. jac_client/examples/basic-auth/.babelrc +0 -9
  76. jac_client/examples/basic-auth/README.md +0 -16
  77. jac_client/examples/basic-auth/app.jac +0 -308
  78. jac_client/examples/basic-auth/package.json +0 -27
  79. jac_client/examples/basic-auth/vite.config.js +0 -27
  80. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  81. jac_client/examples/basic-auth-with-router/README.md +0 -60
  82. jac_client/examples/basic-auth-with-router/app.jac +0 -464
  83. jac_client/examples/basic-auth-with-router/package.json +0 -28
  84. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  85. jac_client/examples/basic-full-stack/.babelrc +0 -9
  86. jac_client/examples/basic-full-stack/README.md +0 -18
  87. jac_client/examples/basic-full-stack/app.jac +0 -320
  88. jac_client/examples/basic-full-stack/package.json +0 -28
  89. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  90. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  91. jac_client/examples/css-styling/js-styling/README.md +0 -183
  92. jac_client/examples/css-styling/js-styling/app.jac +0 -84
  93. jac_client/examples/css-styling/js-styling/package.json +0 -28
  94. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  95. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  96. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  97. jac_client/examples/css-styling/material-ui/README.md +0 -16
  98. jac_client/examples/css-styling/material-ui/app.jac +0 -122
  99. jac_client/examples/css-styling/material-ui/package.json +0 -32
  100. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  101. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  102. jac_client/examples/css-styling/pure-css/README.md +0 -16
  103. jac_client/examples/css-styling/pure-css/app.jac +0 -64
  104. jac_client/examples/css-styling/pure-css/package.json +0 -28
  105. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  106. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  107. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  108. jac_client/examples/css-styling/sass-example/README.md +0 -16
  109. jac_client/examples/css-styling/sass-example/app.jac +0 -64
  110. jac_client/examples/css-styling/sass-example/package.json +0 -29
  111. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  112. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  113. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  114. jac_client/examples/css-styling/styled-components/README.md +0 -16
  115. jac_client/examples/css-styling/styled-components/app.jac +0 -71
  116. jac_client/examples/css-styling/styled-components/package.json +0 -29
  117. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  118. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  119. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  120. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  121. jac_client/examples/css-styling/tailwind-example/app.jac +0 -63
  122. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  123. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  124. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  125. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  126. jac_client/examples/full-stack-with-auth/README.md +0 -16
  127. jac_client/examples/full-stack-with-auth/app.jac +0 -722
  128. jac_client/examples/full-stack-with-auth/package.json +0 -28
  129. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  130. jac_client/examples/little-x/app.jac +0 -724
  131. jac_client/examples/little-x/package.json +0 -23
  132. jac_client/examples/little-x/submit-button.jac +0 -8
  133. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  134. jac_client/examples/nested-folders/nested-advance/ButtonRoot.jac +0 -11
  135. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  136. jac_client/examples/nested-folders/nested-advance/app.jac +0 -35
  137. jac_client/examples/nested-folders/nested-advance/level1/ButtonSecondL.jac +0 -19
  138. jac_client/examples/nested-folders/nested-advance/level1/Card.jac +0 -43
  139. jac_client/examples/nested-folders/nested-advance/level1/level2/ButtonThirdL.jac +0 -25
  140. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  141. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  142. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  143. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  144. jac_client/examples/nested-folders/nested-basic/app.jac +0 -13
  145. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  146. jac_client/examples/nested-folders/nested-basic/button.jac +0 -7
  147. jac_client/examples/nested-folders/nested-basic/components/button.jac +0 -7
  148. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  149. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  150. jac_client/examples/with-router/.babelrc +0 -9
  151. jac_client/examples/with-router/README.md +0 -17
  152. jac_client/examples/with-router/app.jac +0 -323
  153. jac_client/examples/with-router/package.json +0 -28
  154. jac_client/examples/with-router/vite.config.js +0 -27
  155. jac_client/plugin/cli.py +0 -244
  156. jac_client/plugin/client.py +0 -152
  157. jac_client/plugin/client_runtime.jac +0 -234
  158. jac_client/plugin/vite_client_bundle.py +0 -503
  159. jac_client/tests/fixtures/basic-app/app.jac +0 -23
  160. jac_client/tests/fixtures/cl_file/app.cl.jac +0 -48
  161. jac_client/tests/fixtures/cl_file/app.jac +0 -15
  162. jac_client/tests/fixtures/client_app_with_antd/app.jac +0 -34
  163. jac_client/tests/fixtures/js_import/app.jac +0 -34
  164. jac_client/tests/fixtures/js_import/utils.js +0 -21
  165. jac_client/tests/fixtures/package-lock.json +0 -329
  166. jac_client/tests/fixtures/package.json +0 -11
  167. jac_client/tests/fixtures/relative_import/app.jac +0 -11
  168. jac_client/tests/fixtures/relative_import/button.jac +0 -7
  169. jac_client/tests/fixtures/spawn_test/app.jac +0 -129
  170. jac_client/tests/fixtures/test_fragments_spread/app.jac +0 -67
  171. jac_client/tests/test_asset_examples.py +0 -322
  172. jac_client/tests/test_cl.py +0 -530
  173. jac_client/tests/test_create_jac_app.py +0 -131
  174. jac_client/tests/test_nested_file.py +0 -374
  175. jac_client-0.2.3.dist-info/RECORD +0 -171
  176. jac_client-0.2.3.dist-info/entry_points.txt +0 -4
@@ -1,503 +0,0 @@
1
- """Vite-enhanced client bundle generation for Jac web front-ends."""
2
-
3
- from __future__ import annotations
4
-
5
- import contextlib
6
- import hashlib
7
- import shutil
8
- import subprocess
9
- from pathlib import Path
10
- from types import ModuleType
11
- from typing import TYPE_CHECKING, Any
12
-
13
- from jaclang.runtimelib.client_bundle import (
14
- ClientBundle,
15
- ClientBundleBuilder,
16
- ClientBundleError,
17
- )
18
-
19
- if TYPE_CHECKING:
20
- from jaclang.compiler.codeinfo import ClientManifest
21
-
22
-
23
- class ViteClientBundleBuilder(ClientBundleBuilder):
24
- """Enhanced ClientBundleBuilder that uses Vite for optimized bundling."""
25
-
26
- def __init__(
27
- self,
28
- runtime_path: Path | None = None,
29
- vite_output_dir: Path | None = None,
30
- vite_package_json: Path | None = None,
31
- vite_minify: bool = False,
32
- ) -> None:
33
- """Initialize the Vite-enhanced bundle builder.
34
-
35
- Args:
36
- runtime_path: Path to client runtime file
37
- vite_output_dir: Output directory for Vite builds (defaults to temp/dist)
38
- vite_package_json: Path to package.json for Vite (required)
39
- vite_minify: Whether to enable minification in Vite build
40
- """
41
- super().__init__(runtime_path)
42
- self.vite_output_dir = vite_output_dir
43
- self.vite_package_json = vite_package_json
44
- self.vite_minify = vite_minify
45
-
46
- def _process_vite_imports(
47
- self, manifest: ClientManifest | None, module_path: Path
48
- ) -> list[Path | None]:
49
- """Process client imports for Vite bundling.
50
-
51
- Only mark modules as bundled when we actually inline their code (.jac files we compile
52
- and local .js files we embed). Bare package specifiers (e.g., "antd") are left as real
53
- ES imports so Vite can resolve and bundle them.
54
- """
55
- imported_js_modules: list[Path | None] = []
56
- if manifest and manifest.imports:
57
- for _, import_path in manifest.imports.items():
58
- import_path_obj = Path(import_path)
59
-
60
- if import_path_obj.suffix == ".js":
61
- # Inline local JS files and mark as bundled
62
- try:
63
- imported_js_modules.append(import_path_obj)
64
- except FileNotFoundError:
65
- imported_js_modules.append(None)
66
-
67
- elif import_path_obj.suffix == ".jac":
68
- # Compile .jac imports and include transitive .jac imports
69
- try:
70
- imported_js_modules.append(import_path_obj)
71
- except ClientBundleError:
72
- imported_js_modules.append(None)
73
-
74
- else:
75
- # Non .jac/.js entries (likely bare specifiers) should be handled by Vite.
76
- # Do not inline or mark as bundled so their import lines are preserved.
77
- pass
78
-
79
- return imported_js_modules
80
-
81
- def _compile_dependencies_recursively(
82
- self,
83
- module_path: Path,
84
- visited: set[Path] | None = None,
85
- collected_exports: set[str] | None = None,
86
- collected_globals: dict[str, Any] | None = None,
87
- source_root: Path | None = None,
88
- ) -> None:
89
- """Recursively compile/copy .jac/.js imports to temp, skipping bundling.
90
-
91
- Only prepares dependency JS artifacts for Vite by writing compiled JS (.jac)
92
- or copying local JS (.js) into the temp directory. Bare specifiers are left
93
- untouched for Vite to resolve.
94
-
95
- Args:
96
- module_path: Path to the module being compiled
97
- visited: Set of already visited paths to avoid cycles
98
- collected_exports: Set to accumulate exported symbols
99
- collected_globals: Dict to accumulate global values
100
- source_root: Root directory of the source files (for preserving folder structure)
101
- """
102
- if visited is None:
103
- visited = set()
104
- if collected_exports is None:
105
- collected_exports = set()
106
- if collected_globals is None:
107
- collected_globals = {}
108
-
109
- module_path = module_path.resolve()
110
- if module_path in visited:
111
- return
112
- visited.add(module_path)
113
-
114
- # Set source_root on first call (root module's parent directory)
115
- if source_root is None:
116
- source_root = module_path.parent.resolve()
117
-
118
- manifest = None
119
-
120
- # Compile current module to JS and append registration
121
- module_js, mod = self._compile_to_js(module_path)
122
- manifest = mod.gen.client_manifest if mod else None
123
-
124
- # Extract exports from manifest
125
- exports_list = self._extract_client_exports(manifest)
126
- collected_exports.update(exports_list)
127
-
128
- # Build globals map using manifest.globals_values only for non-root
129
- non_root_globals: dict[str, Any] = {}
130
- if manifest:
131
- for name in manifest.globals:
132
- non_root_globals[name] = manifest.globals_values.get(name)
133
- collected_globals.update(non_root_globals)
134
- export_block = (
135
- f"export {{ {', '.join(exports_list)} }};\n" if exports_list else ""
136
- )
137
-
138
- # inport jacJsx from client_runtime_utils.jac
139
- jac_jsx_path = 'import {__jacJsx, __jacSpawn} from "@jac-client/utils";'
140
-
141
- combined_js = f"{jac_jsx_path}\n{module_js}\n{export_block}"
142
- if self.vite_package_json is not None:
143
- # Preserve folder structure: calculate relative path from source_root
144
- try:
145
- relative_path = module_path.relative_to(source_root)
146
- # Change extension from .jac to .js
147
- output_path = (
148
- self.vite_package_json.parent
149
- / "src"
150
- / relative_path.with_suffix(".js")
151
- )
152
- except ValueError:
153
- # If file is outside source_root, fall back to just filename
154
- output_path = (
155
- self.vite_package_json.parent / "src" / f"{module_path.stem}.js"
156
- )
157
-
158
- # Ensure parent directories exist
159
- output_path.parent.mkdir(parents=True, exist_ok=True)
160
- output_path.write_text(combined_js, encoding="utf-8")
161
-
162
- if not manifest or not manifest.imports:
163
- return
164
-
165
- for _name, import_path in manifest.imports.items():
166
- path_obj = Path(import_path).resolve()
167
- # Avoid re-processing
168
- if path_obj in visited:
169
- continue
170
- if path_obj.suffix == ".jac":
171
- # Recurse into transitive deps
172
- self._compile_dependencies_recursively(
173
- path_obj,
174
- visited,
175
- collected_exports=collected_exports,
176
- collected_globals=collected_globals,
177
- source_root=source_root,
178
- )
179
- elif path_obj.suffix == ".js":
180
- try:
181
- js_code = path_obj.read_text(encoding="utf-8")
182
- if self.vite_package_json is not None:
183
- # Preserve folder structure for .js files too
184
- try:
185
- relative_path = path_obj.relative_to(source_root)
186
- output_path = (
187
- self.vite_package_json.parent / "src" / relative_path
188
- )
189
- except ValueError:
190
- # If file is outside source_root, fall back to just filename
191
- output_path = (
192
- self.vite_package_json.parent / "src" / path_obj.name
193
- )
194
-
195
- # Ensure parent directories exist
196
- output_path.parent.mkdir(parents=True, exist_ok=True)
197
- output_path.write_text(js_code, encoding="utf-8")
198
- except FileNotFoundError:
199
- pass
200
- else:
201
- # Bare specifiers or other assets handled by Vite
202
- if self.vite_package_json is not None and path_obj.is_file():
203
- # Preserve folder structure for other assets too
204
- try:
205
- relative_path = path_obj.relative_to(source_root)
206
- output_path = (
207
- self.vite_package_json.parent / "src" / relative_path
208
- )
209
- except ValueError:
210
- # If file is outside source_root, fall back to just filename
211
- output_path = (
212
- self.vite_package_json.parent / "src" / path_obj.name
213
- )
214
-
215
- # Ensure parent directories exist
216
- output_path.parent.mkdir(parents=True, exist_ok=True)
217
- output_path.write_text(
218
- path_obj.read_text(encoding="utf-8"), encoding="utf-8"
219
- )
220
- continue
221
-
222
- def _compile_bundle(
223
- self,
224
- module: ModuleType,
225
- module_path: Path,
226
- ) -> ClientBundle:
227
- """Override to use Vite bundling instead of simple concatenation."""
228
-
229
- # Check if package.json exists before proceeding
230
- if not self.vite_package_json or not self.vite_package_json.exists():
231
- raise ClientBundleError(
232
- "Vite package.json not found. Set vite_package_json when using ViteClientBundleBuilder"
233
- )
234
-
235
- # client_runtime for jac client utils
236
- runtime_utils_path = self.runtime_path.parent / "client_runtime.jac"
237
- runtimeutils_js, mod = self._compile_to_js(runtime_utils_path)
238
- runtimeutils_manifest = mod.gen.client_manifest if mod else None
239
- runtimeutils_exports_list = self._extract_client_exports(runtimeutils_manifest)
240
-
241
- # Add React Router exports that are variable declarations (not functions)
242
- # These need to be manually added since they're 'let' declarations, not 'def' functions
243
- router_exports = [
244
- "Router",
245
- "Routes",
246
- "Route",
247
- "Link",
248
- "Navigate",
249
- "useNavigate",
250
- "useLocation",
251
- "useParams",
252
- ]
253
-
254
- # Combine manifest exports with router exports
255
- all_exports = sorted(set(runtimeutils_exports_list + router_exports))
256
-
257
- export_block = (
258
- f"export {{ {', '.join(all_exports)} }};\n" if all_exports else ""
259
- )
260
-
261
- combined_runtime_utils_js = f"{runtimeutils_js}\n{export_block}"
262
- (self.vite_package_json.parent / "src" / "client_runtime.js").write_text(
263
- combined_runtime_utils_js, encoding="utf-8"
264
- )
265
-
266
- # Get manifest from JacProgram first to check for imports
267
- # Collect exports/globals across root and recursive deps
268
- module_js, mod = self._compile_to_js(module_path)
269
- module_manifest = mod.gen.client_manifest if mod else None
270
- collected_exports: set[str] = set(self._extract_client_exports(module_manifest))
271
-
272
- client_globals_map = self._extract_client_globals(module_manifest, module)
273
- collected_globals: dict[str, Any] = dict(client_globals_map)
274
- # Recursively prepare dependencies and accumulate symbols
275
- self._compile_dependencies_recursively(
276
- module_path,
277
- collected_exports=collected_exports,
278
- collected_globals=collected_globals,
279
- )
280
-
281
- # Copy assets from root assets/ folder to src/assets/ for @jac-client/assets alias
282
- project_dir = self.vite_package_json.parent
283
- root_assets_dir = project_dir / "assets"
284
- src_assets_dir = project_dir / "src" / "assets"
285
- if root_assets_dir.exists() and root_assets_dir.is_dir():
286
- self._copy_asset_files(root_assets_dir, src_assets_dir)
287
-
288
- client_exports = sorted(collected_exports)
289
- client_globals_map = collected_globals
290
-
291
- entry_file = self.vite_package_json.parent / "src" / "main.js"
292
-
293
- entry_content = """import React from "react";
294
- import { createRoot } from "react-dom/client";
295
- import { app as App } from "./app.js";
296
-
297
- const root = createRoot(document.getElementById("root"));
298
- root.render(<App />);
299
- """
300
- entry_file.write_text(entry_content, encoding="utf-8")
301
-
302
- bundle_code, bundle_hash = self._bundle_with_vite(
303
- module.__name__, client_exports
304
- )
305
-
306
- return ClientBundle(
307
- module_name=module.__name__,
308
- code=bundle_code,
309
- client_functions=client_exports,
310
- client_globals=list(client_globals_map.keys()),
311
- hash=bundle_hash,
312
- )
313
-
314
- def _bundle_with_vite(
315
- self, module_name: str, client_functions: list[str]
316
- ) -> tuple[str, str]:
317
- """Bundle JavaScript code using Vite for optimization.
318
-
319
- Args:
320
- module_name: Name of the module being bundled
321
- client_functions: List of client function names
322
-
323
- Returns:
324
- Tuple of (bundle_code, bundle_hash)
325
-
326
- Raises:
327
- ClientBundleError: If Vite bundling fails
328
- """
329
- if not self.vite_package_json or not self.vite_package_json.exists():
330
- raise ClientBundleError(
331
- "Vite package.json not found. Set vite_package_json when using ViteClientBundleBuilder"
332
- )
333
-
334
- # Create temp directory for Vite build
335
- project_dir = self.vite_package_json.parent
336
- src_dir = project_dir / "src"
337
- src_dir.mkdir(exist_ok=True)
338
-
339
- output_dir = self.vite_output_dir or src_dir / "dist" / "assets"
340
- output_dir.mkdir(parents=True, exist_ok=True)
341
-
342
- try:
343
- # Run Vite build from project directory
344
- # need to install packages you told in package.json inside here
345
- # first compile the code
346
- command = ["npm", "run", "compile"]
347
- subprocess.run(
348
- command, cwd=project_dir, check=True, capture_output=True, text=True
349
- )
350
- # Copy CSS and other asset files from src/ to build/ after Babel compilation
351
- # Babel only transpiles JS, so we need to manually copy assets
352
- self._copy_asset_files(project_dir / "src", project_dir / "build")
353
- # then build the code
354
- command = ["npm", "run", "build"]
355
- subprocess.run(
356
- command, cwd=project_dir, check=True, capture_output=True, text=True
357
- )
358
- except subprocess.CalledProcessError as e:
359
- raise ClientBundleError(f"Vite build failed: {e.stderr}") from e
360
- except FileNotFoundError:
361
- raise ClientBundleError(
362
- "npx or vite command not found. Ensure Node.js and npm are installed."
363
- ) from None
364
- # Find the generated bundle file
365
- bundle_file = self._find_vite_bundle(output_dir)
366
- if not bundle_file:
367
- raise ClientBundleError("Vite build completed but no bundle file found")
368
-
369
- # Read the bundled code
370
- bundle_code = bundle_file.read_text(encoding="utf-8")
371
- bundle_hash = hashlib.sha256(bundle_code.encode("utf-8")).hexdigest()
372
-
373
- return bundle_code, bundle_hash
374
-
375
- def _generate_vite_config(self, entry_file: Path, output_dir: Path) -> str:
376
- """Generate Vite configuration for bundling."""
377
- entry_name = entry_file.as_posix()
378
- output_dir_name = output_dir.as_posix()
379
- minify_setting = "true" if self.vite_minify else "false"
380
-
381
- return f"""
382
- import {{ defineConfig }} from 'vite';
383
- import {{ resolve }} from 'path';
384
-
385
- export default defineConfig({{
386
- build: {{
387
- outDir: '{output_dir_name}',
388
- emptyOutDir: true,
389
- rollupOptions: {{
390
- input: {{
391
- main: resolve(__dirname, '{entry_name}'),
392
- }},
393
- output: {{
394
- entryFileNames: 'client.[hash].js',
395
- format: 'iife',
396
- name: 'JacClient',
397
- }},
398
- }},
399
- minify: {minify_setting}, // Configurable minification
400
- }},
401
- resolve: {{
402
- }}
403
- }});
404
- """
405
-
406
- def _copy_asset_files(self, src_dir: Path, build_dir: Path) -> None:
407
- """Copy CSS and other asset files from src/ to build/ directory recursively.
408
-
409
- Babel only transpiles JavaScript files, so CSS and other assets need to be
410
- manually copied to the build directory for Vite to resolve them.
411
- This method recursively copies assets from subdirectories (e.g., src/assets/)
412
- while preserving the directory structure.
413
- """
414
- if not src_dir.exists():
415
- return
416
-
417
- # Ensure build directory exists
418
- build_dir.mkdir(parents=True, exist_ok=True)
419
-
420
- # Asset file extensions to copy
421
- asset_extensions = {
422
- ".css",
423
- ".scss",
424
- ".sass",
425
- ".less",
426
- ".svg",
427
- ".png",
428
- ".jpg",
429
- ".jpeg",
430
- ".gif",
431
- ".webp",
432
- ".ico",
433
- ".woff",
434
- ".woff2",
435
- ".ttf",
436
- ".eot",
437
- ".otf",
438
- ".mp4",
439
- ".webm",
440
- ".mp3",
441
- ".wav",
442
- }
443
-
444
- def copy_recursive(
445
- source: Path, destination: Path, base: Path | None = None
446
- ) -> None:
447
- """Recursively copy asset files from source to destination.
448
-
449
- Args:
450
- source: Source directory to copy from
451
- destination: Destination directory to copy to
452
- base: Base directory for calculating relative paths (defaults to source)
453
- """
454
- if not source.exists():
455
- return
456
-
457
- if base is None:
458
- base = source
459
-
460
- for item in source.iterdir():
461
- if item.is_file() and item.suffix.lower() in asset_extensions:
462
- # Preserve relative path structure from base
463
- relative_path = item.relative_to(base)
464
- dest_file = destination / relative_path
465
- dest_file.parent.mkdir(parents=True, exist_ok=True)
466
- with contextlib.suppress(OSError, shutil.Error):
467
- shutil.copy2(item, dest_file)
468
- elif item.is_dir():
469
- # Recursively process subdirectories
470
- copy_recursive(item, destination, base)
471
-
472
- # Copy files from src_dir root and recursively from subdirectories
473
- copy_recursive(src_dir, build_dir)
474
-
475
- def _find_vite_bundle(self, output_dir: Path) -> Path | None:
476
- """Find the generated Vite bundle file."""
477
- for file in output_dir.glob("client.*.js"):
478
- return file
479
- return None
480
-
481
- def _find_vite_css(self, output_dir: Path) -> Path | None:
482
- """Find the generated Vite CSS file."""
483
- # Vite typically outputs CSS as main.css or with a hash
484
- # Try main.css first (most common), then any .css file
485
- css_file = output_dir / "main.css"
486
- if css_file.exists():
487
- return css_file
488
- # Fallback: find any CSS file
489
- for file in output_dir.glob("*.css"):
490
- return file
491
- return None
492
-
493
- def cleanup_temp_dir(self) -> None:
494
- """Clean up the src directory and its contents."""
495
- if not self.vite_package_json or not self.vite_package_json.exists():
496
- return
497
-
498
- project_dir = self.vite_package_json.parent
499
- temp_dir = project_dir / "src"
500
-
501
- if temp_dir.exists():
502
- with contextlib.suppress(OSError, shutil.Error):
503
- shutil.rmtree(temp_dir)
@@ -1,23 +0,0 @@
1
- """Sample Jac module containing client-side declarations."""
2
-
3
- cl let API_LABEL: str = "Runtime Test";
4
-
5
- cl obj ButtonProps {
6
- has label: str = "Tap Me";
7
- has color: str = "primary";
8
- }
9
-
10
- cl def app() {
11
- let props = ButtonProps(label="Tap Me", color="primary");
12
- return <div class="app">
13
- <h1>
14
- {API_LABEL}
15
- </h1>
16
- <button
17
- class={props.color}
18
- data-id="button"
19
- >
20
- {props.label}
21
- </button>
22
- </div>;
23
- }
@@ -1,48 +0,0 @@
1
- import from react { useState }
2
-
3
- def app() -> any {
4
- let [todos, setTodos] = useState([]);
5
- let [input, setInput] = useState("");
6
-
7
- # Event Handler
8
- async def addTodo() -> None {
9
- if not input.trim() {
10
- return;
11
- }
12
- response = root spawn create_todo(text=input.trim());
13
- new_todo = response.reports[0][0];
14
- setTodos(todos.concat([new_todo]));
15
- setInput("");
16
-
17
- def foo { }
18
- }
19
-
20
- return <div>
21
- <h2>
22
- My Todos
23
- </h2>
24
- <input
25
- value={input}
26
- onChange={lambda e: any -> None{ setInput(e.target.value);} }
27
- onKeyPress={lambda e: any -> None{ if e.key == "Enter" {
28
- addTodo();
29
- }} }
30
- />
31
- <button
32
- onClick={addTodo}
33
- >
34
- Add Todo
35
- </button>
36
- <div>
37
- {todos.map(
38
- lambda todo: any -> any{ return <div
39
- key={todo._jac_id}
40
- >
41
- <span>
42
- {todo.text}
43
- </span>
44
- </div>; }
45
- )}
46
- </div>
47
- </div>;
48
- }
@@ -1,15 +0,0 @@
1
- '''Test file for .cl file serves the client module.'''
2
-
3
- node Todo {
4
- has text: str;
5
- has done: bool = False;
6
- }
7
-
8
- walker create_todo {
9
- has text: str;
10
-
11
- can create with `root entry {
12
- new_todo = here ++> Todo(text=self.text);
13
- report new_todo ;
14
- }
15
- }
@@ -1,34 +0,0 @@
1
- """Sample Jac module using Ant Design components."""
2
-
3
- cl import from antd { Button }
4
-
5
- cl let APP_NAME: str = "Ant Design Test";
6
-
7
- cl def ButtonTest() {
8
- return <div>
9
- <h1>
10
- {APP_NAME}
11
- </h1>
12
- <p>
13
- Testing Ant Design integration
14
- </p>
15
- <Button>
16
- Click Me
17
- </Button>
18
- </div>;
19
- }
20
-
21
- cl def CardTest() {
22
- return <div class="card-wrapper">
23
- <h2>
24
- Card Component
25
- </h2>
26
- </div>;
27
- }
28
-
29
- cl def app() {
30
- return <div>
31
- <ButtonTest />
32
- <CardTest />
33
- </div>;
34
- }
@@ -1,34 +0,0 @@
1
- """Test module that imports JavaScript functions."""
2
-
3
- cl import from .utils { formatMessage, calculateSum, JS_CONSTANT, MessageFormatter }
4
-
5
- cl let JS_IMPORT_LABEL: str = "JavaScript Import Test";
6
-
7
- cl def JsImportTest() -> any {
8
- let greeting = formatMessage("Jac");
9
- let sum = calculateSum(5, 3);
10
- let formatter = MessageFormatter("JS");
11
- let formatted = formatter.format("Hello from JS class");
12
-
13
- return <div class="js-import-test">
14
- <h1>
15
- {JS_IMPORT_LABEL}
16
- </h1>
17
- <p>
18
- Greeting: {greeting}
19
- </p>
20
- <p>
21
- Sum (5 + 3): {sum}
22
- </p>
23
- <p>
24
- Constant: {JS_CONSTANT}
25
- </p>
26
- <p>
27
- Formatted: {formatted}
28
- </p>
29
- </div>;
30
- }
31
-
32
- cl def app() -> any {
33
- return <JsImportTest />;
34
- }
@@ -1,21 +0,0 @@
1
- // JavaScript utility functions for testing JS imports in Jac
2
-
3
- export function formatMessage(name) {
4
- return `Hello, ${name}!`;
5
- }
6
-
7
- export function calculateSum(a, b) {
8
- return a + b;
9
- }
10
-
11
- export const JS_CONSTANT = "Imported from JavaScript";
12
-
13
- export class MessageFormatter {
14
- constructor(prefix) {
15
- this.prefix = prefix;
16
- }
17
-
18
- format(text) {
19
- return `${this.prefix}: ${text}`;
20
- }
21
- }