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.
- jac_client/plugin/cli.jac +3 -3
- jac_client/plugin/client_runtime.cl.jac +3 -2
- jac_client/plugin/impl/client_runtime.impl.jac +17 -6
- jac_client/plugin/src/compiler.jac +4 -0
- jac_client/plugin/src/config_loader.jac +1 -0
- jac_client/plugin/src/impl/compiler.impl.jac +16 -39
- jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +74 -23
- jac_client/plugin/src/targets/desktop/sidecar/main.py +42 -23
- jac_client/plugin/src/targets/desktop_target.jac +4 -2
- jac_client/plugin/src/targets/impl/desktop_target.impl.jac +324 -112
- jac_client/plugin/src/vite_bundler.jac +18 -3
- jac_client/plugin/utils/__init__.jac +1 -0
- jac_client/plugin/utils/client_deps.jac +14 -0
- jac_client/plugin/utils/impl/client_deps.impl.jac +73 -0
- jac_client/templates/client.jacpack +0 -4
- jac_client/templates/fullstack.jacpack +0 -4
- jac_client/tests/test_cli.py +142 -0
- jac_client/tests/test_desktop_api_url.py +854 -0
- jac_client/tests/test_e2e.py +12 -12
- jac_client/tests/test_it.py +209 -11
- {jac_client-0.2.11.dist-info → jac_client-0.2.13.dist-info}/METADATA +2 -2
- {jac_client-0.2.11.dist-info → jac_client-0.2.13.dist-info}/RECORD +26 -23
- {jac_client-0.2.11.dist-info → jac_client-0.2.13.dist-info}/WHEEL +0 -0
- {jac_client-0.2.11.dist-info → jac_client-0.2.13.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.11.dist-info → jac_client-0.2.13.dist-info}/top_level.txt +0 -0
|
@@ -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
|
+
}
|
jac_client/tests/test_cli.py
CHANGED
|
@@ -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)
|