jac-client 0.2.6__py3-none-any.whl → 0.2.11__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/{src/button.jac → button.jac} +4 -3
- jac_client/examples/all-in-one/components/CategoryFilter.jac +47 -0
- jac_client/examples/all-in-one/components/Header.jac +17 -0
- jac_client/examples/all-in-one/components/ProfitOverview.jac +64 -0
- jac_client/examples/all-in-one/components/Summary.jac +76 -0
- jac_client/examples/all-in-one/components/TransactionForm.jac +188 -0
- jac_client/examples/all-in-one/components/TransactionItem.jac +62 -0
- jac_client/examples/all-in-one/components/TransactionList.jac +44 -0
- jac_client/examples/all-in-one/components/button.jac +8 -0
- jac_client/examples/all-in-one/components/navigation.jac +126 -0
- jac_client/examples/all-in-one/constants/categories.jac +36 -0
- jac_client/examples/all-in-one/constants/clients.jac +12 -0
- jac_client/examples/all-in-one/context/BudgetContext.jac +31 -0
- jac_client/examples/all-in-one/hooks/useBudget.jac +122 -0
- jac_client/examples/all-in-one/hooks/useLocalStorage.jac +37 -0
- jac_client/examples/all-in-one/main.jac +542 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +140 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +157 -0
- jac_client/examples/all-in-one/pages/LandingPage.jac +124 -0
- jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
- jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
- jac_client/examples/all-in-one/pages/loginPage.jac +127 -0
- jac_client/examples/all-in-one/pages/nestedDemo.jac +54 -0
- jac_client/examples/all-in-one/pages/notFound.jac +18 -0
- jac_client/examples/all-in-one/pages/signupPage.jac +127 -0
- jac_client/examples/all-in-one/utils/formatters.jac +49 -0
- jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
- jac_client/examples/asset-serving/image-asset/main.jac +56 -0
- jac_client/examples/asset-serving/import-alias/main.jac +109 -0
- jac_client/examples/basic/main.jac +23 -0
- jac_client/examples/basic-auth/main.jac +363 -0
- jac_client/examples/basic-auth-with-router/main.jac +451 -0
- jac_client/examples/basic-full-stack/main.jac +362 -0
- jac_client/examples/css-styling/js-styling/main.jac +63 -0
- jac_client/examples/css-styling/material-ui/main.jac +122 -0
- jac_client/examples/css-styling/pure-css/main.jac +55 -0
- jac_client/examples/css-styling/sass-example/main.jac +55 -0
- jac_client/examples/css-styling/styled-components/main.jac +62 -0
- jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
- jac_client/examples/full-stack-with-auth/main.jac +696 -0
- jac_client/examples/little-x/main.jac +681 -0
- jac_client/examples/little-x/src/submit-button.jac +15 -14
- jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
- jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
- jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
- jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
- jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
- jac_client/examples/ts-support/main.jac +35 -0
- jac_client/examples/with-router/main.jac +286 -0
- jac_client/plugin/cli.jac +507 -470
- jac_client/plugin/client.jac +30 -12
- jac_client/plugin/client_runtime.cl.jac +25 -15
- jac_client/plugin/impl/client.impl.jac +126 -26
- jac_client/plugin/impl/client_runtime.impl.jac +182 -10
- jac_client/plugin/plugin_config.jac +216 -34
- jac_client/plugin/src/__init__.jac +0 -2
- jac_client/plugin/src/compiler.jac +2 -2
- 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 +99 -30
- 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/jac_to_js.impl.jac +5 -1
- jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
- jac_client/plugin/src/impl/vite_bundler.impl.jac +384 -144
- jac_client/plugin/src/package_installer.jac +1 -1
- 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 +2347 -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 +15 -1
- jac_client/plugin/utils/__init__.jac +3 -0
- jac_client/plugin/utils/bun_installer.jac +16 -0
- jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
- jac_client/templates/client.jacpack +72 -0
- jac_client/templates/fullstack.jacpack +61 -0
- jac_client/tests/conftest.py +110 -52
- jac_client/tests/fixtures/spawn_test/app.jac +64 -70
- jac_client/tests/fixtures/with-ts/app.jac +28 -28
- jac_client/tests/test_cli.py +280 -113
- jac_client/tests/test_e2e.py +232 -0
- jac_client/tests/test_helpers.py +58 -0
- jac_client/tests/test_it.py +325 -154
- jac_client/tests/test_it_desktop.py +891 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/METADATA +20 -11
- jac_client-0.2.11.dist-info/RECORD +113 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
- jac_client/examples/all-in-one/src/app.jac +0 -841
- jac_client/examples/all-in-one/src/components/button.jac +0 -7
- jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
- jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
- jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
- jac_client/examples/basic/src/app.jac +0 -21
- jac_client/examples/basic-auth/src/app.jac +0 -377
- jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
- jac_client/examples/basic-full-stack/src/app.jac +0 -365
- jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
- jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
- jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
- jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
- jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
- jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
- jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
- jac_client/examples/little-x/src/app.jac +0 -719
- jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
- jac_client/examples/ts-support/src/app.jac +0 -35
- jac_client/examples/with-router/src/app.jac +0 -323
- jac_client/plugin/src/babel_processor.jac +0 -18
- jac_client/plugin/src/impl/babel_processor.impl.jac +0 -84
- jac_client-0.2.6.dist-info/RECORD +0 -74
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Get the client build directory from project config."""
|
|
2
|
+
impl ViteBundler._get_client_dir(self: ViteBundler) -> 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
|
+
"""Create package.json from config.json during bundling."""
|
|
3
16
|
impl ViteBundler.create_package_json(
|
|
4
17
|
self: ViteBundler, project_name: Optional[str] = None
|
|
5
18
|
) -> Path {
|
|
6
|
-
build_dir = self.
|
|
7
|
-
build_dir.mkdir(exist_ok=True);
|
|
8
|
-
configs_dir = build_dir / '
|
|
19
|
+
build_dir = self._get_client_dir();
|
|
20
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
21
|
+
configs_dir = build_dir / 'configs';
|
|
9
22
|
configs_dir.mkdir(exist_ok=True);
|
|
10
23
|
package_config = self.config_loader.get_package_config();
|
|
11
24
|
package_json_path = configs_dir / 'package.json';
|
|
@@ -29,39 +42,16 @@ impl ViteBundler.create_package_json(
|
|
|
29
42
|
if not name {
|
|
30
43
|
name = self.project_dir.name or 'jac-app';
|
|
31
44
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'react-dom': '^18.2.0',
|
|
36
|
-
'react-router-dom': '^6.22.0'
|
|
37
|
-
};
|
|
38
|
-
default_dev_dependencies = {
|
|
39
|
-
'vite': '^6.4.1',
|
|
40
|
-
'@babel/cli': '^7.28.3',
|
|
41
|
-
'@babel/core': '^7.28.5',
|
|
42
|
-
'@babel/preset-env': '^7.28.5',
|
|
43
|
-
'@babel/preset-react': '^7.28.5',
|
|
44
|
-
'@vitejs/plugin-react': '^4.2.1',
|
|
45
|
-
'typescript': '^5.3.3',
|
|
46
|
-
'@types/react': '^18.2.0',
|
|
47
|
-
'@types/react-dom': '^18.2.0'
|
|
48
|
-
};
|
|
49
|
-
# Merge user dependencies from TOML with defaults (user can override)
|
|
50
|
-
user_dependencies = package_config.get('dependencies', {});
|
|
51
|
-
user_dev_dependencies = package_config.get('devDependencies', {});
|
|
52
|
-
dependencies = {** default_dependencies, ** user_dependencies};
|
|
53
|
-
dev_dependencies = {** default_dev_dependencies, ** user_dev_dependencies};
|
|
45
|
+
dependencies = package_config.get('dependencies', {});
|
|
46
|
+
dev_dependencies = package_config.get('devDependencies', {});
|
|
47
|
+
# Vite handles JSX/TSX transpilation natively with Bun - no Babel compile step needed
|
|
54
48
|
scripts = {
|
|
55
|
-
'build': '
|
|
56
|
-
'dev': 'vite dev --config .
|
|
57
|
-
'preview': 'vite preview --config .
|
|
58
|
-
'compile': 'babel compiled --out-dir build --extensions ".jsx,.js" --out-file-extension .js'
|
|
49
|
+
'build': 'vite build --config .jac/client/configs/vite.config.js',
|
|
50
|
+
'dev': 'vite dev --config .jac/client/configs/vite.config.js',
|
|
51
|
+
'preview': 'vite preview --config .jac/client/configs/vite.config.js'
|
|
59
52
|
};
|
|
60
53
|
user_scripts = package_config.get('scripts', {});
|
|
61
54
|
scripts.update(user_scripts);
|
|
62
|
-
babel_config = {
|
|
63
|
-
'presets': [['@babel/preset-env', {'modules': False}], '@babel/preset-react']
|
|
64
|
-
};
|
|
65
55
|
package_data = {
|
|
66
56
|
'name': name,
|
|
67
57
|
'version': package_config.get('version', '1.0.0'),
|
|
@@ -70,8 +60,7 @@ impl ViteBundler.create_package_json(
|
|
|
70
60
|
'main': 'index.js',
|
|
71
61
|
'scripts': scripts,
|
|
72
62
|
'dependencies': dependencies,
|
|
73
|
-
'devDependencies': dev_dependencies
|
|
74
|
-
'babel': babel_config
|
|
63
|
+
'devDependencies': dev_dependencies
|
|
75
64
|
};
|
|
76
65
|
for (key, value) in package_config.items() {
|
|
77
66
|
if (
|
|
@@ -85,14 +74,16 @@ impl ViteBundler.create_package_json(
|
|
|
85
74
|
}
|
|
86
75
|
# Generate tsconfig.json during build time
|
|
87
76
|
self.create_tsconfig();
|
|
77
|
+
# Generate config files (postcss, tailwind, etc.) from jac.toml
|
|
78
|
+
self.create_config_files();
|
|
88
79
|
return package_json_path;
|
|
89
80
|
}
|
|
90
81
|
|
|
91
82
|
"""Create tsconfig.json during build time, merging user config from jac.toml."""
|
|
92
83
|
impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
|
|
93
|
-
build_dir = self.
|
|
94
|
-
build_dir.mkdir(exist_ok=True);
|
|
95
|
-
configs_dir = build_dir / '
|
|
84
|
+
build_dir = self._get_client_dir();
|
|
85
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
86
|
+
configs_dir = build_dir / 'configs';
|
|
96
87
|
configs_dir.mkdir(exist_ok=True);
|
|
97
88
|
tsconfig_path = configs_dir / 'tsconfig.json';
|
|
98
89
|
# Default tsconfig settings
|
|
@@ -114,7 +105,7 @@ impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
|
|
|
114
105
|
'noFallthroughCasesInSwitch': True
|
|
115
106
|
};
|
|
116
107
|
default_include = ['components/**/*'];
|
|
117
|
-
default_exclude = ['.
|
|
108
|
+
default_exclude = ['.jac'];
|
|
118
109
|
# Get user config from [plugins.client.ts] in jac.toml
|
|
119
110
|
user_ts_config = self.config_loader.get_ts_config();
|
|
120
111
|
user_compiler_options = user_ts_config.get('compilerOptions', {});
|
|
@@ -130,28 +121,28 @@ impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
|
|
|
130
121
|
'include': merged_include,
|
|
131
122
|
'exclude': merged_exclude
|
|
132
123
|
};
|
|
133
|
-
# Write the config to .
|
|
124
|
+
# Write the config to .jac/client/configs/tsconfig.json (no root config file)
|
|
134
125
|
tsconfig_path.write_text(json.dumps(tsconfig_data, indent=2), encoding='utf-8');
|
|
135
126
|
return tsconfig_path;
|
|
136
127
|
}
|
|
137
128
|
|
|
138
129
|
"""
|
|
139
|
-
Clean up root package.json and move
|
|
130
|
+
Clean up root package.json and move bun.lockb to configs/.
|
|
140
131
|
|
|
141
|
-
Remove root package.json and move
|
|
132
|
+
Remove root package.json and move bun.lockb to configs/.
|
|
142
133
|
"""
|
|
143
134
|
impl ViteBundler._cleanup_root_package_files(self: ViteBundler) -> None {
|
|
144
135
|
root_package_json = self.project_dir / 'package.json';
|
|
145
|
-
|
|
146
|
-
build_dir = self.
|
|
147
|
-
configs_dir = build_dir / '
|
|
148
|
-
|
|
149
|
-
if
|
|
136
|
+
root_bun_lockb = self.project_dir / 'bun.lockb';
|
|
137
|
+
build_dir = self._get_client_dir();
|
|
138
|
+
configs_dir = build_dir / 'configs';
|
|
139
|
+
configs_bun_lockb = configs_dir / 'bun.lockb';
|
|
140
|
+
if root_bun_lockb.exists() {
|
|
150
141
|
configs_dir.mkdir(exist_ok=True);
|
|
151
|
-
if
|
|
152
|
-
|
|
142
|
+
if configs_bun_lockb.exists() {
|
|
143
|
+
configs_bun_lockb.unlink();
|
|
153
144
|
}
|
|
154
|
-
shutil.move(str(
|
|
145
|
+
shutil.move(str(root_bun_lockb), str(configs_bun_lockb));
|
|
155
146
|
}
|
|
156
147
|
if root_package_json.exists() {
|
|
157
148
|
root_package_json.unlink();
|
|
@@ -159,12 +150,12 @@ impl ViteBundler._cleanup_root_package_files(self: ViteBundler) -> None {
|
|
|
159
150
|
}
|
|
160
151
|
|
|
161
152
|
"""
|
|
162
|
-
Ensure root package.json exists temporarily for
|
|
153
|
+
Ensure root package.json exists temporarily for bun commands.
|
|
163
154
|
|
|
164
155
|
Create root package.json temporarily if it doesn't exist.
|
|
165
156
|
"""
|
|
166
157
|
impl ViteBundler._ensure_root_package_json(self: ViteBundler) -> None {
|
|
167
|
-
generated_package_json = self.
|
|
158
|
+
generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
|
|
168
159
|
root_package_json = self.project_dir / 'package.json';
|
|
169
160
|
if not generated_package_json.exists() {
|
|
170
161
|
self.create_package_json();
|
|
@@ -235,51 +226,28 @@ impl ViteBundler._get_plugin_var_name(self: ViteBundler, plugin_name: str) -> st
|
|
|
235
226
|
|
|
236
227
|
"""Create vite.config.js from config.json during bundling."""
|
|
237
228
|
impl ViteBundler.create_vite_config(self: ViteBundler, entry_file: Path) -> Path {
|
|
238
|
-
build_dir = self.
|
|
239
|
-
build_dir.mkdir(exist_ok=True);
|
|
240
|
-
configs_dir = build_dir / '
|
|
229
|
+
build_dir = self._get_client_dir();
|
|
230
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
231
|
+
configs_dir = build_dir / 'configs';
|
|
241
232
|
configs_dir.mkdir(exist_ok=True);
|
|
242
233
|
vite_config_data = self.config_loader.get_vite_config();
|
|
243
234
|
config_path = configs_dir / 'vite.config.js';
|
|
244
235
|
# TypeScript is always enabled by default
|
|
245
|
-
build_dir = self.project_dir / '.client-build';
|
|
246
236
|
try {
|
|
247
|
-
# Entry file path relative to
|
|
237
|
+
# Entry file path relative to client build dir (not project root)
|
|
248
238
|
entry_relative = entry_file.relative_to(build_dir).as_posix();
|
|
249
239
|
} except ValueError {
|
|
250
|
-
# Fallback:
|
|
251
|
-
|
|
252
|
-
entry_relative_full = entry_file.relative_to(self.project_dir).as_posix();
|
|
253
|
-
prefix = '.client-build/';
|
|
254
|
-
if entry_relative_full.startswith(prefix) {
|
|
255
|
-
# Remove '.client-build/' prefix (15 characters)
|
|
256
|
-
entry_relative = entry_relative_full[15:];
|
|
257
|
-
} else {
|
|
258
|
-
entry_relative = entry_relative_full;
|
|
259
|
-
}
|
|
260
|
-
} except ValueError {
|
|
261
|
-
entry_relative = entry_file.as_posix();
|
|
262
|
-
}
|
|
240
|
+
# Fallback: use absolute path
|
|
241
|
+
entry_relative = entry_file.as_posix();
|
|
263
242
|
}
|
|
264
243
|
try {
|
|
265
|
-
# Output dir path relative to
|
|
244
|
+
# Output dir path relative to client build dir (not project root)
|
|
266
245
|
output_relative = self.output_dir.relative_to(build_dir).as_posix();
|
|
267
246
|
} except ValueError {
|
|
268
|
-
# Fallback:
|
|
269
|
-
|
|
270
|
-
output_relative_full = self.output_dir.relative_to(self.project_dir).as_posix();
|
|
271
|
-
prefix = '.client-build/';
|
|
272
|
-
if output_relative_full.startswith(prefix) {
|
|
273
|
-
# Remove '.client-build/' prefix (15 characters)
|
|
274
|
-
output_relative = output_relative_full[15:];
|
|
275
|
-
} else {
|
|
276
|
-
output_relative = output_relative_full;
|
|
277
|
-
}
|
|
278
|
-
} except ValueError {
|
|
279
|
-
output_relative = self.output_dir.as_posix();
|
|
280
|
-
}
|
|
247
|
+
# Fallback: use absolute path
|
|
248
|
+
output_relative = self.output_dir.as_posix();
|
|
281
249
|
}
|
|
282
|
-
# Calculate compiled directory path for aliases (relative to
|
|
250
|
+
# Calculate compiled directory path for aliases (relative to client build dir)
|
|
283
251
|
if entry_relative.endswith('/build/main.js') {
|
|
284
252
|
compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
|
|
285
253
|
compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
|
|
@@ -331,11 +299,55 @@ impl ViteBundler.create_vite_config(self: ViteBundler, entry_file: Path) -> Path
|
|
|
331
299
|
else '';
|
|
332
300
|
config_content = f'''import {{ defineConfig }} from "vite";
|
|
333
301
|
import path from "path";
|
|
302
|
+
import fs from "fs";
|
|
334
303
|
import {{ fileURLToPath }} from "url";
|
|
335
304
|
{imports_section}const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
336
|
-
// Config is in
|
|
305
|
+
// Config is in configs/ inside .jac/client/, so go up one level to .jac/client/, then up two more to project root
|
|
337
306
|
const buildDir = path.resolve(__dirname, "..");
|
|
338
|
-
const projectRoot = path.resolve(__dirname, "
|
|
307
|
+
const projectRoot = path.resolve(__dirname, "../../..");
|
|
308
|
+
|
|
309
|
+
// Jac source mapper plugin - maps errors back to original .jac files
|
|
310
|
+
function jacSourceMapper() {{
|
|
311
|
+
const sourceMap = new Map(); // compiled path -> original jac path
|
|
312
|
+
|
|
313
|
+
return {{
|
|
314
|
+
name: 'jac-source-mapper',
|
|
315
|
+
enforce: 'pre',
|
|
316
|
+
|
|
317
|
+
// Extract source mapping from compiled files
|
|
318
|
+
transform(code, id) {{
|
|
319
|
+
if (id.includes('/compiled/') && id.endsWith('.js')) {{
|
|
320
|
+
const match = code.match(/^\\/\\* Source: (.+?) \\*\\//);
|
|
321
|
+
if (match) {{
|
|
322
|
+
sourceMap.set(id, match[1]);
|
|
323
|
+
}}
|
|
324
|
+
}}
|
|
325
|
+
return null;
|
|
326
|
+
}},
|
|
327
|
+
|
|
328
|
+
// Enhance error messages with original source info
|
|
329
|
+
buildEnd() {{
|
|
330
|
+
// Store source map for error reporting
|
|
331
|
+
this._jacSourceMap = sourceMap;
|
|
332
|
+
}},
|
|
333
|
+
|
|
334
|
+
// Handle resolve errors to show original source
|
|
335
|
+
resolveId(source, importer) {{
|
|
336
|
+
if (importer && sourceMap.has(importer)) {{
|
|
337
|
+
const originalSource = sourceMap.get(importer);
|
|
338
|
+
// Check for common issues like double slashes
|
|
339
|
+
if (source.includes('//') && !source.startsWith('http')) {{
|
|
340
|
+
this.error({{
|
|
341
|
+
message: `Cannot resolve "${{source}}" - path contains invalid double slash. Check your import in the original Jac file.`,
|
|
342
|
+
id: originalSource,
|
|
343
|
+
loc: {{ line: 1, column: 0 }}
|
|
344
|
+
}});
|
|
345
|
+
}}
|
|
346
|
+
}}
|
|
347
|
+
return null;
|
|
348
|
+
}}
|
|
349
|
+
}};
|
|
350
|
+
}}
|
|
339
351
|
|
|
340
352
|
/**
|
|
341
353
|
* Vite configuration generated from config.json (in project root)
|
|
@@ -343,14 +355,20 @@ const projectRoot = path.resolve(__dirname, "../..");
|
|
|
343
355
|
*/
|
|
344
356
|
|
|
345
357
|
export default defineConfig({{
|
|
346
|
-
plugins: [
|
|
347
|
-
|
|
358
|
+
plugins: [
|
|
359
|
+
jacSourceMapper(),{(newline + plugins_str + newline + ' ') if plugins_str else ''}],
|
|
360
|
+
root: buildDir, // base folder (.jac/client/) so vite can find node_modules
|
|
348
361
|
build: {{
|
|
362
|
+
sourcemap: true, // Enable source maps for better error messages
|
|
349
363
|
rollupOptions: {{
|
|
350
364
|
input: path.resolve(buildDir, "{entry_relative}"), // your compiled entry file
|
|
351
365
|
output: {{
|
|
352
366
|
entryFileNames: "client.[hash].js", // name of the final js file
|
|
353
|
-
assetFileNames:
|
|
367
|
+
assetFileNames: (assetInfo) => assetInfo.name?.endsWith('.css') ? 'styles.css' : '[name].[ext]',
|
|
368
|
+
sourcemapPathTransform: (relativeSourcePath) => {{
|
|
369
|
+
// Transform source map paths to point to original location
|
|
370
|
+
return relativeSourcePath;
|
|
371
|
+
}},
|
|
354
372
|
}},
|
|
355
373
|
}},
|
|
356
374
|
outDir: path.resolve(buildDir, "{output_relative}"), // final bundled output
|
|
@@ -360,7 +378,7 @@ export default defineConfig({{
|
|
|
360
378
|
publicDir: false,
|
|
361
379
|
{server_section} resolve: {{
|
|
362
380
|
alias: {{
|
|
363
|
-
"@jac
|
|
381
|
+
"@jac/runtime": path.resolve(buildDir, "{compiled_utils_relative}"),
|
|
364
382
|
"@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
|
|
365
383
|
}},
|
|
366
384
|
extensions: [{extensions_str}],
|
|
@@ -391,14 +409,8 @@ impl ViteBundler.read_bundle(self: ViteBundler) -> tuple[str, str] {
|
|
|
391
409
|
|
|
392
410
|
"""Find the generated Vite CSS file."""
|
|
393
411
|
impl ViteBundler.find_css(self: ViteBundler) -> Optional[Path] {
|
|
394
|
-
css_file = self.output_dir / '
|
|
395
|
-
if css_file.exists()
|
|
396
|
-
return css_file;
|
|
397
|
-
}
|
|
398
|
-
for file in self.output_dir.glob('*.css') {
|
|
399
|
-
return file;
|
|
400
|
-
}
|
|
401
|
-
return None;
|
|
412
|
+
css_file = self.output_dir / 'styles.css';
|
|
413
|
+
return css_file if css_file.exists() else None;
|
|
402
414
|
}
|
|
403
415
|
|
|
404
416
|
"""Find the generated Vite bundle file."""
|
|
@@ -409,10 +421,18 @@ impl ViteBundler.find_bundle(self: ViteBundler) -> Optional[Path] {
|
|
|
409
421
|
return None;
|
|
410
422
|
}
|
|
411
423
|
|
|
412
|
-
"""Run Vite build with generated config in .
|
|
424
|
+
"""Run Vite build with generated config in .jac/client/configs/."""
|
|
413
425
|
impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) -> None {
|
|
426
|
+
import sys;
|
|
427
|
+
import time;
|
|
428
|
+
import shutil;
|
|
429
|
+
import from jac_client.plugin.utils { ensure_bun_available }
|
|
430
|
+
# Ensure bun is available before proceeding
|
|
431
|
+
if not ensure_bun_available() {
|
|
432
|
+
raise ClientBundleError('Bun is required. Install manually: https://bun.sh') from None ;
|
|
433
|
+
}
|
|
414
434
|
self.output_dir.mkdir(parents=True, exist_ok=True);
|
|
415
|
-
generated_package_json = self.
|
|
435
|
+
generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
|
|
416
436
|
if not generated_package_json.exists() {
|
|
417
437
|
self.create_package_json();
|
|
418
438
|
} else {
|
|
@@ -420,79 +440,89 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
|
|
|
420
440
|
self.create_tsconfig();
|
|
421
441
|
}
|
|
422
442
|
try {
|
|
423
|
-
build_dir = self.
|
|
443
|
+
build_dir = self._get_client_dir();
|
|
424
444
|
node_modules = build_dir / 'node_modules';
|
|
425
445
|
if not node_modules.exists() {
|
|
426
|
-
# Temporarily copy package.json to
|
|
446
|
+
# Temporarily copy package.json to client build dir for bun install
|
|
427
447
|
build_package_json = build_dir / 'package.json';
|
|
428
|
-
configs_package_json = build_dir / '
|
|
448
|
+
configs_package_json = build_dir / 'configs' / 'package.json';
|
|
429
449
|
if configs_package_json.exists() and not build_package_json.exists() {
|
|
430
450
|
import shutil;
|
|
431
451
|
shutil.copy2(configs_package_json, build_package_json);
|
|
432
452
|
}
|
|
433
453
|
try {
|
|
434
|
-
# Install to .client
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
check=True
|
|
439
|
-
capture_output=True,
|
|
440
|
-
text=True
|
|
454
|
+
# Install to .jac/client/node_modules with progress feedback
|
|
455
|
+
print("\n ⏳ Installing dependencies...\n", flush=True);
|
|
456
|
+
start_time = time.time();
|
|
457
|
+
result = subprocess.run(
|
|
458
|
+
['bun', 'install'], cwd=build_dir, check=False, text=True
|
|
441
459
|
);
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
460
|
+
elapsed = time.time() - start_time;
|
|
461
|
+
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") ;
|
|
467
|
+
}
|
|
468
|
+
print(f"\n ✔ Dependencies installed ({elapsed:.1f}s)", flush=True);
|
|
446
469
|
} except FileNotFoundError {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
470
|
+
# This shouldn't happen since we check for bun at the start
|
|
471
|
+
raise ClientBundleError(
|
|
472
|
+
'Bun command not found. Install Bun: https://bun.sh'
|
|
473
|
+
) from None ;
|
|
450
474
|
}
|
|
451
475
|
}
|
|
452
|
-
build_dir = self.project_dir / '.client-build';
|
|
453
476
|
if self.config_path {
|
|
454
477
|
# Make config path relative to build_dir (where vite runs from)
|
|
455
|
-
|
|
456
|
-
if '.client-build' in config_str {
|
|
457
|
-
# Config is in .client-build/, make it relative to build_dir
|
|
478
|
+
try {
|
|
458
479
|
config_rel = self.config_path.relative_to(build_dir);
|
|
459
|
-
command = ['
|
|
460
|
-
}
|
|
461
|
-
# Config is outside
|
|
462
|
-
command = [
|
|
480
|
+
command = ['bun', 'x', 'vite', 'build', '--config', str(config_rel)];
|
|
481
|
+
} except ValueError {
|
|
482
|
+
# Config is outside client build dir, use absolute path
|
|
483
|
+
command = [
|
|
484
|
+
'bun',
|
|
485
|
+
'x',
|
|
486
|
+
'vite',
|
|
487
|
+
'build',
|
|
488
|
+
'--config',
|
|
489
|
+
str(self.config_path)
|
|
490
|
+
];
|
|
463
491
|
}
|
|
464
492
|
} elif entry_file {
|
|
465
493
|
generated_config = self.create_vite_config(entry_file);
|
|
466
|
-
# Config is in
|
|
494
|
+
# Config is in configs/, make it relative to build_dir
|
|
467
495
|
config_rel = generated_config.relative_to(build_dir);
|
|
468
|
-
command = ['
|
|
496
|
+
command = ['bun', 'x', 'vite', 'build', '--config', str(config_rel)];
|
|
469
497
|
} else {
|
|
470
|
-
command = ['
|
|
498
|
+
command = ['bun', 'run', 'build'];
|
|
471
499
|
}
|
|
472
|
-
# Run vite from
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
);
|
|
500
|
+
# Run vite from client build directory so it can find node_modules
|
|
501
|
+
print("\n ⏳ Building client bundle...\n", flush=True);
|
|
502
|
+
start_time = time.time();
|
|
503
|
+
result = subprocess.run(command, cwd=build_dir, check=False, text=True);
|
|
504
|
+
elapsed = time.time() - start_time;
|
|
476
505
|
if result.returncode != 0 {
|
|
477
|
-
|
|
506
|
+
print(f"\n ✖ Vite build failed after {elapsed:.1f}s", file=sys.stderr);
|
|
478
507
|
raise ClientBundleError(
|
|
479
|
-
f"Vite build failed
|
|
508
|
+
f"Vite build failed (see output above)\nCommand: {' '.join(command)}"
|
|
480
509
|
) from None ;
|
|
481
510
|
}
|
|
511
|
+
print(f"\n ✔ Client bundle built ({elapsed:.1f}s)", flush=True);
|
|
482
512
|
} finally {
|
|
483
|
-
# Clean up temporary package.json in
|
|
513
|
+
# Clean up temporary package.json in client build dir
|
|
484
514
|
build_package_json = build_dir / 'package.json';
|
|
485
515
|
if build_package_json.exists() {
|
|
486
516
|
build_package_json.unlink();
|
|
487
517
|
}
|
|
488
|
-
# Move
|
|
489
|
-
|
|
490
|
-
if
|
|
491
|
-
|
|
492
|
-
if
|
|
493
|
-
|
|
518
|
+
# Move bun.lockb to configs/ if it exists
|
|
519
|
+
build_bun_lockb = build_dir / 'bun.lockb';
|
|
520
|
+
if build_bun_lockb.exists() {
|
|
521
|
+
configs_bun_lockb = build_dir / 'configs' / 'bun.lockb';
|
|
522
|
+
if configs_bun_lockb.exists() {
|
|
523
|
+
configs_bun_lockb.unlink();
|
|
494
524
|
}
|
|
495
|
-
|
|
525
|
+
build_bun_lockb.rename(configs_bun_lockb);
|
|
496
526
|
}
|
|
497
527
|
}
|
|
498
528
|
}
|
|
@@ -506,8 +536,218 @@ impl ViteBundler.init(
|
|
|
506
536
|
config_path: Optional[Path] = None
|
|
507
537
|
) {
|
|
508
538
|
self.project_dir = project_dir;
|
|
509
|
-
self.output_dir = output_dir or (project_dir / '.client-build' / 'dist');
|
|
510
539
|
self.minify = minify;
|
|
511
540
|
self.config_path = config_path;
|
|
512
541
|
self.config_loader = JacClientConfig(project_dir);
|
|
542
|
+
# Set output_dir after config_loader is initialized so _get_client_dir works
|
|
543
|
+
self.output_dir = output_dir or (self._get_client_dir() / 'dist');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
"""Create a dev-mode vite config with API proxy for HMR."""
|
|
547
|
+
impl ViteBundler.create_dev_vite_config(
|
|
548
|
+
self: ViteBundler, entry_file: Path, api_port: int = 8000
|
|
549
|
+
) -> Path {
|
|
550
|
+
build_dir = self._get_client_dir();
|
|
551
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
552
|
+
configs_dir = build_dir / 'configs';
|
|
553
|
+
configs_dir.mkdir(exist_ok=True);
|
|
554
|
+
config_path = configs_dir / 'vite.dev.config.js';
|
|
555
|
+
# Get entry file relative path
|
|
556
|
+
try {
|
|
557
|
+
entry_relative = entry_file.relative_to(build_dir).as_posix();
|
|
558
|
+
} except ValueError {
|
|
559
|
+
entry_relative = entry_file.as_posix();
|
|
560
|
+
}
|
|
561
|
+
# Calculate paths for aliases
|
|
562
|
+
if entry_relative.endswith('/build/main.js') {
|
|
563
|
+
compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
|
|
564
|
+
compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
|
|
565
|
+
} elif entry_relative.endswith('build/main.js') {
|
|
566
|
+
compiled_utils_relative = 'compiled/client_runtime.js';
|
|
567
|
+
compiled_assets_relative = 'compiled/assets';
|
|
568
|
+
} else {
|
|
569
|
+
compiled_utils_relative = 'compiled/client_runtime.js';
|
|
570
|
+
compiled_assets_relative = 'compiled/assets';
|
|
571
|
+
}
|
|
572
|
+
# Extensions for TypeScript
|
|
573
|
+
extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'];
|
|
574
|
+
extensions_str = ', '.join(f'"{ext}"' for ext in extensions);
|
|
575
|
+
# Generate dev config with proxy for API routes
|
|
576
|
+
config_content = f'''import {{ defineConfig }} from "vite";
|
|
577
|
+
import path from "path";
|
|
578
|
+
import {{ fileURLToPath }} from "url";
|
|
579
|
+
import react from "@vitejs/plugin-react";
|
|
580
|
+
|
|
581
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
582
|
+
const buildDir = path.resolve(__dirname, "..");
|
|
583
|
+
const projectRoot = path.resolve(__dirname, "../../..");
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Vite DEV configuration for HMR mode
|
|
587
|
+
* Proxies API routes to Python server at localhost:{api_port}
|
|
588
|
+
*/
|
|
589
|
+
export default defineConfig({{
|
|
590
|
+
plugins: [react()],
|
|
591
|
+
root: buildDir,
|
|
592
|
+
publicDir: false,
|
|
593
|
+
build: {{
|
|
594
|
+
sourcemap: true, // Enable source maps for better error messages
|
|
595
|
+
}},
|
|
596
|
+
server: {{
|
|
597
|
+
watch: {{
|
|
598
|
+
usePolling: true,
|
|
599
|
+
interval: 100,
|
|
600
|
+
}},
|
|
601
|
+
proxy: {{
|
|
602
|
+
"/walker": {{
|
|
603
|
+
target: "http://localhost:{api_port}",
|
|
604
|
+
changeOrigin: true,
|
|
605
|
+
}},
|
|
606
|
+
"/function": {{
|
|
607
|
+
target: "http://localhost:{api_port}",
|
|
608
|
+
changeOrigin: true,
|
|
609
|
+
}},
|
|
610
|
+
"/user": {{
|
|
611
|
+
target: "http://localhost:{api_port}",
|
|
612
|
+
changeOrigin: true,
|
|
613
|
+
}},
|
|
614
|
+
"/introspect": {{
|
|
615
|
+
target: "http://localhost:{api_port}",
|
|
616
|
+
changeOrigin: true,
|
|
617
|
+
}},
|
|
618
|
+
}},
|
|
619
|
+
}},
|
|
620
|
+
resolve: {{
|
|
621
|
+
alias: {{
|
|
622
|
+
"@jac/runtime": path.resolve(buildDir, "{compiled_utils_relative}"),
|
|
623
|
+
"@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
|
|
624
|
+
}},
|
|
625
|
+
extensions: [{extensions_str}],
|
|
626
|
+
}},
|
|
627
|
+
}});
|
|
628
|
+
''';
|
|
629
|
+
config_path.write_text(config_content, encoding='utf-8');
|
|
630
|
+
return config_path;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
"""Create config files from jac.toml [plugins.client.configs].
|
|
634
|
+
|
|
635
|
+
Generates JavaScript config files (e.g., postcss.config.js, tailwind.config.js)
|
|
636
|
+
from TOML configuration. Each key in [plugins.client.configs] becomes a config file.
|
|
637
|
+
|
|
638
|
+
Example jac.toml:
|
|
639
|
+
[plugins.client.configs.postcss]
|
|
640
|
+
plugins = ["tailwindcss", "autoprefixer"]
|
|
641
|
+
|
|
642
|
+
[plugins.client.configs.tailwind]
|
|
643
|
+
content = ["./src/**/*.{js,jsx}"]
|
|
644
|
+
|
|
645
|
+
This generates:
|
|
646
|
+
- .jac/client/configs/postcss.config.js
|
|
647
|
+
- .jac/client/configs/tailwind.config.js
|
|
648
|
+
"""
|
|
649
|
+
impl ViteBundler.create_config_files(self: ViteBundler) -> list[Path] {
|
|
650
|
+
configs = self.config_loader.get_configs();
|
|
651
|
+
if not configs {
|
|
652
|
+
return [];
|
|
653
|
+
}
|
|
654
|
+
build_dir = self._get_client_dir();
|
|
655
|
+
configs_dir = build_dir / 'configs';
|
|
656
|
+
configs_dir.mkdir(parents=True, exist_ok=True);
|
|
657
|
+
created_files: list[Path] = [];
|
|
658
|
+
for (config_name, config_data) in configs.items() {
|
|
659
|
+
config_path = configs_dir / f'{config_name}.config.js';
|
|
660
|
+
|
|
661
|
+
# Convert the TOML config to JavaScript module.exports
|
|
662
|
+
js_content = _toml_config_to_js(config_name, config_data);
|
|
663
|
+
config_path.write_text(js_content, encoding='utf-8');
|
|
664
|
+
created_files.append(config_path);
|
|
665
|
+
}
|
|
666
|
+
return created_files;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
"""Convert TOML config data to JavaScript config file content.
|
|
670
|
+
|
|
671
|
+
Generates a generic module.exports with the config data as JSON.
|
|
672
|
+
"""
|
|
673
|
+
def _toml_config_to_js(config_name: str, config_data: dict) -> str {
|
|
674
|
+
return f"module.exports = {json.dumps(config_data, indent=2)};\n";
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
"""Start Vite dev server as a subprocess."""
|
|
678
|
+
impl ViteBundler.start_dev_server(self: ViteBundler, port: int = 3000) -> Any {
|
|
679
|
+
import sys;
|
|
680
|
+
import time;
|
|
681
|
+
import from jac_client.plugin.utils { ensure_bun_available }
|
|
682
|
+
# Ensure bun is available before starting dev server
|
|
683
|
+
if not ensure_bun_available() {
|
|
684
|
+
raise ClientBundleError(
|
|
685
|
+
'Bun is required for dev server. Install manually: https://bun.sh'
|
|
686
|
+
) from None ;
|
|
687
|
+
}
|
|
688
|
+
build_dir = self._get_client_dir();
|
|
689
|
+
node_modules = build_dir / 'node_modules';
|
|
690
|
+
# Create/update index.html for dev server (load from compiled/ for HMR)
|
|
691
|
+
index_html = build_dir / 'index.html';
|
|
692
|
+
index_content = '''<!DOCTYPE html>
|
|
693
|
+
<html lang="en">
|
|
694
|
+
<head>
|
|
695
|
+
<meta charset="UTF-8" />
|
|
696
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
697
|
+
<title>Jac App (Dev)</title>
|
|
698
|
+
</head>
|
|
699
|
+
<body>
|
|
700
|
+
<div id="root"></div>
|
|
701
|
+
<script type="module" src="/compiled/_entry.js"></script>
|
|
702
|
+
</body>
|
|
703
|
+
</html>
|
|
704
|
+
''';
|
|
705
|
+
index_html.write_text(index_content, encoding='utf-8');
|
|
706
|
+
# Ensure dependencies are installed
|
|
707
|
+
if not node_modules.exists() {
|
|
708
|
+
generated_package_json = build_dir / 'configs' / 'package.json';
|
|
709
|
+
if not generated_package_json.exists() {
|
|
710
|
+
self.create_package_json();
|
|
711
|
+
}
|
|
712
|
+
# Temporarily copy package.json for bun install
|
|
713
|
+
build_package_json = build_dir / 'package.json';
|
|
714
|
+
if not build_package_json.exists() {
|
|
715
|
+
shutil.copy2(generated_package_json, build_package_json);
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
print("\n ⏳ Installing dependencies...\n", flush=True);
|
|
719
|
+
start_time = time.time();
|
|
720
|
+
result = subprocess.run(
|
|
721
|
+
['bun', 'install'], cwd=build_dir, check=False, text=True
|
|
722
|
+
);
|
|
723
|
+
elapsed = time.time() - start_time;
|
|
724
|
+
if result.returncode != 0 {
|
|
725
|
+
print(
|
|
726
|
+
f"\n ✖ bun install failed after {elapsed:.1f}s", file=sys.stderr
|
|
727
|
+
);
|
|
728
|
+
raise ClientBundleError("Failed to install dependencies") ;
|
|
729
|
+
}
|
|
730
|
+
print(f"\n ✔ Dependencies installed ({elapsed:.1f}s)", flush=True);
|
|
731
|
+
} finally {
|
|
732
|
+
# Clean up temp package.json
|
|
733
|
+
if build_package_json.exists() {
|
|
734
|
+
build_package_json.unlink();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
# Find the dev config
|
|
739
|
+
dev_config = build_dir / 'configs' / 'vite.dev.config.js';
|
|
740
|
+
if not dev_config.exists() {
|
|
741
|
+
raise ClientBundleError(
|
|
742
|
+
"Dev config not found. Call create_dev_vite_config first."
|
|
743
|
+
) ;
|
|
744
|
+
}
|
|
745
|
+
config_rel = dev_config.relative_to(build_dir);
|
|
746
|
+
logger.debug(f"Starting Vite dev server on port {port}");
|
|
747
|
+
# Start Vite in dev mode (let output go to terminal for HMR visibility)
|
|
748
|
+
process = subprocess.Popen(
|
|
749
|
+
['bun', 'x', 'vite', '--config', str(config_rel), '--port', str(port)],
|
|
750
|
+
cwd=build_dir
|
|
751
|
+
);
|
|
752
|
+
return process;
|
|
513
753
|
}
|