jac-client 0.2.11__py3-none-any.whl → 0.2.12__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.
@@ -426,22 +426,29 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
426
426
  import sys;
427
427
  import time;
428
428
  import shutil;
429
- import from jac_client.plugin.utils { ensure_bun_available }
429
+ import from jac_client.plugin.utils { ensure_bun_available, ensure_client_deps }
430
430
  # Ensure bun is available before proceeding
431
431
  if not ensure_bun_available() {
432
432
  raise ClientBundleError('Bun is required. Install manually: https://bun.sh') from None ;
433
433
  }
434
- self.output_dir.mkdir(parents=True, exist_ok=True);
435
- generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
436
- if not generated_package_json.exists() {
437
- self.create_package_json();
438
- } else {
439
- # Ensure tsconfig.json exists even if package.json already exists
440
- self.create_tsconfig();
434
+ # Ensure client npm deps are configured; prompt to install defaults if missing
435
+ if not ensure_client_deps(self.config_loader) {
436
+ raise ClientBundleError(
437
+ 'Client dependencies not configured. Add [dependencies.npm] to jac.toml '
438
+ 'or create a project with: jac create --use client'
439
+ ) from None ;
441
440
  }
441
+ self.output_dir.mkdir(parents=True, exist_ok=True);
442
+ # Always regenerate package.json to pick up any dependency changes
443
+ self.create_package_json();
442
444
  try {
443
445
  build_dir = self._get_client_dir();
444
446
  node_modules = build_dir / 'node_modules';
447
+ # Reinstall if node_modules is missing or stale (e.g. empty from a prior failed install)
448
+ vite_bin = node_modules / '.bin' / 'vite';
449
+ if node_modules.exists() and not vite_bin.exists() {
450
+ shutil.rmtree(node_modules);
451
+ }
445
452
  if not node_modules.exists() {
446
453
  # Temporarily copy package.json to client build dir for bun install
447
454
  build_package_json = build_dir / 'package.json';
@@ -455,15 +462,17 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
455
462
  print("\n ⏳ Installing dependencies...\n", flush=True);
456
463
  start_time = time.time();
457
464
  result = subprocess.run(
458
- ['bun', 'install'], cwd=build_dir, check=False, text=True
465
+ ['bun', 'install'],
466
+ cwd=build_dir,
467
+ check=False,
468
+ text=True,
469
+ capture_output=True
459
470
  );
460
471
  elapsed = time.time() - start_time;
461
472
  if result.returncode != 0 {
462
- print(
463
- f"\n ✖ bun install failed after {elapsed:.1f}s",
464
- file=sys.stderr
465
- );
466
- raise ClientBundleError("Failed to install dependencies") ;
473
+ error_output = result.stderr or result.stdout;
474
+ error_msg = f"Dependency installation failed after {elapsed:.1f}s\n\n{error_output}\nCommand: bun install";
475
+ raise ClientBundleError(error_msg) from None ;
467
476
  }
468
477
  print(f"\n ✔ Dependencies installed ({elapsed:.1f}s)", flush=True);
469
478
  } except FileNotFoundError {
@@ -500,13 +509,16 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
500
509
  # Run vite from client build directory so it can find node_modules
501
510
  print("\n ⏳ Building client bundle...\n", flush=True);
502
511
  start_time = time.time();
503
- result = subprocess.run(command, cwd=build_dir, check=False, text=True);
512
+ result = subprocess.run(
513
+ command, cwd=build_dir, check=False, text=True, capture_output=True
514
+ );
504
515
  elapsed = time.time() - start_time;
505
516
  if result.returncode != 0 {
506
- print(f"\n ✖ Vite build failed after {elapsed:.1f}s", file=sys.stderr);
507
- raise ClientBundleError(
508
- f"Vite build failed (see output above)\nCommand: {' '.join(command)}"
509
- ) from None ;
517
+ error_output = result.stderr or result.stdout;
518
+ error_msg = f"Vite build failed after {elapsed:.1f}s\n\n{error_output}\nCommand: {' '.join(
519
+ command
520
+ )}";
521
+ raise ClientBundleError(error_msg) from None ;
510
522
  }
511
523
  print(f"\n ✔ Client bundle built ({elapsed:.1f}s)", flush=True);
512
524
  } finally {
@@ -615,6 +627,10 @@ export default defineConfig({{
615
627
  target: "http://localhost:{api_port}",
616
628
  changeOrigin: true,
617
629
  }},
630
+ "/static": {{
631
+ target: "http://localhost:{api_port}",
632
+ changeOrigin: true,
633
+ }},
618
634
  }},
