jac-client 0.2.2__py3-none-any.whl → 0.2.6__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 (202) hide show
  1. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  2. jac_client/examples/all-in-one/src/app.jac +841 -0
  3. jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
  4. jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
  5. jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
  6. jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
  7. jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +7 -7
  8. jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
  9. jac_client/examples/basic-auth/src/app.jac +377 -0
  10. jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
  11. jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
  12. jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
  13. jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
  14. jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
  15. jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
  16. jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
  17. jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
  18. jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
  19. jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
  20. jac_client/examples/little-x/src/submit-button.jac +16 -0
  21. jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
  22. jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
  23. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
  24. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
  25. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
  26. jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
  27. jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
  28. jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
  29. jac_client/examples/ts-support/src/app.jac +35 -0
  30. jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
  31. jac_client/plugin/cli.jac +547 -0
  32. jac_client/plugin/client.jac +52 -0
  33. jac_client/plugin/client_runtime.cl.jac +38 -0
  34. jac_client/plugin/impl/client.impl.jac +134 -0
  35. jac_client/plugin/impl/client_runtime.impl.jac +177 -0
  36. jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
  37. jac_client/plugin/plugin_config.jac +195 -0
  38. jac_client/plugin/src/__init__.jac +20 -0
  39. jac_client/plugin/src/asset_processor.jac +33 -0
  40. jac_client/plugin/src/babel_processor.jac +18 -0
  41. jac_client/plugin/src/compiler.jac +66 -0
  42. jac_client/plugin/src/config_loader.jac +32 -0
  43. jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
  44. jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
  45. jac_client/plugin/src/impl/compiler.impl.jac +251 -0
  46. jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
  47. jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
  48. jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
  49. jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
  50. jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
  51. jac_client/plugin/src/import_processor.jac +19 -0
  52. jac_client/plugin/src/jac_to_js.jac +35 -0
  53. jac_client/plugin/src/package_installer.jac +26 -0
  54. jac_client/plugin/src/vite_bundler.jac +36 -0
  55. jac_client/plugin/vite_client_bundle.jac +31 -0
  56. jac_client/tests/conftest.py +281 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +2 -2
  58. jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
  59. jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
  60. jac_client/tests/fixtures/js_import/app.jac +5 -5
  61. jac_client/tests/fixtures/spawn_test/app.jac +7 -7
  62. jac_client/tests/fixtures/with-ts/app.jac +35 -0
  63. jac_client/tests/test_cli.py +755 -0
  64. jac_client/tests/test_it.py +347 -67
  65. {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/METADATA +30 -24
  66. jac_client-0.2.6.dist-info/RECORD +74 -0
  67. {jac_client-0.2.2.dist-info → jac_client-0.2.6.dist-info}/WHEEL +2 -1
  68. jac_client-0.2.6.dist-info/entry_points.txt +4 -0
  69. jac_client-0.2.6.dist-info/top_level.txt +1 -0
  70. jac_client/docs/README.md +0 -689
  71. jac_client/docs/advanced-state.md +0 -1265
  72. jac_client/docs/asset-serving/intro.md +0 -209
  73. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  74. jac_client/docs/assets/pipe_line.png +0 -0
  75. jac_client/docs/file-system/app.jac.md +0 -121
  76. jac_client/docs/file-system/backend-frontend.md +0 -217
  77. jac_client/docs/file-system/intro.md +0 -72
  78. jac_client/docs/file-system/nested-imports.md +0 -348
  79. jac_client/docs/guide-example/intro.md +0 -115
  80. jac_client/docs/guide-example/step-01-setup.md +0 -270
  81. jac_client/docs/guide-example/step-02-components.md +0 -416
  82. jac_client/docs/guide-example/step-03-styling.md +0 -478
  83. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  84. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  85. jac_client/docs/guide-example/step-06-events.md +0 -749
  86. jac_client/docs/guide-example/step-07-effects.md +0 -468
  87. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  88. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  89. jac_client/docs/guide-example/step-10-routing.md +0 -539
  90. jac_client/docs/guide-example/step-11-final.md +0 -963
  91. jac_client/docs/imports.md +0 -1141
  92. jac_client/docs/lifecycle-hooks.md +0 -773
  93. jac_client/docs/routing.md +0 -659
  94. jac_client/docs/styling/intro.md +0 -249
  95. jac_client/docs/styling/js-styling.md +0 -367
  96. jac_client/docs/styling/material-ui.md +0 -341
  97. jac_client/docs/styling/pure-css.md +0 -299
  98. jac_client/docs/styling/sass.md +0 -403
  99. jac_client/docs/styling/styled-components.md +0 -395
  100. jac_client/docs/styling/tailwind.md +0 -298
  101. jac_client/examples/all-in-one/.babelrc +0 -9
  102. jac_client/examples/all-in-one/README.md +0 -16
  103. jac_client/examples/all-in-one/app.jac +0 -426
  104. jac_client/examples/all-in-one/assets/burger.png +0 -0
  105. jac_client/examples/all-in-one/package.json +0 -29
  106. jac_client/examples/all-in-one/styles.css +0 -26
  107. jac_client/examples/all-in-one/vite.config.js +0 -28
  108. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  109. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  110. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  111. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  112. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  113. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  114. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  115. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  116. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  117. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  118. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  119. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  120. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  121. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  122. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  123. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  124. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  125. jac_client/examples/basic/.babelrc +0 -9
  126. jac_client/examples/basic/README.md +0 -16
  127. jac_client/examples/basic/package.json +0 -27
  128. jac_client/examples/basic/vite.config.js +0 -27
  129. jac_client/examples/basic-auth/.babelrc +0 -9
  130. jac_client/examples/basic-auth/README.md +0 -16
  131. jac_client/examples/basic-auth/app.jac +0 -308
  132. jac_client/examples/basic-auth/package.json +0 -27
  133. jac_client/examples/basic-auth/vite.config.js +0 -27
  134. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  135. jac_client/examples/basic-auth-with-router/README.md +0 -60
  136. jac_client/examples/basic-auth-with-router/package.json +0 -28
  137. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  138. jac_client/examples/basic-full-stack/.babelrc +0 -9
  139. jac_client/examples/basic-full-stack/README.md +0 -18
  140. jac_client/examples/basic-full-stack/package.json +0 -28
  141. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  142. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  143. jac_client/examples/css-styling/js-styling/README.md +0 -183
  144. jac_client/examples/css-styling/js-styling/package.json +0 -28
  145. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  146. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  147. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  148. jac_client/examples/css-styling/material-ui/README.md +0 -16
  149. jac_client/examples/css-styling/material-ui/package.json +0 -32
  150. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  151. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  152. jac_client/examples/css-styling/pure-css/README.md +0 -16
  153. jac_client/examples/css-styling/pure-css/package.json +0 -28
  154. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  155. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  156. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  157. jac_client/examples/css-styling/sass-example/README.md +0 -16
  158. jac_client/examples/css-styling/sass-example/package.json +0 -29
  159. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  160. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  161. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  162. jac_client/examples/css-styling/styled-components/README.md +0 -16
  163. jac_client/examples/css-styling/styled-components/package.json +0 -29
  164. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  165. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  166. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  167. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  168. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  169. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  170. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  171. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  172. jac_client/examples/full-stack-with-auth/README.md +0 -16
  173. jac_client/examples/full-stack-with-auth/package.json +0 -28
  174. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  175. jac_client/examples/little-x/package.json +0 -23
  176. jac_client/examples/little-x/submit-button.jac +0 -8
  177. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  178. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  179. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  180. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  181. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  182. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  183. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  184. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  185. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  186. jac_client/examples/with-router/.babelrc +0 -9
  187. jac_client/examples/with-router/README.md +0 -17
  188. jac_client/examples/with-router/package.json +0 -28
  189. jac_client/examples/with-router/vite.config.js +0 -27
  190. jac_client/plugin/cli.py +0 -244
  191. jac_client/plugin/client.py +0 -152
  192. jac_client/plugin/client_runtime.jac +0 -234
  193. jac_client/plugin/vite_client_bundle.py +0 -503
  194. jac_client/tests/fixtures/js_import/utils.js +0 -21
  195. jac_client/tests/fixtures/package-lock.json +0 -329
  196. jac_client/tests/fixtures/package.json +0 -11
  197. jac_client/tests/test_asset_examples.py +0 -322
  198. jac_client/tests/test_cl.py +0 -530
  199. jac_client/tests/test_create_jac_app.py +0 -131
  200. jac_client/tests/test_nested_file.py +0 -374
  201. jac_client-0.2.2.dist-info/RECORD +0 -171
  202. jac_client-0.2.2.dist-info/entry_points.txt +0 -4
@@ -0,0 +1,513 @@
1
+ """Create package.json from config.json during bundling."""
2
+
3
+ impl ViteBundler.create_package_json(
4
+ self: ViteBundler, project_name: Optional[str] = None
5
+ ) -> Path {
6
+ build_dir = self.project_dir / '.client-build';
7
+ build_dir.mkdir(exist_ok=True);
8
+ configs_dir = build_dir / '.jac-client.configs';
9
+ configs_dir.mkdir(exist_ok=True);
10
+ package_config = self.config_loader.get_package_config();
11
+ package_json_path = configs_dir / 'package.json';
12
+ name = package_config.get('name', '');
13
+ if (not name and project_name) {
14
+ name = project_name;
15
+ }
16
+ if not name {
17
+ existing_package = self.project_dir / 'package.json';
18
+ if existing_package.exists() {
19
+ try {
20
+ with existing_package.open() as f {
21
+ existing_data = json.load(f);
22
+ name = existing_data.get('name', '');
23
+ }
24
+ } except (json.JSONDecodeError, KeyError) {
25
+ ;
26
+ }
27
+ }
28
+ }
29
+ if not name {
30
+ name = self.project_dir.name or 'jac-app';
31
+ }
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};
54
+ 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',
58
+ 'compile': 'babel compiled --out-dir build --extensions ".jsx,.js" --out-file-extension .js'
59
+ };
60
+ user_scripts = package_config.get('scripts', {});
61
+ scripts.update(user_scripts);
62
+ babel_config = {
63
+ 'presets': [['@babel/preset-env', {'modules': False}], '@babel/preset-react']
64
+ };
65
+ package_data = {
66
+ 'name': name,
67
+ 'version': package_config.get('version', '1.0.0'),
68
+ 'description': package_config.get('description', f"Jac application: {name}"),
69
+ 'type': 'module',
70
+ 'main': 'index.js',
71
+ 'scripts': scripts,
72
+ 'dependencies': dependencies,
73
+ 'devDependencies': dev_dependencies,
74
+ 'babel': babel_config
75
+ };
76
+ for (key, value) in package_config.items() {
77
+ if (
78
+ key not in {'name','version','description','dependencies','devDependencies'}
79
+ ) {
80
+ package_data[key] = value;
81
+ }
82
+ }
83
+ with package_json_path.open('w', encoding='utf-8') as f {
84
+ json.dump(package_data, f, indent=2);
85
+ }
86
+ # Generate tsconfig.json during build time
87
+ self.create_tsconfig();
88
+ return package_json_path;
89
+ }
90
+
91
+ """Create tsconfig.json during build time, merging user config from jac.toml."""
92
+ 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';
96
+ configs_dir.mkdir(exist_ok=True);
97
+ tsconfig_path = configs_dir / 'tsconfig.json';
98
+ # Default tsconfig settings
99
+ default_compiler_options = {
100
+ 'target': 'ES2020',
101
+ 'useDefineForClassFields': True,
102
+ 'lib': ['ES2020', 'DOM', 'DOM.Iterable'],
103
+ 'module': 'ESNext',
104
+ 'skipLibCheck': True,
105
+ 'moduleResolution': 'bundler',
106
+ 'allowImportingTsExtensions': True,
107
+ 'resolveJsonModule': True,
108
+ 'isolatedModules': True,
109
+ 'noEmit': True,
110
+ 'jsx': 'react-jsx',
111
+ 'strict': True,
112
+ 'noUnusedLocals': True,
113
+ 'noUnusedParameters': True,
114
+ 'noFallthroughCasesInSwitch': True
115
+ };
116
+ default_include = ['components/**/*'];
117
+ default_exclude = ['.client-build'];
118
+ # Get user config from [plugins.client.ts] in jac.toml
119
+ user_ts_config = self.config_loader.get_ts_config();
120
+ user_compiler_options = user_ts_config.get('compilerOptions', {});
121
+ user_include = user_ts_config.get('include', []);
122
+ user_exclude = user_ts_config.get('exclude', []);
123
+ # Merge compiler options (user overrides defaults)
124
+ merged_compiler_options = {** default_compiler_options, ** user_compiler_options};
125
+ # For include/exclude, use user values if provided, otherwise defaults
126
+ merged_include = user_include or default_include;
127
+ merged_exclude = user_exclude or default_exclude;
128
+ tsconfig_data = {
129
+ 'compilerOptions': merged_compiler_options,
130
+ 'include': merged_include,
131
+ 'exclude': merged_exclude
132
+ };
133
+ # Write the config to .client-build/.jac-client.configs/tsconfig.json (no root config file)
134
+ tsconfig_path.write_text(json.dumps(tsconfig_data, indent=2), encoding='utf-8');
135
+ return tsconfig_path;
136
+ }
137
+
138
+ """
139
+ Clean up root package.json and move package-lock.json to .jac-client.configs/.
140
+
141
+ Remove root package.json and move package-lock.json to .jac-client.configs/.
142
+ """
143
+ impl ViteBundler._cleanup_root_package_files(self: ViteBundler) -> None {
144
+ root_package_json = self.project_dir / 'package.json';
145
+ 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';
148
+ configs_package_lock = configs_dir / 'package-lock.json';
149
+ if root_package_lock.exists() {
150
+ configs_dir.mkdir(exist_ok=True);
151
+ if configs_package_lock.exists() {
152
+ configs_package_lock.unlink();
153
+ }
154
+ shutil.move(str(root_package_lock), str(configs_package_lock));
155
+ }
156
+ if root_package_json.exists() {
157
+ root_package_json.unlink();
158
+ }
159
+ }
160
+
161
+ """
162
+ Ensure root package.json exists temporarily for npm commands.
163
+
164
+ Create root package.json temporarily if it doesn't exist.
165
+ """
166
+ impl ViteBundler._ensure_root_package_json(self: ViteBundler) -> None {
167
+ generated_package_json = self.project_dir / '.client-build' / '.jac-client.configs' / 'package.json';
168
+ root_package_json = self.project_dir / 'package.json';
169
+ if not generated_package_json.exists() {
170
+ self.create_package_json();
171
+ }
172
+ if not root_package_json.exists() {
173
+ shutil.copy2(generated_package_json, root_package_json);
174
+ }
175
+ }
176
+
177
+ """Format config object as JavaScript object string."""
178
+ impl ViteBundler._format_config_object(
179
+ self: ViteBundler, config: dict, indent: int = 0
180
+ ) -> str {
181
+ if not config {
182
+ return '';
183
+ }
184
+ indent_str = ' ' * indent;
185
+ items = [];
186
+ for (key, value) in config.items() {
187
+ if isinstance(value, str) {
188
+ items.append(f"{indent_str} {key}: '{value}',");
189
+ } elif isinstance(value, bool) {
190
+ items.append(f"{indent_str} {key}: {str(value).lower()},");
191
+ } elif isinstance(value, (int, float)) {
192
+ items.append(f"{indent_str} {key}: {value},");
193
+ } elif isinstance(value, <>list) {
194
+ list_str = ', '.join(repr(v) for v in value);
195
+ items.append(f"{indent_str} {key}: [{list_str}],");
196
+ } elif isinstance(value, <>dict) {
197
+ nested = self._format_config_object(value, (indent + 2));
198
+ items.append(f"{indent_str} {key}: {{{nested}{indent_str} }},");
199
+ } else {
200
+ items.append(f"{indent_str} {key}: {repr(value)},");
201
+ }
202
+ }
203
+ return '\n'.join(items);
204
+ }
205
+
206
+ """Format plugin options as JavaScript object string."""
207
+ impl ViteBundler._format_plugin_options(self: ViteBundler, options: dict) -> str {
208
+ if not options {
209
+ return '';
210
+ }
211
+ items = [];
212
+ for (key, value) in options.items() {
213
+ if isinstance(value, str) {
214
+ items.append(f"{key}: '{value}'");
215
+ } elif isinstance(value, bool) {
216
+ items.append(f"{key}: {str(value).lower()}");
217
+ } elif isinstance(value, (int, float)) {
218
+ items.append(f"{key}: {value}");
219
+ } elif isinstance(value, <>list) {
220
+ items.append(f"{key}: [{', '.join(repr(v) for v in value)}]");
221
+ } else {
222
+ items.append(f"{key}: {repr(value)}");
223
+ }
224
+ }
225
+ return '{ ' + ', '.join(items) + ' }';
226
+ }
227
+
228
+ """Get a valid JavaScript variable name from plugin module name."""
229
+ impl ViteBundler._get_plugin_var_name(self: ViteBundler, plugin_name: str) -> str {
230
+ name = plugin_name.split('/')[-1];
231
+ name = name.replace('-', '_').replace('.', '_');
232
+ name = name.lstrip('@');
233
+ return name;
234
+ }
235
+
236
+ """Create vite.config.js from config.json during bundling."""
237
+ 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';
241
+ configs_dir.mkdir(exist_ok=True);
242
+ vite_config_data = self.config_loader.get_vite_config();
243
+ config_path = configs_dir / 'vite.config.js';
244
+ # TypeScript is always enabled by default
245
+ build_dir = self.project_dir / '.client-build';
246
+ try {
247
+ # Entry file path relative to .client-build/ (not project root)
248
+ entry_relative = entry_file.relative_to(build_dir).as_posix();
249
+ } 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
+ }
263
+ }
264
+ try {
265
+ # Output dir path relative to .client-build/ (not project root)
266
+ output_relative = self.output_dir.relative_to(build_dir).as_posix();
267
+ } 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
+ }
281
+ }
282
+ # Calculate compiled directory path for aliases (relative to .client-build/)
283
+ if entry_relative.endswith('/build/main.js') {
284
+ compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
285
+ compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
286
+ } elif entry_relative.endswith('build/main.js') {
287
+ compiled_utils_relative = 'compiled/client_runtime.js';
288
+ compiled_assets_relative = 'compiled/assets';
289
+ } else {
290
+ compiled_utils_relative = 'compiled/client_runtime.js';
291
+ compiled_assets_relative = 'compiled/assets';
292
+ }
293
+ plugins = [];
294
+ plugin_imports = [];
295
+ # TypeScript support is always enabled, so always include React plugin
296
+ plugin_imports.append('import react from "@vitejs/plugin-react";');
297
+ plugins.append(' react()');
298
+ lib_imports = vite_config_data.get('lib_imports', []);
299
+ for lib_import in lib_imports {
300
+ if (isinstance(lib_import, str) and lib_import.strip()) {
301
+ plugin_imports.append(lib_import);
302
+ }
303
+ }
304
+ custom_plugins = vite_config_data.get('plugins', []);
305
+ for plugin in custom_plugins {
306
+ if isinstance(plugin, str) {
307
+ plugins.append(f" {plugin}");
308
+ }
309
+ }
310
+ plugins_str = ',\n'.join(plugins) if plugins else '';
311
+ imports_str = '\n'.join(plugin_imports) if plugin_imports else '';
312
+ # TypeScript extensions are always included
313
+ extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'];
314
+ extensions_str = ', '.join(f'"{ext}"' for ext in extensions);
315
+ build_config = vite_config_data.get('build', {});
316
+ build_overrides_str = self._format_config_object(build_config, indent=4)
317
+ if (isinstance(build_config, <>dict) and build_config)
318
+ else '';
319
+ server_config = vite_config_data.get('server', {});
320
+ server_config_str = self._format_config_object(server_config, indent=2)
321
+ if (isinstance(server_config, <>dict) and server_config)
322
+ else '';
323
+ resolve_config = vite_config_data.get('resolve', {});
324
+ resolve_overrides_str = self._format_config_object(resolve_config, indent=6)
325
+ if (isinstance(resolve_config, <>dict) and resolve_config)
326
+ else '';
327
+ imports_section = f"{imports_str}\n" if imports_str else '';
328
+ newline = '\n';
329
+ server_section = f" server: {{{newline}{server_config_str}{newline} }},{newline}"
330
+ if server_config_str
331
+ else '';
332
+ config_content = f'''import {{ defineConfig }} from "vite";
333
+ import path from "path";
334
+ import {{ fileURLToPath }} from "url";
335
+ {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
337
+ const buildDir = path.resolve(__dirname, "..");
338
+ const projectRoot = path.resolve(__dirname, "../..");
339
+
340
+ /**
341
+ * Vite configuration generated from config.json (in project root)
342
+ * To customize, edit config.json instead of this file.
343
+ */
344
+
345
+ export default defineConfig({{
346
+ plugins: [{(newline + plugins_str + newline + ' ') if plugins_str else ''}],
347
+ root: buildDir, // base folder (.client-build/) so vite can find node_modules
348
+ build: {{
349
+ rollupOptions: {{
350
+ input: path.resolve(buildDir, "{entry_relative}"), // your compiled entry file
351
+ output: {{
352
+ entryFileNames: "client.[hash].js", // name of the final js file
353
+ assetFileNames: "[name].[ext]",
354
+ }},
355
+ }},
356
+ outDir: path.resolve(buildDir, "{output_relative}"), // final bundled output
357
+ emptyOutDir: true,
358
+ {build_overrides_str}
359
+ }},
360
+ publicDir: false,
361
+ {server_section} resolve: {{
362
+ alias: {{
363
+ "@jac-client/utils": path.resolve(buildDir, "{compiled_utils_relative}"),
364
+ "@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
365
+ }},
366
+ extensions: [{extensions_str}],
367
+ {resolve_overrides_str}
368
+ }},
369
+ }});
370
+ ''';
371
+ config_path.write_text(config_content, encoding='utf-8');
372
+ return config_path;
373
+ }
374
+
375
+ """Check if the project has TypeScript support. TypeScript is now enabled by default."""
376
+ impl ViteBundler._has_typescript_support(self: ViteBundler) -> bool {
377
+ # TypeScript is always enabled by default
378
+ return True;
379
+ }
380
+
381
+ """Read the bundled code and compute its hash."""
382
+ impl ViteBundler.read_bundle(self: ViteBundler) -> tuple[str, str] {
383
+ bundle_file = self.find_bundle();
384
+ if not bundle_file {
385
+ raise ClientBundleError('Vite build completed but no bundle file found') ;
386
+ }
387
+ bundle_code = bundle_file.read_text(encoding='utf-8');
388
+ bundle_hash = hashlib.sha256(bundle_code.encode('utf-8')).hexdigest();
389
+ return (bundle_code, bundle_hash);
390
+ }
391
+
392
+ """Find the generated Vite CSS file."""
393
+ 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;
402
+ }
403
+
404
+ """Find the generated Vite bundle file."""
405
+ impl ViteBundler.find_bundle(self: ViteBundler) -> Optional[Path] {
406
+ for file in self.output_dir.glob('client.*.js') {
407
+ return file;
408
+ }
409
+ return None;
410
+ }
411
+
412
+ """Run Vite build with generated config in .client-build/.jac-client.configs/."""
413
+ impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) -> None {
414
+ self.output_dir.mkdir(parents=True, exist_ok=True);
415
+ generated_package_json = self.project_dir / '.client-build' / '.jac-client.configs' / 'package.json';
416
+ if not generated_package_json.exists() {
417
+ self.create_package_json();
418
+ } else {
419
+ # Ensure tsconfig.json exists even if package.json already exists
420
+ self.create_tsconfig();
421
+ }
422
+ try {
423
+ build_dir = self.project_dir / '.client-build';
424
+ node_modules = build_dir / 'node_modules';
425
+ if not node_modules.exists() {
426
+ # Temporarily copy package.json to .client-build/ for npm install
427
+ build_package_json = build_dir / 'package.json';
428
+ configs_package_json = build_dir / '.jac-client.configs' / 'package.json';
429
+ if configs_package_json.exists() and not build_package_json.exists() {
430
+ import shutil;
431
+ shutil.copy2(configs_package_json, build_package_json);
432
+ }
433
+ try {
434
+ # Install to .client-build/node_modules
435
+ subprocess.run(
436
+ ['npm', 'install'],
437
+ cwd=build_dir,
438
+ check=True,
439
+ capture_output=True,
440
+ text=True
441
+ );
442
+ } except subprocess.CalledProcessError as e {
443
+ raise e from ClientBundleError(
444
+ f"Failed to install npm dependencies: {e.stderr}"
445
+ ) ;
446
+ } except FileNotFoundError {
447
+ raise None from ClientBundleError(
448
+ 'npm command not found. Ensure Node.js and npm are installed.'
449
+ ) ;
450
+ }
451
+ }
452
+ build_dir = self.project_dir / '.client-build';
453
+ if self.config_path {
454
+ # 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
458
+ config_rel = self.config_path.relative_to(build_dir);
459
+ command = ['npx', 'vite', 'build', '--config', str(config_rel)];
460
+ } else {
461
+ # Config is outside .client-build/, use absolute path
462
+ command = ['npx', 'vite', 'build', '--config', str(self.config_path)];
463
+ }
464
+ } elif entry_file {
465
+ generated_config = self.create_vite_config(entry_file);
466
+ # Config is in .client-build/.jac-client.configs/, make it relative to build_dir
467
+ config_rel = generated_config.relative_to(build_dir);
468
+ command = ['npx', 'vite', 'build', '--config', str(config_rel)];
469
+ } else {
470
+ command = ['npm', 'run', 'build'];
471
+ }
472
+ # Run vite from .client-build/ directory so it can find node_modules
473
+ result = subprocess.run(
474
+ command, cwd=build_dir, check=False, capture_output=True, text=True
475
+ );
476
+ if result.returncode != 0 {
477
+ error_msg = result.stderr or result.stdout or 'Unknown error';
478
+ raise ClientBundleError(
479
+ f"Vite build failed:\n{error_msg}\nCommand: {' '.join(command)}"
480
+ ) from None ;
481
+ }
482
+ } finally {
483
+ # Clean up temporary package.json in .client-build/
484
+ build_package_json = build_dir / 'package.json';
485
+ if build_package_json.exists() {
486
+ build_package_json.unlink();
487
+ }
488
+ # Move package-lock.json to .jac-client.configs/ if it exists
489
+ build_package_lock = build_dir / 'package-lock.json';
490
+ if build_package_lock.exists() {
491
+ configs_package_lock = build_dir / '.jac-client.configs' / 'package-lock.json';
492
+ if configs_package_lock.exists() {
493
+ configs_package_lock.unlink();
494
+ }
495
+ build_package_lock.rename(configs_package_lock);
496
+ }
497
+ }
498
+ }
499
+
500
+ """Initialize the Vite bundler."""
501
+ impl ViteBundler.init(
502
+ self: ViteBundler,
503
+ project_dir: Path,
504
+ output_dir: Optional[Path] = None,
505
+ minify: bool = False,
506
+ config_path: Optional[Path] = None
507
+ ) {
508
+ self.project_dir = project_dir;
509
+ self.output_dir = output_dir or (project_dir / '.client-build' / 'dist');
510
+ self.minify = minify;
511
+ self.config_path = config_path;
512
+ self.config_loader = JacClientConfig(project_dir);
513
+ }
@@ -0,0 +1,19 @@
1
+ """Import processing for Vite bundling."""
2
+ import from pathlib { Path }
3
+ import from typing { TYPE_CHECKING }
4
+ import from jaclang.runtimelib.client_bundle { ClientBundleError }
5
+
6
+ with entry {
7
+ if TYPE_CHECKING {
8
+ import from jaclang.compiler.codeinfo { ClientManifest }
9
+ }
10
+ }
11
+
12
+ """Processes client imports for Vite bundling."""
13
+ class ImportProcessor {
14
+ def process_vite_imports(
15
+ self: ImportProcessor, manifest: (ClientManifest | None), module_path: Path
16
+ ) -> list[(Path | None)];
17
+
18
+ def should_process_import(self: ImportProcessor, import_path: Path) -> bool;
19
+ }
@@ -0,0 +1,35 @@
1
+ """Jac to JavaScript compilation module."""
2
+ import from collections.abc { Callable }
3
+ import from pathlib { Path }
4
+ import from types { ModuleType }
5
+ import from typing { TYPE_CHECKING, Any }
6
+
7
+ with entry {
8
+ if TYPE_CHECKING {
9
+ import from jaclang.compiler.codeinfo { ClientManifest }
10
+ }
11
+ }
12
+
13
+ """Handles compilation of Jac files to JavaScript."""
14
+ class JacToJSCompiler {
15
+ def init(
16
+ self: JacToJSCompiler,
17
+ compile_to_js_func: Callable[([Path], tuple[(str, (ModuleType | None))])],
18
+ extract_exports_func: Callable[([Any], list[str])],
19
+ extract_globals_func: Callable[([Any, ModuleType], dict[(str, Any)])]
20
+ );
21
+
22
+ def compile_module(
23
+ self: JacToJSCompiler, module_path: Path
24
+ ) -> tuple[str, (ModuleType | None), (ClientManifest | None)];
25
+
26
+ def extract_exports(
27
+ self: JacToJSCompiler, manifest: (ClientManifest | None)
28
+ ) -> list[str];
29
+
30
+ def extract_globals(
31
+ self: JacToJSCompiler, manifest: (ClientManifest | None), module: ModuleType
32
+ ) -> dict[str, Any];
33
+
34
+ def add_runtime_imports(self: JacToJSCompiler, js_code: str) -> str;
35
+ }
@@ -0,0 +1,26 @@
1
+ """Package installer for managing npm dependencies in jac.toml."""
2
+
3
+ import shutil;
4
+ import subprocess;
5
+ import from pathlib { Path }
6
+ import from jaclang.runtimelib.client_bundle { ClientBundleError }
7
+ import from .config_loader { JacClientConfig }
8
+ import from .vite_bundler { ViteBundler }
9
+
10
+ class PackageInstaller {
11
+ def init(self: PackageInstaller, project_dir: Path);
12
+ def install_package(
13
+ self: PackageInstaller,
14
+ package_name: str,
15
+ version: (str | None) = None,
16
+ is_dev: bool = False
17
+ ) -> None;
18
+
19
+ def install_all(self: PackageInstaller) -> None;
20
+ def _regenerate_and_install(self: PackageInstaller) -> None;
21
+ def uninstall_package(
22
+ self: PackageInstaller, package_name: str, is_dev: bool = False
23
+ ) -> None;
24
+
25
+ def list_packages(self: PackageInstaller) -> dict[str, dict[str, str]];
26
+ }
@@ -0,0 +1,36 @@
1
+ """Vite bundling module."""
2
+ import hashlib;
3
+ import json;
4
+ import shutil;
5
+ import subprocess;
6
+ import from pathlib { Path }
7
+ import from typing { Optional }
8
+ import from jaclang.runtimelib.client_bundle { ClientBundleError }
9
+ import from .config_loader { JacClientConfig }
10
+ """Handles Vite bundling operations."""
11
+ class ViteBundler {
12
+ def init(
13
+ self: ViteBundler,
14
+ project_dir: Path,
15
+ output_dir: Optional[Path] = None,
16
+ minify: bool = False,
17
+ config_path: Optional[Path] = None
18
+ );
19
+
20
+ def build(self: ViteBundler, entry_file: Optional[Path] = None) -> None;
21
+ def find_bundle(self: ViteBundler) -> Optional[Path];
22
+ def find_css(self: ViteBundler) -> Optional[Path];
23
+ def read_bundle(self: ViteBundler) -> tuple[str, str];
24
+ def _has_typescript_support(self: ViteBundler) -> bool;
25
+ def create_vite_config(self: ViteBundler, entry_file: Path) -> Path;
26
+ def _get_plugin_var_name(self: ViteBundler, plugin_name: str) -> str;
27
+ def _format_plugin_options(self: ViteBundler, options: dict) -> str;
28
+ def _format_config_object(self: ViteBundler, config: dict, indent: int = 0) -> str;
29
+ def _ensure_root_package_json(self: ViteBundler) -> None;
30
+ def _cleanup_root_package_files(self: ViteBundler) -> None;
31
+ def create_package_json(
32
+ self: ViteBundler, project_name: Optional[str] = None
33
+ ) -> Path;
34
+
35
+ def create_tsconfig(self: ViteBundler) -> Path;
36
+ }
@@ -0,0 +1,31 @@
1
+ """Vite-enhanced client bundle generation for Jac web front-ends."""
2
+ import contextlib;
3
+ import shutil;
4
+ import from collections.abc { Callable }
5
+ import from pathlib { Path }
6
+ import from types { ModuleType }
7
+ import from typing { cast }
8
+ import from jaclang.runtimelib.client_bundle {
9
+ ClientBundle,
10
+ ClientBundleBuilder,
11
+ ClientBundleError
12
+ }
13
+ import from .src { ViteCompiler }
14
+
15
+ """Enhanced ClientBundleBuilder that uses Vite for optimized bundling."""
16
+ class ViteClientBundleBuilder(ClientBundleBuilder) {
17
+ def init(
18
+ self: ViteClientBundleBuilder,
19
+ runtime_path: (Path | None) = None,
20
+ vite_output_dir: (Path | None) = None,
21
+ vite_package_json: (Path | None) = None,
22
+ vite_minify: bool = False
23
+ ) -> None;
24
+
25
+ def _get_compiler(self: ViteClientBundleBuilder) -> ViteCompiler;
26
+ def _compile_bundle(
27
+ self: ViteClientBundleBuilder, module: ModuleType, module_path: Path
28
+ ) -> ClientBundle;
29
+
30
+ def cleanup_temp_dir(self: ViteClientBundleBuilder) -> None;
31
+ }