[ Tool ] Remove use of globals from widget-preview commands (#162522)

This commit is contained in:
Ben Konyi 2025-01-31 14:17:45 -05:00 committed by GitHub
parent 53f2b136db
commit de872dd864
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 148 additions and 69 deletions

View File

@ -246,7 +246,12 @@ List<FlutterCommand> generateCommands({required bool verboseHelp, required bool
verbose: verbose,
nativeAssetsBuilder: globals.nativeAssetsBuilder,
),
WidgetPreviewCommand(),
WidgetPreviewCommand(
logger: globals.logger,
fs: globals.fs,
projectFactory: globals.projectFactory,
cache: globals.cache,
),
UpgradeCommand(verboseHelp: verboseHelp),
SymbolizeCommand(stdio: globals.stdio, fileSystem: globals.fs),
// Development-only commands. These are always hidden,

View File

@ -8,24 +8,37 @@ import 'package:meta/meta.dart';
import '../base/common.dart';
import '../base/deferred_component.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../cache.dart';
import '../convert.dart';
import '../dart/pub.dart';
import '../flutter_manifest.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../runner/flutter_command.dart';
import '../widget_preview/preview_code_generator.dart';
import '../widget_preview/preview_detector.dart';
import 'create_base.dart';
// TODO(bkonyi): use dependency injection instead of global accessors throughout this file.
class WidgetPreviewCommand extends FlutterCommand {
WidgetPreviewCommand() {
addSubcommand(WidgetPreviewStartCommand());
addSubcommand(WidgetPreviewCleanCommand());
WidgetPreviewCommand({
required Logger logger,
required FileSystem fs,
required FlutterProjectFactory projectFactory,
required Cache cache,
}) {
addSubcommand(
WidgetPreviewStartCommand(
logger: logger,
fs: fs,
projectFactory: projectFactory,
cache: cache,
),
);
addSubcommand(
WidgetPreviewCleanCommand(logger: logger, fs: fs, projectFactory: projectFactory),
);
}
@override
@ -46,27 +59,30 @@ class WidgetPreviewCommand extends FlutterCommand {
Future<FlutterCommandResult> runCommand() async => FlutterCommandResult.fail();
}
/// Common utilities for the 'start' and 'clean' commands.
mixin WidgetPreviewSubCommandMixin on FlutterCommand {
abstract base class WidgetPreviewSubCommandBase extends FlutterCommand {
FileSystem get fs;
Logger get logger;
FlutterProjectFactory get projectFactory;
FlutterProject getRootProject() {
final ArgResults results = argResults!;
final Directory projectDir;
if (results.rest case <String>[final String directory]) {
projectDir = globals.fs.directory(directory);
projectDir = fs.directory(directory);
if (!projectDir.existsSync()) {
throwToolExit('Could not find ${projectDir.path}.');
}
} else if (results.rest.length > 1) {
throwToolExit('Only one directory should be provided.');
} else {
projectDir = globals.fs.currentDirectory;
projectDir = fs.currentDirectory;
}
return validateFlutterProjectForPreview(projectDir);
}
FlutterProject validateFlutterProjectForPreview(Directory directory) {
globals.logger.printTrace('Verifying that ${directory.path} is a Flutter project.');
final FlutterProject flutterProject = globals.projectFactory.fromDirectory(directory);
logger.printTrace('Verifying that ${directory.path} is a Flutter project.');
final FlutterProject flutterProject = projectFactory.fromDirectory(directory);
if (!flutterProject.dartTool.existsSync()) {
throwToolExit('${flutterProject.directory.path} is not a valid Flutter project.');
}
@ -74,9 +90,13 @@ mixin WidgetPreviewSubCommandMixin on FlutterCommand {
}
}
class WidgetPreviewStartCommand extends FlutterCommand
with CreateBase, WidgetPreviewSubCommandMixin {
WidgetPreviewStartCommand() {
final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with CreateBase {
WidgetPreviewStartCommand({
required this.logger,
required this.fs,
required this.projectFactory,
required this.cache,
}) {
addPubOptions();
}
@ -86,8 +106,20 @@ class WidgetPreviewStartCommand extends FlutterCommand
@override
String get name => 'start';
@override
final FileSystem fs;
@override
final Logger logger;
@override
final FlutterProjectFactory projectFactory;
final Cache cache;
late final PreviewDetector _previewDetector = PreviewDetector(
logger: globals.logger,
logger: logger,
fs: fs,
onChangeDetected: onChangeDetected,
);
@ -101,9 +133,7 @@ class WidgetPreviewStartCommand extends FlutterCommand
// Check to see if a preview scaffold has already been generated. If not,
// generate one.
if (!widgetPreviewScaffold.existsSync()) {
globals.logger.printStatus(
'Creating widget preview scaffolding at: ${widgetPreviewScaffold.path}',
);
logger.printStatus('Creating widget preview scaffolding at: ${widgetPreviewScaffold.path}');
await generateApp(
<String>['widget_preview_scaffold'],
widgetPreviewScaffold,
@ -112,7 +142,7 @@ class WidgetPreviewStartCommand extends FlutterCommand
projectName: 'widget_preview_scaffold',
titleCaseProjectName: 'Widget Preview Scaffold',
flutterRoot: Cache.flutterRoot!,
dartSdkVersionBounds: '^${globals.cache.dartSdkBuild}',
dartSdkVersionBounds: '^${cache.dartSdkBuild}',
linux: const LocalPlatform().isLinux,
macos: const LocalPlatform().isMacOS,
windows: const LocalPlatform().isWindows,
@ -128,7 +158,7 @@ class WidgetPreviewStartCommand extends FlutterCommand
// FlutterManifest before the scaffold project's pubspec has been generated.
_previewCodeGenerator = PreviewCodeGenerator(
widgetPreviewScaffoldProject: rootProject.widgetPreviewScaffoldProject,
fs: globals.fs,
fs: fs,
);
final PreviewMapping initialPreviews = await _previewDetector.initialize(rootProject.directory);
@ -207,7 +237,7 @@ class WidgetPreviewStartCommand extends FlutterCommand
rootManifest.deferredComponents?.map(transformDeferredComponent).toList();
return widgetPreviewManifest.copyWith(
logger: globals.logger,
logger: logger,
assets: assets,
fonts: fonts,
shaders: shaders,
@ -292,26 +322,37 @@ class WidgetPreviewStartCommand extends FlutterCommand
flutterGenPackageConfigEntry,
);
packageConfig.writeAsStringSync(json.encode(packageConfigJson));
globals.logger.printStatus('Added flutter_gen dependency to $previewPackageConfigPath');
logger.printStatus('Added flutter_gen dependency to $previewPackageConfigPath');
}
}
class WidgetPreviewCleanCommand extends FlutterCommand with WidgetPreviewSubCommandMixin {
final class WidgetPreviewCleanCommand extends WidgetPreviewSubCommandBase {
WidgetPreviewCleanCommand({required this.fs, required this.logger, required this.projectFactory});
@override
String get description => 'Cleans up widget preview state.';
@override
String get name => 'clean';
@override
final FileSystem fs;
@override
final Logger logger;
@override
final FlutterProjectFactory projectFactory;
@override
Future<FlutterCommandResult> runCommand() async {
final Directory widgetPreviewScaffold = getRootProject().widgetPreviewScaffold;
if (widgetPreviewScaffold.existsSync()) {
final String scaffoldPath = widgetPreviewScaffold.path;
globals.logger.printStatus('Deleting widget preview scaffold at $scaffoldPath.');
logger.printStatus('Deleting widget preview scaffold at $scaffoldPath.');
widgetPreviewScaffold.deleteSync(recursive: true);
} else {
globals.logger.printStatus('Nothing to clean up.');
logger.printStatus('Nothing to clean up.');
}
return FlutterCommandResult.success();
}

View File

@ -16,14 +16,14 @@ import 'package:watcher/watcher.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/utils.dart';
import '../globals.dart' as globals;
import 'preview_code_generator.dart';
typedef PreviewMapping = Map<String, List<String>>;
class PreviewDetector {
PreviewDetector({required this.logger, required this.onChangeDetected});
PreviewDetector({required this.fs, required this.logger, required this.onChangeDetected});
final FileSystem fs;
final Logger logger;
final void Function(PreviewMapping) onChangeDetected;
StreamSubscription<WatchEvent>? _fileWatcher;
@ -47,7 +47,7 @@ class PreviewDetector {
}
logger.printStatus('Detected change in $eventPath.');
final PreviewMapping filePreviewsMapping = findPreviewFunctions(
globals.fs.file(Uri.file(event.path)),
fs.file(Uri.file(event.path)),
);
if (filePreviewsMapping.isEmpty && !_pathToPreviews.containsKey(eventPath)) {
// No previews found or removed, nothing to do.

View File

@ -5,6 +5,7 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/widget_preview.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
@ -14,6 +15,7 @@ import 'package:test_api/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
void main() {
group('WidgetPreviewStartCommand', () {
@ -21,15 +23,22 @@ void main() {
late ProcessManager processManager;
late WidgetPreviewStartCommand command;
late FlutterProject rootProject;
late Logger logger;
setUp(() {
fileSystem = MemoryFileSystem.test();
processManager = FakeProcessManager.any();
command = WidgetPreviewStartCommand();
logger = BufferLogger.test();
command = WidgetPreviewStartCommand(
fs: fileSystem,
projectFactory: FakeFlutterProjectFactory(),
logger: logger,
cache: Cache.test(processManager: processManager),
);
rootProject = FakeFlutterProject(
projectRoot: 'some_project',
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
);
});

View File

@ -6,11 +6,16 @@ import 'dart:io' as io show IOOverrides;
import 'package:args/command_runner.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/bot_detector.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/signals.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/widget_preview.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/widget_preview/preview_code_generator.dart';
import '../../src/common.dart';
@ -23,11 +28,19 @@ void main() {
late Directory tempDir;
late LoggingProcessManager loggingProcessManager;
late FakeStdio mockStdio;
late Logger logger;
late FileSystem fs;
late BotDetector botDetector;
late Platform platform;
setUp(() {
loggingProcessManager = LoggingProcessManager();
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_create_test.');
logger = BufferLogger.test();
fs = LocalFileSystem.test(signals: Signals.test());
botDetector = const FakeBotDetector(false);
tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_create_test.');
mockStdio = FakeStdio();
platform = const LocalPlatform();
});
tearDown(() {
@ -35,7 +48,7 @@ void main() {
});
Future<Directory> createRootProject() async {
return globals.fs.directory(await createProject(tempDir, arguments: <String>['--pub']));
return fs.directory(await createProject(tempDir, arguments: <String>['--pub']));
}
Directory widgetPreviewScaffoldFromRootProject({required Directory rootProject}) {
@ -43,7 +56,14 @@ void main() {
}
Future<void> runWidgetPreviewCommand(List<String> arguments) async {
final CommandRunner<void> runner = createTestCommandRunner(WidgetPreviewCommand());
final CommandRunner<void> runner = createTestCommandRunner(
WidgetPreviewCommand(
logger: logger,
fs: fs,
projectFactory: FlutterProjectFactory(logger: logger, fileSystem: fs),
cache: Cache.test(processManager: loggingProcessManager, platform: platform),
),
);
await runner.run(<String>['widget-preview', ...arguments]);
}
@ -57,7 +77,7 @@ void main() {
if (rootProject != null) rootProject.path,
]);
final Directory widgetPreviewScaffoldDir = widgetPreviewScaffoldFromRootProject(
rootProject: rootProject ?? globals.fs.currentDirectory,
rootProject: rootProject ?? fs.currentDirectory,
);
expect(widgetPreviewScaffoldDir, exists);
expect(
@ -69,7 +89,7 @@ void main() {
Future<void> cleanWidgetPreview({required Directory rootProject}) async {
await runWidgetPreviewCommand(<String>['clean', rootProject.path]);
expect(
globals.fs
fs
.directory(rootProject)
.childDirectory('.dart_tool')
.childDirectory('widget_preview_scaffold'),
@ -116,11 +136,11 @@ void main() {
overrides: <Type, Generator>{
Pub:
() => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
botDetector: globals.botDetector,
platform: globals.platform,
fileSystem: fs,
logger: logger,
processManager: loggingProcessManager,
botDetector: botDetector,
platform: platform,
stdio: mockStdio,
),
},
@ -138,11 +158,11 @@ void main() {
overrides: <Type, Generator>{
Pub:
() => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
botDetector: globals.botDetector,
platform: globals.platform,
fileSystem: fs,
logger: logger,
processManager: loggingProcessManager,
botDetector: botDetector,
platform: platform,
stdio: mockStdio,
),
},
@ -179,11 +199,11 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/
overrides: <Type, Generator>{
Pub:
() => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
botDetector: globals.botDetector,
platform: globals.platform,
fileSystem: fs,
logger: logger,
processManager: loggingProcessManager,
botDetector: botDetector,
platform: platform,
stdio: mockStdio,
),
},
@ -209,16 +229,16 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/
// Try to execute using the CWD.
await startWidgetPreview(rootProject: null);
expect(generatedFile.readAsStringSync(), expectedGeneratedFileContents);
}, getCurrentDirectory: () => globals.fs.directory(rootProject));
}, getCurrentDirectory: () => fs.directory(rootProject));
},
overrides: <Type, Generator>{
Pub:
() => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
botDetector: globals.botDetector,
platform: globals.platform,
fileSystem: fs,
logger: logger,
processManager: loggingProcessManager,
botDetector: botDetector,
platform: platform,
stdio: mockStdio,
),
},
@ -234,11 +254,11 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/
overrides: <Type, Generator>{
Pub:
() => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
botDetector: globals.botDetector,
platform: globals.platform,
fileSystem: fs,
logger: logger,
processManager: loggingProcessManager,
botDetector: botDetector,
platform: platform,
stdio: mockStdio,
),
},
@ -287,11 +307,11 @@ import 'package:flutter_project/foo.dart' as _i1;import 'package:widget_preview/
ProcessManager: () => loggingProcessManager,
Pub:
() => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
botDetector: globals.botDetector,
platform: globals.platform,
fileSystem: fs,
logger: logger,
processManager: loggingProcessManager,
botDetector: botDetector,
platform: platform,
stdio: mockStdio,
),
},

View File

@ -48,7 +48,11 @@ void main() {
fs = LocalFileSystem.test(signals: Signals.test());
projectRoot = createBasicProjectStructure(fs);
logger = BufferLogger.test();
previewDetector = PreviewDetector(logger: logger, onChangeDetected: onChangeDetectedRoot);
previewDetector = PreviewDetector(
logger: logger,
fs: fs,
onChangeDetected: onChangeDetectedRoot,
);
});
tearDown(() async {