jac-client 0.2.0__py3-none-any.whl → 0.2.1__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 (84) hide show
  1. jac_client/docs/README.md +31 -1
  2. jac_client/docs/asset-serving/intro.md +209 -0
  3. jac_client/docs/assets/pipe_line-v2.svg +32 -0
  4. jac_client/docs/file-system/intro.md +90 -0
  5. jac_client/docs/styling/intro.md +250 -0
  6. jac_client/docs/styling/js-styling.md +373 -0
  7. jac_client/docs/styling/material-ui.md +346 -0
  8. jac_client/docs/styling/pure-css.md +305 -0
  9. jac_client/docs/styling/sass.md +409 -0
  10. jac_client/docs/styling/styled-components.md +401 -0
  11. jac_client/docs/styling/tailwind.md +303 -0
  12. jac_client/examples/asset-serving/css-with-image/.babelrc +9 -0
  13. jac_client/examples/asset-serving/css-with-image/README.md +91 -0
  14. jac_client/examples/asset-serving/css-with-image/app.jac +67 -0
  15. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  16. jac_client/examples/asset-serving/css-with-image/package.json +28 -0
  17. jac_client/examples/asset-serving/css-with-image/styles.css +27 -0
  18. jac_client/examples/asset-serving/css-with-image/vite.config.js +29 -0
  19. jac_client/examples/asset-serving/image-asset/.babelrc +9 -0
  20. jac_client/examples/asset-serving/image-asset/README.md +119 -0
  21. jac_client/examples/asset-serving/image-asset/app.jac +43 -0
  22. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  23. jac_client/examples/asset-serving/image-asset/package.json +28 -0
  24. jac_client/examples/asset-serving/image-asset/styles.css +27 -0
  25. jac_client/examples/asset-serving/image-asset/vite.config.js +29 -0
  26. jac_client/examples/asset-serving/import-alias/.babelrc +9 -0
  27. jac_client/examples/asset-serving/import-alias/README.md +83 -0
  28. jac_client/examples/asset-serving/import-alias/app.jac +57 -0
  29. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  30. jac_client/examples/asset-serving/import-alias/package.json +28 -0
  31. jac_client/examples/asset-serving/import-alias/vite.config.js +29 -0
  32. jac_client/examples/css-styling/js-styling/.babelrc +9 -0
  33. jac_client/examples/css-styling/js-styling/README.md +183 -0
  34. jac_client/examples/css-styling/js-styling/app.jac +63 -0
  35. jac_client/examples/css-styling/js-styling/package.json +28 -0
  36. jac_client/examples/css-styling/js-styling/styles.js +100 -0
  37. jac_client/examples/css-styling/js-styling/vite.config.js +28 -0
  38. jac_client/examples/css-styling/material-ui/.babelrc +9 -0
  39. jac_client/examples/css-styling/material-ui/README.md +16 -0
  40. jac_client/examples/css-styling/material-ui/app.jac +82 -0
  41. jac_client/examples/css-styling/material-ui/package.json +32 -0
  42. jac_client/examples/css-styling/material-ui/vite.config.js +28 -0
  43. jac_client/examples/css-styling/pure-css/.babelrc +9 -0
  44. jac_client/examples/css-styling/pure-css/README.md +16 -0
  45. jac_client/examples/css-styling/pure-css/app.jac +63 -0
  46. jac_client/examples/css-styling/pure-css/package.json +28 -0
  47. jac_client/examples/css-styling/pure-css/styles.css +112 -0
  48. jac_client/examples/css-styling/pure-css/vite.config.js +28 -0
  49. jac_client/examples/css-styling/sass-example/.babelrc +9 -0
  50. jac_client/examples/css-styling/sass-example/README.md +16 -0
  51. jac_client/examples/css-styling/sass-example/app.jac +63 -0
  52. jac_client/examples/css-styling/sass-example/package.json +29 -0
  53. jac_client/examples/css-styling/sass-example/styles.scss +158 -0
  54. jac_client/examples/css-styling/sass-example/vite.config.js +28 -0
  55. jac_client/examples/css-styling/styled-components/.babelrc +9 -0
  56. jac_client/examples/css-styling/styled-components/README.md +16 -0
  57. jac_client/examples/css-styling/styled-components/app.jac +66 -0
  58. jac_client/examples/css-styling/styled-components/package.json +29 -0
  59. jac_client/examples/css-styling/styled-components/styled.js +91 -0
  60. jac_client/examples/css-styling/styled-components/vite.config.js +28 -0
  61. jac_client/examples/css-styling/tailwind-example/.babelrc +9 -0
  62. jac_client/examples/css-styling/tailwind-example/README.md +16 -0
  63. jac_client/examples/css-styling/tailwind-example/app.jac +64 -0
  64. jac_client/examples/css-styling/tailwind-example/global.css +1 -0
  65. jac_client/examples/css-styling/tailwind-example/package.json +30 -0
  66. jac_client/examples/css-styling/tailwind-example/vite.config.js +30 -0
  67. jac_client/examples/with-router/app.jac +1 -1
  68. jac_client/plugin/cli.py +5 -0
  69. jac_client/plugin/client.py +64 -3
  70. jac_client/plugin/vite_client_bundle.py +96 -1
  71. jac_client/tests/__init__.py +0 -1
  72. jac_client/tests/fixtures/cl_file/app.cl.jac +38 -0
  73. jac_client/tests/fixtures/cl_file/app.jac +15 -0
  74. jac_client/tests/fixtures/js_import/app.jac +1 -1
  75. jac_client/tests/fixtures/relative_import/button.jac +2 -2
  76. jac_client/tests/fixtures/test_fragments_spread/app.jac +2 -2
  77. jac_client/tests/test_asset_examples.py +339 -0
  78. jac_client/tests/test_cl.py +165 -87
  79. jac_client/tests/test_create_jac_app.py +40 -44
  80. {jac_client-0.2.0.dist-info → jac_client-0.2.1.dist-info}/METADATA +2 -2
  81. jac_client-0.2.1.dist-info/RECORD +140 -0
  82. jac_client-0.2.0.dist-info/RECORD +0 -72
  83. {jac_client-0.2.0.dist-info → jac_client-0.2.1.dist-info}/WHEEL +0 -0
  84. {jac_client-0.2.0.dist-info → jac_client-0.2.1.dist-info}/entry_points.txt +0 -0
