Skip to content

Commit 98580c6

Browse files
[mustache_template] Initial import (#9944)
The `mustache_template` package has been transferred to the `flutter.dev` publisher, so this imports it into our repository and updates it to follow repository conventions: - Fix/suppress analysis warnings - Some via `dart fix`, some manual - Add repo-level metadata - Update license check command to recognize this package - Add a temporary code-excerpt exclusion - Update pubspec.yaml metadata - Bump version to 2.0.1 to release the changes `mustache_template` was itself a fork by a Flutter team member of the third-party `mustache` package, so the import is into `third_party/packages/`, updating the repo license check to recognize the license of this package. The original code is quite old, and as a result still used a lot of implicit dynamic typing. Some of that that has been addressed in this import, but the trickier aspects have been left for future work, as converting from implicit dynamic typing to specified types has the potential for runtime regressions. (It's possible that even the more minor changes made here will cause regressions that will need fast-follow, but all the tests still pass, and the manual `test/all.dart` tests do not have any failures that were not present before making changes to the imported code). Issues filed for future follow-up work: flutter/flutter#174721 flutter/flutter#174722 flutter/flutter#174742 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 0e22076 commit 98580c6

27 files changed

+3546
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ third_party/packages/cupertino_icons/** @MitchellGoodwin
4646
third_party/packages/cupertino_icons/test/goldens/** @LongCatIsLooong
4747
third_party/packages/flutter_svg/** @domesticmouse
4848
third_party/packages/flutter_svg_test/** @domesticmouse
49+
third_party/packages/mustache_template/** @bkonyi @parlough
4950
third_party/packages/path_parsing/** @domesticmouse
5051

5152
# Plugin platform implementation rules. These should stay last, since the last

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ These are the packages hosted in this repository:
6767
| [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![downloads](https://img.shields.io/pub/dm/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) |
6868
| [quick\_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![downloads](https://img.shields.io/pub/dm/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20quick_actions?label=)](https://github.com/flutter/packages/labels/p%3A%20quick_actions) |
6969
| [google\_identity\_services\_web](./packages/google_identity_services_web/) | [![pub package](https://img.shields.io/pub/v/google_identity_services_web.svg)](https://pub.dev/packages/google_identity_services_web) | [![pub points](https://img.shields.io/pub/points/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![downloads](https://img.shields.io/pub/dm/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_identity_services_web?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_identity_services_web) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_identity_services_web?label=)](https://github.com/flutter/packages/labels/p%3A%20google_identity_services_web) |
70+
| [mustache\_template](./third_party/packages/mustache_template/) | [![pub package](https://img.shields.io/pub/v/mustache_template.svg)](https://pub.dev/packages/mustache_template) | [![pub points](https://img.shields.io/pub/points/mustache_template)](https://pub.dev/packages/mustache_template/score) | [![downloads](https://img.shields.io/pub/dm/mustache_template)](https://pub.dev/packages/mustache_template/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20mustache_template?label=)](https://github.com/flutter/flutter/labels/p%3A%20mustache_template) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20mustache_template?label=)](https://github.com/flutter/packages/labels/p%3A%20mustache_template) |
7071
| [rfw](./packages/rfw/) | [![pub package](https://img.shields.io/pub/v/rfw.svg)](https://pub.dev/packages/rfw) | [![pub points](https://img.shields.io/pub/points/rfw)](https://pub.dev/packages/rfw/score) | [![downloads](https://img.shields.io/pub/dm/rfw)](https://pub.dev/packages/rfw/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20rfw?label=)](https://github.com/flutter/flutter/labels/p%3A%20rfw) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20rfw?label=)](https://github.com/flutter/packages/labels/p%3A%20rfw) |
7172
| [shared\_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://img.shields.io/pub/points/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![downloads](https://img.shields.io/pub/dm/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20shared_preferences?label=)](https://github.com/flutter/packages/labels/p%3A%20shared_preferences) |
7273
| [standard\_message\_codec](./packages/standard_message_codec/) | [![pub package](https://img.shields.io/pub/v/standard_message_codec.svg)](https://pub.dev/packages/standard_message_codec) | [![pub points](https://img.shields.io/pub/points/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![downloads](https://img.shields.io/pub/dm/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20standard_message_codec?label=)](https://github.com/flutter/flutter/labels/p%3A%20standard_message_codec) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20standard_message_codec?label=)](https://github.com/flutter/packages/labels/p%3A%20standard_message_codec) |

script/configs/temp_exclude_excerpt.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
# https://github.com/flutter/flutter/issues/102679
88
- espresso
99
- in_app_purchase/in_app_purchase
10+
- mustache_template
1011
- pointer_interceptor
1112
- quick_actions/quick_actions

script/tool/lib/src/license_check_command.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const Set<String> _ignoredFullBasenameList = <String>{
5353
// the package-level LICENSE file. Each entry must be a directory relative to
5454
// third_party/packages, as that is the only directory where this is allowed.
5555
const Set<String> _unannotatedFileThirdPartyDirectories = <String>{
56+
'mustache_template',
5657
'path_parsing',
5758
'flutter_svg',
5859
'flutter_svg_test',
@@ -85,6 +86,13 @@ final List<RegExp> _thirdPartyLicenseBlockRegexes = <RegExp>[
8586
r'// Use of this source code is governed by a BSD-style license that can be\n'
8687
r'// found in the LICENSE file\.\n',
8788
),
89+
// packages/third_party/mustache_template.
90+
RegExp(
91+
r'Copyright \(c\) 2013, Greg Lowe\n'
92+
r'All rights reserved.\n\n'
93+
r'Redistribution and use in source and binary forms, with or without '
94+
r'modification, are permitted provided that the following conditions are met:\n',
95+
),
8896
// packages/third_party/path_parsing.
8997
RegExp(
9098
r'Copyright \(c\) 2018 Dan Field\n\n'
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
## 2.0.1
2+
3+
* Transfers the package source from https://github.com/jonahwilliams/mustache
4+
to https://github.com/flutter/packages.
5+
* Updates minimum supported SDK version to Dart 3.7.
6+
* Updates code for new analysis options.
7+
8+
## 2.0.0
9+
10+
* Support for null safe dart added.
11+
12+
## 1.0.0+1
13+
14+
* Fixed regression where lookups from list did not work. Removed failing tests
15+
that depend on reflection.
16+
17+
## 1.0.0
18+
19+
* Forked from original repo. Support for mirrors removed.
20+
21+
## Fork
22+
23+
## 1.1.1
24+
25+
* Fixed error "boolean expression must not be null". Thanks Nico.
26+
27+
## 1.1.0
28+
29+
* Better support for class members in sections. Thanks to Janice Collins.
30+
* Set the SDK constraint to Dart 2+.
31+
32+
## 1.0.2
33+
Set the max SDK constraint to <3.0.0.
34+
35+
## 0.2.5
36+
37+
* Remove MustacheFormatException
38+
* Allow templates to specify default delimiters. Thanks to Joris Hermans.
39+
* Fix #24: renderString shrinks multiple newlines to just one (Thanks to John Ryan for the repro).
40+
41+
## 0.2.4
42+
43+
* Fix #23 failure if tag or comment contains "="
44+
45+
## 0.2.3
46+
47+
* Change handling of lenient sections to match python mustache implementation.
48+
49+
## 0.2.2
50+
51+
* Fix MirrorsUsed tag for using mirrors on dart2js.
52+
* Clean up dead code.
53+
54+
## 0.2.1
55+
56+
* Added new methods to LambdaContext.
57+
58+
## 0.2
59+
60+
* Deprecated parse() function - please update your code to use new Template(source).
61+
* Deprecated MustacheFormatException - please update your code to use TemplateException.
62+
* Breaking change: Template.render and Template.renderString methods no longer
63+
take the optional lenient and htmlEscapeValues. These should now be passed to
64+
the Template constructor.
65+
* Fully passing all mustache spec tests.
66+
* Added support for MirrorsUsed.
67+
* Implemented partials. #11
68+
* Implemented lambdas. #4
69+
* Implemented change delimiter tag.
70+
* Add template name parameter, and show this in error messages.
71+
* Allow whitespace at begining of tags. #10
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Copyright (c) 2013, Greg Lowe
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5+
6+
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7+
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: "mustache_template"
2+
description:
3+
"Mustache template Dart library"
4+
5+
third_party {
6+
identifier {
7+
type: "Git"
8+
value: "https://github.com/jonahwilliams/mustache"
9+
primary_source: true
10+
version: "c4344e0dd45f6605758eb11aa4837859e2c055f0"
11+
}
12+
version: "c4344e0dd45f6605758eb11aa4837859e2c055f0"
13+
last_upgrade_date { year: 2025 month: 9 day: 3 }
14+
license_type: NOTICE
15+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Mustache templates
2+
3+
A Dart library to parse and render [mustache templates](https://mustache.github.io/).
4+
5+
See the [mustache manual](http://mustache.github.com/mustache.5.html) for detailed usage information.
6+
7+
This library passes all [mustache specification](https://github.com/mustache/spec/tree/master/specs) tests.
8+
9+
## Example usage
10+
```dart
11+
import 'package:mustache_template/mustache_template.dart';
12+
13+
main() {
14+
var source = '''
15+
{{# names }}
16+
<div>{{ lastname }}, {{ firstname }}</div>
17+
{{/ names }}
18+
{{^ names }}
19+
<div>No names.</div>
20+
{{/ names }}
21+
{{! I am a comment. }}
22+
''';
23+
24+
var template = Template(source, name: 'template-filename.html');
25+
26+
var output = template.renderString({'names': [
27+
{'firstname': 'Greg', 'lastname': 'Lowe'},
28+
{'firstname': 'Bob', 'lastname': 'Johnson'}
29+
]});
30+
31+
print(output);
32+
}
33+
```
34+
35+
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.
36+
37+
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.
38+
39+
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.
40+
41+
## Differences between strict mode and lenient mode.
42+
43+
### Strict mode (default)
44+
45+
* Tag names may only contain the characters a-z, A-Z, 0-9, underscore, period and minus. Other characters in tags will cause a TemplateException to be thrown during parsing.
46+
47+
* During rendering, if no map key or object member which matches the tag name is found, then a TemplateException will be thrown.
48+
49+
### Lenient mode
50+
51+
* Tag names may use any characters.
52+
* During rendering, if no map key or object member which matches the tag name is found, then silently ignore and output nothing.
53+
54+
## Nested paths
55+
56+
```dart
57+
var t = Template('{{ author.name }}');
58+
var output = template.renderString({'author': {'name': 'Greg Lowe'}});
59+
```
60+
61+
## Partials - example usage
62+
63+
```dart
64+
65+
var partial = Template('{{ foo }}', name: 'partial');
66+
67+
var resolver = (String name) {
68+
if (name == 'partial-name') { // Name of partial tag.
69+
return partial;
70+
}
71+
};
72+
73+
var t = Template('{{> partial-name }}', partialResolver: resolver);
74+
75+
var output = t.renderString({'foo': 'bar'}); // bar
76+
77+
```
78+
79+
## Lambdas - example usage
80+
81+
```dart
82+
var t = Template('{{# foo }}');
83+
var lambda = (_) => 'bar';
84+
t.renderString({'foo': lambda}); // bar
85+
```
86+
87+
```dart
88+
var t = Template('{{# foo }}hidden{{/ foo }}');
89+
var lambda = (_) => 'shown';
90+
t.renderString('foo': lambda); // shown
91+
```
92+
93+
```dart
94+
var t = Template('{{# foo }}oi{{/ foo }}');
95+
var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>';
96+
t.renderString({'foo': lambda}); // <b>OI</b>
97+
```
98+
99+
```dart
100+
var t = Template('{{# foo }}{{bar}}{{/ foo }}');
101+
var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>';
102+
t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b>
103+
```
104+
105+
```dart
106+
var t = Template('{{# foo }}{{bar}}{{/ foo }}');
107+
var lambda = (LambdaContext ctx) => '<b>${ctx.renderString().toUpperCase()}</b>';
108+
t.renderString({'foo': lambda, 'bar': 'pub'}); // <b>PUB</b>
109+
```
110+
111+
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.
112+
113+
```dart
114+
var t = Template('{{# foo }}{{bar}}{{/ foo }}');
115+
var lambda = (LambdaContext ctx) => ctx.renderSource(ctx.source + ' {{cmd}}');
116+
t.renderString({'foo': lambda, 'bar': 'pub', 'cmd': 'build'}); // pub build
117+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# See https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#arguments
2+
override_platforms:
3+
chrome:
4+
settings:
5+
executable: chrome
6+
arguments: --no-sandbox
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import 'src/template.dart' as t;
2+
3+
/// A Template can be efficiently rendered multiple times with different
4+
/// values.
5+
abstract class Template {
6+
/// The constructor parses the template source and throws [TemplateException]
7+
/// if the syntax of the source is invalid.
8+
/// Tag names may only contain characters a-z, A-Z, 0-9, underscore, and minus,
9+
/// unless lenient mode is specified.
10+
factory Template(
11+
String source, {
12+
bool lenient,
13+
bool htmlEscapeValues,
14+
String name,
15+
PartialResolver? partialResolver,
16+
String delimiters,
17+
}) = t.Template.fromSource;
18+
19+
/// An optional name used to identify the template in error logging.
20+
String? get name;
21+
22+
/// The template that should be filled when calling [render] or
23+
/// [renderString].
24+
String get source;
25+
26+
/// [values] can be a combination of Map, List, String. Any non-String object
27+
/// will be converted using toString(). Null values will cause a
28+
/// [TemplateException], unless lenient module is enabled.
29+
String renderString(Object? values);
30+
31+
/// [values] can be a combination of Map, List, String. Any non-String object
32+
/// will be converted using toString(). Null values will cause a
33+
/// [TemplateException], unless lenient module is enabled.
34+
void render(Object? values, StringSink sink);
35+
}
36+
37+
// TODO(stuartmorgan): Remove this. See https://github.com/flutter/flutter/issues/174722.
38+
// ignore: public_member_api_docs
39+
typedef PartialResolver = Template? Function(String);
40+
41+
// TODO(stuartmorgan): Remove this. See https://github.com/flutter/flutter/issues/174722.
42+
// ignore: public_member_api_docs
43+
typedef LambdaFunction = Object Function(LambdaContext context);
44+
45+
/// Passed as an argument to a mustache lambda function. The methods on
46+
/// this object may only be called before the lambda function returns. If a
47+
/// method is called after it has returned an exception will be thrown.
48+
abstract class LambdaContext {
49+
/// Render the current section tag in the current context and return the
50+
/// result as a string. If provided, value will be added to the top of the
51+
/// context's stack.
52+
String renderString({Object? value});
53+
54+
/// Render and directly output the current section tag. If provided, value
55+
/// will be added to the top of the context's stack.
56+
void render({Object value});
57+
58+
/// Output a string. The output will not be html escaped, and will be written
59+
/// before the output returned from the lambda.
60+
void write(Object object);
61+
62+
/// Get the unevaluated template source for the current section tag.
63+
String get source;
64+
65+
/// Evaluate the string as a mustache template using the current context. If
66+
/// provided, value will be added to the top of the context's stack.
67+
String renderSource(String source, {Object? value});
68+
69+
/// Lookup the value of a variable in the current context.
70+
Object? lookup(String variableName);
71+
}
72+
73+
/// [TemplateException] is used to obtain the line and column numbers
74+
/// of the token which caused parse or render to fail.
75+
abstract class TemplateException implements Exception {
76+
/// A message describing the problem parsing or rendering the template.
77+
String get message;
78+
79+
/// The name used to identify the template, as passed to the Template
80+
/// constructor.
81+
String? get templateName;
82+
83+
/// The 1-based line number of the token where formatting error was found.
84+
int get line;
85+
86+
/// The 1-based column number of the token where formatting error was found.
87+
int get column;
88+
89+
/// The character offset within the template source.
90+
int? get offset;
91+
92+
/// The template source.
93+
String? get source;
94+
95+
/// A short source substring of the source at the point the problem occurred
96+
/// with parsing or rendering.
97+
String get context;
98+
}

0 commit comments

Comments
 (0)