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
<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
| Function | Description |
|---|---|
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
| Directive | Example | Description |
|---|---|---|
{{expr()}} | <span>{{count()}}</span> | Reactive text interpolation (call signals to read) |
@event | @click="handler" / @click="removeItem(item)" | DOM event listener (name or expression) |
if | if="status() === 'active'" | Conditional rendering |
else-if | else-if="status() === 'pending'" | Alternative branch |
else | else | Default branch |
each | each="item in items()" | List rendering (keyed with :key) |
show | show="isVisible()" | CSS visibility toggle |
model | model="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) |
ref | ref="name" | Mark element for templateRef() |
Slots
| Type | Component | Consumer |
|---|---|---|
| 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
| Command | Description |
|---|---|
wcc build | Compile all .wcc files + copy wcc-runtime.js |
wcc dev | Build + watch + SSE live-reload dev server |