@ox-content/code-play#
Code Play は、ドキュメントのサンプルをオンデマンドで実行します。別プラグインです。
@ox-content/vite-plugin は有効にせず、このパッケージを入れただけでは何も起きません。
言語を列挙するまで動きません。
このサイトの ドキュメント例 とスタンドアロンの
examples/code-play
アプリは、JavaScript、TypeScript、Rust、Go を有効にしています。Rust、Go、
リモート言語はオプトインするまでオフです。
インストール#
vp install @ox-content/code-play@betapnpm add @ox-content/code-play@betabun add @ox-content/code-play@betanpm install @ox-content/code-play@betayarn add @ox-content/code-play@betaimport { oxContent } from "@ox-content/vite-plugin";
import { codePlay } from "@ox-content/code-play";
export default {
plugins: [
oxContent({ highlight: true }),
codePlay({
languages: {
typescript: { execute: true, typecheck: true },
javascript: true,
rust: true,
go: true,
},
ui: "default",
viewers: { config: true, stdio: true, stderr: true, provenance: true, timing: true },
srcDir: "content",
}),
],
};
このプラグインは、パッケージインストールの上に乗る二段目のオプトインです。
play のないフェンス、または一覧にない言語は、普通のハイライト付きブロックのままです。
Code Play ブロックがないページには hydrate スクリプトは入りません。
ビルド全体で Code Play を使わない場合は ox-code-play.js も出力されません。
プラグインオプション#
| オプション | 型 | 既定 | 役割 |
|---|---|---|---|
languages |
Record<string, true | LanguageEnableOptions> |
{} |
execute / typecheck / endpoint を有効化 |
ui |
"default" | "compact" | "headless" |
default |
サンプル周りのクロム |
viewers |
Partial<ViewerFlags> |
すべてオン | stdio / stderr / config / … の表示 |
timeoutMs |
number |
10000 |
実行ごとのタイムアウト |
endpoints |
{ rust?, go?, typecheck? } |
official | プレイグラウンド / typecheck の URL |
proxy |
boolean |
true |
Vite dev の /__ox-code-play/* をマウント |
srcDir |
string |
"docs" |
play フェンス照合に使う Markdown ルート |
outDir |
string |
Vite out | SSG 後に拡張する書き出し HTML |
base |
string |
"/" |
ox-code-play.js の公開パス |
LanguageEnableOptions は、その言語のスキーマ向けに execute、typecheck、endpoint、
config の上書きを受け付けます(TypeScript の strict、Rust の
crateType、Go の withVet、…)。
執筆#
フェンスに play を付けます。言語が対応しているときは typecheck も足せます。
```ts play typecheck play-title="Strict TypeScript" play-strict=false play-target=ESNext
const n: number = 1;
console.log(n);
```
```rust play typecheck play-title="Release-mode Rust" play-mode=release
fn main() {
println!("ok");
}
```
```go play typecheck play-title="Go vet on"
package main
import "fmt"
func main() {
fmt.Println("ok")
}
```
play-title はウィジェットのラベルです。play-compact / play-headless は
そのサンプルだけ UI プリセットを変え、play-timeout=2500 はタイムアウトを変えます。
play-viewers=stdio,stderr,-timing でビューアーを切り替えられます。
play-<config-key>=... は言語ごとの config 値なので、TypeScript は
play-strict=false、Rust は play-edition=2021、Go は play-withVet=false
のように書けます。
HTML / MDX 形式:
<CodePlay lang="ts" title="Loose TS" typecheck ui="compact" config-strict="false">
const n = 1;
</CodePlay>
プロジェクト単位の例は、サンプルごとに play-project または project で明示します。
現在のフェンスは主たる実行スニペットのままにし、project metadata としてファイル名、
provider、外部 fallback link を追加します。
```ts play play-project=stackblitz play-file=src/main.ts play-entry=src/main.ts play-files=package.json,src/App.tsx play-project-url=https://stackblitz.com/edit/example
console.log("project");
```
play-file は現在のフェンスをプロジェクト内のどのファイルとして扱うかを指定します。
play-files は追加ファイルのカンマ区切りリストで、Markdown source file からの相対パスとして
解決され、srcDir の内側に制限されます。provider metadata adapter は
stackblitz、codesandbox、webcontainer、external に対応しています。
Code Play は provider script を読み込みません。安全な http(s) URL があるとき、
生成された widget は project metadata と Open fallback link を描画します。
Headless API#
import { createCodePlay } from "@ox-content/code-play";
const play = createCodePlay({ languages: { typescript: true } });
const session = play.createSession({
language: "ts",
code: "const n: number = 1;\nconsole.log(n);",
});
const check = await session.typecheck();
const run = await session.run();
run.stdio; // timestamped stdin / stdout / stderr events
run.stdout; // concatenated stdout text
run.stderr; // concatenated stderr text
run.provenance; // where it compiled, where it ran
run.timing; // phase durations and totalMs
session.config; // editable language config
有効にしていない言語を求めると createCodePlay() は throw します。
session.setConfig({ strict: false }) は、config ビューアーが編集するのと同じオブジェクトを更新します。
session.cancel() は進行中の run または typecheck を中止し、
status: "cancelled" を返します。既定のツールバーは、実行中に Cancel を出します。
テストでは transport(たとえば createMemoryTransport)を注入し、
CI がライブのプレイグラウンドに触れないようにします。
| フィールド | 意味 |
|---|---|
run.status |
ok / error / offline / timeout / cancelled / unsupported |
run.stdio |
タイムスタンプ付きの stdin / stdout / stderr イベント |
run.stdout |
連結した stdout テキスト |
run.stderr |
連結した stderr テキスト |
run.diagnostics |
任意の行 / 列付きのコンパイラ / ランタイムメッセージ |
run.provenance |
どこでコンパイルし、どこで実行したか |
run.timing |
フェーズ時間と totalMs |
run.preview |
バックエンドが UI のときのフレームワーク iframe srcdoc |
session.stdout |
lastResult.stdout と同じ |
session.stderr |
lastResult.stderr と同じ |
独自 UI では、export されている RunActionState ヘルパー
idleRunActionState()、runningRunActionState(action)、
resultRunActionState(action, result) を使えます。transport や CORS の失敗は
status: "offline" になり、コンパイル / ランタイムエラーとは別に扱えます。
UI#
| プリセット | 振る舞い |
|---|---|
default |
ツールバーと stdio / stderr / config / provenance / timing タブ |
compact |
Run / type-check と stdio、stderr |
headless |
DOM クロムなし。セッション API を使う |
ビューアーは viewers で個別に切り替えられます。hydrate 後のウィジェットは
polite なステータス領域、aria-busy、tab panel、矢印キーによるタブ移動を提供します。
言語#
| 言語 | 実行 | 型チェック | バックエンド |
|---|---|---|---|
| TypeScript | yes | yes | ローカル strip-types + tsgo + node:vm |
| Rust | yes | yes | play.rust-lang.org(または endpoints.rust) |
| Go | yes | yes | play.golang.org(または endpoints.go) |
| JavaScript | yes | no | node:vm / サンドボックス iframe |
| Vue、React、Svelte、Solid | yes | no | iframe srcdoc + esm.sh import map |
| Python、PHP、Ruby、sh、… | yes | no | Piston 互換の languages.<id>.endpoint |
完全なカタログは ロードマップ と同じ一覧です。
ts、c++、bash、coq のようなエイリアスは正規 id に解決されます。
プレイグラウンドプロキシ#
Vite の dev サーバー のみです。codePlay({ proxy: true })(既定)は次をマウントします。
| パス | 転送先 |
|---|---|
POST /__ox-code-play/rust |
endpoints.rust(既定 https://play.rust-lang.org/execute) |
POST /__ox-code-play/go |
endpoints.go(既定 https://play.golang.org/compile) |
POST /__ox-code-play/typecheck |
ローカル tsgo(リモートコンパイラなし) |
これらのルートは POST のみを受け付け、本文を 256 KiB で上限し、
http(s) 以外の宛先や埋め込み認証情報付き URL を拒否します。上流の
失敗は汎用 JSON { "error": "..." } を返し、fetch の詳細は漏らしません。
プロキシは本番の SSG 出力には入りません。公開ページでは endpoints を公式
プレイグラウンド(または自分の HTTPS 実行器)に向けるか、
dev ミドルウェアが不要なら proxy: false にしてください。
静的ホストは POST /__ox-code-play/typecheck を提供しません。TypeScript の
Run はブラウザ内で動きます(型を剥がしてからサンドボックス iframe)。
到達可能な endpoints.typecheck を設定しない限り、公開ウィジェットから
Typecheck ボタンは省かれます。Vite プロキシ経路は vite dev のあいだだけ使います。
公開ページ上の Rust と Go は、ブラウザから直接 endpoints.rust / endpoints.go
を呼びます。公式プレイグラウンドが既定です。より厳密な分離、監査、または上流の
ブラウザポリシー変更への fallback が必要な場合は、endpoints を自分で制御する
実行器へ向けてください。
セキュリティ#
play フェンスは、出荷する他のスクリプトと同じ 信頼できるサイトコンテンツ です。
訪問者が書いたものや未レビューの断片に play を付けないでください。
- サンプルは Markdown transform や SSG のあいだには実行されません。
- JavaScript / TypeScript の実行 は Node では
node:vm、ブラウザでは<iframe sandbox="allow-scripts">(allow-same-originなし)です。 ページ起源のFunctionでは決して動きません。サンプルはホストページの DOM や ストレージを読めません。 - Vue / React / Svelte / Solid プレビューは同じ iframe フラグと
srcdocを使います。プレビューランタイムはesm.shから読みます。 shは docs ホスト上でローカルシェルを起動しません。- Rust / Go はソースを
play.rust-lang.org/play.golang.org(またはendpointsの上書き)へ POST します。それらのホストはサンプルを見ます。 プライバシーポリシーが適用されます。 - Piston 互換の
languages.<id>.endpointはその言語のソースを受け取ります。 信頼できる HTTPS エンドポイントだけを、埋め込み認証情報なしで設定してください。 - Project sandbox payload は、現在のフェンスと
play-filesの信頼済み source を埋め込みます。 追加ファイルは Markdown source root 配下の相対パスだけを受け付けます。symlink の実パスも 埋め込み前に検査され、存在しないファイルや大きすぎるファイルは widget warning になります。 provider URL は認証情報なしのhttp(s)に制限されます。
初回公開#
@ox-content/code-play は npm では新しいです。Trusted publishing はパッケージを
作れないので、メンテナーがノート PC から 一度 公開し、そのあと
npm trust で trusted publisher を登録します。コマンドは
リリース作業 にあります。
後続 PR は Code Play ロードマップ を見てください。