jac-client 0.2.11__py3-none-any.whl → 0.2.13__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.
@@ -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,143 @@ 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"
991
+
992
+
993
+ def test_start_dev_with_client_does_initial_compilation() -> None:
994
+ """Test that `jac start --dev` with client enabled performs initial compilation."""
995
+ import time
996
+
997
+ test_project_name = "test-start-dev-client"
998
+ with tempfile.TemporaryDirectory() as temp_dir:
999
+ original_cwd = os.getcwd()
1000
+ try:
1001
+ os.chdir(temp_dir)
1002
+ # Create a client project
1003
+ process = Popen(
1004
+ ["jac", "create", "--use", "client", test_project_name],
1005
+ stdin=PIPE,
1006
+ stdout=PIPE,
1007
+ stderr=PIPE,
1008
+ text=True,
1009
+ )
1010
+ stdout, stderr = process.communicate()
1011
+ assert process.returncode == 0
1012
+ # Change to project directory
1013
+ os.chdir(test_project_name)
1014
+ # Install dependencies
1015
+ install_process = Popen(
1016
+ ["jac", "install", "--dev"],
1017
+ stdout=PIPE,
1018
+ stderr=PIPE,
1019
+ text=True,
1020
+ )
1021
+ install_stdout, install_stderr = install_process.communicate()
1022
+ assert install_process.returncode == 0, (
1023
+ f"jac install --dev failed: {install_stderr}"
1024
+ )
1025
+ # Run jac start --dev main.jac
1026
+ process = Popen(
1027
+ ["jac", "start", "--dev", "main.jac"],
1028
+ stdout=PIPE,
1029
+ stderr=PIPE,
1030
+ text=True,
1031
+ )
1032
+ # Wait for the initial compilation message or timeout
1033
+ start_time = time.time()
1034
+ output = ""
1035
+ found_message = False
1036
+ while time.time() - start_time < 30: # 30 seconds timeout
1037
+ if process.poll() is not None:
1038
+ break
1039
+ if process.stdout is None:
1040
+ break
1041
+ line = process.stdout.readline()
1042
+ if not line:
1043
+ time.sleep(0.1)
1044
+ continue
1045
+ output += line
1046
+ if "Initial client compilation completed" in output:
1047
+ found_message = True
1048
+ break
1049
+ # Terminate the process
1050
+ process.terminate()
1051
+ try:
1052
+ process.wait(timeout=5)
1053
+ except Exception:
1054
+ process.kill()
1055
+ # Close pipes
1056
+ if process.stdout:
1057
+ process.stdout.close()
1058
+ if process.stderr:
1059
+ process.stderr.close()
1060
+ assert found_message, (
1061
+ f"Expected 'Initial client compilation completed' in output, but got: {output}"
1062
+ )
1063
+ finally:
1064
+ os.chdir(original_cwd)