wcCompiler

Zero-runtime compiler that transforms .wcc single-file components into native web components.

No framework. No virtual DOM. No runtime. Just vanilla custom elements with signals-based reactivity.

Get Started GitHub
npm install -D @sprlab/wccompiler

Features

⚡ Signals Reactivity

signal(), computed(), effect() — fine-grained reactivity with automatic dependency tracking.

📝 Template Directives

if/else, each, show, model, :attr, @event — declarative template syntax.

🔌 Scoped Slots

Named slots, default slots, and scoped slots with reactive prop passing via <template #name="{ prop }">.

🎨 CSS Scoping

Automatic tag-name prefixing. No Shadow DOM — styles are scoped but composable.

🔷 TypeScript

Full TypeScript support with type stripping via esbuild. Generics for props and emits.

📦 Zero Output

Compiled components are self-contained vanilla JS. No runtime library, no imports, no dependencies.

🧩 Nested Components

Auto-import child components. Reactive prop binding with :prop attribute syntax.

🔄 Dev Server

SSE live-reload, file watching, instant recompilation. wcc dev and you're running.

🛠️ VS Code Extension

The wcCompiler (.wcc) Language Support extension provides syntax highlighting, completions, and diagnostics for .wcc files.

Quick Start

1. Install

npm install -D @sprlab/wccompiler

2. Create a component

src/wcc-counter.wcc
<script>
import { defineComponent, signal } from 'wcc'

export default defineComponent({
  tag: 'wcc-counter',
})

const count = signal(0)

function increment() {
  count.set(count() + 1)
}
</script>

<template>
<div class="counter">
  <span>{{count()}}</span>
  <button @click="increment">+</button>
</div>
</template>

<style>
.counter { display: flex; gap: 8px; align-items: center; }
</style>

3. Build

npx wcc build

4. Use

<script type="module" src="dist/wcc-counter.js"></script>
<wcc-counter></wcc-counter>

API Reference

Script API

FunctionDescription
signal(value)Create a reactive variable. Read: x(), Write: x.set(val)
computed(() => expr)Derived value with caching and auto-invalidation
effect(() => { ... })Side effect that re-runs when dependencies change. Supports cleanup via return function.
watch(signal, (n, o) => {}) / watch(() => expr, (n, o) => {})Observe a signal or getter with old/new values
defineProps({ key: default })Declare external props with defaults
defineEmits(['event'])Declare custom events (validated at compile time)
templateRef('name')Get a DOM element reference from the template
templateRef<T>('name')Typed ref — import type from child .wcc for autocomplete
onMount(() => {})Lifecycle: connectedCallback (supports async)
onDestroy(() => {})Lifecycle: disconnectedCallback
defineExpose({ ... })Expose methods/properties for external access via ref

Template Directives

DirectiveExampleDescription
{{expr()}}<span>{{count()}}</span>Reactive text interpolation (call signals to read)
@event@click="handler" / @click="removeItem(item)"DOM event listener (name or expression)
ifif="status() === 'active'"Conditional rendering
else-ifelse-if="status() === 'pending'"Alternative branch
elseelseDefault branch
eacheach="item in items()"List rendering (keyed with :key)
showshow="isVisible()"CSS visibility toggle
modelmodel="name"Two-way binding (input, select, textarea)
:attr:href="url()"Attribute binding
:class:class="{ active: isActive() }"Class binding (string or object)
:style:style="{ color: textColor() }"Style binding (string or object)
refref="name"Mark element for templateRef()

Slots

TypeComponentConsumer
Default<slot>fallback</slot>Plain child elements
Named<slot name="x"></slot><template #x>content</template>
Scoped<slot name="x" :prop="val"></slot><template #x="{ prop }">{{prop}}</template>

CLI

CommandDescription
wcc buildCompile all .wcc files + copy wcc-runtime.js
wcc devBuild + watch + SSE live-reload dev server