Compilation Flow
The Problem
Section titled “The Problem”One .pen.tsx component needs to work in React, Angular, Vue, and vanilla JS. Pencel separates concerns into three layers to achieve this efficiently.
Layer 1: Semantic (IR)
Section titled “Layer 1: Semantic (IR)”Format-agnostic metadata captured from decorators:
{ tag: 'my-button', props: [{ name: 'label', type: 'string' }], events: [{ name: 'onClick' }]}This IR is the single source of truth for all downstream transformations.
Layer 2: Synchronization (AST)
Section titled “Layer 2: Synchronization (AST)”Transformers sync AST nodes with IR via IRRef. IR drives all changes; AST mirrors the result.
Layer 3: Translation (Output)
Section titled “Layer 3: Translation (Output)”Two kinds of output from IR:
- Generators – Global files (ir.json, components.d.ts). Fully rebuild from complete IR each pass.
- Derivatives – Framework adapters (React, Angular, Vue). One per source file, incremental.
Mental Model
Section titled “Mental Model”graph LR
Source["component.pen.tsx"]
IR["FileIR (source of truth)"]
Gen["component.gen.ts"]
React["component.react.tsx"]
Angular["component.angular.ts"]
Source -->|parse| IR
IR -->|normalize| Gen
IR -->|derive| React
IR -->|derive| Angular
style Source fill:#b3e5fc,stroke:#0277bd,color:#000
style IR fill:#fff59d,stroke:#fbc02d,stroke-width:3px,color:#000
style Gen fill:#ffe0b2,stroke:#e65100,stroke-width:3px,color:#000
style React fill:#c8e6c9,stroke:#388e3c,color:#000
style Angular fill:#c8e6c9,stroke:#388e3c,color:#000
Key: .gen.ts is the normalized source (inlined CSS, resolved URLs, extended impl). Derivatives wrap .gen.ts for each framework.