// 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 'configuration.dart'; import 'snippets.dart'; const String _kSerialOption = 'serial'; const String _kElementOption = 'element'; const String _kHelpOption = 'help'; const String _kInputOption = 'input'; const String _kLibraryOption = 'library'; const String _kOutputOption = 'output'; const String _kPackageOption = 'package'; const String _kTemplateOption = 'template'; const String _kTypeOption = 'type'; const String _kShowDartPad = 'dartpad'; String getChannelName() { final RegExp gitBranchRegexp = RegExp(r'^## (.*)'); final ProcessResult gitResult = Process.runSync('git', <String>['status', '-b', '--porcelain']); if (gitResult.exitCode != 0) throw 'git status exit with non-zero exit code: ${gitResult.exitCode}'; final Match gitBranchMatch = gitBranchRegexp.firstMatch( (gitResult.stdout as String).trim().split('\n').first); return gitBranchMatch == null ? '<unknown>' : gitBranchMatch.group(1).split('...').first; } /// Generates snippet dartdoc output for a given input, and creates any sample /// applications needed by the snippet. void main(List<String> argList) { final Map<String, String> environment = Platform.environment; final ArgParser parser = ArgParser(); final List<String> snippetTypes = SnippetType.values.map<String>((SnippetType type) => getEnumName(type)).toList(); parser.addOption( _kTypeOption, defaultsTo: getEnumName(SnippetType.sample), allowed: snippetTypes, allowedHelp: <String, String>{ getEnumName(SnippetType.sample): 'Produce a code sample application complete with embedding the sample in an ' 'application template.', getEnumName(SnippetType.snippet): 'Produce a nicely formatted piece of sample code. Does not embed the ' 'sample into an application template.', }, help: 'The type of snippet to produce.', ); parser.addOption( _kTemplateOption, defaultsTo: null, help: 'The name of the template to inject the code into.', ); parser.addOption( _kOutputOption, defaultsTo: null, help: 'The output path for the generated sample application. Overrides ' 'the naming generated by the --package/--library/--element arguments. ' 'Metadata will be written alongside in a .json file. ' 'The basename of this argument is used as the ID', ); parser.addOption( _kInputOption, defaultsTo: environment['INPUT'], help: 'The input file containing the sample code to inject.', ); parser.addOption( _kPackageOption, defaultsTo: environment['PACKAGE_NAME'], help: 'The name of the package that this sample belongs to.', ); parser.addOption( _kLibraryOption, defaultsTo: environment['LIBRARY_NAME'], help: 'The name of the library that this sample belongs to.', ); parser.addOption( _kElementOption, defaultsTo: environment['ELEMENT_NAME'], help: 'The name of the element that this sample belongs to.', ); parser.addOption( _kSerialOption, defaultsTo: environment['INVOCATION_INDEX'], help: 'A unique serial number for this snippet tool invocation.', ); parser.addFlag( _kHelpOption, defaultsTo: false, negatable: false, help: 'Prints help documentation for this command', ); parser.addFlag( _kShowDartPad, defaultsTo: false, negatable: false, help: "Indicates whether DartPad should be included in the sample's " 'final HTML output. This flag only applies when the type parameter is ' '"sample".', ); final ArgResults args = parser.parse(argList); if (args[_kHelpOption] as bool) { stderr.writeln(parser.usage); exit(0); } final SnippetType snippetType = SnippetType.values .firstWhere((SnippetType type) => getEnumName(type) == args[_kTypeOption], orElse: () => null); assert(snippetType != null, "Unable to find '${args[_kTypeOption]}' in SnippetType enum."); if (args[_kShowDartPad] == true && snippetType != SnippetType.sample) { errorExit('${args[_kTypeOption]} was selected, but the --dartpad flag is only valid ' 'for application sample code.'); } if (args[_kInputOption] == null) { stderr.writeln(parser.usage); errorExit('The --$_kInputOption option must be specified, either on the command ' 'line, or in the INPUT environment variable.'); } final File input = File(args['input'] as String); if (!input.existsSync()) { errorExit('The input file ${input.path} does not exist.'); } String template; if (snippetType == SnippetType.sample) { final String templateArg = args[_kTemplateOption] as String; if (templateArg == null || templateArg.isEmpty) { stderr.writeln(parser.usage); errorExit('The --$_kTemplateOption option must be specified on the command ' 'line for application samples.'); } template = templateArg.replaceAll(RegExp(r'.tmpl$'), ''); } String emptyToNull(String value) => value?.isEmpty ?? true ? null : value; final String packageName = emptyToNull(args[_kPackageOption] as String); final String libraryName = emptyToNull(args[_kLibraryOption] as String); final String elementName = emptyToNull(args[_kElementOption] as String); final String serial = emptyToNull(args[_kSerialOption] as String); final List<String> id = <String>[]; if (args[_kOutputOption] != null) { id.add(path.basename(path.basenameWithoutExtension(args[_kOutputOption] as String))); } else { if (packageName != null && packageName != 'flutter') { id.add(packageName); } if (libraryName != null) { id.add(libraryName); } if (elementName != null) { id.add(elementName); } if (serial != null) { id.add(serial); } if (id.isEmpty) { errorExit('Unable to determine ID. At least one of --$_kPackageOption, ' '--$_kLibraryOption, --$_kElementOption, -$_kSerialOption, or the environment variables ' 'PACKAGE_NAME, LIBRARY_NAME, ELEMENT_NAME, or INVOCATION_INDEX must be non-empty.'); } } final SnippetGenerator generator = SnippetGenerator(); stdout.write(generator.generate( input, snippetType, showDartPad: args[_kShowDartPad] as bool, template: template, output: args[_kOutputOption] != null ? File(args[_kOutputOption] as String) : null, metadata: <String, Object>{ 'sourcePath': environment['SOURCE_PATH'], 'sourceLine': environment['SOURCE_LINE'] != null ? int.tryParse(environment['SOURCE_LINE']) : null, 'id': id.join('.'), 'channel': getChannelName(), 'serial': serial, 'package': packageName, 'library': libraryName, 'element': elementName, }, )); exit(0); }