jac-client 0.2.8__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 (52) hide show
  1. jac_client/examples/all-in-one/{app.jac → main.jac} +5 -5
  2. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +8 -1
  3. jac_client/examples/all-in-one/pages/FeaturesTest.jac +16 -1
  4. jac_client/examples/all-in-one/pages/{FeaturesTest.cl.jac → features_test_ui.cl.jac} +11 -0
  5. jac_client/examples/all-in-one/pages/nestedDemo.jac +1 -1
  6. jac_client/examples/all-in-one/pages/notFound.jac +2 -7
  7. jac_client/plugin/cli.jac +162 -435
  8. jac_client/plugin/client.jac +25 -0
  9. jac_client/plugin/client_runtime.cl.jac +5 -1
  10. jac_client/plugin/impl/client.impl.jac +96 -55
  11. jac_client/plugin/impl/client_runtime.impl.jac +154 -0
  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 +1 -1
  15. jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
  16. jac_client/plugin/src/impl/vite_bundler.impl.jac +97 -16
  17. jac_client/plugin/src/vite_bundler.jac +6 -0
  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 +184 -70
  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 +91 -135
  28. {jac_client-0.2.8.dist-info → jac_client-0.2.9.dist-info}/METADATA +4 -4
  29. {jac_client-0.2.8.dist-info → jac_client-0.2.9.dist-info}/RECORD +52 -45
  30. {jac_client-0.2.8.dist-info → jac_client-0.2.9.dist-info}/WHEEL +1 -1
  31. /jac_client/examples/all-in-one/pages/{BudgetPlanner.cl.jac → budget_planner_ui.cl.jac} +0 -0
  32. /jac_client/examples/asset-serving/css-with-image/{src/app.jac → main.jac} +0 -0
  33. /jac_client/examples/asset-serving/image-asset/{src/app.jac → main.jac} +0 -0
  34. /jac_client/examples/asset-serving/import-alias/{src/app.jac → main.jac} +0 -0
  35. /jac_client/examples/basic/{src/app.jac → main.jac} +0 -0
  36. /jac_client/examples/basic-auth/{src/app.jac → main.jac} +0 -0
  37. /jac_client/examples/basic-auth-with-router/{src/app.jac → main.jac} +0 -0
  38. /jac_client/examples/basic-full-stack/{src/app.jac → main.jac} +0 -0
  39. /jac_client/examples/css-styling/js-styling/{src/app.jac → main.jac} +0 -0
  40. /jac_client/examples/css-styling/material-ui/{src/app.jac → main.jac} +0 -0
  41. /jac_client/examples/css-styling/pure-css/{src/app.jac → main.jac} +0 -0
  42. /jac_client/examples/css-styling/sass-example/{src/app.jac → main.jac} +0 -0
  43. /jac_client/examples/css-styling/styled-components/{src/app.jac → main.jac} +0 -0
  44. /jac_client/examples/css-styling/tailwind-example/{src/app.jac → main.jac} +0 -0
  45. /jac_client/examples/full-stack-with-auth/{src/app.jac → main.jac} +0 -0
  46. /jac_client/examples/little-x/{src/app.jac → main.jac} +0 -0
  47. /jac_client/examples/nested-folders/nested-advance/{src/app.jac → main.jac} +0 -0
  48. /jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +0 -0
  49. /jac_client/examples/ts-support/{src/app.jac → main.jac} +0 -0
  50. /jac_client/examples/with-router/{src/app.jac → main.jac} +0 -0
  51. {jac_client-0.2.8.dist-info → jac_client-0.2.9.dist-info}/entry_points.txt +0 -0
  52. {jac_client-0.2.8.dist-info → jac_client-0.2.9.dist-info}/top_level.txt +0 -0
jac_client/plugin/cli.jac CHANGED
@@ -1,504 +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]
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);
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;
214
151
  }
215
- print("Created main.jac");
216
-
217
- button_cl_jac_content = '''"""Button component for the Jac client application."""
218
152
 
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"
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");
238
165
  }
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);
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);
252
174
  }
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:
175
+ }
291
176
 
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);
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;
298
184
  }
299
- print("Created README.md");
300
185
 
301
- _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 }
302
189
 
303
- # Install default packages unless --skip is specified
304
- if not skip {
305
- print("\nInstalling default packages...");
306
- _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;
307
196
  }
308
197
 
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");
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;
317
204
  }
318
- }
319
205
 
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");
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;
372
214
  }
373
- }
374
215
 
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 }
216
+ dev = ctx.get_arg("dev", False);
380
217
 
381
218
  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
- }
219
+ for name in packages {
220
+ console.info(f"Removing {name} ({dep_type.name})...");
221
+ dep_type.remove_handler(config, name, dev);
499
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);
500
226
  } 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);
227
+ console.error(f"Error removing packages: {e}");
228
+ ctx.set_data("cancel_execution", True);
229
+ ctx.set_data("cancel_return_code", 1);
503
230
  }
504
231
  }