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
jac_client/plugin/cli.jac
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Command line interface tool for the Jac Client.
|
|
2
2
|
|
|
3
3
|
This module extends the core `create` command to add client-side (frontend)
|
|
4
|
-
project setup via the --cl flag.
|
|
4
|
+
project setup via the --cl flag using the extend_command API.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os;
|
|
@@ -10,112 +10,82 @@ import sys;
|
|
|
10
10
|
import pathlib;
|
|
11
11
|
import subprocess;
|
|
12
12
|
import shutil;
|
|
13
|
-
import from jaclang.cli.
|
|
13
|
+
import from jaclang.cli.registry { get_registry }
|
|
14
|
+
import from jaclang.cli.command { Arg, ArgKind, HookContext }
|
|
14
15
|
import from jaclang.pycore.runtime { hookimpl }
|
|
15
|
-
import from jaclang.project.config {
|
|
16
|
+
import from jaclang.project.config { find_project_root }
|
|
16
17
|
|
|
17
18
|
"""Jac CLI extensions for client-side development."""
|
|
18
19
|
class JacCmd {
|
|
19
20
|
"""Create Jac CLI cmds."""
|
|
20
21
|
@hookimpl
|
|
21
22
|
static def create_cmd -> None {
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
_create_client_project(cwd, name, force, skip, verbose);
|
|
55
|
-
} else {
|
|
56
|
-
_create_core_project(cwd, name, force);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
# Register with PLUGIN priority to override core's create command
|
|
61
|
-
cmd_registry.register(
|
|
62
|
-
create, priority=CommandPriority.PLUGIN, source="jac-client"
|
|
23
|
+
"""Extend core create command to add --cl flag for client-side setup.""";
|
|
24
|
+
registry = get_registry();
|
|
25
|
+
|
|
26
|
+
# Extend the core 'create' command with client-specific arguments
|
|
27
|
+
registry.extend_command(
|
|
28
|
+
command_name="create",
|
|
29
|
+
args=[
|
|
30
|
+
Arg.create(
|
|
31
|
+
"cl",
|
|
32
|
+
typ=bool,
|
|
33
|
+
default=False,
|
|
34
|
+
help="Include client-side (frontend) setup",
|
|
35
|
+
short="c"
|
|
36
|
+
),
|
|
37
|
+
Arg.create(
|
|
38
|
+
"skip",
|
|
39
|
+
typ=bool,
|
|
40
|
+
default=False,
|
|
41
|
+
help="Skip installing default packages (only for --cl)",
|
|
42
|
+
short="s"
|
|
43
|
+
),
|
|
44
|
+
Arg.create(
|
|
45
|
+
"verbose",
|
|
46
|
+
typ=bool,
|
|
47
|
+
default=False,
|
|
48
|
+
help="Show detailed output during installation",
|
|
49
|
+
short="v"
|
|
50
|
+
),
|
|
51
|
+
|
|
52
|
+
],
|
|
53
|
+
pre_hook=_handle_client_create,
|
|
54
|
+
source="jac-client"
|
|
63
55
|
);
|
|
64
56
|
}
|
|
65
57
|
}
|
|
66
58
|
|
|
67
|
-
"""
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<>exit(1);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
project_name = name or cwd.name;
|
|
78
|
-
|
|
79
|
-
toml_path = cwd / "jac.toml";
|
|
80
|
-
if toml_path.exists() and not force {
|
|
81
|
-
print("jac.toml already exists. Use --force to overwrite.", file=sys.stderr);
|
|
82
|
-
<>exit(1);
|
|
59
|
+
"""Pre-hook to handle --cl flag for client-side project creation."""
|
|
60
|
+
def _handle_client_create(ctx: HookContext) -> None {
|
|
61
|
+
# Check if --cl flag is set
|
|
62
|
+
cl_flag = ctx.get_arg("cl", False);
|
|
63
|
+
if not cl_flag {
|
|
64
|
+
# Let core create command run normally
|
|
65
|
+
return;
|
|
83
66
|
}
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
main_jac = cwd / f"{name}.jac";
|
|
92
|
-
if not main_jac.exists() {
|
|
93
|
-
with open(main_jac, "w") as f {
|
|
94
|
-
f.write(
|
|
95
|
-
f'''"""Main entry point for {project_name}."""
|
|
96
|
-
|
|
97
|
-
with entry {{
|
|
98
|
-
print("Hello from {project_name}!");
|
|
99
|
-
}}
|
|
100
|
-
'''
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
print(f"Created {main_jac}");
|
|
104
|
-
}
|
|
68
|
+
# Handle client-side project creation
|
|
69
|
+
cwd = pathlib.Path(os.getcwd());
|
|
70
|
+
name = ctx.get_arg("name", "main");
|
|
71
|
+
force = ctx.get_arg("force", False);
|
|
72
|
+
skip_install = ctx.get_arg("skip", False);
|
|
73
|
+
verbose = ctx.get_arg("verbose", False);
|
|
105
74
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
75
|
+
try {
|
|
76
|
+
_create_client_project(cwd, name, force, skip_install, verbose);
|
|
77
|
+
# Cancel core handler with success code
|
|
78
|
+
ctx.set_data("cancel_execution", True);
|
|
79
|
+
ctx.set_data("cancel_return_code", 0);
|
|
80
|
+
} except SystemExit as e {
|
|
81
|
+
# Preserve exit code from sys.exit() calls
|
|
82
|
+
ctx.set_data("cancel_execution", True);
|
|
83
|
+
ctx.set_data("cancel_return_code", e.code if e.code is not None else 1);
|
|
84
|
+
} except Exception as e {
|
|
85
|
+
print(f"Error creating client project: {e}", file=sys.stderr);
|
|
86
|
+
ctx.set_data("cancel_execution", True);
|
|
87
|
+
ctx.set_data("cancel_return_code", 1);
|
|
110
88
|
}
|
|
111
|
-
|
|
112
|
-
_create_gitignore(cwd, client=False);
|
|
113
|
-
|
|
114
|
-
print(f"\nProject '{project_name}' initialized successfully!");
|
|
115
|
-
print("\nNext steps:");
|
|
116
|
-
print(" jac run main.jac # Run the main entry point");
|
|
117
|
-
print(" jac add <package> # Add dependencies");
|
|
118
|
-
print(" jac install # Install all dependencies");
|
|
119
89
|
}
|
|
120
90
|
|
|
121
91
|
"""Create a client-side Jac project with organized folder structure."""
|
|
@@ -166,23 +136,34 @@ def _create_client_project(
|
|
|
166
136
|
|
|
167
137
|
print(f"Creating Jac client application: {project_name}");
|
|
168
138
|
|
|
169
|
-
|
|
170
|
-
src_dir.mkdir(parents=True, exist_ok=True);
|
|
171
|
-
|
|
172
|
-
components_dir = src_dir / "components";
|
|
139
|
+
components_dir = project_path / "components";
|
|
173
140
|
components_dir.mkdir(parents=True, exist_ok=True);
|
|
174
141
|
|
|
175
142
|
assets_dir = project_path / "assets";
|
|
176
143
|
assets_dir.mkdir(parents=True, exist_ok=True);
|
|
177
144
|
|
|
178
|
-
(project_path / ".client
|
|
145
|
+
(project_path / ".jac" / "client").mkdir(parents=True, exist_ok=True);
|
|
179
146
|
|
|
180
147
|
toml_path = project_path / "jac.toml";
|
|
181
148
|
toml_content = f'''[project]
|
|
182
149
|
name = "{project_name}"
|
|
183
150
|
version = "1.0.0"
|
|
184
151
|
description = "Jac client application: {project_name}"
|
|
185
|
-
entry-point = "
|
|
152
|
+
entry-point = "main.jac"
|
|
153
|
+
|
|
154
|
+
[dependencies]
|
|
155
|
+
|
|
156
|
+
[dependencies.npm]
|
|
157
|
+
"jac-client-node" = "1.0.3"
|
|
158
|
+
|
|
159
|
+
[dependencies.npm.dev]
|
|
160
|
+
"@jac-client/dev-deps" = "1.0.0"
|
|
161
|
+
|
|
162
|
+
[dev-dependencies]
|
|
163
|
+
watchdog = ">=3.0.0"
|
|
164
|
+
|
|
165
|
+
[serve]
|
|
166
|
+
base_route_app = "app"
|
|
186
167
|
|
|
187
168
|
[plugins.client]
|
|
188
169
|
# Vite bundler configuration (optional overrides)
|
|
@@ -194,16 +175,16 @@ entry-point = "src/app.jac"
|
|
|
194
175
|
}
|
|
195
176
|
print(f"Created {toml_path}");
|
|
196
177
|
|
|
197
|
-
|
|
178
|
+
main_jac_content = '''"""Main entry point for the Jac client application."""
|
|
198
179
|
|
|
199
|
-
# Client-side imports
|
|
200
|
-
cl import from react {
|
|
201
|
-
cl import from
|
|
180
|
+
# Client-side imports (useState is auto-injected when using `has` variables)
|
|
181
|
+
cl import from react { useEffect }
|
|
182
|
+
cl import from .components.Button { Button }
|
|
202
183
|
|
|
203
184
|
# Client-side component
|
|
204
185
|
cl {
|
|
205
186
|
def:pub app() -> any {
|
|
206
|
-
|
|
187
|
+
has count: int = 0;
|
|
207
188
|
|
|
208
189
|
useEffect(lambda -> None {
|
|
209
190
|
console.log("Count updated:", count);
|
|
@@ -215,12 +196,12 @@ cl {
|
|
|
215
196
|
<div style={{display: "flex", gap: "1rem", marginTop: "1rem"}}>
|
|
216
197
|
<Button
|
|
217
198
|
label="Increment"
|
|
218
|
-
onClick={lambda -> None {
|
|
199
|
+
onClick={lambda -> None { count = count + 1; }}
|
|
219
200
|
variant="primary"
|
|
220
201
|
/>
|
|
221
202
|
<Button
|
|
222
203
|
label="Reset"
|
|
223
|
-
onClick={lambda -> None {
|
|
204
|
+
onClick={lambda -> None { count = 0; }}
|
|
224
205
|
variant="secondary"
|
|
225
206
|
/>
|
|
226
207
|
</div>
|
|
@@ -228,79 +209,62 @@ cl {
|
|
|
228
209
|
}
|
|
229
210
|
}
|
|
230
211
|
''';
|
|
231
|
-
with open(
|
|
232
|
-
f.write(
|
|
212
|
+
with open(project_path / "main.jac", 'w') as f {
|
|
213
|
+
f.write(main_jac_content);
|
|
233
214
|
}
|
|
234
|
-
print("Created
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
215
|
+
print("Created main.jac");
|
|
216
|
+
|
|
217
|
+
button_cl_jac_content = '''"""Button component for the Jac client application."""
|
|
218
|
+
|
|
219
|
+
def:pub Button(label: str, onClick: any, variant: str = "primary", disabled: bool = False) -> any {
|
|
220
|
+
base_styles = {
|
|
221
|
+
"padding": "0.75rem 1.5rem",
|
|
222
|
+
"fontSize": "1rem",
|
|
223
|
+
"fontWeight": "600",
|
|
224
|
+
"borderRadius": "0.5rem",
|
|
225
|
+
"border": "none",
|
|
226
|
+
"cursor": "not-allowed" if disabled else "pointer",
|
|
227
|
+
"transition": "all 0.2s ease"
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
variant_styles = {
|
|
231
|
+
"primary": {
|
|
232
|
+
"backgroundColor": "#9ca3af" if disabled else "#3b82f6",
|
|
233
|
+
"color": "#ffffff"
|
|
234
|
+
},
|
|
235
|
+
"secondary": {
|
|
236
|
+
"backgroundColor": "#e5e7eb" if disabled else "#6b7280",
|
|
237
|
+
"color": "#ffffff"
|
|
238
|
+
}
|
|
239
|
+
};
|
|
244
240
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
disabled = false
|
|
250
|
-
}) => {
|
|
251
|
-
const baseStyles: React.CSSProperties = {
|
|
252
|
-
padding: '0.75rem 1.5rem',
|
|
253
|
-
fontSize: '1rem',
|
|
254
|
-
fontWeight: '600',
|
|
255
|
-
borderRadius: '0.5rem',
|
|
256
|
-
border: 'none',
|
|
257
|
-
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
258
|
-
transition: 'all 0.2s ease',
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const variantStyles: Record<string, React.CSSProperties> = {
|
|
262
|
-
primary: {
|
|
263
|
-
backgroundColor: disabled ? '#9ca3af' : '#3b82f6',
|
|
264
|
-
color: '#ffffff',
|
|
265
|
-
},
|
|
266
|
-
secondary: {
|
|
267
|
-
backgroundColor: disabled ? '#e5e7eb' : '#6b7280',
|
|
268
|
-
color: '#ffffff',
|
|
269
|
-
},
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
return (
|
|
273
|
-
<button
|
|
274
|
-
style={{ ...baseStyles, ...variantStyles[variant] }}
|
|
275
|
-
onClick={onClick}
|
|
276
|
-
disabled={disabled}
|
|
241
|
+
return <button
|
|
242
|
+
style={{**base_styles, **variant_styles[variant]}}
|
|
243
|
+
onClick={onClick}
|
|
244
|
+
disabled={disabled}
|
|
277
245
|
>
|
|
278
|
-
|
|
279
|
-
</button
|
|
280
|
-
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
export default Button;
|
|
246
|
+
{label}
|
|
247
|
+
</button>;
|
|
248
|
+
}
|
|
284
249
|
''';
|
|
285
|
-
with open(components_dir / "Button.
|
|
286
|
-
f.write(
|
|
250
|
+
with open(components_dir / "Button.cl.jac", 'w') as f {
|
|
251
|
+
f.write(button_cl_jac_content);
|
|
287
252
|
}
|
|
288
|
-
print("Created
|
|
253
|
+
print("Created components/Button.cl.jac");
|
|
289
254
|
|
|
290
255
|
readme_content = f''' # {project_name}
|
|
291
256
|
|
|
292
257
|
|
|
293
|
-
A Jac client-side application with React
|
|
258
|
+
A Jac client-side application with React support.
|
|
294
259
|
|
|
295
260
|
## Project Structure
|
|
296
261
|
|
|
297
262
|
```
|
|
298
263
|
{project_name}/
|
|
299
264
|
├── jac.toml # Project configuration
|
|
300
|
-
├──
|
|
301
|
-
|
|
302
|
-
│ └──
|
|
303
|
-
│ └── Button.tsx # Example TypeScript component
|
|
265
|
+
├── main.jac # Main application entry
|
|
266
|
+
├── components/ # Reusable components
|
|
267
|
+
│ └── Button.cl.jac # Example Jac component
|
|
304
268
|
├── assets/ # Static assets (images, fonts, etc.)
|
|
305
269
|
└── build/ # Build output (generated)
|
|
306
270
|
```
|
|
@@ -310,15 +274,15 @@ A Jac client-side application with React and TypeScript support.
|
|
|
310
274
|
Start the development server:
|
|
311
275
|
|
|
312
276
|
```bash
|
|
313
|
-
jac
|
|
277
|
+
jac start main.jac
|
|
314
278
|
```
|
|
315
279
|
|
|
316
|
-
##
|
|
280
|
+
## Components
|
|
317
281
|
|
|
318
|
-
Create
|
|
282
|
+
Create Jac components in `components/` as `.cl.jac` files and import them:
|
|
319
283
|
|
|
320
284
|
```jac
|
|
321
|
-
cl import from
|
|
285
|
+
cl import from .components.Button {{ Button }}
|
|
322
286
|
```
|
|
323
287
|
|
|
324
288
|
## Adding Dependencies
|
|
@@ -334,7 +298,7 @@ jac add --cl react-router-dom
|
|
|
334
298
|
}
|
|
335
299
|
print("Created README.md");
|
|
336
300
|
|
|
337
|
-
_create_gitignore(project_path
|
|
301
|
+
_create_gitignore(project_path);
|
|
338
302
|
|
|
339
303
|
# Install default packages unless --skip is specified
|
|
340
304
|
if not skip {
|
|
@@ -346,15 +310,15 @@ jac add --cl react-router-dom
|
|
|
346
310
|
if name and name != cwd.name {
|
|
347
311
|
print("\nNext steps:");
|
|
348
312
|
print(f" cd {name}");
|
|
349
|
-
print(" jac
|
|
313
|
+
print(" jac start main.jac");
|
|
350
314
|
} else {
|
|
351
315
|
print("\nNext steps:");
|
|
352
|
-
print(" jac
|
|
316
|
+
print(" jac start main.jac");
|
|
353
317
|
}
|
|
354
318
|
}
|
|
355
319
|
|
|
356
|
-
"""Create .gitignore file with
|
|
357
|
-
def _create_gitignore(project_path: pathlib.Path
|
|
320
|
+
"""Create .gitignore file with client-specific entries."""
|
|
321
|
+
def _create_gitignore(project_path: pathlib.Path) -> None {
|
|
358
322
|
gitignore_entries = [
|
|
359
323
|
"# Jac project",
|
|
360
324
|
"packages/",
|
|
@@ -372,24 +336,17 @@ def _create_gitignore(project_path: pathlib.Path, client: bool = False) -> None
|
|
|
372
336
|
"# IDE",
|
|
373
337
|
".idea/",
|
|
374
338
|
".vscode/",
|
|
375
|
-
"*.swp"
|
|
339
|
+
"*.swp",
|
|
340
|
+
"",
|
|
341
|
+
"# Node.js",
|
|
342
|
+
"node_modules/",
|
|
343
|
+
"",
|
|
344
|
+
"# Jac build artifacts",
|
|
345
|
+
".jac/",
|
|
346
|
+
"*.session",
|
|
347
|
+
"*.session.*"
|
|
376
348
|
];
|
|
377
349
|
|
|
378
|
-
if client {
|
|
379
|
-
gitignore_entries.extend(
|
|
380
|
-
[
|
|
381
|
-
"",
|
|
382
|
-
"# Node.js",
|
|
383
|
-
"node_modules/",
|
|
384
|
-
"",
|
|
385
|
-
"# Jac Client",
|
|
386
|
-
".client-build/",
|
|
387
|
-
"*.session",
|
|
388
|
-
"*.session.*"
|
|
389
|
-
]
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
350
|
gitignore_path = project_path / ".gitignore";
|
|
394
351
|
if gitignore_path.exists() {
|
|
395
352
|
with open(gitignore_path, "r") as f {
|
|
@@ -415,7 +372,7 @@ def _create_gitignore(project_path: pathlib.Path, client: bool = False) -> None
|
|
|
415
372
|
}
|
|
416
373
|
}
|
|
417
374
|
|
|
418
|
-
"""Install default npm packages in .client
|
|
375
|
+
"""Install default npm packages in .jac/client directory."""
|
|
419
376
|
def _install_default_packages(
|
|
420
377
|
project_path: pathlib.Path, verbose: bool = False
|
|
421
378
|
) -> None {
|
|
@@ -441,13 +398,13 @@ def _install_default_packages(
|
|
|
441
398
|
# Generate package.json with default packages (defaults are added automatically)
|
|
442
399
|
bundler.create_package_json(project_name=project_name);
|
|
443
400
|
|
|
444
|
-
# Ensure .client
|
|
445
|
-
|
|
446
|
-
|
|
401
|
+
# Ensure .jac/client directory exists
|
|
402
|
+
client_dir = bundler._get_client_dir();
|
|
403
|
+
client_dir.mkdir(parents=True, exist_ok=True);
|
|
447
404
|
|
|
448
|
-
# Copy package.json to .client
|
|
449
|
-
configs_package_json =
|
|
450
|
-
build_package_json =
|
|
405
|
+
# Copy package.json to .jac/client/ for npm install
|
|
406
|
+
configs_package_json = client_dir / 'configs' / 'package.json';
|
|
407
|
+
build_package_json = client_dir / 'package.json';
|
|
451
408
|
|
|
452
409
|
if not configs_package_json.exists() {
|
|
453
410
|
print(
|
|
@@ -457,7 +414,7 @@ def _install_default_packages(
|
|
|
457
414
|
return;
|
|
458
415
|
}
|
|
459
416
|
|
|
460
|
-
# Always copy the generated package.json to .client
|
|
417
|
+
# Always copy the generated package.json to .jac/client/ for npm install
|
|
461
418
|
shutil.copy2(configs_package_json, build_package_json);
|
|
462
419
|
|
|
463
420
|
# Read package data for verbose output
|
|
@@ -485,27 +442,27 @@ def _install_default_packages(
|
|
|
485
442
|
print("\nRunning npm install...");
|
|
486
443
|
}
|
|
487
444
|
|
|
488
|
-
# Run npm install in .client
|
|
445
|
+
# Run npm install in .jac/client/ directory
|
|
489
446
|
try {
|
|
490
447
|
if verbose {
|
|
491
448
|
# Stream output for visibility in verbose mode
|
|
492
449
|
subprocess.run(
|
|
493
|
-
['npm', 'install', '--progress'], cwd=
|
|
450
|
+
['npm', 'install', '--progress'], cwd=client_dir, check=True
|
|
494
451
|
);
|
|
495
452
|
} else {
|
|
496
453
|
# Quiet mode: capture output
|
|
497
454
|
subprocess.run(
|
|
498
455
|
['npm', 'install'],
|
|
499
|
-
cwd=
|
|
456
|
+
cwd=client_dir,
|
|
500
457
|
check=True,
|
|
501
458
|
capture_output=True,
|
|
502
459
|
text=True
|
|
503
460
|
);
|
|
504
461
|
}
|
|
505
462
|
|
|
506
|
-
# Move package-lock.json to
|
|
507
|
-
build_package_lock =
|
|
508
|
-
configs_dir =
|
|
463
|
+
# Move package-lock.json to configs/ if it was created
|
|
464
|
+
build_package_lock = client_dir / 'package-lock.json';
|
|
465
|
+
configs_dir = client_dir / 'configs';
|
|
509
466
|
configs_package_lock = configs_dir / 'package-lock.json';
|
|
510
467
|
if build_package_lock.exists() {
|
|
511
468
|
configs_dir.mkdir(parents=True, exist_ok=True);
|
|
@@ -535,7 +492,7 @@ def _install_default_packages(
|
|
|
535
492
|
);
|
|
536
493
|
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
537
494
|
} finally {
|
|
538
|
-
# Clean up temporary package.json in .client
|
|
495
|
+
# Clean up temporary package.json in .jac/client/
|
|
539
496
|
if build_package_json.exists() {
|
|
540
497
|
build_package_json.unlink();
|
|
541
498
|
}
|
jac_client/plugin/client.jac
CHANGED
|
@@ -18,16 +18,6 @@ glob JsonValue: TypeAlias = None | str | int | float | bool | list['JsonValue']
|
|
|
18
18
|
],
|
|
19
19
|
StatusCode: TypeAlias = Literal[(200, 201, 400, 401, 404, 503)];
|
|
20
20
|
|
|
21
|
-
"""Jac Client Module Introspector."""
|
|
22
|
-
class JacClientModuleIntrospector(ModuleIntrospector) {
|
|
23
|
-
def render_page(
|
|
24
|
-
self: JacClientModuleIntrospector,
|
|
25
|
-
function_name: str,
|
|
26
|
-
args: dict[(str, Any)],
|
|
27
|
-
username: str
|
|
28
|
-
) -> dict[str, Any];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
21
|
"""Jac Client."""
|
|
32
22
|
class JacClient {
|
|
33
23
|
@hookimpl
|
|
@@ -38,15 +28,18 @@ class JacClient {
|
|
|
38
28
|
module: types.ModuleType, force: bool = False
|
|
39
29
|
) -> ClientBundle;
|
|
40
30
|
|
|
41
|
-
@hookimpl
|
|
42
|
-
static def get_module_introspector(
|
|
43
|
-
module_name: str, base_path: (str | None)
|
|
44
|
-
) -> ModuleIntrospector;
|
|
45
|
-
|
|
46
31
|
@hookimpl
|
|
47
32
|
static def send_static_file(
|
|
48
33
|
handler: BaseHTTPRequestHandler,
|
|
49
34
|
file_path: Path,
|
|
50
35
|
content_type: (str | None) = None
|
|
51
36
|
) -> None;
|
|
37
|
+
|
|
38
|
+
@hookimpl
|
|
39
|
+
static def render_page(
|
|
40
|
+
introspector: ModuleIntrospector,
|
|
41
|
+
function_name: str,
|
|
42
|
+
args: dict[(str, Any)],
|
|
43
|
+
username: str
|
|
44
|
+
) -> dict[str, Any];
|
|
52
45
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Client-side runtime for Jac JSX and walker interactions."""
|
|
2
2
|
|
|
3
3
|
import from 'react' { * as React }
|
|
4
|
+
import from 'react' { useState as reactUseState }
|
|
4
5
|
import from 'react-dom/client' { * as ReactDOM }
|
|
5
6
|
import from 'react-router-dom' {
|
|
6
7
|
HashRouter as ReactRouterHashRouter,
|
|
@@ -13,9 +14,11 @@ import from 'react-router-dom' {
|
|
|
13
14
|
useParams as reactRouterUseParams
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
def
|
|
17
|
+
def:pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# React hooks re-exported for auto-injection by `has` variables
|
|
20
|
+
glob:pub useState = reactUseState,
|
|
21
|
+
Router = ReactRouterHashRouter,
|
|
19
22
|
Routes = ReactRouterRoutes,
|
|
20
23
|
Route = ReactRouterRoute,
|
|
21
24
|
Link = ReactRouterLink,
|
|
@@ -24,15 +27,16 @@ glob : pub Router = ReactRouterHashRouter,
|
|
|
24
27
|
useLocation = reactRouterUseLocation,
|
|
25
28
|
useParams = reactRouterUseParams;
|
|
26
29
|
|
|
27
|
-
def
|
|
28
|
-
def
|
|
29
|
-
async def
|
|
30
|
-
def
|
|
31
|
-
async def
|
|
32
|
-
async def
|
|
33
|
-
async def
|
|
34
|
-
def
|
|
35
|
-
def
|
|
36
|
-
def
|
|
37
|
-
def
|
|
38
|
-
def
|
|
30
|
+
def:pub useRouter -> dict;
|
|
31
|
+
def:pub navigate(path: str) -> None;
|
|
32
|
+
async def:pub __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
|
|
33
|
+
def:pub jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
|
|
34
|
+
async def:pub __jacCallFunction(function_name: str, args: dict = {}) -> any;
|
|
35
|
+
async def:pub jacSignup(username: str, password: str) -> dict;
|
|
36
|
+
async def:pub jacLogin(username: str, password: str) -> bool;
|
|
37
|
+
def:pub jacLogout -> None;
|
|
38
|
+
def:pub jacIsLoggedIn -> bool;
|
|
39
|
+
def:pub __getLocalStorage(key: str) -> str;
|
|
40
|
+
def:pub __setLocalStorage(key: str, value: str) -> None;
|
|
41
|
+
def:pub __removeLocalStorage(key: str) -> None;
|
|
42
|
+
# React Router components
|