domiana
v0.1.0
Published
Server-side DOM execution environment with real-time browser serving
Maintainers
Readme
LinkDOM ⚡
قلب الموازين: تشغيل بناء Vite وتطبيقات React بالكامل على السيرفر، مع بث شجرة الـ DOM وتحديثها لحظياً للعملاء بدون إعادة تحميل الصفحة!
Run complete Vite & React apps directly on the server (Node.js & Bun) with zero client bundle, while streaming granular DOM diffs and delegating events bidirectionally over WebSockets.
Features
- ⚡ Zero-Config Vite Plugin (
linkdom()):- Add
plugins: [linkdom()]tovite.config.ts. - Inverts the equation: Vite runs the application inside a simulated DOM on the server and streams the reactive DOM to real browsers.
- Automatic JSX/TSX and CSS transformation with instant live reload.
- Add
- 🔄 Granular DOM Morphing (Diff & Patch):
- Updates only the affected text nodes, attributes, and child elements without reloading the page.
- Preserves user input focus, text selection, and scroll positions across updates.
- 📡 Bi-Directional Event Delegation:
- Browser user interactions (
click,input,change,submit) are captured and forwarded over WebSocket to the server DOM. - Server React components handle synthetic events as if running locally in the browser.
- Browser user interactions (
- ✨ Seamless
react-domCompatibility:- Full React 18 & 19 concurrent features (
createRoot, hooks, state batching). - Controlled inputs & input value tracking (
HTMLInputElement.prototypedescriptors). - Browser global polyfills (
window,document,navigator,customElements,requestAnimationFrame).
- Full React 18 & 19 concurrent features (
- 🎨 Automatic CSS Handling:
import './style.css'in Vite automatically injects<style>into<head>.- Or use
linkdom.css('./style.css')with disk file watching for instant live style updates.
Installation
npm install linkdom
# or
bun add linkdomMode 1: Vite Plugin (Recommended)
Invert the traditional client bundling: let Vite run your app on the server and stream the live DOM to clients.
1. vite.config.ts
import { defineConfig } from 'vite';
import { linkdom } from 'linkdom';
export default defineConfig({
plugins: [linkdom()],
});2. src/main.tsx
Write normal React code with state, event handlers, and CSS:
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import '../style.css';
export function App() {
const [count, setCount] = useState(0);
return (
<div className="container">
<h1>Vite + LinkDOM ⚡</h1>
<p>تطبيق Vite يعمل بالكامل على السيرفر ومربوط لحظياً بالمتصفح!</p>
<button
id="btn"
className="btn"
onClick={() => setCount((c) => c + 1)}
>
عدد النقرات: {count} 🚀
</button>
</div>
);
}
let container = document.getElementById('root');
if (!container) {
container = document.createElement('div');
container.id = 'root';
document.body.appendChild(container);
}
const root = (globalThis as any).__linkdom_root__ ?? ((globalThis as any).__linkdom_root__ = createRoot(container));
root.render(<App />);3. Run Development Server
npm run dev
# Server opens with live DOM morphing and instant hot updates!4. Build for Production (vite build)
LinkDOM inverts Vite's production build as well: instead of outputting static client scripts and HTML, vite build bundles your entire application into a single, executable server bundle:
npm run build
# Generates dist/main.mjsRun your production server with zero client JS bundles:
node dist/main.mjs
# or
PORT=8080 node dist/main.mjsWhen started, main.mjs boots the virtual DOM, mounts your React application, loads styles into <head>, and starts the live HTTP + WebSocket server serving reactive pages to clients.
Mode 2: Standalone Server
Use LinkDOM without Vite in any Node.js / Bun script or microservice:
import { linkdom } from 'linkdom';
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
// 1. Prepare global DOM environment
await linkdom.prepare();
// 2. Load and watch CSS
linkdom.css('./style.css');
// 3. Mount React tree on server
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>LinkDOM Standalone</h1>
<button onClick={() => setCount((c) => c + 1)}>
Clicks: {count}
</button>
</div>
);
}
const root = createRoot(document.getElementById('root')!);
root.render(<Counter />);
// 4. Start HTTP + WebSocket server
const server = await linkdom.serve({ port: 3500 });
console.log(`Ready at ${server.url}`);API Reference
linkdom(options?)
Vite plugin function for vite.config.ts.
entry?: string: Entry file path (defaults to auto-detectingsrc/main.tsx,main.tsx,app.tsx, etc.).liveReload?: boolean: Enable live DOM morphing over WebSocket (default:true).port?: number: Production server port in generatedmain.mjs(default:3000orprocess.env.PORT).host?: string: Production server host in generatedmain.mjs(default:'0.0.0.0'orprocess.env.HOST).
await linkdom.prepare(options?)
Prepares the global DOM environment. Injects window, document, navigator, and all HTML/Event constructors onto globalThis.
url?: string: Initial window URL (default:'http://localhost:3500/').html?: string: Initial HTML template.width?: number: Viewport width (default:1280).height?: number: Viewport height (default:800).
linkdom.css(input)
Injects CSS content or a CSS file path into document.head. Automatically watches the file for real-time live updates.
await linkdom.serve(options?)
Starts a standalone HTTP + WebSocket server serving the current document.
port?: number: Port to listen on (default:3500).host?: string: Host to bind (default:'127.0.0.1').liveReload?: boolean: Enable live DOM morphing (default:true).
Returns LinkdomServer:
server: Underlying Node.js HTTP server.url: Bound URL.reload(): Broadcasts reload/patch across connected clients.close(): Closes server and WebSocket connections.
Testing
Run all 12 comprehensive unit and integration tests:
npm testIncludes:
- React 18 & 19
react-domexecution & hooks verification. - Bi-directional event delegation & value tracking.
- Granular DOM morphing & WebSocket patch broadcasting.
- Vite plugin SSR & live browser synchronization.
License
MIT
