Simplify samples index and remove cookbook recipes (#2102)

Remove the cookbook recipes from the samples index in a step to
eventually remove it as a whole.

The cookbook recipe listings in the index haven't been updated in a long
time, the support for updating them doesn't work, and this isn't
generally how people are finding cookbook recipes.

This has the added benefit of reducing repo size quite a bit due to the
large images.
pull/2103/head
Parker Lougheed 7 months ago committed by GitHub
parent 60a4057c1f
commit ab6d874404
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +0,0 @@
## 1.0.0
- Initial version, created by Stagehand

@ -33,12 +33,3 @@ $ dart run grinder build-release
```
This outputs the completely built index to `./public`.
## Generating cookbook content
The cookbook articles are generated using a WebDriver script
that scrapes the docs.flutter.dev website. To run:
1. Install [ChromeDriver](https://chromedriver.chromium.org/downloads)
2. run `chromedriver --port=4444 --url-base=wd/hub --verbose`
3. run `dart run grinder scrape-cookbook`

@ -1,4 +1,4 @@
include: package:flutter_lints/flutter.yaml
include: package:lints/recommended.yaml
analyzer:
exclude:

@ -1,86 +0,0 @@
// Copyright 2020 The Flutter team. 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:html/parser.dart' show parse;
import 'package:path/path.dart' as path;
import 'package:samples_index/src/data.dart';
/// Utilities for generating cookbook article data
import 'package:webdriver/async_io.dart';
class CookbookScraper {
late WebDriver _driver;
Future<void> init() async {
_driver = await createDriver(desired: <String, dynamic>{});
}
Future<void> dispose() async {
await _driver.quit();
}
Future<List<String>> fetchCookbookLinks() async {
var flutterUrl = 'https://flutter.dev';
var url = Uri.parse('$flutterUrl/docs/cookbook');
await _driver.get(url);
var pageContent = await _driver.pageSource;
var page = parse(pageContent);
var links = page.querySelectorAll('main>.container>ul>li>a');
return links.map((e) => '$flutterUrl${e.attributes["href"]}').toList();
}
Future<Sample> getMetadata(String url) async {
await _driver.get(Uri.parse(url));
var pageContent = await _driver.pageSource;
var page = parse(pageContent);
var search = 'main>.container>header>h1';
var h1 = page.querySelector(search);
if (h1 == null) {
throw ('Could not find match for $search on page $url');
}
var name = h1.text;
var description = page.querySelectorAll('main>.container>p').first.text;
var urlSegments = Uri.parse(url).pathSegments;
var category = urlSegments[urlSegments.length - 2];
return Sample(
name: name,
description: description,
author: 'Flutter',
type: 'cookbook',
screenshots: [Screenshot(screenshotPath(url), 'Cookbook article')],
tags: ['cookbook', category],
source: url,
difficulty: 'advanced',
);
}
Future<void> takeScreenshot(String url) async {
var screenshot = await _driver.captureScreenshotAsList();
var file = File('web/${screenshotPath(url)}');
await file.create(recursive: true);
await file.writeAsBytes(screenshot);
}
}
String screenshotPath(String url) {
var filename = parseFileName(url);
return 'images/cookbook/$filename.png';
}
/// Parses a filename from a cookbook link. E.g.
/// `https://flutter.dev/docs/cookbook/navigation/returning-data.html` changes
/// to `returning_data.png`
String parseFileName(String link) {
var p = path.basename(link);
var dot = p.indexOf('.');
var detailName = p.substring(0, dot);
// var categoryName = path.split(link);
var components = path.split(link);
var categoryName = components[components.length - 2];
return '$categoryName-$detailName';
}

@ -2,7 +2,6 @@
// 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';
import 'package:checked_yaml/checked_yaml.dart';
@ -12,14 +11,10 @@ export 'src/data.dart';
Future<List<Sample>> getSamples() async {
var yamlFile = File('lib/src/samples.yaml');
var cookbookFile = File('lib/src/cookbook.json');
var contents = await yamlFile.readAsString();
var cookbookContents = await cookbookFile.readAsString();
var index = checkedYamlDecode(
contents, (m) => m != null ? Index.fromJson(m) : null,
sourceUrl: yamlFile.uri);
if (index == null) throw ('unable to get load from ${yamlFile.uri}');
var cookbookIndex =
Index.fromJson(json.decode(cookbookContents) as Map<dynamic, dynamic>);
return index.samples..addAll(cookbookIndex.samples);
return index.samples;
}

File diff suppressed because it is too large Load Diff

@ -36,11 +36,10 @@ class Sample {
/// The author of the sample. Typically "Flutter"
final String? author;
/// Screenshots of the sample or cookbook article. At least 1 screenshot is
/// required.
/// Screenshots of the sample. At least 1 screenshot is required.
final List<Screenshot> screenshots;
/// A link to the source code or cookbook article if type is 'cookbook'.
/// A link to the source code.
final String source;
/// A link to this sample running in the browser.
@ -69,7 +68,7 @@ class Sample {
final List<String> platforms;
/// The type of the sample. Supported values are either 'sample' or
/// 'cookbook'.
/// 'demo'.
final String type;
/// The date this sample was created.

@ -110,12 +110,6 @@ String _indexBody(List<Sample> samples) => '''
<span role="button" tabindex="-1" class="mdc-chip__text">Sample</span>
</span>
</div>
<div class="mdc-chip" role="row">
<div class="mdc-chip__ripple"></div>
<span role="gridcell">
<span role="button" tabindex="-1" class="mdc-chip__text">Cookbook</span>
</span>
</div>
<div class="mdc-chip" role="row">
<div class="mdc-chip__ripple"></div>
<span role="gridcell">
@ -205,11 +199,6 @@ String _descriptionButtons(Sample sample) {
<span class="mdc-button__label">Source Code</span>
</button>''');
}
if (sample.type == 'cookbook') {
buf.write(
'''<button class="mdc-button mdc-button--outlined" onclick="window.location.href = '${sample.source}';"> <span class="mdc-button__ripple"></span>View Recipe</button>''');
}
return buf.toString();
}

