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 67f53c1a8fc9..5d58f3bb4bdd 100644 --- a/third_party/packages/mustache_template/CHANGELOG.md +++ b/third_party/packages/mustache_template/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.4 + +* Adds a runnable example app and refreshes README code samples. + ## 2.0.3 * Updates minimum supported SDK version to Flutter 3.35/Dart 3.9. diff --git a/third_party/packages/mustache_template/README.md b/third_party/packages/mustache_template/README.md index 7602c2755160..469fca64a40c 100644 --- a/third_party/packages/mustache_template/README.md +++ b/third_party/packages/mustache_template/README.md @@ -1,3 +1,5 @@ + + # Mustache templates A Dart library to parse and render [mustache templates](https://mustache.github.io/). @@ -7,29 +9,34 @@ See the [mustache manual](http://mustache.github.com/mustache.5.html) for detail This library passes all [mustache specification](https://github.com/mustache/spec/tree/master/specs) tests. ## Example usage + + ```dart import 'package:mustache_template/mustache_template.dart'; -main() { - var source = ''' - {{# names }} -
{{ lastname }}, {{ firstname }}
- {{/ names }} - {{^ names }} -
No names.
- {{/ names }} - {{! I am a comment. }} - '''; - - var template = Template(source, name: 'template-filename.html'); - - var output = template.renderString({'names': [ - {'firstname': 'Greg', 'lastname': 'Lowe'}, - {'firstname': 'Bob', 'lastname': 'Johnson'} - ]}); - - print(output); +void main() { + const source = ''' +{{# names }} +
{{ lastname }}, {{ firstname }}
+{{/ names }} +{{^ names }} +
No names.
+{{/ names }} +{{! I am a comment. }} +'''; + + final template = Template(source, name: 'template-filename.html'); + + final String output = template.renderString({ + 'names': [ + {'firstname': 'Greg', 'lastname': 'Lowe'}, + {'firstname': 'Bob', 'lastname': 'Johnson'}, + ], + }); + + print(output); } + ``` A template is parsed when it is created, after parsing it can be rendered any number of times with different values. A TemplateException is thrown if there is a problem parsing or rendering the template. @@ -53,65 +60,75 @@ By default all output from `{{variable}}` tags is html escaped, this behaviour c ## Nested paths + ```dart - var t = Template('{{ author.name }}'); - var output = template.renderString({'author': {'name': 'Greg Lowe'}}); +final t = Template('{{ author.name }}'); +final output = t.renderString({ + 'author': {'name': 'Greg Lowe'}, +}); ``` ## Partials - example usage + ```dart - -var partial = Template('{{ foo }}', name: 'partial'); - -var resolver = (String name) { - if (name == 'partial-name') { // Name of partial tag. - return partial; - } +final partial = Template('{{ foo }}', name: 'partial'); + +final resolver = (String name) { + if (name == 'partial-name') { + // Name of partial tag. + return partial; + } + return null; }; -var t = Template('{{> partial-name }}', partialResolver: resolver); - -var output = t.renderString({'foo': 'bar'}); // bar +final t = Template('{{> partial-name }}', partialResolver: resolver); +final output = t.renderString({'foo': 'bar'}); // bar ``` ## Lambdas - example usage + ```dart -var t = Template('{{# foo }}'); -var lambda = (_) => 'bar'; -t.renderString({'foo': lambda}); // bar -``` - -```dart -var t = Template('{{# foo }}hidden{{/ foo }}'); -var lambda = (_) => 'shown'; -t.renderString('foo': lambda); // shown +final t = Template('{{# foo }}{{/ foo }}'); +final lambda = (_) => 'bar'; +final output = t.renderString({'foo': lambda}); // bar ``` + ```dart -var t = Template('{{# foo }}oi{{/ foo }}'); -var lambda = (LambdaContext ctx) => '${ctx.renderString().toUpperCase()}'; -t.renderString({'foo': lambda}); // OI +final t = Template('{{# foo }}hidden{{/ foo }}'); +final lambda = (_) => 'shown'; +final output = t.renderString({'foo': lambda}); // shown ``` + ```dart -var t = Template('{{# foo }}{{bar}}{{/ foo }}'); -var lambda = (LambdaContext ctx) => '${ctx.renderString().toUpperCase()}'; -t.renderString({'foo': lambda, 'bar': 'pub'}); // PUB +final t = Template('{{# foo }}oi{{/ foo }}'); +final lambda = (LambdaContext ctx) => + '${ctx.renderString().toUpperCase()}'; +final output = t.renderString({'foo': lambda}); // OI ``` + ```dart -var t = Template('{{# foo }}{{bar}}{{/ foo }}'); -var lambda = (LambdaContext ctx) => '${ctx.renderString().toUpperCase()}'; -t.renderString({'foo': lambda, 'bar': 'pub'}); // PUB +final t = Template('{{# foo }}{{bar}}{{/ foo }}'); +final lambda = (LambdaContext ctx) => + '${ctx.renderString().toUpperCase()}'; +final output = t.renderString({'foo': lambda, 'bar': 'pub'}); // PUB ``` In the following example `LambdaContext.renderSource(source)` re-parses the source string in the current context, this is the default behaviour in many mustache implementations. Since re-parsing the content is slow, and often not required, this library makes this step optional. + ```dart -var t = Template('{{# foo }}{{bar}}{{/ foo }}'); -var lambda = (LambdaContext ctx) => ctx.renderSource(ctx.source + ' {{cmd}}'); -t.renderString({'foo': lambda, 'bar': 'pub', 'cmd': 'build'}); // pub build +final t = Template('{{# foo }}{{bar}}{{/ foo }}'); +final lambda = (LambdaContext ctx) => + ctx.renderSource('${ctx.source} {{cmd}}'); +final output = t.renderString({ + 'foo': lambda, + 'bar': 'pub', + 'cmd': 'build', +}); // pub build ``` diff --git a/third_party/packages/mustache_template/example/lib/main.dart b/third_party/packages/mustache_template/example/lib/main.dart new file mode 100644 index 000000000000..e5b237e9b501 --- /dev/null +++ b/third_party/packages/mustache_template/example/lib/main.dart @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: avoid_print, prefer_function_declarations_over_variables + +// #docregion ExampleUsage +import 'package:mustache_template/mustache_template.dart'; + +void main() { + // #enddocregion ExampleUsage + print('=== Basic Template ==='); + + // Basic template rendering with sections and inverted sections. + // #docregion ExampleUsage + const source = ''' +{{# names }} +
{{ lastname }}, {{ firstname }}
+{{/ names }} +{{^ names }} +
No names.
+{{/ names }} +{{! I am a comment. }} +'''; + + final template = Template(source, name: 'template-filename.html'); + + final String output = template.renderString({ + 'names': [ + {'firstname': 'Greg', 'lastname': 'Lowe'}, + {'firstname': 'Bob', 'lastname': 'Johnson'}, + ], + }); + + print(output); + // #enddocregion ExampleUsage + + // Nested paths + final nested = Template('{{ author.name }}'); + print('=== Nested Paths ==='); + print( + nested.renderString({ + 'author': {'name': 'Greg Lowe'}, + }), + ); + + // Lambdas + final lambdaTemplate = Template('{{# transform }}hello{{/ transform }}'); + final String Function(LambdaContext) lambda = (LambdaContext ctx) => + ctx.renderString().toUpperCase(); + print('=== Lambdas ==='); + print(lambdaTemplate.renderString({'transform': lambda})); + // #docregion ExampleUsage +} + +// #enddocregion ExampleUsage 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..793d80186698 --- /dev/null +++ b/third_party/packages/mustache_template/example/lib/readme_excerpts.dart @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file exists solely to host compiled excerpts for README.md, and is not +// intended for use as an actual example application. + +// ignore_for_file: avoid_print, public_member_api_docs, unreachable_from_main +// ignore_for_file: prefer_function_declarations_over_variables +// ignore_for_file: specify_nonobvious_local_variable_types + +import 'package:mustache_template/mustache_template.dart'; + +String nestedPathsExample() { + // #docregion NestedPaths + final t = Template('{{ author.name }}'); + final output = t.renderString({ + 'author': {'name': 'Greg Lowe'}, + }); + // #enddocregion NestedPaths + return output; +} + +String partialsExample() { + // #docregion Partials + final partial = Template('{{ foo }}', name: 'partial'); + + final resolver = (String name) { + if (name == 'partial-name') { + // Name of partial tag. + return partial; + } + return null; + }; + + final t = Template('{{> partial-name }}', partialResolver: resolver); + + final output = t.renderString({'foo': 'bar'}); // bar + // #enddocregion Partials + return output; +} + +String lambdaSimpleExample() { + // #docregion LambdaSimple + final t = Template('{{# foo }}{{/ foo }}'); + final lambda = (_) => 'bar'; + final output = t.renderString({'foo': lambda}); // bar + // #enddocregion LambdaSimple + return output; +} + +String lambdaShownExample() { + // #docregion LambdaShown + final t = Template('{{# foo }}hidden{{/ foo }}'); + final lambda = (_) => 'shown'; + final output = t.renderString({'foo': lambda}); // shown + // #enddocregion LambdaShown + return output; +} + +String lambdaRenderExample() { + // #docregion LambdaRender + final t = Template('{{# foo }}oi{{/ foo }}'); + final lambda = (LambdaContext ctx) => + '${ctx.renderString().toUpperCase()}'; + final output = t.renderString({'foo': lambda}); // OI + // #enddocregion LambdaRender + return output; +} + +String lambdaRenderBarExample() { + // #docregion LambdaRenderBar + final t = Template('{{# foo }}{{bar}}{{/ foo }}'); + final lambda = (LambdaContext ctx) => + '${ctx.renderString().toUpperCase()}'; + final output = t.renderString({'foo': lambda, 'bar': 'pub'}); // PUB + // #enddocregion LambdaRenderBar + return output; +} + +String lambdaRenderSourceExample() { + // #docregion LambdaRenderSource + final t = Template('{{# foo }}{{bar}}{{/ foo }}'); + final lambda = (LambdaContext ctx) => + ctx.renderSource('${ctx.source} {{cmd}}'); + final output = t.renderString({ + 'foo': lambda, + 'bar': 'pub', + 'cmd': 'build', + }); // pub build + // #enddocregion LambdaRenderSource + return output; +} 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..3dcf29ef1dc0 --- /dev/null +++ b/third_party/packages/mustache_template/example/pubspec.yaml @@ -0,0 +1,10 @@ +name: mustache_template_example +description: Example app for mustache_template package. +publish_to: none + +environment: + sdk: ^3.9.0 + +dependencies: + mustache_template: + path: ../ diff --git a/third_party/packages/mustache_template/pubspec.yaml b/third_party/packages/mustache_template/pubspec.yaml index 264e0ee0c4d4..8a8bdeae8b94 100644 --- a/third_party/packages/mustache_template/pubspec.yaml +++ b/third_party/packages/mustache_template/pubspec.yaml @@ -2,7 +2,7 @@ name: mustache_template description: A templating library that implements the Mustache template specification repository: https://github.com/flutter/packages/tree/main/third_party/packages/mustache_template issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+mustache_template%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ^3.9.0 diff --git a/third_party/packages/mustache_template/test/all.dart b/third_party/packages/mustache_template/test/all.dart index 0e7d0e4cbb20..e50e87f1585a 100644 --- a/third_party/packages/mustache_template/test/all.dart +++ b/third_party/packages/mustache_template/test/all.dart @@ -1,8 +1,10 @@ +import 'example_test.dart' as example; import 'mustache_specs.dart' as specs; import 'mustache_test.dart' as test; import 'parser_test.dart' as parser; void main() { + example.main(); specs.main(); test.main(); parser.main(); diff --git a/third_party/packages/mustache_template/test/example_test.dart b/third_party/packages/mustache_template/test/example_test.dart new file mode 100644 index 000000000000..091c5127f64d --- /dev/null +++ b/third_party/packages/mustache_template/test/example_test.dart @@ -0,0 +1,44 @@ +// ignore_for_file: avoid_relative_lib_imports + +import 'package:test/test.dart'; + +import '../example/lib/main.dart' as example_app; +import '../example/lib/readme_excerpts.dart' as readme_excerpts; + +void main() { + group('Example app', () { + test('example app runs without error', () { + expect(example_app.main, returnsNormally); + }); + }); + + group('README excerpts', () { + 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('OI')); + }); + + test('lambda render with context data includes the variable value', () { + expect(readme_excerpts.lambdaRenderBarExample(), equals('PUB')); + }); + + test('lambda renderSource example reparses in the current context', () { + expect(readme_excerpts.lambdaRenderSourceExample(), equals('pub build')); + }); + }); +}