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,33 @@
1
+ """Asset file processing and copying utilities."""
2
+ import contextlib;
3
+ import shutil;
4
+ import from pathlib { Path }
5
+ import from jaclang.project.config { get_config }
6
+
7
+ """Handles copying of asset files (CSS, images, fonts, etc.) between directories."""
8
+ class AssetProcessor {
9
+ with entry {
10
+ ASSET_EXTENSIONS = {'.css','.scss','.sass','.less','.svg','.png','.jpg','.jpeg','.gif','.webp','.ico','.woff','.woff2','.ttf','.eot','.otf','.mp4','.webm','.mp3','.wav'};
11
+ }
12
+
13
+ def copy_assets(
14
+ self: AssetProcessor,
15
+ src_dir: Path,
16
+ dest_dir: Path,
17
+ preserve_structure: bool = True
18
+ ) -> None;
19
+
20
+ def copy_typescript_files(
21
+ self: AssetProcessor,
22
+ src_dir: Path,
23
+ dest_dir: Path,
24
+ preserve_structure: bool = True
25
+ ) -> None;
26
+
27
+ def copy_custom_asset_types(
28
+ self: AssetProcessor,
29
+ src_dir: Path,
30
+ dest_dir: Path,
31
+ preserve_structure: bool = True
32
+ ) -> None;
33
+ }
@@ -0,0 +1,18 @@
1
+ """Babel processing for JavaScript transpilation."""
2
+ import subprocess;
3
+ import from pathlib { Path }
4
+ import from jaclang.runtimelib.client_bundle { ClientBundleError }
5
+ import from .asset_processor { AssetProcessor }
6
+ import from .vite_bundler { ViteBundler }
7
+
8
+ """Handles Babel compilation of JavaScript files."""
9
+ class BabelProcessor {
10
+ def init(self: BabelProcessor, project_dir: Path);
11
+ def compile(self: BabelProcessor) -> None;
12
+ def copy_assets_after_compile(
13
+ self: BabelProcessor,
14
+ compiled_dir: Path,
15
+ build_dir: Path,
16
+ asset_processor: AssetProcessor
17
+ ) -> None;
18
+ }
@@ -0,0 +1,67 @@
1
+ """Main compilation orchestration module."""
2
+ import contextlib;
3
+ import from collections.abc { Callable }
4
+ import from pathlib { Path }
5
+ import from types { ModuleType }
6
+ import from typing { TYPE_CHECKING, Any }
7
+ import from jaclang.runtimelib.client_bundle { ClientBundleError }
8
+ import from .asset_processor { AssetProcessor }
9
+ import from .babel_processor { BabelProcessor }
10
+ import from .import_processor { ImportProcessor }
11
+ import from .jac_to_js { JacToJSCompiler }
12
+ import from .vite_bundler { ViteBundler }
13
+
14
+ with entry {
15
+ if TYPE_CHECKING { }
16
+ }
17
+
18
+ """Orchestrates the compilation process for Vite client bundles."""
19
+ class ViteCompiler {
20
+ with entry {
21
+ ROUTER_EXPORTS = [
22
+ 'Router',
23
+ 'Routes',
24
+ 'Route',
25
+ 'Link',
26
+ 'Navigate',
27
+ 'useNavigate',
28
+ 'useLocation',
29
+ 'useParams'
30
+ ];
31
+ }
32
+
33
+ def init(
34
+ self: ViteCompiler,
35
+ vite_package_json: Path,
36
+ vite_output_dir: (Path | None) = None,
37
+ vite_minify: bool = False,
38
+ runtime_path: (Path | None) = None,
39
+ compile_to_js_func:
40
+ (Callable[([Path], tuple[(str, (ModuleType | None))])] | None) = None,
41
+ extract_exports_func: (Callable[([Any], list[str])] | None) = None,
42
+ extract_globals_func: (Callable[([Any, ModuleType], dict[(str, Any)])] | None) = None
43
+ );
44
+
45
+ def compile_runtime_utils(self: ViteCompiler) -> tuple[str, list[str]];
46
+ def compile_dependencies_recursively(
47
+ self: ViteCompiler,
48
+ module_path: Path,
49
+ visited: (set[Path] | None) = None,
50
+ collected_exports: (set[str] | None) = None,
51
+ collected_globals: (dict[(str, Any)] | None) = None,
52
+ source_root: (Path | None) = None
53
+ ) -> None;
54
+
55
+ def _get_client_dir(self: ViteCompiler) -> Path;
56
+ def _copy_js_file(self: ViteCompiler, js_path: Path, source_root: Path) -> None;
57
+ def _copy_ts_file(self: ViteCompiler, ts_path: Path, source_root: Path) -> None;
58
+ def _copy_asset_file(
59
+ self: ViteCompiler, asset_path: Path, source_root: Path
60
+ ) -> None;
61
+
62
+ def copy_root_assets(self: ViteCompiler) -> None;
63
+ def create_entry_file(self: ViteCompiler, module_path: Path) -> None;
64
+ def compile_and_bundle(
65
+ self: ViteCompiler, module: ModuleType, module_path: Path
66
+ ) -> tuple[str, str, list[str], list[str]];
67
+ }
@@ -0,0 +1,32 @@
1
+ """Configuration loader for Jac Client build system.
2
+
3
+ This module provides configuration access for the client plugin.
4
+ Configuration is loaded from jac.toml under [plugins.client] section.
5
+
6
+ Inherits from PluginConfigBase to eliminate boilerplate code.
7
+ """
8
+ import from pathlib { Path }
9
+ import from typing { Any }
10
+ import from jaclang.project.plugin_config { PluginConfigBase }
11
+
12
+ """Client-specific configuration loader.
13
+
14
+ Provides access to vite, ts, and package configuration
15
+ from the [plugins.client] section of jac.toml.
16
+ """
17
+ class JacClientConfig(PluginConfigBase) {
18
+ override def get_plugin_name(self: JacClientConfig) -> str;
19
+ override def get_default_config(self: JacClientConfig) -> dict[str, Any];
20
+ override def load(self: JacClientConfig) -> dict[str, Any];
21
+ def save(self: JacClientConfig) -> None;
22
+ def get_vite_config(self: JacClientConfig) -> dict[str, Any];
23
+ def get_ts_config(self: JacClientConfig) -> dict[str, Any];
24
+ def get_package_config(self: JacClientConfig) -> dict[str, Any];
25
+ def add_dependency(
26
+ self: JacClientConfig, name: str, version: str, is_dev: bool = False
27
+ ) -> None;
28
+
29
+ def remove_dependency(
30
+ self: JacClientConfig, name: str, is_dev: bool = False
31
+ ) -> bool;
32
+ }
@@ -0,0 +1,127 @@
1
+ """Copy custom asset files (based on jac.toml configuration) from source to destination recursively."""
2
+
3
+ impl AssetProcessor.copy_custom_asset_types(
4
+ self: AssetProcessor,
5
+ src_dir: Path,
6
+ dest_dir: Path,
7
+ preserve_structure: bool = True
8
+ ) -> None {
9
+ if not src_dir.exists() {
10
+ return;
11
+ }
12
+ dest_dir.mkdir(parents=True, exist_ok=True);
13
+ def copy_recursive(
14
+ source: Path, destination: Path, base: (Path | None) = None
15
+ ) -> None {
16
+ if not source.exists() {
17
+ return;
18
+ }
19
+ if (base is None) {
20
+ base = source;
21
+ }
22
+ for item in source.iterdir() {
23
+ if (item.is_file() and (item.suffix.lower() in custom_asset_extensions)) {
24
+ if preserve_structure {
25
+ relative_path = item.relative_to(base);
26
+ dest_file = destination / relative_path;
27
+ } else {
28
+ dest_file = destination / item.name;
29
+ }
30
+ dest_file.parent.mkdir(parents=True, exist_ok=True);
31
+ with contextlib.suppress(OSError, shutil.Error) {
32
+ shutil.copy2(item, dest_file);
33
+ }
34
+ } elif item.is_dir() {
35
+ copy_recursive(item, destination, base);
36
+ }
37
+ }
38
+ }
39
+ config = get_config();
40
+ assets_config = config.get_plugin_config("client").get("assets", {});
41
+ custom_asset_extensions = set(assets_config.get("custom_extensions", []));
42
+ if custom_asset_extensions {
43
+ copy_recursive(src_dir, dest_dir);
44
+ }
45
+ return;
46
+ }
47
+
48
+ """Copy TypeScript files (.ts, .tsx) from source to destination recursively."""
49
+ impl AssetProcessor.copy_typescript_files(
50
+ self: AssetProcessor,
51
+ src_dir: Path,
52
+ dest_dir: Path,
53
+ preserve_structure: bool = True
54
+ ) -> None {
55
+ if not src_dir.exists() {
56
+ return;
57
+ }
58
+ dest_dir.mkdir(parents=True, exist_ok=True);
59
+ ts_extensions = {'.ts','.tsx'};
60
+ def copy_recursive(
61
+ source: Path, destination: Path, base: (Path | None) = None
62
+ ) -> None {
63
+ if not source.exists() {
64
+ return;
65
+ }
66
+ if (base is None) {
67
+ base = source;
68
+ }
69
+ for item in source.iterdir() {
70
+ if (item.is_file() and (item.suffix.lower() in ts_extensions)) {
71
+ if preserve_structure {
72
+ relative_path = item.relative_to(base);
73
+ dest_file = destination / relative_path;
74
+ } else {
75
+ dest_file = destination / item.name;
76
+ }
77
+ dest_file.parent.mkdir(parents=True, exist_ok=True);
78
+ with contextlib.suppress(OSError, shutil.Error) {
79
+ shutil.copy2(item, dest_file);
80
+ }
81
+ } elif item.is_dir() {
82
+ copy_recursive(item, destination, base);
83
+ }
84
+ }
85
+ }
86
+ copy_recursive(src_dir, dest_dir);
87
+ }
88
+
89
+ """Copy CSS and other asset files from source to destination recursively."""
90
+ impl AssetProcessor.copy_assets(
91
+ self: AssetProcessor,
92
+ src_dir: Path,
93
+ dest_dir: Path,
94
+ preserve_structure: bool = True
95
+ ) -> None {
96
+ if not src_dir.exists() {
97
+ return;
98
+ }
99
+ dest_dir.mkdir(parents=True, exist_ok=True);
100
+ def copy_recursive(
101
+ source: Path, destination: Path, base: (Path | None) = None
102
+ ) -> None {
103
+ if not source.exists() {
104
+ return;
105
+ }
106
+ if (base is None) {
107
+ base = source;
108
+ }
109
+ for item in source.iterdir() {
110
+ if (item.is_file() and (item.suffix.lower() in self.ASSET_EXTENSIONS)) {
111
+ if preserve_structure {
112
+ relative_path = item.relative_to(base);
113
+ dest_file = destination / relative_path;
114
+ } else {
115
+ dest_file = destination / item.name;
116
+ }
117
+ dest_file.parent.mkdir(parents=True, exist_ok=True);
118
+ with contextlib.suppress(OSError, shutil.Error) {
119
+ shutil.copy2(item, dest_file);
120
+ }
121
+ } elif item.is_dir() {
122
+ copy_recursive(item, destination, base);
123
+ }
124
+ }
125
+ }
126
+ copy_recursive(src_dir, dest_dir);
127
+ }
@@ -0,0 +1,89 @@
1
+ """Copy CSS, assets, and TypeScript files from compiled/ to build/ after Babel compilation."""
2
+
3
+ impl BabelProcessor.copy_assets_after_compile(
4
+ self: BabelProcessor,
5
+ compiled_dir: Path,
6
+ build_dir: Path,
7
+ asset_processor: AssetProcessor
8
+ ) -> None {
9
+ asset_processor.copy_assets(compiled_dir, build_dir);
10
+ asset_processor.copy_typescript_files(compiled_dir, build_dir);
11
+ }
12
+
13
+ """Run Babel compilation (npm run compile)."""
14
+ impl BabelProcessor.compile(self: BabelProcessor) -> None {
15
+ bundler = ViteBundler(self.project_dir);
16
+ # Ensure root package.json exists temporarily for npm commands
17
+ bundler._ensure_root_package_json();
18
+ try {
19
+ # Ensure dependencies are installed (check if node_modules exists)
20
+ client_dir = bundler._get_client_dir();
21
+ node_modules = client_dir / 'node_modules';
22
+ if not node_modules.exists() {
23
+ # Temporarily copy package.json to .jac/client/ for npm install
24
+ build_package_json = client_dir / 'package.json';
25
+ configs_package_json = client_dir / 'configs' / 'package.json';
26
+ if configs_package_json.exists() and not build_package_json.exists() {
27
+ import shutil;
28
+ shutil.copy2(configs_package_json, build_package_json);
29
+ }
30
+ try {
31
+ # Install to .jac/client/node_modules
32
+ subprocess.run(
33
+ ['npm', 'install'],
34
+ cwd=client_dir,
35
+ check=True,
36
+ capture_output=True,
37
+ text=True
38
+ );
39
+ } except subprocess.CalledProcessError as e {
40
+ raise ClientBundleError(
41
+ f'Failed to install npm dependencies: {e.stderr}'
42
+ ) from e ;
43
+ } except FileNotFoundError {
44
+ raise ClientBundleError(
45
+ 'npm command not found. Ensure Node.js and npm are installed.'
46
+ ) from None ;
47
+ }
48
+ }
49
+
50
+ # Temporarily copy package.json to .jac/client/ for npm run compile
51
+ build_package_json = client_dir / 'package.json';
52
+ configs_package_json = client_dir / 'configs' / 'package.json';
53
+ if configs_package_json.exists() and not build_package_json.exists() {
54
+ import shutil;
55
+ shutil.copy2(configs_package_json, build_package_json);
56
+ }
57
+ command = ['npm', 'run', 'compile'];
58
+ result = subprocess.run(
59
+ command, cwd=client_dir, capture_output=True, text=True
60
+ );
61
+ if result.returncode != 0 {
62
+ # Show the actual error from npm/babel
63
+ error_output = result.stderr or result.stdout or "Unknown error";
64
+ raise RuntimeError(f"Client bundle compilation failed:\n{error_output}") ;
65
+ }
66
+ } finally {
67
+ # Clean up temporary package.json in .jac/client/
68
+ build_package_json = client_dir / 'package.json';
69
+ if build_package_json.exists() {
70
+ build_package_json.unlink();
71
+ }
72
+ # Move package-lock.json to configs/ if it exists
73
+ build_package_lock = client_dir / 'package-lock.json';
74
+ if build_package_lock.exists() {
75
+ configs_package_lock = client_dir / 'configs' / 'package-lock.json';
76
+ if configs_package_lock.exists() {
77
+ configs_package_lock.unlink();
78
+ }
79
+ build_package_lock.rename(configs_package_lock);
80
+ }
81
+ # Always clean up root package.json and move package-lock.json
82
+ bundler._cleanup_root_package_files();
83
+ }
84
+ }
85
+
86
+ """Initialize the Babel processor."""
87
+ impl BabelProcessor.init(self: BabelProcessor, project_dir: Path) {
88
+ self.project_dir = project_dir;
89
+ }
@@ -0,0 +1,288 @@
1
+ """Get the client build directory from project config."""
2
+ impl ViteCompiler._get_client_dir(self: ViteCompiler) -> Path {
3
+ # Try to get from project config
4
+ try {
5
+ import from jaclang.project.config { get_config }
6
+ config = get_config();
7
+ if config is not None {
8
+ return config.get_client_dir();
9
+ }
10
+ } except ImportError { }
11
+ # Fallback to default
12
+ return self.project_dir / '.jac' / 'client';
13
+ }
14
+
15
+ """Compile module and dependencies, then bundle with Vite."""
16
+ impl ViteCompiler.compile_and_bundle(
17
+ self: ViteCompiler, module: ModuleType, module_path: Path
18
+ ) -> tuple[str, str, list[str], list[str]] {
19
+ self.compile_runtime_utils();
20
+ (module_js, mod, module_manifest) = self.jac_compiler.compile_module(module_path);
21
+ collected_exports: set[str] = set(
22
+ self.jac_compiler.extract_exports(module_manifest)
23
+ );
24
+ client_globals_map = self.jac_compiler.extract_globals(module_manifest, module);
25
+ collected_globals: dict[(str, Any)] = dict(client_globals_map);
26
+ self.compile_dependencies_recursively(
27
+ module_path,
28
+ collected_exports=collected_exports,
29
+ collected_globals=collected_globals
30
+ );
31
+ self.copy_root_assets();
32
+ self.create_entry_file(module_path);
33
+ self.babel_processor.compile();
34
+ client_dir = self._get_client_dir();
35
+ self.babel_processor.copy_assets_after_compile(
36
+ self.compiled_dir, (client_dir / 'build'), self.asset_processor
37
+ );
38
+ entry_file = client_dir / 'build' / '_entry.js';
39
+ self.vite_bundler.build(entry_file=entry_file);
40
+ (bundle_code, bundle_hash) = self.vite_bundler.read_bundle();
41
+ client_exports = sorted(collected_exports);
42
+ client_globals = list(collected_globals.keys());
43
+ return (bundle_code, bundle_hash, client_exports, client_globals);
44
+ }
45
+
46
+ """Create the main entry file for Vite bundling."""
47
+ impl ViteCompiler.create_entry_file(self: ViteCompiler, module_path: Path) -> None {
48
+ # Use _entry.js to avoid conflict with compiled modules that may be named main.js
49
+ entry_file = self.compiled_dir / '_entry.js';
50
+ # Derive the app module filename from the entry point (e.g., main.jac -> main.js, app.jac -> app.js)
51
+ app_module_name = module_path.stem;
52
+ entry_content = f'import React from "react";\nimport {{ createRoot }} from "react-dom/client";\nimport {{ app as App }} from "./{app_module_name}.js";\n\nconst root = createRoot(document.getElementById("root"));\nroot.render(React.createElement(App, null));\n';
53
+ entry_file.write_text(entry_content, encoding='utf-8');
54
+ }
55
+
56
+ """Copy assets from root assets/ folder to compiled/assets/ for @jac-client/assets alias."""
57
+ impl ViteCompiler.copy_root_assets(self: ViteCompiler) -> None {
58
+ root_assets_dir = self.project_dir / 'assets';
59
+ compiled_assets_dir = self.compiled_dir / 'assets';
60
+ if (root_assets_dir.exists() and root_assets_dir.is_dir()) {
61
+ self.asset_processor.copy_assets(root_assets_dir, compiled_assets_dir);
62
+ }
63
+ # Copy configured_asset files from root assets/ to build assets/ (bypassing compiled)
64
+ build_assets_dir = self._get_client_dir() / 'build' / 'assets';
65
+ if (root_assets_dir.exists() and root_assets_dir.is_dir()) {
66
+ self.asset_processor.copy_custom_asset_types(root_assets_dir, build_assets_dir);
67
+ }
68
+ }
69
+
70
+ """Copy an asset file to the compiled directory."""
71
+ impl ViteCompiler._copy_asset_file(
72
+ self: ViteCompiler, asset_path: Path, source_root: Path
73
+ ) -> None {
74
+ if not asset_path.exists() {
75
+ return;
76
+ }
77
+ try {
78
+ relative_path = asset_path.relative_to(source_root);
79
+ output_path = self.compiled_dir / relative_path;
80
+ } except ValueError {
81
+ output_path = self.compiled_dir / asset_path.name;
82
+ }
83
+ output_path.parent.mkdir(parents=True, exist_ok=True);
84
+ with contextlib.suppress(FileNotFoundError, OSError) {
85
+ output_path.write_text(
86
+ asset_path.read_text(encoding='utf-8'), encoding='utf-8'
87
+ );
88
+ }
89
+ }
90
+
91
+ """Copy a TypeScript file to the compiled directory."""
92
+ impl ViteCompiler._copy_ts_file(
93
+ self: ViteCompiler, ts_path: Path, source_root: Path
94
+ ) -> None {
95
+ if not ts_path.exists() {
96
+ return;
97
+ }
98
+ try {
99
+ ts_code = ts_path.read_text(encoding='utf-8');
100
+ try {
101
+ relative_path = ts_path.relative_to(source_root);
102
+ output_path = self.compiled_dir / relative_path;
103
+ } except ValueError {
104
+ output_path = self.compiled_dir / ts_path.name;
105
+ }
106
+ output_path.parent.mkdir(parents=True, exist_ok=True);
107
+ output_path.write_text(ts_code, encoding='utf-8');
108
+ } except (FileNotFoundError, OSError) { }
109
+ }
110
+
111
+ """Copy a JavaScript file to the compiled directory."""
112
+ impl ViteCompiler._copy_js_file(
113
+ self: ViteCompiler, js_path: Path, source_root: Path
114
+ ) -> None {
115
+ try {
116
+ js_code = js_path.read_text(encoding='utf-8');
117
+ try {
118
+ relative_path = js_path.relative_to(source_root);
119
+ output_path = self.compiled_dir / relative_path;
120
+ } except ValueError {
121
+ output_path = self.compiled_dir / js_path.name;
122
+ }
123
+ output_path.parent.mkdir(parents=True, exist_ok=True);
124
+ output_path.write_text(js_code, encoding='utf-8');
125
+ } except FileNotFoundError { }
126
+ }
127
+
128
+ """Recursively compile/copy .jac/.js imports to temp, skipping bundling."""
129
+ impl ViteCompiler.compile_dependencies_recursively(
130
+ self: ViteCompiler,
131
+ module_path: Path,
132
+ visited: (set[Path] | None) = None,
133
+ collected_exports: (set[str] | None) = None,
134
+ collected_globals: (dict[(str, Any)] | None) = None,
135
+ source_root: (Path | None) = None
136
+ ) -> None {
137
+ if (visited is None) {
138
+ visited = set();
139
+ }
140
+ if (collected_exports is None) {
141
+ collected_exports = set();
142
+ }
143
+ if (collected_globals is None) {
144
+ collected_globals = {};
145
+ }
146
+ module_path = module_path.resolve();
147
+ if (module_path in visited) {
148
+ return;
149
+ }
150
+ visited.add(module_path);
151
+ if (source_root is None) {
152
+ source_root = module_path.parent.resolve();
153
+ }
154
+ (module_js, mod, manifest) = self.jac_compiler.compile_module(module_path);
155
+ exports_list = self.jac_compiler.extract_exports(manifest);
156
+ collected_exports.update(exports_list);
157
+ non_root_globals: dict[(str, Any)] = {};
158
+ if manifest {
159
+ for name in manifest.globals {
160
+ non_root_globals[name] = manifest.globals_values.get(name);
161
+ }
162
+ }
163
+ collected_globals.update(non_root_globals);
164
+ combined_js = self.jac_compiler.add_runtime_imports(module_js);
165
+ try {
166
+ relative_path = module_path.relative_to(source_root);
167
+ # Handle compound extensions like .cl.jac, .impl.jac -> .js
168
+ rel_str = str(relative_path);
169
+ for compound_ext in ['.cl.jac', '.impl.jac', '.test.jac'] {
170
+ if rel_str.endswith(compound_ext) {
171
+ rel_str = rel_str[:-len(compound_ext)] + '.js';
172
+ break;
173
+ }
174
+ } else {
175
+ rel_str = str(relative_path.with_suffix('.js'));
176
+ }
177
+ output_path = self.compiled_dir / rel_str;
178
+ } except ValueError {
179
+ # Handle compound extensions in filename
180
+ name = module_path.name;
181
+ for compound_ext in ['.cl.jac', '.impl.jac', '.test.jac'] {
182
+ if name.endswith(compound_ext) {
183
+ name = name[:-len(compound_ext)] + '.js';
184
+ break;
185
+ }
186
+ } else {
187
+ name = module_path.stem + '.js';
188
+ }
189
+ output_path = self.compiled_dir / name;
190
+ }
191
+ output_path.parent.mkdir(parents=True, exist_ok=True);
192
+ output_path.write_text(combined_js, encoding='utf-8');
193
+ if (not manifest or not manifest.imports) {
194
+ return;
195
+ }
196
+ for (_name, import_path) in manifest.imports.items() {
197
+ path_obj = Path(import_path).resolve();
198
+ if (path_obj in visited) {
199
+ continue;
200
+ }
201
+ if (path_obj.suffix == '.jac') {
202
+ self.compile_dependencies_recursively(
203
+ path_obj,
204
+ visited,
205
+ collected_exports=collected_exports,
206
+ collected_globals=collected_globals,
207
+ source_root=source_root
208
+ );
209
+ } elif (path_obj.suffix == '.js') {
210
+ self._copy_js_file(path_obj, source_root);
211
+ } elif (path_obj.suffix in {'.ts','.tsx'}) {
212
+ self._copy_ts_file(path_obj, source_root);
213
+ } elif path_obj.is_file() {
214
+ self._copy_asset_file(path_obj, source_root);
215
+ }
216
+ }
217
+ }
218
+
219
+ """Compile client runtime utilities."""
220
+ impl ViteCompiler.compile_runtime_utils(self: ViteCompiler) -> tuple[str, list[str]] {
221
+ if not self.runtime_path {
222
+ raise ClientBundleError('Runtime path not set') ;
223
+ }
224
+ runtime_utils_path = self.runtime_path.parent / 'client_runtime.cl.jac';
225
+ (runtimeutils_js, mod, runtimeutils_manifest) = self.jac_compiler.compile_module(
226
+ runtime_utils_path
227
+ );
228
+ runtimeutils_exports_list = self.jac_compiler.extract_exports(
229
+ runtimeutils_manifest
230
+ );
231
+ all_exports = sorted(set((runtimeutils_exports_list + self.ROUTER_EXPORTS)));
232
+ combined_runtime_utils_js = runtimeutils_js;
233
+ self.compiled_dir.mkdir(parents=True, exist_ok=True);
234
+ (self.compiled_dir / 'client_runtime.js').write_text(
235
+ combined_runtime_utils_js, encoding='utf-8'
236
+ );
237
+ return (combined_runtime_utils_js, all_exports);
238
+ }
239
+
240
+ """Initialize the Vite compiler."""
241
+ impl ViteCompiler.init(
242
+ self: ViteCompiler,
243
+ vite_package_json: Path,
244
+ vite_output_dir: (Path | None) = None,
245
+ vite_minify: bool = False,
246
+ runtime_path: (Path | None) = None,
247
+ compile_to_js_func: (Callable[([Path], tuple[(str, (ModuleType | None))])] | None) = None,
248
+ extract_exports_func: (Callable[([Any], list[str])] | None) = None,
249
+ extract_globals_func: (Callable[([Any, ModuleType], dict[(str, Any)])] | None) = None
250
+ ) {
251
+ if (not vite_package_json or not vite_package_json.exists()) {
252
+ raise ClientBundleError(
253
+ 'Vite package.json not found. Set vite_package_json when using ViteCompiler'
254
+ ) ;
255
+ }
256
+ if (
257
+ (compile_to_js_func is None)
258
+ or (extract_exports_func is None)
259
+ or (extract_globals_func is None)
260
+ ) {
261
+ raise ClientBundleError(
262
+ 'compile_to_js_func, extract_exports_func, and extract_globals_func are required'
263
+ ) ;
264
+ }
265
+ self.vite_package_json = vite_package_json;
266
+ # Detect project root: package.json may be in .jac/client/configs/ or project root
267
+ if (
268
+ vite_package_json.parent.name == 'configs'
269
+ and vite_package_json.parent.parent.name == 'client'
270
+ and vite_package_json.parent.parent.parent.name == '.jac'
271
+ ) {
272
+ # .jac/client/configs/package.json -> go up 3 levels to project root
273
+ self.project_dir = vite_package_json.parent.parent.parent.parent;
274
+ } elif (vite_package_json.parent.name == 'configs') {
275
+ self.project_dir = vite_package_json.parent.parent;
276
+ } else {
277
+ self.project_dir = vite_package_json.parent;
278
+ }
279
+ self.runtime_path = runtime_path;
280
+ self.compiled_dir = self._get_client_dir() / 'compiled';
281
+ self.jac_compiler = JacToJSCompiler(
282
+ compile_to_js_func, extract_exports_func, extract_globals_func
283
+ );
284
+ self.import_processor = ImportProcessor();
285
+ self.asset_processor = AssetProcessor();
286
+ self.babel_processor = BabelProcessor(self.project_dir);
287
+ self.vite_bundler = ViteBundler(self.project_dir, vite_output_dir, vite_minify);
288
+ }