👏

VScode 上で今開いているファイルを Vite でプレビューする拡張を作ったら便利

2023/10/19に公開

ほしかったので作ったシリーズ。

画像を見たらどういうものか伝わると思う。

自分で使っているが、ある程度の縛りがあるが、かなり快適。

これは何

https://github.com/mizchi/vscode-vite-preview

現在開いているファイルを vite でビルドして、 vscode 内の Webview Panel としてプレビューする VSCode 拡張。

src/components/Sub.tsx
// props を持たないファイル名と同名のコンポーネント
export default function Sub() {
  return <h1 className="flex">
    <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
      Click mee
    </button>
  </h1>
}

または __PREVIEW__ コンポーネントが export されていればそれを使う。

src/components/Sub.tsx
// props を持たないファイル名と同名のコンポーネント
export default function Sub(props: {name: string}) {
  return <h1 className="flex">
    <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
      Click {props.name}
    </button>
  </h1>
}

// ここが render される
export const __PREVIEW__ = () => {
  return <Sub name="dummy" />
}

他にも .svelte.html にも対応してる。対応パターンは以下。

https://github.com/mizchi/vscode-vite-preview/tree/main/extensions/vite-preview/test/ws1/src

注意点として、 dynamic import が絡むとプレビューに失敗する。

React+Tailwind が動いているプロジェクトのサンプル

https://github.com/mizchi/vscode-vite-preview/tree/main/examples/simple

webview panel で作った利点として、 vscode は electron なので、 実は Chrome と同じ devtool を内蔵している。 Developper: Toggle Developper Tools から devtool を開き vscode 内の要素としてインスペクトするこができる。

debugger もそのまま使える。

似たような HTML プレビューツールとして https://marketplace.visualstudio.com/items?itemName=antfu.browse-lite があるが、これは中で puppeteer を動かして画像を転送しているだけなので、解像度がボケボケになってしまう。vscode-vite も同じ問題を抱えている。

https://github.com/antfu/vscode-vite

今回は webview panel で動かしているので解像度の問題はないが、 vscode が自動的に注入する CSS に影響を受けてしまう可能性はある。

導入

まだ vscode marketplace にアップロードしてないので、バイナリ(vsix)から入れる想定。

$ wget https://github.com/mizchi/vscode-vite-preview/raw/main/extensions/vite-preview/releases/vite-preview-0.0.1.vsix
## CLI から vscode にインストール
$ code --install-extension vite-preview-0.0.1.vsix

これで vscode のコマンドとして Vite Preview: Preview Current(デフォルトでは ctrl-alt+r) が使えるようになる。これは現在開いているファイルを vite でビルドし、それを vscode 内の webview として表示する。

ただ、そのプロジェクトの vite の設定が済んでいる必要がある。

Create project

$ pnpm create vite@latest myapp
$ cd myapp
$ pnpm install

## Optional
$ pnpm add tailwindcss postcss autoprefixer -D
$ pnpm tailwind init

Optional: postcss and tailwind

tailwind.config.js と postcss.config.js からのファイル参照を絶対パス指定に修正する。(TODO: 直したい...)

tailwind.config.js
const path = require("path");
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    path.join(__dirname, "src/**/*.{html,ts,tsx,svelte}"),
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
postcss.config.js
const path = require("path");
module.exports = {
  plugins: {
    autoprefixer: {},
    tailwindcss: {
      config: path.join(__dirname, "tailwind.config.js"),
    },
  }
}

Optional: preview.html

vite.config.ts と同じディレクトリに preview.html を置くと、それがプレビュー時に使われるエントリポイントになる。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body.vscode-light,
    body.vscode-dark {
      color: black;
      background-color: white;
    }

    #preview-root {
      padding: 10px;
      box-sizing: border-box;
      width: 100%;
      height: 100%;
    }
  </style>
</head>
<body>
  <div id="preview-root"></div>
  <script type="module" src="/src/pre.ts"></script>
  <script type="module" src="/__PREVIEW__.tsx"></script>
</body>
</html>

この vscode 拡張は、現在開いているファイルのコンテキストを自動で類推して /__PREVIEW__.tsx の中身を自動生成する。

どのように動いているか

https://github.com/richardtallent/vite-plugin-singlefile を元に作った専用のプラグインで、今の開いているファイルをマウントする単一HTMLとして吐き出し、それを vscode の webview の HTML として埋め込んで開いている。

https://code.visualstudio.com/api/extension-guides/webview

作ってて困った点として、vite を vscode 拡張の権限で動かそうとすると、 sosukesuzuki も困っていた vm.Script 内では dynamic import が使えない問題に直面した。

https://speakerdeck.com/sosukesuzuki/prettier-3-dot-0-no-vscode-kuo-zhang-dui-ying-niokeruji-shu-de-nayi-si-jue-ding-vscode-kuo-zhang-de-dynamic-import-gadong-kanai?slide=26

なので、vscode-prettier と同じく worker_threads 内で vite を動かすようにするとなんとか動いた。

今後

もうちょっと自分で使い込んで、本当に必要なパターンを発見したら vscode marketplace に公開したい。

今現在、前提が多くてコードを読みながらでないと使える気がしない。postcss の実行パスの関係で部分的に絶対パスでないといけないとか、dynamic import 周りが動かないとか...

実のところ、これは単独で使うものというより chatgpt に生成させたコードをプレビューするために作っていたので、そのためのパーツになると思う。

Discussion