@@ -22,10 +22,12 @@ class ViteClientBundleBuilderTests(TestCase):
22
22
  def tearDown(self) -> None:
23
23
  Jac.reset_machine()
24
24
  return super().tearDown()
25
-
26
- def _create_test_project_with_vite(self, temp_path: Path, include_antd: bool = False) -> tuple[Path, Path]:
25
+
26
+ def _create_test_project_with_vite(
27
+ self, temp_path: Path, include_antd: bool = False
28
+ ) -> tuple[Path, Path]:
27
29
  """Create a minimal test project with Vite installed.
28
-
30
+
29
31
  Args:
30
32
  temp_path: Path to the temporary directory
31
33
  include_antd: If True, includes antd in dependencies
@@ -34,13 +36,13 @@ class ViteClientBundleBuilderTests(TestCase):
34
36
  dependencies = {
35
37
  "react": "^19.2.0",
36
38
  "react-dom": "^19.2.0",
37
- "react-router-dom": "^7.3.0"
39
+ "react-router-dom": "^7.3.0",
38
40
  }
39
-
41
+
40
42
  # Add antd if requested
41
43
  if include_antd:
42
44
  dependencies["antd"] = "^5.0.0"
43
-
45
+
44
46
  # Create package.json structure
45
47
  package_data = {
46
48
  "name": "test-client",
@@ -50,7 +52,7 @@ class ViteClientBundleBuilderTests(TestCase):
50
52
  "build": "npm run compile && vite build",
51
53
  "dev": "vite dev",
52
54
  "preview": "vite preview",
53
- "compile": 'babel src --out-dir build --extensions ".jsx,.js" --out-file-extension .js'
55
+ "compile": 'babel src --out-dir build --extensions ".jsx,.js" --out-file-extension .js',
54
56
  },
55
57
  "dependencies": dependencies,
56
58
  "devDependencies": {
@@ -58,17 +60,18 @@ class ViteClientBundleBuilderTests(TestCase):
58
60
  "@babel/cli": "^7.28.3",
59
61
  "@babel/core": "^7.28.5",
60
62
  "@babel/preset-env": "^7.28.5",
61
- "@babel/preset-react": "^7.28.5"
62
- }
63
+ "@babel/preset-react": "^7.28.5",
64
+ },
63
65
  }
64
-
66
+
65
67
  package_json = temp_path / "package.json"
66
68
  with package_json.open("w", encoding="utf-8") as f:
67
69
  json.dump(package_data, f, indent=2)
68
-
70
+
69
71
  # Create .babelrc file
70
72
  babelrc = temp_path / ".babelrc"
71
- babelrc.write_text("""{
73
+ babelrc.write_text(
74
+ """{
72
75
  "presets": [[
73
76
  "@babel/preset-env",
74
77
  {
@@ -76,11 +79,14 @@ class ViteClientBundleBuilderTests(TestCase):
76
79
  }
77
80
  ], "@babel/preset-react"]
78
81
  }
79
- """, encoding="utf-8")
80
-
82
+ """,
83
+ encoding="utf-8",
84
+ )
85
+
81
86
  # Create vite.config.js file
82
87
  vite_config = temp_path / "vite.config.js"
83
- vite_config.write_text("""import { defineConfig } from "vite";
88
+ vite_config.write_text(
89
+ """import { defineConfig } from "vite";
84
90
  import path from "path";
85
91
  import { fileURLToPath } from "url";
86
92
 
@@ -107,8 +113,10 @@ export default defineConfig({
107
113
  },
108
114
  },
109
115
  });
110
- """, encoding="utf-8")
111
-
116
+ """,
117
+ encoding="utf-8",
118
+ )
119
+
112
120
  # Install dependencies
113
121
  result = subprocess.run(
114
122
  ["npm", "install"],
@@ -122,27 +130,29 @@ export default defineConfig({
122
130
  error_msg += f"stdout: {result.stdout}\n"
123
131
  error_msg += f"stderr: {result.stderr}\n"
124
132
  raise RuntimeError(error_msg)
125
-
133
+
126
134
  # Create output directory
127
135
  output_dir = temp_path / "dist"
128
136
  output_dir.mkdir(parents=True, exist_ok=True)
129
-
137
+
130
138
  src_dir = temp_path / "src"
131
139
  src_dir.mkdir(parents=True, exist_ok=True)
132
-
140
+
133
141
  build_dir = temp_path / "build"
134
142
  build_dir.mkdir(parents=True, exist_ok=True)
135
-
143
+
136
144
  return package_json, output_dir
137
145
 
138
146
  def test_build_bundle_with_vite(self) -> None:
139
147
  """Test that Vite bundling produces optimized output with proper structure."""
140
148
  # Create a temporary directory for our test project
141
- with tempfile.TemporaryDirectory() as temp_dir:
149
+ with tempfile.TemporaryDirectory() as temp_dir:
142
150
  temp_path = Path(temp_dir)
143
-
151
+
144
152
  package_json, output_dir = self._create_test_project_with_vite(temp_path)
145
- runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
153
+ runtime_path = (
154
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
155
+ )
146
156
  # Initialize the Vite builder
147
157
  builder = ViteClientBundleBuilder(
148
158
  runtime_path=runtime_path,
@@ -162,16 +172,17 @@ export default defineConfig({
162
172
  self.assertIn("ButtonProps", bundle.client_functions)
163
173
  self.assertIn("API_LABEL", bundle.client_globals)
164
174
  self.assertGreater(len(bundle.hash), 10)
165
-
175
+
166
176
  # Verify bundle code contains expected content
167
177
  self.assertIn("function app()", bundle.code)
168
178
  self.assertIn('API_LABEL = "Runtime Test";', bundle.code)
169
-
170
-
179
+
171
180
  # Verify bundle was written to output directory
172
181
  bundle_files = list(output_dir.glob("client.*.js"))
173
- self.assertGreater(len(bundle_files), 0, "Expected at least one bundle file")
174
-
182
+ self.assertGreater(
183
+ len(bundle_files), 0, "Expected at least one bundle file"
184
+ )
185
+
175
186
  # Verify cached bundle is identical
176
187
  cached = builder.build(module, force=False)
177
188
  self.assertEqual(bundle.hash, cached.hash)
@@ -181,33 +192,38 @@ export default defineConfig({
181
192
  """Test that missing package.json raises appropriate error."""
182
193
  fixtures_dir = Path(__file__).parent / "fixtures" / "basic-app"
183
194
  (module,) = Jac.jac_import("app", str(fixtures_dir))
184
-
195
+
185
196
  runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
186
-
197
+
187
198
  # Create builder without package.json
188
199
  builder = ViteClientBundleBuilder(
189
200
  runtime_path=runtime_path,
190
201
  vite_package_json=Path("/nonexistent/package.json"),
191
202
  vite_output_dir=Path("/tmp/output"),
192
203
  )
193
-
204
+
194
205
  # Building should raise an error
195
206
  from jaclang.runtimelib.client_bundle import ClientBundleError
207
+
196
208
  with self.assertRaises(ClientBundleError) as cm:
197
209
  builder.build(module, force=True)
198
-
210
+
199
211
  self.assertIn("Vite package.json not found", str(cm.exception))
200
212
 
201
213
  def test_build_bundle_with_antd(self) -> None:
202
214
  """Test that Vite bundling works with Ant Design components."""
203
215
  with tempfile.TemporaryDirectory() as temp_dir:
204
-
216
+
205
217
  temp_path = Path(temp_dir)
206
-
218
+
207
219
  # Create project with Vite and Ant Design installed
208
- package_json, output_dir = self._create_test_project_with_vite(temp_path, include_antd=True)
209
- runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
210
-
220
+ package_json, output_dir = self._create_test_project_with_vite(
221
+ temp_path, include_antd=True
222
+ )
223
+ runtime_path = (
224
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
225
+ )
226
+
211
227
  # Initialize the Vite builder
212
228
  builder = ViteClientBundleBuilder(
213
229
  runtime_path=runtime_path,
@@ -215,36 +231,38 @@ export default defineConfig({
215
231
  vite_output_dir=output_dir,
216
232
  vite_minify=False,
217
233
  )
218
-
234
+
219
235
  # Import the test module with Ant Design
220
236
  fixtures_dir = Path(__file__).parent / "fixtures" / "client_app_with_antd"
221
237
  (module,) = Jac.jac_import("app", str(fixtures_dir))
222
-
238
+
223
239
  # Build the bundle
224
240
  bundle = builder.build(module, force=True)
225
-
241
+
226
242
  # Verify bundle structure
227
243
  self.assertIsNotNone(bundle)
228
244
  self.assertEqual(bundle.module_name, "app")
229
245
  self.assertIn("ButtonTest", bundle.client_functions)
230
246
  self.assertIn("CardTest", bundle.client_functions)
231
247
  self.assertIn("APP_NAME", bundle.client_globals)
232
-
248
+
233
249
  # Verify bundle code contains expected content
234
250
  self.assertIn("function ButtonTest()", bundle.code)
235
251
  self.assertIn("function CardTest()", bundle.code)
236
252
  self.assertIn('APP_NAME = "Ant Design Test";', bundle.code)
237
-
253
+
238
254
  # verify antd components are present
239
255
  self.assertIn("ButtonGroup", bundle.code)
240
256
 
241
257
  # Verify the Ant Design fixture content is present
242
258
  self.assertIn("Testing Ant Design integration", bundle.code)
243
-
259
+
244
260
  # Verify bundle was written to output directory
245
261
  bundle_files = list(output_dir.glob("client.*.js"))
246
- self.assertGreater(len(bundle_files), 0, "Expected at least one bundle file")
247
-
262
+ self.assertGreater(
263
+ len(bundle_files), 0, "Expected at least one bundle file"
264
+ )
265
+
248
266
  # Cleanup
249
267
  builder.cleanup_temp_dir()
250
268
 
@@ -252,11 +270,15 @@ export default defineConfig({
252
270
  """Test that relative imports work correctly in Vite bundling."""
253
271
  with tempfile.TemporaryDirectory() as temp_dir:
254
272
  temp_path = Path(temp_dir)
255
-
273
+
256
274
  # Create project with Vite installed
257
- package_json, output_dir = self._create_test_project_with_vite(temp_path, include_antd=True)
258
- runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
259
-
275
+ package_json, output_dir = self._create_test_project_with_vite(
276
+ temp_path, include_antd=True
277
+ )
278
+ runtime_path = (
279
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
280
+ )
281
+
260
282
  # Initialize the Vite builder
261
283
  builder = ViteClientBundleBuilder(
262
284
  runtime_path=runtime_path,
@@ -264,32 +286,34 @@ export default defineConfig({
264
286
  vite_output_dir=output_dir,
265
287
  vite_minify=False,
266
288
  )
267
-
289
+
268
290
  # Import the test module with relative import
269
291
  fixtures_dir = Path(__file__).parent / "fixtures" / "relative_import"
270
292
  (module,) = Jac.jac_import("app", str(fixtures_dir))
271
-
293
+
272
294
  # Build the bundle
273
295
  bundle = builder.build(module, force=True)
274
-
296
+
275
297
  # Verify bundle structure
276
298
  self.assertIsNotNone(bundle)
277
299
  self.assertEqual(bundle.module_name, "app")
278
300
  self.assertIn("RelativeImport", bundle.client_functions)
279
301
  self.assertIn("app", bundle.client_functions)
280
302
  self.assertIn("CustomButton", bundle.code)
281
-
303
+
282
304
  # Verify bundle code contains expected content
283
305
  self.assertIn("function RelativeImport()", bundle.code)
284
306
  self.assertIn("function app()", bundle.code)
285
-
307
+
286
308
  # Verify that the relative import (Button from .button) is properly resolved
287
309
  self.assertIn("ButtonGroup", bundle.code)
288
-
310
+
289
311
  # Verify bundle was written to output directory
290
312
  bundle_files = list(output_dir.glob("client.*.js"))
291
- self.assertGreater(len(bundle_files), 0, "Expected at least one bundle file")
292
-
313
+ self.assertGreater(
314
+ len(bundle_files), 0, "Expected at least one bundle file"
315
+ )
316
+
293
317
  # Cleanup
294
318
  builder.cleanup_temp_dir()
295
319
 
@@ -299,7 +323,9 @@ export default defineConfig({
299
323
  temp_path = Path(temp_dir)
300
324
  # Create project with Vite installed
301
325
  package_json, output_dir = self._create_test_project_with_vite(temp_path)
302
- runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
326
+ runtime_path = (
327
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
328
+ )
303
329
  # Initialize the Vite builder
304
330
  builder = ViteClientBundleBuilder(
305
331
  runtime_path=runtime_path,
@@ -307,53 +333,57 @@ export default defineConfig({
307
333
  vite_output_dir=output_dir,
308
334
  vite_minify=False,
309
335
  )
310
-
336
+
311
337
  # Import the test module with JavaScript import
312
338
  fixtures_dir = Path(__file__).parent / "fixtures" / "js_import"
313
339
  (module,) = Jac.jac_import("app", str(fixtures_dir))
314
-
340
+
315
341
  # Build the bundle
316
342
  bundle = builder.build(module, force=True)
317
-
343
+
318
344
  # Verify bundle structure
319
345
  self.assertIsNotNone(bundle)
320
346
  self.assertEqual(bundle.module_name, "app")
321
347
  self.assertIn("JsImportTest", bundle.client_functions)
322
348
  self.assertIn("app", bundle.client_functions)
323
349
  self.assertIn("JS_IMPORT_LABEL", bundle.client_globals)
324
-
350
+
325
351
  # Verify bundle code contains expected content
326
352
  self.assertIn("function JsImportTest()", bundle.code)
327
353
  self.assertIn("function app()", bundle.code)
328
354
  self.assertIn('JS_IMPORT_LABEL = "JavaScript Import Test";', bundle.code)
329
-
355
+
330
356
  # Verify JavaScript imports are present in the bundle
331
357
  # The JavaScript functions should be available in the bundle
332
358
  self.assertIn("formatMessage", bundle.code)
333
359
  self.assertIn("calculateSum", bundle.code)
334
360
  self.assertIn("JS_CONSTANT", bundle.code)
335
361
  self.assertIn("MessageFormatter", bundle.code)
336
-
362
+
337
363
  # Verify the JavaScript utility code is included
338
364
  self.assertIn("Hello,", bundle.code) # From formatMessage function
339
365
  self.assertIn("Imported from JavaScript", bundle.code) # From JS_CONSTANT
340
-
366
+
341
367
  # Verify bundle was written to output directory
342
368
  bundle_files = list(output_dir.glob("client.*.js"))
343
- self.assertGreater(len(bundle_files), 0, "Expected at least one bundle file")
344
-
369
+ self.assertGreater(
370
+ len(bundle_files), 0, "Expected at least one bundle file"
371
+ )
372
+
345
373
  # Cleanup
346
374
  builder.cleanup_temp_dir()
347
-
375
+
348
376
  def test_jsx_fragments_and_spread_props(self) -> None:
349
377
  """Test that JSX fragments and spread props work correctly."""
350
378
  with tempfile.TemporaryDirectory() as temp_dir:
351
379
  temp_path = Path(temp_dir)
352
-
380
+
353
381
  # Create project with Vite installed
354
382
  package_json, output_dir = self._create_test_project_with_vite(temp_path)
355
- runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
356
-
383
+ runtime_path = (
384
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
385
+ )
386
+
357
387
  # Initialize the Vite builder
358
388
  builder = ViteClientBundleBuilder(
359
389
  runtime_path=runtime_path,
@@ -361,14 +391,14 @@ export default defineConfig({
361
391
  vite_output_dir=output_dir,
362
392
  vite_minify=False,
363
393
  )
364
-
394
+
365
395
  # Import the test module with fragments and spread props
366
396
  fixtures_dir = Path(__file__).parent / "fixtures" / "test_fragments_spread"
367
397
  (module,) = Jac.jac_import("app", str(fixtures_dir))
368
-
398
+
369
399
  # Build the bundle
370
400
  bundle = builder.build(module, force=True)
371
-
401
+
372
402
  # Verify bundle structure
373
403
  self.assertIsNotNone(bundle)
374
404
  self.assertEqual(bundle.module_name, "app")
@@ -376,20 +406,22 @@ export default defineConfig({
376
406
  self.assertIn("SpreadPropsTest", bundle.client_functions)
377
407
  self.assertIn("MixedTest", bundle.client_functions)
378
408
  self.assertIn("NestedFragments", bundle.client_functions)
379
-
409
+
380
410
  # Verify spread props handling (Object.assign is used by compiler)
381
411
  self.assertIn("Object.assign", bundle.code)
382
-
412
+
383
413
  # Verify fragment test function exists
384
414
  self.assertIn("function FragmentTest()", bundle.code)
385
-
415
+
386
416
  # Verify spread props test function exists
387
417
  self.assertIn("function SpreadPropsTest()", bundle.code)
388
-
418
+
389
419
  # Verify bundle was written to output directory
390
420
  bundle_files = list(output_dir.glob("client.*.js"))
391
- self.assertGreater(len(bundle_files), 0, "Expected at least one bundle file")
392
-
421
+ self.assertGreater(
422
+ len(bundle_files), 0, "Expected at least one bundle file"
423
+ )
424
+
393
425
  # Cleanup
394
426
  builder.cleanup_temp_dir()
395
427
 
@@ -400,7 +432,9 @@ export default defineConfig({
400
432
 
401
433
  # Create project with Vite installed
402
434
  package_json, output_dir = self._create_test_project_with_vite(temp_path)
403
- runtime_path = Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
435
+ runtime_path = (
436
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
437
+ )
404
438
 
405
439
  # Initialize the Vite builder
406
440
  builder = ViteClientBundleBuilder(
@@ -434,7 +468,7 @@ export default defineConfig({
434
468
  # Should generate: __jacSpawn("test_walker", "", {message: "Reverse spawn!"})
435
469
  self.assertRegex(
436
470
  bundle.code,
437
- r'__jacSpawn\("test_walker",\s*"",\s*\{[^}]*"message":\s*"Reverse spawn!"[^}]*\}\)'
471
+ r'__jacSpawn\("test_walker",\s*"",\s*\{[^}]*"message":\s*"Reverse spawn!"[^}]*\}\)',
438
472
  )
439
473
 
440
474
  # Verify UUID spawn scenarios with complete calls
@@ -447,7 +481,7 @@ export default defineConfig({
447
481
  # Should generate: __jacSpawn("parameterized_walker", another_node_id, {value: 100})
448
482
  self.assertRegex(
449
483
  bundle.code,
450
- r'__jacSpawn\("parameterized_walker",\s*another_node_id,\s*\{[^}]*"value":\s*100[^}]*\}\)'
484
+ r'__jacSpawn\("parameterized_walker",\s*another_node_id,\s*\{[^}]*"value":\s*100[^}]*\}\)',
451
485
  )
452
486
  self.assertIn('"6ba7b810-9dad-11d1-80b4-00c04fd430c8"', bundle.code)
453
487
 
@@ -464,13 +498,57 @@ export default defineConfig({
464
498
 
465
499
  # Verify we have at least 7 __jacSpawn calls (previous cases + new positional/spread)
466
500
  self.assertTrue(
467
- bundle.code.count('__jacSpawn') >= 7,
468
- "Expected at least 7 __jacSpawn calls in bundle"
501
+ bundle.code.count("__jacSpawn") >= 7,
502
+ "Expected at least 7 __jacSpawn calls in bundle",
469
503
  )
470
504
 
471
505
  # Verify bundle was written to output directory
472
506
  bundle_files = list(output_dir.glob("client.*.js"))
473
- self.assertGreater(len(bundle_files), 0, "Expected at least one bundle file")
507
+ self.assertGreater(
508
+ len(bundle_files), 0, "Expected at least one bundle file"
509
+ )
510
+
511
+ # Cleanup
512
+ builder.cleanup_temp_dir()
513
+
514
+ def test_serve_cl_file(self) -> None:
515
+ """Test that serving a .cl file works correctly."""
516
+ with tempfile.TemporaryDirectory() as temp_dir:
517
+ temp_path = Path(temp_dir)
518
+
519
+ # Create project with Vite installed
520
+ package_json, output_dir = self._create_test_project_with_vite(temp_path)
521
+ runtime_path = (
522
+ Path(__file__).parent.parent / "plugin" / "client_runtime.jac"
523
+ )
524
+
525
+ # Initialize the Vite builder
526
+ builder = ViteClientBundleBuilder(
527
+ runtime_path=runtime_path,
528
+ vite_package_json=package_json,
529
+ vite_output_dir=output_dir,
530
+ vite_minify=False,
531
+ )
474
532
 
533
+ # Import the test module with both spawn operator orderings
534
+ fixtures_dir = Path(__file__).parent / "fixtures" / "cl_file"
535
+ (module,) = Jac.jac_import("app", str(fixtures_dir))
536
+
537
+ # Build the bundle
538
+ bundle = builder.build(module, force=True)
539
+ # Verify bundle structure
540
+ self.assertIsNotNone(bundle)
541
+ self.assertEqual(bundle.module_name, "app")
542
+ self.assertIn("app", bundle.client_functions)
543
+
544
+ self.assertIn("function app()", bundle.code)
545
+ self.assertIn(
546
+ '__jacJsx("div", {}, [__jacJsx("h2", {}, ["My Todos"])', bundle.code
547
+ )
548
+ self.assertIn("root.render(/* @__PURE__ */ React.c", bundle.code)
549
+ self.assertIn(
550
+ "ar _useState = reactExports.useState([]), _useStat", bundle.code
551
+ )
552
+ self.assertIn('turn __jacSpawn("create_todo", ', bundle.code)
475
553
  # Cleanup
476
554
  builder.cleanup_temp_dir()