VSCode拡張機能 CodeZoo
vscode-pets
vscode-petsのようなVScodeの拡張機能を作成したい。
【拡張機能の要件】
- VScode内で猫を育成する。
- 3段階に成長する
- 夜は寝る。
- 夜に起こすと、好感度が下がる
- 最初はなついていない
- 餌やり、おもちゃで遊ぶことを複数回繰り返すと好感度が上がりなつく
- 育成と作業の連動: VS Codeでのタスク(例えばコードのコンパイル、一定時間の作業など)を完了するたびに、リワードを猫に与えられる仕組み。
- 音楽や効果音: 餌をあげる音や猫の鳴き声、遊ぶときの効果音を追加。癒し効果がアップ。
- 健康ステータス追加: 猫に「満腹度」「エネルギー」「ストレス」などのステータスを導入。プレイヤーはバランスを取るために餌をあげたり、遊んだり、休ませたりする必要がある。ステータスが低下すると猫が病気になるリスクを追加。
- タイムイベントを管理: システムの時間に基づき、夜(例えば22時~6時)になると猫が自動的に寝るように設定。
- UIの作成: WebviewまたはStatusBarを使って猫を表示する
- ステート管理: データを永続化(例: VS Codeのグローバルステート)し、拡張機能を再起動しても猫の状態が保持されるようにする。
- デバッグ: 各機能をテストし、挙動が期待通りか確認。
vscodeのGlobal State
ExtensionContext.workspaceStateでキー/値のペアを書き込み可能
// on activate
const versionKey = 'shown.version';
context.globalState.setKeysForSync([versionKey]);
// later on show page
const currentVersion = context.extension.packageJSON.version;
const lastVersionShown = context.globalState.get(versionKey);
if (isHigher(currentVersion, lastVersionShown)) {
context.globalState.update(versionKey, currentVersion);
}
export type CAT_STATUS {
stage: "kitten" | "young" | "adult"; // 子猫, 若猫, 大人猫
affection: number; // 好感度
awake: boolean; // 現在の時間(昼/夜)
}
一定の条件で好感度を設定。
function growCat(cat: Cat): void {
if (cat.stage === "kitten" && cat.affection >= 10) {
cat.stage = "young";
} else if (cat.stage === "young" && cat.affection >= 20) {
cat.stage = "adult";
}
}
導入手順
Yeoman と VS Code Extension Generatorジェネレータをインストール
npm install -g yo generator-code
yo code // 拡張機能プロジェクトの生成
インストールされたパッケージを確認
➜ ~ npm -g list
/Users/takahashi_masaki/.nodebrew/node/v16.0.0/lib
├── generator-code@1.11.4
├── npm@7.10.0
├── yarn@1.22.19
└── yo@5.0.0
npx yo code
src/extension.tsを主な開発ファイルとして使用
公式ドキュメント
コマンド パレット ( ⇧⌘P ) からコマンドデバッグ: デバッグの開始を実行
初期コード
import * as vscode from "vscode";
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "codezoo" is now active!');
const disposable = vscode.commands.registerCommand(
"codezoo.helloWorld",
() => {
vscode.window.showInformationMessage("Hello World from CodeZoo!");
}
);
context.subscriptions.push(disposable);
}
export function deactivate() {}
VScode拡張機能公開までの流れ
アニメーションの生成
requestAnimationFrame()
WebView API
Webviewはアプリケーション内に埋め込まれたブラウザコンポーネント
- ペットの動きをユーザーの操作に連動。
- ユーザーのマウス移動に反応
- ペットが大きくなる。
WebviewとVS Code APIの間でメッセージ通信
WebviewからVS Codeへのメッセージ送信
const vscode = acquireVsCodeApi();
vscode.postMessage({ type: 'move', direction: 'left' });
逆にVS CodeからWebviewへメッセージを送るには、postMessageメソッドを使用
panel.webview.postMessage({ command: 'movePet', direction: 'right' });
VS Codeの「Run Extension」機能で拡張機能をテスト
vsceツールを使用して拡張機能をパッケージ化し、VS Codeのマーケットプレイスに公開
Github Actions
CodeQL
CodeQLを使用して、コード内の脆弱性とエラーを特定する。Code Scanning (with CodeQL) は、Public リポジトリであれば無料で利用可能。
name: 'CodeQL Advanced'
on:
push:
branches: ['main']
pull_request:
branches: ['main']
schedule:
- cron: '40 15 * * 5'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
security-events: write
packages: read
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: javascript-typescript
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: '/language:${{matrix.language}}'
fail-fast
fail-fastは、matrix 内の job が失敗した時のエラー制御を行うためのオプション
false にすると、job の成功・失敗に関わらず matrix 内のすべての job が完了するまで実行する。false でいずれかの job が失敗した場合でも、ワークフロー全体のステータスは失敗となるため、失敗に気づけないということはない。
Dependabot設定
GitHub Dependabotを使ったアップデート運用。Dependabotとは、プロジェクト内の依存関係に含まれる脆弱性やアップデートの有無を検知して自動でプルリクエストを作成したり、アラートを出すことができるサービスのこと。GitHub組み込みの機能。
▪️ 公式ドキュメント
.github/dependabot.yml
に記述を行い、アップデート可能なパッケージがある場合、Dependabotはこの設定を元に自動的でプルリクエストを作成する。
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
time: '00:00'
open-pull-requests-limit: 10
reviewers:
- MASAKi-cell
assignees:
- MASAKi-cell
commit-message:
prefix: fix
prefix-development: chore
include: scope
ignore: # バージョンアップの除外設定
- dependency-name: '*'
update-types:
- version-update:semver-patch # パッチバージョンは除外
バージョンアップ戦略はZennに記述されている以下が良さそう
- major:Dependabot version updatesの対象外とする:majorのアップデートは情報として流れてきて気づくことも多い。本番運用している場合は、気軽にメジャーを上げられない。
- minor:デフォルトの動作とする(Dependabot version updatesがPRを作成し、自分でマージする):新しい機能が追加されたのであれば、自動マージするのではなく、どのような機能なのか確認した上でマージしたい
- patch:自動的にマージする:バグFIXであれば、中身を1つ1つ知る重要度が低い。脆弱性に対するパッチであれば、速度を優先できる。パッチバージョンの変更はバグ修正がメインでソフトウェアが壊れるリスクは低いため。
除外設定を追加した場合は、除外理由をコメントやコミットメッセージに残す。理由が不明な場合は、除外設定自体の削除がしにくいため。
github Actionsの自動マージ設定を追加する。dependabotsは依存関係のバージョンアップを検知して、プルリクエストを出すことが仕事であるが、依存関係が多いとレビュー、マージが大変であるため、自動マージするフローを組み込む。github cliでマージするコマンドをワークフローに組み込む。
gh pr merge [<number> | <url> | <branch>] [flags]
▪️ 公式ドキュメント
name: dependabot auto mage
on:
pull_request:
branches: ['main']
jobs:
merge:
if: ${{ github.actor == 'dependabot[bot]' }} # Dependabotのプルリクエストのみ許可
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # GitHub CLIのクレデンシャル
steps:
- uses: actions/checkout@v4
- id: meta
uses: dependabot/fetch-metadata@v2
- if: ${{ steps.meta.outputs.update-type == 'version-update:semver-patch' }} # patchを自動マージする
run: |
gh pr review "${GITHUB_HEAD_REF}" --approve
gh pr merge "${GITHUB_HEAD_REF}" --merge --auto
dependabotが起動したワークフローでは、 dependabot/fetch-metadataで依存関係のメタデータが取得できる。update-type
などのバージョンアップの種類が参照可能。patchのみに絞込みを行う。
これ以外に、github actions向けの変更もユーザーに影響がないため自動マージしておく。
tsconfig
ESLint
eslint-config-prettier でコードフォーマットの設定はオフにする。
.eslintrc.json を選ぶメリットはほぼなく、.eslintrc.js または .eslintrc.cjsで記述する。
eslintのFlatConfig
について。。。eslintの設定形式、 v10 では FlatConfig しかサポートしない予定とのこと。
exntendの概念がなくなり、代わりにconfiguration object
と呼ばれる、各設定情報を要素とする配列で格納される。適用するruleが重複した場合は後続のものが適用される。
pluginについてはjavascriptのモジュールとして明示的にimportする必要がある。
これまでは、eslint側がconfig や pluginを独自で設定していた為、パスへの解決を行う必要があった。
ユーザーもlintコマンドを実行しないと、設定したpluginやconfigが有効なものかわからなかった。
latConfig では config や plugin の解決が全て JavaScript のモジュール解決の仕組みに乗りました。ESLint は設定ファイルを評価するだけで依存が全て解決された設定情報を得られるわけです。
FlatConfigは一次元の配列になるため、どれを適用するかは後続のruleだけ確認すればよく、計算が楽。
.mjs拡張子について:CommonJS(CJS) はESM 以前のJSの範囲で実装されており、依存解決するべきところにおいて曖昧さが存在した。ESM は、関数やオブジェクトではなく、新しく「構文(syntax)」を用意することによって、この問題を解決している。
Node.js において、パース時にそのファイルが ESM/CJS どちらなかのかを判別する方法で議論が行われた模様。 → .mjs拡張子を採用。
ブラウザは読み込む前にそのファイルを Script/Module どちらとしてパースするかを判断することができ、.js を維持したまま先に進むことができ、二回パースといったオーバーヘッドもない。
prettier
importの順番を指定する。
➜ codeZoo git:(main) npm run format
> codezoo@0.0.1 format
> prettier --write src
[error] none is not defined
Prettier が .prettierrc や他の Prettier 設定ファイルで無効な設定 (none) を検出している可能性が高いので、設定ファイルを確認する。