Skip to content

@ox-content/code-play#

View Markdown

Code Play は、ドキュメントのサンプルをオンデマンドで実行します。別プラグインです。 @ox-content/vite-plugin は有効にせず、このパッケージを入れただけでは何も起きません。 言語を列挙するまで動きません。

このサイトの ドキュメント例 とスタンドアロンの examples/code-play アプリは、JavaScript、TypeScript、Rust、Go を有効にしています。Rust、Go、 リモート言語はオプトインするまでオフです。

インストール#

vp install @ox-content/code-play@beta
pnpm add @ox-content/code-play@beta
bun add @ox-content/code-play@beta
npm install @ox-content/code-play@beta
yarn add @ox-content/code-play@beta

import { 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 は、その言語のスキーマ向けに executetypecheckendpointconfig の上書きを受け付けます(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 は stackblitzcodesandboxwebcontainerexternal に対応しています。 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

完全なカタログは ロードマップ と同じ一覧です。 tsc++bashcoq のようなエイリアスは正規 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 ロードマップ を見てください。

Last updated: