jac-client 0.2.7__py3-none-any.whl → 0.2.9__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.
Files changed (72) hide show
  1. jac_client/examples/all-in-one/{src/app.jac → main.jac} +5 -5
  2. jac_client/examples/all-in-one/{src/pages → pages}/BudgetPlanner.jac +8 -1
  3. jac_client/examples/all-in-one/{src/pages → pages}/FeaturesTest.jac +16 -1
  4. jac_client/examples/all-in-one/{src/pages/FeaturesTest.cl.jac → pages/features_test_ui.cl.jac} +11 -0
  5. jac_client/examples/all-in-one/{src/pages → pages}/nestedDemo.jac +1 -1
  6. jac_client/examples/all-in-one/{src/pages → pages}/notFound.jac +2 -7
  7. jac_client/plugin/cli.jac +162 -430
  8. jac_client/plugin/client.jac +30 -12
  9. jac_client/plugin/client_runtime.cl.jac +19 -15
  10. jac_client/plugin/impl/client.impl.jac +107 -69
  11. jac_client/plugin/impl/client_runtime.impl.jac +181 -9
  12. jac_client/plugin/plugin_config.jac +243 -15
  13. jac_client/plugin/src/config_loader.jac +1 -0
  14. jac_client/plugin/src/impl/compiler.impl.jac +2 -4
  15. jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
  16. jac_client/plugin/src/impl/vite_bundler.impl.jac +241 -11
  17. jac_client/plugin/src/vite_bundler.jac +14 -1
  18. jac_client/plugin/utils/__init__.jac +1 -0
  19. jac_client/plugin/utils/impl/node_installer.impl.jac +249 -0
  20. jac_client/plugin/utils/node_installer.jac +41 -0
  21. jac_client/templates/client.jacpack +72 -0
  22. jac_client/templates/fullstack.jacpack +61 -0
  23. jac_client/tests/conftest.py +48 -7
  24. jac_client/tests/test_cli.py +189 -73
  25. jac_client/tests/test_e2e.py +232 -0
  26. jac_client/tests/test_helpers.py +65 -0
  27. jac_client/tests/test_it.py +97 -137
  28. {jac_client-0.2.7.dist-info → jac_client-0.2.9.dist-info}/METADATA +4 -4
  29. jac_client-0.2.9.dist-info/RECORD +104 -0
  30. {jac_client-0.2.7.dist-info → jac_client-0.2.9.dist-info}/WHEEL +1 -1
  31. jac_client-0.2.7.dist-info/RECORD +0 -97
  32. /jac_client/examples/all-in-one/{src/button.jac → button.jac} +0 -0
  33. /jac_client/examples/all-in-one/{src/components → components}/CategoryFilter.jac +0 -0
  34. /jac_client/examples/all-in-one/{src/components → components}/Header.jac +0 -0
  35. /jac_client/examples/all-in-one/{src/components → components}/ProfitOverview.jac +0 -0
  36. /jac_client/examples/all-in-one/{src/components → components}/Summary.jac +0 -0
  37. /jac_client/examples/all-in-one/{src/components → components}/TransactionForm.jac +0 -0
  38. /jac_client/examples/all-in-one/{src/components → components}/TransactionItem.jac +0 -0
  39. /jac_client/examples/all-in-one/{src/components → components}/TransactionList.jac +0 -0
  40. /jac_client/examples/all-in-one/{src/components → components}/button.jac +0 -0
  41. /jac_client/examples/all-in-one/{src/components → components}/navigation.jac +0 -0
  42. /jac_client/examples/all-in-one/{src/constants → constants}/categories.jac +0 -0
  43. /jac_client/examples/all-in-one/{src/constants → constants}/clients.jac +0 -0
  44. /jac_client/examples/all-in-one/{src/context → context}/BudgetContext.jac +0 -0
  45. /jac_client/examples/all-in-one/{src/hooks → hooks}/useBudget.jac +0 -0
  46. /jac_client/examples/all-in-one/{src/hooks → hooks}/useLocalStorage.jac +0 -0
  47. /jac_client/examples/all-in-one/{src/pages → pages}/LandingPage.jac +0 -0
  48. /jac_client/examples/all-in-one/{src/pages/BudgetPlanner.cl.jac → pages/budget_planner_ui.cl.jac} +0 -0
  49. /jac_client/examples/all-in-one/{src/pages → pages}/loginPage.jac +0 -0
  50. /jac_client/examples/all-in-one/{src/pages → pages}/signupPage.jac +0 -0
  51. /jac_client/examples/all-in-one/{src/utils → utils}/formatters.jac +0 -0
  52. /jac_client/examples/asset-serving/css-with-image/{src/app.jac → main.jac} +0 -0
  53. /jac_client/examples/asset-serving/image-asset/{src/app.jac → main.jac} +0 -0
  54. /jac_client/examples/asset-serving/import-alias/{src/app.jac → main.jac} +0 -0
  55. /jac_client/examples/basic/{src/app.jac → main.jac} +0 -0
  56. /jac_client/examples/basic-auth/{src/app.jac → main.jac} +0 -0
  57. /jac_client/examples/basic-auth-with-router/{src/app.jac → main.jac} +0 -0
  58. /jac_client/examples/basic-full-stack/{src/app.jac → main.jac} +0 -0
  59. /jac_client/examples/css-styling/js-styling/{src/app.jac → main.jac} +0 -0
  60. /jac_client/examples/css-styling/material-ui/{src/app.jac → main.jac} +0 -0
  61. /jac_client/examples/css-styling/pure-css/{src/app.jac → main.jac} +0 -0
  62. /jac_client/examples/css-styling/sass-example/{src/app.jac → main.jac} +0 -0
  63. /jac_client/examples/css-styling/styled-components/{src/app.jac → main.jac} +0 -0
  64. /jac_client/examples/css-styling/tailwind-example/{src/app.jac → main.jac} +0 -0
  65. /jac_client/examples/full-stack-with-auth/{src/app.jac → main.jac} +0 -0
  66. /jac_client/examples/little-x/{src/app.jac → main.jac} +0 -0
  67. /jac_client/examples/nested-folders/nested-advance/{src/app.jac → main.jac} +0 -0
  68. /jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +0 -0
  69. /jac_client/examples/ts-support/{src/app.jac → main.jac} +0 -0
  70. /jac_client/examples/with-router/{src/app.jac → main.jac} +0 -0
  71. {jac_client-0.2.7.dist-info → jac_client-0.2.9.dist-info}/entry_points.txt +0 -0
  72. {jac_client-0.2.7.dist-info → jac_client-0.2.9.dist-info}/top_level.txt +0 -0
