はじめに
Flutter Advent Calendar 2021の記事です。
追記
flutter formatコマンドは削除されました。現在はdart formatを使用する必要があります
記事本編
flutter formatとdart formatの違いを調べてみました。
TL;DR
flutter formatはdart formatのラッパーです。flutter formatは指定VersionのFlutterで動くように良きように設定してくれているのでflutter formatを使っておきましょう。
きっかけ
Effective Dartを読んでいる時に、Dartもformatあるよねなんでflutter formatコマンドがあるんだろうと思って調べました。
何が違うのか
何が違うのか把握するために、それぞれを-h
オプションで実行してみます。
flutter format -h
Idiomatically format Dart source code.
Usage: dart format [options...] <files or directories...>
-h, --help Print this usage information.
-v, --verbose Show all options and flags with --help.
-o, --output Set where to write formatted output.
[json] Print code and selection as JSON.
[none] Discard output.
[show] Print code to terminal.
[write] (default) Overwrite formatted files on disk.
--set-exit-if-changed Return exit code 1 if there are any formatting changes.
--fix Apply all style fixes.
-l, --line-length Wrap lines longer than this.
(defaults to "80")
Run "dart help" to see global options.
dart format -h
Idiomatically format Dart source code.
Usage: dart format [options...] <files or directories...>
-h, --help Print this usage information.
-v, --verbose Show all options and flags with --help.
-o, --output Set where to write formatted output.
[json] Print code and selection as JSON.
[none] Discard output.
[show] Print code to terminal.
[write] (default) Overwrite formatted files on disk.
--set-exit-if-changed Return exit code 1 if there are any formatting changes.
--fix Apply all style fixes.
-l, --line-length Wrap lines longer than this.
(defaults to "80")
Run "dart help" to see global options.
結果は一言一句全く同じでした。
どうやらCLIのインターフェース上からは回答を得られそうにないので、直接コードをみてみます。
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/args.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../globals.dart' as globals;
import '../runner/flutter_command.dart';
class FormatCommand extends FlutterCommand {
FormatCommand({required this.verboseHelp});
@override
ArgParser argParser = ArgParser.allowAnything();
final bool verboseHelp;
@override
final String name = 'format';
@override
List<String> get aliases => const <String>['dartfmt'];
@override
final String description = 'Format one or more Dart files.';
@override
String get category => FlutterCommandCategory.project;
@override
String get invocation => '${runner?.executableName} $name <one or more paths>';
@override
Future<FlutterCommandResult> runCommand() async {
final String dartBinary = globals.artifacts!.getHostArtifact(HostArtifact.engineDartBinary).path;
final List<String> command = <String>[
dartBinary,
'format',
];
final List<String> rest = argResults?.rest ?? <String>[];
if (rest.isEmpty) {
globals.printError(
'No files specified to be formatted.'
);
command.add('-h');
} else {
command.addAll(<String>[
for (String arg in rest)
if (arg == '--dry-run' || arg == '-n')
'--output=none'
else
arg
]);
}
final int result = await globals.processUtils.stream(command);
if (result != 0) {
throwToolExit('Formatting failed: $result', exitCode: result);
}
return FlutterCommandResult.success();
}
}
実行部分のコードを見る限り、追加のオプションなども定義されていますが、formatを実行するという点においては同じもののように見えます。
それではなぜわざわざ処理をラップするためだけのコードが書かれたのでしょうか。
答えは過去のログを遡っていくことで見つけられました。
以下引用
In order to reduce occasions where a user runs the wrong version of a utility, I think we can (and should) wrap utilities provided by the Dart SDK with our own flutter command.
For example, I would like our users to try formatting. The instructions to ensure they are using the right version of the formatter are fairly complicated, complete with a bunch of "check this path" and "check this version".
Instead, I would like to say, "Run flutter format foo.dart" and have the flutter command use dartfmt that we bundle.
(we might want to do this for pub, too, but that's probably a different issue)
要はformatterの実行時に、ユーザ側がバージョニングなど余計なところで引っかからないように、最初から期待するVersionのformatterをバンドルしてくれているようです。
上記のような結果から、気にすることなくflutter format
を使っていこうと思いました。
余談
以下にはコードを読んでみて知った余談を書いています。
隠しオプション
意図して隠しているわけではなさそうですが、--output=none
のオプションは、--dry-run
か-n
を利用することでも実行できます。
経緯はここら辺に書かれています。後述しますが、内部で利用しているformatterを変更する際、既存の処理を壊さないように、workaroundとして実装されたようです。
隠しエイリアス
コマンドにはエイリアスとしてdartfmt
が定義されているため、flutter dartfmt
でも同様の処理を実行できます。
上記のような実装になっている背景としては、実装当初はdart format
を利用するのではなく、dartfmt
を利用していたためです。
そして下記のように、dartfmt
が廃止され、dart format
が推奨になったことで、内部の実装もdart format
にリプレイスされました。
前述の隠しオプションについてもこの対応の煽りを受けた結果の事象のようです。
ちなみにこのエイリアスが完全に利用されていないかまでは確認していないので、もしかしたらまだ利用されている箇所もあるかもしれません。
最後に
他にもアドベントカレンダーで色々記事書いたので興味あれば読んでみていただけるありがたいです。。