Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion script/configs/temp_exclude_excerpt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions third_party/packages/mustache_template/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 2.0.4

* Adds an example package with runnable samples and tests.
* Migrates README.md to use code excerpts from the new example.
* Fixes a broken README link to the Mustache manual.

## 2.0.3
Expand Down
120 changes: 46 additions & 74 deletions third_party/packages/mustache_template/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<?code-excerpt path-base="example/lib"?>

# Mustache templates

A Dart library to parse and render [mustache templates](https://mustache.github.io/).
Expand All @@ -7,29 +9,25 @@ See the [mustache manual](https://mustache.github.io/mustache.5.html) for detail
This library passes all [mustache specification](https://github.com/mustache/spec/tree/master/specs) tests.

## Example usage
<?code-excerpt "readme_excerpts.dart (BasicRender)"?>
```dart
import 'package:mustache_template/mustache_template.dart';

main() {
var source = '''
{{# names }}
<div>{{ lastname }}, {{ firstname }}</div>
{{/ names }}
{{^ names }}
<div>No names.</div>
{{/ 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);
}
var source = '''
{{# names }}
<div>{{ lastname }}, {{ firstname }}</div>
{{/ names }}
{{^ names }}
<div>No names.</div>
{{/ names }}
{{! I am a comment. }}
''';

var template = Template(source, name: 'names-template');
var output = template.renderString(<String, Object>{
'names': <Map<String, String>>[
<String, String>{'firstname': 'Greg', 'lastname': 'Lowe'},
<String, String>{'firstname': 'Bob', 'lastname': 'Johnson'},
],
});
```

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.
Expand All @@ -46,72 +44,46 @@ 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.

<?code-excerpt "readme_excerpts.dart (StrictMode)"?>
```dart
late String result;
try {
Template('{{missing}}').renderString({});
result = 'No exception thrown (unexpected)';
} on TemplateException catch (e) {
// Strict mode (default): missing keys throw when rendering.
result = 'Strict mode exception: ${e.runtimeType}';
}
```

### 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.

## Nested paths

<?code-excerpt "readme_excerpts.dart (LenientMode)"?>
```dart
var t = Template('{{ author.name }}');
var output = template.renderString({'author': {'name': 'Greg Lowe'}});
final t = Template('{{missing}}', lenient: true);
final String output = t.renderString({}); // ''
```

## Partials - example usage
## Partials

<?code-excerpt "readme_excerpts.dart (Partials)"?>
```dart

var partial = Template('{{ foo }}', name: 'partial');

var resolver = (String name) {
if (name == 'partial-name') { // Name of partial tag.
return 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

```

## 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
```

```dart
var t = Template('{{# foo }}oi{{/ foo }}');
var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>';
t.renderString({'foo': lambda}); // <b>OI</b>
```
## More examples

```dart
var t = Template('{{# foo }}{{bar}}{{/ foo }}');
var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>';
t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b>
```

```dart
var t = Template('{{# foo }}{{bar}}{{/ foo }}');
var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>';
t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b>
```

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
```
For additional usage including nested paths, lambdas, and
`LambdaContext.renderSource`, see the [example app](example/).
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// 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, specify_nonobvious_local_variable_types, prefer_function_declarations_over_variables

import 'package:mustache_template/mustache_template.dart';

String basicRenderExample() {
// #docregion BasicRender
const source = '''
{{# names }}
<div>{{ lastname }}, {{ firstname }}</div>
{{/ names }}
{{^ names }}
<div>No names.</div>
{{/ names }}
{{! I am a comment. }}
''';

final template = Template(source, name: 'names-template');
final output = template.renderString(<String, Object>{
'names': <Map<String, String>>[
<String, String>{'firstname': 'Greg', 'lastname': 'Lowe'},
<String, String>{'firstname': 'Bob', 'lastname': 'Johnson'},
],
});
// #enddocregion BasicRender
return output;
}

String nestedPathsExample() {
// #docregion NestedPaths
final t = Template('{{ author.name }}');
final output = t.renderString(<String, Object>{
'author': {'name': 'Greg Lowe'},
});
// #enddocregion NestedPaths
return output;
}

String partialsExample() {
// #docregion Partials
final partial = Template('{{ foo }}', name: 'partial');
Template resolver(String name) {
if (name == 'partial-name') {
return partial;
}
throw StateError('Unknown partial: $name');
}

final t = Template('{{> partial-name }}', partialResolver: resolver);
var output = t.renderString({'foo': 'bar'}); // bar
// #enddocregion Partials
return output;
}

String lambdaSimpleExample() {
// #docregion LambdaSimpleValue
final t = Template('{{ foo }}');

final lambda = (_) => 'bar';

final output = t.renderString({'foo': lambda}); // bar
// #enddocregion LambdaSimpleValue
return output;
}

String lambdaShownExample() {
// #docregion LambdaSectionReplacement
final t = Template('{{# foo }}hidden{{/ foo }}');
final lambda = (_) => 'shown';

final output = t.renderString({'foo': lambda}); // shown
// #enddocregion LambdaSectionReplacement
return output;
}

String lambdaUppercaseOibExample() {
// #docregion LambdaUppercaseOib
final t = Template('{{# foo }}oi{{/ foo }}');
final lambda = (LambdaContext ctx) =>
'<b>${ctx.renderString().toUpperCase()}</b>';

final output = t.renderString({'foo': lambda}); // <b>OI</b>
// #enddocregion LambdaUppercaseOib
return output;
}

String lambdaRenderExample() {
// #docregion LambdaRenderString
final t = Template('{{# foo }}{{bar}}{{/ foo }}');
final lambda = (LambdaContext context) =>
'<b>${context.renderString().toUpperCase()}</b>';

final output = t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b>
// #enddocregion LambdaRenderString
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;
}

String strictModeBehaviorExample() {
// #docregion StrictMode
late String result;
try {
Template('{{missing}}').renderString({});
result = 'No exception thrown (unexpected)';
} on TemplateException catch (e) {
// Strict mode (default): missing keys throw when rendering.
result = 'Strict mode exception: ${e.runtimeType}';
}
// #enddocregion StrictMode
return result;
}

String lenientModeBehaviorExample() {
// #docregion LenientMode
final t = Template('{{missing}}', lenient: true);
final String output = t.renderString({}); // ''
// #enddocregion LenientMode
return output;
}
16 changes: 16 additions & 0 deletions third_party/packages/mustache_template/example/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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(lambdaUppercaseOibExample());
print(lambdaRenderExample());
print(lambdaRenderSourceExample());
print(strictModeBehaviorExample());
print(lenientModeBehaviorExample());
}
12 changes: 12 additions & 0 deletions third_party/packages/mustache_template/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Loading