最近、この効果を実現できる Prettier プラグインを作成しました。
同僚がコードを保存するたびに、インポート ステートメントの順序がランダムに変更されます。
しかし、よりきれいな構成ファイルには何も見つかりませんでした。
だからあなたは唖然とするでしょう。
では、この同僚はどのようにして、より魅力的なプラグインを見つけたのでしょうか?
Pretier の仕組み
フロントエンド コンパイル ツールはすべてソース コードからソース コードへの変換であるため、解析、変換、生成の 3 つの手順があります。
parse はソース コード文字列を AST オブジェクト ツリーに変換すること、transform は AST を追加、削除、変更すること、generate (または printer) は変換された AST をターゲット コードに再帰的に出力することです。
Prettier は実際にはコンパイルに基づいて実装されますが、中間の変換は行わず、単に解析して出力 (生成とも呼ばれます) するため、次の 2 つのステップに分けられます。
その主なフォーマット機能は、印刷フェーズで実行されます。
全体のプロセスは比較的単純ですが、なぜこれほど多くの言語をサポートしているのでしょうか?
もちろん、各言語には独自のパーサーとプリンターがあります。
たとえば、次のパーサーが組み込まれています。
ts、js、css、scss、html などはすべてサポートされています。これは、サフィックスが異なれば、異なるパーサーとプリンターが有効になるためです。
さらに、プラグインをサポートしているため、よりきれいなプラグインを使用して、任意の言語でフォーマットを実現できます。
プラグインは当然、サフィックス名、使用するパーサーとプリンターを指定するファイルであると考えるのは簡単なので、次の形式になります。
実際のプラグイン、nginx 構成ファイルをフォーマットする prettier プラグイン prettier-plugin-nginx を見てみましょう。
言語部分は、言語の名前、使用するファイル拡張子、および使用するパーサーを指定することです。
次に、パーサー部分は、文字列から AST への解析を実装することです。
プリンター部分は、AST をコードに出力することです。
もちろん、prettier プラグインのプリンターは文字列を直接出力するのではなく、doc 形式で出力します。これは、prettier がフォーマット制御の別のレイヤーを実行するのに便利です。
つまり、新しい言語のフォーマットを拡張したい場合は、パーサーとプリンターを実装するだけで済みます。
しかし、インポートを変更する以前のプラグインは新しい言語ではなく、js/ts コードではありませんか? よりきれいなプラグインを作成するには?
実際、パーサーはプリプロセッサーも指定できます。
解析前にコンテンツにいくつかの変更を加えます。
したがって、よりきれいなプロセス全体は次のようになります。
次に、よりきれいなプラグインを作成し、js/ts/vue/flow コードで同じ前処理を行います。インポートをランダムに中断する効果を実現できませんか?
書きましょう:
prettier のデフォルトのバベルと typescript パーサーを変更するだけです。
他の構成は変更されません。前処理部分を変更するだけです。
const babelParsers = require("prettier/parser-babel").parsers;
const typescriptParsers = require("prettier/parser-typescript").parsers;
function myPreprocessor(code, options) {
return code + 'guangguangguang';
}
module.exports = {
parsers: {
babel: {
...babelParsers.babel,
preprocess: myPreprocessor,
},
typescript: {
...typescriptParsers.typescript,
preprocess: myPreprocessor,
},
},
};
复制代码
コードの後に guangguangguang を追加しました。
prettier 構成ファイルにこのプラグインを導入します。
次に、よりきれいに実行します。
私たちの最初のきれいなプラグインが動作します!
js と ts に加えて、vue ファイルでも有効になります。
これは、vue の sfc を解析するときに、スクリプト部分が引き続き babel または tsc を使用するためです。
もちろん、通常は、保存時に自動的に prettyer を呼び出してフォーマットするように vscode を構成します。
これには、よりきれいなプラグインをインストールする必要があります。
次に、ドキュメントに従って設定を構成します。
次のように一致させるだけです:
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
复制代码
次に、保存するたびに自動的にきれいにフォーマットされます。
次に、インポートをシャッフルする機能の実装を開始しました。
インポートのコードを見つけて、いくつかの変更を加えるには、babel の api を渡すことを考えるのが自然です。
したがって、次のように記述できます。
最初にこれらのパッケージをインポートします。
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generate = require("@babel/generator").default;
const types = require("@babel/types");
const _ = require("lodash");
复制代码
パッケージ parser、traverse、および generate は理解しやすく、babel コンパイルの 3 つのステップに対応しています。
types パッケージは、AST の作成に使用されます。
一部のパッケージは esm によってエクスポートされるため、それらを commonjs にインポートするには .default 属性が必要です。
次に、いくつかのユーティリティ関数である lodash を紹介します。
最初のステップは、parser.parse を呼び出してコードを AST に変換することです。
function myPreprocessor(code, options) {
const ast = parser.parse(code, {
plugins: ["typescript", "jsx"],
sourceType: "module",
});
}
复制代码
ts および jsx コードを解析する場合は、typescript および jsx プラグインをそれぞれ指定する必要があります。
sourceType is module は、インポートまたはエクスポートを伴うモジュール コードであることを意味します。
2 番目のステップは、インポート ノードを見つけることです。
const importNodes = [];
traverse(ast, {
ImportDeclaration(path) {
importNodes.push(_.clone(path.node));
path.remove();
}
});
复制代码
import ステートメントの処理を宣言して、AST をウォークします。
astexplorer.netで 視覚化できるAST の特定のコードは次のとおりです 。
lodash のクローン機能を使用して AST ノードをコピーし、配列に配置します。
次に、元の AST のインポート ノードを削除します。
3 番目のステップは、インポート ノードをソートすることです。
このステップでは、lodash のシャッフル機能を使用します。
const newImports = _.shuffle(importNodes);
复制代码
4 番目のステップは、ターゲット コードを出力することです。
AST を変更した後、それをターゲット コードとして出力するだけですが、コードには 2 つの部分があり、別々に生成されてから結合されます。
const newAST = types.file({
type: "Program",
body: newImports,
});
const newCode = generate(newAST).code +
"\n" +
generate(ast, {
retainLines: true,
}).code;
复制代码
import ステートメントは、 @babel/types パッケージの API で作成されたファイルのレイヤーのルート ノードをラップする必要があります。
生成時にretainLinesをtrue、つまり印刷時にソースコードに保持する行数に追加することで、印刷後に行数が変わらないようにすることができます。
これで、インポートの順序をランダムにシャッフルする、よりきれいなプラグインが完成しました。
完全なコードは次のとおりです。
const babelParsers = require("prettier/parser-babel").parsers;
const typescriptParsers = require("prettier/parser-typescript").parsers;
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generate = require("@babel/generator").default;
const types = require("@babel/types");
const _ = require("lodash");
function myPreprocessor(code, options) {
const ast = parser.parse(code, {
plugins: ["typescript", "jsx"],
sourceType: "module",
});
const importNodes = [];
traverse(ast, {
ImportDeclaration(path) {
importNodes.push(_.clone(path.node));
path.remove();
},
});
const newImports = _.shuffle(importNodes);
const newAST = types.file({
type: "Program",
body: newImports,
});
const newCode = generate(newAST).code +
"\n" +
generate(ast, {
retainLines: true,
}).code;
return newCode;
}
module.exports = {
parsers: {
babel: {
...babelParsers.babel,
preprocess: myPreprocessor,
},
typescript: {
...typescriptParsers.typescript,
preprocess: myPreprocessor,
},
},
};
复制代码
試してみよう。
js/ts ファイル内:
vue ファイルで:
それはすべて働いています!(きれいなプラグインはキャッシュがあるので、効かない場合は一度閉じてエディタを開き直してください)
これまでのところ、私たちの同僚があなたにヒットするプラグインを発見し、完成しました!
何人かの学生が言ったが、それは設定ファイルで導入されるだろう、これはあまりにも明白です。
実際にはありません。デフォルトでは、prettier はすべての prettier-plugin-xx または @xxx/prettier-plugin-yy プラグインを node_modules の下にロードします。手動でプラグインを指定する必要はありません。これはローカル開発にのみ必要です。
たとえば、コミュニティには、インポートの並べ替え用の prettier-plugin-sort-import というプラグインがあります。
自分でインポートせずに直接構成できます。
したがって、インポートを中断する prettier プラグインの依存関係をインストールする限り、prettier は自動的に適用され、同僚が package.json を見ずにそれを見つけることは困難です。
要約する
Prettier はコンパイル技術に基づいて実装されています. フロントエンドのコンパイルは、prettier と同様に解析、変換、生成の 3 ステップのプロセスですが、中間の変換は必要ありません。
パーサーとプリンターのみが含まれていますが、多くの言語をサポートしています。各言語には、独自のパーサーとプリンターがあります。
新しい言語をサポートするフォーマット済みのよりきれいなプラグインを作成するには、言語、パーサー、およびプリトナー構成をエクスポートするファイルのみが必要です。
- 言語部分は、言語の名前、ファイル拡張子、使用するパーサーなどを指定します。
- パーサー部分は文字列からASTへのパースを実現し、前処理関数preprocessも指定できます。
- printers 部分は AST から doc への印刷を実現します. doc は prettier の中間フォーマットであり、prettier が統一されたフォーマット制御の別のレイヤーを実行し、文字列として出力するのに便利です
今日書いたよりきれいなプラグインは新しい言語をサポートしていないため、コードの前処理には preprocess のみを使用し、コードのインポートの処理には babel API を使用します。
したがって、babel プラグインを知っている場合は、よりきれいなプラグインを作成して js/ts を前処理します. 同様に、postcss、posthtml などを使用して、コードをフォーマットするときに css、scss、less、html などを前処理することもできます。カスタムロジック。
最後に、この記事のよりきれいなプラグインのケースは、学習のためのものであり、このプラグインをプロジェクトに導入することはお勧めしません。