jac-client 0.2.3__py3-none-any.whl → 0.2.8__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.
- jac_client/examples/all-in-one/app.jac +494 -347
- jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
- jac_client/examples/all-in-one/button.jac +1 -1
- jac_client/examples/all-in-one/components/CategoryFilter.jac +35 -0
- jac_client/examples/all-in-one/components/Header.jac +13 -0
- jac_client/examples/all-in-one/components/ProfitOverview.jac +50 -0
- jac_client/examples/all-in-one/components/Summary.jac +53 -0
- jac_client/examples/all-in-one/components/TransactionForm.jac +158 -0
- jac_client/examples/all-in-one/components/TransactionItem.jac +55 -0
- jac_client/examples/all-in-one/components/TransactionList.jac +37 -0
- jac_client/examples/all-in-one/components/button.jac +1 -1
- jac_client/examples/all-in-one/components/navigation.jac +132 -0
- jac_client/examples/all-in-one/constants/categories.jac +37 -0
- jac_client/examples/all-in-one/constants/clients.jac +13 -0
- jac_client/examples/all-in-one/context/BudgetContext.jac +28 -0
- jac_client/examples/all-in-one/hooks/useBudget.jac +116 -0
- jac_client/examples/all-in-one/hooks/useLocalStorage.jac +36 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +70 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +126 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +552 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +126 -0
- jac_client/examples/all-in-one/pages/LandingPage.jac +101 -0
- jac_client/examples/all-in-one/pages/loginPage.jac +132 -0
- jac_client/examples/all-in-one/pages/nestedDemo.jac +61 -0
- jac_client/examples/all-in-one/pages/notFound.jac +24 -0
- jac_client/examples/all-in-one/pages/signupPage.jac +133 -0
- jac_client/examples/all-in-one/utils/formatters.jac +52 -0
- jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +4 -4
- jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +4 -4
- jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +5 -5
- jac_client/examples/basic/{app.jac → src/app.jac} +4 -4
- jac_client/examples/basic-auth/src/app.jac +371 -0
- jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +28 -28
- jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +166 -127
- jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +7 -7
- jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +7 -7
- jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +7 -7
- jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +6 -6
- jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +7 -7
- jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +47 -47
- jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
- jac_client/examples/little-x/src/submit-button.jac +16 -0
- jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
- jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
- jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
- jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
- jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
- jac_client/examples/ts-support/src/app.jac +35 -0
- jac_client/examples/with-router/{app.jac → src/app.jac} +15 -15
- jac_client/plugin/cli.jac +504 -0
- jac_client/plugin/client.jac +45 -0
- jac_client/plugin/client_runtime.cl.jac +42 -0
- jac_client/plugin/impl/client.impl.jac +193 -0
- jac_client/plugin/impl/client_runtime.impl.jac +195 -0
- jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
- jac_client/plugin/plugin_config.jac +195 -0
- jac_client/plugin/src/__init__.jac +20 -0
- jac_client/plugin/src/asset_processor.jac +33 -0
- jac_client/plugin/src/babel_processor.jac +18 -0
- jac_client/plugin/src/compiler.jac +67 -0
- jac_client/plugin/src/config_loader.jac +32 -0
- jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
- jac_client/plugin/src/impl/babel_processor.impl.jac +89 -0
- jac_client/plugin/src/impl/compiler.impl.jac +288 -0
- jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
- jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
- jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +626 -0
- jac_client/plugin/src/import_processor.jac +19 -0
- jac_client/plugin/src/jac_to_js.jac +35 -0
- jac_client/plugin/src/package_installer.jac +26 -0
- jac_client/plugin/src/vite_bundler.jac +44 -0
- jac_client/plugin/vite_client_bundle.jac +31 -0
- jac_client/tests/conftest.py +283 -0
- jac_client/tests/fixtures/basic-app/app.jac +2 -2
- jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
- jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
- jac_client/tests/fixtures/js_import/app.jac +5 -5
- jac_client/tests/fixtures/spawn_test/app.jac +15 -18
- jac_client/tests/fixtures/with-ts/app.jac +35 -0
- jac_client/tests/test_cli.py +811 -0
- jac_client/tests/test_it.py +592 -97
- {jac_client-0.2.3.dist-info → jac_client-0.2.8.dist-info}/METADATA +41 -34
- jac_client-0.2.8.dist-info/RECORD +97 -0
- {jac_client-0.2.3.dist-info → jac_client-0.2.8.dist-info}/WHEEL +2 -1
- jac_client-0.2.8.dist-info/entry_points.txt +4 -0
- jac_client-0.2.8.dist-info/top_level.txt +1 -0
- jac_client/docs/README.md +0 -689
- jac_client/docs/advanced-state.md +0 -1265
- jac_client/docs/asset-serving/intro.md +0 -209
- jac_client/docs/assets/pipe_line-v2.svg +0 -32
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/file-system/app.jac.md +0 -121
- jac_client/docs/file-system/backend-frontend.md +0 -217
- jac_client/docs/file-system/intro.md +0 -72
- jac_client/docs/file-system/nested-imports.md +0 -348
- jac_client/docs/guide-example/intro.md +0 -115
- jac_client/docs/guide-example/step-01-setup.md +0 -270
- jac_client/docs/guide-example/step-02-components.md +0 -416
- jac_client/docs/guide-example/step-03-styling.md +0 -478
- jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
- jac_client/docs/guide-example/step-05-local-state.md +0 -530
- jac_client/docs/guide-example/step-06-events.md +0 -749
- jac_client/docs/guide-example/step-07-effects.md +0 -468
- jac_client/docs/guide-example/step-08-walkers.md +0 -534
- jac_client/docs/guide-example/step-09-authentication.md +0 -586
- jac_client/docs/guide-example/step-10-routing.md +0 -539
- jac_client/docs/guide-example/step-11-final.md +0 -963
- jac_client/docs/imports.md +0 -1141
- jac_client/docs/lifecycle-hooks.md +0 -773
- jac_client/docs/routing.md +0 -659
- jac_client/docs/styling/intro.md +0 -249
- jac_client/docs/styling/js-styling.md +0 -367
- jac_client/docs/styling/material-ui.md +0 -341
- jac_client/docs/styling/pure-css.md +0 -299
- jac_client/docs/styling/sass.md +0 -403
- jac_client/docs/styling/styled-components.md +0 -395
- jac_client/docs/styling/tailwind.md +0 -298
- jac_client/examples/all-in-one/.babelrc +0 -9
- jac_client/examples/all-in-one/README.md +0 -16
- jac_client/examples/all-in-one/assets/burger.png +0 -0
- jac_client/examples/all-in-one/package.json +0 -29
- jac_client/examples/all-in-one/styles.css +0 -26
- jac_client/examples/all-in-one/vite.config.js +0 -28
- jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
- jac_client/examples/asset-serving/css-with-image/README.md +0 -91
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +0 -28
- jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
- jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
- jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
- jac_client/examples/asset-serving/image-asset/README.md +0 -119
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +0 -28
- jac_client/examples/asset-serving/image-asset/styles.css +0 -26
- jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
- jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
- jac_client/examples/asset-serving/import-alias/README.md +0 -83
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +0 -28
- jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
- jac_client/examples/basic/.babelrc +0 -9
- jac_client/examples/basic/README.md +0 -16
- jac_client/examples/basic/package.json +0 -27
- jac_client/examples/basic/vite.config.js +0 -27
- jac_client/examples/basic-auth/.babelrc +0 -9
- jac_client/examples/basic-auth/README.md +0 -16
- jac_client/examples/basic-auth/app.jac +0 -308
- jac_client/examples/basic-auth/package.json +0 -27
- jac_client/examples/basic-auth/vite.config.js +0 -27
- jac_client/examples/basic-auth-with-router/.babelrc +0 -9
- jac_client/examples/basic-auth-with-router/README.md +0 -60
- jac_client/examples/basic-auth-with-router/package.json +0 -28
- jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
- jac_client/examples/basic-full-stack/.babelrc +0 -9
- jac_client/examples/basic-full-stack/README.md +0 -18
- jac_client/examples/basic-full-stack/package.json +0 -28
- jac_client/examples/basic-full-stack/vite.config.js +0 -27
- jac_client/examples/css-styling/js-styling/.babelrc +0 -9
- jac_client/examples/css-styling/js-styling/README.md +0 -183
- jac_client/examples/css-styling/js-styling/package.json +0 -28
- jac_client/examples/css-styling/js-styling/styles.js +0 -100
- jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
- jac_client/examples/css-styling/material-ui/.babelrc +0 -9
- jac_client/examples/css-styling/material-ui/README.md +0 -16
- jac_client/examples/css-styling/material-ui/package.json +0 -32
- jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
- jac_client/examples/css-styling/pure-css/.babelrc +0 -9
- jac_client/examples/css-styling/pure-css/README.md +0 -16
- jac_client/examples/css-styling/pure-css/package.json +0 -28
- jac_client/examples/css-styling/pure-css/styles.css +0 -111
- jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
- jac_client/examples/css-styling/sass-example/.babelrc +0 -9
- jac_client/examples/css-styling/sass-example/README.md +0 -16
- jac_client/examples/css-styling/sass-example/package.json +0 -29
- jac_client/examples/css-styling/sass-example/styles.scss +0 -153
- jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
- jac_client/examples/css-styling/styled-components/.babelrc +0 -9
- jac_client/examples/css-styling/styled-components/README.md +0 -16
- jac_client/examples/css-styling/styled-components/package.json +0 -29
- jac_client/examples/css-styling/styled-components/styled.js +0 -90
- jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
- jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
- jac_client/examples/css-styling/tailwind-example/README.md +0 -16
- jac_client/examples/css-styling/tailwind-example/global.css +0 -1
- jac_client/examples/css-styling/tailwind-example/package.json +0 -30
- jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
- jac_client/examples/full-stack-with-auth/.babelrc +0 -9
- jac_client/examples/full-stack-with-auth/README.md +0 -16
- jac_client/examples/full-stack-with-auth/package.json +0 -28
- jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
- jac_client/examples/little-x/package.json +0 -23
- jac_client/examples/little-x/submit-button.jac +0 -8
- jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-advance/README.md +0 -77
- jac_client/examples/nested-folders/nested-advance/package.json +0 -29
- jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
- jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-basic/README.md +0 -183
- jac_client/examples/nested-folders/nested-basic/app.js +0 -7
- jac_client/examples/nested-folders/nested-basic/package.json +0 -28
- jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
- jac_client/examples/with-router/.babelrc +0 -9
- jac_client/examples/with-router/README.md +0 -17
- jac_client/examples/with-router/package.json +0 -28
- jac_client/examples/with-router/vite.config.js +0 -27
- jac_client/plugin/cli.py +0 -244
- jac_client/plugin/client.py +0 -152
- jac_client/plugin/client_runtime.jac +0 -234
- jac_client/plugin/vite_client_bundle.py +0 -503
- jac_client/tests/fixtures/js_import/utils.js +0 -21
- jac_client/tests/fixtures/package-lock.json +0 -329
- jac_client/tests/fixtures/package.json +0 -11
- jac_client/tests/test_asset_examples.py +0 -322
- jac_client/tests/test_cl.py +0 -530
- jac_client/tests/test_create_jac_app.py +0 -131
- jac_client/tests/test_nested_file.py +0 -374
- jac_client-0.2.3.dist-info/RECORD +0 -171
- jac_client-0.2.3.dist-info/entry_points.txt +0 -4
|
@@ -0,0 +1,626 @@
|
|
|
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
|
+
# Fallback to default
|
|
12
|
+
return self.project_dir / '.jac' / 'client';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
"""Create package.json from config.json during bundling."""
|
|
16
|
+
impl ViteBundler.create_package_json(
|
|
17
|
+
self: ViteBundler, project_name: Optional[str] = None
|
|
18
|
+
) -> Path {
|
|
19
|
+
build_dir = self._get_client_dir();
|
|
20
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
21
|
+
configs_dir = build_dir / 'configs';
|
|
22
|
+
configs_dir.mkdir(exist_ok=True);
|
|
23
|
+
package_config = self.config_loader.get_package_config();
|
|
24
|
+
package_json_path = configs_dir / 'package.json';
|
|
25
|
+
name = package_config.get('name', '');
|
|
26
|
+
if (not name and project_name) {
|
|
27
|
+
name = project_name;
|
|
28
|
+
}
|
|
29
|
+
if not name {
|
|
30
|
+
existing_package = self.project_dir / 'package.json';
|
|
31
|
+
if existing_package.exists() {
|
|
32
|
+
try {
|
|
33
|
+
with existing_package.open() as f {
|
|
34
|
+
existing_data = json.load(f);
|
|
35
|
+
name = existing_data.get('name', '');
|
|
36
|
+
}
|
|
37
|
+
} except (json.JSONDecodeError, KeyError) {
|
|
38
|
+
;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if not name {
|
|
43
|
+
name = self.project_dir.name or 'jac-app';
|
|
44
|
+
}
|
|
45
|
+
dependencies = package_config.get('dependencies', {});
|
|
46
|
+
dev_dependencies = package_config.get('devDependencies', {});
|
|
47
|
+
scripts = {
|
|
48
|
+
'build': 'npm run compile && vite build --config .jac/client/configs/vite.config.js',
|
|
49
|
+
'dev': 'vite dev --config .jac/client/configs/vite.config.js',
|
|
50
|
+
'preview': 'vite preview --config .jac/client/configs/vite.config.js',
|
|
51
|
+
'compile': 'babel compiled --out-dir build --extensions ".jsx,.js" --out-file-extension .js'
|
|
52
|
+
};
|
|
53
|
+
user_scripts = package_config.get('scripts', {});
|
|
54
|
+
scripts.update(user_scripts);
|
|
55
|
+
babel_config = {
|
|
56
|
+
'presets': [['@babel/preset-env', {'modules': False}], '@babel/preset-react']
|
|
57
|
+
};
|
|
58
|
+
package_data = {
|
|
59
|
+
'name': name,
|
|
60
|
+
'version': package_config.get('version', '1.0.0'),
|
|
61
|
+
'description': package_config.get('description', f"Jac application: {name}"),
|
|
62
|
+
'type': 'module',
|
|
63
|
+
'main': 'index.js',
|
|
64
|
+
'scripts': scripts,
|
|
65
|
+
'dependencies': dependencies,
|
|
66
|
+
'devDependencies': dev_dependencies,
|
|
67
|
+
'babel': babel_config
|
|
68
|
+
};
|
|
69
|
+
for (key, value) in package_config.items() {
|
|
70
|
+
if (
|
|
71
|
+
key not in {'name','version','description','dependencies','devDependencies'}
|
|
72
|
+
) {
|
|
73
|
+
package_data[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
with package_json_path.open('w', encoding='utf-8') as f {
|
|
77
|
+
json.dump(package_data, f, indent=2);
|
|
78
|
+
}
|
|
79
|
+
# Generate tsconfig.json during build time
|
|
80
|
+
self.create_tsconfig();
|
|
81
|
+
return package_json_path;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
"""Create tsconfig.json during build time, merging user config from jac.toml."""
|
|
85
|
+
impl ViteBundler.create_tsconfig(self: ViteBundler) -> Path {
|
|
86
|
+
build_dir = self._get_client_dir();
|
|
87
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
88
|
+
configs_dir = build_dir / 'configs';
|
|
89
|
+
configs_dir.mkdir(exist_ok=True);
|
|
90
|
+
tsconfig_path = configs_dir / 'tsconfig.json';
|
|
91
|
+
# Default tsconfig settings
|
|
92
|
+
default_compiler_options = {
|
|
93
|
+
'target': 'ES2020',
|
|
94
|
+
'useDefineForClassFields': True,
|
|
95
|
+
'lib': ['ES2020', 'DOM', 'DOM.Iterable'],
|
|
96
|
+
'module': 'ESNext',
|
|
97
|
+
'skipLibCheck': True,
|
|
98
|
+
'moduleResolution': 'bundler',
|
|
99
|
+
'allowImportingTsExtensions': True,
|
|
100
|
+
'resolveJsonModule': True,
|
|
101
|
+
'isolatedModules': True,
|
|
102
|
+
'noEmit': True,
|
|
103
|
+
'jsx': 'react-jsx',
|
|
104
|
+
'strict': True,
|
|
105
|
+
'noUnusedLocals': True,
|
|
106
|
+
'noUnusedParameters': True,
|
|
107
|
+
'noFallthroughCasesInSwitch': True
|
|
108
|
+
};
|
|
109
|
+
default_include = ['components/**/*'];
|
|
110
|
+
default_exclude = ['.jac'];
|
|
111
|
+
# Get user config from [plugins.client.ts] in jac.toml
|
|
112
|
+
user_ts_config = self.config_loader.get_ts_config();
|
|
113
|
+
user_compiler_options = user_ts_config.get('compilerOptions', {});
|
|
114
|
+
user_include = user_ts_config.get('include', []);
|
|
115
|
+
user_exclude = user_ts_config.get('exclude', []);
|
|
116
|
+
# Merge compiler options (user overrides defaults)
|
|
117
|
+
merged_compiler_options = {** default_compiler_options, ** user_compiler_options};
|
|
118
|
+
# For include/exclude, use user values if provided, otherwise defaults
|
|
119
|
+
merged_include = user_include or default_include;
|
|
120
|
+
merged_exclude = user_exclude or default_exclude;
|
|
121
|
+
tsconfig_data = {
|
|
122
|
+
'compilerOptions': merged_compiler_options,
|
|
123
|
+
'include': merged_include,
|
|
124
|
+
'exclude': merged_exclude
|
|
125
|
+
};
|
|
126
|
+
# Write the config to .jac/client/configs/tsconfig.json (no root config file)
|
|
127
|
+
tsconfig_path.write_text(json.dumps(tsconfig_data, indent=2), encoding='utf-8');
|
|
128
|
+
return tsconfig_path;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
Clean up root package.json and move package-lock.json to configs/.
|
|
133
|
+
|
|
134
|
+
Remove root package.json and move package-lock.json to configs/.
|
|
135
|
+
"""
|
|
136
|
+
impl ViteBundler._cleanup_root_package_files(self: ViteBundler) -> None {
|
|
137
|
+
root_package_json = self.project_dir / 'package.json';
|
|
138
|
+
root_package_lock = self.project_dir / 'package-lock.json';
|
|
139
|
+
build_dir = self._get_client_dir();
|
|
140
|
+
configs_dir = build_dir / 'configs';
|
|
141
|
+
configs_package_lock = configs_dir / 'package-lock.json';
|
|
142
|
+
if root_package_lock.exists() {
|
|
143
|
+
configs_dir.mkdir(exist_ok=True);
|
|
144
|
+
if configs_package_lock.exists() {
|
|
145
|
+
configs_package_lock.unlink();
|
|
146
|
+
}
|
|
147
|
+
shutil.move(str(root_package_lock), str(configs_package_lock));
|
|
148
|
+
}
|
|
149
|
+
if root_package_json.exists() {
|
|
150
|
+
root_package_json.unlink();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
Ensure root package.json exists temporarily for npm commands.
|
|
156
|
+
|
|
157
|
+
Create root package.json temporarily if it doesn't exist.
|
|
158
|
+
"""
|
|
159
|
+
impl ViteBundler._ensure_root_package_json(self: ViteBundler) -> None {
|
|
160
|
+
generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
|
|
161
|
+
root_package_json = self.project_dir / 'package.json';
|
|
162
|
+
if not generated_package_json.exists() {
|
|
163
|
+
self.create_package_json();
|
|
164
|
+
}
|
|
165
|
+
if not root_package_json.exists() {
|
|
166
|
+
shutil.copy2(generated_package_json, root_package_json);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
"""Format config object as JavaScript object string."""
|
|
171
|
+
impl ViteBundler._format_config_object(
|
|
172
|
+
self: ViteBundler, config: dict, indent: int = 0
|
|
173
|
+
) -> str {
|
|
174
|
+
if not config {
|
|
175
|
+
return '';
|
|
176
|
+
}
|
|
177
|
+
indent_str = ' ' * indent;
|
|
178
|
+
items = [];
|
|
179
|
+
for (key, value) in config.items() {
|
|
180
|
+
if isinstance(value, str) {
|
|
181
|
+
items.append(f"{indent_str} {key}: '{value}',");
|
|
182
|
+
} elif isinstance(value, bool) {
|
|
183
|
+
items.append(f"{indent_str} {key}: {str(value).lower()},");
|
|
184
|
+
} elif isinstance(value, (int, float)) {
|
|
185
|
+
items.append(f"{indent_str} {key}: {value},");
|
|
186
|
+
} elif isinstance(value, <>list) {
|
|
187
|
+
list_str = ', '.join(repr(v) for v in value);
|
|
188
|
+
items.append(f"{indent_str} {key}: [{list_str}],");
|
|
189
|
+
} elif isinstance(value, <>dict) {
|
|
190
|
+
nested = self._format_config_object(value, (indent + 2));
|
|
191
|
+
items.append(f"{indent_str} {key}: {{{nested}{indent_str} }},");
|
|
192
|
+
} else {
|
|
193
|
+
items.append(f"{indent_str} {key}: {repr(value)},");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return '\n'.join(items);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
"""Format plugin options as JavaScript object string."""
|
|
200
|
+
impl ViteBundler._format_plugin_options(self: ViteBundler, options: dict) -> str {
|
|
201
|
+
if not options {
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
204
|
+
items = [];
|
|
205
|
+
for (key, value) in options.items() {
|
|
206
|
+
if isinstance(value, str) {
|
|
207
|
+
items.append(f"{key}: '{value}'");
|
|
208
|
+
} elif isinstance(value, bool) {
|
|
209
|
+
items.append(f"{key}: {str(value).lower()}");
|
|
210
|
+
} elif isinstance(value, (int, float)) {
|
|
211
|
+
items.append(f"{key}: {value}");
|
|
212
|
+
} elif isinstance(value, <>list) {
|
|
213
|
+
items.append(f"{key}: [{', '.join(repr(v) for v in value)}]");
|
|
214
|
+
} else {
|
|
215
|
+
items.append(f"{key}: {repr(value)}");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return '{ ' + ', '.join(items) + ' }';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
"""Get a valid JavaScript variable name from plugin module name."""
|
|
222
|
+
impl ViteBundler._get_plugin_var_name(self: ViteBundler, plugin_name: str) -> str {
|
|
223
|
+
name = plugin_name.split('/')[-1];
|
|
224
|
+
name = name.replace('-', '_').replace('.', '_');
|
|
225
|
+
name = name.lstrip('@');
|
|
226
|
+
return name;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
"""Create vite.config.js from config.json during bundling."""
|
|
230
|
+
impl ViteBundler.create_vite_config(self: ViteBundler, entry_file: Path) -> Path {
|
|
231
|
+
build_dir = self._get_client_dir();
|
|
232
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
233
|
+
configs_dir = build_dir / 'configs';
|
|
234
|
+
configs_dir.mkdir(exist_ok=True);
|
|
235
|
+
vite_config_data = self.config_loader.get_vite_config();
|
|
236
|
+
config_path = configs_dir / 'vite.config.js';
|
|
237
|
+
# TypeScript is always enabled by default
|
|
238
|
+
try {
|
|
239
|
+
# Entry file path relative to client build dir (not project root)
|
|
240
|
+
entry_relative = entry_file.relative_to(build_dir).as_posix();
|
|
241
|
+
} except ValueError {
|
|
242
|
+
# Fallback: use absolute path
|
|
243
|
+
entry_relative = entry_file.as_posix();
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
# Output dir path relative to client build dir (not project root)
|
|
247
|
+
output_relative = self.output_dir.relative_to(build_dir).as_posix();
|
|
248
|
+
} except ValueError {
|
|
249
|
+
# Fallback: use absolute path
|
|
250
|
+
output_relative = self.output_dir.as_posix();
|
|
251
|
+
}
|
|
252
|
+
# Calculate compiled directory path for aliases (relative to client build dir)
|
|
253
|
+
if entry_relative.endswith('/build/main.js') {
|
|
254
|
+
compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
|
|
255
|
+
compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
|
|
256
|
+
} elif entry_relative.endswith('build/main.js') {
|
|
257
|
+
compiled_utils_relative = 'compiled/client_runtime.js';
|
|
258
|
+
compiled_assets_relative = 'compiled/assets';
|
|
259
|
+
} else {
|
|
260
|
+
compiled_utils_relative = 'compiled/client_runtime.js';
|
|
261
|
+
compiled_assets_relative = 'compiled/assets';
|
|
262
|
+
}
|
|
263
|
+
plugins = [];
|
|
264
|
+
plugin_imports = [];
|
|
265
|
+
# TypeScript support is always enabled, so always include React plugin
|
|
266
|
+
plugin_imports.append('import react from "@vitejs/plugin-react";');
|
|
267
|
+
plugins.append(' react()');
|
|
268
|
+
lib_imports = vite_config_data.get('lib_imports', []);
|
|
269
|
+
for lib_import in lib_imports {
|
|
270
|
+
if (isinstance(lib_import, str) and lib_import.strip()) {
|
|
271
|
+
plugin_imports.append(lib_import);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
custom_plugins = vite_config_data.get('plugins', []);
|
|
275
|
+
for plugin in custom_plugins {
|
|
276
|
+
if isinstance(plugin, str) {
|
|
277
|
+
plugins.append(f" {plugin}");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
plugins_str = ',\n'.join(plugins) if plugins else '';
|
|
281
|
+
imports_str = '\n'.join(plugin_imports) if plugin_imports else '';
|
|
282
|
+
# TypeScript extensions are always included
|
|
283
|
+
extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'];
|
|
284
|
+
extensions_str = ', '.join(f'"{ext}"' for ext in extensions);
|
|
285
|
+
build_config = vite_config_data.get('build', {});
|
|
286
|
+
build_overrides_str = self._format_config_object(build_config, indent=4)
|
|
287
|
+
if (isinstance(build_config, <>dict) and build_config)
|
|
288
|
+
else '';
|
|
289
|
+
server_config = vite_config_data.get('server', {});
|
|
290
|
+
server_config_str = self._format_config_object(server_config, indent=2)
|
|
291
|
+
if (isinstance(server_config, <>dict) and server_config)
|
|
292
|
+
else '';
|
|
293
|
+
resolve_config = vite_config_data.get('resolve', {});
|
|
294
|
+
resolve_overrides_str = self._format_config_object(resolve_config, indent=6)
|
|
295
|
+
if (isinstance(resolve_config, <>dict) and resolve_config)
|
|
296
|
+
else '';
|
|
297
|
+
imports_section = f"{imports_str}\n" if imports_str else '';
|
|
298
|
+
newline = '\n';
|
|
299
|
+
server_section = f" server: {{{newline}{server_config_str}{newline} }},{newline}"
|
|
300
|
+
if server_config_str
|
|
301
|
+
else '';
|
|
302
|
+
config_content = f'''import {{ defineConfig }} from "vite";
|
|
303
|
+
import path from "path";
|
|
304
|
+
import {{ fileURLToPath }} from "url";
|
|
305
|
+
{imports_section}const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
306
|
+
// Config is in configs/ inside .jac/client/, so go up one level to .jac/client/, then up two more to project root
|
|
307
|
+
const buildDir = path.resolve(__dirname, "..");
|
|
308
|
+
const projectRoot = path.resolve(__dirname, "../../..");
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Vite configuration generated from config.json (in project root)
|
|
312
|
+
* To customize, edit config.json instead of this file.
|
|
313
|
+
*/
|
|
314
|
+
|
|
315
|
+
export default defineConfig({{
|
|
316
|
+
plugins: [{(newline + plugins_str + newline + ' ') if plugins_str else ''}],
|
|
317
|
+
root: buildDir, // base folder (.jac/client/) so vite can find node_modules
|
|
318
|
+
build: {{
|
|
319
|
+
rollupOptions: {{
|
|
320
|
+
input: path.resolve(buildDir, "{entry_relative}"), // your compiled entry file
|
|
321
|
+
output: {{
|
|
322
|
+
entryFileNames: "client.[hash].js", // name of the final js file
|
|
323
|
+
assetFileNames: (assetInfo) => assetInfo.name?.endsWith('.css') ? 'styles.css' : '[name].[ext]',
|
|
324
|
+
}},
|
|
325
|
+
}},
|
|
326
|
+
outDir: path.resolve(buildDir, "{output_relative}"), // final bundled output
|
|
327
|
+
emptyOutDir: true,
|
|
328
|
+
{build_overrides_str}
|
|
329
|
+
}},
|
|
330
|
+
publicDir: false,
|
|
331
|
+
{server_section} resolve: {{
|
|
332
|
+
alias: {{
|
|
333
|
+
"@jac-client/utils": path.resolve(buildDir, "{compiled_utils_relative}"),
|
|
334
|
+
"@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
|
|
335
|
+
}},
|
|
336
|
+
extensions: [{extensions_str}],
|
|
337
|
+
{resolve_overrides_str}
|
|
338
|
+
}},
|
|
339
|
+
}});
|
|
340
|
+
''';
|
|
341
|
+
config_path.write_text(config_content, encoding='utf-8');
|
|
342
|
+
return config_path;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
"""Check if the project has TypeScript support. TypeScript is now enabled by default."""
|
|
346
|
+
impl ViteBundler._has_typescript_support(self: ViteBundler) -> bool {
|
|
347
|
+
# TypeScript is always enabled by default
|
|
348
|
+
return True;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
"""Read the bundled code and compute its hash."""
|
|
352
|
+
impl ViteBundler.read_bundle(self: ViteBundler) -> tuple[str, str] {
|
|
353
|
+
bundle_file = self.find_bundle();
|
|
354
|
+
if not bundle_file {
|
|
355
|
+
raise ClientBundleError('Vite build completed but no bundle file found') ;
|
|
356
|
+
}
|
|
357
|
+
bundle_code = bundle_file.read_text(encoding='utf-8');
|
|
358
|
+
bundle_hash = hashlib.sha256(bundle_code.encode('utf-8')).hexdigest();
|
|
359
|
+
return (bundle_code, bundle_hash);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
"""Find the generated Vite CSS file."""
|
|
363
|
+
impl ViteBundler.find_css(self: ViteBundler) -> Optional[Path] {
|
|
364
|
+
css_file = self.output_dir / 'styles.css';
|
|
365
|
+
return css_file if css_file.exists() else None;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
"""Find the generated Vite bundle file."""
|
|
369
|
+
impl ViteBundler.find_bundle(self: ViteBundler) -> Optional[Path] {
|
|
370
|
+
for file in self.output_dir.glob('client.*.js') {
|
|
371
|
+
return file;
|
|
372
|
+
}
|
|
373
|
+
return None;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
"""Run Vite build with generated config in .jac/client/configs/."""
|
|
377
|
+
impl ViteBundler.build(self: ViteBundler, entry_file: Optional[Path] = None) -> None {
|
|
378
|
+
self.output_dir.mkdir(parents=True, exist_ok=True);
|
|
379
|
+
generated_package_json = self._get_client_dir() / 'configs' / 'package.json';
|
|
380
|
+
if not generated_package_json.exists() {
|
|
381
|
+
self.create_package_json();
|
|
382
|
+
} else {
|
|
383
|
+
# Ensure tsconfig.json exists even if package.json already exists
|
|
384
|
+
self.create_tsconfig();
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
build_dir = self._get_client_dir();
|
|
388
|
+
node_modules = build_dir / 'node_modules';
|
|
389
|
+
if not node_modules.exists() {
|
|
390
|
+
# Temporarily copy package.json to client build dir for npm install
|
|
391
|
+
build_package_json = build_dir / 'package.json';
|
|
392
|
+
configs_package_json = build_dir / 'configs' / 'package.json';
|
|
393
|
+
if configs_package_json.exists() and not build_package_json.exists() {
|
|
394
|
+
import shutil;
|
|
395
|
+
shutil.copy2(configs_package_json, build_package_json);
|
|
396
|
+
}
|
|
397
|
+
try {
|
|
398
|
+
# Install to .jac/client/node_modules
|
|
399
|
+
subprocess.run(
|
|
400
|
+
['npm', 'install'],
|
|
401
|
+
cwd=build_dir,
|
|
402
|
+
check=True,
|
|
403
|
+
capture_output=True,
|
|
404
|
+
text=True
|
|
405
|
+
);
|
|
406
|
+
} except subprocess.CalledProcessError as e {
|
|
407
|
+
raise e from ClientBundleError(
|
|
408
|
+
f"Failed to install npm dependencies: {e.stderr}"
|
|
409
|
+
) ;
|
|
410
|
+
} except FileNotFoundError {
|
|
411
|
+
raise None from ClientBundleError(
|
|
412
|
+
'npm command not found. Ensure Node.js and npm are installed.'
|
|
413
|
+
) ;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if self.config_path {
|
|
417
|
+
# Make config path relative to build_dir (where vite runs from)
|
|
418
|
+
try {
|
|
419
|
+
config_rel = self.config_path.relative_to(build_dir);
|
|
420
|
+
command = ['npx', 'vite', 'build', '--config', str(config_rel)];
|
|
421
|
+
} except ValueError {
|
|
422
|
+
# Config is outside client build dir, use absolute path
|
|
423
|
+
command = ['npx', 'vite', 'build', '--config', str(self.config_path)];
|
|
424
|
+
}
|
|
425
|
+
} elif entry_file {
|
|
426
|
+
generated_config = self.create_vite_config(entry_file);
|
|
427
|
+
# Config is in configs/, make it relative to build_dir
|
|
428
|
+
config_rel = generated_config.relative_to(build_dir);
|
|
429
|
+
command = ['npx', 'vite', 'build', '--config', str(config_rel)];
|
|
430
|
+
} else {
|
|
431
|
+
command = ['npm', 'run', 'build'];
|
|
432
|
+
}
|
|
433
|
+
# Run vite from client build directory so it can find node_modules
|
|
434
|
+
result = subprocess.run(
|
|
435
|
+
command, cwd=build_dir, check=False, capture_output=True, text=True
|
|
436
|
+
);
|
|
437
|
+
if result.returncode != 0 {
|
|
438
|
+
error_msg = result.stderr or result.stdout or 'Unknown error';
|
|
439
|
+
raise ClientBundleError(
|
|
440
|
+
f"Vite build failed:\n{error_msg}\nCommand: {' '.join(command)}"
|
|
441
|
+
) from None ;
|
|
442
|
+
}
|
|
443
|
+
} finally {
|
|
444
|
+
# Clean up temporary package.json in client build dir
|
|
445
|
+
build_package_json = build_dir / 'package.json';
|
|
446
|
+
if build_package_json.exists() {
|
|
447
|
+
build_package_json.unlink();
|
|
448
|
+
}
|
|
449
|
+
# Move package-lock.json to configs/ if it exists
|
|
450
|
+
build_package_lock = build_dir / 'package-lock.json';
|
|
451
|
+
if build_package_lock.exists() {
|
|
452
|
+
configs_package_lock = build_dir / 'configs' / 'package-lock.json';
|
|
453
|
+
if configs_package_lock.exists() {
|
|
454
|
+
configs_package_lock.unlink();
|
|
455
|
+
}
|
|
456
|
+
build_package_lock.rename(configs_package_lock);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
"""Initialize the Vite bundler."""
|
|
462
|
+
impl ViteBundler.init(
|
|
463
|
+
self: ViteBundler,
|
|
464
|
+
project_dir: Path,
|
|
465
|
+
output_dir: Optional[Path] = None,
|
|
466
|
+
minify: bool = False,
|
|
467
|
+
config_path: Optional[Path] = None
|
|
468
|
+
) {
|
|
469
|
+
self.project_dir = project_dir;
|
|
470
|
+
self.minify = minify;
|
|
471
|
+
self.config_path = config_path;
|
|
472
|
+
self.config_loader = JacClientConfig(project_dir);
|
|
473
|
+
# Set output_dir after config_loader is initialized so _get_client_dir works
|
|
474
|
+
self.output_dir = output_dir or (self._get_client_dir() / 'dist');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
"""Create a dev-mode vite config with API proxy for HMR."""
|
|
478
|
+
impl ViteBundler.create_dev_vite_config(
|
|
479
|
+
self: ViteBundler, entry_file: Path, api_port: int = 8000
|
|
480
|
+
) -> Path {
|
|
481
|
+
build_dir = self._get_client_dir();
|
|
482
|
+
build_dir.mkdir(parents=True, exist_ok=True);
|
|
483
|
+
configs_dir = build_dir / 'configs';
|
|
484
|
+
configs_dir.mkdir(exist_ok=True);
|
|
485
|
+
config_path = configs_dir / 'vite.dev.config.js';
|
|
486
|
+
# Get entry file relative path
|
|
487
|
+
try {
|
|
488
|
+
entry_relative = entry_file.relative_to(build_dir).as_posix();
|
|
489
|
+
} except ValueError {
|
|
490
|
+
entry_relative = entry_file.as_posix();
|
|
491
|
+
}
|
|
492
|
+
# Calculate paths for aliases
|
|
493
|
+
if entry_relative.endswith('/build/main.js') {
|
|
494
|
+
compiled_utils_relative = entry_relative[:-13] + '/compiled/client_runtime.js';
|
|
495
|
+
compiled_assets_relative = entry_relative[:-13] + '/compiled/assets';
|
|
496
|
+
} elif entry_relative.endswith('build/main.js') {
|
|
497
|
+
compiled_utils_relative = 'compiled/client_runtime.js';
|
|
498
|
+
compiled_assets_relative = 'compiled/assets';
|
|
499
|
+
} else {
|
|
500
|
+
compiled_utils_relative = 'compiled/client_runtime.js';
|
|
501
|
+
compiled_assets_relative = 'compiled/assets';
|
|
502
|
+
}
|
|
503
|
+
# Extensions for TypeScript
|
|
504
|
+
extensions = ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'];
|
|
505
|
+
extensions_str = ', '.join(f'"{ext}"' for ext in extensions);
|
|
506
|
+
# Generate dev config with proxy for API routes
|
|
507
|
+
config_content = f'''import {{ defineConfig }} from "vite";
|
|
508
|
+
import path from "path";
|
|
509
|
+
import {{ fileURLToPath }} from "url";
|
|
510
|
+
import react from "@vitejs/plugin-react";
|
|
511
|
+
|
|
512
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
513
|
+
const buildDir = path.resolve(__dirname, "..");
|
|
514
|
+
const projectRoot = path.resolve(__dirname, "../../..");
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Vite DEV configuration for HMR mode
|
|
518
|
+
* Proxies API routes to Python server at localhost:{api_port}
|
|
519
|
+
*/
|
|
520
|
+
export default defineConfig({{
|
|
521
|
+
plugins: [react()],
|
|
522
|
+
root: buildDir,
|
|
523
|
+
publicDir: false,
|
|
524
|
+
server: {{
|
|
525
|
+
watch: {{
|
|
526
|
+
usePolling: true,
|
|
527
|
+
interval: 100,
|
|
528
|
+
}},
|
|
529
|
+
proxy: {{
|
|
530
|
+
"/walker": {{
|
|
531
|
+
target: "http://localhost:{api_port}",
|
|
532
|
+
changeOrigin: true,
|
|
533
|
+
}},
|
|
534
|
+
"/function": {{
|
|
535
|
+
target: "http://localhost:{api_port}",
|
|
536
|
+
changeOrigin: true,
|
|
537
|
+
}},
|
|
538
|
+
"/user": {{
|
|
539
|
+
target: "http://localhost:{api_port}",
|
|
540
|
+
changeOrigin: true,
|
|
541
|
+
}},
|
|
542
|
+
"/introspect": {{
|
|
543
|
+
target: "http://localhost:{api_port}",
|
|
544
|
+
changeOrigin: true,
|
|
545
|
+
}},
|
|
546
|
+
}},
|
|
547
|
+
}},
|
|
548
|
+
resolve: {{
|
|
549
|
+
alias: {{
|
|
550
|
+
"@jac-client/utils": path.resolve(buildDir, "{compiled_utils_relative}"),
|
|
551
|
+
"@jac-client/assets": path.resolve(buildDir, "{compiled_assets_relative}"),
|
|
552
|
+
}},
|
|
553
|
+
extensions: [{extensions_str}],
|
|
554
|
+
}},
|
|
555
|
+
}});
|
|
556
|
+
''';
|
|
557
|
+
config_path.write_text(config_content, encoding='utf-8');
|
|
558
|
+
return config_path;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
"""Start Vite dev server as a subprocess."""
|
|
562
|
+
impl ViteBundler.start_dev_server(self: ViteBundler, port: int = 3000) -> Any {
|
|
563
|
+
build_dir = self._get_client_dir();
|
|
564
|
+
node_modules = build_dir / 'node_modules';
|
|
565
|
+
# Create/update index.html for dev server (load from compiled/ for HMR)
|
|
566
|
+
index_html = build_dir / 'index.html';
|
|
567
|
+
index_content = '''<!DOCTYPE html>
|
|
568
|
+
<html lang="en">
|
|
569
|
+
<head>
|
|
570
|
+
<meta charset="UTF-8" />
|
|
571
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
572
|
+
<title>Jac App (Dev)</title>
|
|
573
|
+
</head>
|
|
574
|
+
<body>
|
|
575
|
+
<div id="root"></div>
|
|
576
|
+
<script type="module" src="/compiled/_entry.js"></script>
|
|
577
|
+
</body>
|
|
578
|
+
</html>
|
|
579
|
+
''';
|
|
580
|
+
index_html.write_text(index_content, encoding='utf-8');
|
|
581
|
+
# Ensure dependencies are installed
|
|
582
|
+
if not node_modules.exists() {
|
|
583
|
+
generated_package_json = build_dir / 'configs' / 'package.json';
|
|
584
|
+
if not generated_package_json.exists() {
|
|
585
|
+
self.create_package_json();
|
|
586
|
+
}
|
|
587
|
+
# Temporarily copy package.json for npm install
|
|
588
|
+
build_package_json = build_dir / 'package.json';
|
|
589
|
+
if not build_package_json.exists() {
|
|
590
|
+
shutil.copy2(generated_package_json, build_package_json);
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
print("[Vite] Installing dependencies...");
|
|
594
|
+
subprocess.run(
|
|
595
|
+
['npm', 'install'],
|
|
596
|
+
cwd=build_dir,
|
|
597
|
+
check=True,
|
|
598
|
+
capture_output=True,
|
|
599
|
+
text=True
|
|
600
|
+
);
|
|
601
|
+
} except subprocess.CalledProcessError as e {
|
|
602
|
+
print(f"[Vite] Error installing dependencies: {e.stderr}");
|
|
603
|
+
raise ;
|
|
604
|
+
} finally {
|
|
605
|
+
# Clean up temp package.json
|
|
606
|
+
if build_package_json.exists() {
|
|
607
|
+
build_package_json.unlink();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
# Find the dev config
|
|
612
|
+
dev_config = build_dir / 'configs' / 'vite.dev.config.js';
|
|
613
|
+
if not dev_config.exists() {
|
|
614
|
+
raise ClientBundleError(
|
|
615
|
+
"Dev config not found. Call create_dev_vite_config first."
|
|
616
|
+
) ;
|
|
617
|
+
}
|
|
618
|
+
config_rel = dev_config.relative_to(build_dir);
|
|
619
|
+
print(f"[Vite] Starting dev server on port {port}...");
|
|
620
|
+
# Start Vite in dev mode (let output go to terminal for HMR visibility)
|
|
621
|
+
process = subprocess.Popen(
|
|
622
|
+
['npx', 'vite', '--config', str(config_rel), '--port', str(port)],
|
|
623
|
+
cwd=build_dir
|
|
624
|
+
);
|
|
625
|
+
return process;
|
|
626
|
+
}
|
|
@@ -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
|
+
}
|