# Material HU — Dev Server Enables instant HMR for `material-hu` and `hu-translations` in consumer apps — no rebuild required. Changes to source files are reflected in the browser immediately. ## Prerequisites The consumer app and the repos you want to develop against must be siblings on disk: ``` parent/ ├── humand-web/ ← consumer app (run commands from here) ├── material-hu/ ← optional, enables component HMR └── hu-translations/ ← optional, enables translation HMR ``` At least one of `material-hu` or `hu-translations` must be present. ## Setup in the consumer repo ### 1. `vite.config.ts` Add the plugins and gate them behind the `ENABLE_HMR_PLUGINS` env variable: ```ts import { createHuTranslationsVitePlugin, createMaterialHuVitePlugin, } from '@humanddev/material-hu/vite'; import { defineConfig, loadEnv } from 'vite'; export default defineConfig(({ command, mode }) => { const env = loadEnv(mode, process.cwd(), 'ENABLE_HMR_PLUGINS'); const isDev = command === 'serve' && Boolean(env.ENABLE_HMR_PLUGINS); return { plugins: [ createMaterialHuVitePlugin({ enabled: isDev }), createHuTranslationsVitePlugin({ enabled: isDev }), ], }; }); ``` Each plugin is a no-op if its sibling folder is not found, so it's safe to register both unconditionally. ### 2. `i18n.ts` (only if using hu-translations) Import the client handler so the browser listens for translation updates: ```ts import '@humanddev/material-hu/vite/hu-translations/clientSocketHandler'; ``` ### 3. `package.json` Add a script that runs the dev server with HMR enabled: ```jsonc { "scripts": { "start:dev": "tsx ./node_modules/@humanddev/material-hu/vite/dev-server/index.ts" } } ``` Then run: ```sh bun run start:dev ``` ## What dev-server does 1. **Validates** that at least one sibling repo exists, exits with an error otherwise 2. **Patches `tsconfig.json`** for IDE / `tsc` type resolution (see below) 3. **Spawns Vite** with `ENABLE_HMR_PLUGINS=true`, which activates both plugins 4. **Restores `tsconfig.json`** on exit (Ctrl+C, SIGTERM, or Vite close) The `tsconfig.json` change is hidden from git via `skip-worktree` while the server is running. ### Tsconfig patch (`dev-server/patch-tsconfig.ts`) Vite HMR already resolves `@material-hu/*` to `../material-hu/src/*` via the Vite plugin. TypeScript needs a separate fix: **do not** add `tsconfig.dev.json` to `extends`. TypeScript replaces the entire `paths` object when extending (it does not deep-merge). Extending the dev tsconfig would drop consumer paths such as `src/*` or `@material-hu/mui-old/*`, and paths like `../material-hu/src/*` would resolve relative to `node_modules/@humanddev/material-hu/vite/` instead of the consumer root. On start (when `../material-hu` exists as a sibling), dev-server: 1. Reads the consumer's effective `paths` (via the TypeScript config API, including its `extends` chain) 2. Reads `node_modules/@humanddev/material-hu/vite/dev-server/tsconfig.dev.json` and resolves those entries from the **consumer root** 3. Merges both maps (dev entries win so `@material-hu/*` points at sibling source) 4. Writes the result **inline** into `compilerOptions.paths` (consumer `extends` is unchanged) 5. Adds theme augmentation files to `include` and a marker key for idempotency A `tsconfig.json.backup` is created and restored on exit. Re-running dev-server is safe: a stale backup is recovered first, and an already-patched config is skipped via the marker. ## Layout ``` vite/ ├── material-hu/ # Vite plugin — component HMR ├── hu-translations/ # Vite plugin — translation HMR ├── dev-server/ # start:dev entry, tsconfig patch └── shared/ # utils, types, constants (internal) ``` ## Plugins - [material-hu](./material-hu/README.md) — maps `@material-hu/*` imports to local source - [hu-translations](./hu-translations/README.md) — hot-reloads translation JSON files