619
635
  }},
620
636
  resolve: {{
@@ -1,3 +1,4 @@
1
1
  """Utility modules for jac-client plugin."""
2
2
 
3
3
  import from .bun_installer { ensure_bun_available, prompt_install_bun }
4
+ import from .client_deps { ensure_client_deps }
@@ -0,0 +1,14 @@
1
+ """Client dependency checker utility for jac-client.
2
+
3
+ Provides a function to check if required npm dependencies are configured
4
+ and prompt the user to install defaults if missing.
5
+ """
6
+
7
+ """Check if client npm dependencies are configured, prompt to install if missing.
8
+
9
+ Takes a JacClientConfig instance, checks if dependencies and devDependencies
10
+ are both empty, and if so prompts the user to install default jac-client deps.
11
+
12
+ Returns True if deps are configured (or were just installed), False if user declined.
13
+ """
14
+ def ensure_client_deps(config_loader: object) -> bool;
@@ -0,0 +1,73 @@
1
+ """Implementation of client dependency checker utility."""
2
+
3
+ """Check if client npm dependencies are configured, prompt to install if missing."""
4
+ impl ensure_client_deps(config_loader: object) -> bool {
5
+ package_config = config_loader.get_package_config();
6
+ dependencies = package_config.get('dependencies', {});
7
+ dev_dependencies = package_config.get('devDependencies', {});
8
+ # If either deps or devDeps has entries, assume configured
9
+ if dependencies or dev_dependencies {
10
+ return True;
11
+ }
12
+ # No deps configured — prompt the user
13
+ return prompt_install_client_deps(config_loader);
14
+ }
15
+
16
+ """Prompt user to install default jac-client npm dependencies."""
17
+ def prompt_install_client_deps(config_loader: object) -> bool {
18
+ import sys;
19
+ print("\n ⚠ Client dependencies are not configured.", file=sys.stderr);
20
+ print(
21
+ " jac-client requires npm packages (react, vite, etc.) to build client pages.",
22
+ file=sys.stderr
23
+ );
24
+ print(" These will be added to your jac.toml file.\n", file=sys.stderr);
25
+ try {
26
+ response = input(" Install default client dependencies now? [Y/n]: ").strip().lower();
27
+ } except (EOFError, KeyboardInterrupt) {
28
+ print("", file=sys.stderr);
29
+ return False;
30
+ }
31
+ if response and response not in ('y', 'yes', '') {
32
+ print(
33
+ "\n To configure manually, add [dependencies.npm] to your jac.toml.",
34
+ file=sys.stderr
35
+ );
36
+ print(
37
+ " Or create a new project with: jac create --use client\n",
38
+ file=sys.stderr
39
+ );
40
+ return False;
41
+ }
42
+ # Default runtime dependencies
43
+ default_deps: dict[str, str] = {
44
+ 'react': '^18.2.0',
45
+ 'react-dom': '^18.2.0',
46
+ 'react-router-dom': '^6.22.0',
47
+ 'react-error-boundary': '^5.0.0'
48
+ };
49
+ # Default build infrastructure dependencies
50
+ default_dev_deps: dict[str, str] = {
51
+ 'vite': '^6.4.1',
52
+ '@vitejs/plugin-react': '^4.2.1',
53
+ 'typescript': '^5.3.3',
54
+ '@types/react': '^18.2.0',
55
+ '@types/react-dom': '^18.2.0'
56
+ };
57
+ print("\n ⏳ Adding default client dependencies to jac.toml...", flush=True);
58
+ try {
59
+ for (name, version) in default_deps.items() {
60
+ config_loader.add_dependency(name, version, is_dev=False);
61
+ }
62
+ for (name, version) in default_dev_deps.items() {
63
+ config_loader.add_dependency(name, version, is_dev=True);
64
+ }
65
+ config_loader.save();
66
+ config_loader.invalidate();
67
+ print(" ✔ Default client dependencies added to jac.toml\n", flush=True);
68
+ return True;
69
+ } except Exception as e {
70
+ print(f" ✖ Failed to add dependencies: {e}", file=sys.stderr);
71
+ return False;
72
+ }
73
+ }
@@ -33,10 +33,6 @@
33
33
  "assets",
34
34
  ".jac/client"
35
35
  ],
36
- "gitignore_entries": [
37
- "# Ignore all build artifacts in .jac directory",
38
- "*"
39
- ],
40
36
  "root_gitignore_entries": [
41
37
  "# Jac project",
42
38
  "packages/",
@@ -36,10 +36,6 @@
36
36
  ".jac",
37
37
  "assets"
38
38
  ],
39
- "gitignore_entries": [
40
- "# Ignore all build artifacts in .jac directory",
41
- "*"
42
- ],
43
39
  "root_gitignore_entries": [
44
40
  "# Jac build artifacts",
45
41
  ".jac/",
@@ -1,9 +1,11 @@
1
1
  """Test create-jac-app command."""
2
2
 
3
+ import contextlib
3
4
  import os
4
5
  import tempfile
5
6
  import tomllib
6
7
  from subprocess import PIPE, Popen, run
8
+ from unittest.mock import patch
7
9
 
8
10
 
9
11
  def test_create_jac_app() -> None:
@@ -920,3 +922,69 @@ def test_create_cl_and_run_no_root_files() -> None:
920
922
 
921
923
  finally:
922
924
  os.chdir(original_cwd)
925
+
926
+
927
+ def test_vite_build_prompts_for_missing_client_deps() -> None:
928
+ """Test that ViteBundler.build() prompts to install deps when jac.toml is missing.
929
+
930
+ Exercises the same code path as `jac start` → server.start() → ensure_bundle()
931
+ → ViteBundler.build(), which checks for npm deps before building.
932
+ """
933
+ import json
934
+ from pathlib import Path
935
+
936
+ from jac_client.plugin.src.vite_bundler import ViteBundler
937
+
938
+ with tempfile.TemporaryDirectory() as temp_dir:
939
+ project_dir = Path(temp_dir)
940
+ config_file = project_dir / "jac.toml"
941
+
942
+ # No jac.toml — ViteBundler.build() should prompt via ensure_client_deps
943
+ bundler = ViteBundler(project_dir)
944
+
945
+ # Mock input to accept, and mock bun/vite so we don't need real installs.
946
+ # We only care that ensure_client_deps wrote jac.toml before reaching bun.
947
+ with (
948
+ patch("builtins.input", return_value="Y"),
949
+ patch(
950
+ "jac_client.plugin.utils.bun_installer.ensure_bun_available",
951
+ return_value=True,
952
+ ),
953
+ patch("subprocess.run") as mock_subprocess,
954
+ ):
955
+ # bun install returns success, vite build returns failure
956
+ # (we don't have real vite — that's fine, we're testing the dep prompt)
957
+ mock_subprocess.side_effect = [
958
+ type("Result", (), {"returncode": 0})(), # bun install
959
+ type("Result", (), {"returncode": 1})(), # vite build
960
+ ]
961
+ with contextlib.suppress(Exception):
962
+ bundler.build() # vite build failure is expected
963
+
964
+ # The key assertion: jac.toml was created with default client deps
965
+ assert config_file.exists(), (
966
+ "jac.toml should have been created after accepting the prompt"
967
+ )
968
+
969
+ with open(config_file, "rb") as f:
970
+ data = tomllib.load(f)
971
+
972
+ npm_deps = data.get("dependencies", {}).get("npm", {})
973
+ assert "react" in npm_deps, "react should be in dependencies.npm"
974
+ assert "react-dom" in npm_deps, "react-dom should be in dependencies.npm"
975
+
976
+ npm_dev = npm_deps.get("dev", {})
977
+ assert "vite" in npm_dev, "vite should be in dev dependencies"
978
+ assert "@vitejs/plugin-react" in npm_dev, (
979
+ "@vitejs/plugin-react should be in dev deps"
980
+ )
981
+
982
+ # Verify the generated package.json also picked up the deps
983
+ package_json = project_dir / ".jac" / "client" / "configs" / "package.json"
984
+ assert package_json.exists(), "package.json should have been generated"
985
+
986
+ with open(package_json) as f:
987
+ pkg = json.load(f)
988
+
989
+ assert pkg["dependencies"].get("react"), "package.json should have react"
990
+ assert pkg["devDependencies"].get("vite"), "package.json should have vite"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jac-client
3
- Version: 0.2.11
3
+ Version: 0.2.12
4
4
  Summary: Build full-stack web applications with Jac - one language for frontend and backend.
5
5
  Author-email: Jason Mars <jason@mars.ninja>
6
6
  Maintainer-email: Jason Mars <jason@mars.ninja>
@@ -11,7 +11,7 @@ Project-URL: Documentation, https://jac-lang.org
11
11
  Keywords: jac,jaclang,jaseci,frontend,full-stack,web-development
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
- Requires-Dist: jaclang>=0.9.11
14
+ Requires-Dist: jaclang>=0.9.12
15
15
  Provides-Extra: dev
16
16
  Requires-Dist: python-dotenv==1.0.1; extra == "dev"
17
17
  Requires-Dist: pytest==8.3.5; extra == "dev"
@@ -75,7 +75,7 @@ jac_client/plugin/src/impl/desktop_config.impl.jac,sha256=mitiS3RfHjMstUqPJs2OnB
75
75
  jac_client/plugin/src/impl/import_processor.impl.jac,sha256=BMkkZXlF-6jbC-r3tuFj8PDFvDZrbfzuhnaw8qraWIQ,1214
76
76
  jac_client/plugin/src/impl/jac_to_js.impl.jac,sha256=KHblc0PloMJqsX2DRdp3tyJ0k5d39ArPxBLKXsw7mjA,1692
77
77
  jac_client/plugin/src/impl/package_installer.impl.jac,sha256=YbXlzqmrzNtNSyfDcVY3-975Pqbc9H9wxQ1LQxO6ypE,4020
78
- jac_client/plugin/src/impl/vite_bundler.impl.jac,sha256=KFEuPqqAAgS1IuWPBxGT5T9Rib6n0vbL3UcMX9UM-uE,28805
78
+ jac_client/plugin/src/impl/vite_bundler.impl.jac,sha256=DqFgC2vkAw5udxPv1qN3WqOx_KiVLWCjrSibUaSnFhM,29538
79
79
  jac_client/plugin/src/targets/desktop_target.jac,sha256=TxDdpActsLAvrOZ_QpeQ1F22PehctAraz3zuo4CZ0Lk,1286
80
80
  jac_client/plugin/src/targets/register.jac,sha256=tiHmOKEgOWlYdY5DYko6M0Ktp2kZr7TZFWKq18Tmp24,835
81
81
  jac_client/plugin/src/targets/registry.jac,sha256=wl1QJEWAyI7KAfTSjIw3PdxpYgtUCpaj7D0c09jZxFM,2664
@@ -84,14 +84,16 @@ jac_client/plugin/src/targets/desktop/sidecar/main.py,sha256=PVmHvXg2Gta2UzD1O3H
84
84
  jac_client/plugin/src/targets/impl/desktop_target.impl.jac,sha256=7a-lem6Qvlghl6MPiT8_YYYw73xExsrgulir8S-t5Ls,83003
85
85
  jac_client/plugin/src/targets/impl/registry.impl.jac,sha256=2PUw_cZ9Us6PJRxO9o4aSOwYW_5_VEi6WXbIVS_6fN8,1691
86
86
  jac_client/plugin/src/targets/impl/web_target.impl.jac,sha256=fQQ1zahAnvsD4BFr8JrbF52xZxSJL_K3yE0litbJdBg,5110
87
- jac_client/plugin/utils/__init__.jac,sha256=1s-8qMGXIdGfhlZD4XXb2lHcN9M6pIGllTK-AyHdeH0,118
87
+ jac_client/plugin/utils/__init__.jac,sha256=u_rH9sxR8EgLUsbaNpv0tgYmt5sLlrb41LhPRk0w0g4,166
88
88
  jac_client/plugin/utils/bun_installer.jac,sha256=irvOnt2WF6sB_aOH5v6L9Jde-NoP39JDjysXB2V0nv0,473
89
+ jac_client/plugin/utils/client_deps.jac,sha256=BLJ7UaBi2aC-V-Vl4IsfGYfnRI75QtaO1JNYo_ut0UI,569
89
90
  jac_client/plugin/utils/impl/bun_installer.impl.jac,sha256=S6yqaxNvQ6HYgSfO7bRu8NiNsYvG8mvK8j4d1_Vf_FM,3483
90
- jac_client/templates/client.jacpack,sha256=7Atks2Swszn-0Amcl7LfQWXuCw4sOoGOSArMKlKzGmI,4279
91
- jac_client/templates/fullstack.jacpack,sha256=wVHTQw13fWI_hy8gndteWL0ht9VidygupJ8dgNpdQ1g,23201
91
+ jac_client/plugin/utils/impl/client_deps.impl.jac,sha256=u2APi7hYTTCzDuiG2wTlsM4Dfi1QPY9FNO6xAIq_gPw,2774
92
+ jac_client/templates/client.jacpack,sha256=o3aVPZTI1T52gvLHv5bGK0czX8cAx0GgWfMjQP5yA3w,4187
93
+ jac_client/templates/fullstack.jacpack,sha256=qS5vlhHjpInxVFkAD8T0FftqWFVW6hAbexl9LufW0eE,23109
92
94
  jac_client/tests/__init__.py,sha256=HSr78dvKyvNmP7bLmAssfYItAn4puFwAe1oWnV8l7pA,36
93
95
  jac_client/tests/conftest.py,sha256=PU-eYPagFRXSKeXaGadES3s2LlNjzy5VSsXuUPse8_4,11136
94
- jac_client/tests/test_cli.py,sha256=lK2LlwqxzmZse-ewlaaMTqDkjwispMsVa9iEqCjilj4,32452
96
+ jac_client/tests/test_cli.py,sha256=EWpvBNgUfKk7RW5TW1wkM8g6WD0vDRsF3B7mFiOdG48,35318
95
97
  jac_client/tests/test_e2e.py,sha256=MqMXiUnLl8qwsW-OQFN1GK-5hbCKKzxT2SewmVWEsow,8197
96
98
  jac_client/tests/test_helpers.py,sha256=UqXYIl0Vi_8IDJ1efsJh1BcaJPudINfpLojhwDxxtNg,1615
97
99
  jac_client/tests/test_it.py,sha256=Wy8VeBLcchXZFDHUBoMEWeU4oUXxDwvWVCnMPvxlnFQ,37825
@@ -106,8 +108,8 @@ jac_client/tests/fixtures/relative_import/button.jac,sha256=kCDNaHEcxMEFdezfnecy
106
108
  jac_client/tests/fixtures/spawn_test/app.jac,sha256=cooYrB7xhHMk9xWOZ6MIiY4I6kmodW2v-nyVnd1RR74,3863
107
109
  jac_client/tests/fixtures/test_fragments_spread/app.jac,sha256=CMzAz4Ydx_5eAV82-io8sJdy8_xy-mR7YOVc7FcozlU,1277
108
110
  jac_client/tests/fixtures/with-ts/app.jac,sha256=1j1IH_4mfgLtgPq2Xc5PrGHBvmLQl6U0ddbb2L5KwuA,981
109
- jac_client-0.2.11.dist-info/METADATA,sha256=OKfil5EIrTpafAYyKF_cj6Q7DSAN2jkhZ_ic35rEAcQ,5552
110
- jac_client-0.2.11.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
111
- jac_client-0.2.11.dist-info/entry_points.txt,sha256=fVrlaJKcSa2DK2hcfR6bNaQDB9mszMpZeEa6pitMdt4,154
112
- jac_client-0.2.11.dist-info/top_level.txt,sha256=u1VEBfiqwRrZEopKraIh-Ym55qSnDZR3Q5xdw2HinhU,11
113
- jac_client-0.2.11.dist-info/RECORD,,
111
+ jac_client-0.2.12.dist-info/METADATA,sha256=odF-aHda-UyjLXi5xA460DkljU5gE98qB0PdEL1hIds,5552
112
+ jac_client-0.2.12.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
113
+ jac_client-0.2.12.dist-info/entry_points.txt,sha256=fVrlaJKcSa2DK2hcfR6bNaQDB9mszMpZeEa6pitMdt4,154
114
+ jac_client-0.2.12.dist-info/top_level.txt,sha256=u1VEBfiqwRrZEopKraIh-Ym55qSnDZR3Q5xdw2HinhU,11
115
+ jac_client-0.2.12.dist-info/RECORD,,