Rust製のコードフォーマッターdprintの導入手順
dprintとは
公式ドキュメントには以下のように書かれていました
Formatting is very fast.
Plugins are WebAssembly files that may be imported from a URL or file path.
Official plugins are highly configurable enabling you to auto format code closer to your preferences.
Configuration may be imported from URLs for reuse and sharing.
これを含めるとdprintには以下のような特徴があります
- Rust製のコードフォーマッター
- 高速に動作する
- 細かいフォーマットの設定が可能
- JavaScript, Typescript, JSON, Markdown, TOMAL, Dockerfileなどのファイルが対象
インストール
様々な導入方法がありますが、ここではnpmパッケージを使用します
他のインストール方法は公式ドキュメントに載っています
npm i dprint --save-dev
or
yarn add -D dprint
or
pnpm add -D dprint
scriptに追加します
{
...
"scripts": {
...
+ "fmt": "pnpm dprint fmt",
},
初期設定
dprint init
を実行します
pnpm dprint init
spaceで選択をし、Enterで決定します
ここでは、typescriptとjsonを選択しました
> pnpm dprint init
Select plugins (use the spacebar to select/deselect and then press enter when finished):
> [x] dprint-plugin-typescript
[x] dprint-plugin-json
[ ] dprint-plugin-markdown
[ ] dprint-plugin-toml
[ ] dprint-plugin-dockerfile
設定ファイルであるdprint.json
がプロジェクト直下に生成されます
{
"typescript": {
},
"json": {
},
"excludes": [
"**/node_modules",
"**/*-lock.json"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.87.1.wasm",
"https://plugins.dprint.dev/json-0.17.4.wasm"
]
}
私のプロジェクトがNext.jsだったので.next
内のファイルを除外します
{
"typescript": {
},
"json": {
},
"excludes": [
"**/node_modules",
"**/*-lock.json",
+ "**/.next"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.87.1.wasm",
"https://plugins.dprint.dev/json-0.17.4.wasm"
]
}
動かしてみる
dprint fmt
で対象ファイルのフォーマットを実行します
pnpm dprint fmt
フォーマットされました。体感すごい速かったです🎉
計測している方の記事では、小規模のプロジェクトでPrettierの2倍の速度で動いているようです
速いですね
vscodeで保存時にフォーマットさせる
- vscodeの拡張機能を入れます
- .vscode/settings.jsonにdprintの設定を追加します
これで、保存した時にフォーマットがされるようになります
{
"editor.defaultFormatter": "dprint.dprint",
"editor.formatOnSave": true
}
Ctrl+Sで保存すると、ちゃんとフォーマットされました🎉
- .vscode/extensions.jsonに追加
vscodeの拡張機能を他の人にオススメする
{
"recommendations": [
"dprint.dprint"
]
}
フォーマットの細かい設定
設定ファイルは型が効くので嬉しいです
playgroundにTypescriptの設定一覧が載っています
フォーマットの設定を細かく設定することが出来ます
Typescriptの設定項目一覧
{
"lineWidth": 80,
"indentWidth": 2,
"useTabs": false,
"semiColons": "prefer",
"quoteStyle": "alwaysDouble",
"quoteProps": "preserve",
"newLineKind": "lf",
"useBraces": "whenNotSingleLine",
"bracePosition": "sameLineUnlessHanging",
"singleBodyPosition": "maintain",
"nextControlFlowPosition": "sameLine",
"trailingCommas": "onlyMultiLine",
"operatorPosition": "nextLine",
"preferHanging": false,
"preferSingleLine": false,
"arrowFunction.useParentheses": "maintain",
"binaryExpression.linePerExpression": false,
"jsx.bracketPosition": "nextLine",
"jsx.forceNewLinesSurroundingContent": false,
"jsx.quoteStyle": "preferDouble",
"jsx.multiLineParens": "prefer",
"memberExpression.linePerExpression": false,
"typeLiteral.separatorKind": "semiColon",
"enumDeclaration.memberSpacing": "maintain",
"spaceAround": false,
"spaceSurroundingProperties": true,
"objectExpression.spaceSurroundingProperties": true,
"objectPattern.spaceSurroundingProperties": true,
"typeLiteral.spaceSurroundingProperties": true,
"binaryExpression.spaceSurroundingBitwiseAndArithmeticOperator": true,
"commentLine.forceSpaceAfterSlashes": true,
"constructor.spaceBeforeParentheses": false,
"constructorType.spaceAfterNewKeyword": false,
"constructSignature.spaceAfterNewKeyword": false,
"doWhileStatement.spaceAfterWhileKeyword": true,
"exportDeclaration.spaceSurroundingNamedExports": true,
"forInStatement.spaceAfterForKeyword": true,
"forOfStatement.spaceAfterForKeyword": true,
"forStatement.spaceAfterForKeyword": true,
"forStatement.spaceAfterSemiColons": true,
"functionDeclaration.spaceBeforeParentheses": false,
"functionExpression.spaceBeforeParentheses": false,
"functionExpression.spaceAfterFunctionKeyword": false,
"getAccessor.spaceBeforeParentheses": false,
"ifStatement.spaceAfterIfKeyword": true,
"importDeclaration.spaceSurroundingNamedImports": true,
"jsxSelfClosingElement.spaceBeforeSlash": true,
"jsxExpressionContainer.spaceSurroundingExpression": false,
"method.spaceBeforeParentheses": false,
"setAccessor.spaceBeforeParentheses": false,
"taggedTemplate.spaceBeforeLiteral": false,
"typeAnnotation.spaceBeforeColon": false,
"typeAssertion.spaceBeforeExpression": true,
"whileStatement.spaceAfterWhileKeyword": true,
"ignoreNodeCommentText": "dprint-ignore",
"ignoreFileCommentText": "dprint-ignore-file"
}
今までPrettierを使っていたので、以下のPrettierの設定に近づけてカスタマイズしてみます
{
"semi": false,
"singleQuote": true,
"jsxSingleQuote": true,
"trailingComma": "all",
"arrowParens": "avoid"
}
この時のdprint.jsonは以下になりました(コメントは消してください)
{
"lineWidth": 80, // 折り返しの横幅
"indentWidth": 2, // インデントの幅
"typescript": {
"quoteStyle": "alwaysSingle", // シングルクォーテーションを使用
"semiColons": "asi", // セミコロンを使わない
"arrowFunction.useParentheses": "force", // アロー関数の引数が1つの時()を省略しない
"binaryExpression.linePerExpression": false, // 二項演算で折り返すとき、全ての項を折り返す
},
"json": {},
"excludes": [
"**/node_modules",
"**/*-lock.json",
"**/.next"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.87.1.wasm",
"https://plugins.dprint.dev/json-0.17.4.wasm"
]
}
他にも良さそうな設定があったので、プロジェクトに合わせて設定しても良いと思います
useBraces
-
説明: 単一行ステートメントのブレース(中括弧)の使用を指定します。
-
例:
"useBraces": "whenNotSingleLine"(規定値)
if (true) console.log('Inside if block')
"useBraces": "always"
if (true) { console.log('Inside if block') }
bracePosition
-
説明: ブレースの位置を指定します。
-
例:
"bracePosition": "sameLineUnlessHanging"(規定値)
function myFunction() { return "Indented text."; }
"bracePosition": "nextLine"
function myFunction() { return "Indented text."; }
singleBodyPosition
-
説明: 単一行関数のボディの位置を指定します。
-
例:
"singleBodyPosition": "maintain"(規定)
if (true) console.log('Inside if block')
"singleBodyPosition": "nextLine"
if (true) { console.log('Inside if block') }
trailingCommas
-
説明: 配列やオブジェクトリテラルの最後の要素の後にカンマを使用するかどうかを指定します。"onlyMultiLine"を指定すると、複数行の場合のみカンマが挿入されます。
-
例:
"trailingCommas": "onlyMultiLine"(規定値)
const obj = { 'key': 'value', 'a-2': '', '0-22': '', }
"trailingCommas": ”never"
const obj = { 'key': 'value', 'a-2': '', '0-22': '' }
jsx.forceNewLinesSurroundingContent
-
説明: JSX要素の内容の周りに新しい行を強制的に挿入するかどうかを指定します。trueに設定すると、内容の周りに新しい行が挿入されます。
-
例:
"jsx.forceNewLinesSurroundingContent": false(規定値)
const element = <div><p>Hello, world!</p></div>; const element1 = ( <div> <p>Hello, world!</p> </div> )
"jsx.forceNewLinesSurroundingContent": true
const element = ( <div> <p> Hello, world! </p> </div> ) const element1 = ( <div> <p> Hello, world! </p> </div> )
参考
Discussion