jac_client/plugin/cli.jac CHANGED
@@ -1,499 +1,231 @@
1
1
  """Command line interface tool for the Jac Client.
2
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.
3
+ This module extends core CLI commands to add the --npm flag for
4
+ client-side (frontend) package management:
5
+ - `jac add --npm`: Add npm dependencies
6
+ - `jac remove --npm`: Remove npm dependencies
7
+
8
+ For creating client projects, use: `jac create --use client`
5
9
  """
6
10
 
7
- import os;
8
- import re;
9
- import sys;
10
- import pathlib;
11
- import subprocess;
12
- import shutil;
13
11
  import from jaclang.cli.registry { get_registry }
14
- import from jaclang.cli.command { Arg, ArgKind, HookContext }
12
+ import from jaclang.cli.command { Arg, HookContext }
15
13
  import from jaclang.pycore.runtime { hookimpl }
16
- import from jaclang.project.config { find_project_root }
14
+ import os;
17
15
 
18
16
  """Jac CLI extensions for client-side development."""
19
17
  class JacCmd {
20
18
  """Create Jac CLI cmds."""
21
19
  @hookimpl
22
20
  static def create_cmd -> None {
23
- """Extend core create command to add --cl flag for client-side setup.""";
21
+ """Extend core commands to add --npm flag for npm package management.""";
24
22
  registry = get_registry();
25
23
 
26
- # Extend the core 'create' command with client-specific arguments
24
+ # Extend the core 'create' command with --skip flag for client template
27
25
  registry.extend_command(
28
26
  command_name="create",
29
27
  args=[
30
28
  Arg.create(
31
- "cl",
29
+ "skip",
32
30
  typ=bool,
33
31
  default=False,
34
- help="Include client-side (frontend) setup",
35
- short="c"
32
+ help="Skip npm package installation (for --use client)"
36
33
  ),
34
+
35
+ ],
36
+ pre_hook=_handle_create_skip,
37
+ source="jac-client"
38
+ );
39
+
40
+ # Extend the core 'add' command with --npm flag
41
+ registry.extend_command(
42
+ command_name="add",
43
+ args=[
37
44
  Arg.create(
38
- "skip",
45
+ "npm",
39
46
  typ=bool,
40
47
  default=False,
41
- help="Skip installing default packages (only for --cl)",
42
- short="s"
48
+ help="Add as npm (client-side) dependency"
43
49
  ),
50
+
51
+ ],
52
+ pre_hook=_handle_npm_add,
53
+ source="jac-client"
54
+ );
55
+
56
+ # Extend the core 'remove' command with --npm flag
57
+ registry.extend_command(
58
+ command_name="remove",
59
+ args=[
44
60
  Arg.create(
45
- "verbose",
61
+ "npm",
46
62
  typ=bool,
47
63
  default=False,
48
- help="Show detailed output during installation",
49
- short="v"
64
+ help="Remove npm (client-side) dependency"
50
65
  ),
51
66
 
52
67
  ],
53
- pre_hook=_handle_client_create,
68
+ pre_hook=_handle_npm_remove,
54
69
  source="jac-client"
55
70
  );
56
71
  }
57
72
  }
