/
magnusroot
/
flutter
Обзор
Документация
Войти
/
magnusroot
/
flutter
Код
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
dev/tools/bin/format.dart
287 строк
9 KB
Kate Lovett
Modernize framework lints (#179089)
26 ноя 2025, 04:10
Не верифицирован
26 ноя 2025, 04:10
9d96df2
Код
Авторство
О чём код?
// 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 'dart:io'; import 'package:args/args.dart'; import 'package:path/path.dart' as path; import 'package:process_runner/process_runner.dart'; Future<int> main(List<String> arguments) async { final parser = ArgParser(); parser.addFlag('help', help: 'Print help.', abbr: 'h'); parser.addFlag( 'fix', abbr: 'f', help: 'Instead of just checking for formatting errors, fix them in place.', ); parser.addFlag( 'all-files', abbr: 'a', help: 'Instead of just checking for formatting errors in changed files, ' 'check for them in all files.', ); late final ArgResults options; try { options = parser.parse(arguments); } on FormatException catch (e) { stderr.writeln('ERROR: $e'); _usage(parser, exitCode: 0); } if (options['help'] as bool) { _usage(parser, exitCode: 0); } final File script = File.fromUri(Platform.script).absolute; final Directory flutterRoot = script.parent.parent.parent.parent; final result = (await DartFormatChecker( flutterRoot: flutterRoot, allFiles: options['all-files'] as bool, ).check(fix: options['fix'] as bool)) == 0; exit(result ? 0 : 1); } void _usage(ArgParser parser, {int exitCode = 1}) { stderr.writeln('format.dart [--help] [--fix] [--all-files]'); stderr.writeln(parser.usage); exit(exitCode); } class DartFormatChecker { DartFormatChecker({required this.flutterRoot, required this.allFiles}) : processRunner = ProcessRunner(defaultWorkingDirectory: flutterRoot); final Directory flutterRoot; final bool allFiles; final ProcessRunner processRunner; Future<int> check({required bool fix}) async { final String baseGitRef = await _getDiffBaseRevision(); final List<String> filesToCheck = await _getFileList( types: <String>['*.dart'], allFiles: allFiles, baseGitRef: baseGitRef, ); return _checkFormat(filesToCheck: filesToCheck, fix: fix); } Future<String> _getDiffBaseRevision() async { var upstream = 'upstream'; final String upstreamUrl = await _runGit( <String>['remote', 'get-url', upstream], processRunner, failOk: true, ); if (upstreamUrl.isEmpty) { upstream = 'origin'; } await _runGit(<String>['fetch', upstream, 'main'], processRunner); var result = ''; try { // This is the preferred command to use, but developer checkouts often do // not have a clear fork point, so we fall back to just the regular // merge-base in that case. result = await _runGit(<String>[ 'merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD', ], processRunner); } on ProcessRunnerException { result = await _runGit(<String>['merge-base', 'FETCH_HEAD', 'HEAD'], processRunner); } return result.trim(); } Future<String> _runGit( List<String> args, ProcessRunner processRunner, { bool failOk = false, }) async { final ProcessRunnerResult result = await processRunner.runProcess(<String>[ 'git', ...args, ], failOk: failOk); return result.stdout; } Future<List<String>> _getFileList({ required List<String> types, required bool allFiles, required String baseGitRef, }) async { String output; if (allFiles) { output = await _runGit(<String>['ls-files', '--', ...types], processRunner); } else { output = await _runGit(<String>[ 'diff', '-U0', '--no-color', '--diff-filter=d', '--name-only', baseGitRef, '--', ...types, ], processRunner); } return output .split('\n') .where((String line) => line.isNotEmpty && !line.startsWith('engine')) .toList(); } Future<int> _checkFormat({required List<String> filesToCheck, required bool fix}) async { final cmd = <String>[ path.join(flutterRoot.path, 'bin', 'dart'), 'format', '--set-exit-if-changed', '--show=none', if (!fix) '--output=show', if (fix) '--output=write', ]; final jobs = <WorkerJob>[]; for (final file in filesToCheck) { jobs.add(WorkerJob(<String>[...cmd, file])); } final dartFmt = ProcessPool( processRunner: processRunner, printReport: _namedReport('dart format'), ); Iterable<WorkerJob> incorrect; final errorJobs = <WorkerJob>[]; if (!fix) { final Stream<WorkerJob> completedJobs = dartFmt.startWorkers(jobs); final diffJobs = <WorkerJob>[]; await for (final WorkerJob completedJob in completedJobs) { if (completedJob.result.exitCode != 0 && completedJob.result.exitCode != 1) { // The formatter had a problem formatting the file. errorJobs.add(completedJob); } else if (completedJob.result.exitCode == 1) { diffJobs.add( WorkerJob(<String>[ 'git', 'diff', '--no-index', '--no-color', '--ignore-cr-at-eol', '--', completedJob.command.last, '-', ], stdinRaw: _codeUnitsAsStream(completedJob.result.stdoutRaw)), ); } } final diffPool = ProcessPool(processRunner: processRunner, printReport: _namedReport('diff')); final List<WorkerJob> completedDiffs = await diffPool.runToCompletion(diffJobs); incorrect = completedDiffs.where((WorkerJob job) => job.result.exitCode != 0); } else { final List<WorkerJob> completedJobs = await dartFmt.runToCompletion(jobs); final List<WorkerJob> incorrectJobs = incorrect = <WorkerJob>[]; for (final job in completedJobs) { if (job.result.exitCode != 0 && job.result.exitCode != 1) { // The formatter had a problem formatting the file. errorJobs.add(job); } else if (job.result.exitCode == 1) { incorrectJobs.add(job); } } } _clearOutput(); if (incorrect.isNotEmpty) { final bool plural = incorrect.length > 1; if (fix) { stdout.writeln( 'Fixing ${incorrect.length} dart file${plural ? 's' : ''}' ' which ${plural ? 'were' : 'was'} formatted incorrectly.', ); } else { stderr.writeln( 'Found ${incorrect.length} Dart file${plural ? 's' : ''}' ' which ${plural ? 'were' : 'was'} formatted incorrectly.', ); final String fileList = incorrect .map((WorkerJob job) => job.command[job.command.length - 2]) .join(' '); stdout.writeln(); stdout.writeln('To fix, run `dart format $fileList` or:'); stdout.writeln(); stdout.writeln('git apply <<DONE'); for (final job in incorrect) { stdout.write( job.result.stdout .replaceFirst('b/-', 'b/${job.command[job.command.length - 2]}') .replaceFirst('b/-', 'b/${job.command[job.command.length - 2]}') .replaceFirst( RegExp('\\+Formatted \\d+ files? \\(\\d+ changed\\) in \\d+.\\d+ seconds.\n'), '', ), ); } stdout.writeln('DONE'); stdout.writeln(); } _printErrorJobs(errorJobs); } else if (errorJobs.isNotEmpty) { _printErrorJobs(errorJobs); } else { stdout.writeln('All dart files formatted correctly.'); } return fix ? errorJobs.length : (incorrect.length + errorJobs.length); } void _printErrorJobs(List<WorkerJob> errorJobs) { if (errorJobs.isNotEmpty) { final bool plural = errorJobs.length > 1; stderr.writeln( 'The formatter failed to run on ${errorJobs.length} Dart file${plural ? 's' : ''}.', ); stdout.writeln(); for (final job in errorJobs) { stdout.writeln('--> ${job.command.last} produced the following error:'); stdout.write(job.result.stderr); stdout.writeln(); } } } } ProcessPoolProgressReporter _namedReport(String name) { return (int total, int completed, int inProgress, int pending, int failed) { final String percent = total == 0 ? '100' : ((100 * completed) ~/ total).toString().padLeft(3); final String completedStr = completed.toString().padLeft(3); final String totalStr = total.toString().padRight(3); final String inProgressStr = inProgress.toString().padLeft(2); final String pendingStr = pending.toString().padLeft(3); final String failedStr = failed.toString().padLeft(3); stdout.write( '$name Jobs: $percent% done, ' '$completedStr/$totalStr completed, ' '$inProgressStr in progress, ' '$pendingStr pending, ' '$failedStr failed.${' ' * 20}\r', ); }; } void _clearOutput() { stdout.write('\r${' ' * 100}\r'); } Stream<List<int>> _codeUnitsAsStream(List<int>? input) async* { if (input != null) { yield input; } }