Prettierの境界線を突破せよ:独自DSLを「美しく整える」プラグイン開発の神髄
こんにちは。開発環境の深淵を覗き込み、日々の退屈な作業を自動化することに情熱を燃やすエンジニアの皆さん。
私たちは普段、ESLintやPrettierの恩恵を当たり前のように受けています。「保存するだけでコードが整う」という体験は、もはや現代開発の基本的人権とも言えます。しかし、皆さんのプロジェクトに「独自のDSL(ドメイン固有言語)」や「レガシーな設定ファイル」「社内独自のテンプレート記法」が登場した瞬間、その魔法は解けてしまいます。
「ここだけ手動で直す」という苦痛を終わらせる方法。今日は、Prettierのプラグインを自作し、未知の言語をフォーマットする「神の領域」へとあなたを招待します。
—
1. なぜ「パーサー」と「プリンタ」の分離が重要なのか
Prettierのアーキテクチャは、非常に洗練された「変換パイプライン」です。自作プラグインを作る際、以下の3つのステップを理解することが唯一の道標となります。
1. Parse(解析): ソースコードを読み込み、構造化された「AST(抽象構文木)」に変換する。
2. Print(出力): ASTを巡回し、Prettierの「Doc(レイアウト用の中間表現)」へ変換する。
3. Format(整形): Docをもとに、画面幅を考慮して最終的な文字列を構築する。
つまり、あなたがやるべきことは「コードを文字列としていじる」ことではなく、「コードを論理構造(AST)として理解し、それを再構成するルールを書く」ことなのです。
—
2. 準備:最小構成のプラグイン構造
まずはプラグインの雛形を作成しましょう。`package.json` での定義が、Prettierに「この拡張子は俺が担当する」と宣言する鍵になります。
{
“name”: “prettier-plugin-my-dsl”,
“version”: “1.0.0”,
“main”: “index.js”,
“devDependencies”: {
“prettier”: “^3.0.0”
}
}
次に、エントリーポイントとなる `index.js` です。ここでPrettierに「言語の定義」と「パーサー」を教え込みます。
// index.js
const { parsers } = require(“./parser”);
const { printers } = require(“./printer”);
module.exports = {
languages: [{
name: “MyDSL”,
parsers: [“my-dsl-parser”], // パーサーの名前を登録
extensions: [“.dsl”], // .dsl ファイルを対象にする
}],
parsers: {
“my-dsl-parser”: parsers.myParser,
},
printers: {
“my-dsl-printer”: printers.myPrinter,
}
};
—
3. ASTの構築:パーサーの実装(心臓部)
ここでは、例えば `key = value` という単純なDSLをパースする想定をしましょう。AST構築には `acorn` や `babel` のパーサーを流用するか、単純なものなら正規表現や字句解析器を自作します。
// parser.js
exports.parsers = {
myParser: {
parse: (text) => {
// 本来は本格的なパーサーライブラリを使うべきですが、
// ここではロジックを理解するために簡易的にオブジェクトを生成します。
return {
type: “Root”,
body: text.trim().split(“\n”).map(line => {
const [key, value] = line.split(“=”);
return { type: “Assignment”, key: key.trim(), value: value.trim() };
})
};
},
astFormat: “my-dsl-printer”, // どのプリンタを使うか紐付ける
locStart: () => 0,
locEnd: () => 0,
}
};
—
4. Docの生成:プリンタの実装(美学の具現化)
ここが最も面白い場所です。Prettierの `builders` を使って、ASTを「どう表示するか」の指示書(Doc)を作成します。
// printer.js
const { builders } = require(“prettier/doc”);
const { concat, group, indent, hardline } = builders;
exports.printers = {
myPrinter: {
print: (path, options, print) => {
const node = path.getValue();
switch (node.type) {
case “Root”:
// 各行を改行で結合する
return group(path.map(print, “body”));
case “Assignment”:
// 「キー = 値」という形式で美しく整形
return concat([node.key, ” = “, node.value, hardline]);
default:
throw new Error(`Unknown node type: ${node.type}`);
}
}
}
};
—
5. 動作確認:現場で使える検証フロー
開発中のプラグインを試すには、`npm link` を使うのが最強です。
1. リンク作成: プラグインディレクトリで `npm link` を実行。
2. 対象プロジェクトで適用: 開発中のプロジェクトで `npm link prettier-plugin-my-dsl`。
3. 実行: `npx prettier –write “src//.dsl”`。
これで、あなたの独自言語がPrettierのエンジンを通り、設定したルール通りに美しく整形されるはずです。
—
アーキテクトからのアドバイス:なぜこれをするのか?
正直に言えば、パーサーを自作するのは骨が折れる作業です。しかし、「チーム全員が数秒間、構文の細部を気にする必要がなくなる」という投資対効果は、規模が大きくなればなるほど指数関数的に増大します。
人は「自分のコードが汚いこと」を気にするよりも、「フォーマットが統一されていないことによる脳の負荷」に無意識のストレスを感じています。そのストレスをコードの解析という技術で解決する。これこそが、エンジニアリングの真髄ではないでしょうか。
さあ、あなたのプロジェクトに眠る「手動で直しているあのコード」を、Prettierの世界へ引きずり出してください。最初の一歩を踏み出せば、その先には「誰もが快適にコードを書ける環境」という報酬が待っています。
何か詰まったら、いつでもASTを覗き込んでみてください。木構造の中に、解決の糸口は必ずあります。