@@ -28,9 +28,6 @@ Directory outputDirectory;
2828Directory sampleDirectory;
2929Directory testDirectory;
3030Directory driverDirectory;
31- String sampleTemplate;
32- String screenshotTemplate;
33- String screenshotDriverTemplate;
3431
3532void logMessage (String s) { print (s); }
3633void logError (String s) { print (s); }
@@ -44,18 +41,11 @@ File outputFile(String name, [Directory directory]) {
4441}
4542
4643void initialize () {
47- final File sampleTemplateFile = inputFile ('bin' , 'sample_page.md.template' );
48- final File screenshotTemplateFile = inputFile ('bin' , 'screenshot.dart.template' );
49- final File screenshotDriverTemplateFile = inputFile ('bin' , 'screenshot_test.dart.template' );
50-
5144 outputDirectory = new Directory ('.generated' );
5245 sampleDirectory = new Directory ('lib' );
5346 testDirectory = new Directory ('test' );
5447 driverDirectory = new Directory ('test_driver' );
5548 outputDirectory.createSync ();
56- sampleTemplate = sampleTemplateFile.readAsStringSync ();
57- screenshotTemplate = screenshotTemplateFile.readAsStringSync ();
58- screenshotDriverTemplate = screenshotDriverTemplateFile.readAsStringSync ();
5949}
6050
6151// Return a copy of template with each occurrence of @(foo) replaced
@@ -76,19 +66,31 @@ void writeExpandedTemplate(File output, String template, Map<String, String> val
7666 logMessage ('wrote $output ' );
7767}
7868
79- class SampleGenerator {
80- SampleGenerator (this .sourceFile);
69+ class SampleInfo {
70+ SampleInfo (this .sourceFile, this .commit );
8171
8272 final File sourceFile;
73+ final String commit;
8374 String sourceCode;
8475 Map <String , String > commentValues;
8576
8677 // If sourceFile is lib/foo.dart then sourceName is foo. The sourceName
8778 // is used to create derived filenames like foo.md or foo.png.
8879 String get sourceName => basenameWithoutExtension (sourceFile.path);
8980
81+ // The website's link to this page will be /catalog/samples/@(link)/.
82+ String get link => sourceName.replaceAll ('_' , '-' );
83+
9084 // The name of the widget class that defines this sample app, like 'FooSample'.
91- String get sampleClass => commentValues["sample" ];
85+ String get sampleClass => commentValues['sample' ];
86+
87+ // The value of the 'Classes:' comment as a list of class names.
88+ Iterable <String > get highlightedClasses {
89+ final String classNames = commentValues['classes' ];
90+ if (classNames == null )
91+ return const < String > [];
92+ return classNames.split (',' ).map ((String s) => s.trim ()).where ((String s) => s.isNotEmpty);
93+ }
9294
9395 // The relative import path for this sample, like '../lib/foo.dart'.
9496 String get importPath => '..' + Platform .pathSeparator + sourceFile.path;
@@ -133,64 +135,123 @@ class SampleGenerator {
133135 commentValues['name' ] = sourceName;
134136 commentValues['path' ] = 'examples/catalog/${sourceFile .path }' ;
135137 commentValues['source' ] = sourceCode.trim ();
138+ commentValues['link' ] = link;
139+ commentValues['android screenshot' ] = 'https://storage.googleapis.com/flutter-catalog/$commit /${sourceName }_small.png' ;
136140
137141 return true ;
138142 }
139143}
140144
141- void generate () {
145+ void generate (String commit ) {
142146 initialize ();
143147
144- final List <SampleGenerator > samples = < SampleGenerator > [];
148+ final List <SampleInfo > samples = < SampleInfo > [];
145149 sampleDirectory.listSync ().forEach ((FileSystemEntity entity) {
146150 if (entity is File && entity.path.endsWith ('.dart' )) {
147- final SampleGenerator sample = new SampleGenerator (entity);
148- if (sample.initialize ()) { // skip files that lack the Sample Catalog comment
149- writeExpandedTemplate (
150- outputFile (sample.sourceName + '.md' ),
151- sampleTemplate,
152- sample.commentValues,
153- );
151+ final SampleInfo sample = new SampleInfo (entity, commit);
152+ if (sample.initialize ()) // skip files that lack the Sample Catalog comment
154153 samples.add (sample);
155- }
156154 }
157155 });
158156
159157 // Causes the generated imports to appear in alphabetical order.
160158 // Avoid complaints from flutter lint.
161- samples.sort ((SampleGenerator a, SampleGenerator b) {
159+ samples.sort ((SampleInfo a, SampleInfo b) {
162160 return a.sourceName.compareTo (b.sourceName);
163161 });
164162
163+ final String entryTemplate = inputFile ('bin' , 'entry.md.template' ).readAsStringSync ();
164+
165+ // Write the sample catalog's home page: index.md
166+ final Iterable <String > entries = samples.map ((SampleInfo sample) {
167+ return expandTemplate (entryTemplate, sample.commentValues);
168+ });
169+ writeExpandedTemplate (
170+ outputFile ('index.md' ),
171+ inputFile ('bin' , 'index.md.template' ).readAsStringSync (),
172+ < String , String > {
173+ 'entries' : entries.join ('\n ' ),
174+ },
175+ );
176+
177+ // Write the sample app files, like animated_list.md
178+ for (SampleInfo sample in samples) {
179+ writeExpandedTemplate (
180+ outputFile (sample.sourceName + '.md' ),
181+ inputFile ('bin' , 'sample_page.md.template' ).readAsStringSync (),
182+ sample.commentValues,
183+ );
184+ }
185+
186+ // For each unique class listened in a sample app's "Classes:" list, generate
187+ // a file that's structurally the same as index.md but only contains samples
188+ // that feature one class. For example AnimatedList_index.md would only
189+ // include samples that had AnimatedList in their "Classes:" list.
190+ final Map <String , List <SampleInfo >> classToSamples = < String , List <SampleInfo >> {};
191+ for (SampleInfo sample in samples) {
192+ for (String className in sample.highlightedClasses) {
193+ classToSamples[className] ?? = < SampleInfo > [];
194+ classToSamples[className].add (sample);
195+ }
196+ }
197+ for (String className in classToSamples.keys) {
198+ final Iterable <String > entries = classToSamples[className].map ((SampleInfo sample) {
199+ return expandTemplate (entryTemplate, sample.commentValues);
200+ });
201+ writeExpandedTemplate (
202+ outputFile ('${className }_index.md' ),
203+ inputFile ('bin' , 'class_index.md.template' ).readAsStringSync (),
204+ < String , String > {
205+ 'class' : '$className ' ,
206+ 'entries' : entries.join ('\n ' ),
207+ 'link' : '${className }_index' ,
208+ },
209+ );
210+ }
211+
212+ // Write screenshot.dart, a "test" app that displays each sample
213+ // app in turn when the app is tapped.
165214 writeExpandedTemplate (
166215 outputFile ('screenshot.dart' , driverDirectory),
167- screenshotTemplate ,
216+ inputFile ( 'bin' , 'screenshot.dart.template' ). readAsStringSync () ,
168217 < String , String > {
169- 'imports' : samples.map ((SampleGenerator page) {
218+ 'imports' : samples.map ((SampleInfo page) {
170219 return "import '${page .importPath }' show ${page .sampleClass };\n " ;
171220 }).toList ().join (),
172- 'widgets' : samples.map ((SampleGenerator sample) {
221+ 'widgets' : samples.map ((SampleInfo sample) {
173222 return 'new ${sample .sampleClass }(),\n ' ;
174223 }).toList ().join (),
175224 },
176225 );
177226
227+ // Write screenshot_test.dart, a test driver for screenshot.dart
228+ // that collects screenshots of each app and saves them.
178229 writeExpandedTemplate (
179230 outputFile ('screenshot_test.dart' , driverDirectory),
180- screenshotDriverTemplate ,
231+ inputFile ( 'bin' , 'screenshot_test.dart.template' ). readAsStringSync () ,
181232 < String , String > {
182- 'paths' : samples.map ((SampleGenerator sample) {
233+ 'paths' : samples.map ((SampleInfo sample) {
183234 return "'${outputFile (sample .sourceName + '.png' ).path }'" ;
184235 }).toList ().join (',\n ' ),
185236 },
186237 );
187238
188- // To generate the screenshots: flutter drive test_driver/screenshot.dart
239+ // For now, the website's index.json file must be updated by hand.
240+ logMessage ('The following entries must appear in _data/catalog/widgets.json' );
241+ for (String className in classToSamples.keys)
242+ logMessage ('"sample": "${className }_index"' );
189243}
190244
191245void main (List <String > args) {
246+ if (args.length != 1 ) {
247+ logError (
248+ 'Usage (cd examples/catalog/; dart bin/sample_page.dart commit)\n '
249+ 'The flutter commit hash locates screenshots on storage.googleapis.com/flutter-catalog/'
250+ );
251+ exit (255 );
252+ }
192253 try {
193- generate ();
254+ generate (args[ 0 ] );
194255 } catch (error) {
195256 logError (
196257 'Error: sample_page.dart failed: $error \n '
@@ -199,6 +260,5 @@ void main(List<String> args) {
199260 );
200261 exit (255 );
201262 }
202-
203263 exit (0 );
204264}
0 commit comments