jac-client 0.2.3__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 +494 -347
- jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
- jac_client/examples/all-in-one/button.jac +1 -1
- 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/button.jac +1 -1
- 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/{app.jac → src/app.jac} +4 -4
- jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +4 -4
- jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +5 -5
- jac_client/examples/basic/{app.jac → src/app.jac} +4 -4
- jac_client/examples/basic-auth/src/app.jac +371 -0
- jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +28 -28
- jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +166 -127
- jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +7 -7
- jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +7 -7
- jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +7 -7
- jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +7 -7
- jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +47 -47
- 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} +15 -15
- jac_client/plugin/cli.jac +504 -0
- jac_client/plugin/client.jac +45 -0
- jac_client/plugin/client_runtime.cl.jac +42 -0
- jac_client/plugin/impl/client.impl.jac +193 -0
- jac_client/plugin/impl/client_runtime.impl.jac +195 -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 +67 -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 +89 -0
- jac_client/plugin/src/impl/compiler.impl.jac +288 -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 +626 -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 +44 -0
- jac_client/plugin/vite_client_bundle.jac +31 -0
- jac_client/tests/conftest.py +283 -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 +15 -18
- jac_client/tests/fixtures/with-ts/app.jac +35 -0
- jac_client/tests/test_cli.py +811 -0
- jac_client/tests/test_it.py +592 -97
- {jac_client-0.2.3.dist-info → jac_client-0.2.8.dist-info}/METADATA +41 -34
- jac_client-0.2.8.dist-info/RECORD +97 -0
- {jac_client-0.2.3.dist-info → jac_client-0.2.8.dist-info}/WHEEL +2 -1
- jac_client-0.2.8.dist-info/entry_points.txt +4 -0
- jac_client-0.2.8.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/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.3.dist-info/RECORD +0 -171
- jac_client-0.2.3.dist-info/entry_points.txt +0 -4
|
@@ -0,0 +1,504 @@
|
|
|
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 using the extend_command API.
|
|
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.registry { get_registry }
|
|
14
|
+
import from jaclang.cli.command { Arg, ArgKind, HookContext }
|
|
15
|
+
import from jaclang.pycore.runtime { hookimpl }
|
|
16
|
+
import from jaclang.project.config { find_project_root }
|
|
17
|
+
|
|
18
|
+
"""Jac CLI extensions for client-side development."""
|
|
19
|
+
class JacCmd {
|
|
20
|
+
"""Create Jac CLI cmds."""
|
|
21
|
+
@hookimpl
|
|
22
|
+
static def create_cmd -> None {
|
|
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"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
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;
|
|
66
|
+
}
|
|
67
|
+
|
|
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);
|
|
74
|
+
|
|
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);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
"""Create a client-side Jac project with organized folder structure."""
|
|
92
|
+
def _create_client_project(
|
|
93
|
+
cwd: pathlib.Path,
|
|
94
|
+
name: str,
|
|
95
|
+
force: bool,
|
|
96
|
+
skip: bool = False,
|
|
97
|
+
verbose: bool = False
|
|
98
|
+
) -> None {
|
|
99
|
+
project_name = name or cwd.name;
|
|
100
|
+
|
|
101
|
+
if not project_name or project_name == 'main' {
|
|
102
|
+
print(
|
|
103
|
+
"Error: Project name is required for client projects. Use: jac create --cl <name>",
|
|
104
|
+
file=sys.stderr
|
|
105
|
+
);
|
|
106
|
+
<>exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if not re.match('^[a-zA-Z0-9_-]+$', project_name) {
|
|
110
|
+
print(
|
|
111
|
+
"Error: Project name must contain only letters, numbers, hyphens, and underscores",
|
|
112
|
+
file=sys.stderr
|
|
113
|
+
);
|
|
114
|
+
<>exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
existing = find_project_root(cwd);
|
|
118
|
+
if existing and not force {
|
|
119
|
+
(project_root, toml_path) = existing;
|
|
120
|
+
print(f"Already in a Jac project: {toml_path}", file=sys.stderr);
|
|
121
|
+
print("Use --force to reinitialize.", file=sys.stderr);
|
|
122
|
+
<>exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
project_path: pathlib.Path;
|
|
126
|
+
if name and name != cwd.name {
|
|
127
|
+
project_path = cwd / name;
|
|
128
|
+
if project_path.exists() and not force {
|
|
129
|
+
print(f"Error: Directory '{name}' already exists", file=sys.stderr);
|
|
130
|
+
<>exit(1);
|
|
131
|
+
}
|
|
132
|
+
project_path.mkdir(parents=True, exist_ok=True);
|
|
133
|
+
} else {
|
|
134
|
+
project_path = cwd;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
print(f"Creating Jac client application: {project_name}");
|
|
138
|
+
|
|
139
|
+
components_dir = project_path / "components";
|
|
140
|
+
components_dir.mkdir(parents=True, exist_ok=True);
|
|
141
|
+
|
|
142
|
+
assets_dir = project_path / "assets";
|
|
143
|
+
assets_dir.mkdir(parents=True, exist_ok=True);
|
|
144
|
+
|
|
145
|
+
(project_path / ".jac" / "client").mkdir(parents=True, exist_ok=True);
|
|
146
|
+
|
|
147
|
+
toml_path = project_path / "jac.toml";
|
|
148
|
+
toml_content = f'''[project]
|
|
149
|
+
name = "{project_name}"
|
|
150
|
+
version = "1.0.0"
|
|
151
|
+
description = "Jac client application: {project_name}"
|
|
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"
|
|
167
|
+
|
|
168
|
+
[plugins.client]
|
|
169
|
+
# Vite bundler configuration (optional overrides)
|
|
170
|
+
# vite.plugins = []
|
|
171
|
+
# vite.build = {{}}
|
|
172
|
+
''';
|
|
173
|
+
with open(toml_path, 'w') as f {
|
|
174
|
+
f.write(toml_content);
|
|
175
|
+
}
|
|
176
|
+
print(f"Created {toml_path}");
|
|
177
|
+
|
|
178
|
+
main_jac_content = '''"""Main entry point for the Jac client application."""
|
|
179
|
+
|
|
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 }
|
|
183
|
+
|
|
184
|
+
# Client-side component
|
|
185
|
+
cl {
|
|
186
|
+
def:pub app() -> any {
|
|
187
|
+
has count: int = 0;
|
|
188
|
+
|
|
189
|
+
useEffect(lambda -> None {
|
|
190
|
+
console.log("Count updated:", count);
|
|
191
|
+
}, [count]);
|
|
192
|
+
|
|
193
|
+
return <div style={{padding: "2rem", fontFamily: "Arial, sans-serif"}}>
|
|
194
|
+
<h1>Hello, World!</h1>
|
|
195
|
+
<p>Count: {count}</p>
|
|
196
|
+
<div style={{display: "flex", gap: "1rem", marginTop: "1rem"}}>
|
|
197
|
+
<Button
|
|
198
|
+
label="Increment"
|
|
199
|
+
onClick={lambda -> None { count = count + 1; }}
|
|
200
|
+
variant="primary"
|
|
201
|
+
/>
|
|
202
|
+
<Button
|
|
203
|
+
label="Reset"
|
|
204
|
+
onClick={lambda -> None { count = 0; }}
|
|
205
|
+
variant="secondary"
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
</div>;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
''';
|
|
212
|
+
with open(project_path / "main.jac", 'w') as f {
|
|
213
|
+
f.write(main_jac_content);
|
|
214
|
+
}
|
|
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
|
+
};
|
|
240
|
+
|
|
241
|
+
return <button
|
|
242
|
+
style={{**base_styles, **variant_styles[variant]}}
|
|
243
|
+
onClick={onClick}
|
|
244
|
+
disabled={disabled}
|
|
245
|
+
>
|
|
246
|
+
{label}
|
|
247
|
+
</button>;
|
|
248
|
+
}
|
|
249
|
+
''';
|
|
250
|
+
with open(components_dir / "Button.cl.jac", 'w') as f {
|
|
251
|
+
f.write(button_cl_jac_content);
|
|
252
|
+
}
|
|
253
|
+
print("Created components/Button.cl.jac");
|
|
254
|
+
|
|
255
|
+
readme_content = f''' # {project_name}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
A Jac client-side application with React support.
|
|
259
|
+
|
|
260
|
+
## Project Structure
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
{project_name}/
|
|
264
|
+
├── jac.toml # Project configuration
|
|
265
|
+
├── main.jac # Main application entry
|
|
266
|
+
├── components/ # Reusable components
|
|
267
|
+
│ └── Button.cl.jac # Example Jac component
|
|
268
|
+
├── assets/ # Static assets (images, fonts, etc.)
|
|
269
|
+
└── build/ # Build output (generated)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Getting Started
|
|
273
|
+
|
|
274
|
+
Start the development server:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
jac start main.jac
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Components
|
|
281
|
+
|
|
282
|
+
Create Jac components in `components/` as `.cl.jac` files and import them:
|
|
283
|
+
|
|
284
|
+
```jac
|
|
285
|
+
cl import from .components.Button {{ Button }}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Adding Dependencies
|
|
289
|
+
|
|
290
|
+
Add npm packages with the --cl flag:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
jac add --cl react-router-dom
|
|
294
|
+
```
|
|
295
|
+
''';
|
|
296
|
+
with open(project_path / "README.md", 'w') as f {
|
|
297
|
+
f.write(readme_content);
|
|
298
|
+
}
|
|
299
|
+
print("Created README.md");
|
|
300
|
+
|
|
301
|
+
_create_gitignore(project_path);
|
|
302
|
+
|
|
303
|
+
# Install default packages unless --skip is specified
|
|
304
|
+
if not skip {
|
|
305
|
+
print("\nInstalling default packages...");
|
|
306
|
+
_install_default_packages(project_path, verbose);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
print(f"\nProject '{project_name}' created successfully!");
|
|
310
|
+
if name and name != cwd.name {
|
|
311
|
+
print("\nNext steps:");
|
|
312
|
+
print(f" cd {name}");
|
|
313
|
+
print(" jac start main.jac");
|
|
314
|
+
} else {
|
|
315
|
+
print("\nNext steps:");
|
|
316
|
+
print(" jac start main.jac");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
"""Create .gitignore file with client-specific entries."""
|
|
321
|
+
def _create_gitignore(project_path: pathlib.Path) -> None {
|
|
322
|
+
gitignore_entries = [
|
|
323
|
+
"# Jac project",
|
|
324
|
+
"packages/",
|
|
325
|
+
".jac_cache/",
|
|
326
|
+
"*.jbc",
|
|
327
|
+
"*.jir",
|
|
328
|
+
"__jaccache__/",
|
|
329
|
+
"",
|
|
330
|
+
"# Python",
|
|
331
|
+
"__pycache__/",
|
|
332
|
+
"*.py[cod]",
|
|
333
|
+
".venv/",
|
|
334
|
+
"venv/",
|
|
335
|
+
"",
|
|
336
|
+
"# IDE",
|
|
337
|
+
".idea/",
|
|
338
|
+
".vscode/",
|
|
339
|
+
"*.swp",
|
|
340
|
+
"",
|
|
341
|
+
"# Node.js",
|
|
342
|
+
"node_modules/",
|
|
343
|
+
"",
|
|
344
|
+
"# Jac build artifacts",
|
|
345
|
+
".jac/",
|
|
346
|
+
"*.session",
|
|
347
|
+
"*.session.*"
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
gitignore_path = project_path / ".gitignore";
|
|
351
|
+
if gitignore_path.exists() {
|
|
352
|
+
with open(gitignore_path, "r") as f {
|
|
353
|
+
existing_content = f.read();
|
|
354
|
+
}
|
|
355
|
+
new_entries: list = [];
|
|
356
|
+
for entry in gitignore_entries {
|
|
357
|
+
if entry and entry not in existing_content {
|
|
358
|
+
new_entries.append(entry);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if new_entries {
|
|
362
|
+
with open(gitignore_path, "a") as f {
|
|
363
|
+
f.write("\n" + "\n".join(new_entries));
|
|
364
|
+
}
|
|
365
|
+
print("Updated .gitignore");
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
with open(gitignore_path, "w") as f {
|
|
369
|
+
f.write("\n".join(gitignore_entries) + "\n");
|
|
370
|
+
}
|
|
371
|
+
print("Created .gitignore");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
"""Install default npm packages in .jac/client directory."""
|
|
376
|
+
def _install_default_packages(
|
|
377
|
+
project_path: pathlib.Path, verbose: bool = False
|
|
378
|
+
) -> None {
|
|
379
|
+
import from jac_client.plugin.src.vite_bundler { ViteBundler }
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
# Verify jac.toml exists
|
|
383
|
+
toml_path = project_path / "jac.toml";
|
|
384
|
+
if not toml_path.exists() {
|
|
385
|
+
print(
|
|
386
|
+
"Warning: jac.toml not found, skipping package installation",
|
|
387
|
+
file=sys.stderr
|
|
388
|
+
);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
# Get project name from jac.toml or use directory name
|
|
393
|
+
project_name = project_path.name;
|
|
394
|
+
|
|
395
|
+
# Create ViteBundler instance (it will load config internally)
|
|
396
|
+
bundler = ViteBundler(project_path);
|
|
397
|
+
|
|
398
|
+
# Generate package.json with default packages (defaults are added automatically)
|
|
399
|
+
bundler.create_package_json(project_name=project_name);
|
|
400
|
+
|
|
401
|
+
# Ensure .jac/client directory exists
|
|
402
|
+
client_dir = bundler._get_client_dir();
|
|
403
|
+
client_dir.mkdir(parents=True, exist_ok=True);
|
|
404
|
+
|
|
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';
|
|
408
|
+
|
|
409
|
+
if not configs_package_json.exists() {
|
|
410
|
+
print(
|
|
411
|
+
"Warning: package.json was not generated, skipping package installation",
|
|
412
|
+
file=sys.stderr
|
|
413
|
+
);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
# Always copy the generated package.json to .jac/client/ for npm install
|
|
418
|
+
shutil.copy2(configs_package_json, build_package_json);
|
|
419
|
+
|
|
420
|
+
# Read package data for verbose output
|
|
421
|
+
import json;
|
|
422
|
+
with open(configs_package_json, 'r') as f {
|
|
423
|
+
pkg_data = json.load(f);
|
|
424
|
+
}
|
|
425
|
+
deps = pkg_data.get('dependencies', {});
|
|
426
|
+
dev_deps = pkg_data.get('devDependencies', {});
|
|
427
|
+
|
|
428
|
+
if verbose {
|
|
429
|
+
# Verbose mode: show detailed package list and stream npm output
|
|
430
|
+
if deps {
|
|
431
|
+
print(" Dependencies:");
|
|
432
|
+
for (name, version) in deps.items() {
|
|
433
|
+
print(f" - {name}@{version}");
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if dev_deps {
|
|
437
|
+
print(" Dev dependencies:");
|
|
438
|
+
for (name, version) in dev_deps.items() {
|
|
439
|
+
print(f" - {name}@{version}");
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
print("\nRunning npm install...");
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
# Run npm install in .jac/client/ directory
|
|
446
|
+
try {
|
|
447
|
+
if verbose {
|
|
448
|
+
# Stream output for visibility in verbose mode
|
|
449
|
+
subprocess.run(
|
|
450
|
+
['npm', 'install', '--progress'], cwd=client_dir, check=True
|
|
451
|
+
);
|
|
452
|
+
} else {
|
|
453
|
+
# Quiet mode: capture output
|
|
454
|
+
subprocess.run(
|
|
455
|
+
['npm', 'install'],
|
|
456
|
+
cwd=client_dir,
|
|
457
|
+
check=True,
|
|
458
|
+
capture_output=True,
|
|
459
|
+
text=True
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
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';
|
|
466
|
+
configs_package_lock = configs_dir / 'package-lock.json';
|
|
467
|
+
if build_package_lock.exists() {
|
|
468
|
+
configs_dir.mkdir(parents=True, exist_ok=True);
|
|
469
|
+
if configs_package_lock.exists() {
|
|
470
|
+
configs_package_lock.unlink();
|
|
471
|
+
}
|
|
472
|
+
shutil.move(str(build_package_lock), str(configs_package_lock));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
print("Default packages installed successfully");
|
|
476
|
+
} except subprocess.CalledProcessError as e {
|
|
477
|
+
if verbose {
|
|
478
|
+
print(
|
|
479
|
+
f"Warning: Failed to install packages (exit code {e.returncode})",
|
|
480
|
+
file=sys.stderr
|
|
481
|
+
);
|
|
482
|
+
} else {
|
|
483
|
+
print(
|
|
484
|
+
f"Warning: Failed to install packages: {e.stderr}", file=sys.stderr
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
488
|
+
} except FileNotFoundError {
|
|
489
|
+
print(
|
|
490
|
+
"Warning: npm command not found. Install Node.js and npm to install packages.",
|
|
491
|
+
file=sys.stderr
|
|
492
|
+
);
|
|
493
|
+
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
494
|
+
} finally {
|
|
495
|
+
# Clean up temporary package.json in .jac/client/
|
|
496
|
+
if build_package_json.exists() {
|
|
497
|
+
build_package_json.unlink();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
} except Exception as e {
|
|
501
|
+
print(f"Warning: Could not install default packages: {e}", file=sys.stderr);
|
|
502
|
+
print("You can install packages later with: jac add --cl", file=sys.stderr);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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."""
|
|
22
|
+
class JacClient {
|
|
23
|
+
@hookimpl
|
|
24
|
+
static def get_client_bundle_builder -> ViteClientBundleBuilder;
|
|
25
|
+
|
|
26
|
+
@hookimpl
|
|
27
|
+
static def build_client_bundle(
|
|
28
|
+
module: types.ModuleType, force: bool = False
|
|
29
|
+
) -> ClientBundle;
|
|
30
|
+
|
|
31
|
+
@hookimpl
|
|
32
|
+
static def send_static_file(
|
|
33
|
+
handler: BaseHTTPRequestHandler,
|
|
34
|
+
file_path: Path,
|
|
35
|
+
content_type: (str | None) = None
|
|
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];
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Client-side runtime for Jac JSX and walker interactions."""
|
|
2
|
+
|
|
3
|
+
import from 'react' { * as React }
|
|
4
|
+
import from 'react' { useState as reactUseState }
|
|
5
|
+
import from 'react-dom/client' { * as ReactDOM }
|
|
6
|
+
import from 'react-router-dom' {
|
|
7
|
+
HashRouter as ReactRouterHashRouter,
|
|
8
|
+
Routes as ReactRouterRoutes,
|
|
9
|
+
Route as ReactRouterRoute,
|
|
10
|
+
Link as ReactRouterLink,
|
|
11
|
+
Navigate as ReactRouterNavigate,
|
|
12
|
+
useNavigate as reactRouterUseNavigate,
|
|
13
|
+
useLocation as reactRouterUseLocation,
|
|
14
|
+
useParams as reactRouterUseParams
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def:pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
|
|
18
|
+
|
|
19
|
+
# React hooks re-exported for auto-injection by `has` variables
|
|
20
|
+
glob:pub useState = reactUseState,
|
|
21
|
+
Router = ReactRouterHashRouter,
|
|
22
|
+
Routes = ReactRouterRoutes,
|
|
23
|
+
Route = ReactRouterRoute,
|
|
24
|
+
Link = ReactRouterLink,
|
|
25
|
+
Navigate = ReactRouterNavigate,
|
|
26
|
+
useNavigate = reactRouterUseNavigate,
|
|
27
|
+
useLocation = reactRouterUseLocation,
|
|
28
|
+
useParams = reactRouterUseParams;
|
|
29
|
+
|
|
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
|