jac-client 0.2.2__py3-none-any.whl → 0.2.6__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/assets/workers/worker.py +5 -0
- jac_client/examples/all-in-one/src/app.jac +841 -0
- jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
- jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
- jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
- jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
- jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +7 -7
- jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/basic-auth/src/app.jac +377 -0
- jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
- jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
- jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
- jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
- jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
- jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
- jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
- jac_client/examples/little-x/src/submit-button.jac +16 -0
- jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
- jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
- jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
- jac_client/examples/ts-support/src/app.jac +35 -0
- jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
- jac_client/plugin/cli.jac +547 -0
- jac_client/plugin/client.jac +52 -0
- jac_client/plugin/client_runtime.cl.jac +38 -0
- jac_client/plugin/impl/client.impl.jac +134 -0
- jac_client/plugin/impl/client_runtime.impl.jac +177 -0
- jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
- jac_client/plugin/plugin_config.jac +195 -0
- jac_client/plugin/src/__init__.jac +20 -0
- jac_client/plugin/src/asset_processor.jac +33 -0
- jac_client/plugin/src/babel_processor.jac +18 -0
- jac_client/plugin/src/compiler.jac +66 -0
- jac_client/plugin/src/config_loader.jac +32 -0
- jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
- jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
- jac_client/plugin/src/impl/compiler.impl.jac +251 -0
- jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
- jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
- jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
- jac_client/plugin/src/import_processor.jac +19 -0
- jac_client/plugin/src/jac_to_js.jac +35 -0
- jac_client/plugin/src/package_installer.jac +26 -0
- jac_client/plugin/src/vite_bundler.jac +36 -0
- jac_client/plugin/vite_client_bundle.jac +31 -0
- jac_client/tests/conftest.py +281 -0
- jac_client/tests/fixtures/basic-app/app.jac +2 -2
- jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
- jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
- jac_client/tests/fixtures/js_import/app.jac +5 -5
- jac_client/tests/fixtures/spawn_test/app.jac +7 -7
- jac_client/tests/fixtures/with-ts/app.jac +35 -0
- jac_client/tests/test_cli.py +755 -0
- jac_client/tests/test_it.py +347 -67
- {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/METADATA +30 -24
- jac_client-0.2.6.dist-info/RECORD +74 -0
- {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/WHEEL +2 -1
- jac_client-0.2.6.dist-info/entry_points.txt +4 -0
- jac_client-0.2.6.dist-info/top_level.txt +1 -0
- jac_client/docs/README.md +0 -689
- jac_client/docs/advanced-state.md +0 -1265
- jac_client/docs/asset-serving/intro.md +0 -209
- jac_client/docs/assets/pipe_line-v2.svg +0 -32
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/file-system/app.jac.md +0 -121
- jac_client/docs/file-system/backend-frontend.md +0 -217
- jac_client/docs/file-system/intro.md +0 -72
- jac_client/docs/file-system/nested-imports.md +0 -348
- jac_client/docs/guide-example/intro.md +0 -115
- jac_client/docs/guide-example/step-01-setup.md +0 -270
- jac_client/docs/guide-example/step-02-components.md +0 -416
- jac_client/docs/guide-example/step-03-styling.md +0 -478
- jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
- jac_client/docs/guide-example/step-05-local-state.md +0 -530
- jac_client/docs/guide-example/step-06-events.md +0 -749
- jac_client/docs/guide-example/step-07-effects.md +0 -468
- jac_client/docs/guide-example/step-08-walkers.md +0 -534
- jac_client/docs/guide-example/step-09-authentication.md +0 -586
- jac_client/docs/guide-example/step-10-routing.md +0 -539
- jac_client/docs/guide-example/step-11-final.md +0 -963
- jac_client/docs/imports.md +0 -1141
- jac_client/docs/lifecycle-hooks.md +0 -773
- jac_client/docs/routing.md +0 -659
- jac_client/docs/styling/intro.md +0 -249
- jac_client/docs/styling/js-styling.md +0 -367
- jac_client/docs/styling/material-ui.md +0 -341
- jac_client/docs/styling/pure-css.md +0 -299
- jac_client/docs/styling/sass.md +0 -403
- jac_client/docs/styling/styled-components.md +0 -395
- jac_client/docs/styling/tailwind.md +0 -298
- jac_client/examples/all-in-one/.babelrc +0 -9
- jac_client/examples/all-in-one/README.md +0 -16
- jac_client/examples/all-in-one/app.jac +0 -426
- jac_client/examples/all-in-one/assets/burger.png +0 -0
- jac_client/examples/all-in-one/package.json +0 -29
- jac_client/examples/all-in-one/styles.css +0 -26
- jac_client/examples/all-in-one/vite.config.js +0 -28
- jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
- jac_client/examples/asset-serving/css-with-image/README.md +0 -91
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +0 -28
- jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
- jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
- jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
- jac_client/examples/asset-serving/image-asset/README.md +0 -119
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +0 -28
- jac_client/examples/asset-serving/image-asset/styles.css +0 -26
- jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
- jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
- jac_client/examples/asset-serving/import-alias/README.md +0 -83
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +0 -28
- jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
- jac_client/examples/basic/.babelrc +0 -9
- jac_client/examples/basic/README.md +0 -16
- jac_client/examples/basic/package.json +0 -27
- jac_client/examples/basic/vite.config.js +0 -27
- jac_client/examples/basic-auth/.babelrc +0 -9
- jac_client/examples/basic-auth/README.md +0 -16
- jac_client/examples/basic-auth/app.jac +0 -308
- jac_client/examples/basic-auth/package.json +0 -27
- jac_client/examples/basic-auth/vite.config.js +0 -27
- jac_client/examples/basic-auth-with-router/.babelrc +0 -9
- jac_client/examples/basic-auth-with-router/README.md +0 -60
- jac_client/examples/basic-auth-with-router/package.json +0 -28
- jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
- jac_client/examples/basic-full-stack/.babelrc +0 -9
- jac_client/examples/basic-full-stack/README.md +0 -18
- jac_client/examples/basic-full-stack/package.json +0 -28
- jac_client/examples/basic-full-stack/vite.config.js +0 -27
- jac_client/examples/css-styling/js-styling/.babelrc +0 -9
- jac_client/examples/css-styling/js-styling/README.md +0 -183
- jac_client/examples/css-styling/js-styling/package.json +0 -28
- jac_client/examples/css-styling/js-styling/styles.js +0 -100
- jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
- jac_client/examples/css-styling/material-ui/.babelrc +0 -9
- jac_client/examples/css-styling/material-ui/README.md +0 -16
- jac_client/examples/css-styling/material-ui/package.json +0 -32
- jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
- jac_client/examples/css-styling/pure-css/.babelrc +0 -9
- jac_client/examples/css-styling/pure-css/README.md +0 -16
- jac_client/examples/css-styling/pure-css/package.json +0 -28
- jac_client/examples/css-styling/pure-css/styles.css +0 -111
- jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
- jac_client/examples/css-styling/sass-example/.babelrc +0 -9
- jac_client/examples/css-styling/sass-example/README.md +0 -16
- jac_client/examples/css-styling/sass-example/package.json +0 -29
- jac_client/examples/css-styling/sass-example/styles.scss +0 -153
- jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
- jac_client/examples/css-styling/styled-components/.babelrc +0 -9
- jac_client/examples/css-styling/styled-components/README.md +0 -16
- jac_client/examples/css-styling/styled-components/package.json +0 -29
- jac_client/examples/css-styling/styled-components/styled.js +0 -90
- jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
- jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
- jac_client/examples/css-styling/tailwind-example/README.md +0 -16
- jac_client/examples/css-styling/tailwind-example/global.css +0 -1
- jac_client/examples/css-styling/tailwind-example/package.json +0 -30
- jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
- jac_client/examples/full-stack-with-auth/.babelrc +0 -9
- jac_client/examples/full-stack-with-auth/README.md +0 -16
- jac_client/examples/full-stack-with-auth/package.json +0 -28
- jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
- jac_client/examples/little-x/package.json +0 -23
- jac_client/examples/little-x/submit-button.jac +0 -8
- jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-advance/README.md +0 -77
- jac_client/examples/nested-folders/nested-advance/package.json +0 -29
- jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
- jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-basic/README.md +0 -183
- jac_client/examples/nested-folders/nested-basic/app.js +0 -7
- jac_client/examples/nested-folders/nested-basic/package.json +0 -28
- jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
- jac_client/examples/with-router/.babelrc +0 -9
- jac_client/examples/with-router/README.md +0 -17
- jac_client/examples/with-router/package.json +0 -28
- jac_client/examples/with-router/vite.config.js +0 -27
- jac_client/plugin/cli.py +0 -244
- jac_client/plugin/client.py +0 -152
- jac_client/plugin/client_runtime.jac +0 -234
- jac_client/plugin/vite_client_bundle.py +0 -503
- jac_client/tests/fixtures/js_import/utils.js +0 -21
- jac_client/tests/fixtures/package-lock.json +0 -329
- jac_client/tests/fixtures/package.json +0 -11
- jac_client/tests/test_asset_examples.py +0 -322
- jac_client/tests/test_cl.py +0 -530
- jac_client/tests/test_create_jac_app.py +0 -131
- jac_client/tests/test_nested_file.py +0 -374
- jac_client-0.2.2.dist-info/RECORD +0 -171
- jac_client-0.2.2.dist-info/entry_points.txt +0 -4
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
"""Command line interface tool for the Jac Client.
|
|
2
|
+
|
|
3
|
+
This module extends the core `create` command to add client-side (frontend)
|
|
4
|
+
project setup via the --cl flag.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os;
|
|
8
|
+
import re;
|
|
9
|
+
import sys;
|
|
10
|
+
import pathlib;
|
|
11
|
+
import subprocess;
|
|
12
|
+
import shutil;
|
|
13
|
+
import from jaclang.cli.cmdreg { cmd_registry, CommandPriority }
|
|
14
|
+
import from jaclang.pycore.runtime { hookimpl }
|
|
15
|
+
import from jaclang.project.config { JacConfig, find_project_root }
|
|
16
|
+
|
|
17
|
+
"""Jac CLI extensions for client-side development."""
|
|
18
|
+
class JacCmd {
|
|
19
|
+
"""Create Jac CLI cmds."""
|
|
20
|
+
@hookimpl
|
|
21
|
+
static def create_cmd -> None {
|
|
22
|
+
"""Override core create command to add --cl flag for client-side setup.""";
|
|
23
|
+
|
|
24
|
+
def create(
|
|
25
|
+
name: str = 'main',
|
|
26
|
+
force: bool = False,
|
|
27
|
+
cl: bool = False,
|
|
28
|
+
skip: bool = False,
|
|
29
|
+
verbose: bool = False
|
|
30
|
+
) -> None {
|
|
31
|
+
"""Initialize a new Jac project.
|
|
32
|
+
|
|
33
|
+
Creates a jac.toml configuration file and basic project structure.
|
|
34
|
+
Use --cl to include client-side (frontend) setup with Vite bundling.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
name: Project name (default: 'main')
|
|
38
|
+
force: Overwrite existing jac.toml if present
|
|
39
|
+
cl: Include client-side (frontend) setup
|
|
40
|
+
skip: Skip installing default packages (only for --cl projects)
|
|
41
|
+
verbose: Show detailed output during package installation
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
jac create
|
|
45
|
+
jac create myapp
|
|
46
|
+
jac create --cl myapp
|
|
47
|
+
jac create --cl --skip myapp
|
|
48
|
+
jac create --cl --verbose myapp
|
|
49
|
+
jac create --force
|
|
50
|
+
""";
|
|
51
|
+
cwd = pathlib.Path(os.getcwd());
|
|
52
|
+
|
|
53
|
+
if cl {
|
|
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"
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
"""Create a standard Jac project (core behavior)."""
|
|
68
|
+
def _create_core_project(cwd: pathlib.Path, name: str, force: bool) -> None {
|
|
69
|
+
existing = find_project_root(cwd);
|
|
70
|
+
if existing and not force {
|
|
71
|
+
(project_root, toml_path) = existing;
|
|
72
|
+
print(f"Already in a Jac project: {toml_path}", file=sys.stderr);
|
|
73
|
+
print("Use --force to reinitialize.", file=sys.stderr);
|
|
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);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
toml_content = JacConfig.create_default_toml(project_name);
|
|
86
|
+
with open(toml_path, "w") as f {
|
|
87
|
+
f.write(toml_content);
|
|
88
|
+
}
|
|
89
|
+
print(f"Created {toml_path}");
|
|
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
|
+
}
|
|
105
|
+
|
|
106
|
+
packages_dir = cwd / "packages";
|
|
107
|
+
if not packages_dir.exists() {
|
|
108
|
+
packages_dir.mkdir(parents=True, exist_ok=True);
|
|
109
|
+
print(f"Created {packages_dir}/");
|
|
110
|
+
}
|
|
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
|
+
}
|
|
120
|
+
|
|
121
|
+
"""Create a client-side Jac project with organized folder structure."""
|
|
122
|
+
def _create_client_project(
|
|
123
|
+
cwd: pathlib.Path,
|
|
124
|
+
name: str,
|
|
125
|
+
force: bool,
|
|
126
|
+
skip: bool = False,
|
|
127
|
+
verbose: bool = False
|
|
128
|
+
) -> None {
|
|
129
|
+
project_name = name or cwd.name;
|
|
130
|
+
|
|
131
|
+
if not project_name or project_name == 'main' {
|
|
132
|
+
print(
|
|
133
|
+
"Error: Project name is required for client projects. Use: jac create --cl <name>",
|
|
134
|
+
file=sys.stderr
|
|
135
|
+
);
|
|
136
|
+
<>exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if not re.match('^[a-zA-Z0-9_-]+$', project_name) {
|
|
140
|
+
print(
|
|
141
|
+
"Error: Project name must contain only letters, numbers, hyphens, and underscores",
|
|
142
|
+
file=sys.stderr
|
|
143
|
+
);
|
|
144
|
+
<>exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
existing = find_project_root(cwd);
|
|
148
|
+
if existing and not force {
|
|
149
|
+
(project_root, toml_path) = existing;
|
|
150
|
+
print(f"Already in a Jac project: {toml_path}", file=sys.stderr);
|
|
151
|
+
print("Use --force to reinitialize.", file=sys.stderr);
|
|
152
|
+
<>exit(1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
project_path: pathlib.Path;
|
|
156
|
+
if name and name != cwd.name {
|
|
157
|
+
project_path = cwd / name;
|
|
158
|
+
if project_path.exists() and not force {
|
|
159
|
+
print(f"Error: Directory '{name}' already exists", file=sys.stderr);
|
|
160
|
+
<>exit(1);
|
|
161
|
+
}
|
|
162
|
+
project_path.mkdir(parents=True, exist_ok=True);
|
|
163
|
+
} else {
|
|
164
|
+
project_path = cwd;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
print(f"Creating Jac client application: {project_name}");
|
|
168
|
+
|
|
169
|
+
src_dir = project_path / "src";
|
|
170
|
+
src_dir.mkdir(parents=True, exist_ok=True);
|
|
171
|
+
|
|
172
|
+
components_dir = src_dir / "components";
|
|
173
|
+
components_dir.mkdir(parents=True, exist_ok=True);
|
|
174
|
+
|
|
175
|
+
assets_dir = project_path / "assets";
|
|
176
|
+
assets_dir.mkdir(parents=True, exist_ok=True);
|
|
177
|
+
|
|
178
|
+
(project_path / ".client-build").mkdir(parents=True, exist_ok=True);
|
|
179
|
+
|
|
180
|
+
toml_path = project_path / "jac.toml";
|
|
181
|
+
toml_content = f'''[project]
|
|
182
|
+
name = "{project_name}"
|
|
183
|
+
version = "1.0.0"
|
|
184
|
+
description = "Jac client application: {project_name}"
|
|
185
|
+
entry-point = "src/app.jac"
|
|
186
|
+
|
|
187
|
+
[plugins.client]
|
|
188
|
+
# Vite bundler configuration (optional overrides)
|
|
189
|
+
# vite.plugins = []
|
|
190
|
+
# vite.build = {{}}
|
|
191
|
+
''';
|
|
192
|
+
with open(toml_path, 'w') as f {
|
|
193
|
+
f.write(toml_content);
|
|
194
|
+
}
|
|
195
|
+
print(f"Created {toml_path}");
|
|
196
|
+
|
|
197
|
+
app_jac_content = '''"""Main entry point for the Jac client application."""
|
|
198
|
+
|
|
199
|
+
# Client-side imports
|
|
200
|
+
cl import from react { useState, useEffect }
|
|
201
|
+
cl import from ".components/Button.tsx" { Button }
|
|
202
|
+
|
|
203
|
+
# Client-side component
|
|
204
|
+
cl {
|
|
205
|
+
def:pub app() -> any {
|
|
206
|
+
[count, setCount] = useState(0);
|
|
207
|
+
|
|
208
|
+
useEffect(lambda -> None {
|
|
209
|
+
console.log("Count updated:", count);
|
|
210
|
+
}, [count]);
|
|
211
|
+
|
|
212
|
+
return <div style={{padding: "2rem", fontFamily: "Arial, sans-serif"}}>
|
|
213
|
+
<h1>Hello, World!</h1>
|
|
214
|
+
<p>Count: {count}</p>
|
|
215
|
+
<div style={{display: "flex", gap: "1rem", marginTop: "1rem"}}>
|
|
216
|
+
<Button
|
|
217
|
+
label="Increment"
|
|
218
|
+
onClick={lambda -> None { setCount(count + 1); }}
|
|
219
|
+
variant="primary"
|
|
220
|
+
/>
|
|
221
|
+
<Button
|
|
222
|
+
label="Reset"
|
|
223
|
+
onClick={lambda -> None { setCount(0); }}
|
|
224
|
+
variant="secondary"
|
|
225
|
+
/>
|
|
226
|
+
</div>
|
|
227
|
+
</div>;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
''';
|
|
231
|
+
with open(src_dir / "app.jac", 'w') as f {
|
|
232
|
+
f.write(app_jac_content);
|
|
233
|
+
}
|
|
234
|
+
print("Created src/app.jac");
|
|
235
|
+
|
|
236
|
+
button_tsx_content = '''import React from 'react';
|
|
237
|
+
|
|
238
|
+
interface ButtonProps {
|
|
239
|
+
label: string;
|
|
240
|
+
onClick?: () => void;
|
|
241
|
+
variant?: 'primary' | 'secondary';
|
|
242
|
+
disabled?: boolean;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const Button: React.FC<ButtonProps> = ({
|
|
246
|
+
label,
|
|
247
|
+
onClick,
|
|
248
|
+
variant = 'primary',
|
|
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}
|
|
277
|
+
>
|
|
278
|
+
{label}
|
|
279
|
+
</button>
|
|
280
|
+
);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export default Button;
|
|
284
|
+
''';
|
|
285
|
+
with open(components_dir / "Button.tsx", 'w') as f {
|
|
286
|
+
f.write(button_tsx_content);
|
|
287
|
+
}
|
|
288
|
+
print("Created src/components/Button.tsx");
|
|
289
|
+
|
|
290
|
+
readme_content = f''' # {project_name}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
A Jac client-side application with React and TypeScript support.
|
|
294
|
+
|
|
295
|
+
## Project Structure
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
{project_name}/
|
|
299
|
+
├── jac.toml # Project configuration
|
|
300
|
+
├── src/ # Source files
|
|
301
|
+
│ ├── app.jac # Main application entry
|
|
302
|
+
│ └── components/ # Reusable components
|
|
303
|
+
│ └── Button.tsx # Example TypeScript component
|
|
304
|
+
├── assets/ # Static assets (images, fonts, etc.)
|
|
305
|
+
└── build/ # Build output (generated)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Getting Started
|
|
309
|
+
|
|
310
|
+
Start the development server:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
jac serve src/app.jac
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## TypeScript Support
|
|
317
|
+
|
|
318
|
+
Create TypeScript components in `src/components/` and import them in your Jac files:
|
|
319
|
+
|
|
320
|
+
```jac
|
|
321
|
+
cl import from "./components/Button.tsx" {{ Button }}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Adding Dependencies
|
|
325
|
+
|
|
326
|
+
Add npm packages with the --cl flag:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
jac add --cl react-router-dom
|
|
330
|
+
```
|
|
331
|
+
''';
|
|
332
|
+
with open(project_path / "README.md", 'w') as f {
|
|
333
|
+
f.write(readme_content);
|
|
334
|
+
}
|
|
335
|
+
print("Created README.md");
|
|
336
|
+
|
|
337
|
+
_create_gitignore(project_path, client=True);
|
|
338
|
+
|
|
339
|
+
# Install default packages unless --skip is specified
|
|
340
|
+
if not skip {
|
|
341
|
+
print("\nInstalling default packages...");
|
|
342
|
+
_install_default_packages(project_path, verbose);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
print(f"\nProject '{project_name}' created successfully!");
|
|
346
|
+
if name and name != cwd.name {
|
|
347
|
+
print("\nNext steps:");
|
|
348
|
+
print(f" cd {name}");
|
|
349
|
+
print(" jac serve src/app.jac");
|
|
350
|
+
} else {
|
|
351
|
+
print("\nNext steps:");
|
|
352
|
+
print(" jac serve src/app.jac");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
"""Create .gitignore file with appropriate entries."""
|
|
357
|
+
def _create_gitignore(project_path: pathlib.Path, client: bool = False) -> None {
|
|
358
|
+
gitignore_entries = [
|
|
359
|
+
"# Jac project",
|
|
360
|
+
"packages/",
|
|
361
|
+
".jac_cache/",
|
|
362
|
+
"*.jbc",
|
|
363
|
+
"*.jir",
|
|
364
|
+
"__jaccache__/",
|
|
365
|
+
"",
|
|
366
|
+
"# Python",
|
|
367
|
+
"__pycache__/",
|
|
368
|
+
"*.py[cod]",
|
|
369
|
+
".venv/",
|
|
370
|
+
"venv/",
|
|
371
|
+
"",
|
|
372
|
+
"# IDE",
|
|
373
|
+
".idea/",
|
|
374
|
+
".vscode/",
|
|
375
|
+
"*.swp"
|
|
376
|
+
];
|
|
377
|
+
|
|
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
|
+
gitignore_path = project_path / ".gitignore";
|
|
394
|
+
if gitignore_path.exists() {
|
|
395
|
+
with open(gitignore_path, "r") as f {
|
|
396
|
+
existing_content = f.read();
|
|
397
|
+
}
|
|
398
|
+
new_entries: list = [];
|
|
399
|
+
for entry in gitignore_entries {
|
|
400
|
+
if entry and entry not in existing_content {
|
|
401
|
+
new_entries.append(entry);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if new_entries {
|
|
405
|
+
with open(gitignore_path, "a") as f {
|
|
406
|
+
f.write("\n" + "\n".join(new_entries));
|
|
407
|
+
}
|
|
408
|
+
print("Updated .gitignore");
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
with open(gitignore_path, "w") as f {
|
|
412
|
+
f.write("\n".join(gitignore_entries) + "\n");
|
|
413
|
+
}
|
|
414
|
+
print("Created .gitignore");
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
"""Install default npm packages in .client-build directory."""
|
|
419
|
+
def _install_default_packages(
|
|
420
|
+
project_path: pathlib.Path, verbose: bool = False
|
|
421
|
+
) -> None {
|
|
422
|
+
import from jac_client.plugin.src.vite_bundler { ViteBundler }
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
# Verify jac.toml exists
|
|
426
|
+
toml_path = project_path / "jac.toml";
|
|
427
|
+
if not toml_path.exists() {
|
|
428
|
+
print(
|
|
429
|
+
"Warning: jac.toml not found, skipping package installation",
|
|
430
|
+
file=sys.stderr
|
|
431
|
+
);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# Get project name from jac.toml or use directory name
|
|
436
|
+
project_name = project_path.name;
|
|
437
|
+
|
|
438
|
+
# Create ViteBundler instance (it will load config internally)
|
|
439
|
+
bundler = ViteBundler(project_path);
|
|
440
|
+
|
|
441
|
+
# Generate package.json with default packages (defaults are added automatically)
|
|
442
|
+
bundler.create_package_json(project_name=project_name);
|
|
443
|
+
|
|
444
|
+
# Ensure .client-build directory exists
|
|
445
|
+
build_dir = project_path / '.client-build';
|
|
446
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
447
|
+
|
|
448
|
+
# Copy package.json to .client-build/ for npm install
|
|
449
|
+
configs_package_json = build_dir / '.jac-client.configs' / 'package.json';
|
|
450
|
+
build_package_json = build_dir / 'package.json';
|
|
451
|
+
|
|
452
|
+
if not configs_package_json.exists() {
|
|
453
|
+
print(
|
|
454
|
+
"Warning: package.json was not generated, skipping package installation",
|
|
455
|
+
file=sys.stderr
|
|
456
|
+
);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
# Always copy the generated package.json to .client-build/ for npm install
|
|
461
|
+
shutil.copy2(configs_package_json, build_package_json);
|
|
462
|
+
|
|
463
|
+
# Read package data for verbose output
|
|
464
|
+
import json;
|
|
465
|
+
with open(configs_package_json, 'r') as f {
|
|
466
|
+
pkg_data = json.load(f);
|
|
467
|
+
}
|
|
468
|
+
deps = pkg_data.get('dependencies', {});
|
|
469
|
+
dev_deps = pkg_data.get('devDependencies', {});
|
|
470
|
+
|
|
471
|
+
if verbose {
|
|
472
|
+
# Verbose mode: show detailed package list and stream npm output
|
|
473
|
+
if deps {
|
|
474
|
+
print(" Dependencies:");
|
|
475
|
+
for (name, version) in deps.items() {
|
|
476
|
+
print(f" - {name}@{version}");
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if dev_deps {
|
|
480
|
+
print(" Dev dependencies:");
|
|
481
|
+
for (name, version) in dev_deps.items() {
|
|
482
|
+
print(f" - {name}@{version}");
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
print("\nRunning npm install...");
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
# Run npm install in .client-build/ directory
|
|
489
|
+
try {
|
|
490
|
+
if verbose {
|
|
491
|
+
# Stream output for visibility in verbose mode
|
|
492
|
+
subprocess.run(
|
|
493
|
+
['npm', 'install', '--progress'], cwd=build_dir, check=True
|
|
494
|
+
);
|
|
495
|
+
} else {
|
|
496
|
+
# Quiet mode: capture output
|
|
497
|
+
subprocess.run(
|
|
498
|
+
['npm', 'install'],
|
|
499
|
+
cwd=build_dir,
|
|
500
|
+
check=True,
|
|
501
|
+
capture_output=True,
|
|
502
|
+
text=True
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
# Move package-lock.json to .jac-client.configs/ if it was created
|
|
507
|
+
build_package_lock = build_dir / 'package-lock.json';
|
|
508
|
+
configs_dir = build_dir / '.jac-client.configs';
|
|
509
|
+
configs_package_lock = configs_dir / 'package-lock.json';
|
|
510
|
+
if build_package_lock.exists() {
|
|
511
|
+
configs_dir.mkdir(parents=True, exist_ok=True);
|
|
512
|
+
if configs_package_lock.exists() {
|
|
513
|
+
configs_package_lock.unlink();
|
|
514
|
+
}
|
|
515
|
+
shutil.move(str(build_package_lock), str(configs_package_lock));
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
print("Default packages installed successfully");
|
|
519
|
+
} except subprocess.CalledProcessError as e {
|
|
520
|
+
if verbose {
|
|
521
|
+
print(
|
|
522
|
+
f"Warning: Failed to install packages (exit code {e.returncode})",
|
|
523
|
+
file=sys.stderr
|
|
524
|
+
);
|
|
525
|
+
} else {
|
|
526
|
+
print(
|
|
527
|
+
f"Warning: Failed to install packages: {e.stderr}", file=sys.stderr
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
531
|
+
} except FileNotFoundError {
|
|
532
|
+
print(
|
|
533
|
+
"Warning: npm command not found. Install Node.js and npm to install packages.",
|
|
534
|
+
file=sys.stderr
|
|
535
|
+
);
|
|
536
|
+
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
537
|
+
} finally {
|
|
538
|
+
# Clean up temporary package.json in .client-build/
|
|
539
|
+
if build_package_json.exists() {
|
|
540
|
+
build_package_json.unlink();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
} except Exception as e {
|
|
544
|
+
print(f"Warning: Could not install default packages: {e}", file=sys.stderr);
|
|
545
|
+
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Jac Client plugin for client-side rendering."""
|
|
2
|
+
import hashlib;
|
|
3
|
+
import html;
|
|
4
|
+
import mimetypes;
|
|
5
|
+
import types;
|
|
6
|
+
import from http.server { BaseHTTPRequestHandler }
|
|
7
|
+
import from pathlib { Path }
|
|
8
|
+
import from typing { Any, Literal, TypeAlias }
|
|
9
|
+
import from jaclang.runtimelib.client_bundle { ClientBundle, ClientBundleError }
|
|
10
|
+
import from jaclang.runtimelib.runtime { JacRuntime as Jac }
|
|
11
|
+
import from jaclang.runtimelib.runtime { hookimpl }
|
|
12
|
+
import from jaclang.runtimelib.server { ModuleIntrospector }
|
|
13
|
+
import from .vite_client_bundle { ViteClientBundleBuilder }
|
|
14
|
+
import from .src.vite_bundler { ViteBundler }
|
|
15
|
+
|
|
16
|
+
glob JsonValue: TypeAlias = None | str | int | float | bool | list['JsonValue'] | dict[
|
|
17
|
+
(str, 'JsonValue')
|
|
18
|
+
],
|
|
19
|
+
StatusCode: TypeAlias = Literal[(200, 201, 400, 401, 404, 503)];
|
|
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
|
+
"""Jac Client."""
|
|
32
|
+
class JacClient {
|
|
33
|
+
@hookimpl
|
|
34
|
+
static def get_client_bundle_builder -> ViteClientBundleBuilder;
|
|
35
|
+
|
|
36
|
+
@hookimpl
|
|
37
|
+
static def build_client_bundle(
|
|
38
|
+
module: types.ModuleType, force: bool = False
|
|
39
|
+
) -> ClientBundle;
|
|
40
|
+
|
|
41
|
+
@hookimpl
|
|
42
|
+
static def get_module_introspector(
|
|
43
|
+
module_name: str, base_path: (str | None)
|
|
44
|
+
) -> ModuleIntrospector;
|
|
45
|
+
|
|
46
|
+
@hookimpl
|
|
47
|
+
static def send_static_file(
|
|
48
|
+
handler: BaseHTTPRequestHandler,
|
|
49
|
+
file_path: Path,
|
|
50
|
+
content_type: (str | None) = None
|
|
51
|
+
) -> None;
|
|
52
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Client-side runtime for Jac JSX and walker interactions."""
|
|
2
|
+
|
|
3
|
+
import from 'react' { * as React }
|
|
4
|
+
import from 'react-dom/client' { * as ReactDOM }
|
|
5
|
+
import from 'react-router-dom' {
|
|
6
|
+
HashRouter as ReactRouterHashRouter,
|
|
7
|
+
Routes as ReactRouterRoutes,
|
|
8
|
+
Route as ReactRouterRoute,
|
|
9
|
+
Link as ReactRouterLink,
|
|
10
|
+
Navigate as ReactRouterNavigate,
|
|
11
|
+
useNavigate as reactRouterUseNavigate,
|
|
12
|
+
useLocation as reactRouterUseLocation,
|
|
13
|
+
useParams as reactRouterUseParams
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def : pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
|
|
17
|
+
|
|
18
|
+
glob : pub Router = ReactRouterHashRouter,
|
|
19
|
+
Routes = ReactRouterRoutes,
|
|
20
|
+
Route = ReactRouterRoute,
|
|
21
|
+
Link = ReactRouterLink,
|
|
22
|
+
Navigate = ReactRouterNavigate,
|
|
23
|
+
useNavigate = reactRouterUseNavigate,
|
|
24
|
+
useLocation = reactRouterUseLocation,
|
|
25
|
+
useParams = reactRouterUseParams;
|
|
26
|
+
|
|
27
|
+
def : pub useRouter -> dict;
|
|
28
|
+
def : pub navigate(path: str) -> None;
|
|
29
|
+
async def : pub __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
|
|
30
|
+
def : pub jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
|
|
31
|
+
async def : pub __jacCallFunction(function_name: str, args: dict = {}) -> any;
|
|
32
|
+
async def : pub jacSignup(username: str, password: str) -> dict;
|
|
33
|
+
async def : pub jacLogin(username: str, password: str) -> bool;
|
|
34
|
+
def : pub jacLogout -> None;
|
|
35
|
+
def : pub jacIsLoggedIn -> bool;
|
|
36
|
+
def : pub __getLocalStorage(key: str) -> str;
|
|
37
|
+
def : pub __setLocalStorage(key: str, value: str) -> None;
|
|
38
|
+
def : pub __removeLocalStorage(key: str) -> None;
|