https://github.com/wcstack/wcstack
Web Components Stack
https://github.com/wcstack/wcstack
buildless custom-elements navigation-api no-build state-management web-components
Last synced: 5 days ago
JSON representation
Web Components Stack
- Host: GitHub
- URL: https://github.com/wcstack/wcstack
- Owner: wcstack
- License: mit
- Created: 2026-01-16T22:54:19.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-07-14T21:55:38.000Z (8 days ago)
- Last Synced: 2026-07-14T23:10:37.364Z (8 days ago)
- Topics: buildless, custom-elements, navigation-api, no-build, state-management, web-components
- Language: TypeScript
- Homepage: https://wcstack.github.io/
- Size: 15.8 MB
- Stars: 5
- Watchers: 0
- Forks: 0
- Open Issues: 10
-
Metadata Files:
- Readme: README.ja.md
- License: LICENSE
Awesome Lists containing this project
README
# wcstack
**もしブラウザにこれが最初からあったら?**
wcstack は「未来のWeb標準を想像し、ライブラリとして実装する」プロジェクトです。リアクティブなデータバインディング、宣言的ルーティング、コンポーネントの自動読み込み — これらがブラウザに最初から組み込まれていたら、どんな形になるでしょうか?
フレームワークではなく、*あるべきだった* HTMLタグの実現を目指しています。
---
## ルール
このプロジェクトには5つの縛りがあります。これが面白さの源泉です。
| # | ルール | 理由 |
|---|--------|------|
| 1 | **CDN一発** | `` タグ1つ。npm不要、バンドラー不要、設定不要。 |
| 2 | **機能はカスタムタグで提供** | すべてがカスタム要素。`<wcs-something>` で表現できないなら、このプロジェクトの範囲外。 |
| 3 | **初期ロード = タグ定義だけ** | スクリプトはカスタム要素を登録するだけ。初期化コードもブートストラップも不要。 |
| 4 | **HTMLのセマンティクスを崩さない** | 式は `data-*` 属性とテキストノードに収まる — HTMLが拡張を許している場所だけを使う。DOM構造とセマンティクスはそのまま。 |
| 5 | **最新のECMAScript** | 最新のJS機能を積極的に採用。ES5へのトランスパイルはしない。未来を作ってるんだから。 |
この縛り、簡単そうに見えるでしょう? そうでもないです。
HTMLのセマンティクスを崩さないためには、仕様のどこが拡張を許していて、どこが許していないかを深く理解していないと破綻する。すべてをカスタムタグで作るには、ライフサイクル・順序制御・コンポーネント間通信をCustom Elementsの仕組みの中で解決しないといけない。依存ライブラリゼロということは、すべてのアルゴリズムを自分で書くということ。そしてそのすべてが、「ブラウザ組み込みかも」と思えるクオリティでなければならない。
---
## 設計の核心
既存のすべてのフレームワークでは、**コンポーネント**がUIと状態の出会う場所になっている。状態ストアを外部に切り出しても、コンポーネント内に状態を引き込むグルーコードを書くことになる。UIと状態は常にJavaScriptの中で結合する。
wcstack は、文字通り別の **パス** を選んだ。
UIと状態を結びつけている**唯一の契約(コントラクト)**は**パス文字列**です。 — `user.name`、`cart.items.*.subtotal`、`@shared`。フックもインポートも結合のためのコードもありません。コンポーネントのJavaScriptには状態を参照するコードが一切含まれていません。HTMLだけが、すべてのデータ依存関係を宣言的に記述します。
```
State ← "user.name" → UI パスが2つのレイヤーを結ぶ
Comp A ← "@app" → Comp B 名前付きパスがコンポーネントを横断する
Loop ← "items.*" → Template ワイルドカードがインデックスを抽象化する
```
つまり、UIを作り直しても状態に触れなくていい。状態をリファクタリングしてもDOMに触れなくていい。HTMLを読めばすべてが分かる。REST APIのURLと同じ発想 — シンプルな文字列契約、共有コードなし。
---
## パッケージ
41個の独立したランタイムパッケージ + 1つのツール拡張パッケージ。ランタイム依存ゼロ(SSR用のhappy-domを除く)。ビルド不要。
### もしHTMLにリアクティブなデータバインディングがあったら?
[`@wcstack/state`](packages/state/) — 状態をインラインで宣言し、属性でDOMにバインドする。
```html
<wcs-state>
<script type="module">
export default {
taxRate: 0.1,
cart: {
items: [
{ name: "ウィジェット", price: 500, quantity: 2 },
{ name: "ガジェット", price: 1200, quantity: 1 }
]
},
removeItem(event, index) {
this["cart.items"] = this["cart.items"].toSpliced(index, 1);
},
get "cart.items.*.subtotal"() {
return this["cart.items.*.price"] * this["cart.items.*.quantity"];
},
get "cart.total"() {
return this.$getAll("cart.items.*.subtotal", []).reduce((a, b) => a + b, 0);
},
get "cart.grandTotal"() {
return this["cart.total"] * (1 + this.taxRate);
}
};
{{ .name }} ×
=
削除
合計:
```
- **パスgetter** — `get "users.*.fullName"()` あらゆる深さの算出プロパティ
- **構造ディレクティブ** — `` による `for`、`if` / `elseif` / `else`
- **40以上のフィルタ** — 比較、算術、文字列、日付、フォーマット
- **双方向バインディング** — ``、``、`` で自動
- **Mustache構文** — テキストノード内の `{{ path|filter }}`
- **Web Componentバインディング** — Shadow DOMとの双方向状態同期
[詳細ドキュメント →](packages/state/README.ja.md)
---
### もしルーティングがただのHTMLタグだったら?
[`@wcstack/router`](packages/router/) — アプリのナビゲーション構造をマークアップで定義する。
```html
ホーム
商品一覧
ホーム
```
- **ネストされたルート & レイアウト** — Light DOMで宣言的にUI構造を組み立て
- **型付きパラメータ** — `:id(int)`、`:slug(slug)`、`:date(isoDate)` で自動変換
- **自動バインディング** — `data-bind` でURLパラメータをコンポーネントに注入
- **Head管理** — `` でルートごとに `` と `` を切り替え
- **Navigation API** — モダンな標準APIベース、popstateフォールバック付き
- **ルートガード** — 非同期の判定関数でルートを保護
[詳細ドキュメント →](packages/router/README.ja.md)
---
### もし fetch がタグだったら?
[`@wcstack/fetch`](packages/fetch/) — 宣言的な HTTP 通信をヘッドレス Web Component として。
```html
export default {
users: [],
loading: false,
filterRole: "",
get usersUrl() {
const role = this.filterRole;
return role ? "/api/users?role=" + role : "/api/users";
},
};
読み込み中...
```
- **CSBC アーキテクチャ** — Core / Shell / Binding Contract 分離
- **wc-bindable-protocol** — React、Vue、Svelte、Solid と薄いアダプタで連携
- **URL 監視** — バインドされた URL の変更で自動再フェッチ
- **trigger プロパティ** — 状態から宣言的に fetch 実行、DOM 参照不要
- **HTML リプレースモード** — htmx 的な `target` 属性でサーバーレンダリング断片を差し替え
- **ヘッドレス Core** — `FetchCore` は Node.js、Deno、Cloudflare Workers で動作
[詳細ドキュメント →](packages/fetch/README.ja.md)
---
### もしカスタム要素が勝手に読み込まれたら?
[`@wcstack/autoloader`](packages/autoloader/) — タグを書くだけで読み込まれる。登録コード不要。
```html
{
"imports": {
"@components/ui/": "./components/ui/",
"@components/ui|lit/": "./components/ui-lit/"
}
}
```
- **Import Mapベース** — 名前空間解決、コンポーネントごとの登録不要
- **即時 & 遅延読み込み** — 重要なコンポーネントを先に、残りはオンデマンドで
- **MutationObserver** — 動的に追加された要素も自動検知
- **プラガブルローダー** — Vanilla、Lit、カスタムローダーを混在可能
- **`is` 属性** — カスタマイズされた組み込み要素の `extends` 自動検出
[詳細ドキュメント →](packages/autoloader/README.ja.md)
---
### もしテンプレートがサーバーでレンダリングされたら?
[`@wcstack/server`](packages/server/) — 同じHTML、サーバーでレンダリング。特別な構文不要。
```javascript
import { renderToString } from "@wcstack/server";
const html = await renderToString(`
export default {
items: [],
async $connectedCallback() {
const res = await fetch("/api/items");
this.items = await res.json();
}
};
`);
```
- **ドロップインSSR** — `` に `enable-ssr` を追加して `renderToString()` を呼ぶだけ
- **自動ハイドレーション** — クライアントがサーバーの続きをシームレスに引き継ぎ、フリッカーなし
- **相対URL自動解決** — `baseUrl` オプションで `fetch("/api/...")` がサーバー上でも動作
- **バージョン安全フォールバック** — バージョン不一致時はDOMをクリーンアップしてCSRにフォールバック
- **`` ハイドレーションデータ** — 状態スナップショット、テンプレート、プロパティを1要素に集約
[詳細ドキュメント →](packages/server/README.ja.md)
---
### 追加パッケージ
- [`@wcstack/websocket`](packages/websocket/) — `` でリアルタイム通信を宣言的に扱い、接続状態や受信データをバインド可能。
- [`@wcstack/upload`](packages/upload/) — ファイルアップロードを宣言的に記述し、進捗・状態管理をフレームワーク非依存で提供。
- [`@wcstack/storage`](packages/storage/) — `` で localStorage / sessionStorage と状態を宣言的に同期。
- [`@wcstack/timer`](packages/timer/) — `` で時刻経過やポーリングを宣言的な状態変化として扱う。
- [`@wcstack/raf`](packages/raf/) — `` で requestAnimationFrame を宣言的に。フレーム tick・一級の `dt`・非表示タブの `suspended` 出力。
- [`@wcstack/geolocation`](packages/geolocation/) — `` で位置情報を宣言的に扱い、単発/継続取得、精度、ライブな権限状態を提供。
- [`@wcstack/debounce`](packages/debounce/) — `` と `` で値・シグナルのストリームをまとめる debounce/throttle を宣言的に。
- [`@wcstack/clipboard`](packages/clipboard/) — `` でクリップボードの読み書き、リッチな `ClipboardItem`、copy/cut/paste 監視、ライブな権限状態を宣言的に。
- [`@wcstack/broadcast`](packages/broadcast/) — `` で同一オリジンの BroadcastChannel による pub/sub をバインド可能な状態としてタブ間メッセージング。
- [`@wcstack/worker`](packages/worker/) — `` で重い処理をバックグラウンドスレッドに退避し、message/error/running 状態をバインド可能に。
- [`@wcstack/sse`](packages/sse/) — `` で Server-Sent Events(EventSource)による一方向ストリーミングを、message/接続状態をバインド可能な状態として、名前付きイベント対応で。
- [`@wcstack/intersection`](packages/intersection/) — `` で遅延読み込み・無限スクロール・スクロールスパイを、可視状態をバインド可能な IntersectionObserver として。
- [`@wcstack/wakelock`](packages/wakelock/) — `` で Screen Wake Lock を宣言的に。バインドした boolean が true の間スクリーンを起こしたままにし、visibility 変化をまたいで再取得する。
- [`@wcstack/resize`](packages/resize/) — `` で要素サイズ・コンテナ幅の測定・サイズ依存ロジックを、バインド可能な状態として ResizeObserver で。
- [`@wcstack/speech`](packages/speech/) — ``(text-to-speech を command-token として)と ``(認識結果を event-token 状態として)で音声を宣言的に。
- [`@wcstack/permission`](packages/permission/) — `` で Permissions API を監視し、ライブな `granted`/`denied`/`prompt` 状態を公開。読み取り専用ウォッチャー(コマンドなし)で、`` などの機能ノードと組み合わせる。
- [`@wcstack/network`](packages/network/) — `` で Network Information を監視し、アダプティブ読み込み向けにライブな `effectiveType`/`downlink`/`rtt`/`saveData` 状態を公開。読み取り専用ウォッチャー(コマンド・属性なし)で、非対応(Firefox/Safari)がエッジケースではなく常態。
- [`@wcstack/screen-orientation`](packages/screen-orientation/) — `` で画面の向きを監視し `lock`/`unlock` コマンドを提供、`type`/`angle`/`portrait`/`landscape` を公開。監視は同期のため `_gen` ガード不要、`lock()` は非同期のため必要(監視とは独立)。
- [`@wcstack/fullscreen`](packages/fullscreen/) — `` で Fullscreen API を宣言的に。`` の target 解決パターンを再利用し、`active` は解決した target が document の `fullscreenElement` かを追跡。
- [`@wcstack/picture-in-picture`](packages/picture-in-picture/) — ``(target は `` 要素)で Picture-in-Picture を宣言的に。`` と同じ target 解決パターン。
- [`@wcstack/pointer-lock`](packages/pointer-lock/) — `` でゲームや canvas UI 向けの Pointer Lock を。`movementX`/`movementY` は v1 では意図的に対象外(必要なら後日 `@wcstack/debounce`/`@wcstack/throttle` と組み合わせ)。
- [`@wcstack/share`](packages/share/) — `` で Web Share API を宣言的に。`share(data)` コマンド、`value`/`loading`/`error`/`cancelled` 状態。`cancelled`(ユーザーが共有シートを閉じた)は `error`(真の失敗)と区別。
- [`@wcstack/eyedropper`](packages/eyedropper/) — `` で EyeDropper API(デスクトップのカラーピッカー)を。`open()`/`abort()` コマンド、`value` は `{ sRGBHex }`。`` と同じ `value`/`loading`/`error`/`cancelled` の形。
- [`@wcstack/contacts`](packages/contacts/) — `` で Contact Picker API を。`select(properties, options)` コマンド(Android Chrome のみ — それ以外は非対応が既定)。`value` は `multiple: false` でも常に配列。
- [`@wcstack/credential`](packages/credential/) — `` で Credential Management(パスワード/フェデレーションのみ — WebAuthn は明確に対象外)を。`get(options)`/`store(credential)` コマンドが1つの `_gen` を共有(文書化済みの並行性制限)。
- [`@wcstack/idle`](packages/idle/) — `` で Idle Detection を。ジェスチャ必須の `requestPermission()` + `start`/`stop`、`userState`/`screenState`/`active` を公開。権限状態は重複させず `` と組み合わせる。接続時に自動開始しない。
- [`@wcstack/tilt`](packages/tilt/) — `` で Device Orientation を。iOS のジェスチャ必須 `requestPermission()`(他環境では no-op)を吸収し、どこでも同じフローで書ける。`permissionState` はローカルで追跡する3値語彙(対応する Permissions API エントリが存在しない)。
- [`@wcstack/accelerometer`](packages/accelerometer/) / [`@wcstack/gyroscope`](packages/gyroscope/) / [`@wcstack/magnetometer`](packages/magnetometer/) / [`@wcstack/ambient-light-sensor`](packages/ambient-light-sensor/) — Generic Sensor API ファミリ。``/``/`` は `x`/`y`/`z` を、`` は単一の `illuminance` スカラーを公開(ブラウザ対応が最も弱く、フィンガープリンティング対策で無効化しているブラウザもある)。4つとも権限状態を重複させず `` と組み合わせ、ガード付きセンサーコンストラクタ呼び出し以外に `_gen` ガードは不要(同期的な start/stop)。
- [`@wcstack/notification`](packages/notification/) — `` でデスクトップ通知を宣言的に。command-token(`notify`)で表示し、event-token(`clicked`)でクリックを受け取る — 双方向を1タグで。権限は自己完結、モバイル向けに Service Worker フォールバック。
- [`@wcstack/defined`](packages/defined/) — `` でカスタム要素の準備完了を。タグ集合の `whenDefined()` を監視し `defined`/`pending`/`missing`/`count`/`total` 状態を公開、タイムアウトによる読み込み失敗検知付き。autoloader の相棒で、CSS `:defined` にできないことを実現。
- [`@wcstack/camera`](packages/camera/) — ``(getUserMedia + 組み込みプレビュー)と ``(MediaRecorder)でカメラ撮影・録画を宣言的に。ライブな `MediaStream` は command-token 引数で要素へ直接バインドし、**シリアライズ可能な状態には決して格納しない** — 派生値(権限、録画フラグ、録画した `Blob`/URL)だけが状態を流れる。
- [`@wcstack/signals`](packages/signals/) — シグナルベースのきめ細かいリアクティブ**コア**(`@wcstack/state` の JS ファースト版)。`signal`/`computed`/`effect`、非同期の `resource`/`streamResource`、keyed な `For`/`Index`、同じ wc-bindable IO ノードをシグナル経由で駆動する `bindNode` アダプタ。TC39-Signals 準拠、依存ゼロ。
- [`@wcstack/devtools`](packages/devtools/) — `` によるページ内 DevTools オーバーレイ。state ツリーの検査(通常のリアクティブパイプラインを通るインライン編集付き)、各パスがどの DOM ノードに配線されているかの表示、write / 更新バッチ / command・event トークン発火のライブタイムライン — 購読者ゼロの「空撃ち」警告付き。`` 一行、DevTools Hook Protocol で接続、依存ゼロ。
- [`wcstack-intellisense`](packages/vscode-wcs/) — `<wcs-state>` インラインスクリプト向けの VS Code 言語サポート拡張。
---
## Quick Start
```html
<!DOCTYPE html>
<html>
<head>
<script type="module" src="https://esm.run/@wcstack/state/auto">
export default {
count: 0,
countUp() { this.count++; }
};
Count: {{ count }}
+1