Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@ gradlew.bat
.project
.classpath
.settings

# FVM Version Cache
.fvm/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignores for local tools should be done in your user-level git ignore, not added to the repository.

164 changes: 96 additions & 68 deletions third_party/packages/mustache_template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,47 @@ 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 "main.dart (example_usage)"?>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reference is not correct. Did you run the excerpting tool, or did you manually copy everything between the files?

```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'}
]});
/// The main entrypoint for the example app.
void main() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of this is relevant to the example; just the original code, omitting the main() and print, should be part of the excerpt.

exampleUsage();
nestedPaths();
partialsExample();
lambdasExample();
}

print(output);
/// Demonstrates basic usage of mustache templates.
void exampleUsage() {
const String source = '''
{{# names }}
<div>{{ lastname }}, {{ firstname }}</div>
{{/ names }}
{{^ names }}
<div>No names.</div>
{{/ names }}
{{! I am a comment. }}
''';

final Template template = Template(source, name: 'template-filename.html');

final String output = template.renderString(<String, dynamic>{
'names': <Map<String, String>>[
<String, String>{'firstname': 'Greg', 'lastname': 'Lowe'},
<String, String>{'firstname': 'Bob', 'lastname': 'Johnson'},
],
});

print(output);
}
Comment thread
princesoni18 marked this conversation as resolved.
```

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.

The Template contstructor allows passing a name, this name will be used in error messages. When working with a number of templates, it is important to pass a name so that the error messages specify which template caused the error.
The Template constructor allows passing a name, this name will be used in error messages. When working with a number of templates, it is important to pass a name so that the error messages specify which template caused the error.

By default all output from `{{variable}}` tags is html escaped, this behaviour can be changed by passing htmlEscapeValues : false to the Template constructor. You can also use a `{{{triple mustache}}}` tag, or a unescaped variable tag `{{&unescaped}}`, the output from these tags is not escaped.

Expand All @@ -53,65 +66,80 @@ By default all output from `{{variable}}` tags is html escaped, this behaviour c

## Nested paths

<?code-excerpt "main.dart (nested_paths)"?>
```dart
var t = Template('{{ author.name }}');
var output = template.renderString({'author': {'name': 'Greg Lowe'}});
/// Demonstrates how to access nested map properties.
void nestedPaths() {
final Template template = Template('The author is {{ author.name }}');
final String output = template.renderString(<String, dynamic>{
'author': <String, String>{'name': 'Greg Lowe'},
});
print(output);
}
Comment thread
princesoni18 marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And then more generally, you've significantly changed the structure of all of these examples, making them longer by adding unnecessary boilerplate. The code being excerpted should be structurally the same as it was, with the changes being those necessary to follow style rules.

```

## Partials - example usage

<?code-excerpt "main.dart (partials)"?>
```dart

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

var resolver = (String name) {
if (name == 'partial-name') { // Name of partial tag.
return partial;
}
};

var t = Template('{{> partial-name }}', partialResolver: resolver);

var output = t.renderString({'foo': 'bar'}); // bar

/// Demonstrates the usage of partials with a custom resolver.
void partialsExample() {
final Template partial = Template('{{ foo }}', name: 'partial');

Template? resolver(String name) {
if (name == 'partial-name') {
// Name of partial tag.
return partial;
}
return null;
}

final Template t = Template('{{> partial-name }}', partialResolver: resolver);

final String output = t.renderString(<String, dynamic>{'foo': 'bar'});
print(output); // bar
}
```

## Lambdas - example usage

<?code-excerpt "main.dart (lambdas)"?>
```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>
```

