VS CodeのMarkdownプレビューで、%文字%{色}という記法を使って文字色を変更できる拡張機能「Markdown MojiColor」の1.2.0を公開しました。
今回のリリースでは、太字・斜体の自動着色、YAML Front Matterによる文書単位の設定、追加の色辞書、色付き範囲内のMarkdown記法などを追加しています。
しかし、開発の中心になったのは機能追加だけではありません。YAMLを入力している途中に例外が発生し、Markdownのプレビューだけでなく、document link、folding range、code action、document symbolといった言語機能まで失敗する問題への対処でした。
この記事では、入力途中の不完全なYAMLをどこで受け止め、どこで厳格に検証するかという責務分担から、設定画面の改善、日英ローカライズ、VSIXの内容確認、自動公開までを振り返ります。
1.2.0で追加したもの
1.2.0の主な変更は次のとおりです。
- 太字と斜体の自動着色
- YAML Front Matterの
markdown.mojicolorによる文書単位の上書き - 追加の色辞書JSONファイル
- 色付き範囲内の太字、斜体、リンク、インラインコード
- 設定変更時のVS Code再読み込み案内
- 拡張機能と設定説明の日英ローカライズ
グローバルな自動着色はVS Code設定から指定できます。
{
"markdownMojicolor.styles.bold": "blue",
"markdownMojicolor.styles.italic": "桜色"
}文書ごとに変えたい場合は、Markdownファイルの先頭で指定します。
---
markdown:
mojicolor:
bold: yellow
italic: null
---この例では太字を黄色にし、斜体の自動着色を無効にします。YAMLで指定していない項目はVS Code設定を継承し、%...%{色}による明示的な色指定は自動着色より優先されます。
変更点
1. YAMLキー入力の例外対処
開発中、次のようなエラーが繰り返し出ました。
Request textDocument/documentLink failed.
Request textDocument/foldingRange failed.
Request textDocument/codeAction failed.
Request textDocument/documentSymbol failed.
Request markdown/parse failed with message:
env.markdownMojicolor must be an object完成した設定が間違っているだけなら、原因を直せば済みます。厄介だったのは、本来入力したいbold:へ到達する前のb:やbo:でもエラーになったことです。
エディター上の文書は、保存済みの完成品だけではありません。ユーザーが1文字ずつ入力している間、構文木には空の値、不完全なキー、まだ型が定まらない値が現れます。Markdownの解析がそのたびに走るため、ライブラリへ渡す前の境界で不完全な入力を扱わないと、例外がVS Codeの言語機能全体へ伝播します。
対策1. 正式な設定経路を一本化する
まず整理したのは、設定をどの経路で渡すかです。
Markdown MojiColorが文書設定として扱う正式な場所は、次の構造です。
env.frontmatter.markdown.mojicolor以前の検討中に登場したenv.markdownMojicolorへ変換する旧経路は廃止しました。VS Code拡張側で正式なFront Matter構造へ統一し、markdown-it-mojicolor側には厳格な最終検証を残しています。
ここで重要だったのは、ライブラリのthrowを消さなかったことです。ライブラリ単体で不正なオプションを受け取った場合に例外を出すのは、誤設定を見逃さないための保険になります。一方、エディター統合では入力途中の状態が日常的に発生します。そのため、VS Code拡張が入力境界で安全な値だけを選び、ライブラリには検証済みの設定を渡す構成にしました。
YAML Front Matter
↓
js-yamlで構文解析
↓
VS Code拡張で許可キーと値を検証
↓
env.frontmatter.markdown.mojicolor
↓
markdown-it-mojicolorで最終検証・描画対策2. 入力境界では許可リストと値検証を使う
自動着色で受け付けるキーはboldとitalicだけです。
const STYLE_NAMES = ['bold', 'italic'];Front Matterから得たオブジェクトをそのまま渡さず、この許可リストにある項目だけを新しいオブジェクトへコピーします。未対応キーは描画設定として使用しません。
値は次のように扱います。
- 空でない文字列は、危険な区切り文字や制御文字がない場合だけ採用
nullは、その項目の自動着色を無効にする値として採用- 配列、オブジェクト、不正な文字列は不採用
- YAML自体を解析できない場合は文書設定なしとして扱う
CSSへ渡す色値なので、;、{、}、バックスラッシュ、コメント区切り、制御文字を拒否しています。不正値は描画に使わず、入力途中の文書によってMarkdown解析全体が停止しないようにしました。
また、環境オブジェクトを直接書き換えず、新しいオブジェクトを組み立てています。これにより、ほかのMarkdownプラグインが同じenvに保存した情報を維持しながら、文書間でMojicolorの設定が残ることを防いでいます。
対策3. nullを「編集中」と「無効化」で使い分ける
YAMLでは、値をまだ入力していないキーがnullとして解析されます。
markdown:
mojicolor:この状態は、文書設定そのものをまだ入力している途中です。そのため、mojicolor: nullは「文書設定なし」として扱い、VS Code設定を継承します。
一方、個別項目のnullには明確な意味を持たせています。
markdown:
mojicolor:
bold: nullこちらは太字の自動着色を無効にします。同じnullでも、設定オブジェクト全体と個別項目で役割を分けることで、入力途中の安定性と明示的な無効化を両立できました。
2. VS Code設定を入力欄から編集できるようにする
太字と斜体の色は、以前の設定スキーマではJSONを直接編集する印象が強い状態でした。1.2.0では文字列型・空文字を既定値にし、VS Codeの設定UIへ色名を直接入力できるようにしました。
"markdownMojicolor.styles.bold": {
"type": "string",
"default": ""
}空欄と空白だけの値は自動着色なしへ正規化します。既存のsettings.jsonにnullが残っていても、実行時には自動着色なしとして扱うため、以前の設定との互換性も維持しています。
設定はMarkdownプレビューの初期化時に読み込まれるため、変更時には再読み込みを案内するようにしました。
vscode.workspace.onDidChangeConfiguration(event => {
void recommendReload(event);
});Markdown MojiColorの設定が変わった場合だけ確認メッセージを出し、ユーザーが同意したときだけworkbench.action.reloadWindowを実行します。メッセージ表示中に設定変更が続いても、同じ確認を重複表示しないようPromiseを共有しています。
3. 拡張機能の説明と設定説明を日英対応する
実行中の確認メッセージにはVS Codeのvscode.l10nを使用し、拡張機能マニフェストの説明にはpackage.nls.jsonとpackage.nls.ja.jsonを使用しました。
package.jsonには文章を直接書かず、キーを指定します。
{
"description": "%extension.description%",
"contributes": {
"configuration": {
"properties": {
"markdownMojicolor.styles.bold": {
"markdownDescription": "%markdownMojicolor.styles.bold.markdownDescription%"
}
}
}
}
}英語の既定文をpackage.nls.json、日本語をpackage.nls.ja.jsonへ置きました。これで英語UIでは英語、日本語UIでは日本語の短い説明と設定説明を表示できます。
READMEとCHANGELOGにも日本語と英語を用意しました。READMEの使用例は各言語で入力例を分け、英語版では%yellow%{yellow}のように英語だけで読める例へ揃えています。GitHub Releaseの最新バージョンを表示するShields.ioバッジも追加しました。
テストで確認したこと
VS Code 1.137.0の拡張機能ホストで、最終的に23件のテストを実行しました。
特に重視したのは次のケースです。
- Markdown Clipが渡すFront Matterでも文書単位の設定が反映される
- HTML出力元の
env情報を壊さない b:やbo:など、YAMLキーの入力途中で言語機能を失敗させない- 空キーと個別
nullを意図どおり区別する - 未対応キーと不正値を描画へ使用しない
markdown-it-mojicolor単体の厳格なthrowは維持する- VS Code設定、YAML設定、明示色の優先順位が正しい
- 設定変更時に同意した場合だけ再読み込みする
- 拡張機能と設定説明の日英NLSキーが対応している
検証結果は次のとおりです。
npm ci成功- ESLint成功
- 全23テスト成功
git diff --check成功npm audit --omit=devで実行時依存の脆弱性0件npx --no-install vsce package成功
開発依存を含む監査では推移依存にmoderate 1件、high 1件が報告されましたが、公開したVSIXへ入る実行時依存には該当しないことを分けて確認しました。
まとめ
今回の開発で改めて重要だと感じたのは、エディター統合における入力は常に完成しているわけではない、という点です。
ライブラリ単体では、不正な設定にthrowする厳格さが役立ちます。しかし、ユーザーが文字を入力するたびに解析されるVS Code拡張では、不完全な状態を入力境界で吸収しなければ、無関係に見える言語機能まで止めてしまいます。
1.2.0では、次の分担に落ち着きました。
- VS Code拡張は、入力途中のYAMLから安全な値だけを取り出す
markdown-it-mojicolorは、最終防衛線として厳格な検証を続ける- 不正値は描画に使わない
- 明示的な
nullによる無効化は維持する
さらに、機能が動くだけでなく、設定UI、ローカライズ、README、CHANGELOG、VSIXの中身、公開CLI、CIの成功までをリリース候補として検証しました。