@ -1,30 +1,29 @@
name: samples_index
description: A visual index of Flutter samples
description: A visual index of Flutter samples.
homepage: https://github.com/flutter/samples/tree/main/web/samples_index
version: 0.0.1
publish_to: none
environment:
sdk: ^3.2.0
dependencies:
checked_yaml: ^2.0.3
json_annotation: ^4.8.1
path: ^1.8.3
yaml: ^3.1.2
mdc_web: ^0.6.0
path: ^1.8.3
sass_builder: ^2.2.1
checked_yaml: ^2.0.3
webdriver: ^3.0.2
html: ^0.15.3
yaml: ^3.1.2
dev_dependencies:
grinder: ^0.9.4
flutter_lints: ">=2.0.1 <4.0.0"
test: ^1.24.2
json_serializable: ^6.6.2
build: ^2.4.0
build_runner: ^2.4.2
build_web_compilers: ^4.0.3
image: ">=3.2.0 <5.0.0"
grinder: ^0.9.4
image: ^4.1.3
json_serializable: ^6.6.2
lints: ^3.0.0
test: ^1.24.2
# package:mdc_web needs to upgrade the version of material-components-web 12.0.0
# or above, which includes this fix for the division operator:

@ -122,8 +122,8 @@ void main() {
// Test if queries match by type
expect(matchesQuery('type:sample', attributes), true);
expect(matchesQuery('type:cookbook', attributes), false);
expect(matchesQuery('kittens type:cookbook', attributes), false);
expect(matchesQuery('type:demo', attributes), false);
expect(matchesQuery('kittens type:demo', attributes), false);
});
});
@ -134,7 +134,7 @@ void main() {
expect(parseHash('#?search=kittens&platform=web'),
containsPair('platform', 'web'));
expect(parseHash('#?type=sample'), containsPair('type', 'sample'));
expect(parseHash('#?type=cookbook'), containsPair('type', 'cookbook'));
expect(parseHash('#?type=demo'), containsPair('type', 'demo'));
});
test('can be set', () {

@ -22,6 +22,6 @@ samples:
href: https://apps.apple.com/us/app/neko-atsume-kitty-collector/id923917775
- text: author
href: http://jpryan.me
type: sample # sample, app, or cookbook
type: sample # sample or app
date: 2019-12-15T02:59:43.1Z
channel: stable

@ -16,6 +16,6 @@ samples:
- path
tags: ['beginner', 'kittens', 'cats']
platforms: ['web', 'ios', 'android']
type: sample # sample, app, or cookbook
type: sample # sample or app
date: 2019-12-15T02:59:43.1Z
channel: stable

@ -2,13 +2,11 @@
// 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';
import 'package:grinder/grinder.dart';
import 'package:image/image.dart' as image;
import 'package:path/path.dart' as path;
import 'package:samples_index/cookbook.dart';
import 'package:samples_index/samples_index.dart';
import 'package:samples_index/src/templates.dart' as templates;
@ -60,36 +58,9 @@ Future<void> generate() async {
log('Generated index for ${samples.length} samples.');
}
@Task('Scrape the cookbook for images and descriptions')
Future<void> scrapeCookbook() async {
var driver = await Process.start(
'chromedriver', ['--port=4444', '--url-base=wd/hub', '--verbose']);
await driver.stdout.pipe(stdout);
await driver.stderr.pipe(stderr);
var scraper = CookbookScraper();
await scraper.init();
var links = await scraper.fetchCookbookLinks();
log('Scraping ${links.length} cookbook articles');
var allSamples = <Sample>[];
for (final link in links) {
allSamples.add(await scraper.getMetadata(link));
await scraper.takeScreenshot(link);
}
var file = File('lib/src/cookbook.json');
await file.create();
var encoder = const JsonEncoder.withIndent('\t');
await file.writeAsString(encoder.convert(Index(allSamples)));
await scraper.dispose();
var killed = driver.kill();
if (!killed) {
log('failed to kill chromedriver process');
}
}
@Task('creates thumbnail images in web/images')
Future<void> createThumbnails() async {
await _createThumbnails(Directory('web/images'));
await _createThumbnails(Directory('web/images/cookbook'));
}
// Creates a thumbnail image for each png file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 648 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 688 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save