Monorepo Structure
HMI compiler projects use a pnpm monorepo with @figma-hmi/* aliases:
| Package | Purpose |
|---|---|
schema |
Domain types + Zod validation (base dependency) |
design |
Design document structure + CSS rendering |
protocol |
WebSocket message schemas |
runtime-core |
Pure business logic — no React |
runtime-web |
React UI + connectors (mock, WebSocket) |
compiler |
CLI to package into deployable artifact |
figma-plugin |
Figma plugin UI |
opcua-gateway |
OPC-UA server bridge |
Cross-package imports use @figma-hmi/* aliases. Internal deps use workspace:* protocol.
TypeScript Rules
- Target ES2022, strict mode, ESM (
"type": "module") - Explicit
typeimports:import type { Foo } from '...' node:prefix for Node.js built-ins:import { resolve } from 'node:path'- Define explicit return types on functions
- Discriminated unions + Zod schemas for domain types; infer via
z.infer<typeof Schema>
React (Runtime Web)
- React 19, functional components only
- Wrap presentational components in
memo()with callback props - State: local
useStateonly — no global state manager - Async effects with cancellation:
let cancelled = false+ cleanup return - Inline styles via
CSSProperties— no Tailwind, no CSS modules - Props: explicit interface (
interface FooProps { ... })
Figma Plugin Architecture
The Figma plugin bridges the design tool with the HMI compiler:
- Read: Extract component hierarchy, styles, variables, and interactive states from Figma
- Transform: Convert to the
@figma-hmi/schemaformat (Zod-validated) - Bundle:
_generated-project-bundle.tsis auto-generated from the Figma design - Deploy: Compiler packages into Docker container with OPC-UA gateway
Rebuild runtime-web before regenerating the bundle:
corepack pnpm build
node apps/figma-plugin/scripts/build.mjs
OPC-UA Integration
- The
opcua-gatewaybridges HMI runtime to industrial OPC-UA servers - WebSocket protocol defined in
@figma-hmi/protocolwith Zod schemas - All message types are discriminated unions for type safety
- Gateway runs as Docker container alongside the compiled HMI frontend
Testing
- Vitest with
describe/itsyntax - Test files in
test/folder per package, named*.test.ts - React tests:
createRoot+act(), DOM queries viaquerySelector - Run per-package:
corepack pnpm --filter <package> test
Code Style
- PascalCase: components, types, schemas
- camelCase: functions, variables, properties
as constarrays for enum-like values- Section headers:
// ─── Section Name ─────────────── - Pure functions, immutable patterns — return new objects, don't mutate