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,626 @@
1
+ """Get the client build directory from project config."""
2
+ impl ViteBundler._get_client_dir(self: ViteBundler) -> 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
+ """Create package.json from config.json during bundling."""
16
+ impl ViteBundler.create_package_json(
17
+ self: ViteBundler, project_name: Optional[str] = None
18
+ ) -> Path {
19
+ build_dir = self._get_client_dir();
20
+ build_dir.mkdir(parents=True, exist_ok=True);
21
+ configs_dir = build_dir / 'configs';
22
+ configs_dir.mkdir(exist_ok=True);
23
+ package_config = self.config_loader.get_package_config();
24
+ package_json_path = configs_dir / 'package.json';
25
+ name = package_config.get('name', '');
26
+ if (not name and project_name) {
27
+ name = project_name;
28
+ }
29
+ if not name {
30
+ existing_package = self.project_dir / 'package.json';
31
+ if existing_package.exists() {
32
+ try {
33
+ with existing_package.open() as f {
34
+ existing_data = json.load(f);
35
+ name = existing_data.get('name', '');
36
+ }
37
+ } except (json.JSONDecodeError, KeyError) {
38
+ ;
39
+ }
40
+ }
41
+ }
42
+ if not name {
43
+ name = self.project_dir.name or 'jac-app';
44
+ }
45
+ dependencies = package_config.get('dependencies', {});
46
+ dev_dependencies = package_config.get('devDependencies', {});
47
+ scripts = {
48
+ 'build': 'npm run compile && vite build --config .jac/client/configs/vite.config.js',
49
+ 'dev': 'vite dev --config .jac/client/configs/vite.config.js',
50
+ 'preview': 'vite preview --config .jac/client/configs/vite.config.js',
51
+ 'compile': 'babel compiled --out-dir build --extensions ".jsx,.js" --out-file-extension .js'
52
+ };
53
+ user_scripts = package_config.get('scripts', {});
54
+ scripts.update(user_scripts);
55
+ babel_config = {
56
+ 'presets': [['@babel/preset-env', {'modules': False}], '@babel/preset-react']
57
+ };
58
+ package_data = {
59
+ 'name': name,
60
+ 'version': package_config.get('version', '1.0.0'),
61
+ 'description': package_config.get('description', f"Jac application: {name}"),
62
+ 'type': 'module',
63
+ 'main': 'index.js',
64
+ 'scripts': scripts,
65
+ 'dependencies': dependencies,
66
+ 'devDependencies': dev_dependencies,
67
+ 'babel': babel_config
68
+ };
69
+ for (key, value) in package_config.items() {
70
+ if (
71
+ key not in {'name','version','description','dependencies','devDependencies'}
72
+ ) {
73
+ package_data[key] = value;
74
+ }
75
+ }
76
+ with package_json_path.open('w', encoding='utf-8') as f {
77
+ json.dump(package_data, f, indent=2);
78
+ }
79
+ # Generate tsconfig.json during build time
80
+ self.create_tsconfig();
81
+ return package_json_path;
82
+ }
83
+
84
+ """Create tsconfig.json during build time, merging user config from jac.toml."""
85
+ impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
86
+ build_dir = self._get_client_dir();
87
+ build_dir.mkdir(parents=True, exist_ok=True);
88
+ configs_dir = build_dir / 'configs';
89
+ configs_dir.mkdir(exist_ok=True);
90
+ tsconfig_path = configs_dir / 'tsconfig.json';
91
+ # Default tsconfig settings
92
+ default_compiler_options = {
93
+ 'target': 'ES2020',
94
+ 'useDefineForClassFields': True,
95
+ 'lib': ['ES2020', 'DOM', 'DOM.Iterable'],
96
+ 'module': 'ESNext',
97
+ 'skipLibCheck': True,
98
+ 'moduleResolution': 'bundler',
99
+ 'allowImportingTsExtensions': True,
100
+ 'resolveJsonModule': True,
101
+ 'isolatedModules': True,
102
+ 'noEmit': True,
103
+ 'jsx': 'react-jsx',
104
+ 'strict': True,
105
+ 'noUnusedLocals': True,
106
+ 'noUnusedParameters': True,
107
+ 'noFallthroughCasesInSwitch': True
108
+ };
109
+ default_include = ['components/**/*'];
110
+ default_exclude = ['.jac'];
111
+ # Get user config from [plugins.client.ts] in jac.toml
112
+ user_ts_config = self.config_loader.get_ts_config();
113
+ user_compiler_options = user_ts_config.get('compilerOptions', {});
114
+ user_include = user_ts_config.get('include', []);
115
+ user_exclude = user_ts_config.get('exclude', []);
116
+ # Merge compiler options (user overrides defaults)
117
+ merged_compiler_options = {** default_compiler_options, ** user_compiler_options};
118
+ # For include/exclude, use user values if provided, otherwise defaults
119
+ merged_include = user_include or default_include;
120
+ merged_exclude = user_exclude or default_exclude;
121
+ tsconfig_data = {
122
+ 'compilerOptions': merged_compiler_options,
123
+ 'include': merged_include,
124
+ 'exclude': merged_exclude
125
+ };
126
+ # Write the config to .jac/client/configs/tsconfig.json (no root config file)
127
+ tsconfig_path.write_text(json.dumps(tsconfig_data, indent=2), encoding='utf-8');
128
+ return tsconfig_path;
129
+ }
130
+
131
+ """
132
+ Clean up root package.json and move package-lock.json to configs/.
133
+
134
+ Remove root package.json and move package-lock.json to configs/.
135
+ """
136
+ impl ViteBundler._cleanup_root_package_files(self: ViteBundler) -> None {
137
+ root_package_json = self.project_dir / 'package.json';
138
+ root_package_lock = self.project_dir / 'package-lock.json';
139
+ build_dir = self._get_client_dir();
140
+ configs_dir = build_dir / 'configs';
141
+ configs_package_lock = configs_dir / 'package-lock.json';
142
+ if root_package_lock.exists() {
143
+ configs_dir.mkdir(exist_ok=True);
144
+ if configs_package_lock.exists() {
145
+ configs_package_lock.unlink();
146
+ }
147
+ shutil.move(str(root_package_lock), str(configs_package_lock));
148
+ }
149
+ if root_package_json.exists() {
150
+ root_package_json.unlink();
151
+ }
152
+ }
153
+
154
+ """
155
+ Ensure root package.json exists temporarily for npm commands.
156
+
157
+ Create root package.json temporarily if it doesn't exist.
158
+ """
159
+ impl ViteBundler._ensure_root_package_json(self: ViteBundler) -> None {
160
+ generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
161
+ root_package_json = self.project_dir / 'package.json';
162
+ if not generated_package_json.exists() {
163
+ self.create_package_json();
164
+ }
165
+ if not root_package_json.exists() {
166
+ shutil.copy2(generated_package_json, root_package_json);
167
+ }
168
+ }
169
+
170
+ """Format config object as JavaScript object string."""
171
+ impl ViteBundler._format_config_object(
172
+ self: ViteBundler, config: dict, indent: int = 0
173
+ ) -> str {
174
+ if not config {
175
+ return '';
176
+ }
177
+ indent_str = ' ' * indent;
178
+ items = [];
179
+ for (key, value) in config.items() {
180
+ if isinstance(value, str) {
181
+ items.append(f"{indent_str} {key}: '{value}',");
182
+ } elif isinstance(value, bool) {
183
+ items.append(f"{indent_str} {key}: {str(value).lower()},");
184
+ } elif isinstance(value, (int, float)) {
185
+ items.append(f"{indent_str} {key}: {value},");
186
+ } elif isinstance(value, <>list) {
187
+ list_str = ', '.join(repr(v) for v in value);
188
+ items.append(f"{indent_str} {key}: [{list_str}],");
189
+ } elif isinstance(value, <>dict) {
190
+ nested = self._format_config_object(value, (indent + 2));
191
+ items.append(f"{indent_str} {key}: {{{nested}{indent_str} }},");
192
+ } else {
193
+ items.append(f"{indent_str} {key}: {repr(value)},");
194
+ }
195
+ }
196
+ return '\n'.join(items);
197
+ }
198
+
199
+ """Format plugin options as JavaScript object string."""
200
+ impl ViteBundler._format_plugin_options(self: ViteBundler, options: dict) -> str {
201
+ if not options {
202
+ return '';
203
+ }
204
+ items = [];
205
+ for (key, value) in options.items() {
206
+ if isinstance(value, str) {
207
+ items.append(f"{key}: '{value}'");
208
+ } elif isinstance(value, bool) {
209
+ items.append(f"{key}: {str(value).lower()}");
210
+ } elif isinstance(value, (int, float)) {
211
+ items.append(f"{key}: {value}");
212
+ } elif isinstance(value, <>list) {
213
+ items.append(f"{key}: [{', '.join(repr(v) for v in value)}]");
214
+ } else {
215
+ items.append(f"{key}: {repr(value)}");
216
+ }
217
+ }
218
+ return '{ ' + ', '.join(items) + ' }';
219
+ }
220
+
221
+ """Get a valid JavaScript variable name from plugin module name."""
222
+ impl ViteBundler._get_plugin_var_name(self: ViteBundler, plugin_name: str) -> str {
223
+ name = plugin_name.split('/')[-1];
224
+ name = name.replace('-', '_').replace('.', '_');
225
+ name = name.lstrip('@');
226
+ return name;
227
+ }
228
+
229
+ """Create vite.config.js from config.json during bundling."""
230
+ impl ViteBundler.create_vite_config(self: ViteBundler, entry_file: Path) -> Path {
231
+ build_dir = self._get_client_dir();
232
+ build_dir.mkdir(parents=True, exist_ok=True);
233
+ configs_dir = build_dir / 'configs';
234
+ configs_dir.mkdir(exist_ok=True);
235
+ vite_config_data = self.config_loader.get_vite_config();
236
+ config_path = configs_dir / 'vite.config.js';
237
+ # TypeScript is always enabled by default
238
+ try {
239
+ # Entry file path relative to client build dir (not project root)
240
+ entry_relative = entry_file.relative_to(build_dir).as_posix();
241
+ } except ValueError {
242
+ # Fallback: use absolute path
243
+ entry_relative = entry_file.as_posix();
244
+ }
245
+ try {
246
+ # Output dir path relative to client build dir (not project root)
247
+ output_relative = self.output_dir.relative_to(build_dir).as_posix();
248
+ } except ValueError {
249
+ # Fallback: use absolute path
250
+ output_relative = self.output_dir.as_posix();
251
+ }
252
+ # Calculate compiled directory path for aliases (relative to client build dir)
253
+ if entry_relative.endswith('/build/main.js') {
254
+ compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
255
+ compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
256
+ } elif entry_relative.endswith('build/main.js') {
257
+ compiled_utils_relative = 'compiled/client_runtime.js';
258
+ compiled_assets_relative = 'compiled/assets';
259
+ } else {
260
+ compiled_utils_relative = 'compiled/client_runtime.js';
261
+ compiled_assets_relative = 'compiled/assets';
262
+ }
263
+ plugins = [];
264
+ plugin_imports = [];
265
+ # TypeScript support is always enabled, so always include React plugin
266
+ plugin_imports.append('import react from "@vitejs/plugin-react";');
267
+ plugins.append(' react()');
268
+ lib_imports = vite_config_data.get('lib_imports', []);
269
+ for lib_import in lib_imports {
270
+ if (isinstance(lib_import, str) and lib_import.strip()) {
271
+ plugin_imports.append(lib_import);
272
+ }
273
+ }
274
+ custom_plugins = vite_config_data.get('plugins', []);
275
+ for plugin in custom_plugins {
276
+ if isinstance(plugin, str) {
277
+ plugins.append(f" {plugin}");
278
+ }
279
+ }
280
+ plugins_str = ',\n'.join(plugins) if plugins else '';
281
+ imports_str = '\n'.join(plugin_imports) if plugin_imports else '';
282
+ # TypeScript extensions are always included
283
+ extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'];
284
+ extensions_str = ', '.join(f'"{ext}"' for ext in extensions);
285
+ build_config = vite_config_data.get('build', {});
286
+ build_overrides_str = self._format_config_object(build_config, indent=4)
287
+ if (isinstance(build_config, <>dict) and build_config)
288
+ else '';
289
+ server_config = vite_config_data.get('server', {});
290
+ server_config_str = self._format_config_object(server_config, indent=2)
291
+ if (isinstance(server_config, <>dict) and server_config)
292
+ else '';
293
+ resolve_config = vite_config_data.get('resolve', {});
294
+ resolve_overrides_str = self._format_config_object(resolve_config, indent=6)
295
+ if (isinstance(resolve_config, <>dict) and resolve_config)
296
+ else '';
297
+ imports_section = f"{imports_str}\n" if imports_str else '';
298
+ newline = '\n';
299
+ server_section = f" server: {{{newline}{server_config_str}{newline} }},{newline}"
300
+ if server_config_str
301
+ else '';
302
+ config_content = f'''import {{ defineConfig }} from "vite";
303
+ import path from "path";
304
+ import {{ fileURLToPath }} from "url";
305
+ {imports_section}const __dirname = path.dirname(fileURLToPath(import.meta.url));
306
+ // Config is in configs/ inside .jac/client/, so go up one level to .jac/client/, then up two more to project root
307
+ const buildDir = path.resolve(__dirname, "..");
308
+ const projectRoot = path.resolve(__dirname, "../../..");
309
+
310
+ /**
311
+ * Vite configuration generated from config.json (in project root)
312
+ * To customize, edit config.json instead of this file.
313
+ */
314
+
315
+ export default defineConfig({{
316
+ plugins: [{(newline + plugins_str + newline + ' ') if plugins_str else ''}],
317
+ root: buildDir, // base folder (.jac/client/) so vite can find node_modules
318
+ build: {{
319
+ rollupOptions: {{
320
+ input: path.resolve(buildDir, "{entry_relative}"), // your compiled entry file
321
+ output: {{
322
+ entryFileNames: "client.[hash].js", // name of the final js file
323
+ assetFileNames: (assetInfo) => assetInfo.name?.endsWith('.css') ? 'styles.css' : '[name].[ext]',
324
+ }},
325
+ }},
326
+ outDir: path.resolve(buildDir, "{output_relative}"), // final bundled output
327
+ emptyOutDir: true,
328
+ {build_overrides_str}
329
+ }},
330
+ publicDir: false,
331
+ {server_section} resolve: {{
332
+ alias: {{
333
+ "@jac-client/utils": path.resolve(buildDir, "{compiled_utils_relative}"),
334
+ "@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
335
+ }},
336
+ extensions: [{extensions_str}],
337
+ {resolve_overrides_str}
338
+ }},
339
+ }});
340
+ ''';
341
+ config_path.write_text(config_content, encoding='utf-8');
342
+ return config_path;
343
+ }
344
+
345
+ """Check if the project has TypeScript support. TypeScript is now enabled by default."""
346
+ impl ViteBundler._has_typescript_support(self: ViteBundler) -> bool {
347
+ # TypeScript is always enabled by default
348
+ return True;
349
+ }
350
+
351
+ """Read the bundled code and compute its hash."""
352
+ impl ViteBundler.read_bundle(self: ViteBundler) -> tuple[str, str] {
353
+ bundle_file = self.find_bundle();
354
+ if not bundle_file {
355
+ raise ClientBundleError('Vite build completed but no bundle file found') ;
356
+ }
357
+ bundle_code = bundle_file.read_text(encoding='utf-8');
358
+ bundle_hash = hashlib.sha256(bundle_code.encode('utf-8')).hexdigest();
359
+ return (bundle_code, bundle_hash);
360
+ }
361
+
362
+ """Find the generated Vite CSS file."""
363
+ impl ViteBundler.find_css(self: ViteBundler) -> Optional[Path] {
364
+ css_file = self.output_dir / 'styles.css';
365
+ return css_file if css_file.exists() else None;
366
+ }
367
+
368
+ """Find the generated Vite bundle file."""
369
+ impl ViteBundler.find_bundle(self: ViteBundler) -> Optional[Path] {
370
+ for file in self.output_dir.glob('client.*.js') {
371
+ return file;
372
+ }
373
+ return None;
374
+ }
375
+
376
+ """Run Vite build with generated config in .jac/client/configs/."""
377
+ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) -> None {
378
+ self.output_dir.mkdir(parents=True, exist_ok=True);
379
+ generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
380
+ if not generated_package_json.exists() {
381
+ self.create_package_json();
382
+ } else {
383
+ # Ensure tsconfig.json exists even if package.json already exists
384
+ self.create_tsconfig();
385
+ }
386
+ try {
387
+ build_dir = self._get_client_dir();
388
+ node_modules = build_dir / 'node_modules';
389
+ if not node_modules.exists() {
390
+ # Temporarily copy package.json to client build dir for npm install
391
+ build_package_json = build_dir / 'package.json';
392
+ configs_package_json = build_dir / 'configs' / 'package.json';
393
+ if configs_package_json.exists() and not build_package_json.exists() {
394
+ import shutil;
395
+ shutil.copy2(configs_package_json, build_package_json);
396
+ }
397
+ try {
398
+ # Install to .jac/client/node_modules
399
+ subprocess.run(
400
+ ['npm', 'install'],
401
+ cwd=build_dir,
402
+ check=True,
403
+ capture_output=True,
404
+ text=True
405
+ );
406
+ } except subprocess.CalledProcessError as e {
407
+ raise e from ClientBundleError(
408
+ f"Failed to install npm dependencies: {e.stderr}"
409
+ ) ;
410
+ } except FileNotFoundError {
411
+ raise None from ClientBundleError(
412
+ 'npm command not found. Ensure Node.js and npm are installed.'
413
+ ) ;
414
+ }
415
+ }
416
+ if self.config_path {
417
+ # Make config path relative to build_dir (where vite runs from)
418
+ try {
419
+ config_rel = self.config_path.relative_to(build_dir);
420
+ command = ['npx', 'vite', 'build', '--config', str(config_rel)];
421
+ } except ValueError {
422
+ # Config is outside client build dir, use absolute path
423
+ command = ['npx', 'vite', 'build', '--config', str(self.config_path)];
424
+ }
425
+ } elif entry_file {
426
+ generated_config = self.create_vite_config(entry_file);
427
+ # Config is in configs/, make it relative to build_dir
428
+ config_rel = generated_config.relative_to(build_dir);
429
+ command = ['npx', 'vite', 'build', '--config', str(config_rel)];
430
+ } else {
431
+ command = ['npm', 'run', 'build'];
432
+ }
433
+ # Run vite from client build directory so it can find node_modules
434
+ result = subprocess.run(
435
+ command, cwd=build_dir, check=False, capture_output=True, text=True
436
+ );
437
+ if result.returncode != 0 {
438
+ error_msg = result.stderr or result.stdout or 'Unknown error';
439
+ raise ClientBundleError(
440
+ f"Vite build failed:\n{error_msg}\nCommand: {' '.join(command)}"
441
+ ) from None ;
442
+ }
443
+ } finally {
444
+ # Clean up temporary package.json in client build dir
445
+ build_package_json = build_dir / 'package.json';
446
+ if build_package_json.exists() {
447
+ build_package_json.unlink();
448
+ }
449
+ # Move package-lock.json to configs/ if it exists
450
+ build_package_lock = build_dir / 'package-lock.json';
451
+ if build_package_lock.exists() {
452
+ configs_package_lock = build_dir / 'configs' / 'package-lock.json';
453
+ if configs_package_lock.exists() {
454
+ configs_package_lock.unlink();
455
+ }
456
+ build_package_lock.rename(configs_package_lock);
457
+ }
458
+ }
459
+ }
460
+
461
+ """Initialize the Vite bundler."""
462
+ impl ViteBundler.init(
463
+ self: ViteBundler,
464
+ project_dir: Path,
465
+ output_dir: Optional[Path] = None,
466
+ minify: bool = False,
467
+ config_path: Optional[Path] = None
468
+ ) {
469
+ self.project_dir = project_dir;
470
+ self.minify = minify;
471
+ self.config_path = config_path;
472
+ self.config_loader = JacClientConfig(project_dir);
473
+ # Set output_dir after config_loader is initialized so _get_client_dir works
474
+ self.output_dir = output_dir or (self._get_client_dir() / 'dist');
475
+ }
476
+
477
+ """Create a dev-mode vite config with API proxy for HMR."""
478
+ impl ViteBundler.create_dev_vite_config(
479
+ self: ViteBundler, entry_file: Path, api_port: int = 8000
480
+ ) -> Path {
481
+ build_dir = self._get_client_dir();
482
+ build_dir.mkdir(parents=True, exist_ok=True);
483
+ configs_dir = build_dir / 'configs';
484
+ configs_dir.mkdir(exist_ok=True);
485
+ config_path = configs_dir / 'vite.dev.config.js';
486
+ # Get entry file relative path
487
+ try {
488
+ entry_relative = entry_file.relative_to(build_dir).as_posix();
489
+ } except ValueError {
490
+ entry_relative = entry_file.as_posix();
491
+ }
492
+ # Calculate paths for aliases
493
+ if entry_relative.endswith('/build/main.js') {
494
+ compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
495
+ compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
496
+ } elif entry_relative.endswith('build/main.js') {
497
+ compiled_utils_relative = 'compiled/client_runtime.js';
498
+ compiled_assets_relative = 'compiled/assets';
499
+ } else {
500
+ compiled_utils_relative = 'compiled/client_runtime.js';
501
+ compiled_assets_relative = 'compiled/assets';
502
+ }
503
+ # Extensions for TypeScript
504
+ extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'];
505
+ extensions_str = ', '.join(f'"{ext}"' for ext in extensions);
506
+ # Generate dev config with proxy for API routes
507
+ config_content = f'''import {{ defineConfig }} from "vite";
508
+ import path from "path";
509
+ import {{ fileURLToPath }} from "url";
510
+ import react from "@vitejs/plugin-react";
511
+
512
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
513
+ const buildDir = path.resolve(__dirname, "..");
514
+ const projectRoot = path.resolve(__dirname, "../../..");
515
+
516
+ /**
517
+ * Vite DEV configuration for HMR mode
518
+ * Proxies API routes to Python server at localhost:{api_port}
519
+ */
520
+ export default defineConfig({{
521
+ plugins: [react()],
522
+ root: buildDir,
523
+ publicDir: false,
524
+ server: {{
525
+ watch: {{
526
+ usePolling: true,
527
+ interval: 100,
528
+ }},
529
+ proxy: {{
530
+ "/walker": {{
531
+ target: "http://localhost:{api_port}",
532
+ changeOrigin: true,
533
+ }},
534
+ "/function": {{
535
+ target: "http://localhost:{api_port}",
536
+ changeOrigin: true,
537
+ }},
538
+ "/user": {{
539
+ target: "http://localhost:{api_port}",
540
+ changeOrigin: true,
541
+ }},
542
+ "/introspect": {{
543
+ target: "http://localhost:{api_port}",
544
+ changeOrigin: true,
545
+ }},
546
+ }},
547
+ }},
548
+ resolve: {{
549
+ alias: {{
550
+ "@jac-client/utils": path.resolve(buildDir, "{compiled_utils_relative}"),
551
+ "@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
552
+ }},
553
+ extensions: [{extensions_str}],
554
+ }},
555
+ }});
556
+ ''';
557
+ config_path.write_text(config_content, encoding='utf-8');
558
+ return config_path;
559
+ }
560
+
561
+ """Start Vite dev server as a subprocess."""
562
+ impl ViteBundler.start_dev_server(self: ViteBundler, port: int = 3000) -> Any {
563
+ build_dir = self._get_client_dir();
564
+ node_modules = build_dir / 'node_modules';
565
+ # Create/update index.html for dev server (load from compiled/ for HMR)
566
+ index_html = build_dir / 'index.html';
567
+ index_content = '''<!DOCTYPE html>
568
+ <html lang="en">
569
+ <head>
570
+ <meta charset="UTF-8" />
571
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
572
+ <title>Jac App (Dev)</title>
573
+ </head>
574
+ <body>
575
+ <div id="root"></div>
576
+ <script type="module" src="/compiled/_entry.js"></script>
577
+ </body>
578
+ </html>
579
+ ''';
580
+ index_html.write_text(index_content, encoding='utf-8');
581
+ # Ensure dependencies are installed
582
+ if not node_modules.exists() {
583
+ generated_package_json = build_dir / 'configs' / 'package.json';
584
+ if not generated_package_json.exists() {
585
+ self.create_package_json();
586
+ }
587
+ # Temporarily copy package.json for npm install
588
+ build_package_json = build_dir / 'package.json';
589
+ if not build_package_json.exists() {
590
+ shutil.copy2(generated_package_json, build_package_json);
591
+ }
592
+ try {
593
+ print("[Vite] Installing dependencies...");
594
+ subprocess.run(
595
+ ['npm', 'install'],
596
+ cwd=build_dir,
597
+ check=True,
598
+ capture_output=True,
599
+ text=True
600
+ );
601
+ } except subprocess.CalledProcessError as e {
602
+ print(f"[Vite] Error installing dependencies: {e.stderr}");
603
+ raise ;
604
+ } finally {
605
+ # Clean up temp package.json
606
+ if build_package_json.exists() {
607
+ build_package_json.unlink();
608
+ }
609
+ }
610
+ }
611
+ # Find the dev config
612
+ dev_config = build_dir / 'configs' / 'vite.dev.config.js';
613
+ if not dev_config.exists() {
614
+ raise ClientBundleError(
615
+ "Dev config not found. Call create_dev_vite_config first."
616
+ ) ;
617
+ }
618
+ config_rel = dev_config.relative_to(build_dir);
619
+ print(f"[Vite] Starting dev server on port {port}...");
620
+ # Start Vite in dev mode (let output go to terminal for HMR visibility)
621
+ process = subprocess.Popen(
622
+ ['npx', 'vite', '--config', str(config_rel), '--port', str(port)],
623
+ cwd=build_dir
624
+ );
625
+ return process;
626
+ }
@@ -0,0 +1,19 @@
1
+ """Import processing for Vite bundling."""
2
+ import from pathlib { Path }
3
+ import from typing { TYPE_CHECKING }
4
+ import from jaclang.runtimelib.client_bundle { ClientBundleError }
5
+
6
+ with entry {
7
+ if TYPE_CHECKING {
8
+ import from jaclang.compiler.codeinfo { ClientManifest }
9
+ }
10
+ }
11
+
12
+ """Processes client imports for Vite bundling."""
13
+ class ImportProcessor {
14
+ def process_vite_imports(
15
+ self: ImportProcessor, manifest: (ClientManifest | None), module_path: Path
16
+ ) -> list[(Path | None)];
17
+
18
+ def should_process_import(self: ImportProcessor, import_path: Path) -> bool;
19
+ }
@@ -0,0 +1,35 @@
1
+ """Jac to JavaScript compilation module."""
2
+ import from collections.abc { Callable }
3
+ import from pathlib { Path }
4
+ import from types { ModuleType }
5
+ import from typing { TYPE_CHECKING, Any }
6
+
7
+ with entry {
8
+ if TYPE_CHECKING {
9
+ import from jaclang.compiler.codeinfo { ClientManifest }
10
+ }
11
+ }
12
+
13
+ """Handles compilation of Jac files to JavaScript."""
14
+ class JacToJSCompiler {
15
+ def init(
16
+ self: JacToJSCompiler,
17
+ compile_to_js_func: Callable[([Path], tuple[(str, (ModuleType | None))])],
18
+ extract_exports_func: Callable[([Any], list[str])],
19
+ extract_globals_func: Callable[([Any, ModuleType], dict[(str, Any)])]
20
+ );
21
+
22
+ def compile_module(
23
+ self: JacToJSCompiler, module_path: Path
24
+ ) -> tuple[str, (ModuleType | None), (ClientManifest | None)];
25
+
26
+ def extract_exports(
27
+ self: JacToJSCompiler, manifest: (ClientManifest | None)
28
+ ) -> list[str];
29
+
30
+ def extract_globals(
31
+ self: JacToJSCompiler, manifest: (ClientManifest | None), module: ModuleType
32
+ ) -> dict[str, Any];
33
+
34
+ def add_runtime_imports(self: JacToJSCompiler, js_code: str) -> str;
35
+ }