jac-client 0.2.8__py3-none-any.whl → 0.2.10__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/examples/all-in-one/{app.jac → main.jac} +5 -5
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +8 -1
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +16 -1
- jac_client/examples/all-in-one/pages/{FeaturesTest.cl.jac → features_test_ui.cl.jac} +11 -0
- jac_client/examples/all-in-one/pages/nestedDemo.jac +1 -1
- jac_client/examples/all-in-one/pages/notFound.jac +2 -7
- jac_client/plugin/cli.jac +491 -411
- jac_client/plugin/client.jac +25 -0
- jac_client/plugin/client_runtime.cl.jac +5 -1
- jac_client/plugin/impl/client.impl.jac +96 -55
- jac_client/plugin/impl/client_runtime.impl.jac +154 -0
- jac_client/plugin/plugin_config.jac +243 -15
- jac_client/plugin/src/config_loader.jac +1 -0
- jac_client/plugin/src/desktop_config.jac +31 -0
- jac_client/plugin/src/impl/compiler.impl.jac +1 -1
- jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
- jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +97 -16
- jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
- jac_client/plugin/src/targets/desktop_target.jac +37 -0
- jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2334 -0
- jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
- jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
- jac_client/plugin/src/targets/register.jac +21 -0
- jac_client/plugin/src/targets/registry.jac +87 -0
- jac_client/plugin/src/targets/web_target.jac +35 -0
- jac_client/plugin/src/vite_bundler.jac +6 -0
- jac_client/plugin/utils/__init__.jac +1 -0
- jac_client/plugin/utils/impl/node_installer.impl.jac +249 -0
- jac_client/plugin/utils/node_installer.jac +41 -0
- jac_client/templates/client.jacpack +72 -0
- jac_client/templates/fullstack.jacpack +61 -0
- jac_client/tests/conftest.py +48 -7
- jac_client/tests/test_cli.py +184 -70
- jac_client/tests/test_e2e.py +232 -0
- jac_client/tests/test_helpers.py +65 -0
- jac_client/tests/test_it.py +91 -135
- jac_client/tests/test_it_desktop.py +891 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.10.dist-info}/METADATA +4 -4
- jac_client-0.2.10.dist-info/RECORD +115 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.10.dist-info}/WHEEL +1 -1
- jac_client-0.2.8.dist-info/RECORD +0 -97
- /jac_client/examples/all-in-one/pages/{BudgetPlanner.cl.jac → budget_planner_ui.cl.jac} +0 -0
- /jac_client/examples/asset-serving/css-with-image/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/asset-serving/image-asset/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/asset-serving/import-alias/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/basic/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/basic-auth/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/basic-auth-with-router/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/basic-full-stack/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/css-styling/js-styling/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/css-styling/material-ui/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/css-styling/pure-css/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/css-styling/sass-example/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/css-styling/styled-components/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/css-styling/tailwind-example/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/full-stack-with-auth/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/little-x/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/nested-folders/nested-advance/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/ts-support/{src/app.jac → main.jac} +0 -0
- /jac_client/examples/with-router/{src/app.jac → main.jac} +0 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.10.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.8.dist-info → jac_client-0.2.10.dist-info}/top_level.txt +0 -0
|
@@ -78,6 +78,8 @@ impl ViteBundler.create_package_json(
|
|
|
78
78
|
}
|
|
79
79
|
# Generate tsconfig.json during build time
|
|
80
80
|
self.create_tsconfig();
|
|
81
|
+
# Generate config files (postcss, tailwind, etc.) from jac.toml
|
|
82
|
+
self.create_config_files();
|
|
81
83
|
return package_json_path;
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -375,6 +377,8 @@ impl ViteBundler.find_bundle(self: ViteBundler) -> Optional[Path] {
|
|
|
375
377
|
|
|
376
378
|
"""Run Vite build with generated config in .jac/client/configs/."""
|
|
377
379
|
impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) -> None {
|
|
380
|
+
import sys;
|
|
381
|
+
import time;
|
|
378
382
|
self.output_dir.mkdir(parents=True, exist_ok=True);
|
|
379
383
|
generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
|
|
380
384
|
if not generated_package_json.exists() {
|
|
@@ -395,18 +399,31 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
|
|
|
395
399
|
shutil.copy2(configs_package_json, build_package_json);
|
|
396
400
|
}
|
|
397
401
|
try {
|
|
398
|
-
# Install to .jac/client/node_modules
|
|
399
|
-
|
|
400
|
-
|
|
402
|
+
# Install to .jac/client/node_modules with progress feedback
|
|
403
|
+
print(
|
|
404
|
+
" ⏳ Installing npm dependencies (this may take a minute)...",
|
|
405
|
+
flush=True
|
|
406
|
+
);
|
|
407
|
+
start_time = time.time();
|
|
408
|
+
result = subprocess.run(
|
|
409
|
+
['npm', 'install', '--progress'],
|
|
401
410
|
cwd=build_dir,
|
|
402
|
-
check=
|
|
411
|
+
check=False,
|
|
403
412
|
capture_output=True,
|
|
404
413
|
text=True
|
|
405
414
|
);
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
415
|
+
elapsed = time.time() - start_time;
|
|
416
|
+
if result.returncode != 0 {
|
|
417
|
+
print(
|
|
418
|
+
f"\n ✖ npm install failed after {elapsed:.1f}s",
|
|
419
|
+
file=sys.stderr
|
|
420
|
+
);
|
|
421
|
+
raise ClientBundleError(
|
|
422
|
+
f"Failed to install npm dependencies: {result.stderr
|
|
423
|
+
or result.stdout}"
|
|
424
|
+
) ;
|
|
425
|
+
}
|
|
426
|
+
print(f" ✔ Dependencies installed ({elapsed:.1f}s)", flush=True);
|
|
410
427
|
} except FileNotFoundError {
|
|
411
428
|
raise None from ClientBundleError(
|
|
412
429
|
'npm command not found. Ensure Node.js and npm are installed.'
|
|
@@ -431,15 +448,20 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
|
|
|
431
448
|
command = ['npm', 'run', 'build'];
|
|
432
449
|
}
|
|
433
450
|
# Run vite from client build directory so it can find node_modules
|
|
451
|
+
print(" ⏳ Building client bundle...", flush=True);
|
|
452
|
+
start_time = time.time();
|
|
434
453
|
result = subprocess.run(
|
|
435
454
|
command, cwd=build_dir, check=False, capture_output=True, text=True
|
|
436
455
|
);
|
|
456
|
+
elapsed = time.time() - start_time;
|
|
437
457
|
if result.returncode != 0 {
|
|
458
|
+
print(f"\n ✖ Vite build failed after {elapsed:.1f}s", file=sys.stderr);
|
|
438
459
|
error_msg = result.stderr or result.stdout or 'Unknown error';
|
|
439
460
|
raise ClientBundleError(
|
|
440
461
|
f"Vite build failed:\n{error_msg}\nCommand: {' '.join(command)}"
|
|
441
462
|
) from None ;
|
|
442
463
|
}
|
|
464
|
+
print(f" ✔ Client bundle built ({elapsed:.1f}s)", flush=True);
|
|
443
465
|
} finally {
|
|
444
466
|
# Clean up temporary package.json in client build dir
|
|
445
467
|
build_package_json = build_dir / 'package.json';
|
|
@@ -558,8 +580,54 @@ export default defineConfig({{
|
|
|
558
580
|
return config_path;
|
|
559
581
|
}
|
|
560
582
|
|
|
583
|
+
"""Create config files from jac.toml [plugins.client.configs].
|
|
584
|
+
|
|
585
|
+
Generates JavaScript config files (e.g., postcss.config.js, tailwind.config.js)
|
|
586
|
+
from TOML configuration. Each key in [plugins.client.configs] becomes a config file.
|
|
587
|
+
|
|
588
|
+
Example jac.toml:
|
|
589
|
+
[plugins.client.configs.postcss]
|
|
590
|
+
plugins = ["tailwindcss", "autoprefixer"]
|
|
591
|
+
|
|
592
|
+
[plugins.client.configs.tailwind]
|
|
593
|
+
content = ["./src/**/*.{js,jsx}"]
|
|
594
|
+
|
|
595
|
+
This generates:
|
|
596
|
+
- .jac/client/configs/postcss.config.js
|
|
597
|
+
- .jac/client/configs/tailwind.config.js
|
|
598
|
+
"""
|
|
599
|
+
impl ViteBundler.create_config_files(self: ViteBundler) -> list[Path] {
|
|
600
|
+
configs = self.config_loader.get_configs();
|
|
601
|
+
if not configs {
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
604
|
+
build_dir = self._get_client_dir();
|
|
605
|
+
configs_dir = build_dir / 'configs';
|
|
606
|
+
configs_dir.mkdir(parents=True, exist_ok=True);
|
|
607
|
+
created_files: list[Path] = [];
|
|
608
|
+
for (config_name, config_data) in configs.items() {
|
|
609
|
+
config_path = configs_dir / f'{config_name}.config.js';
|
|
610
|
+
|
|
611
|
+
# Convert the TOML config to JavaScript module.exports
|
|
612
|
+
js_content = _toml_config_to_js(config_name, config_data);
|
|
613
|
+
config_path.write_text(js_content, encoding='utf-8');
|
|
614
|
+
created_files.append(config_path);
|
|
615
|
+
}
|
|
616
|
+
return created_files;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
"""Convert TOML config data to JavaScript config file content.
|
|
620
|
+
|
|
621
|
+
Generates a generic module.exports with the config data as JSON.
|
|
622
|
+
"""
|
|
623
|
+
def _toml_config_to_js(config_name: str, config_data: dict) -> str {
|
|
624
|
+
return f"module.exports = {json.dumps(config_data, indent=2)};\n";
|
|
625
|
+
}
|
|
626
|
+
|
|
561
627
|
"""Start Vite dev server as a subprocess."""
|
|
562
628
|
impl ViteBundler.start_dev_server(self: ViteBundler, port: int = 3000) -> Any {
|
|
629
|
+
import sys;
|
|
630
|
+
import time;
|
|
563
631
|
build_dir = self._get_client_dir();
|
|
564
632
|
node_modules = build_dir / 'node_modules';
|
|
565
633
|
# Create/update index.html for dev server (load from compiled/ for HMR)
|
|
@@ -590,17 +658,30 @@ impl ViteBundler.start_dev_server(self: ViteBundler, port: int = 3000) -> Any {
|
|
|
590
658
|
shutil.copy2(generated_package_json, build_package_json);
|
|
591
659
|
}
|
|
592
660
|
try {
|
|
593
|
-
print(
|
|
594
|
-
|
|
595
|
-
|
|
661
|
+
print(
|
|
662
|
+
" ⏳ Installing npm dependencies (this may take a minute)...",
|
|
663
|
+
flush=True
|
|
664
|
+
);
|
|
665
|
+
start_time = time.time();
|
|
666
|
+
result = subprocess.run(
|
|
667
|
+
['npm', 'install', '--progress'],
|
|
596
668
|
cwd=build_dir,
|
|
597
|
-
check=
|
|
669
|
+
check=False,
|
|
598
670
|
capture_output=True,
|
|
599
671
|
text=True
|
|
600
672
|
);
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
673
|
+
elapsed = time.time() - start_time;
|
|
674
|
+
if result.returncode != 0 {
|
|
675
|
+
print(
|
|
676
|
+
f"\n ✖ npm install failed after {elapsed:.1f}s", file=sys.stderr
|
|
677
|
+
);
|
|
678
|
+
print(f" Error: {result.stderr or result.stdout}", file=sys.stderr);
|
|
679
|
+
raise ClientBundleError(
|
|
680
|
+
f"Failed to install npm dependencies: {result.stderr
|
|
681
|
+
or result.stdout}"
|
|
682
|
+
) ;
|
|
683
|
+
}
|
|
684
|
+
print(f" ✔ Dependencies installed ({elapsed:.1f}s)", flush=True);
|
|
604
685
|
} finally {
|
|
605
686
|
# Clean up temp package.json
|
|
606
687
|
if build_package_json.exists() {
|
|
@@ -616,7 +697,7 @@ impl ViteBundler.start_dev_server(self: ViteBundler, port: int = 3000) -> Any {
|
|
|
616
697
|
) ;
|
|
617
698
|
}
|
|
618
699
|
config_rel = dev_config.relative_to(build_dir);
|
|
619
|
-
|
|
700
|
+
logger.debug(f"Starting Vite dev server on port {port}");
|
|
620
701
|
# Start Vite in dev mode (let output go to terminal for HMR visibility)
|
|
621
702
|
process = subprocess.Popen(
|
|
622
703
|
['npx', 'vite', '--config', str(config_rel), '--port', str(port)],
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Jac Sidecar Entry Point
|
|
4
|
+
|
|
5
|
+
This is the entry point for the Jac backend sidecar.
|
|
6
|
+
It launches the Jac runtime and starts an HTTP API server.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python -m jac_client.plugin.src.targets.desktop.sidecar.main [OPTIONS]
|
|
10
|
+
# Or via wrapper script: ./jac-sidecar.sh [OPTIONS]
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--module-path PATH Path to the .jac module file (default: main.jac)
|
|
14
|
+
--port PORT Port to bind the API server (default: 8000)
|
|
15
|
+
--base-path PATH Base path for the project (default: current directory)
|
|
16
|
+
--host HOST Host to bind to (default: 127.0.0.1)
|
|
17
|
+
--help Show this help message
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
from jaclang.cli.console import console
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
"""Main entry point for the sidecar."""
|
|
31
|
+
parser = argparse.ArgumentParser(
|
|
32
|
+
description="Jac Backend Sidecar - Runs Jac API server in a bundled executable"
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--module-path",
|
|
36
|
+
type=str,
|
|
37
|
+
default="main.jac",
|
|
38
|
+
help="Path to the .jac module file (default: main.jac)",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--port",
|
|
42
|
+
type=int,
|
|
43
|
+
default=8000,
|
|
44
|
+
help="Port to bind the API server (default: 8000)",
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--base-path",
|
|
48
|
+
type=str,
|
|
49
|
+
default=None,
|
|
50
|
+
help="Base path for the project (default: current directory)",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--host",
|
|
54
|
+
type=str,
|
|
55
|
+
default="127.0.0.1",
|
|
56
|
+
help="Host to bind to (default: 127.0.0.1)",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
|
|
61
|
+
# Determine base path
|
|
62
|
+
if args.base_path:
|
|
63
|
+
base_path = Path(args.base_path).resolve()
|
|
64
|
+
else:
|
|
65
|
+
# Try to find project root (look for jac.toml)
|
|
66
|
+
base_path = Path.cwd()
|
|
67
|
+
for parent in [base_path] + list(base_path.parents):
|
|
68
|
+
if (parent / "jac.toml").exists():
|
|
69
|
+
base_path = parent
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
# Resolve module path
|
|
73
|
+
module_path = Path(args.module_path)
|
|
74
|
+
if not module_path.is_absolute():
|
|
75
|
+
module_path = base_path / module_path
|
|
76
|
+
|
|
77
|
+
if not module_path.exists():
|
|
78
|
+
console.print(f"Error: Module file not found: {module_path}", file=sys.stderr)
|
|
79
|
+
console.print(f" Base path: {base_path}", file=sys.stderr)
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
# Extract module name (without .jac extension)
|
|
83
|
+
module_name = module_path.stem
|
|
84
|
+
module_base = module_path.parent
|
|
85
|
+
|
|
86
|
+
# Import Jac runtime and server
|
|
87
|
+
try:
|
|
88
|
+
# Import jaclang (must be installed via pip)
|
|
89
|
+
from jaclang.pycore.runtime import JacRuntime as Jac
|
|
90
|
+
except ImportError as e:
|
|
91
|
+
console.print(f"Error: Failed to import Jac runtime: {e}", file=sys.stderr)
|
|
92
|
+
console.print(
|
|
93
|
+
" Make sure jaclang is installed: pip install jaclang", file=sys.stderr
|
|
94
|
+
)
|
|
95
|
+
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
# Initialize Jac runtime
|
|
98
|
+
try:
|
|
99
|
+
# Import the module
|
|
100
|
+
Jac.jac_import(target=module_name, base_path=str(module_base), lng="jac")
|
|
101
|
+
if Jac.program.errors_had:
|
|
102
|
+
console.print("Error: Failed to compile module:", file=sys.stderr)
|
|
103
|
+
for error in Jac.program.errors_had:
|
|
104
|
+
console.print(f" {error}", file=sys.stderr)
|
|
105
|
+
sys.exit(1)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
console.print(
|
|
108
|
+
f"Error: Failed to load module '{module_name}': {e}", file=sys.stderr
|
|
109
|
+
)
|
|
110
|
+
import traceback
|
|
111
|
+
|
|
112
|
+
traceback.print_exc()
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
# Create and start the API server
|
|
116
|
+
try:
|
|
117
|
+
# Get server class (allows plugins like jac-scale to provide enhanced server)
|
|
118
|
+
server_class = Jac.get_api_server_class()
|
|
119
|
+
server = server_class(
|
|
120
|
+
module_name=module_name, port=args.port, base_path=str(base_path)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
console.print("Jac Sidecar starting...")
|
|
124
|
+
console.print(f" Module: {module_name}")
|
|
125
|
+
console.print(f" Base path: {base_path}")
|
|
126
|
+
console.print(f" Server: http://{args.host}:{args.port}")
|
|
127
|
+
console.print("\nPress Ctrl+C to stop the server\n")
|
|
128
|
+
|
|
129
|
+
# Start the server (blocks until interrupted)
|
|
130
|
+
server.start(dev=False)
|
|
131
|
+
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
console.print("\nShutting down sidecar...")
|
|
134
|
+
sys.exit(0)
|
|
135
|
+
except Exception as e:
|
|
136
|
+
console.print(f"Error: Server failed to start: {e}", file=sys.stderr)
|
|
137
|
+
import traceback
|
|
138
|
+
|
|
139
|
+
traceback.print_exc()
|
|
140
|
+
sys.exit(1)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
main()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Desktop target implementation.
|
|
2
|
+
|
|
3
|
+
This target will be implemented in Phase 2.
|
|
4
|
+
"""
|
|
5
|
+
import from pathlib { Path }
|
|
6
|
+
import from typing { Optional }
|
|
7
|
+
import from jac_client.plugin.src.targets.registry { ClientTarget }
|
|
8
|
+
|
|
9
|
+
"""Desktop build target (placeholder for Phase 2)."""
|
|
10
|
+
class DesktopTarget(ClientTarget) {
|
|
11
|
+
def init(self: DesktopTarget) {
|
|
12
|
+
self.name = "desktop";
|
|
13
|
+
self.default = False;
|
|
14
|
+
self.requires_setup = True;
|
|
15
|
+
self.config_section = "desktop";
|
|
16
|
+
self.output_dir = Path("src-tauri/target/release/bundle");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
"""Setup desktop target - scaffold Tauri project structure."""
|
|
20
|
+
override def setup(self: DesktopTarget, project_dir: Path) -> None;
|
|
21
|
+
|
|
22
|
+
"""Build desktop app - build web bundle first, then wrap with Tauri."""
|
|
23
|
+
override def build(
|
|
24
|
+
self: DesktopTarget,
|
|
25
|
+
entry_file: Path,
|
|
26
|
+
project_dir: Path,
|
|
27
|
+
platform: Optional[str] = None
|
|
28
|
+
) -> Path;
|
|
29
|
+
|
|
30
|
+
"""Start desktop dev server - start web dev server and launch tauri dev."""
|
|
31
|
+
override def dev(self: DesktopTarget, entry_file: Path, project_dir: Path) -> None;
|
|
32
|
+
|
|
33
|
+
"""Start desktop app - build web bundle and launch Tauri with built bundle."""
|
|
34
|
+
override def start(
|
|
35
|
+
self: DesktopTarget, entry_file: Path, project_dir: Path
|
|
36
|
+
) -> None;
|
|
37
|
+
}
|