58
73
 
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;
74
+ """Pre-hook to handle --skip flag for create command."""
75
+ def _handle_create_skip(ctx: HookContext) -> None {
76
+ # Check if --skip flag is set
77
+ skip_flag = ctx.get_arg("skip", False);
78
+ if skip_flag {
79
+ # Set environment variable to skip npm installation
80
+ os.environ["JAC_CLIENT_SKIP_NPM_INSTALL"] = "1";
66
81
  }
82
+ # Let core create command run normally
83
+ }
67
84
 
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);
85
+ """Pre-hook to handle --npm flag for adding npm dependencies."""
86
+ def _handle_npm_add(ctx: HookContext) -> None {
87
+ # Check if --npm flag is set
88
+ npm_flag = ctx.get_arg("npm", False);
89
+ if not npm_flag {
90
+ # Let core add command run normally
91
+ return;
88
92
  }
89
- }
90
93
 
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;
94
+ import from jaclang.project.config { get_config }
95
+ import from jaclang.project.dep_registry { get_dependency_registry }
96
+ import from jaclang.project.dependencies { DependencyResolver }
97
+ import from jaclang.cli.console { console }
100
98
 
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
99
+ config = get_config();
100
+ if config is None {
101
+ console.error(
102
+ "No jac.toml found", hint="Run 'jac create' to create a project."
105
103
  );
106
- <>exit(1);
104
+ ctx.set_data("cancel_execution", True);
105
+ ctx.set_data("cancel_return_code", 1);
106
+ return;
107
107
  }
108
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
109
+ registry = get_dependency_registry();
110
+ dep_type = registry.get_by_flag("--npm");
111
+ if dep_type is None {
112
+ console.error(
113
+ "--npm flag requires jac-client plugin",
114
+ hint="Install with: pip install jac-client"
113
115
  );
114
- <>exit(1);
116
+ ctx.set_data("cancel_execution", True);
117
+ ctx.set_data("cancel_return_code", 1);
118
+ return;
115
119
  }
