Unverified Commit 09987dc0 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate create command to null safety (#104484)

parent b5adbee1
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import '../android/gradle_utils.dart' as gradle; import '../android/gradle_utils.dart' as gradle;
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
...@@ -32,8 +30,8 @@ const String kPlatformHelp = ...@@ -32,8 +30,8 @@ const String kPlatformHelp =
class CreateCommand extends CreateBase { class CreateCommand extends CreateBase {
CreateCommand({ CreateCommand({
bool verboseHelp = false, super.verboseHelp = false,
}) : super(verboseHelp: verboseHelp) { }) {
addPlatformsOptions(customHelp: kPlatformHelp); addPlatformsOptions(customHelp: kPlatformHelp);
argParser.addOption( argParser.addOption(
'template', 'template',
...@@ -57,7 +55,6 @@ class CreateCommand extends CreateBase { ...@@ -57,7 +55,6 @@ class CreateCommand extends CreateBase {
flutterProjectTypeToString(FlutterProjectType.module): 'Generate a project to add a Flutter module to an ' flutterProjectTypeToString(FlutterProjectType.module): 'Generate a project to add a Flutter module to an '
'existing Android or iOS application.', 'existing Android or iOS application.',
}, },
defaultsTo: null,
); );
argParser.addOption( argParser.addOption(
'sample', 'sample',
...@@ -66,7 +63,6 @@ class CreateCommand extends CreateBase { ...@@ -66,7 +63,6 @@ class CreateCommand extends CreateBase {
'"--template=app". The value should be the sample ID of the desired sample from the API ' '"--template=app". The value should be the sample ID of the desired sample from the API '
'documentation website (http://docs.flutter.dev/). An example can be found at: ' 'documentation website (http://docs.flutter.dev/). An example can be found at: '
'https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html', 'https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html',
defaultsTo: null,
valueHelp: 'id', valueHelp: 'id',
); );
argParser.addOption( argParser.addOption(
...@@ -88,7 +84,7 @@ class CreateCommand extends CreateBase { ...@@ -88,7 +84,7 @@ class CreateCommand extends CreateBase {
String get category => FlutterCommandCategory.project; String get category => FlutterCommandCategory.project;
@override @override
String get invocation => '${runner.executableName} $name <output directory>'; String get invocation => '${runner?.executableName} $name <output directory>';
@override @override
Future<CustomDimensions> get usageValues async { Future<CustomDimensions> get usageValues async {
...@@ -100,8 +96,7 @@ class CreateCommand extends CreateBase { ...@@ -100,8 +96,7 @@ class CreateCommand extends CreateBase {
} }
// Lazy-initialize the net utilities with values from the context. // Lazy-initialize the net utilities with values from the context.
Net _cachedNet; late final Net _net = Net(
Net get _net => _cachedNet ??= Net(
httpClientFactory: context.get<HttpClientFactory>(), httpClientFactory: context.get<HttpClientFactory>(),
logger: globals.logger, logger: globals.logger,
platform: globals.platform, platform: globals.platform,
...@@ -112,7 +107,7 @@ class CreateCommand extends CreateBase { ...@@ -112,7 +107,7 @@ class CreateCommand extends CreateBase {
? 'api.flutter.dev' ? 'api.flutter.dev'
: 'master-api.flutter.dev'; : 'master-api.flutter.dev';
Future<String> _fetchSampleFromServer(String sampleId) async { Future<String?> _fetchSampleFromServer(String sampleId) async {
// Sanity check the sampleId // Sanity check the sampleId
if (sampleId.contains(RegExp(r'[^-\w\.]'))) { if (sampleId.contains(RegExp(r'[^-\w\.]'))) {
throwToolExit('Sample ID "$sampleId" contains invalid characters. Check the ID in the ' throwToolExit('Sample ID "$sampleId" contains invalid characters. Check the ID in the '
...@@ -120,7 +115,7 @@ class CreateCommand extends CreateBase { ...@@ -120,7 +115,7 @@ class CreateCommand extends CreateBase {
} }
final Uri snippetsUri = Uri.https(_snippetsHost, 'snippets/$sampleId.dart'); final Uri snippetsUri = Uri.https(_snippetsHost, 'snippets/$sampleId.dart');
final List<int> data = await _net.fetchUrl(snippetsUri); final List<int>? data = await _net.fetchUrl(snippetsUri);
if (data == null || data.isEmpty) { if (data == null || data.isEmpty) {
return null; return null;
} }
...@@ -128,9 +123,9 @@ class CreateCommand extends CreateBase { ...@@ -128,9 +123,9 @@ class CreateCommand extends CreateBase {
} }
/// Fetches the samples index file from the Flutter docs website. /// Fetches the samples index file from the Flutter docs website.
Future<String> _fetchSamplesIndexFromServer() async { Future<String?> _fetchSamplesIndexFromServer() async {
final Uri snippetsUri = Uri.https(_snippetsHost, 'snippets/index.json'); final Uri snippetsUri = Uri.https(_snippetsHost, 'snippets/index.json');
final List<int> data = await _net.fetchUrl(snippetsUri, maxAttempts: 2); final List<int>? data = await _net.fetchUrl(snippetsUri, maxAttempts: 2);
if (data == null || data.isEmpty) { if (data == null || data.isEmpty) {
return null; return null;
} }
...@@ -145,7 +140,7 @@ class CreateCommand extends CreateBase { ...@@ -145,7 +140,7 @@ class CreateCommand extends CreateBase {
if (outputFile.existsSync()) { if (outputFile.existsSync()) {
throwToolExit('File "$outputFilePath" already exists', exitCode: 1); throwToolExit('File "$outputFilePath" already exists', exitCode: 1);
} }
final String samplesJson = await _fetchSamplesIndexFromServer(); final String? samplesJson = await _fetchSamplesIndexFromServer();
if (samplesJson == null) { if (samplesJson == null) {
throwToolExit('Unable to download samples', exitCode: 2); throwToolExit('Unable to download samples', exitCode: 2);
} else { } else {
...@@ -158,11 +153,12 @@ class CreateCommand extends CreateBase { ...@@ -158,11 +153,12 @@ class CreateCommand extends CreateBase {
} }
FlutterProjectType _getProjectType(Directory projectDir) { FlutterProjectType _getProjectType(Directory projectDir) {
FlutterProjectType template; FlutterProjectType? template;
FlutterProjectType detectedProjectType; FlutterProjectType? detectedProjectType;
final bool metadataExists = projectDir.absolute.childFile('.metadata').existsSync(); final bool metadataExists = projectDir.absolute.childFile('.metadata').existsSync();
if (argResults['template'] != null) { final String? templateArgument = stringArg('template');
template = stringToProjectType(stringArgDeprecated('template')); if (templateArgument != null) {
template = stringToProjectType(templateArgument);
} }
// If the project directory exists and isn't empty, then try to determine the template // If the project directory exists and isn't empty, then try to determine the template
// type from the project directory. // type from the project directory.
...@@ -188,23 +184,25 @@ class CreateCommand extends CreateBase { ...@@ -188,23 +184,25 @@ class CreateCommand extends CreateBase {
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (argResults['list-samples'] != null) { final String? listSamples = stringArg('list-samples');
if (listSamples != null) {
// _writeSamplesJson can potentially be long-lived. // _writeSamplesJson can potentially be long-lived.
await _writeSamplesJson(stringArgDeprecated('list-samples')); await _writeSamplesJson(listSamples);
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
validateOutputDirectoryArg(); validateOutputDirectoryArg();
String sampleCode; String? sampleCode;
if (argResults['sample'] != null) { final String? sampleArgument = stringArg('sample');
if (argResults['template'] != null && if (sampleArgument != null) {
stringToProjectType(stringArgDeprecated('template') ?? 'app') != FlutterProjectType.app) { final String? templateArgument = stringArg('template');
if (templateArgument != null && stringToProjectType(templateArgument) != FlutterProjectType.app) {
throwToolExit('Cannot specify --sample with a project type other than ' throwToolExit('Cannot specify --sample with a project type other than '
'"${flutterProjectTypeToString(FlutterProjectType.app)}"'); '"${flutterProjectTypeToString(FlutterProjectType.app)}"');
} }
// Fetch the sample from the server. // Fetch the sample from the server.
sampleCode = await _fetchSampleFromServer(stringArgDeprecated('sample')); sampleCode = await _fetchSampleFromServer(sampleArgument);
} }
final FlutterProjectType template = _getProjectType(projectDir); final FlutterProjectType template = _getProjectType(projectDir);
...@@ -215,7 +213,7 @@ class CreateCommand extends CreateBase { ...@@ -215,7 +213,7 @@ class CreateCommand extends CreateBase {
final List<String> platforms = stringsArg('platforms'); final List<String> platforms = stringsArg('platforms');
// `--platforms` does not support module or package. // `--platforms` does not support module or package.
if (argResults.wasParsed('platforms') && (generateModule || generatePackage)) { if (argResults!.wasParsed('platforms') && (generateModule || generatePackage)) {
final String template = generateModule ? 'module' : 'package'; final String template = generateModule ? 'module' : 'package';
throwToolExit( throwToolExit(
'The "--platforms" argument is not supported in $template template.', 'The "--platforms" argument is not supported in $template template.',
...@@ -224,18 +222,18 @@ class CreateCommand extends CreateBase { ...@@ -224,18 +222,18 @@ class CreateCommand extends CreateBase {
} else if (platforms == null || platforms.isEmpty) { } else if (platforms == null || platforms.isEmpty) {
throwToolExit('Must specify at least one platform using --platforms', throwToolExit('Must specify at least one platform using --platforms',
exitCode: 2); exitCode: 2);
} else if (generateFfiPlugin && argResults.wasParsed('platforms') && platforms.contains('web')) { } else if (generateFfiPlugin && argResults!.wasParsed('platforms') && platforms.contains('web')) {
throwToolExit( throwToolExit(
'The web platform is not supported in plugin_ffi template.', 'The web platform is not supported in plugin_ffi template.',
exitCode: 2, exitCode: 2,
); );
} else if (generateFfiPlugin && argResults.wasParsed('ios-language')) { } else if (generateFfiPlugin && argResults!.wasParsed('ios-language')) {
throwToolExit( throwToolExit(
'The "ios-language" option is not supported with the plugin_ffi ' 'The "ios-language" option is not supported with the plugin_ffi '
'template: the language will always be C or C++.', 'template: the language will always be C or C++.',
exitCode: 2, exitCode: 2,
); );
} else if (generateFfiPlugin && argResults.wasParsed('android-language')) { } else if (generateFfiPlugin && argResults!.wasParsed('android-language')) {
throwToolExit( throwToolExit(
'The "android-language" option is not supported with the plugin_ffi ' 'The "android-language" option is not supported with the plugin_ffi '
'template: the language will always be C or C++.', 'template: the language will always be C or C++.',
...@@ -258,7 +256,7 @@ class CreateCommand extends CreateBase { ...@@ -258,7 +256,7 @@ class CreateCommand extends CreateBase {
final String dartSdk = globals.cache.dartSdkBuild; final String dartSdk = globals.cache.dartSdkBuild;
final bool includeIos = featureFlags.isIOSEnabled && platforms.contains('ios'); final bool includeIos = featureFlags.isIOSEnabled && platforms.contains('ios');
String developmentTeam; String? developmentTeam;
if (includeIos) { if (includeIos) {
developmentTeam = await getCodeSigningIdentityDevelopmentTeam( developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: globals.processManager, processManager: globals.processManager,
...@@ -272,7 +270,7 @@ class CreateCommand extends CreateBase { ...@@ -272,7 +270,7 @@ class CreateCommand extends CreateBase {
// The dart project_name is in snake_case, this variable is the Title Case of the Project Name. // The dart project_name is in snake_case, this variable is the Title Case of the Project Name.
final String titleCaseProjectName = snakeCaseToTitleCase(projectName); final String titleCaseProjectName = snakeCaseToTitleCase(projectName);
final Map<String, Object> templateContext = createTemplateContext( final Map<String, Object?> templateContext = createTemplateContext(
organization: organization, organization: organization,
projectName: projectName, projectName: projectName,
titleCaseProjectName: titleCaseProjectName, titleCaseProjectName: titleCaseProjectName,
...@@ -432,12 +430,12 @@ Your $application code is in $relativeAppMain. ...@@ -432,12 +430,12 @@ Your $application code is in $relativeAppMain.
Future<int> _generateModule( Future<int> _generateModule(
Directory directory, Directory directory,
Map<String, dynamic> templateContext, { Map<String, Object?> templateContext, {
bool overwrite = false, bool overwrite = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
}) async { }) async {
int generatedCount = 0; int generatedCount = 0;
final String description = argResults.wasParsed('description') final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description') ? stringArgDeprecated('description')
: 'A new Flutter module project.'; : 'A new Flutter module project.';
templateContext['description'] = description; templateContext['description'] = description;
...@@ -453,7 +451,6 @@ Your $application code is in $relativeAppMain. ...@@ -453,7 +451,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.create, context: PubContext.create,
directory: directory.path, directory: directory.path,
offline: boolArgDeprecated('offline'), offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
); );
final FlutterProject project = FlutterProject.fromDirectory(directory); final FlutterProject project = FlutterProject.fromDirectory(directory);
await project.ensureReadyForPlatformSpecificTooling( await project.ensureReadyForPlatformSpecificTooling(
...@@ -466,12 +463,12 @@ Your $application code is in $relativeAppMain. ...@@ -466,12 +463,12 @@ Your $application code is in $relativeAppMain.
Future<int> _generatePackage( Future<int> _generatePackage(
Directory directory, Directory directory,
Map<String, dynamic> templateContext, { Map<String, Object?> templateContext, {
bool overwrite = false, bool overwrite = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
}) async { }) async {
int generatedCount = 0; int generatedCount = 0;
final String description = argResults.wasParsed('description') final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description') ? stringArgDeprecated('description')
: 'A new Flutter package project.'; : 'A new Flutter package project.';
templateContext['description'] = description; templateContext['description'] = description;
...@@ -487,7 +484,6 @@ Your $application code is in $relativeAppMain. ...@@ -487,7 +484,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.createPackage, context: PubContext.createPackage,
directory: directory.path, directory: directory.path,
offline: boolArgDeprecated('offline'), offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
); );
} }
return generatedCount; return generatedCount;
...@@ -495,13 +491,13 @@ Your $application code is in $relativeAppMain. ...@@ -495,13 +491,13 @@ Your $application code is in $relativeAppMain.
Future<int> _generateMethodChannelPlugin( Future<int> _generateMethodChannelPlugin(
Directory directory, Directory directory,
Map<String, dynamic> templateContext, { Map<String, Object?> templateContext, {
bool overwrite = false, bool overwrite = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
FlutterProjectType projectType, required FlutterProjectType projectType,
}) async { }) async {
// Plugins only add a platform if it was requested explicitly by the user. // Plugins only add a platform if it was requested explicitly by the user.
if (!argResults.wasParsed('platforms')) { if (!argResults!.wasParsed('platforms')) {
for (final String platform in kAllCreatePlatforms) { for (final String platform in kAllCreatePlatforms) {
templateContext[platform] = false; templateContext[platform] = false;
} }
...@@ -517,7 +513,7 @@ Your $application code is in $relativeAppMain. ...@@ -517,7 +513,7 @@ Your $application code is in $relativeAppMain.
final bool willAddPlatforms = platformsToAdd.isNotEmpty; final bool willAddPlatforms = platformsToAdd.isNotEmpty;
templateContext['no_platforms'] = !willAddPlatforms; templateContext['no_platforms'] = !willAddPlatforms;
int generatedCount = 0; int generatedCount = 0;
final String description = argResults.wasParsed('description') final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description') ? stringArgDeprecated('description')
: 'A new Flutter plugin project.'; : 'A new Flutter plugin project.';
templateContext['description'] = description; templateContext['description'] = description;
...@@ -534,7 +530,6 @@ Your $application code is in $relativeAppMain. ...@@ -534,7 +530,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.createPlugin, context: PubContext.createPlugin,
directory: directory.path, directory: directory.path,
offline: boolArgDeprecated('offline'), offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
); );
} }
...@@ -545,9 +540,9 @@ Your $application code is in $relativeAppMain. ...@@ -545,9 +540,9 @@ Your $application code is in $relativeAppMain.
project: project, requireAndroidSdk: false); project: project, requireAndroidSdk: false);
} }
final String projectName = templateContext['projectName'] as String; final String? projectName = templateContext['projectName'] as String?;
final String organization = templateContext['organization'] as String; final String organization = templateContext['organization']! as String; // Required to make the context.
final String androidPluginIdentifier = templateContext['androidIdentifier'] as String; final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?;
final String exampleProjectName = '${projectName}_example'; final String exampleProjectName = '${projectName}_example';
templateContext['projectName'] = exampleProjectName; templateContext['projectName'] = exampleProjectName;
templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName); templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName);
...@@ -572,13 +567,13 @@ Your $application code is in $relativeAppMain. ...@@ -572,13 +567,13 @@ Your $application code is in $relativeAppMain.
Future<int> _generateFfiPlugin( Future<int> _generateFfiPlugin(
Directory directory, Directory directory,
Map<String, dynamic> templateContext, { Map<String, Object?> templateContext, {
bool overwrite = false, bool overwrite = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
FlutterProjectType projectType, required FlutterProjectType projectType,
}) async { }) async {
// Plugins only add a platform if it was requested explicitly by the user. // Plugins only add a platform if it was requested explicitly by the user.
if (!argResults.wasParsed('platforms')) { if (!argResults!.wasParsed('platforms')) {
for (final String platform in kAllCreatePlatforms) { for (final String platform in kAllCreatePlatforms) {
templateContext[platform] = false; templateContext[platform] = false;
} }
...@@ -596,7 +591,7 @@ Your $application code is in $relativeAppMain. ...@@ -596,7 +591,7 @@ Your $application code is in $relativeAppMain.
final bool willAddPlatforms = platformsToAdd.isNotEmpty; final bool willAddPlatforms = platformsToAdd.isNotEmpty;
templateContext['no_platforms'] = !willAddPlatforms; templateContext['no_platforms'] = !willAddPlatforms;
int generatedCount = 0; int generatedCount = 0;
final String description = argResults.wasParsed('description') final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description') ? stringArgDeprecated('description')
: 'A new Flutter FFI plugin project.'; : 'A new Flutter FFI plugin project.';
templateContext['description'] = description; templateContext['description'] = description;
...@@ -613,7 +608,6 @@ Your $application code is in $relativeAppMain. ...@@ -613,7 +608,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.createPlugin, context: PubContext.createPlugin,
directory: directory.path, directory: directory.path,
offline: boolArgDeprecated('offline'), offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
); );
} }
...@@ -623,9 +617,9 @@ Your $application code is in $relativeAppMain. ...@@ -623,9 +617,9 @@ Your $application code is in $relativeAppMain.
gradle.updateLocalProperties(project: project, requireAndroidSdk: false); gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
} }
final String projectName = templateContext['projectName'] as String; final String? projectName = templateContext['projectName'] as String?;
final String organization = templateContext['organization'] as String; final String organization = templateContext['organization']! as String; // Required to make the context.
final String androidPluginIdentifier = templateContext['androidIdentifier'] as String; final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?;
final String exampleProjectName = '${projectName}_example'; final String exampleProjectName = '${projectName}_example';
templateContext['projectName'] = exampleProjectName; templateContext['projectName'] = exampleProjectName;
templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName); templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName);
...@@ -662,7 +656,7 @@ Your $application code is in $relativeAppMain. ...@@ -662,7 +656,7 @@ Your $application code is in $relativeAppMain.
return -files.length; return -files.length;
} }
List<String> _getSupportedPlatformsFromTemplateContext(Map<String, dynamic> templateContext) { List<String> _getSupportedPlatformsFromTemplateContext(Map<String, Object?> templateContext) {
return <String>[ return <String>[
for (String platform in kAllCreatePlatforms) for (String platform in kAllCreatePlatforms)
if (templateContext[platform] == true) platform, if (templateContext[platform] == true) platform,
...@@ -671,7 +665,7 @@ Your $application code is in $relativeAppMain. ...@@ -671,7 +665,7 @@ Your $application code is in $relativeAppMain.
// Returns a list of platforms that are explicitly requested by user via `--platforms`. // Returns a list of platforms that are explicitly requested by user via `--platforms`.
List<String> _getUserRequestedPlatforms() { List<String> _getUserRequestedPlatforms() {
if (!argResults.wasParsed('platforms')) { if (!argResults!.wasParsed('platforms')) {
return <String>[]; return <String>[];
} }
return stringsArg('platforms'); return stringsArg('platforms');
...@@ -682,10 +676,11 @@ Your $application code is in $relativeAppMain. ...@@ -682,10 +676,11 @@ Your $application code is in $relativeAppMain.
// Determine what platforms are supported based on generated files. // Determine what platforms are supported based on generated files.
List<String> _getSupportedPlatformsInPlugin(Directory projectDir) { List<String> _getSupportedPlatformsInPlugin(Directory projectDir) {
final String pubspecPath = globals.fs.path.join(projectDir.absolute.path, 'pubspec.yaml'); final String pubspecPath = globals.fs.path.join(projectDir.absolute.path, 'pubspec.yaml');
final FlutterManifest manifest = FlutterManifest.createFromPath(pubspecPath, fileSystem: globals.fs, logger: globals.logger); final FlutterManifest? manifest = FlutterManifest.createFromPath(pubspecPath, fileSystem: globals.fs, logger: globals.logger);
final List<String> platforms = manifest.validSupportedPlatforms == null final Map<String, Object?>? validSupportedPlatforms = manifest?.validSupportedPlatforms;
final List<String> platforms = validSupportedPlatforms == null
? <String>[] ? <String>[]
: manifest.validSupportedPlatforms.keys.toList(); : validSupportedPlatforms.keys.toList();
return platforms; return platforms;
} }
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:pub_semver/pub_semver.dart'; import 'package:pub_semver/pub_semver.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
...@@ -54,7 +52,7 @@ const String _kDefaultPlatformArgumentHelp = ...@@ -54,7 +52,7 @@ const String _kDefaultPlatformArgumentHelp =
/// Common behavior for `flutter create` commands. /// Common behavior for `flutter create` commands.
abstract class CreateBase extends FlutterCommand { abstract class CreateBase extends FlutterCommand {
CreateBase({ CreateBase({
@required bool verboseHelp, required bool verboseHelp,
}) { }) {
argParser.addFlag( argParser.addFlag(
'pub', 'pub',
...@@ -64,7 +62,6 @@ abstract class CreateBase extends FlutterCommand { ...@@ -64,7 +62,6 @@ abstract class CreateBase extends FlutterCommand {
); );
argParser.addFlag( argParser.addFlag(
'offline', 'offline',
defaultsTo: false,
help: help:
'When "flutter pub get" is run by the create command, this indicates ' 'When "flutter pub get" is run by the create command, this indicates '
'whether to run it in offline mode or not. In offline mode, it will need to ' 'whether to run it in offline mode or not. In offline mode, it will need to '
...@@ -72,8 +69,6 @@ abstract class CreateBase extends FlutterCommand { ...@@ -72,8 +69,6 @@ abstract class CreateBase extends FlutterCommand {
); );
argParser.addFlag( argParser.addFlag(
'with-driver-test', 'with-driver-test',
negatable: true,
defaultsTo: false,
help: '(deprecated) Historically, this added a flutter_driver dependency and generated a ' help: '(deprecated) Historically, this added a flutter_driver dependency and generated a '
'sample "flutter drive" test. Now it does nothing. Consider using the ' 'sample "flutter drive" test. Now it does nothing. Consider using the '
'"integration_test" package: https://pub.dev/packages/integration_test', '"integration_test" package: https://pub.dev/packages/integration_test',
...@@ -81,8 +76,6 @@ abstract class CreateBase extends FlutterCommand { ...@@ -81,8 +76,6 @@ abstract class CreateBase extends FlutterCommand {
); );
argParser.addFlag( argParser.addFlag(
'overwrite', 'overwrite',
negatable: true,
defaultsTo: false,
help: 'When performing operations, overwrite existing files.', help: 'When performing operations, overwrite existing files.',
); );
argParser.addOption( argParser.addOption(
...@@ -100,7 +93,6 @@ abstract class CreateBase extends FlutterCommand { ...@@ -100,7 +93,6 @@ abstract class CreateBase extends FlutterCommand {
); );
argParser.addOption( argParser.addOption(
'project-name', 'project-name',
defaultsTo: null,
help: help:
'The project name for this new Flutter project. This must be a valid dart package name.', 'The project name for this new Flutter project. This must be a valid dart package name.',
); );
...@@ -134,7 +126,6 @@ abstract class CreateBase extends FlutterCommand { ...@@ -134,7 +126,6 @@ abstract class CreateBase extends FlutterCommand {
); );
argParser.addOption( argParser.addOption(
'initial-create-revision', 'initial-create-revision',
defaultsTo: null,
help: 'The Flutter SDK git commit hash to store in .migrate_config. This parameter is used by the tool ' help: 'The Flutter SDK git commit hash to store in .migrate_config. This parameter is used by the tool '
'internally and should generally not be used manually.', 'internally and should generally not be used manually.',
hide: !verboseHelp, hide: !verboseHelp,
...@@ -144,7 +135,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -144,7 +135,7 @@ abstract class CreateBase extends FlutterCommand {
/// The output directory of the command. /// The output directory of the command.
@protected @protected
Directory get projectDir { Directory get projectDir {
return globals.fs.directory(argResults.rest.first); return globals.fs.directory(argResults!.rest.first);
} }
/// The normalized absolute path of [projectDir]. /// The normalized absolute path of [projectDir].
...@@ -157,7 +148,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -157,7 +148,7 @@ abstract class CreateBase extends FlutterCommand {
/// ///
/// The help message of the argument is replaced with `customHelp` if `customHelp` is not null. /// The help message of the argument is replaced with `customHelp` if `customHelp` is not null.
@protected @protected
void addPlatformsOptions({String customHelp}) { void addPlatformsOptions({String? customHelp}) {
argParser.addMultiOption('platforms', argParser.addMultiOption('platforms',
help: customHelp ?? _kDefaultPlatformArgumentHelp, help: customHelp ?? _kDefaultPlatformArgumentHelp,
aliases: <String>[ 'platform' ], aliases: <String>[ 'platform' ],
...@@ -173,16 +164,17 @@ abstract class CreateBase extends FlutterCommand { ...@@ -173,16 +164,17 @@ abstract class CreateBase extends FlutterCommand {
/// Throw with exit code 2 if the output directory is invalid. /// Throw with exit code 2 if the output directory is invalid.
@protected @protected
void validateOutputDirectoryArg() { void validateOutputDirectoryArg() {
if (argResults.rest.isEmpty) { final List<String>? rest = argResults?.rest;
if (rest == null || rest.isEmpty) {
throwToolExit( throwToolExit(
'No option specified for the output directory.\n$usage', 'No option specified for the output directory.\n$usage',
exitCode: 2, exitCode: 2,
); );
} }
if (argResults.rest.length > 1) { if (rest.length > 1) {
String message = 'Multiple output directories specified.'; String message = 'Multiple output directories specified.';
for (final String arg in argResults.rest) { for (final String arg in rest) {
if (arg.startsWith('-')) { if (arg.startsWith('-')) {
message += '\nTry moving $arg to be immediately following $name'; message += '\nTry moving $arg to be immediately following $name';
break; break;
...@@ -194,7 +186,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -194,7 +186,7 @@ abstract class CreateBase extends FlutterCommand {
/// Gets the flutter root directory. /// Gets the flutter root directory.
@protected @protected
String get flutterRoot => Cache.flutterRoot; String get flutterRoot => Cache.flutterRoot!;
/// Determines the project type in an existing flutter project. /// Determines the project type in an existing flutter project.
/// ///
...@@ -207,14 +199,15 @@ abstract class CreateBase extends FlutterCommand { ...@@ -207,14 +199,15 @@ abstract class CreateBase extends FlutterCommand {
/// Throws assertion if [projectDir] does not exist or empty. /// Throws assertion if [projectDir] does not exist or empty.
/// Returns null if no project type can be determined. /// Returns null if no project type can be determined.
@protected @protected
FlutterProjectType determineTemplateType() { FlutterProjectType? determineTemplateType() {
assert(projectDir.existsSync() && projectDir.listSync().isNotEmpty); assert(projectDir.existsSync() && projectDir.listSync().isNotEmpty);
final File metadataFile = globals.fs final File metadataFile = globals.fs
.file(globals.fs.path.join(projectDir.absolute.path, '.metadata')); .file(globals.fs.path.join(projectDir.absolute.path, '.metadata'));
final FlutterProjectMetadata projectMetadata = final FlutterProjectMetadata projectMetadata =
FlutterProjectMetadata(metadataFile, globals.logger); FlutterProjectMetadata(metadataFile, globals.logger);
if (projectMetadata.projectType != null) { final FlutterProjectType? projectType = projectMetadata.projectType;
return projectMetadata.projectType; if (projectType != null) {
return projectType;
} }
bool exists(List<String> path) { bool exists(List<String> path) {
...@@ -243,8 +236,8 @@ abstract class CreateBase extends FlutterCommand { ...@@ -243,8 +236,8 @@ abstract class CreateBase extends FlutterCommand {
/// If `--org` is not specified, returns the organization from the existing project. /// If `--org` is not specified, returns the organization from the existing project.
@protected @protected
Future<String> getOrganization() async { Future<String> getOrganization() async {
String organization = stringArgDeprecated('org'); String? organization = stringArgDeprecated('org');
if (!argResults.wasParsed('org')) { if (!argResults!.wasParsed('org')) {
final FlutterProject project = FlutterProject.fromDirectory(projectDir); final FlutterProject project = FlutterProject.fromDirectory(projectDir);
final Set<String> existingOrganizations = await project.organizationNames; final Set<String> existingOrganizations = await project.organizationNames;
if (existingOrganizations.length == 1) { if (existingOrganizations.length == 1) {
...@@ -255,6 +248,9 @@ abstract class CreateBase extends FlutterCommand { ...@@ -255,6 +248,9 @@ abstract class CreateBase extends FlutterCommand {
'The --org command line argument must be specified to recreate project.'); 'The --org command line argument must be specified to recreate project.');
} }
} }
if (organization == null) {
throwToolExit('The --org command line argument must be specified to create a project.');
}
return organization; return organization;
} }
...@@ -297,12 +293,10 @@ abstract class CreateBase extends FlutterCommand { ...@@ -297,12 +293,10 @@ abstract class CreateBase extends FlutterCommand {
// Do not overwrite files. // Do not overwrite files.
throwToolExit("Invalid project name: '$projectDirPath' - file exists.", throwToolExit("Invalid project name: '$projectDirPath' - file exists.",
exitCode: 2); exitCode: 2);
break;
case FileSystemEntityType.link: case FileSystemEntityType.link:
// Do not overwrite links. // Do not overwrite links.
throwToolExit("Invalid project name: '$projectDirPath' - refers to a link.", throwToolExit("Invalid project name: '$projectDirPath' - refers to a link.",
exitCode: 2); exitCode: 2);
break;
case FileSystemEntityType.directory: case FileSystemEntityType.directory:
case FileSystemEntityType.notFound: case FileSystemEntityType.notFound:
break; break;
...@@ -317,7 +311,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -317,7 +311,7 @@ abstract class CreateBase extends FlutterCommand {
final String projectName = final String projectName =
stringArgDeprecated('project-name') ?? globals.fs.path.basename(projectDirPath); stringArgDeprecated('project-name') ?? globals.fs.path.basename(projectDirPath);
if (!boolArgDeprecated('skip-name-checks')) { if (!boolArgDeprecated('skip-name-checks')) {
final String error = _validateProjectName(projectName); final String? error = _validateProjectName(projectName);
if (error != null) { if (error != null) {
throwToolExit(error); throwToolExit(error);
} }
...@@ -328,19 +322,19 @@ abstract class CreateBase extends FlutterCommand { ...@@ -328,19 +322,19 @@ abstract class CreateBase extends FlutterCommand {
/// Creates a template to use for [renderTemplate]. /// Creates a template to use for [renderTemplate].
@protected @protected
Map<String, Object> createTemplateContext({ Map<String, Object?> createTemplateContext({
String organization, required String organization,
String projectName, required String projectName,
String titleCaseProjectName, required String titleCaseProjectName,
String projectDescription, String? projectDescription,
String androidLanguage, String? androidLanguage,
String iosDevelopmentTeam, String? iosDevelopmentTeam,
String iosLanguage, String? iosLanguage,
String flutterRoot, required String flutterRoot,
String dartSdkVersionBounds, required String dartSdkVersionBounds,
String agpVersion, String? agpVersion,
String kotlinVersion, String? kotlinVersion,
String gradleVersion, String? gradleVersion,
bool withPlatformChannelPluginHook = false, bool withPlatformChannelPluginHook = false,
bool withFfiPluginHook = false, bool withFfiPluginHook = false,
bool ios = false, bool ios = false,
...@@ -376,7 +370,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -376,7 +370,7 @@ abstract class CreateBase extends FlutterCommand {
? globals.flutterVersion.frameworkVersion ? globals.flutterVersion.frameworkVersion
: ffiPluginStableRelease.toString(); : ffiPluginStableRelease.toString();
return <String, Object>{ return <String, Object?>{
'organization': organization, 'organization': organization,
'projectName': projectName, 'projectName': projectName,
'titleCaseProjectName': titleCaseProjectName, 'titleCaseProjectName': titleCaseProjectName,
...@@ -433,7 +427,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -433,7 +427,7 @@ abstract class CreateBase extends FlutterCommand {
Future<int> renderTemplate( Future<int> renderTemplate(
String templateName, String templateName,
Directory directory, Directory directory,
Map<String, Object> context, { Map<String, Object?> context, {
bool overwrite = false, bool overwrite = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
}) async { }) async {
...@@ -461,7 +455,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -461,7 +455,7 @@ abstract class CreateBase extends FlutterCommand {
Future<int> renderMerged( Future<int> renderMerged(
List<String> names, List<String> names,
Directory directory, Directory directory,
Map<String, Object> context, { Map<String, Object?> context, {
bool overwrite = false, bool overwrite = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
}) async { }) async {
...@@ -488,12 +482,12 @@ abstract class CreateBase extends FlutterCommand { ...@@ -488,12 +482,12 @@ abstract class CreateBase extends FlutterCommand {
Future<int> generateApp( Future<int> generateApp(
List<String> templateNames, List<String> templateNames,
Directory directory, Directory directory,
Map<String, Object> templateContext, { Map<String, Object?> templateContext, {
bool overwrite = false, bool overwrite = false,
bool pluginExampleApp = false, bool pluginExampleApp = false,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
bool generateMetadata = true, bool generateMetadata = true,
FlutterProjectType projectType, FlutterProjectType? projectType,
}) async { }) async {
int generatedCount = 0; int generatedCount = 0;
generatedCount += await renderMerged( generatedCount += await renderMerged(
...@@ -508,16 +502,16 @@ abstract class CreateBase extends FlutterCommand { ...@@ -508,16 +502,16 @@ abstract class CreateBase extends FlutterCommand {
generatedCount += _injectGradleWrapper(project); generatedCount += _injectGradleWrapper(project);
} }
final bool androidPlatform = templateContext['android'] as bool ?? false; final bool androidPlatform = templateContext['android'] as bool? ?? false;
final bool iosPlatform = templateContext['ios'] as bool ?? false; final bool iosPlatform = templateContext['ios'] as bool? ?? false;
final bool linuxPlatform = templateContext['linux'] as bool ?? false; final bool linuxPlatform = templateContext['linux'] as bool? ?? false;
final bool macOSPlatform = templateContext['macos'] as bool ?? false; final bool macOSPlatform = templateContext['macos'] as bool? ?? false;
final bool windowsPlatform = templateContext['windows'] as bool ?? false; final bool windowsPlatform = templateContext['windows'] as bool? ?? false;
final bool webPlatform = templateContext['web'] as bool ?? false; final bool webPlatform = templateContext['web'] as bool? ?? false;
if (boolArgDeprecated('pub')) { if (boolArgDeprecated('pub')) {
final Environment environment = Environment( final Environment environment = Environment(
artifacts: globals.artifacts, artifacts: globals.artifacts!,
logger: globals.logger, logger: globals.logger,
cacheDir: globals.cache.getRoot(), cacheDir: globals.cache.getRoot(),
engineVersion: globals.flutterVersion.engineRevision, engineVersion: globals.flutterVersion.engineRevision,
...@@ -591,7 +585,6 @@ abstract class CreateBase extends FlutterCommand { ...@@ -591,7 +585,6 @@ abstract class CreateBase extends FlutterCommand {
metadata.populate( metadata.populate(
platforms: platformsForMigrateConfig, platforms: platformsForMigrateConfig,
projectDirectory: directory, projectDirectory: directory,
create: true,
update: false, update: false,
currentRevision: stringArgDeprecated('initial-create-revision') ?? globals.flutterVersion.frameworkRevision, currentRevision: stringArgDeprecated('initial-create-revision') ?? globals.flutterVersion.frameworkRevision,
createRevision: globals.flutterVersion.frameworkRevision, createRevision: globals.flutterVersion.frameworkRevision,
...@@ -663,12 +656,10 @@ abstract class CreateBase extends FlutterCommand { ...@@ -663,12 +656,10 @@ abstract class CreateBase extends FlutterCommand {
return segments.join('.'); return segments.join('.');
} }
Set<Uri> get _templateManifest => late final Set<Uri> _templateManifest = _computeTemplateManifest();
__templateManifest ??= _computeTemplateManifest();
Set<Uri> __templateManifest;
Set<Uri> _computeTemplateManifest() { Set<Uri> _computeTemplateManifest() {
final String flutterToolsAbsolutePath = globals.fs.path.join( final String flutterToolsAbsolutePath = globals.fs.path.join(
Cache.flutterRoot, Cache.flutterRoot!,
'packages', 'packages',
'flutter_tools', 'flutter_tools',
); );
...@@ -681,7 +672,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -681,7 +672,7 @@ abstract class CreateBase extends FlutterCommand {
globals.fs.file(manifestPath).readAsStringSync(), globals.fs.file(manifestPath).readAsStringSync(),
) as Map<String, Object>; ) as Map<String, Object>;
return Set<Uri>.from( return Set<Uri>.from(
(manifest['files'] as List<Object>).cast<String>().map<Uri>( (manifest['files']! as List<Object>).cast<String>().map<Uri>(
(String path) => (String path) =>
Uri.file(globals.fs.path.join(flutterToolsAbsolutePath, path))), Uri.file(globals.fs.path.join(flutterToolsAbsolutePath, path))),
); );
...@@ -793,7 +784,7 @@ const Set<String> _packageDependencies = <String>{ ...@@ -793,7 +784,7 @@ const Set<String> _packageDependencies = <String>{
/// Whether [name] is a valid Pub package. /// Whether [name] is a valid Pub package.
@visibleForTesting @visibleForTesting
bool isValidPackageName(String name) { bool isValidPackageName(String name) {
final Match match = _identifierRegExp.matchAsPrefix(name); final Match? match = _identifierRegExp.matchAsPrefix(name);
return match != null && return match != null &&
match.end == name.length && match.end == name.length &&
!_keywords.contains(name); !_keywords.contains(name);
...@@ -801,7 +792,7 @@ bool isValidPackageName(String name) { ...@@ -801,7 +792,7 @@ bool isValidPackageName(String name) {
// Return null if the project name is legal. Return a validation message if // Return null if the project name is legal. Return a validation message if
// we should disallow the project name. // we should disallow the project name.
String _validateProjectName(String projectName) { String? _validateProjectName(String projectName) {
if (!isValidPackageName(projectName)) { if (!isValidPackageName(projectName)) {
return '"$projectName" is not a valid Dart package name.\n\n' return '"$projectName" is not a valid Dart package name.\n\n'
'See https://dart.dev/tools/pub/pubspec#name for more information.'; 'See https://dart.dev/tools/pub/pubspec#name for more information.';
......
...@@ -155,7 +155,7 @@ class Template { ...@@ -155,7 +155,7 @@ class Template {
/// May throw a [ToolExit] if the directory is not writable. /// May throw a [ToolExit] if the directory is not writable.
int render( int render(
Directory destination, Directory destination,
Map<String, Object> context, { Map<String, Object?> context, {
bool overwriteExisting = true, bool overwriteExisting = true,
bool printStatusWhenWriting = true, bool printStatusWhenWriting = true,
}) { }) {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'package:args/args.dart'; import 'package:args/args.dart';
...@@ -20,7 +18,7 @@ import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; ...@@ -20,7 +18,7 @@ import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
export 'package:test_api/test_api.dart' hide test, isInstanceOf; // ignore: deprecated_member_use export 'package:test_api/test_api.dart' hide test, isInstanceOf; // ignore: deprecated_member_use
CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) { CommandRunner<void> createTestCommandRunner([ FlutterCommand? command ]) {
final FlutterCommandRunner runner = TestFlutterCommandRunner(); final FlutterCommandRunner runner = TestFlutterCommandRunner();
if (command != null) { if (command != null) {
runner.addCommand(command); runner.addCommand(command);
...@@ -31,7 +29,7 @@ CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) { ...@@ -31,7 +29,7 @@ CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) {
/// Creates a flutter project in the [temp] directory using the /// Creates a flutter project in the [temp] directory using the
/// [arguments] list if specified, or `--no-pub` if not. /// [arguments] list if specified, or `--no-pub` if not.
/// Returns the path to the flutter project. /// Returns the path to the flutter project.
Future<String> createProject(Directory temp, { List<String> arguments }) async { Future<String> createProject(Directory temp, { List<String>? arguments }) async {
arguments ??= <String>['--no-pub']; arguments ??= <String>['--no-pub'];
final String projectPath = globals.fs.path.join(temp.path, 'flutter_project'); final String projectPath = globals.fs.path.join(temp.path, 'flutter_project');
final CreateCommand command = CreateCommand(); final CreateCommand command = CreateCommand();
...@@ -61,7 +59,7 @@ class TestFlutterCommandRunner extends FlutterCommandRunner { ...@@ -61,7 +59,7 @@ class TestFlutterCommandRunner extends FlutterCommandRunner {
userMessages: UserMessages(), userMessages: UserMessages(),
); );
// For compatibility with tests that set this to a relative path. // For compatibility with tests that set this to a relative path.
Cache.flutterRoot = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot)); Cache.flutterRoot = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot!));
return super.runCommand(topLevelResults); return super.runCommand(topLevelResults);
} }
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment