Skip to content

Compilation Flow

One .pen.tsx component needs to work in React, Angular, Vue, and vanilla JS. Pencel separates concerns into three layers to achieve this efficiently.

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.

Transformers sync AST nodes with IR via IRRef. IR drives all changes; AST mirrors the result.

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.
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.