jac-client 0.2.8__py3-none-any.whl → 0.2.11__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 (119) hide show
  1. jac_client/examples/all-in-one/button.jac +4 -3
  2. jac_client/examples/all-in-one/components/CategoryFilter.jac +36 -24
  3. jac_client/examples/all-in-one/components/Header.jac +12 -8
  4. jac_client/examples/all-in-one/components/ProfitOverview.jac +49 -35
  5. jac_client/examples/all-in-one/components/Summary.jac +59 -36
  6. jac_client/examples/all-in-one/components/TransactionForm.jac +142 -112
  7. jac_client/examples/all-in-one/components/TransactionItem.jac +37 -30
  8. jac_client/examples/all-in-one/components/TransactionList.jac +33 -26
  9. jac_client/examples/all-in-one/components/button.jac +4 -3
  10. jac_client/examples/all-in-one/components/navigation.jac +111 -117
  11. jac_client/examples/all-in-one/constants/categories.jac +23 -24
  12. jac_client/examples/all-in-one/constants/clients.jac +7 -8
  13. jac_client/examples/all-in-one/context/BudgetContext.jac +9 -6
  14. jac_client/examples/all-in-one/hooks/useBudget.jac +18 -12
  15. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +14 -13
  16. jac_client/examples/all-in-one/main.jac +542 -0
  17. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +26 -12
  18. jac_client/examples/all-in-one/pages/FeaturesTest.jac +43 -12
  19. jac_client/examples/all-in-one/pages/LandingPage.jac +113 -90
  20. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
  21. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
  22. jac_client/examples/all-in-one/pages/loginPage.jac +114 -119
  23. jac_client/examples/all-in-one/pages/nestedDemo.jac +44 -51
  24. jac_client/examples/all-in-one/pages/notFound.jac +15 -21
  25. jac_client/examples/all-in-one/pages/signupPage.jac +113 -119
  26. jac_client/examples/all-in-one/utils/formatters.jac +5 -8
  27. jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
  28. jac_client/examples/asset-serving/image-asset/main.jac +56 -0
  29. jac_client/examples/asset-serving/import-alias/main.jac +109 -0
  30. jac_client/examples/basic/main.jac +23 -0
  31. jac_client/examples/basic-auth/main.jac +363 -0
  32. jac_client/examples/basic-auth-with-router/main.jac +451 -0
  33. jac_client/examples/basic-full-stack/main.jac +362 -0
  34. jac_client/examples/css-styling/js-styling/main.jac +63 -0
  35. jac_client/examples/css-styling/material-ui/main.jac +122 -0
  36. jac_client/examples/css-styling/pure-css/main.jac +55 -0
  37. jac_client/examples/css-styling/sass-example/main.jac +55 -0
  38. jac_client/examples/css-styling/styled-components/main.jac +62 -0
  39. jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
  40. jac_client/examples/full-stack-with-auth/main.jac +696 -0
  41. jac_client/examples/little-x/main.jac +681 -0
  42. jac_client/examples/little-x/src/submit-button.jac +15 -14
  43. jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
  44. jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
  45. jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
  46. jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
  47. jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
  48. jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
  49. jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
  50. jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
  51. jac_client/examples/ts-support/main.jac +35 -0
  52. jac_client/examples/with-router/main.jac +286 -0
  53. jac_client/plugin/cli.jac +491 -411
  54. jac_client/plugin/client.jac +25 -0
  55. jac_client/plugin/client_runtime.cl.jac +10 -4
  56. jac_client/plugin/impl/client.impl.jac +96 -55
  57. jac_client/plugin/impl/client_runtime.impl.jac +155 -1
  58. jac_client/plugin/plugin_config.jac +211 -29
  59. jac_client/plugin/src/__init__.jac +0 -2
  60. jac_client/plugin/src/compiler.jac +0 -1
  61. jac_client/plugin/src/config_loader.jac +1 -0
  62. jac_client/plugin/src/desktop_config.jac +31 -0
  63. jac_client/plugin/src/impl/compiler.impl.jac +49 -17
  64. jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
  65. jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
  66. jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
  67. jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
  68. jac_client/plugin/src/impl/vite_bundler.impl.jac +191 -64
  69. jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
  70. jac_client/plugin/src/targets/desktop_target.jac +37 -0
  71. jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
  72. jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
  73. jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
  74. jac_client/plugin/src/targets/register.jac +21 -0
  75. jac_client/plugin/src/targets/registry.jac +87 -0
  76. jac_client/plugin/src/targets/web_target.jac +35 -0
  77. jac_client/plugin/src/vite_bundler.jac +6 -0
  78. jac_client/plugin/utils/__init__.jac +3 -0
  79. jac_client/plugin/utils/bun_installer.jac +16 -0
  80. jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
  81. jac_client/templates/client.jacpack +72 -0
  82. jac_client/templates/fullstack.jacpack +61 -0
  83. jac_client/tests/conftest.py +103 -47
  84. jac_client/tests/fixtures/spawn_test/app.jac +49 -52
  85. jac_client/tests/fixtures/with-ts/app.jac +27 -27
  86. jac_client/tests/test_cli.py +182 -71
  87. jac_client/tests/test_e2e.py +232 -0
  88. jac_client/tests/test_helpers.py +58 -0
  89. jac_client/tests/test_it.py +91 -135
  90. jac_client/tests/test_it_desktop.py +891 -0
  91. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/METADATA +6 -6
  92. jac_client-0.2.11.dist-info/RECORD +113 -0
  93. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
  94. jac_client/examples/all-in-one/app.jac +0 -573
  95. jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +0 -70
  96. jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +0 -552
  97. jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
  98. jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
  99. jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
  100. jac_client/examples/basic/src/app.jac +0 -21
  101. jac_client/examples/basic-auth/src/app.jac +0 -371
  102. jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
  103. jac_client/examples/basic-full-stack/src/app.jac +0 -359
  104. jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
  105. jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
  106. jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
  107. jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
  108. jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
  109. jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
  110. jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
  111. jac_client/examples/little-x/src/app.jac +0 -719
  112. jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
  113. jac_client/examples/ts-support/src/app.jac +0 -35
  114. jac_client/examples/with-router/src/app.jac +0 -323
  115. jac_client/plugin/src/babel_processor.jac +0 -18
  116. jac_client/plugin/src/impl/babel_processor.impl.jac +0 -89
  117. jac_client-0.2.8.dist-info/RECORD +0 -97
  118. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
  119. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
