diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index 2dd229b184cf..3d39abc136bb 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -7,6 +7,5 @@ # https://github.com/flutter/flutter/issues/102679 - espresso - in_app_purchase/in_app_purchase -- mustache_template - pointer_interceptor - quick_actions/quick_actions diff --git a/third_party/packages/mustache_template/CHANGELOG.md b/third_party/packages/mustache_template/CHANGELOG.md index 634e9abae3cd..7980772e932d 100644 --- a/third_party/packages/mustache_template/CHANGELOG.md +++ b/third_party/packages/mustache_template/CHANGELOG.md @@ -1,5 +1,11 @@ ## 2.0.4 +* Adds an `example/` app with runnable usage samples. +* Updates README examples to use excerpt-managed snippets. +* Adds unit tests for README code excerpts under `example/test/`. +* Moves excerpt sources to `example/lib/readme_excerpts.dart`. +* Fixes the simple lambda README example to use `{{ foo }}` instead of an + invalid unclosed section tag. * Fixes a broken README link to the Mustache manual. ## 2.0.3 diff --git a/third_party/packages/mustache_template/README.md b/third_party/packages/mustache_template/README.md index cf4dab61e4e6..e536ac7c1b50 100644 --- a/third_party/packages/mustache_template/README.md +++ b/third_party/packages/mustache_template/README.md @@ -2,7 +2,7 @@ A Dart library to parse and render [mustache templates](https://mustache.github.io/). -See the [mustache manual](https://mustache.github.io/mustache.5.html) for detailed usage information. +See the [mustache manual](http://mustache.github.com/mustache.5.html) for detailed usage information. This library passes all [mustache specification](https://github.com/mustache/spec/tree/master/specs) tests. @@ -46,11 +46,24 @@ By default all output from `{{variable}}` tags is html escaped, this behaviour c * During rendering, if no map key or object member which matches the tag name is found, then a TemplateException will be thrown. +```dart +try { + Template('{{missing}}').renderString({}); +} on TemplateException catch (e) { + // Strict mode (default): missing keys throw when rendering. +} +``` + ### Lenient mode * Tag names may use any characters. * During rendering, if no map key or object member which matches the tag name is found, then silently ignore and output nothing. +```dart +final t = Template('{{missing}}', lenient: true); +final String output = t.renderString({}); // '' +``` + ## Nested paths ```dart diff --git a/third_party/packages/mustache_template/example/lib/readme_excerpts.dart b/third_party/packages/mustache_template/example/lib/readme_excerpts.dart new file mode 100644 index 000000000000..cb40410aa94f --- /dev/null +++ b/third_party/packages/mustache_template/example/lib/readme_excerpts.dart @@ -0,0 +1,121 @@ +// This file hosts README code excerpts (see README.md) and is imported by the +// example runner and excerpt tests. + +// ignore_for_file: public_member_api_docs + +import 'package:mustache_template/mustache_template.dart'; + +String basicRenderExample() { + // #docregion BasicRender + var source = ''' +{{# names }} +
{{ lastname }}, {{ firstname }}
+{{/ names }} +{{^ names }} +
No names.
+{{/ names }} +{{! I am a comment. }} +'''; + + var template = Template(source, name: 'names-template'); + var output = template.renderString({ + 'names': >[ + {'firstname': 'Greg', 'lastname': 'Lowe'}, + {'firstname': 'Bob', 'lastname': 'Johnson'}, + ], + }); + // #enddocregion BasicRender + return output; +} + +String nestedPathsExample() { + // #docregion NestedPaths + var t = Template('{{ author.name }}'); + var output = t.renderString({ + 'author': {'name': 'Greg Lowe'}, + }); + // #enddocregion NestedPaths + return output; +} + +String partialsExample() { + // #docregion Partials + var partial = Template('{{ foo }}', name: 'partial'); + Template resolver(String name) { + if (name == 'partial-name') { + return partial; + } + throw StateError('Unknown partial: $name'); + } + + var t = Template('{{> partial-name }}', partialResolver: resolver); + var output = t.renderString({'foo': 'bar'}); // bar + // #enddocregion Partials + return output; +} + +String lambdaSimpleExample() { + // #docregion LambdaSimpleValue + var t = Template('{{ foo }}'); + + var lambda = (_) => 'bar'; + + var output = t.renderString({'foo': lambda}); // bar + // #enddocregion LambdaSimpleValue + return output; +} + +String lambdaShownExample() { + // #docregion LambdaSectionReplacement + var t = Template('{{# foo }}hidden{{/ foo }}'); + var lambda = (_) => 'shown'; + + var output = t.renderString({'foo': lambda}); // shown + // #enddocregion LambdaSectionReplacement + return output; +} + +String lambdaRenderExample() { + // #docregion LambdaRenderString + var t = Template('{{# foo }}{{bar}}{{/ foo }}'); + var lambda = (LambdaContext context) => + '${context.renderString().toUpperCase()}'; + + var output = t.renderString({'foo': lambda, + 'bar': 'pub', + }); // PUB + // #enddocregion LambdaRenderString + return output; +} + +String lambdaRenderSourceExample() { + // #docregion LambdaRenderSource + var t = Template('{{# foo }}{{bar}}{{/ foo }}'); + var lambda = (LambdaContext ctx) => + '${ctx.renderString().toUpperCase()}'; + + var output = t.renderString({'foo': lambda, + 'bar': 'pub', + }); // PUB + // #enddocregion LambdaRenderSource + return output; +} + +String strictModeBehaviorExample() { + // #docregion StrictMode + try { + Template('{{missing}}').renderString({}); + return 'No exception thrown (unexpected)'; + } on TemplateException catch (e) { + return 'Strict mode exception: ${e.runtimeType}'; + } + // #enddocregion StrictMode +} + +String lenientModeBehaviorExample() { + // #docregion LenientMode + final t = Template('{{missing}}', lenient: true); + final String output = t.renderString({}); // '' + // #enddocregion LenientMode + return output; +} diff --git a/third_party/packages/mustache_template/example/main.dart b/third_party/packages/mustache_template/example/main.dart new file mode 100644 index 000000000000..7bbed9529aed --- /dev/null +++ b/third_party/packages/mustache_template/example/main.dart @@ -0,0 +1,15 @@ +// ignore_for_file: avoid_print + +import 'package:mustache_template_example/readme_excerpts.dart'; + +void main() { + print(basicRenderExample()); + print(nestedPathsExample()); + print(partialsExample()); + print(lambdaSimpleExample()); + print(lambdaShownExample()); + print(lambdaRenderExample()); + print(lambdaRenderSourceExample()); + print(strictModeBehaviorExample()); + print(lenientModeBehaviorExample()); +} diff --git a/third_party/packages/mustache_template/example/pubspec.yaml b/third_party/packages/mustache_template/example/pubspec.yaml new file mode 100644 index 000000000000..040ea71d2ff5 --- /dev/null +++ b/third_party/packages/mustache_template/example/pubspec.yaml @@ -0,0 +1,12 @@ +name: mustache_template_example +publish_to: none + +environment: + sdk: ^3.9.0 + +dependencies: + mustache_template: + path: ../ + +dev_dependencies: + test: ^1.16.5 diff --git a/third_party/packages/mustache_template/example/test/readme_excerpts_test.dart b/third_party/packages/mustache_template/example/test/readme_excerpts_test.dart new file mode 100644 index 000000000000..74e8c65b6173 --- /dev/null +++ b/third_party/packages/mustache_template/example/test/readme_excerpts_test.dart @@ -0,0 +1,69 @@ +// ignore_for_file: avoid_relative_lib_imports + +import 'dart:async'; + +import 'package:mustache_template_example/readme_excerpts.dart' + as readme_excerpts; +import 'package:test/test.dart'; + +import '../main.dart' as example_app; + +void main() { + group('Example app', () { + test('example app runs without error', () { + expect( + () => runZoned( + () => example_app.main(), + zoneSpecification: ZoneSpecification( + print: (Zone self, ZoneDelegate parent, Zone zone, String line) {}, + ), + ), + returnsNormally, + ); + }); + }); + + group('README excerpts', () { + test('basic render example includes rendered names', () { + final String out = readme_excerpts.basicRenderExample(); + expect(out, contains('Lowe, Greg')); + expect(out, contains('Johnson, Bob')); + }); + + test('nested paths example renders the nested value', () { + expect(readme_excerpts.nestedPathsExample(), equals('Greg Lowe')); + }); + + test('partials example renders the partial output', () { + expect(readme_excerpts.partialsExample(), equals('bar')); + }); + + test('simple lambda example renders the replacement text', () { + expect(readme_excerpts.lambdaSimpleExample(), equals('bar')); + }); + + test('lambda block example renders the alternate text', () { + expect(readme_excerpts.lambdaShownExample(), equals('shown')); + }); + + test('lambda render example uppercases the section body', () { + expect(readme_excerpts.lambdaRenderExample(), equals('PUB')); + }); + + test('lambda renderSource example uppercases the section body', () { + expect( + readme_excerpts.lambdaRenderSourceExample(), equals('PUB')); + }); + + test('strict mode throws for missing keys', () { + expect( + readme_excerpts.strictModeBehaviorExample(), + contains('Strict mode exception'), + ); + }); + + test('lenient mode renders empty for missing keys', () { + expect(readme_excerpts.lenientModeBehaviorExample(), isEmpty); + }); + }); +}