jac-client 0.2.6__py3-none-any.whl → 0.2.8__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 +573 -0
- jac_client/examples/all-in-one/components/CategoryFilter.jac +35 -0
- jac_client/examples/all-in-one/components/Header.jac +13 -0
- jac_client/examples/all-in-one/components/ProfitOverview.jac +50 -0
- jac_client/examples/all-in-one/components/Summary.jac +53 -0
- jac_client/examples/all-in-one/components/TransactionForm.jac +158 -0
- jac_client/examples/all-in-one/components/TransactionItem.jac +55 -0
- jac_client/examples/all-in-one/components/TransactionList.jac +37 -0
- jac_client/examples/all-in-one/components/navigation.jac +132 -0
- jac_client/examples/all-in-one/constants/categories.jac +37 -0
- jac_client/examples/all-in-one/constants/clients.jac +13 -0
- jac_client/examples/all-in-one/context/BudgetContext.jac +28 -0
- jac_client/examples/all-in-one/hooks/useBudget.jac +116 -0
- jac_client/examples/all-in-one/hooks/useLocalStorage.jac +36 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +70 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +126 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +552 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +126 -0
- jac_client/examples/all-in-one/pages/LandingPage.jac +101 -0
- jac_client/examples/all-in-one/pages/loginPage.jac +132 -0
- jac_client/examples/all-in-one/pages/nestedDemo.jac +61 -0
- jac_client/examples/all-in-one/pages/notFound.jac +24 -0
- jac_client/examples/all-in-one/pages/signupPage.jac +133 -0
- jac_client/examples/all-in-one/utils/formatters.jac +52 -0
- jac_client/examples/asset-serving/css-with-image/src/app.jac +3 -3
- jac_client/examples/asset-serving/image-asset/src/app.jac +3 -3
- jac_client/examples/asset-serving/import-alias/src/app.jac +3 -3
- jac_client/examples/basic/src/app.jac +3 -3
- jac_client/examples/basic-auth/src/app.jac +31 -37
- jac_client/examples/basic-auth-with-router/src/app.jac +16 -16
- jac_client/examples/basic-full-stack/src/app.jac +24 -30
- jac_client/examples/css-styling/js-styling/src/app.jac +5 -5
- jac_client/examples/css-styling/material-ui/src/app.jac +5 -5
- jac_client/examples/css-styling/pure-css/src/app.jac +5 -5
- jac_client/examples/css-styling/sass-example/src/app.jac +5 -5
- jac_client/examples/css-styling/styled-components/src/app.jac +5 -5
- jac_client/examples/css-styling/tailwind-example/src/app.jac +5 -5
- jac_client/examples/full-stack-with-auth/src/app.jac +16 -16
- jac_client/examples/ts-support/src/app.jac +4 -4
- jac_client/examples/with-router/src/app.jac +4 -4
- jac_client/plugin/cli.jac +160 -203
- jac_client/plugin/client.jac +8 -15
- jac_client/plugin/client_runtime.cl.jac +18 -14
- jac_client/plugin/impl/client.impl.jac +85 -26
- jac_client/plugin/impl/client_runtime.impl.jac +27 -9
- jac_client/plugin/plugin_config.jac +11 -11
- jac_client/plugin/src/compiler.jac +2 -1
- jac_client/plugin/src/impl/babel_processor.impl.jac +22 -17
- jac_client/plugin/src/impl/compiler.impl.jac +55 -18
- jac_client/plugin/src/impl/vite_bundler.impl.jac +215 -102
- jac_client/plugin/src/package_installer.jac +1 -1
- jac_client/plugin/src/vite_bundler.jac +9 -1
- jac_client/tests/conftest.py +10 -8
- jac_client/tests/fixtures/spawn_test/app.jac +15 -18
- jac_client/tests/fixtures/with-ts/app.jac +4 -4
- jac_client/tests/test_cli.py +105 -49
- jac_client/tests/test_it.py +297 -82
- {jac_client-0.2.6.dist-info → jac_client-0.2.8.dist-info}/METADATA +16 -7
- jac_client-0.2.8.dist-info/RECORD +97 -0
- jac_client/examples/all-in-one/src/app.jac +0 -841
- jac_client-0.2.6.dist-info/RECORD +0 -74
- /jac_client/examples/all-in-one/{src/button.jac → button.jac} +0 -0
- /jac_client/examples/all-in-one/{src/components → components}/button.jac +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.8.dist-info}/WHEEL +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.8.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.8.dist-info}/top_level.txt +0 -0
|
@@ -30,13 +30,6 @@ impl JacClient.send_static_file(
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
"""Get a module introspector for the supplied module."""
|
|
34
|
-
impl JacClient.get_module_introspector(
|
|
35
|
-
module_name: str, base_path: (str | None)
|
|
36
|
-
) -> ModuleIntrospector {
|
|
37
|
-
return JacClientModuleIntrospector(module_name, base_path);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
33
|
"""Build a client bundle for the supplied module."""
|
|
41
34
|
impl JacClient.build_client_bundle(
|
|
42
35
|
module: types.ModuleType, force: bool = False
|
|
@@ -57,12 +50,13 @@ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
|
|
|
57
50
|
# Fallback to base_path_dir if no project root found
|
|
58
51
|
base_path = base_path_dir;
|
|
59
52
|
}
|
|
60
|
-
#
|
|
61
|
-
|
|
53
|
+
# Use ViteBundler to get the client directory
|
|
54
|
+
bundler = ViteBundler(base_path);
|
|
55
|
+
client_dir = bundler._get_client_dir();
|
|
56
|
+
# package.json should only exist in .jac/client/configs/
|
|
57
|
+
generated_package_json = client_dir / 'configs' / 'package.json';
|
|
62
58
|
# Generate package.json if it doesn't exist
|
|
63
59
|
if not generated_package_json.exists() {
|
|
64
|
-
# Use ViteBundler to generate package.json from config.json
|
|
65
|
-
bundler = ViteBundler(base_path);
|
|
66
60
|
generated_path = bundler.create_package_json();
|
|
67
61
|
# Verify the file was created and resolve to absolute path
|
|
68
62
|
if not generated_path.exists() {
|
|
@@ -80,7 +74,7 @@ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
|
|
|
80
74
|
f'package.json not found at {package_json_path}. Expected at {generated_package_json}'
|
|
81
75
|
) ;
|
|
82
76
|
}
|
|
83
|
-
output_dir =
|
|
77
|
+
output_dir = client_dir / 'dist';
|
|
84
78
|
runtime_path = Path(__file__).with_name('client_runtime.cl.jac');
|
|
85
79
|
return ViteClientBundleBuilder(
|
|
86
80
|
runtime_path=runtime_path,
|
|
@@ -91,22 +85,22 @@ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
|
|
|
91
85
|
}
|
|
92
86
|
|
|
93
87
|
"""Render HTML page for client function using the Vite bundle."""
|
|
94
|
-
impl
|
|
95
|
-
|
|
88
|
+
impl JacClient.render_page(
|
|
89
|
+
introspector: ModuleIntrospector,
|
|
96
90
|
function_name: str,
|
|
97
91
|
args: dict[(str, Any)],
|
|
98
92
|
username: str
|
|
99
93
|
) -> dict[str, Any] {
|
|
100
|
-
|
|
94
|
+
introspector.load();
|
|
101
95
|
available_exports = (
|
|
102
|
-
set(
|
|
103
|
-
or set(
|
|
96
|
+
set(introspector._client_manifest.get('exports', []))
|
|
97
|
+
or set(introspector.get_client_functions().keys())
|
|
104
98
|
);
|
|
105
99
|
if (function_name not in available_exports) {
|
|
106
100
|
raise ValueError(f"Client function '{function_name}' not found") ;
|
|
107
101
|
}
|
|
108
|
-
bundle_hash =
|
|
109
|
-
import from jaclang.project.config { find_project_root }
|
|
102
|
+
bundle_hash = introspector.ensure_bundle();
|
|
103
|
+
import from jaclang.project.config { find_project_root, get_config }
|
|
110
104
|
# Find project root by looking for jac.toml (base_path_dir might be src/ for entry files)
|
|
111
105
|
base_path_dir = Path(Jac.base_path_dir);
|
|
112
106
|
project_root_result = find_project_root(base_path_dir);
|
|
@@ -116,19 +110,84 @@ impl JacClientModuleIntrospector.render_page(
|
|
|
116
110
|
# Fallback to base_path_dir if no project root found
|
|
117
111
|
base_path = base_path_dir;
|
|
118
112
|
}
|
|
119
|
-
|
|
113
|
+
# Get client directory from config or use default
|
|
114
|
+
config = get_config();
|
|
115
|
+
if config is not None {
|
|
116
|
+
dist_dir = config.get_client_dir() / 'dist';
|
|
117
|
+
} else {
|
|
118
|
+
dist_dir = base_path / '.jac' / 'client' / 'dist';
|
|
119
|
+
}
|
|
120
120
|
css_link = '';
|
|
121
|
-
css_file = dist_dir / '
|
|
121
|
+
css_file = dist_dir / 'styles.css';
|
|
122
122
|
if css_file.exists() {
|
|
123
123
|
css_hash = hashlib.sha256(css_file.read_bytes()).hexdigest()[:8];
|
|
124
|
-
css_link = f'<link rel="stylesheet" href="/static/
|
|
124
|
+
css_link = f'<link rel="stylesheet" href="/static/styles.css?hash={css_hash}"/>';
|
|
125
|
+
}
|
|
126
|
+
# Get meta data from config
|
|
127
|
+
client_cfg = config.get_plugin_config("client") if config else None;
|
|
128
|
+
meta_data = client_cfg.get("app_meta_data", {}) if client_cfg else {};
|
|
129
|
+
charset = meta_data.get("charset", "UTF-8");
|
|
130
|
+
title = meta_data.get("title", function_name);
|
|
131
|
+
viewport = meta_data.get("viewport", "width=device-width, initial-scale=1");
|
|
132
|
+
description = meta_data.get("description", None);
|
|
133
|
+
robots = meta_data.get("robots", "index, follow");
|
|
134
|
+
canonical = meta_data.get("canonical", None);
|
|
135
|
+
og_type = meta_data.get("og_type", "website");
|
|
136
|
+
og_title = meta_data.get("og_title", title);
|
|
137
|
+
og_description = meta_data.get("og_description", None);
|
|
138
|
+
og_url = meta_data.get("og_url", None);
|
|
139
|
+
og_image = meta_data.get("og_image", None);
|
|
140
|
+
theme_color = meta_data.get("theme_color", "#ffffff");
|
|
141
|
+
icon = meta_data.get("icon", None);
|
|
142
|
+
# Build head content from TOML metadata
|
|
143
|
+
head_content = f'<meta charset="{html.escape(charset)}"/>\n <meta name="viewport" content="{html.escape(
|
|
144
|
+
viewport
|
|
145
|
+
)}"/>\n <title>{html.escape(title)}</title>';
|
|
146
|
+
head_content += f'\n <meta name="robots" content="{html.escape(robots)}"/>';
|
|
147
|
+
head_content += f'\n <meta name="theme-color" content="{html.escape(
|
|
148
|
+
theme_color
|
|
149
|
+
)}"/>';
|
|
150
|
+
head_content += f'\n <meta property="og:type" content="{html.escape(
|
|
151
|
+
og_type
|
|
152
|
+
)}"/>';
|
|
153
|
+
head_content += f'\n <meta property="og:title" content="{html.escape(
|
|
154
|
+
og_title
|
|
155
|
+
)}"/>';
|
|
156
|
+
if description {
|
|
157
|
+
head_content += f'\n <meta name="description" content="{html.escape(
|
|
158
|
+
description
|
|
159
|
+
)}"/>';
|
|
160
|
+
}
|
|
161
|
+
if canonical {
|
|
162
|
+
head_content += f'\n <link rel="canonical" href="{html.escape(
|
|
163
|
+
canonical
|
|
164
|
+
)}"/>';
|
|
165
|
+
}
|
|
166
|
+
if icon {
|
|
167
|
+
head_content += f'\n <link rel="icon" href="{html.escape(icon)}"/>';
|
|
168
|
+
}
|
|
169
|
+
if og_url {
|
|
170
|
+
head_content += f'\n <meta property="og:url" content="{html.escape(
|
|
171
|
+
og_url
|
|
172
|
+
)}"/>';
|
|
173
|
+
}
|
|
174
|
+
if og_image {
|
|
175
|
+
head_content += f'\n <meta property="og:image" content="{html.escape(
|
|
176
|
+
og_image
|
|
177
|
+
)}"/>';
|
|
178
|
+
}
|
|
179
|
+
if og_description {
|
|
180
|
+
head_content += f'\n <meta property="og:description" content="{html.escape(
|
|
181
|
+
og_description
|
|
182
|
+
)}"/>';
|
|
125
183
|
}
|
|
126
|
-
head_content = f'<meta charset="utf-8"/>\n <title>{html.escape(
|
|
127
|
-
function_name
|
|
128
|
-
)}</title>';
|
|
129
184
|
if css_link {
|
|
130
185
|
head_content += f"\n {css_link}";
|
|
131
186
|
}
|
|
132
187
|
page = f'<!DOCTYPE html><html lang="en"><head>{head_content}</head><body><div id="root"></div><script src="/static/client.js?hash={bundle_hash}" defer></script></body></html>';
|
|
133
|
-
return {
|
|
188
|
+
return {
|
|
189
|
+
'html': page,
|
|
190
|
+
'bundle_hash': bundle_hash,
|
|
191
|
+
'bundle_code': introspector._bundle.code
|
|
192
|
+
};
|
|
134
193
|
}
|
|
@@ -65,9 +65,11 @@ impl __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any {
|
|
|
65
65
|
);
|
|
66
66
|
if not response.ok {
|
|
67
67
|
error_text = await response.json();
|
|
68
|
-
|
|
68
|
+
walker_name = f"{left}/{right}" if right else left;
|
|
69
|
+
raise Exception(f"Walker {walker_name} failed: {error_text}") ;
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
+
payload = await response.json();
|
|
72
|
+
return payload["data"] if payload["data"] else {};
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
impl jacSpawn(left: str, right: str = "", fields: dict = {}) -> any {
|
|
@@ -87,12 +89,18 @@ impl __jacCallFunction(function_name: str, args: dict = {}) -> any {
|
|
|
87
89
|
"body": JSON.stringify({"args": args})
|
|
88
90
|
}
|
|
89
91
|
);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
payload = await response.json();
|
|
93
|
+
if not payload["ok"] {
|
|
94
|
+
error_msg = payload["error"] if payload["error"] else "Unknown error";
|
|
95
|
+
raise Exception(f"Function {function_name} failed: {error_msg}") ;
|
|
93
96
|
}
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
result = None;
|
|
98
|
+
try {
|
|
99
|
+
if payload["data"] and payload["data"]["result"] {
|
|
100
|
+
result = payload["data"]["result"];
|
|
101
|
+
}
|
|
102
|
+
} except Exception { }
|
|
103
|
+
return result;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
impl jacSignup(username: str, password: str) -> dict {
|
|
@@ -106,7 +114,10 @@ impl jacSignup(username: str, password: str) -> dict {
|
|
|
106
114
|
);
|
|
107
115
|
if response.ok {
|
|
108
116
|
data = JSON.parse(await response.text());
|
|
109
|
-
token =
|
|
117
|
+
token = None;
|
|
118
|
+
if data["data"] and data["data"]["token"] {
|
|
119
|
+
token = data["data"]["token"];
|
|
120
|
+
}
|
|
110
121
|
if token {
|
|
111
122
|
__setLocalStorage("jac_token", token);
|
|
112
123
|
return {"success": True, "token": token, "username": username};
|
|
@@ -139,7 +150,14 @@ impl jacLogin(username: str, password: str) -> bool {
|
|
|
139
150
|
);
|
|
140
151
|
if response.ok {
|
|
141
152
|
data = JSON.parse(await response.text());
|
|
142
|
-
|
|
153
|
+
console.log("data", data);
|
|
154
|
+
token = None;
|
|
155
|
+
try {
|
|
156
|
+
if data["data"] and data["data"]["token"] {
|
|
157
|
+
token = data["data"]["token"];
|
|
158
|
+
}
|
|
159
|
+
} except Exception { }
|
|
160
|
+
console.log("token", token);
|
|
143
161
|
if token {
|
|
144
162
|
__setLocalStorage("jac_token", token);
|
|
145
163
|
return True;
|
|
@@ -80,7 +80,7 @@ class JacClientPluginConfig {
|
|
|
80
80
|
"name": "npm",
|
|
81
81
|
"dev_name": "npm.dev",
|
|
82
82
|
"cli_flag": "--cl",
|
|
83
|
-
"install_dir": ".
|
|
83
|
+
"install_dir": ".jac/client/configs",
|
|
84
84
|
"install_handler": _npm_install_handler,
|
|
85
85
|
"install_all_handler": _npm_install_all_handler,
|
|
86
86
|
"remove_handler": _npm_remove_handler
|
|
@@ -152,13 +152,13 @@ def _regenerate_and_install(project_dir: Path) -> None {
|
|
|
152
152
|
bundler = ViteBundler(project_dir);
|
|
153
153
|
bundler.create_package_json();
|
|
154
154
|
|
|
155
|
-
# Install to .client
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
# Install to .jac/client/ directory where babel processor expects it
|
|
156
|
+
client_dir = bundler._get_client_dir();
|
|
157
|
+
client_dir.mkdir(parents=True, exist_ok=True);
|
|
158
158
|
|
|
159
|
-
# Copy package.json to .client
|
|
160
|
-
configs_package_json =
|
|
161
|
-
build_package_json =
|
|
159
|
+
# Copy package.json to .jac/client/ for npm install
|
|
160
|
+
configs_package_json = client_dir / 'configs' / 'package.json';
|
|
161
|
+
build_package_json = client_dir / 'package.json';
|
|
162
162
|
if configs_package_json.exists() {
|
|
163
163
|
shutil.copy2(configs_package_json, build_package_json);
|
|
164
164
|
}
|
|
@@ -166,7 +166,7 @@ def _regenerate_and_install(project_dir: Path) -> None {
|
|
|
166
166
|
try {
|
|
167
167
|
subprocess.run(
|
|
168
168
|
["npm", "install"],
|
|
169
|
-
cwd=
|
|
169
|
+
cwd=client_dir,
|
|
170
170
|
check=True,
|
|
171
171
|
capture_output=True,
|
|
172
172
|
text=True
|
|
@@ -182,10 +182,10 @@ def _regenerate_and_install(project_dir: Path) -> None {
|
|
|
182
182
|
if build_package_json.exists() {
|
|
183
183
|
build_package_json.unlink();
|
|
184
184
|
}
|
|
185
|
-
# Move package-lock.json to
|
|
186
|
-
build_package_lock =
|
|
185
|
+
# Move package-lock.json to configs/ if it exists
|
|
186
|
+
build_package_lock = client_dir / 'package-lock.json';
|
|
187
187
|
if build_package_lock.exists() {
|
|
188
|
-
configs_package_lock =
|
|
188
|
+
configs_package_lock = client_dir / 'configs' / 'package-lock.json';
|
|
189
189
|
if configs_package_lock.exists() {
|
|
190
190
|
configs_package_lock.unlink();
|
|
191
191
|
}
|
|
@@ -52,6 +52,7 @@ class ViteCompiler {
|
|
|
52
52
|
source_root: (Path | None) = None
|
|
53
53
|
) -> None;
|
|
54
54
|
|
|
55
|
+
def _get_client_dir(self: ViteCompiler) -> Path;
|
|
55
56
|
def _copy_js_file(self: ViteCompiler, js_path: Path, source_root: Path) -> None;
|
|
56
57
|
def _copy_ts_file(self: ViteCompiler, ts_path: Path, source_root: Path) -> None;
|
|
57
58
|
def _copy_asset_file(
|
|
@@ -59,7 +60,7 @@ class ViteCompiler {
|
|
|
59
60
|
) -> None;
|
|
60
61
|
|
|
61
62
|
def copy_root_assets(self: ViteCompiler) -> None;
|
|
62
|
-
def create_entry_file(self: ViteCompiler) -> None;
|
|
63
|
+
def create_entry_file(self: ViteCompiler, module_path: Path) -> None;
|
|
63
64
|
def compile_and_bundle(
|
|
64
65
|
self: ViteCompiler, module: ModuleType, module_path: Path
|
|
65
66
|
) -> tuple[str, str, list[str], list[str]];
|
|
@@ -17,21 +17,21 @@ impl BabelProcessor.compile(self: BabelProcessor) -> None {
|
|
|
17
17
|
bundler._ensure_root_package_json();
|
|
18
18
|
try {
|
|
19
19
|
# Ensure dependencies are installed (check if node_modules exists)
|
|
20
|
-
|
|
21
|
-
node_modules =
|
|
20
|
+
client_dir = bundler._get_client_dir();
|
|
21
|
+
node_modules = client_dir / 'node_modules';
|
|
22
22
|
if not node_modules.exists() {
|
|
23
|
-
# Temporarily copy package.json to .client
|
|
24
|
-
build_package_json =
|
|
25
|
-
configs_package_json =
|
|
23
|
+
# Temporarily copy package.json to .jac/client/ for npm install
|
|
24
|
+
build_package_json = client_dir / 'package.json';
|
|
25
|
+
configs_package_json = client_dir / 'configs' / 'package.json';
|
|
26
26
|
if configs_package_json.exists() and not build_package_json.exists() {
|
|
27
27
|
import shutil;
|
|
28
28
|
shutil.copy2(configs_package_json, build_package_json);
|
|
29
29
|
}
|
|
30
30
|
try {
|
|
31
|
-
# Install to .client
|
|
31
|
+
# Install to .jac/client/node_modules
|
|
32
32
|
subprocess.run(
|
|
33
33
|
['npm', 'install'],
|
|
34
|
-
cwd=
|
|
34
|
+
cwd=client_dir,
|
|
35
35
|
check=True,
|
|
36
36
|
capture_output=True,
|
|
37
37
|
text=True
|
|
@@ -47,27 +47,32 @@ impl BabelProcessor.compile(self: BabelProcessor) -> None {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
# Temporarily copy package.json to .client
|
|
51
|
-
build_package_json =
|
|
52
|
-
configs_package_json =
|
|
50
|
+
# Temporarily copy package.json to .jac/client/ for npm run compile
|
|
51
|
+
build_package_json = client_dir / 'package.json';
|
|
52
|
+
configs_package_json = client_dir / 'configs' / 'package.json';
|
|
53
53
|
if configs_package_json.exists() and not build_package_json.exists() {
|
|
54
54
|
import shutil;
|
|
55
55
|
shutil.copy2(configs_package_json, build_package_json);
|
|
56
56
|
}
|
|
57
57
|
command = ['npm', 'run', 'compile'];
|
|
58
|
-
subprocess.run(
|
|
59
|
-
command, cwd=
|
|
58
|
+
result = subprocess.run(
|
|
59
|
+
command, cwd=client_dir, capture_output=True, text=True
|
|
60
60
|
);
|
|
61
|
+
if result.returncode != 0 {
|
|
62
|
+
# Show the actual error from npm/babel
|
|
63
|
+
error_output = result.stderr or result.stdout or "Unknown error";
|
|
64
|
+
raise RuntimeError(f"Client bundle compilation failed:\n{error_output}") ;
|
|
65
|
+
}
|
|
61
66
|
} finally {
|
|
62
|
-
# Clean up temporary package.json in .client
|
|
63
|
-
build_package_json =
|
|
67
|
+
# Clean up temporary package.json in .jac/client/
|
|
68
|
+
build_package_json = client_dir / 'package.json';
|
|
64
69
|
if build_package_json.exists() {
|
|
65
70
|
build_package_json.unlink();
|
|
66
71
|
}
|
|
67
|
-
# Move package-lock.json to
|
|
68
|
-
build_package_lock =
|
|
72
|
+
# Move package-lock.json to configs/ if it exists
|
|
73
|
+
build_package_lock = client_dir / 'package-lock.json';
|
|
69
74
|
if build_package_lock.exists() {
|
|
70
|
-
configs_package_lock =
|
|
75
|
+
configs_package_lock = client_dir / 'configs' / 'package-lock.json';
|
|
71
76
|
if configs_package_lock.exists() {
|
|
72
77
|
configs_package_lock.unlink();
|
|
73
78
|
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Get the client build directory from project config."""
|
|
2
|
+
impl ViteCompiler._get_client_dir(self: ViteCompiler) -> Path {
|
|
3
|
+
# Try to get from project config
|
|
4
|
+
try {
|
|
5
|
+
import from jaclang.project.config { get_config }
|
|
6
|
+
config = get_config();
|
|
7
|
+
if config is not None {
|
|
8
|
+
return config.get_client_dir();
|
|
9
|
+
}
|
|
10
|
+
} except ImportError { }
|
|
11
|
+
# Fallback to default
|
|
12
|
+
return self.project_dir / '.jac' / 'client';
|
|
13
|
+
}
|
|
2
14
|
|
|
15
|
+
"""Compile module and dependencies, then bundle with Vite."""
|
|
3
16
|
impl ViteCompiler.compile_and_bundle(
|
|
4
17
|
self: ViteCompiler, module: ModuleType, module_path: Path
|
|
5
18
|
) -> tuple[str, str, list[str], list[str]] {
|
|
@@ -16,14 +29,13 @@ impl ViteCompiler.compile_and_bundle(
|
|
|
16
29
|
collected_globals=collected_globals
|
|
17
30
|
);
|
|
18
31
|
self.copy_root_assets();
|
|
19
|
-
self.create_entry_file();
|
|
32
|
+
self.create_entry_file(module_path);
|
|
20
33
|
self.babel_processor.compile();
|
|
34
|
+
client_dir = self._get_client_dir();
|
|
21
35
|
self.babel_processor.copy_assets_after_compile(
|
|
22
|
-
self.compiled_dir,
|
|
23
|
-
(self.project_dir / '.client-build' / 'build'),
|
|
24
|
-
self.asset_processor
|
|
36
|
+
self.compiled_dir, (client_dir / 'build'), self.asset_processor
|
|
25
37
|
);
|
|
26
|
-
entry_file =
|
|
38
|
+
entry_file = client_dir / 'build' / '_entry.js';
|
|
27
39
|
self.vite_bundler.build(entry_file=entry_file);
|
|
28
40
|
(bundle_code, bundle_hash) = self.vite_bundler.read_bundle();
|
|
29
41
|
client_exports = sorted(collected_exports);
|
|
@@ -32,9 +44,12 @@ impl ViteCompiler.compile_and_bundle(
|
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
"""Create the main entry file for Vite bundling."""
|
|
35
|
-
impl ViteCompiler.create_entry_file(self: ViteCompiler) -> None {
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
impl ViteCompiler.create_entry_file(self: ViteCompiler, module_path: Path) -> None {
|
|
48
|
+
# Use _entry.js to avoid conflict with compiled modules that may be named main.js
|
|
49
|
+
entry_file = self.compiled_dir / '_entry.js';
|
|
50
|
+
# Derive the app module filename from the entry point (e.g., main.jac -> main.js, app.jac -> app.js)
|
|
51
|
+
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';
|
|
38
53
|
entry_file.write_text(entry_content, encoding='utf-8');
|
|
39
54
|
}
|
|
40
55
|
|
|
@@ -46,7 +61,7 @@ impl ViteCompiler.copy_root_assets(self: ViteCompiler) -> None {
|
|
|
46
61
|
self.asset_processor.copy_assets(root_assets_dir, compiled_assets_dir);
|
|
47
62
|
}
|
|
48
63
|
# Copy configured_asset files from root assets/ to build assets/ (bypassing compiled)
|
|
49
|
-
build_assets_dir = self.
|
|
64
|
+
build_assets_dir = self._get_client_dir() / 'build' / 'assets';
|
|
50
65
|
if (root_assets_dir.exists() and root_assets_dir.is_dir()) {
|
|
51
66
|
self.asset_processor.copy_custom_asset_types(root_assets_dir, build_assets_dir);
|
|
52
67
|
}
|
|
@@ -149,9 +164,29 @@ impl ViteCompiler.compile_dependencies_recursively(
|
|
|
149
164
|
combined_js = self.jac_compiler.add_runtime_imports(module_js);
|
|
150
165
|
try {
|
|
151
166
|
relative_path = module_path.relative_to(source_root);
|
|
152
|
-
|
|
167
|
+
# Handle compound extensions like .cl.jac, .impl.jac -> .js
|
|
168
|
+
rel_str = str(relative_path);
|
|
169
|
+
for compound_ext in ['.cl.jac', '.impl.jac', '.test.jac'] {
|
|
170
|
+
if rel_str.endswith(compound_ext) {
|
|
171
|
+
rel_str = rel_str[:-len(compound_ext)] + '.js';
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
rel_str = str(relative_path.with_suffix('.js'));
|
|
176
|
+
}
|
|
177
|
+
output_path = self.compiled_dir / rel_str;
|
|
153
178
|
} except ValueError {
|
|
154
|
-
|
|
179
|
+
# Handle compound extensions in filename
|
|
180
|
+
name = module_path.name;
|
|
181
|
+
for compound_ext in ['.cl.jac', '.impl.jac', '.test.jac'] {
|
|
182
|
+
if name.endswith(compound_ext) {
|
|
183
|
+
name = name[:-len(compound_ext)] + '.js';
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
name = module_path.stem + '.js';
|
|
188
|
+
}
|
|
189
|
+
output_path = self.compiled_dir / name;
|
|
155
190
|
}
|
|
156
191
|
output_path.parent.mkdir(parents=True, exist_ok=True);
|
|
157
192
|
output_path.write_text(combined_js, encoding='utf-8');
|
|
@@ -228,19 +263,21 @@ impl ViteCompiler.init(
|
|
|
228
263
|
) ;
|
|
229
264
|
}
|
|
230
265
|
self.vite_package_json = vite_package_json;
|
|
231
|
-
#
|
|
266
|
+
# Detect project root: package.json may be in .jac/client/configs/ or project root
|
|
232
267
|
if (
|
|
233
|
-
vite_package_json.parent.name == '
|
|
234
|
-
and vite_package_json.parent.parent.name == '
|
|
268
|
+
vite_package_json.parent.name == 'configs'
|
|
269
|
+
and vite_package_json.parent.parent.name == 'client'
|
|
270
|
+
and vite_package_json.parent.parent.parent.name == '.jac'
|
|
235
271
|
) {
|
|
236
|
-
|
|
237
|
-
|
|
272
|
+
# .jac/client/configs/package.json -> go up 3 levels to project root
|
|
273
|
+
self.project_dir = vite_package_json.parent.parent.parent.parent;
|
|
274
|
+
} elif (vite_package_json.parent.name == 'configs') {
|
|
238
275
|
self.project_dir = vite_package_json.parent.parent;
|
|
239
276
|
} else {
|
|
240
277
|
self.project_dir = vite_package_json.parent;
|
|
241
278
|
}
|
|
242
279
|
self.runtime_path = runtime_path;
|
|
243
|
-
self.compiled_dir = self.
|
|
280
|
+
self.compiled_dir = self._get_client_dir() / 'compiled';
|
|
244
281
|
self.jac_compiler = JacToJSCompiler(
|
|
245
282
|
compile_to_js_func, extract_exports_func, extract_globals_func
|
|
246
283
|
);
|