@@ -5,10 +5,13 @@ configuration system, registering:
5
5
  - Plugin metadata (name, version)
6
6
  - Config schema for [plugins.client] section
7
7
  - npm dependency type for [dependencies.npm] section
8
+ - Project templates (client, fullstack)
8
9
  """
9
10
 
11
+ import os;
10
12
  import subprocess;
11
13
  import sys;
14
+ import json;
12
15
  import from pathlib { Path }
13
16
  import from typing { Any }
14
17
  import from jaclang.pycore.runtime { hookimpl }
@@ -68,6 +71,11 @@ class JacClientPluginConfig {
68
71
  "type": "dict",
69
72
  "default": {},
70
73
  "description": "TypeScript configuration overrides"
74
+ },
75
+ "configs": {
76
+ "type": "dict",
77
+ "default": {},
78
+ "description": "Generate config files for npm packages. Keys are config names (e.g., 'postcss', 'tailwind'), values are the config objects to export."
71
79
  }
72
80
  }
73
81
  };
@@ -79,15 +87,194 @@ class JacClientPluginConfig {
79
87
  return {
80
88
  "name": "npm",
81
89
  "dev_name": "npm.dev",
82
- "cli_flag": "--cl",
90
+ "cli_flag": "--npm",
83
91
  "install_dir": ".jac/client/configs",
84
92
  "install_handler": _npm_install_handler,
85
93
  "install_all_handler": _npm_install_all_handler,
86
94
  "remove_handler": _npm_remove_handler
87
95
  };
88
96
  }
97
+
98
+ """Register the client and fullstack project templates."""
99
+ @hookimpl
100
+ static def register_project_template -> list[dict[str, Any]] {
101
+ templates: list[dict[str, Any]] = [];
102
+
103
+ # Load templates from bundled JSON files
104
+ for template_name in ["client", "fullstack"] {
105
+ template = _load_template(template_name);
106
+ if template {
107
+ templates.append(template);
108
+ }
109
+ }
110
+
111
+ return templates;
112
+ }
113
+ }
114
+
115
+ # ===============================================================================
116
+ # Template Loader
117
+ # ===============================================================================
118
+ """Load a template from bundled JSON file."""
119
+ def _load_template(template_name: str) -> dict[str, Any] | None {
120
+ # Find the template JSON file relative to this module
121
+ # plugin_config.jac is in jac_client/plugin/, so .parent.parent = jac_client/
122
+ module_dir = Path(__file__).parent.parent; # jac_client directory
123
+ template_path = module_dir / "templates" / f"{template_name}.jacpack";
124
+
125
+ if not template_path.exists() {
126
+ # Fallback: look in installed package location
127
+ import from importlib.resources { files }
128
+ try {
129
+ package_files = files("jac_client");
130
+ template_path = Path(str(package_files)) / "templates" / f"{template_name}.jacpack";
131
+ } except Exception {
132
+ return None;
133
+ }
134
+ }
135
+
136
+ if not template_path.exists() {
137
+ return None;
138
+ }
139
+
140
+ try {
141
+ with open(template_path, "r") as f {
142
+ data = json.load(f);
143
+ }
144
+
145
+ # Attach the post_create hook
146
+ data["post_create"] = _post_create_client;
147
+
148
+ return data;
149
+ } except Exception as e {
150
+ print(
151
+ f"Warning: Could not load {template_name} template: {e}", file=sys.stderr
152
+ );
153
+ return None;
154
+ }
89
155
  }
90
156
 
157
+ # ===============================================================================
158
+ # Post-create Hook for Client Template
159
+ # ===============================================================================
160
+ """Post-create hook to ensure Bun is installed and optionally install packages."""
161
+ def _post_create_client(project_path: Path, project_name: str) -> None {
162
+ import from jac_client.plugin.src.vite_bundler { ViteBundler }
163
+ import from jaclang.cli.console { console }
164
+ import from jac_client.plugin.utils { ensure_bun_available }
165
+
166
+ # First, ensure Bun is installed (prompt to install if not)
167
+ console.print();
168
+ if not ensure_bun_available() {
169
+ console.warning("Bun is required for client development");
170
+ console.print(" After installing Bun, run: jac add --cl\n", style="muted");
171
+ return;
172
+ }
173
+
174
+ console.print(" ✔ Bun is installed", style="success");
175
+
176
+ # Check if package installation should be skipped
177
+ if os.environ.get("JAC_CLIENT_SKIP_NPM_INSTALL") {
178
+ console.print(
179
+ "\n ⏭ Skipping package installation (JAC_CLIENT_SKIP_NPM_INSTALL is set)",
180
+ style="muted"
181
+ );
182
+ console.print(
183
+ " Run 'jac add --npm' when ready to install packages.\n", style="muted"
184
+ );
185
+ return;
186
+ }
187
+
188
+ console.print();
189
+
190
+ try {
191
+ # Verify jac.toml exists
192
+ toml_path = project_path / "jac.toml";
193
+ if not toml_path.exists() {
194
+ print(
195
+ "Warning: jac.toml not found, skipping package installation",
196
+ file=sys.stderr
197
+ );
198
+ return;
199
+ }
200
+
201
+ # Create ViteBundler instance
202
+ bundler = ViteBundler(project_path);
203
+
204
+ # Generate package.json with default packages
205
+ bundler.create_package_json(project_name=project_name);
206
+
207
+ # Ensure .jac/client directory exists
208
+ client_dir = bundler._get_client_dir();
209
+ client_dir.mkdir(parents=True, exist_ok=True);
210
+
211
+ # Copy package.json to .jac/client/ for bun install
212
+ configs_package_json = client_dir / 'configs' / 'package.json';
213
+ build_package_json = client_dir / 'package.json';
214
+
215
+ if not configs_package_json.exists() {
216
+ print(
217
+ "Warning: package.json was not generated, skipping package installation",
218
+ file=sys.stderr
219
+ );
220
+ return;
221
+ }
222
+
223
+ shutil.copy2(configs_package_json, build_package_json);
224
+
225
+ # Run bun install (ensure bun is available first)
226
+ import from jac_client.plugin.utils { ensure_bun_available }
227
+ if not ensure_bun_available() {
228
+ console.warning("Bun is required. Install manually: https://bun.sh");
229
+ console.print(
230
+ " 💡 You can install packages later with: jac add --cl\n",
231
+ style="muted"
232
+ );
233
+ # Clean up temporary package.json
234
+ if build_package_json.exists() {
235
+ build_package_json.unlink();
236
+ }
237
+ return;
238
+ }
239
+
240
+ try {
241
+ console.print("\n ⏳ Installing packages...\n", style="cyan");
242
+ subprocess.run(['bun', 'install'], cwd=client_dir, check=True, timeout=300);
243
+
244
+ # Move bun.lockb to configs/ if it was created
245
+ build_bun_lockb = client_dir / 'bun.lockb';
246
+ configs_bun_lockb = client_dir / 'configs' / 'bun.lockb';
247
+ if build_bun_lockb.exists() {
248
+ if configs_bun_lockb.exists() {
249
+ configs_bun_lockb.unlink();
250
+ }
251
+ shutil.move(str(build_bun_lockb), str(configs_bun_lockb));
252
+ }
253
+
254
+ console.print(" ✔ Packages installed", style="success");
255
+ } except subprocess.CalledProcessError as e {
256
+ console.warning(f"Failed to install packages: {e}");
257
+ console.print(
258
+ " 💡 You can install packages later with: jac add --cl\n",
259
+ style="muted"
260
+ );
261
+ } finally {
262
+ # Clean up temporary package.json
263
+ if build_package_json.exists() {
264
+ build_package_json.unlink();
265
+ }
266
+ }
267
+ } except Exception as e {
268
+ console.warning(f"Could not install default packages: {e}");
269
+ console.print(
270
+ " 💡 You can install packages later with: jac add --cl\n", style="muted"
271
+ );
272
+ }
273
+ }
274
+
275
+ # ===============================================================================
276
+ # NPM Dependency Handlers
277
+ # ===============================================================================
91
278
  """Install an npm package."""
92
279
  def _npm_install_handler(
93
280
  config: JacConfig,
@@ -101,15 +288,13 @@ def _npm_install_handler(
101
288
  raise RuntimeError("No project root found") ;
102
289
  }
103
290
 
104
- project_dir = config.project_root;
105
-
106
291
  # Add to config (core JacConfig handles this)
107
- package_version = version or "latest";
292
+ package_version: str = version or "latest";
108
293
  config.add_dependency(package_name, package_version, dev=is_dev, dep_type="npm");
109
294
  config.save();
110
295
 
111
296
  # Generate package.json and run npm install
112
- _regenerate_and_install(project_dir);
297
+ _regenerate_and_install(Path(str(config.project_root)));
113
298
  }
114
299
 
115
300
  """Install all npm packages from jac.toml."""
@@ -118,7 +303,7 @@ def _npm_install_all_handler(config: JacConfig) -> None {
118
303
  raise RuntimeError("No project root found") ;
119
304
  }
120
305
 
121
- _regenerate_and_install(config.project_root);
306
+ _regenerate_and_install(Path(str(config.project_root)));
122
307
  }
123
308
 
124
309
  """Remove an npm package."""
@@ -129,7 +314,7 @@ def _npm_remove_handler(
129
314
  raise RuntimeError("No project root found") ;
130
315
  }
131
316
 
132
- project_dir = config.project_root;
317
+ project_dir: Path = Path(str(config.project_root));
133
318
 
134
319
  # Remove from config (core JacConfig handles this)
135
320
  result = config.remove_dependency(package_name, dev=is_dev, dep_type="npm");
@@ -144,19 +329,25 @@ def _npm_remove_handler(
144
329
  _regenerate_and_install(project_dir);
145
330
  }
146
331
 
147
- """Regenerate package.json from jac.toml and run npm install."""
332
+ """Regenerate package.json from jac.toml and run bun install."""
148
333
  def _regenerate_and_install(project_dir: Path) -> None {
149
334
  import from jac_client.plugin.src.vite_bundler { ViteBundler }
335
+ import from jac_client.plugin.utils { ensure_bun_available }
150
336
  import shutil;
151
337
 
338
+ # Ensure Bun is installed before proceeding (prompt to install if not)
339
+ if not ensure_bun_available() {
340
+ raise RuntimeError("Bun is required. Install manually: https://bun.sh") from None ;
341
+ }
342
+
152
343
  bundler = ViteBundler(project_dir);
153
344
  bundler.create_package_json();
154
345
 
155
- # Install to .jac/client/ directory where babel processor expects it
346
+ # Install to .jac/client/ directory
156
347
  client_dir = bundler._get_client_dir();
157
348
  client_dir.mkdir(parents=True, exist_ok=True);
158
349
 
159
- # Copy package.json to .jac/client/ for npm install
350
+ # Copy package.json to .jac/client/ for bun install
160
351
  configs_package_json = client_dir / 'configs' / 'package.json';
161
352
  build_package_json = client_dir / 'package.json';
162
353
  if configs_package_json.exists() {
@@ -164,32 +355,23 @@ def _regenerate_and_install(project_dir: Path) -> None {
164
355
  }
165
356
 
166
357
  try {
167
- subprocess.run(
168
- ["npm", "install"],
169
- cwd=client_dir,
170
- check=True,
171
- capture_output=True,
172
- text=True
173
- );
358
+ # Run bun install
359
+ subprocess.run(['bun', 'install'], cwd=client_dir, check=True, timeout=300);
174
360
  } except subprocess.CalledProcessError as e {
175
- raise RuntimeError(f"Failed to install npm packages: {e.stderr}") from e ;
176
- } except FileNotFoundError {
177
- raise RuntimeError(
178
- "npm command not found. Ensure Node.js and npm are installed."
179
- ) from None ;
361
+ raise RuntimeError(f"Failed to install packages: {e}") from e ;
180
362
  } finally {
181
363
  # Clean up temporary package.json
182
364
  if build_package_json.exists() {
183
365
  build_package_json.unlink();
184
366
  }
185
- # Move package-lock.json to configs/ if it exists
186
- build_package_lock = client_dir / 'package-lock.json';
187
- if build_package_lock.exists() {
188
- configs_package_lock = client_dir / 'configs' / 'package-lock.json';
189
- if configs_package_lock.exists() {
190
- configs_package_lock.unlink();
367
+ # Move bun.lockb to configs/ if it exists
368
+ build_bun_lockb = client_dir / 'bun.lockb';
369
+ if build_bun_lockb.exists() {
370
+ configs_bun_lockb = client_dir / 'configs' / 'bun.lockb';
371
+ if configs_bun_lockb.exists() {
372
+ configs_bun_lockb.unlink();
191
373
  }
192
- build_package_lock.rename(configs_package_lock);
374
+ build_bun_lockb.rename(configs_bun_lockb);
193
375
  }
194
376
  }
195
377
  }
@@ -1,6 +1,5 @@
1
1
  """Vite client bundle processing modules."""
2
2
  import from jac_client.plugin.src.asset_processor { AssetProcessor }
3
- import from jac_client.plugin.src.babel_processor { BabelProcessor }
4
3
  import from jac_client.plugin.src.compiler { ViteCompiler }
5
4
  import from jac_client.plugin.src.config_loader { JacClientConfig }
6
5
  import from jac_client.plugin.src.import_processor { ImportProcessor }
@@ -10,7 +9,6 @@ import from jac_client.plugin.src.vite_bundler { ViteBundler }
10
9
 
11
10
  glob __all__ = [
12
11
  'AssetProcessor',
13
- 'BabelProcessor',
14
12
  'ViteCompiler',
15
13
  'JacClientConfig',
16
14
  'ImportProcessor',
@@ -6,7 +6,6 @@ import from types { ModuleType }
6
6
  import from typing { TYPE_CHECKING, Any }
7
7
  import from jaclang.runtimelib.client_bundle { ClientBundleError }
8
8
  import from .asset_processor { AssetProcessor }
9
- import from .babel_processor { BabelProcessor }
10
9
  import from .import_processor { ImportProcessor }
11
10
  import from .jac_to_js { JacToJSCompiler }
12
11
  import from .vite_bundler { ViteBundler }
@@ -21,6 +21,7 @@ class JacClientConfig(PluginConfigBase) {
21
21
  def save(self: JacClientConfig) -> None;
22
22
  def get_vite_config(self: JacClientConfig) -> dict[str, Any];
23
23
  def get_ts_config(self: JacClientConfig) -> dict[str, Any];
24
+ def get_configs(self: JacClientConfig) -> dict[str, Any];
24
25
  def get_package_config(self: JacClientConfig) -> dict[str, Any];
25
26
  def add_dependency(
26
27
  self: JacClientConfig, name: str, version: str, is_dev: bool = False
@@ -0,0 +1,31 @@
1
+ """Configuration loader for Desktop target (Tauri).
2
+
3
+ This module provides configuration access for the desktop target.
4
+ Configuration is loaded from jac.toml under [desktop] section.
5
+
6
+ Similar to JacClientConfig but loads [desktop] directly (not under [plugins]).
7
+ """
8
+ import from pathlib { Path }
9
+ import from typing { Any }
10
+ import from jaclang.project.config { JacConfig, get_config }
11
+
12
+ """Desktop target configuration loader.
13
+
14
+ Provides access to desktop/Tauri configuration
15
+ from the [desktop] section of jac.toml.
16
+ """
17
+ class DesktopConfig {
18
+ has project_dir: Path | None = None,
19
+ _config: dict[str, Any] | None = None,
20
+ _jac_config: JacConfig | None = None;
21
+
22
+ def init(self: DesktopConfig, project_dir: Path | None = None) -> None;
23
+ def load(self: DesktopConfig) -> dict[str, Any];
24
+ def get_desktop_config(self: DesktopConfig) -> dict[str, Any];
25
+ def get_window_config(self: DesktopConfig) -> dict[str, Any];
26
+ def get_platforms_config(self: DesktopConfig) -> dict[str, Any];
27
+ def get_features_config(self: DesktopConfig) -> dict[str, Any];
28
+ def validate(self: DesktopConfig) -> None;
29
+ def get_default_config(self: DesktopConfig) -> dict[str, Any];
30
+ # def _get_jac_config(self: DesktopConfig) -> JacConfig;
31
+ }
@@ -16,7 +16,6 @@ impl ViteCompiler._get_client_dir(self: ViteCompiler) -> Path {
16
16
  impl ViteCompiler.compile_and_bundle(
17
17
  self: ViteCompiler, module: ModuleType, module_path: Path
18
18
  ) -> tuple[str, str, list[str], list[str]] {
19
- self.compile_runtime_utils();
20
19
  (module_js, mod, module_manifest) = self.jac_compiler.compile_module(module_path);
21
20
  collected_exports: set[str] = set(
22
21
  self.jac_compiler.extract_exports(module_manifest)
@@ -28,14 +27,14 @@ impl ViteCompiler.compile_and_bundle(
28
27
  collected_exports=collected_exports,
29
28
  collected_globals=collected_globals
30
29
  );
30
+ # Compile runtime AFTER dependencies so it overwrites any dependency-compiled
31
+ # version of client_runtime.js with the proper ES module exports
32
+ self.compile_runtime_utils();
31
33
  self.copy_root_assets();
32
34
  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';
35
+ # Vite handles JSX/TSX transpilation natively with Bun - no Babel needed
36
+ # Vite builds directly from compiled/ directory
37
+ entry_file = self.compiled_dir / '_entry.js';
39
38
  self.vite_bundler.build(entry_file=entry_file);
40
39
  (bundle_code, bundle_hash) = self.vite_bundler.read_bundle();
41
40
  client_exports = sorted(collected_exports);
@@ -49,7 +48,7 @@ impl ViteCompiler.create_entry_file(self: ViteCompiler, module_path: Path) -> No
49
48
  entry_file = self.compiled_dir / '_entry.js';
50
49
  # Derive the app module filename from the entry point (e.g., main.jac -> main.js, app.jac -> app.js)
51
50
  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';
51
+ entry_content = f'import React from "react";\nimport {{ createRoot }} from "react-dom/client";\nimport {{ app as App }} from "./{app_module_name}.js";\nimport {{ JacClientErrorBoundary, ErrorFallback }} from "@jac/runtime";\n\nconst root = createRoot(document.getElementById("root"));\nroot.render(\n\tReact.createElement(\n\t\tJacClientErrorBoundary,{{ FallbackComponent: ErrorFallback }},\n\t\tReact.createElement(App, null)\n\t)\n);\n';
53
52
  entry_file.write_text(entry_content, encoding='utf-8');
54
53
  }
55
54
 
@@ -59,11 +58,10 @@ impl ViteCompiler.copy_root_assets(self: ViteCompiler) -> None {
59
58
  compiled_assets_dir = self.compiled_dir / 'assets';
60
59
  if (root_assets_dir.exists() and root_assets_dir.is_dir()) {
61
60
  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);
61
+ # Copy configured custom asset types to compiled/assets/
62
+ self.asset_processor.copy_custom_asset_types(
63
+ root_assets_dir, compiled_assets_dir
64
+ );
67
65
  }
68
66
  }
69
67
 
@@ -189,7 +187,21 @@ impl ViteCompiler.compile_dependencies_recursively(
189
187
  output_path = self.compiled_dir / name;
190
188
  }
191
189
  output_path.parent.mkdir(parents=True, exist_ok=True);
192
- output_path.write_text(combined_js, encoding='utf-8');
190
+ # Add source file header comment for better error messages
191
+ source_header = f"/* Source: {module_path} */\n";
192
+ # Add ES module exports so Vite can resolve named imports between modules
193
+ module_export_names = sorted(set(exports_list + list(non_root_globals.keys())));
194
+ if module_export_names {
195
+ import re;
196
+ # Strip any existing export declarations to avoid duplicates
197
+ clean_combined_js = re.sub(r'\nexport\s*\{[^}]*\}\s*;?\s*', '\n', combined_js);
198
+ clean_combined_js = re.sub(
199
+ r'\bexport\s+(let|const|var|function|class)\b', r'\1', clean_combined_js
200
+ );
201
+ export_stmt = f"\nexport {{ {', '.join(module_export_names)} }};\n";
202
+ combined_js = clean_combined_js + export_stmt;
203
+ }
204
+ output_path.write_text(source_header + combined_js, encoding='utf-8');
193
205
  if (not manifest or not manifest.imports) {
194
206
  return;
195
207
  }
@@ -228,8 +240,29 @@ impl ViteCompiler.compile_runtime_utils(self: ViteCompiler) -> tuple[str, list[s
228
240
  runtimeutils_exports_list = self.jac_compiler.extract_exports(
229
241
  runtimeutils_manifest
230
242
  );
231
- all_exports = sorted(set((runtimeutils_exports_list + self.ROUTER_EXPORTS)));
232
- combined_runtime_utils_js = runtimeutils_js;
243
+ # Include both function exports and glob exports (e.g., useState, useEffect)
244
+ glob_names = list(runtimeutils_manifest.globals) if runtimeutils_manifest else [];
245
+ all_exports = sorted(
246
+ set(runtimeutils_exports_list + self.ROUTER_EXPORTS + glob_names)
247
+ );
248
+ # Strip all existing export statements from compiled JS to avoid duplicates.
249
+ # The codegen produces `export let`, `export function`, `export class`, and
250
+ # `export { ... }` forms. We remove the export keyword from declarations and
251
+ # remove export blocks entirely, then add one comprehensive export at the end.
252
+ import re;
253
+ clean_js = re.sub(r'\nexport\s*\{[^}]*\}\s*;?\s*', '\n', runtimeutils_js);
254
+ clean_js = re.sub(r'\bexport\s+(let|const|var|function|class)\b', r'\1', clean_js);
255
+ # Append single comprehensive ES module export for Vite to resolve @jac/runtime
256
+ export_names = [
257
+ e
258
+ for e in all_exports
259
+ if not e.startswith('_')
260
+ ];
261
+ # Also include internal names that are imported by compiled modules
262
+ internal_exports = ['__jacJsx', '__jacSpawn', '__jacCallFunction'];
263
+ all_export_names = sorted(set(export_names + internal_exports));
264
+ export_stmt = f"\nexport {{ {', '.join(all_export_names)} }};\n";
265
+ combined_runtime_utils_js = clean_js + export_stmt;
233
266
  self.compiled_dir.mkdir(parents=True, exist_ok=True);
234
267
  (self.compiled_dir / 'client_runtime.js').write_text(
235
268
  combined_runtime_utils_js, encoding='utf-8'
@@ -283,6 +316,5 @@ impl ViteCompiler.init(
283
316
  );
284
317
  self.import_processor = ImportProcessor();
285
318
  self.asset_processor = AssetProcessor();
286
- self.babel_processor = BabelProcessor(self.project_dir);
287
319
  self.vite_bundler = ViteBundler(self.project_dir, vite_output_dir, vite_minify);
288
320
  }
@@ -27,6 +27,7 @@ impl JacClientConfig.get_default_config(self: JacClientConfig) -> dict[str, Any]
27
27
  'resolve': {}
28
28
  },
29
29
  'ts': {'compilerOptions': {}, 'include': [], 'exclude': []},
30
+ 'configs': {}, # Generic config file generation: { "postcss": {...}, "tailwind": {...} }
30
31
  'package': {
31
32
  'name': '',
32
33
  'version': '1.0.0',
@@ -61,6 +62,7 @@ impl JacClientConfig.load(self: JacClientConfig) -> dict[str, Any] {
61
62
  user_config: dict[str, Any] = {
62
63
  'vite': client_config.get('vite', {}),
63
64
  'ts': client_config.get('ts', {}),
65
+ 'configs': client_config.get('configs', {}),
64
66
  'package': {
65
67
  'name': jac_config.project.name,
66
68
  'version': jac_config.project.version,
@@ -91,6 +93,12 @@ impl JacClientConfig.get_ts_config(self: JacClientConfig) -> dict[str, Any] {
91
93
  return config.get('ts', {});
92
94
  }
93
95
 
96
+ """Get generic config files to generate (postcss, tailwind, etc.)."""
97
+ impl JacClientConfig.get_configs(self: JacClientConfig) -> dict[str, Any] {
98
+ config = self.load();
99
+ return config.get('configs', {});
100
+ }
101
+
94
102
  """Save configuration back to jac.toml using core JacConfig."""
95
103
  impl JacClientConfig.save(self: JacClientConfig) -> None {
96
104
  jac_config = self.get_jac_config();