116
120
 
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
- }
121
+ packages = ctx.get_arg("packages", []);
122
+ dev = ctx.get_arg("dev", False);
124
123
 
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);
124
+ # If no packages specified, install all npm packages from jac.toml
125
+ if not packages {
126
+ if dep_type.install_all_handler is None {
127
+ console.error(f"No install_all handler registered for {dep_type.name}");
128
+ ctx.set_data("cancel_execution", True);
129
+ ctx.set_data("cancel_return_code", 1);
130
+ return;
131
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.npm]
155
- "jac-client-node" = "1.0.3"
156
-
157
- [dependencies.npm.dev]
158
- "@jac-client/dev-deps" = "1.0.0"
159
-
160
- [serve]
161
- base_route_app = "app"
162
-
163
- [plugins.client]
164
- # Vite bundler configuration (optional overrides)
165
- # vite.plugins = []
166
- # vite.build = {{}}
167
- ''';
168
- with open(toml_path, 'w') as f {
169
- f.write(toml_content);
170
- }
171
- print(f"Created {toml_path}");
172
-
173
- main_jac_content = '''"""Main entry point for the Jac client application."""
174
-
175
- # Client-side imports (useState is auto-injected when using `has` variables)
176
- cl import from react { useEffect }
177
- cl import from .components.Button { Button }
178
-
179
- # Client-side component
180
- cl {
181
- def:pub app() -> any {
182
- has count: int = 0;
183
-
184
- useEffect(lambda -> None {
185
- console.log("Count updated:", count);
186
- }, [count]);
187
-
188
- return <div style={{padding: "2rem", fontFamily: "Arial, sans-serif"}}>
189
- <h1>Hello, World!</h1>
190
- <p>Count: {count}</p>
191
- <div style={{display: "flex", gap: "1rem", marginTop: "1rem"}}>
192
- <Button
193
- label="Increment"
194
- onClick={lambda -> None { count = count + 1; }}
195
- variant="primary"
196
- />
197
- <Button
198
- label="Reset"
199
- onClick={lambda -> None { count = 0; }}
200
- variant="secondary"
201
- />
202
- </div>
203
- </div>;
204
- }
205
- }
206
- ''';
207
- with open(project_path / "main.jac", 'w') as f {
208
- f.write(main_jac_content);
132
+ try {
133
+ console.print(
134
+ f"\n📦 Installing all {dep_type.name} packages from jac.toml",
135
+ style="bold"
136
+ );
137
+ with console.status(
138
+ f"[cyan]Installing {dep_type.name} packages...[/cyan]", spinner="dots"
139
+ ) as status {
140
+ dep_type.install_all_handler(config);
141
+ }
142
+ console.success(f"Installed all {dep_type.name} packages");
143
+ ctx.set_data("cancel_execution", True);
144
+ ctx.set_data("cancel_return_code", 0);
145
+ } except Exception as e {
146
+ console.error(f"Failed to install packages: {e}");
147
+ ctx.set_data("cancel_execution", True);
148
+ ctx.set_data("cancel_return_code", 1);
149
+ }
150
+ return;
209
151
  }
210
- print("Created main.jac");
211
152
 
212
- button_cl_jac_content = '''"""Button component for the Jac client application."""
213
-
214
- def:pub Button(label: str, onClick: any, variant: str = "primary", disabled: bool = False) -> any {
215
- base_styles = {
216
- "padding": "0.75rem 1.5rem",
217
- "fontSize": "1rem",
218
- "fontWeight": "600",
219
- "borderRadius": "0.5rem",
220
- "border": "none",
221
- "cursor": "not-allowed" if disabled else "pointer",
222
- "transition": "all 0.2s ease"
223
- };
224
-
225
- variant_styles = {
226
- "primary": {
227
- "backgroundColor": "#9ca3af" if disabled else "#3b82f6",
228
- "color": "#ffffff"
229
- },
230
- "secondary": {
231
- "backgroundColor": "#e5e7eb" if disabled else "#6b7280",
232
- "color": "#ffffff"
153
+ # Install specific packages
154
+ try {
155
+ resolver = DependencyResolver(config=config);
156
+ console.print(
157
+ f"\n📦 Adding {len(packages)} {dep_type.name} package(s)", style="bold"
158
+ );
159
+ for pkg_spec in packages {
160
+ (name, version) = resolver.parse_spec(pkg_spec);
161
+ with console.status(f"[cyan]Installing {name}...[/cyan]", spinner="dots") as status {
162
+ dep_type.install_handler(config, name, version, dev);
163
+ }
164
+ console.print(f" ✔ {name}", style="success");
233
165
  }
234
- };
235
-
236
- return <button
237
- style={{**base_styles, **variant_styles[variant]}}
238
- onClick={onClick}
239
- disabled={disabled}
240
- >
241
- {label}
242
- </button>;
243
- }
244
- ''';
245
- with open(components_dir / "Button.cl.jac", 'w') as f {
246
- f.write(button_cl_jac_content);
166
+ section = f"dependencies.{dep_type.dev_name if dev else dep_type.name}";
167
+ console.print(f"\n ✔ Updated jac.toml [{section}]", style="muted");
168
+ ctx.set_data("cancel_execution", True);
169
+ ctx.set_data("cancel_return_code", 0);
170
+ } except Exception as e {
171
+ console.error(f"Failed to install packages: {e}");
172
+ ctx.set_data("cancel_execution", True);
173
+ ctx.set_data("cancel_return_code", 1);
247
174
  }
248
- print("Created components/Button.cl.jac");
249
-
250
- readme_content = f''' # {project_name}
251
-
252
-
253
- A Jac client-side application with React support.
254
-
255
- ## Project Structure
256
-
257
- ```
258
- {project_name}/
259
- ├── jac.toml # Project configuration
260
- ├── main.jac # Main application entry
261
- ├── components/ # Reusable components
262
- │ └── Button.cl.jac # Example Jac component
263
- ├── assets/ # Static assets (images, fonts, etc.)
264
- └── build/ # Build output (generated)
265
- ```
266
-
267
- ## Getting Started
268
-
269
- Start the development server:
270
-
271
- ```bash
272
- jac start main.jac
273
- ```
274
-
275
- ## Components
276
-
277
- Create Jac components in `components/` as `.cl.jac` files and import them:
278
-
279
- ```jac
280
- cl import from .components.Button {{ Button }}
281
- ```
282
-
283
- ## Adding Dependencies
284
-
285
- Add npm packages with the --cl flag:
175
+ }
286
176
 
287
- ```bash
288
- jac add --cl react-router-dom
289
- ```
290
- ''';
291
- with open(project_path / "README.md", 'w') as f {
292
- f.write(readme_content);
177
+ """Pre-hook to handle --npm flag for removing npm dependencies."""
178
+ def _handle_npm_remove(ctx: HookContext) -> None {
179
+ # Check if --npm flag is set
180
+ npm_flag = ctx.get_arg("npm", False);
181
+ if not npm_flag {
182
+ # Let core remove command run normally
183
+ return;
293
184
  }
294
- print("Created README.md");
295
185
 
296
- _create_gitignore(project_path);
186
+ import from jaclang.project.config { get_config }
187
+ import from jaclang.project.dep_registry { get_dependency_registry }
188
+ import from jaclang.cli.console { console }
297
189
 
298
- # Install default packages unless --skip is specified
299
- if not skip {
300
- print("\nInstalling default packages...");
301
- _install_default_packages(project_path, verbose);
190
+ config = get_config();
191
+ if config is None {
192
+ console.error("No jac.toml found. Run 'jac create' to create a project.");
193
+ ctx.set_data("cancel_execution", True);
194
+ ctx.set_data("cancel_return_code", 1);
195
+ return;
302
196
  }
303
197
 
304
- print(f"\nProject '{project_name}' created successfully!");
305
- if name and name != cwd.name {
306
- print("\nNext steps:");
307
- print(f" cd {name}");
308
- print(" jac start main.jac");
309
- } else {
310
- print("\nNext steps:");
311
- print(" jac start main.jac");
198
+ packages = ctx.get_arg("packages", []);
199
+ if not packages {
200
+ console.error("No packages specified.");
201
+ ctx.set_data("cancel_execution", True);
202
+ ctx.set_data("cancel_return_code", 1);
203
+ return;
312
204
  }
313
- }
314
-
315
- """Create .gitignore file with client-specific entries."""
316
- def _create_gitignore(project_path: pathlib.Path) -> None {
317
- gitignore_entries = [
318
- "# Jac project",
319
- "packages/",
320
- ".jac_cache/",
321
- "*.jbc",
322
- "*.jir",
323
- "__jaccache__/",
324
- "",
325
- "# Python",
326
- "__pycache__/",
327
- "*.py[cod]",
328
- ".venv/",
329
- "venv/",
330
- "",
331
- "# IDE",
332
- ".idea/",
333
- ".vscode/",
334
- "*.swp",
335
- "",
336
- "# Node.js",
337
- "node_modules/",
338
- "",
339
- "# Jac build artifacts",
340
- ".jac/",
341
- "*.session",
342
- "*.session.*"
343
- ];
344
205
 
345
- gitignore_path = project_path / ".gitignore";
346
- if gitignore_path.exists() {
347
- with open(gitignore_path, "r") as f {
348
- existing_content = f.read();
349
- }
350
- new_entries: list = [];
351
- for entry in gitignore_entries {
352
- if entry and entry not in existing_content {
353
- new_entries.append(entry);
354
- }
355
- }
356
- if new_entries {
357
- with open(gitignore_path, "a") as f {
358
- f.write("\n" + "\n".join(new_entries));
359
- }
360
- print("Updated .gitignore");
361
- }
362
- } else {
363
- with open(gitignore_path, "w") as f {
364
- f.write("\n".join(gitignore_entries) + "\n");
365
- }
366
- print("Created .gitignore");
206
+ registry = get_dependency_registry();
207
+ dep_type = registry.get_by_flag("--npm");
208
+ if dep_type is None {
209
+ console.error("--npm flag requires jac-client plugin to be installed.");
210
+ console.print("Install with: pip install jac-client", style="muted");
211
+ ctx.set_data("cancel_execution", True);
212
+ ctx.set_data("cancel_return_code", 1);
213
+ return;
367
214
  }
368
- }
369
215
 
370
- """Install default npm packages in .jac/client directory."""
371
- def _install_default_packages(
372
- project_path: pathlib.Path, verbose: bool = False
373
- ) -> None {
374
- import from jac_client.plugin.src.vite_bundler { ViteBundler }
216
+ dev = ctx.get_arg("dev", False);
375
217
 
376
218
  try {
377
- # Verify jac.toml exists
378
- toml_path = project_path / "jac.toml";
379
- if not toml_path.exists() {
380
- print(
381
- "Warning: jac.toml not found, skipping package installation",
382
- file=sys.stderr
383
- );
384
- return;
385
- }
386
-
387
- # Get project name from jac.toml or use directory name
388
- project_name = project_path.name;
389
-
390
- # Create ViteBundler instance (it will load config internally)
391
- bundler = ViteBundler(project_path);
392
-
393
- # Generate package.json with default packages (defaults are added automatically)
394
- bundler.create_package_json(project_name=project_name);
395
-
396
- # Ensure .jac/client directory exists
397
- client_dir = bundler._get_client_dir();
398
- client_dir.mkdir(parents=True, exist_ok=True);
399
-
400
- # Copy package.json to .jac/client/ for npm install
401
- configs_package_json = client_dir / 'configs' / 'package.json';
402
- build_package_json = client_dir / 'package.json';
403
-
404
- if not configs_package_json.exists() {
405
- print(
406
- "Warning: package.json was not generated, skipping package installation",
407
- file=sys.stderr
408
- );
409
- return;
410
- }
411
-
412
- # Always copy the generated package.json to .jac/client/ for npm install
413
- shutil.copy2(configs_package_json, build_package_json);
414
-
415
- # Read package data for verbose output
416
- import json;
417
- with open(configs_package_json, 'r') as f {
418
- pkg_data = json.load(f);
419
- }
420
- deps = pkg_data.get('dependencies', {});
421
- dev_deps = pkg_data.get('devDependencies', {});
422
-
423
- if verbose {
424
- # Verbose mode: show detailed package list and stream npm output
425
- if deps {
426
- print(" Dependencies:");
427
- for (name, version) in deps.items() {
428
- print(f" - {name}@{version}");
429
- }
430
- }
431
- if dev_deps {
432
- print(" Dev dependencies:");
433
- for (name, version) in dev_deps.items() {
434
- print(f" - {name}@{version}");
435
- }
436
- }
437
- print("\nRunning npm install...");
438
- }
439
-
440
- # Run npm install in .jac/client/ directory
441
- try {
442
- if verbose {
443
- # Stream output for visibility in verbose mode
444
- subprocess.run(
445
- ['npm', 'install', '--progress'], cwd=client_dir, check=True
446
- );
447
- } else {
448
- # Quiet mode: capture output
449
- subprocess.run(
450
- ['npm', 'install'],
451
- cwd=client_dir,
452
- check=True,
453
- capture_output=True,
454
- text=True
455
- );
456
- }
457
-
458
- # Move package-lock.json to configs/ if it was created
459
- build_package_lock = client_dir / 'package-lock.json';
460
- configs_dir = client_dir / 'configs';
461
- configs_package_lock = configs_dir / 'package-lock.json';
462
- if build_package_lock.exists() {
463
- configs_dir.mkdir(parents=True, exist_ok=True);
464
- if configs_package_lock.exists() {
465
- configs_package_lock.unlink();
466
- }
467
- shutil.move(str(build_package_lock), str(configs_package_lock));
468
- }
469
-
470
- print("Default packages installed successfully");
471
- } except subprocess.CalledProcessError as e {
472
- if verbose {
473
- print(
474
- f"Warning: Failed to install packages (exit code {e.returncode})",
475
- file=sys.stderr
476
- );
477
- } else {
478
- print(
479
- f"Warning: Failed to install packages: {e.stderr}", file=sys.stderr
480
- );
481
- }
482
- print("You can install packages later with: jac add --cl", file=sys.stderr);
483
- } except FileNotFoundError {
484
- print(
485
- "Warning: npm command not found. Install Node.js and npm to install packages.",
486
- file=sys.stderr
487
- );
488
- print("You can install packages later with: jac add --cl", file=sys.stderr);
489
- } finally {
490
- # Clean up temporary package.json in .jac/client/
491
- if build_package_json.exists() {
492
- build_package_json.unlink();
493
- }
219
+ for name in packages {
220
+ console.info(f"Removing {name} ({dep_type.name})...");
221
+ dep_type.remove_handler(config, name, dev);
494
222
  }
223
+ console.success(f"Removed {len(packages)} package(s)");
224
+ ctx.set_data("cancel_execution", True);
225
+ ctx.set_data("cancel_return_code", 0);
495
226
  } except Exception as e {
496
- print(f"Warning: Could not install default packages: {e}", file=sys.stderr);
497
- print("You can install packages later with: jac add --cl", file=sys.stderr);
227
+ console.error(f"Error removing packages: {e}");
228
+ ctx.set_data("cancel_execution", True);
229
+ ctx.set_data("cancel_return_code", 1);
498
230
  }
499
231
  }