```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>
/// Demonstrates various usages of lambdas, including hidden sections and lambda contexts.
void lambdasExample() {
// Simple lambda
final Template t1 = Template('{{# foo }}inner{{/ foo }}');
Object lambda1(Object? _) => 'bar';
print(t1.renderString(<String, dynamic>{'foo': lambda1})); // bar

// Lambda returning text for a hidden section
final Template t2 = Template('{{# foo }}hidden{{/ foo }}');
Object lambda2(Object? _) => 'shown';
print(t2.renderString(<String, dynamic>{'foo': lambda2})); // shown

// Lambda Context
final Template t3 = Template('{{# foo }}oi{{/ foo }}');
Object lambda3(LambdaContext ctx) =>
'<b>${ctx.renderString().toUpperCase()}</b>';
print(t3.renderString(<String, dynamic>{'foo': lambda3})); // <b>OI</b>

// Lambda Context with variables
final Template t4 = Template('{{# foo }}{{bar}}{{/ foo }}');
Object lambda4(LambdaContext ctx) =>
'<b>${ctx.renderString().toUpperCase()}</b>';
print(t4.renderString(
<String, dynamic>{'foo': lambda4, 'bar': 'pub'})); // <b>PUB</b>

// Lambda Context re-parsing source
final Template t5 = Template('{{# foo }}{{bar}}{{/ foo }}');
Object lambda5(LambdaContext ctx) =>
ctx.renderSource('${ctx.source} {{cmd}}');
print(t5.renderString(<String, dynamic>{
'foo': lambda5,
'bar': 'pub',
'cmd': 'build',
})); // pub build
}
Comment thread
princesoni18 marked this conversation as resolved.
```

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
```
In the last lambda 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.
110 changes: 110 additions & 0 deletions third_party/packages/mustache_template/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2026 The Flutter Authors. All rights reserved.
// 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

// #docregion example_usage
import 'package:mustache_template/mustache_template.dart';

/// The main entrypoint for the example app.
void main() {
exampleUsage();
nestedPaths();
partialsExample();
lambdasExample();
}

/// Demonstrates basic usage of mustache templates.
void exampleUsage() {
const String source = '''
{{# names }}
<div>{{ lastname }}, {{ firstname }}</div>
{{/ names }}
{{^ names }}
<div>No names.</div>
{{/ names }}
{{! I am a comment. }}
''';

final Template template = Template(source, name: 'template-filename.html');

final String output = template.renderString(<String, dynamic>{
'names': <Map<String, String>>[
<String, String>{'firstname': 'Greg', 'lastname': 'Lowe'},
<String, String>{'firstname': 'Bob', 'lastname': 'Johnson'},
],
});

print(output);
}
// #enddocregion example_usage

// #docregion nested_paths
/// Demonstrates how to access nested map properties.
void nestedPaths() {
final Template template = Template('The author is {{ author.name }}');
final String output = template.renderString(<String, dynamic>{
'author': <String, String>{'name': 'Greg Lowe'},
});
print(output);
}
// #enddocregion nested_paths

// #docregion partials
/// Demonstrates the usage of partials with a custom resolver.
void partialsExample() {
final Template partial = Template('{{ foo }}', name: 'partial');

Template? resolver(String name) {
if (name == 'partial-name') {
// Name of partial tag.
return partial;
}
return null;
}

final Template t = Template('{{> partial-name }}', partialResolver: resolver);

final String output = t.renderString(<String, dynamic>{'foo': 'bar'});
print(output); // bar
}
// #enddocregion partials

// #docregion lambdas
/// Demonstrates various usages of lambdas, including hidden sections and lambda contexts.
void lambdasExample() {
// Simple lambda
final Template t1 = Template('{{# foo }}inner{{/ foo }}');
Object lambda1(Object? _) => 'bar';
print(t1.renderString(<String, dynamic>{'foo': lambda1})); // bar

// Lambda returning text for a hidden section
final Template t2 = Template('{{# foo }}hidden{{/ foo }}');
Object lambda2(Object? _) => 'shown';
print(t2.renderString(<String, dynamic>{'foo': lambda2})); // shown

// Lambda Context
final Template t3 = Template('{{# foo }}oi{{/ foo }}');
Object lambda3(LambdaContext ctx) =>
'<b>${ctx.renderString().toUpperCase()}</b>';
print(t3.renderString(<String, dynamic>{'foo': lambda3})); // <b>OI</b>

// Lambda Context with variables
final Template t4 = Template('{{# foo }}{{bar}}{{/ foo }}');
Object lambda4(LambdaContext ctx) =>
'<b>${ctx.renderString().toUpperCase()}</b>';
print(t4.renderString(
<String, dynamic>{'foo': lambda4, 'bar': 'pub'})); // <b>PUB</b>

// Lambda Context re-parsing source
final Template t5 = Template('{{# foo }}{{bar}}{{/ foo }}');
Object lambda5(LambdaContext ctx) =>
ctx.renderSource('${ctx.source} {{cmd}}');
print(t5.renderString(<String, dynamic>{
'foo': lambda5,
'bar': 'pub',
'cmd': 'build',
})); // pub build
}
// #enddocregion lambdas
13 changes: 13 additions & 0 deletions third_party/packages/mustache_template/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: mustache_template_example
description: Example app for the mustache_template package.
publish_to: 'none'

environment:
sdk: ^3.10.0

dependencies:
mustache_template:
path: ../

dev_dependencies:
lints: ^3.0.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use lints.

Loading