From b5c5bae42bbecb71abdecc1484b5b3986ffc3bd0 Mon Sep 17 00:00:00 2001 From: Danny Tuppeny Date: Tue, 8 Jan 2019 10:38:59 +0000 Subject: [PATCH] Write snippets index file when generating docs (#25515) --- dev/snippets/lib/main.dart | 25 ++++++++++++------- dev/snippets/lib/snippets.dart | 21 +++++++++++++++- dev/snippets/test/snippets_test.dart | 36 ++++++++++++++++++++++++++++ dev/tools/dartdoc.dart | 18 ++++++++++++++ 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/dev/snippets/lib/main.dart b/dev/snippets/lib/main.dart index 3f47559205..997e44260d 100644 --- a/dev/snippets/lib/main.dart +++ b/dev/snippets/lib/main.dart @@ -52,6 +52,7 @@ void main(List argList) { defaultsTo: null, help: 'The output path for the generated snippet 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( @@ -113,20 +114,21 @@ void main(List argList) { template = args[_kTemplateOption].toString().replaceAll(RegExp(r'.tmpl$'), ''); } + final String packageName = args[_kPackageOption] != null && args[_kPackageOption].isNotEmpty ? args[_kPackageOption] : null; + final String libraryName = args[_kLibraryOption] != null && args[_kLibraryOption].isNotEmpty ? args[_kLibraryOption] : null; + final String elementName = args[_kElementOption] != null && args[_kElementOption].isNotEmpty ? args[_kElementOption] : null; final List id = []; if (args[_kOutputOption] != null) { id.add(path.basename(path.basenameWithoutExtension(args[_kOutputOption]))); } else { - if (args[_kPackageOption] != null && - args[_kPackageOption].isNotEmpty && - args[_kPackageOption] != 'flutter') { - id.add(args[_kPackageOption]); + if (packageName != null && packageName != 'flutter') { + id.add(packageName); } - if (args[_kLibraryOption] != null && args[_kLibraryOption].isNotEmpty) { - id.add(args[_kLibraryOption]); + if (libraryName != null) { + id.add(libraryName); } - if (args[_kElementOption] != null && args[_kElementOption].isNotEmpty) { - id.add(args[_kElementOption]); + if (elementName != null) { + id.add(elementName); } if (id.isEmpty) { errorExit('Unable to determine ID. At least one of --$_kPackageOption, ' @@ -142,6 +144,13 @@ void main(List argList) { template: template, id: id.join('.'), output: args[_kOutputOption] != null ? File(args[_kOutputOption]) : null, + metadata: { + 'sourcePath': environment['SOURCE_PATH'], + 'package': packageName, + 'library': libraryName, + 'element': elementName, + }, )); + exit(0); } diff --git a/dev/snippets/lib/snippets.dart b/dev/snippets/lib/snippets.dart index cb3201c57c..0881694dd3 100644 --- a/dev/snippets/lib/snippets.dart +++ b/dev/snippets/lib/snippets.dart @@ -39,6 +39,8 @@ class SnippetGenerator { /// snippet. final Configuration configuration; + static const JsonEncoder jsonEncoder = JsonEncoder.withIndent(' '); + /// A Dart formatted used to format the snippet code and finished application /// code. static DartFormatter formatter = DartFormatter(pageWidth: 80, fixes: StyleFix.all); @@ -193,7 +195,7 @@ class SnippetGenerator { /// The [id] is a string ID to use for the output file, and to tell the user /// about in the `flutter create` hint. It must not be null if the [type] is /// [SnippetType.application]. - String generate(File input, SnippetType type, {String template, String id, File output}) { + String generate(File input, SnippetType type, {String template, String id, File output, Map metadata}) { assert(template != null || type != SnippetType.application); assert(id != null || type != SnippetType.application); assert(input != null); @@ -226,6 +228,23 @@ class SnippetGenerator { final File outputFile = output ?? getOutputFile(id); stderr.writeln('Writing to ${outputFile.absolute.path}'); outputFile.writeAsStringSync(app); + + final File metadataFile = File(path.join(path.dirname(outputFile.path), + '${path.basenameWithoutExtension(outputFile.path)}.json')); + stderr.writeln('Writing metadata to ${metadataFile.absolute.path}'); + final _ComponentTuple description = snippetData.firstWhere( + (_ComponentTuple data) => data.name == 'description', + orElse: () => null, + ); + metadata ??= {}; + metadata.addAll({ + 'id': id, + 'file': path.basename(outputFile.path), + 'description': description != null + ? description.mergedContent + : null, + }); + metadataFile.writeAsStringSync(jsonEncoder.convert(metadata)); break; case SnippetType.sample: break; diff --git a/dev/snippets/test/snippets_test.dart b/dev/snippets/test/snippets_test.dart index c73e38ded9..9b1a07bff0 100644 --- a/dev/snippets/test/snippets_test.dart +++ b/dev/snippets/test/snippets_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert'; import 'dart:io' hide Platform; import 'package:path/path.dart' as path; @@ -111,5 +112,40 @@ void main() { 'On several lines.{@inject-html}\n')); expect(html, contains('main() {')); }); + + test('generates snippet application metadata', () async { + final File inputFile = File(path.join(tmpDir.absolute.path, 'snippet_in.txt')) + ..createSync(recursive: true) + ..writeAsStringSync(''' +A description of the snippet. + +On several lines. + +```code +void main() { + print('The actual \$name.'); +} +``` +'''); + + final File outputFile = File(path.join(tmpDir.absolute.path, 'snippet_out.dart')); + final File expectedMetadataFile = File(path.join(tmpDir.absolute.path, 'snippet_out.json')); + + generator.generate( + inputFile, + SnippetType.application, + template: 'template', + id: 'id', + output: outputFile, + metadata: {'sourcePath': 'some/path.dart'}, + ); + expect(expectedMetadataFile.existsSync(), isTrue); + final Map json = jsonDecode(expectedMetadataFile.readAsStringSync()); + expect(json['id'], equals('id')); + expect(json['file'], equals('snippet_out.dart')); + expect(json['description'], equals('A description of the snippet.\n\nOn several lines.')); + // Ensure any passed metadata is included in the output JSON too. + expect(json['sourcePath'], equals('some/path.dart')); + }); }); } diff --git a/dev/tools/dartdoc.dart b/dev/tools/dartdoc.dart index 2c259cf39e..de3751c2be 100644 --- a/dev/tools/dartdoc.dart +++ b/dev/tools/dartdoc.dart @@ -353,6 +353,7 @@ void createIndexAndCleanup() { addHtmlBaseToIndex(); changePackageToSdkInTitlebar(); putRedirectInOldIndexLocation(); + writeSnippetsIndexFile(); print('\nDocs ready to go!'); } @@ -407,6 +408,23 @@ void putRedirectInOldIndexLocation() { File('$kPublishRoot/flutter/index.html').writeAsStringSync(metaTag); } + +void writeSnippetsIndexFile() { + final Directory snippetsDir = Directory(path.join(kPublishRoot, 'snippets')); + if (snippetsDir.existsSync()) { + const JsonEncoder jsonEncoder = JsonEncoder.withIndent(' '); + final Iterable files = snippetsDir + .listSync() + .whereType() + .where((File file) => path.extension(file.path) == '.json'); + // Combine all the metadata into a single JSON array. + final Iterable fileContents = files.map((File file) => file.readAsStringSync()); + final List metadataObjects = fileContents.map(json.decode).toList(); + final String jsonArray = jsonEncoder.convert(metadataObjects); + File('$kPublishRoot/snippets/index.json').writeAsStringSync(jsonArray); + } +} + List findPackageNames() { return findPackages().map((FileSystemEntity file) => path.basename(file.path)).toList(); }