@zenithbuild/bundler 1.3.1

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.
package/Cargo.toml ADDED
@@ -0,0 +1,65 @@
1
+ [package]
2
+ name = "zenith-bundler"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Rolldown Plugin for the Zenith Framework - Zero-Cost Abstraction Bundler"
6
+ license = "MIT"
7
+
8
+ [lib]
9
+ crate-type = ["cdylib", "rlib"]
10
+
11
+ [features]
12
+ napi = []
13
+
14
+ [dependencies]
15
+ # CSS Optimization & Pruning
16
+ lightningcss = "1.0.0-alpha.70"
17
+
18
+ # Serialization
19
+ serde = { version = "1.0", features = ["derive"] }
20
+ serde_json = "1.0"
21
+
22
+ # Zenith Compiler (local crate)
23
+ compiler-native = { path = "../zenith-compiler/native/compiler-native", default-features = false }
24
+
25
+ # Node.js FFI (for TypeScript interface)
26
+ napi = { version = "2.16.0", features = ["async", "serde-json"] }
27
+ napi-derive = "2.16.0"
28
+
29
+ # Async runtime
30
+ tokio = { version = "1.0", features = ["fs", "macros", "rt-multi-thread"] }
31
+
32
+ # Concurrent collections for CSS buffering
33
+ dashmap = "5.5"
34
+
35
+ # ArcStr for Rolldown compatibility
36
+ arcstr = "1.2"
37
+
38
+ # Error handling
39
+ anyhow = "1.0"
40
+
41
+ # Regex for CSS class extraction
42
+ regex = "1.10"
43
+
44
+ # Rolldown from git (latest compatible version)
45
+ # Note: Using git source because crates.io version 0.1.0 has broken oxc_resolver compat
46
+ [dependencies.rolldown]
47
+ git = "https://github.com/rolldown/rolldown"
48
+ branch = "main"
49
+ default-features = false
50
+
51
+ [dependencies.rolldown_plugin]
52
+ git = "https://github.com/rolldown/rolldown"
53
+ branch = "main"
54
+ default-features = false
55
+
56
+ [dependencies.rolldown_common]
57
+ git = "https://github.com/rolldown/rolldown"
58
+ branch = "main"
59
+ default-features = false
60
+
61
+ [build-dependencies]
62
+ napi-build = "2.1.0"
63
+
64
+ [dev-dependencies]
65
+ pretty_assertions = "1.4"
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # Zenith Bundler
2
+
3
+ Zero-Cost Abstraction Bundler for the Zenith Framework.
4
+
5
+ ## Overview
6
+
7
+ The Zenith Bundler provides capability-based runtime chunking, CSS pruning, and deferred hydration for optimal production builds. Instead of shipping a monolithic runtime, it selectively includes only the capabilities used by each page.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ Compiler (ZenIR) → Manifest → Bundler → Optimized Output
13
+ ```
14
+
15
+ ### Runtime Slices
16
+
17
+ | Slice | Size | When Included |
18
+ |-------|------|---------------|
19
+ | **Core** | ~2KB | Always required |
20
+ | **Reactivity** | ~8KB | If `{value}` expressions or state used |
21
+ | **Hydration** | ~5KB | If page is interactive |
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ # Rust crate
27
+ cargo add zenith-bundler
28
+
29
+ # TypeScript package
30
+ bun add @zenithbuild/bundler
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Rust
36
+
37
+ ```rust
38
+ use zenith_bundler::{bundle, analyze_manifest, ZenManifest};
39
+
40
+ let manifest = ZenManifest::new("src/pages/index.zen".to_string());
41
+ let analysis = analyze_manifest(&manifest);
42
+
43
+ println!("Required slices: {:?}", analysis.required_slices);
44
+ println!("Is static: {}", analysis.is_static);
45
+ ```
46
+
47
+ ### TypeScript
48
+
49
+ ```typescript
50
+ import { bundle, generateRuntime } from '@zenithbuild/bundler'
51
+
52
+ // Full production bundle
53
+ const result = bundle(manifest, {
54
+ minifyJs: true,
55
+ minifyCss: true,
56
+ basePath: '/assets/'
57
+ })
58
+
59
+ // Dev server (HMR)
60
+ const { code, slices } = generateRuntime(manifest, true)
61
+ ```
62
+
63
+ ## API
64
+
65
+ ### `bundle(manifest, options?)`
66
+
67
+ Generates complete HTML/JS/CSS output.
68
+
69
+ **Options:**
70
+ - `minifyJs` - Minify JavaScript (default: true)
71
+ - `minifyCss` - Minify CSS (default: true)
72
+ - `inlineCriticalCss` - Inline critical CSS (default: true)
73
+ - `sourceMaps` - Generate source maps (default: false)
74
+ - `devMode` - Skip optimizations (default: false)
75
+ - `basePath` - Asset base path (default: "/")
76
+ - `lazyLoad` - Lazy load non-critical chunks (default: true)
77
+ - `maxChunkSize` - Max chunk size in bytes (default: 50000)
78
+
79
+ ### `generateRuntime(manifest, devMode?)`
80
+
81
+ Generates only the runtime code (for HMR/dev server).
82
+
83
+ ### `analyzeManifest(manifest)`
84
+
85
+ Analyzes a manifest and returns required slices.
86
+
87
+ ## Bundle Size Budgets
88
+
89
+ | Page Type | Budget |
90
+ |-----------|--------|
91
+ | Static | < 5KB |
92
+ | Interactive | < 20KB |
93
+ | Complex | < 50KB |
94
+
95
+ Run size gate: `bun run js/scripts/size-gate.ts`
96
+
97
+ ## Testing
98
+
99
+ ```bash
100
+ # Rust tests
101
+ cargo test
102
+
103
+ # TypeScript tests
104
+ cd js && bun test
105
+
106
+ # Size gate
107
+ cd js && bun run scripts/size-gate.ts
108
+ ```
109
+
110
+ ## Project Structure
111
+
112
+ ```
113
+ zenith-bundler/
114
+ ├── src/
115
+ │ ├── lib.rs # Main exports
116
+ │ ├── analysis.rs # Manifest analysis
117
+ │ ├── chunking/ # Chunk computation
118
+ │ ├── codegen/ # Runtime generation
119
+ │ ├── css/ # CSS optimization
120
+ │ └── manifest/ # Types & capabilities
121
+ ├── tests/
122
+ │ └── integration.rs # Integration tests
123
+ └── js/
124
+ ├── src/
125
+ │ ├── index.ts # TypeScript API
126
+ │ ├── types.ts # TypeScript types
127
+ │ └── index.test.ts
128
+ └── scripts/
129
+ └── size-gate.ts # Bundle size CI gate
130
+ ```
131
+
132
+ ## License
133
+
134
+ MIT
135
+ # zenith-bundler
package/build.rs ADDED
@@ -0,0 +1,5 @@
1
+ extern crate napi_build;
2
+
3
+ fn main() {
4
+ napi_build::setup();
5
+ }
package/dev-server.js ADDED
@@ -0,0 +1,117 @@
1
+ const express = require('express');
2
+ const { ZenithDevController } = require('./index.node'); // Assuming built target via napi
3
+ const path = require('path');
4
+ const chokidar = require('chokidar');
5
+ const { WebSocketServer } = require('ws');
6
+ const http = require('http');
7
+
8
+ const app = express();
9
+ const port = 3000;
10
+
11
+ // Initialize Controller (Points to current project root)
12
+ // Default to parent directory (assuming zenith-bundler is inside project or sibling)
13
+ // Adjust this based on where you run the server.
14
+ const start = async (projectRoot = process.env.PROJECT_ROOT || path.resolve(__dirname, '..')) => {
15
+ console.log(`[DevServer] Starting Zenith for: ${projectRoot}`);
16
+
17
+ let controller;
18
+ try {
19
+ controller = new ZenithDevController(projectRoot);
20
+ } catch (e) {
21
+ console.error("Failed to initialize ZenithDevController:", e);
22
+ console.error("Ensure you have built the native module: 'napi build --platform'");
23
+ process.exit(1);
24
+ }
25
+
26
+ // Create HTTP and WS Server
27
+ const server = http.createServer(app);
28
+ const wss = new WebSocketServer({ server });
29
+
30
+ wss.on('connection', (ws) => {
31
+ // console.log('[HMR] Client connected');
32
+ });
33
+
34
+ function notifyHMR() {
35
+ // console.log('[HMR] Broadcasting update signal...');
36
+ wss.clients.forEach((client) => {
37
+ if (client.readyState === 1) { // OPEN
38
+ client.send(JSON.stringify({ type: 'update' }));
39
+ }
40
+ });
41
+ }
42
+
43
+ // Watcher Integration (Tactical Fix)
44
+ const watchPath = path.join(projectRoot, 'src/**/*.zen');
45
+ console.log(`[Watcher] Watching ${watchPath}`);
46
+ const watcher = chokidar.watch(watchPath, {
47
+ ignoreInitial: true
48
+ });
49
+
50
+ watcher.on('change', async (filePath) => {
51
+ // console.log(`[Watcher] File changed: ${filePath}`);
52
+ try {
53
+ // 1. Tell Rust to re-compile (updates the AssetStore in RAM)
54
+ console.time('Rebuild');
55
+ await controller.rebuild();
56
+ console.timeEnd('Rebuild');
57
+
58
+ // 2. Tell the Browser to fetch the new assets
59
+ notifyHMR();
60
+ } catch (e) {
61
+ console.error("Rebuild failed:", e);
62
+ }
63
+ });
64
+
65
+ // Serve Assets
66
+ app.use(async (req, res, next) => {
67
+ // 1. Try to serve from memory store
68
+ const asset = controller.getAsset(req.path);
69
+
70
+ if (asset) {
71
+ if (req.path.endsWith('.js')) {
72
+ res.setHeader('Content-Type', 'application/javascript');
73
+ } else if (req.path.endsWith('.css')) {
74
+ res.setHeader('Content-Type', 'text/css');
75
+ }
76
+ return res.send(asset);
77
+ }
78
+
79
+ // 2. SPA / HTML Fallback
80
+ if (req.path === '/' || !path.extname(req.path)) {
81
+ res.send(`
82
+ <!DOCTYPE html>
83
+ <html>
84
+ <head>
85
+ <title>Zenith Dev</title>
86
+ <script type="module" src="/index.js"></script>
87
+ <link rel="stylesheet" href="/zenith.css">
88
+ <script>
89
+ // HMR Client
90
+ const ws = new WebSocket('ws://' + location.host);
91
+ ws.onmessage = (event) => {
92
+ const msg = JSON.parse(event.data);
93
+ if (msg.type === 'update') {
94
+ console.log('[HMR] Update received. Reloading...');
95
+ location.reload();
96
+ }
97
+ };
98
+ ws.onopen = () => console.log("[Zenith] HMR Connected");
99
+ </script>
100
+ </head>
101
+ <body>
102
+ <div id="app"></div>
103
+ </body>
104
+ </html>
105
+ `);
106
+ return;
107
+ }
108
+
109
+ next();
110
+ });
111
+
112
+ server.listen(port, () => {
113
+ console.log(`Zenith Dev Server running at http://localhost:${port}`);
114
+ });
115
+ };
116
+
117
+ module.exports = { start };
package/index.js ADDED
@@ -0,0 +1,18 @@
1
+ const devServer = require('./dev-server.js');
2
+
3
+ function createZenithBundler() {
4
+ return {
5
+ async dev(options) {
6
+ console.log("Starting Zenith Bundler (Dev)...");
7
+ // Assuming options has root or fallback
8
+ const root = options?.root || process.cwd();
9
+ await devServer.start(root);
10
+ },
11
+ async build(options) {
12
+ console.log("Zenith Bundler Build: Not yet implemented in Phase 6.");
13
+ // Placeholder for production build integration
14
+ }
15
+ };
16
+ }
17
+
18
+ module.exports = { createZenithBundler };
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@zenithbuild/bundler",
3
+ "version": "1.3.1",
4
+ "devDependencies": {
5
+ "@types/bun": "latest"
6
+ },
7
+ "peerDependencies": {
8
+ "typescript": "^5"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "main": "index.js"
14
+ }
package/src/bundler.rs ADDED
@@ -0,0 +1,81 @@
1
+ //! Zenith Bundler Configuration
2
+ //!
3
+ //! Provides helper functions to create a pre-configured Rolldown bundler
4
+ //! with Zenith's required settings (capability splitting, plugin injection, etc).
5
+
6
+ use rolldown::{Bundler, BundlerBuilder, BundlerOptions, InputItem};
7
+ use std::sync::Arc;
8
+
9
+ use crate::plugin::ZenithPlugin;
10
+
11
+ /// Create a configured Rolldown bundler for a Zenith project
12
+ pub fn create_zenith_bundler(entry_point: &str, components_dir: Option<&str>) -> Bundler {
13
+ // 1. Initialize the Zenith Plugin
14
+ let mut plugin = ZenithPlugin::new(entry_point);
15
+ if let Some(dir) = components_dir {
16
+ plugin = plugin.with_components_dir(dir);
17
+ }
18
+
19
+ // 2. Configure Bundler Options
20
+ // Note: manual_chunks is currently experimental in Rolldown Rust API,
21
+ // we'll need to verify the exact API surface.
22
+ // For now, we rely on the plugin's dynamic imports to drive chunking naturally,
23
+ // and we can enhance this with explicit manual_chunks if the API allows.
24
+
25
+ let options = BundlerOptions {
26
+ input: Some(vec![InputItem {
27
+ name: Some("index".into()),
28
+ import: "virtual:zenith-entry".into(), // Start with our Hydration Controller
29
+ }]),
30
+ // Enable code splitting
31
+ format: Some(rolldown_common::OutputFormat::Esm),
32
+ // Ensure we target browser environment
33
+ platform: Some(rolldown_common::Platform::Browser),
34
+ // Capability-Based Chunking:
35
+ // GSAP should be handled as a separate chunk (capability: "anim").
36
+ // We rely on dynamic imports (import('gsap')) in the user's code to automatically split it.
37
+ // Explicit manual_chunks can be added here if stricter control is needed.
38
+ // Capability-based chunking configuration will go here once verified
39
+ ..Default::default()
40
+ };
41
+
42
+ let builder = BundlerBuilder::default()
43
+ .with_options(options)
44
+ .with_plugins(vec![Arc::new(plugin)]);
45
+
46
+ builder.build().expect("Failed to build bundler")
47
+ }
48
+
49
+ /// Create a configured Rolldown bundler for Dev Mode (Watch + HMR + InMemory)
50
+ pub fn create_dev_bundler(
51
+ entry_point: &str,
52
+ components_dir: Option<&str>,
53
+ store: std::sync::Arc<crate::store::AssetStore>,
54
+ ) -> Bundler {
55
+ // 1. Initialize the Zenith Plugin with Store and Dev Mode
56
+ let mut plugin = ZenithPlugin::new(entry_point)
57
+ .with_store(store)
58
+ .with_dev_mode(true);
59
+
60
+ if let Some(dir) = components_dir {
61
+ plugin = plugin.with_components_dir(dir);
62
+ }
63
+
64
+ // 2. Configure Bundler Options (Dev Optimized)
65
+ let options = BundlerOptions {
66
+ input: Some(vec![InputItem {
67
+ name: Some("index".into()),
68
+ import: "virtual:zenith-entry".into(),
69
+ }]),
70
+ format: Some(rolldown_common::OutputFormat::Esm),
71
+ platform: Some(rolldown_common::Platform::Browser),
72
+ sourcemap: Some(rolldown_common::SourceMapType::File), // Enable sourcemaps for dev
73
+ ..Default::default()
74
+ };
75
+
76
+ let builder = BundlerBuilder::default()
77
+ .with_options(options)
78
+ .with_plugins(vec![Arc::new(plugin)]);
79
+
80
+ builder.build().expect("Failed to build dev bundler")
81
+ }