jac-client 0.2.6__py3-none-any.whl → 0.2.7__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 (61) hide show
  1. jac_client/examples/all-in-one/src/app.jac +473 -741
  2. jac_client/examples/all-in-one/src/components/CategoryFilter.jac +35 -0
  3. jac_client/examples/all-in-one/src/components/Header.jac +13 -0
  4. jac_client/examples/all-in-one/src/components/ProfitOverview.jac +50 -0
  5. jac_client/examples/all-in-one/src/components/Summary.jac +53 -0
  6. jac_client/examples/all-in-one/src/components/TransactionForm.jac +158 -0
  7. jac_client/examples/all-in-one/src/components/TransactionItem.jac +55 -0
  8. jac_client/examples/all-in-one/src/components/TransactionList.jac +37 -0
  9. jac_client/examples/all-in-one/src/components/navigation.jac +132 -0
  10. jac_client/examples/all-in-one/src/constants/categories.jac +37 -0
  11. jac_client/examples/all-in-one/src/constants/clients.jac +13 -0
  12. jac_client/examples/all-in-one/src/context/BudgetContext.jac +28 -0
  13. jac_client/examples/all-in-one/src/hooks/useBudget.jac +116 -0
  14. jac_client/examples/all-in-one/src/hooks/useLocalStorage.jac +36 -0
  15. jac_client/examples/all-in-one/src/pages/BudgetPlanner.cl.jac +70 -0
  16. jac_client/examples/all-in-one/src/pages/BudgetPlanner.jac +126 -0
  17. jac_client/examples/all-in-one/src/pages/FeaturesTest.cl.jac +552 -0
  18. jac_client/examples/all-in-one/src/pages/FeaturesTest.jac +126 -0
  19. jac_client/examples/all-in-one/src/pages/LandingPage.jac +101 -0
  20. jac_client/examples/all-in-one/src/pages/loginPage.jac +132 -0
  21. jac_client/examples/all-in-one/src/pages/nestedDemo.jac +61 -0
  22. jac_client/examples/all-in-one/src/pages/notFound.jac +24 -0
  23. jac_client/examples/all-in-one/src/pages/signupPage.jac +133 -0
  24. jac_client/examples/all-in-one/src/utils/formatters.jac +52 -0
  25. jac_client/examples/asset-serving/css-with-image/src/app.jac +3 -3
  26. jac_client/examples/asset-serving/image-asset/src/app.jac +3 -3
  27. jac_client/examples/asset-serving/import-alias/src/app.jac +3 -3
  28. jac_client/examples/basic/src/app.jac +3 -3
  29. jac_client/examples/basic-auth/src/app.jac +31 -37
  30. jac_client/examples/basic-auth-with-router/src/app.jac +16 -16
  31. jac_client/examples/basic-full-stack/src/app.jac +24 -30
  32. jac_client/examples/css-styling/js-styling/src/app.jac +5 -5
  33. jac_client/examples/css-styling/material-ui/src/app.jac +5 -5
  34. jac_client/examples/css-styling/pure-css/src/app.jac +5 -5
  35. jac_client/examples/css-styling/sass-example/src/app.jac +5 -5
  36. jac_client/examples/css-styling/styled-components/src/app.jac +5 -5
  37. jac_client/examples/css-styling/tailwind-example/src/app.jac +5 -5
  38. jac_client/examples/full-stack-with-auth/src/app.jac +16 -16
  39. jac_client/examples/ts-support/src/app.jac +4 -4
  40. jac_client/examples/with-router/src/app.jac +4 -4
  41. jac_client/plugin/cli.jac +155 -203
  42. jac_client/plugin/client_runtime.cl.jac +5 -1
  43. jac_client/plugin/impl/client.impl.jac +74 -12
  44. jac_client/plugin/plugin_config.jac +11 -11
  45. jac_client/plugin/src/compiler.jac +2 -1
  46. jac_client/plugin/src/impl/babel_processor.impl.jac +22 -17
  47. jac_client/plugin/src/impl/compiler.impl.jac +57 -18
  48. jac_client/plugin/src/impl/vite_bundler.impl.jac +66 -102
  49. jac_client/plugin/src/package_installer.jac +1 -1
  50. jac_client/plugin/src/vite_bundler.jac +1 -0
  51. jac_client/tests/conftest.py +10 -8
  52. jac_client/tests/fixtures/spawn_test/app.jac +15 -18
  53. jac_client/tests/fixtures/with-ts/app.jac +4 -4
  54. jac_client/tests/test_cli.py +99 -45
  55. jac_client/tests/test_it.py +290 -79
  56. {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/METADATA +16 -7
  57. jac_client-0.2.7.dist-info/RECORD +97 -0
  58. jac_client-0.2.6.dist-info/RECORD +0 -74
  59. {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/WHEEL +0 -0
  60. {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/entry_points.txt +0 -0
  61. {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,26 @@
1
- """Create package.json from config.json during bundling."""
1
+ """Get the client build directory from project config."""
2
+ impl ViteBundler._get_client_dir(self: ViteBundler) -> Path {
3
+ # Try to get from project config
4
+ try {
5
+ import from jaclang.project.config { get_config }
6
+ config = get_config();
7
+ if config is not None {
8
+ return config.get_client_dir();
9
+ }
10
+ } except ImportError {
11
+ pass;
12
+ }
13
+ # Fallback to default
14
+ return self.project_dir / '.jac' / 'client';
15
+ }
2
16
 
17
+ """Create package.json from config.json during bundling."""
3
18
  impl ViteBundler.create_package_json(
4
19
  self: ViteBundler, project_name: Optional[str] = None
5
20
  ) -> Path {
6
- build_dir = self.project_dir / '.client-build';
7
- build_dir.mkdir(exist_ok=True);
8
- configs_dir = build_dir / '.jac-client.configs';
21
+ build_dir = self._get_client_dir();
22
+ build_dir.mkdir(parents=True, exist_ok=True);
23
+ configs_dir = build_dir / 'configs';
9
24
  configs_dir.mkdir(exist_ok=True);
10
25
  package_config = self.config_loader.get_package_config();
11
26
  package_json_path = configs_dir / 'package.json';
@@ -29,32 +44,12 @@ impl ViteBundler.create_package_json(
29
44
  if not name {
30
45
  name = self.project_dir.name or 'jac-app';
31
46
  }
32
- # Default hardcoded dependencies (always included)
33
- default_dependencies = {
34
- 'react': '^18.2.0',
35
- 'react-dom': '^18.2.0',
36
- 'react-router-dom': '^6.22.0'
37
- };
38
- default_dev_dependencies = {
39
- 'vite': '^6.4.1',
40
- '@babel/cli': '^7.28.3',
41
- '@babel/core': '^7.28.5',
42
- '@babel/preset-env': '^7.28.5',
43
- '@babel/preset-react': '^7.28.5',
44
- '@vitejs/plugin-react': '^4.2.1',
45
- 'typescript': '^5.3.3',
46
- '@types/react': '^18.2.0',
47
- '@types/react-dom': '^18.2.0'
48
- };
49
- # Merge user dependencies from TOML with defaults (user can override)
50
- user_dependencies = package_config.get('dependencies', {});
51
- user_dev_dependencies = package_config.get('devDependencies', {});
52
- dependencies = {** default_dependencies, ** user_dependencies};
53
- dev_dependencies = {** default_dev_dependencies, ** user_dev_dependencies};
47
+ dependencies = package_config.get('dependencies', {});
48
+ dev_dependencies = package_config.get('devDependencies', {});
54
49
  scripts = {
55
- 'build': 'npm run compile && vite build --config .client-build/.jac-client.configs/vite.config.js',
56
- 'dev': 'vite dev --config .client-build/.jac-client.configs/vite.config.js',
57
- 'preview': 'vite preview --config .client-build/.jac-client.configs/vite.config.js',
50
+ 'build': 'npm run compile && vite build --config .jac/client/configs/vite.config.js',
51
+ 'dev': 'vite dev --config .jac/client/configs/vite.config.js',
52
+ 'preview': 'vite preview --config .jac/client/configs/vite.config.js',
58
53
  'compile': 'babel compiled --out-dir build --extensions ".jsx,.js" --out-file-extension .js'
59
54
  };
60
55
  user_scripts = package_config.get('scripts', {});
@@ -90,9 +85,9 @@ impl ViteBundler.create_package_json(
90
85
 
91
86
  """Create tsconfig.json during build time, merging user config from jac.toml."""
92
87
  impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
93
- build_dir = self.project_dir / '.client-build';
94
- build_dir.mkdir(exist_ok=True);
95
- configs_dir = build_dir / '.jac-client.configs';
88
+ build_dir = self._get_client_dir();
89
+ build_dir.mkdir(parents=True, exist_ok=True);
90
+ configs_dir = build_dir / 'configs';
96
91
  configs_dir.mkdir(exist_ok=True);
97
92
  tsconfig_path = configs_dir / 'tsconfig.json';
98
93
  # Default tsconfig settings
@@ -114,7 +109,7 @@ impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
114
109
  'noFallthroughCasesInSwitch': True
115
110
  };
116
111
  default_include = ['components/**/*'];
117
- default_exclude = ['.client-build'];
112
+ default_exclude = ['.jac'];
118
113
  # Get user config from [plugins.client.ts] in jac.toml
119
114
  user_ts_config = self.config_loader.get_ts_config();
120
115
  user_compiler_options = user_ts_config.get('compilerOptions', {});
@@ -130,21 +125,21 @@ impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
130
125
  'include': merged_include,
131
126
  'exclude': merged_exclude
132
127
  };
133
- # Write the config to .client-build/.jac-client.configs/tsconfig.json (no root config file)
128
+ # Write the config to .jac/client/configs/tsconfig.json (no root config file)
134
129
  tsconfig_path.write_text(json.dumps(tsconfig_data, indent=2), encoding='utf-8');
135
130
  return tsconfig_path;
136
131
  }
137
132
 
138
133
  """
139
- Clean up root package.json and move package-lock.json to .jac-client.configs/.
134
+ Clean up root package.json and move package-lock.json to configs/.
140
135
 
141
- Remove root package.json and move package-lock.json to .jac-client.configs/.
136
+ Remove root package.json and move package-lock.json to configs/.
142
137
  """
143
138
  impl ViteBundler._cleanup_root_package_files(self: ViteBundler) -> None {
144
139
  root_package_json = self.project_dir / 'package.json';
145
140
  root_package_lock = self.project_dir / 'package-lock.json';
146
- build_dir = self.project_dir / '.client-build';
147
- configs_dir = build_dir / '.jac-client.configs';
141
+ build_dir = self._get_client_dir();
142
+ configs_dir = build_dir / 'configs';
148
143
  configs_package_lock = configs_dir / 'package-lock.json';
149
144
  if root_package_lock.exists() {
150
145
  configs_dir.mkdir(exist_ok=True);
@@ -164,7 +159,7 @@ Ensure root package.json exists temporarily for npm commands.
164
159
  Create root package.json temporarily if it doesn't exist.
165
160
  """
166
161
  impl ViteBundler._ensure_root_package_json(self: ViteBundler) -> None {
167
- generated_package_json = self.project_dir / '.client-build' / '.jac-client.configs' / 'package.json';
162
+ generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
168
163
  root_package_json = self.project_dir / 'package.json';
169
164
  if not generated_package_json.exists() {
170
165
  self.create_package_json();
@@ -235,51 +230,28 @@ impl ViteBundler._get_plugin_var_name(self: ViteBundler, plugin_name: str) -> st
235
230
 
236
231
  """Create vite.config.js from config.json during bundling."""
237
232
  impl ViteBundler.create_vite_config(self: ViteBundler, entry_file: Path) -> Path {
238
- build_dir = self.project_dir / '.client-build';
239
- build_dir.mkdir(exist_ok=True);
240
- configs_dir = build_dir / '.jac-client.configs';
233
+ build_dir = self._get_client_dir();
234
+ build_dir.mkdir(parents=True, exist_ok=True);
235
+ configs_dir = build_dir / 'configs';
241
236
  configs_dir.mkdir(exist_ok=True);
242
237
  vite_config_data = self.config_loader.get_vite_config();
243
238
  config_path = configs_dir / 'vite.config.js';
244
239
  # TypeScript is always enabled by default
245
- build_dir = self.project_dir / '.client-build';
246
240
  try {
247
- # Entry file path relative to .client-build/ (not project root)
241
+ # Entry file path relative to client build dir (not project root)
248
242
  entry_relative = entry_file.relative_to(build_dir).as_posix();
249
243
  } except ValueError {
250
- # Fallback: try relative to project_dir and strip .client-build/ prefix
251
- try {
252
- entry_relative_full = entry_file.relative_to(self.project_dir).as_posix();
253
- prefix = '.client-build/';
254
- if entry_relative_full.startswith(prefix) {
255
- # Remove '.client-build/' prefix (15 characters)
256
- entry_relative = entry_relative_full[15:];
257
- } else {
258
- entry_relative = entry_relative_full;
259
- }
260
- } except ValueError {
261
- entry_relative = entry_file.as_posix();
262
- }
244
+ # Fallback: use absolute path
245
+ entry_relative = entry_file.as_posix();
263
246
  }
264
247
  try {
265
- # Output dir path relative to .client-build/ (not project root)
248
+ # Output dir path relative to client build dir (not project root)
266
249
  output_relative = self.output_dir.relative_to(build_dir).as_posix();
267
250
  } except ValueError {
268
- # Fallback: try relative to project_dir and strip .client-build/ prefix
269
- try {
270
- output_relative_full = self.output_dir.relative_to(self.project_dir).as_posix();
271
- prefix = '.client-build/';
272
- if output_relative_full.startswith(prefix) {
273
- # Remove '.client-build/' prefix (15 characters)
274
- output_relative = output_relative_full[15:];
275
- } else {
276
- output_relative = output_relative_full;
277
- }
278
- } except ValueError {
279
- output_relative = self.output_dir.as_posix();
280
- }
251
+ # Fallback: use absolute path
252
+ output_relative = self.output_dir.as_posix();
281
253
  }
282
- # Calculate compiled directory path for aliases (relative to .client-build/)
254
+ # Calculate compiled directory path for aliases (relative to client build dir)
283
255
  if entry_relative.endswith('/build/main.js') {
284
256
  compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
285
257
  compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
@@ -333,9 +305,9 @@ impl ViteBundler.create_vite_config(self: ViteBundler, entry_file: Path) -> Path
333
305
  import path from "path";
334
306
  import {{ fileURLToPath }} from "url";
335
307
  {imports_section}const __dirname = path.dirname(fileURLToPath(import.meta.url));
336
- // Config is in .jac-client.configs/ inside .client-build/, so go up one level to .client-build/, then up one more to project root
308
+ // Config is in configs/ inside .jac/client/, so go up one level to .jac/client/, then up two more to project root
337
309
  const buildDir = path.resolve(__dirname, "..");
338
- const projectRoot = path.resolve(__dirname, "../..");
310
+ const projectRoot = path.resolve(__dirname, "../../..");
339
311
 
340
312
  /**
341
313
  * Vite configuration generated from config.json (in project root)
@@ -344,13 +316,13 @@ const projectRoot = path.resolve(__dirname, "../..");
344
316
 
345
317
  export default defineConfig({{
346
318
  plugins: [{(newline + plugins_str + newline + ' ') if plugins_str else ''}],
347
- root: buildDir, // base folder (.client-build/) so vite can find node_modules
319
+ root: buildDir, // base folder (.jac/client/) so vite can find node_modules
348
320
  build: {{
349
321
  rollupOptions: {{
350
322
  input: path.resolve(buildDir, "{entry_relative}"), // your compiled entry file
351
323
  output: {{
352
324
  entryFileNames: "client.[hash].js", // name of the final js file
353
- assetFileNames: "[name].[ext]",
325
+ assetFileNames: (assetInfo) => assetInfo.name?.endsWith('.css') ? 'styles.css' : '[name].[ext]',
354
326
  }},
355
327
  }},
356
328
  outDir: path.resolve(buildDir, "{output_relative}"), // final bundled output
@@ -391,14 +363,8 @@ impl ViteBundler.read_bundle(self: ViteBundler) -> tuple[str, str] {
391
363
 
392
364
  """Find the generated Vite CSS file."""
393
365
  impl ViteBundler.find_css(self: ViteBundler) -> Optional[Path] {
394
- css_file = self.output_dir / 'main.css';
395
- if css_file.exists() {
396
- return css_file;
397
- }
398
- for file in self.output_dir.glob('*.css') {
399
- return file;
400
- }
401
- return None;
366
+ css_file = self.output_dir / 'styles.css';
367
+ return css_file if css_file.exists() else None;
402
368
  }
403
369
 
404
370
  """Find the generated Vite bundle file."""
@@ -409,10 +375,10 @@ impl ViteBundler.find_bundle(self: ViteBundler) -> Optional[Path] {
409
375
  return None;
410
376
  }
411
377
 
412
- """Run Vite build with generated config in .client-build/.jac-client.configs/."""
378
+ """Run Vite build with generated config in .jac/client/configs/."""
413
379
  impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) -> None {
414
380
  self.output_dir.mkdir(parents=True, exist_ok=True);
415
- generated_package_json = self.project_dir / '.client-build' / '.jac-client.configs' / 'package.json';
381
+ generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
416
382
  if not generated_package_json.exists() {
417
383
  self.create_package_json();
418
384
  } else {
@@ -420,18 +386,18 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
420
386
  self.create_tsconfig();
421
387
  }
422
388
  try {
423
- build_dir = self.project_dir / '.client-build';
389
+ build_dir = self._get_client_dir();
424
390
  node_modules = build_dir / 'node_modules';
425
391
  if not node_modules.exists() {
426
- # Temporarily copy package.json to .client-build/ for npm install
392
+ # Temporarily copy package.json to client build dir for npm install
427
393
  build_package_json = build_dir / 'package.json';
428
- configs_package_json = build_dir / '.jac-client.configs' / 'package.json';
394
+ configs_package_json = build_dir / 'configs' / 'package.json';
429
395
  if configs_package_json.exists() and not build_package_json.exists() {
430
396
  import shutil;
431
397
  shutil.copy2(configs_package_json, build_package_json);
432
398
  }
433
399
  try {
434
- # Install to .client-build/node_modules
400
+ # Install to .jac/client/node_modules
435
401
  subprocess.run(
436
402
  ['npm', 'install'],
437
403
  cwd=build_dir,
@@ -449,27 +415,24 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
449
415
  ) ;
450
416
  }
451
417
  }
452
- build_dir = self.project_dir / '.client-build';
453
418
  if self.config_path {
454
419
  # Make config path relative to build_dir (where vite runs from)
455
- config_str = str(self.config_path);
456
- if '.client-build' in config_str {
457
- # Config is in .client-build/, make it relative to build_dir
420
+ try {
458
421
  config_rel = self.config_path.relative_to(build_dir);
459
422
  command = ['npx', 'vite', 'build', '--config', str(config_rel)];
460
- } else {
461
- # Config is outside .client-build/, use absolute path
423
+ } except ValueError {
424
+ # Config is outside client build dir, use absolute path
462
425
  command = ['npx', 'vite', 'build', '--config', str(self.config_path)];
463
426
  }
464
427
  } elif entry_file {
465
428
  generated_config = self.create_vite_config(entry_file);
466
- # Config is in .client-build/.jac-client.configs/, make it relative to build_dir
429
+ # Config is in configs/, make it relative to build_dir
467
430
  config_rel = generated_config.relative_to(build_dir);
468
431
  command = ['npx', 'vite', 'build', '--config', str(config_rel)];
469
432
  } else {
470
433
  command = ['npm', 'run', 'build'];
471
434
  }
472
- # Run vite from .client-build/ directory so it can find node_modules
435
+ # Run vite from client build directory so it can find node_modules
473
436
  result = subprocess.run(
474
437
  command, cwd=build_dir, check=False, capture_output=True, text=True
475
438
  );
@@ -480,15 +443,15 @@ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) ->
480
443
  ) from None ;
481
444
  }
482
445
  } finally {
483
- # Clean up temporary package.json in .client-build/
446
+ # Clean up temporary package.json in client build dir
484
447
  build_package_json = build_dir / 'package.json';
485
448
  if build_package_json.exists() {
486
449
  build_package_json.unlink();
487
450
  }
488
- # Move package-lock.json to .jac-client.configs/ if it exists
451
+ # Move package-lock.json to configs/ if it exists
489
452
  build_package_lock = build_dir / 'package-lock.json';
490
453
  if build_package_lock.exists() {
491
- configs_package_lock = build_dir / '.jac-client.configs' / 'package-lock.json';
454
+ configs_package_lock = build_dir / 'configs' / 'package-lock.json';
492
455
  if configs_package_lock.exists() {
493
456
  configs_package_lock.unlink();
494
457
  }
@@ -506,8 +469,9 @@ impl ViteBundler.init(
506
469
  config_path: Optional[Path] = None
507
470
  ) {
508
471
  self.project_dir = project_dir;
509
- self.output_dir = output_dir or (project_dir / '.client-build' / 'dist');
510
472
  self.minify = minify;
511
473
  self.config_path = config_path;
512
474
  self.config_loader = JacClientConfig(project_dir);
475
+ # Set output_dir after config_loader is initialized so _get_client_dir works
476
+ self.output_dir = output_dir or (self._get_client_dir() / 'dist');
513
477
  }
@@ -22,5 +22,5 @@ class PackageInstaller {
22
22
  self: PackageInstaller, package_name: str, is_dev: bool = False
23
23
  ) -> None;
24
24
 
25
- def list_packages(self: PackageInstaller) -> dict[str, dict[str, str]];
25
+ def list_packages(self: PackageInstaller) -> dict[str, dict[(str, str)]];
26
26
  }
@@ -17,6 +17,7 @@ class ViteBundler {
17
17
  config_path: Optional[Path] = None
18
18
  );
19
19
 
20
+ def _get_client_dir(self: ViteBundler) -> Path;
20
21
  def build(self: ViteBundler, entry_file: Optional[Path] = None) -> None;
21
22
  def find_bundle(self: ViteBundler) -> Optional[Path];
22
23
  def find_css(self: ViteBundler) -> Optional[Path];
@@ -108,7 +108,7 @@ def npm_cache_dir() -> Generator[Path, None, None]:
108
108
  """Session-scoped fixture that provides a directory with npm packages installed.
109
109
 
110
110
  This runs npm install once per test session and provides the path to the
111
- .jac-client.configs directory containing node_modules.
111
+ .jac/client/configs directory containing node_modules.
112
112
  """
113
113
  global _npm_cache_dir
114
114
 
@@ -156,13 +156,14 @@ def vite_project_dir(npm_cache_dir: Path, tmp_path: Path) -> Path:
156
156
  jac_toml = tmp_path / "jac.toml"
157
157
  jac_toml.write_text(_get_minimal_jac_toml())
158
158
 
159
- # Copy .jac-client.configs directory (contains package.json)
160
- source_configs = npm_cache_dir / ".jac-client.configs"
161
- dest_configs = tmp_path / ".jac-client.configs"
159
+ # Copy .jac/client/configs directory (contains package.json)
160
+ source_configs = npm_cache_dir / ".jac" / "client" / "configs"
161
+ dest_configs = tmp_path / ".jac" / "client" / "configs"
162
162
  if source_configs.exists():
163
+ dest_configs.parent.mkdir(parents=True, exist_ok=True)
163
164
  shutil.copytree(source_configs, dest_configs, symlinks=True)
164
165
 
165
- # Copy node_modules from project root (npm installs there, not in .jac-client.configs)
166
+ # Copy node_modules from project root (npm installs there, not in .jac/client/configs)
166
167
  source_node_modules = npm_cache_dir / "node_modules"
167
168
  dest_node_modules = tmp_path / "node_modules"
168
169
  if source_node_modules.exists():
@@ -195,10 +196,11 @@ antd = "^6.0.0"
195
196
  jac_toml = tmp_path / "jac.toml"
196
197
  jac_toml.write_text(jac_toml_content)
197
198
 
198
- # Copy base .jac-client.configs first for faster install
199
- source_configs = npm_cache_dir / ".jac-client.configs"
200
- dest_configs = tmp_path / ".jac-client.configs"
199
+ # Copy base .jac/client/configs first for faster install
200
+ source_configs = npm_cache_dir / ".jac" / "client" / "configs"
201
+ dest_configs = tmp_path / ".jac" / "client" / "configs"
201
202
  if source_configs.exists():
203
+ dest_configs.parent.mkdir(parents=True, exist_ok=True)
202
204
  shutil.copytree(source_configs, dest_configs, symlinks=True)
203
205
 
204
206
  # Copy base node_modules for faster install (npm will add antd on top)
@@ -32,51 +32,48 @@ walker positional_walker {
32
32
  }
33
33
 
34
34
  # Client-side code testing both spawn orderings
35
- cl import from react {
36
- useState,
37
- useEffect
38
- }
35
+ cl import from react { useEffect }
39
36
 
40
37
  cl {
41
38
  def app() -> any {
42
- [standardResult, setStandardResult] = useState(None);
43
- [standardComputed, setStandardComputed] = useState(None);
44
- [reverseResult, setReverseResult] = useState(None);
45
- [uuidResult, setUuidResult] = useState(None);
46
- [reverseUuidResult, setReverseUuidResult] = useState(None);
47
- [positionalResult, setPositionalResult] = useState(None);
48
- [spreadResult, setSpreadResult] = useState(None);
39
+ has standardResult: any = None;
40
+ has standardComputed: any = None;
41
+ has reverseResult: any = None;
42
+ has uuidResult: any = None;
43
+ has reverseUuidResult: any = None;
44
+ has positionalResult: any = None;
45
+ has spreadResult: any = None;
49
46
 
50
47
  async def loadData() -> None {
51
48
  # Test standard spawn order: node spawn walker()
52
49
  data1 = root spawn test_walker();
53
- setStandardResult(data1);
50
+ standardResult = data1;
54
51
 
55
52
  data2 = root spawn parameterized_walker(value=42);
56
- setStandardComputed(data2);
53
+ standardComputed = data2;
57
54
 
58
55
  # Test reverse spawn order: walker() spawn node
59
56
  data3 = test_walker(message="Reverse spawn!") spawn root;
60
- setReverseResult(data3);
57
+ reverseResult = data3;
61
58
 
62
59
  # Test spawn with UUID string: uuid_string spawn walker()
63
60
  node_id = "550e8400-e29b-41d4-a716-446655440000";
64
61
  data4 = node_id spawn test_walker();
65
- setUuidResult(data4);
62
+ uuidResult = data4;
66
63
 
67
64
  # Test reverse spawn with UUID string: walker() spawn uuid_string
68
65
  another_node_id = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
69
66
  data5 = parameterized_walker(value=100) spawn another_node_id;
70
- setReverseUuidResult(data5);
67
+ reverseUuidResult = data5;
71
68
 
72
69
  # Test positional walker arguments inferred from has fields
73
70
  data6 = node_id spawn positional_walker("Node positional", 2);
74
- setPositionalResult(data6);
71
+ positionalResult = data6;
75
72
 
76
73
  # Test **kwargs via spread when walker is on left-hand side
77
74
  extra_fields = {"metadata": {"source": "client-side"}};
78
75
  data7 = positional_walker("Spread order", 5, **extra_fields) spawn root;
79
- setSpreadResult(data7);
76
+ spreadResult = data7;
80
77
  }
81
78
 
82
79
  useEffect(lambda -> None{ loadData();} , []);
@@ -1,11 +1,11 @@
1
1
 
2
2
  # Pages
3
- cl import from react { useState, useEffect }
3
+ cl import from react { useEffect }
4
4
  cl import from ".components/Button.tsx" { Button }
5
5
 
6
6
  cl {
7
7
  def app() -> any {
8
- [count, setCount] = useState(0);
8
+ has count: int = 0;
9
9
  useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
10
10
  return <div
11
11
  style={{padding: "2rem", fontFamily: "Arial, sans-serif"}}
@@ -21,12 +21,12 @@ cl {
21
21
  >
22
22
  <Button
23
23
  label="Increment"
24
- onClick={lambda -> None{ setCount(count + 1);} }
24
+ onClick={lambda -> None{ count = count + 1;} }
25
25
  variant="primary"
26
26
  />
27
27
  <Button
28
28
  label="Reset"
29
- onClick={lambda -> None{ setCount(0);} }
29
+ onClick={lambda -> None{ count = 0;} }
30
30
  variant="secondary"
31
31
  />
32
32
  </div>