Skip to content

Commit

Permalink
Migrate create command to null safety (#104484)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmagman committed May 25, 2022
1 parent b5adbee commit 09987dc
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 121 deletions.
111 changes: 53 additions & 58 deletions packages/flutter_tools/lib/src/commands/create.dart
Expand Up @@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.8

import '../android/gradle_utils.dart' as gradle;
import '../base/common.dart';
import '../base/context.dart';
Expand Down Expand Up @@ -32,8 +30,8 @@ const String kPlatformHelp =

class CreateCommand extends CreateBase {
CreateCommand({
bool verboseHelp = false,
}) : super(verboseHelp: verboseHelp) {
super.verboseHelp = false,
}) {
addPlatformsOptions(customHelp: kPlatformHelp);
argParser.addOption(
'template',
Expand All @@ -57,7 +55,6 @@ class CreateCommand extends CreateBase {
flutterProjectTypeToString(FlutterProjectType.module): 'Generate a project to add a Flutter module to an '
'existing Android or iOS application.',
},
defaultsTo: null,
);
argParser.addOption(
'sample',
Expand All @@ -66,7 +63,6 @@ class CreateCommand extends CreateBase {
'"--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: '
'https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html',
defaultsTo: null,
valueHelp: 'id',
);
argParser.addOption(
Expand All @@ -88,7 +84,7 @@ class CreateCommand extends CreateBase {
String get category => FlutterCommandCategory.project;

@override
String get invocation => '${runner.executableName} $name <output directory>';
String get invocation => '${runner?.executableName} $name <output directory>';

@override
Future<CustomDimensions> get usageValues async {
Expand All @@ -100,8 +96,7 @@ class CreateCommand extends CreateBase {
}

// Lazy-initialize the net utilities with values from the context.
Net _cachedNet;
Net get _net => _cachedNet ??= Net(
late final Net _net = Net(
httpClientFactory: context.get<HttpClientFactory>(),
logger: globals.logger,
platform: globals.platform,
Expand All @@ -112,25 +107,25 @@ class CreateCommand extends CreateBase {
? 'api.flutter.dev'
: 'master-api.flutter.dev';

Future<String> _fetchSampleFromServer(String sampleId) async {
Future<String?> _fetchSampleFromServer(String sampleId) async {
// Sanity check the sampleId
if (sampleId.contains(RegExp(r'[^-\w\.]'))) {
throwToolExit('Sample ID "$sampleId" contains invalid characters. Check the ID in the '
'documentation and try again.');
}

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) {
return null;
}
return utf8.decode(data);
}

/// 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 List<int> data = await _net.fetchUrl(snippetsUri, maxAttempts: 2);
final List<int>? data = await _net.fetchUrl(snippetsUri, maxAttempts: 2);
if (data == null || data.isEmpty) {
return null;
}
Expand All @@ -145,7 +140,7 @@ class CreateCommand extends CreateBase {
if (outputFile.existsSync()) {
throwToolExit('File "$outputFilePath" already exists', exitCode: 1);
}
final String samplesJson = await _fetchSamplesIndexFromServer();
final String? samplesJson = await _fetchSamplesIndexFromServer();
if (samplesJson == null) {
throwToolExit('Unable to download samples', exitCode: 2);
} else {
Expand All @@ -158,11 +153,12 @@ class CreateCommand extends CreateBase {
}

FlutterProjectType _getProjectType(Directory projectDir) {
FlutterProjectType template;
FlutterProjectType detectedProjectType;
FlutterProjectType? template;
FlutterProjectType? detectedProjectType;
final bool metadataExists = projectDir.absolute.childFile('.metadata').existsSync();
if (argResults['template'] != null) {
template = stringToProjectType(stringArgDeprecated('template'));
final String? templateArgument = stringArg('template');
if (templateArgument != null) {
template = stringToProjectType(templateArgument);
}
// If the project directory exists and isn't empty, then try to determine the template
// type from the project directory.
Expand All @@ -188,23 +184,25 @@ class CreateCommand extends CreateBase {

@override
Future<FlutterCommandResult> runCommand() async {
if (argResults['list-samples'] != null) {
final String? listSamples = stringArg('list-samples');
if (listSamples != null) {
// _writeSamplesJson can potentially be long-lived.
await _writeSamplesJson(stringArgDeprecated('list-samples'));
await _writeSamplesJson(listSamples);
return FlutterCommandResult.success();
}

validateOutputDirectoryArg();

String sampleCode;
if (argResults['sample'] != null) {
if (argResults['template'] != null &&
stringToProjectType(stringArgDeprecated('template') ?? 'app') != FlutterProjectType.app) {
String? sampleCode;
final String? sampleArgument = stringArg('sample');
if (sampleArgument != null) {
final String? templateArgument = stringArg('template');
if (templateArgument != null && stringToProjectType(templateArgument) != FlutterProjectType.app) {
throwToolExit('Cannot specify --sample with a project type other than '
'"${flutterProjectTypeToString(FlutterProjectType.app)}"');
}
// Fetch the sample from the server.
sampleCode = await _fetchSampleFromServer(stringArgDeprecated('sample'));
sampleCode = await _fetchSampleFromServer(sampleArgument);
}

final FlutterProjectType template = _getProjectType(projectDir);
Expand All @@ -215,7 +213,7 @@ class CreateCommand extends CreateBase {

final List<String> platforms = stringsArg('platforms');
// `--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';
throwToolExit(
'The "--platforms" argument is not supported in $template template.',
Expand All @@ -224,18 +222,18 @@ class CreateCommand extends CreateBase {
} else if (platforms == null || platforms.isEmpty) {
throwToolExit('Must specify at least one platform using --platforms',
exitCode: 2);
} else if (generateFfiPlugin && argResults.wasParsed('platforms') && platforms.contains('web')) {
} else if (generateFfiPlugin && argResults!.wasParsed('platforms') && platforms.contains('web')) {
throwToolExit(
'The web platform is not supported in plugin_ffi template.',
exitCode: 2,
);
} else if (generateFfiPlugin && argResults.wasParsed('ios-language')) {
} else if (generateFfiPlugin && argResults!.wasParsed('ios-language')) {
throwToolExit(
'The "ios-language" option is not supported with the plugin_ffi '
'template: the language will always be C or C++.',
exitCode: 2,
);
} else if (generateFfiPlugin && argResults.wasParsed('android-language')) {
} else if (generateFfiPlugin && argResults!.wasParsed('android-language')) {
throwToolExit(
'The "android-language" option is not supported with the plugin_ffi '
'template: the language will always be C or C++.',
Expand All @@ -258,7 +256,7 @@ class CreateCommand extends CreateBase {

final String dartSdk = globals.cache.dartSdkBuild;
final bool includeIos = featureFlags.isIOSEnabled && platforms.contains('ios');
String developmentTeam;
String? developmentTeam;
if (includeIos) {
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: globals.processManager,
Expand All @@ -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.
final String titleCaseProjectName = snakeCaseToTitleCase(projectName);

final Map<String, Object> templateContext = createTemplateContext(
final Map<String, Object?> templateContext = createTemplateContext(
organization: organization,
projectName: projectName,
titleCaseProjectName: titleCaseProjectName,
Expand Down Expand Up @@ -432,12 +430,12 @@ Your $application code is in $relativeAppMain.

Future<int> _generateModule(
Directory directory,
Map<String, dynamic> templateContext, {
Map<String, Object?> templateContext, {
bool overwrite = false,
bool printStatusWhenWriting = true,
}) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description')
: 'A new Flutter module project.';
templateContext['description'] = description;
Expand All @@ -453,7 +451,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.create,
directory: directory.path,
offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
);
final FlutterProject project = FlutterProject.fromDirectory(directory);
await project.ensureReadyForPlatformSpecificTooling(
Expand All @@ -466,12 +463,12 @@ Your $application code is in $relativeAppMain.

Future<int> _generatePackage(
Directory directory,
Map<String, dynamic> templateContext, {
Map<String, Object?> templateContext, {
bool overwrite = false,
bool printStatusWhenWriting = true,
}) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description')
: 'A new Flutter package project.';
templateContext['description'] = description;
Expand All @@ -487,21 +484,20 @@ Your $application code is in $relativeAppMain.
context: PubContext.createPackage,
directory: directory.path,
offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
);
}
return generatedCount;
}

Future<int> _generateMethodChannelPlugin(
Directory directory,
Map<String, dynamic> templateContext, {
Map<String, Object?> templateContext, {
bool overwrite = false,
bool printStatusWhenWriting = true,
FlutterProjectType projectType,
required FlutterProjectType projectType,
}) async {
// 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) {
templateContext[platform] = false;
}
Expand All @@ -517,7 +513,7 @@ Your $application code is in $relativeAppMain.
final bool willAddPlatforms = platformsToAdd.isNotEmpty;
templateContext['no_platforms'] = !willAddPlatforms;
int generatedCount = 0;
final String description = argResults.wasParsed('description')
final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description')
: 'A new Flutter plugin project.';
templateContext['description'] = description;
Expand All @@ -534,7 +530,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.createPlugin,
directory: directory.path,
offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
);
}

Expand All @@ -545,9 +540,9 @@ Your $application code is in $relativeAppMain.
project: project, requireAndroidSdk: false);
}

final String projectName = templateContext['projectName'] as String;
final String organization = templateContext['organization'] as String;
final String androidPluginIdentifier = templateContext['androidIdentifier'] as String;
final String? projectName = templateContext['projectName'] as String?;
final String organization = templateContext['organization']! as String; // Required to make the context.
final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?;
final String exampleProjectName = '${projectName}_example';
templateContext['projectName'] = exampleProjectName;
templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName);
Expand All @@ -572,13 +567,13 @@ Your $application code is in $relativeAppMain.

Future<int> _generateFfiPlugin(
Directory directory,
Map<String, dynamic> templateContext, {
Map<String, Object?> templateContext, {
bool overwrite = false,
bool printStatusWhenWriting = true,
FlutterProjectType projectType,
required FlutterProjectType projectType,
}) async {
// 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) {
templateContext[platform] = false;
}
Expand All @@ -596,7 +591,7 @@ Your $application code is in $relativeAppMain.
final bool willAddPlatforms = platformsToAdd.isNotEmpty;
templateContext['no_platforms'] = !willAddPlatforms;
int generatedCount = 0;
final String description = argResults.wasParsed('description')
final String? description = argResults!.wasParsed('description')
? stringArgDeprecated('description')
: 'A new Flutter FFI plugin project.';
templateContext['description'] = description;
Expand All @@ -613,7 +608,6 @@ Your $application code is in $relativeAppMain.
context: PubContext.createPlugin,
directory: directory.path,
offline: boolArgDeprecated('offline'),
generateSyntheticPackage: false,
);
}

Expand All @@ -623,9 +617,9 @@ Your $application code is in $relativeAppMain.
gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
}

final String projectName = templateContext['projectName'] as String;
final String organization = templateContext['organization'] as String;
final String androidPluginIdentifier = templateContext['androidIdentifier'] as String;
final String? projectName = templateContext['projectName'] as String?;
final String organization = templateContext['organization']! as String; // Required to make the context.
final String? androidPluginIdentifier = templateContext['androidIdentifier'] as String?;
final String exampleProjectName = '${projectName}_example';
templateContext['projectName'] = exampleProjectName;
templateContext['androidIdentifier'] = CreateBase.createAndroidIdentifier(organization, exampleProjectName);
Expand Down Expand Up @@ -662,7 +656,7 @@ Your $application code is in $relativeAppMain.
return -files.length;
}

List<String> _getSupportedPlatformsFromTemplateContext(Map<String, dynamic> templateContext) {
List<String> _getSupportedPlatformsFromTemplateContext(Map<String, Object?> templateContext) {
return <String>[
for (String platform in kAllCreatePlatforms)
if (templateContext[platform] == true) platform,
Expand All @@ -671,7 +665,7 @@ Your $application code is in $relativeAppMain.

// Returns a list of platforms that are explicitly requested by user via `--platforms`.
List<String> _getUserRequestedPlatforms() {
if (!argResults.wasParsed('platforms')) {
if (!argResults!.wasParsed('platforms')) {
return <String>[];
}
return stringsArg('platforms');
Expand All @@ -682,10 +676,11 @@ Your $application code is in $relativeAppMain.
// Determine what platforms are supported based on generated files.
List<String> _getSupportedPlatformsInPlugin(Directory projectDir) {
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 List<String> platforms = manifest.validSupportedPlatforms == null
final FlutterManifest? manifest = FlutterManifest.createFromPath(pubspecPath, fileSystem: globals.fs, logger: globals.logger);
final Map<String, Object?>? validSupportedPlatforms = manifest?.validSupportedPlatforms;
final List<String> platforms = validSupportedPlatforms == null
? <String>[]
: manifest.validSupportedPlatforms.keys.toList();
: validSupportedPlatforms.keys.toList();
return platforms;
}

Expand Down

0 comments on commit 09987dc

Please sign in to comment.