Skip to content

Support custom partial resolvers in Mustachio #2482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 15, 2021
Merged
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
36 changes: 22 additions & 14 deletions lib/src/generator/templates.renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ String _simpleResolveErrorMessage(List<String> key, String type) =>
'expose the properties of $type by adding it to the @Renderer '
"annotation's 'visibleTypes' list";

String renderIndex(PackageTemplateData context, File file) {
String renderIndex(PackageTemplateData context, File file,
{PartialResolver partialResolver}) {
try {
var parser = MustachioParser(file.readAsStringSync());
return _render_PackageTemplateData(context, parser.parse(), file);
return _render_PackageTemplateData(context, parser.parse(), file,
partialResolver: partialResolver);
} on FileSystemException catch (e) {
throw MustachioResolutionError(
'FileSystemException when reading template "${file.path}": ${e.message}');
Expand All @@ -28,8 +30,9 @@ String renderIndex(PackageTemplateData context, File file) {

String _render_PackageTemplateData(
PackageTemplateData context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = _Renderer_PackageTemplateData(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer = _Renderer_PackageTemplateData(context, parent, file,
partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand Down Expand Up @@ -201,8 +204,9 @@ class _Renderer_PackageTemplateData extends RendererBase<PackageTemplateData> {
};

_Renderer_PackageTemplateData(
PackageTemplateData context, RendererBase<Object> parent, File file)
: super(context, parent, file);
PackageTemplateData context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<PackageTemplateData> getProperty(String key) {
Expand All @@ -215,8 +219,9 @@ class _Renderer_PackageTemplateData extends RendererBase<PackageTemplateData> {
}

String _render_Object(Object context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = _Renderer_Object(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer =
_Renderer_Object(context, parent, file, partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand All @@ -241,8 +246,9 @@ class _Renderer_Object extends RendererBase<Object> {
),
};

_Renderer_Object(Object context, RendererBase<Object> parent, File file)
: super(context, parent, file);
_Renderer_Object(Object context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<Object> getProperty(String key) {
Expand All @@ -256,8 +262,9 @@ class _Renderer_Object extends RendererBase<Object> {

String _render_TemplateData<T extends Documentable>(
TemplateData<T> context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = _Renderer_TemplateData(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer = _Renderer_TemplateData(context, parent, file,
partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand Down Expand Up @@ -563,8 +570,9 @@ class _Renderer_TemplateData<T extends Documentable>
};

_Renderer_TemplateData(
TemplateData<T> context, RendererBase<Object> parent, File file)
: super(context, parent, file);
TemplateData<T> context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<TemplateData<T>> getProperty(String key) {
Expand Down
34 changes: 26 additions & 8 deletions lib/src/mustachio/renderer_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@

import 'package:analyzer/file_system/file_system.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'parser.dart';

/// The signature of a partial resolver function.
typedef PartialResolver = String Function(String path);

/// The signature of a generated render function.
typedef Renderer<T> = String Function(T context, File file,
{PartialResolver partialResolver});

/// The base class for a generated Mustache renderer.
abstract class RendererBase<T> {
/// The context object which this renderer can render.
Expand All @@ -16,10 +24,23 @@ abstract class RendererBase<T> {

final File template;

final PartialResolver partialResolver;

/// The output buffer into which [context] is rendered, using a template.
final buffer = StringBuffer();

RendererBase(this.context, this.parent, this.template);
RendererBase(this.context, this.parent, this.template,
{this.partialResolver});

path.Context get pathContext => template.provider.pathContext;

String _defaultResolver(String path) {
var partialPath = pathContext.isAbsolute(path)
? path
: pathContext.join(template.parent.path, path);
var file = template.provider.getFile(pathContext.normalize(partialPath));
return file.readAsStringSync();
}

void write(String text) => buffer.write(text);

Expand Down Expand Up @@ -122,18 +143,15 @@ abstract class RendererBase<T> {

void partial(Partial node) {
var key = node.key;
var partialPath = template.provider.pathContext.isAbsolute(key)
? key
: template.provider.pathContext.join(template.parent.path, key);
try {
var file = template.provider
.getFile(template.provider.pathContext.normalize(partialPath));
var parser = MustachioParser(file.readAsStringSync());
var partial = (partialResolver ?? _defaultResolver)(key);
var parser = MustachioParser(partial);
var ast = parser.parse();
renderBlock(ast);
} on FileSystemException catch (e) {
throw MustachioResolutionError(
'FileSystemException when reading partial "$key" found in template "${template.path}": ${e.message}');
'FileSystemException when reading partial "$key" found in template '
'"${template.path}": ${e.message}');
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion test/mustachio/builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ import 'package:mustachio/annotations.dart';

test('with a corresponding public API function', () async {
expect(generatedContent,
contains('String renderFoo<T>(Foo<T> context, File file)'));
contains('String renderFoo<T>(Foo<T> context, File file,'));
expect(generatedContent, contains('{PartialResolver partialResolver})'));
});

test('with a corresponding render function', () async {
Expand Down
55 changes: 33 additions & 22 deletions test/mustachio/foo.renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@ String _simpleResolveErrorMessage(List<String> key, String type) =>
'expose the properties of $type by adding it to the @Renderer '
"annotation's 'visibleTypes' list";

String renderFoo(Foo context, File file) {
String renderFoo(Foo context, File file, {PartialResolver partialResolver}) {
try {
var parser = MustachioParser(file.readAsStringSync());
return _render_Foo(context, parser.parse(), file);
return _render_Foo(context, parser.parse(), file,
partialResolver: partialResolver);
} on FileSystemException catch (e) {
throw MustachioResolutionError(
'FileSystemException when reading template "${file.path}": ${e.message}');
}
}

String _render_Foo(Foo context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = Renderer_Foo(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer =
Renderer_Foo(context, parent, file, partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand Down Expand Up @@ -107,8 +109,9 @@ class Renderer_Foo extends RendererBase<Foo> {
...Renderer_Object.propertyMap<CT_>(),
};

Renderer_Foo(Foo context, RendererBase<Object> parent, File file)
: super(context, parent, file);
Renderer_Foo(Foo context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<Foo> getProperty(String key) {
Expand All @@ -121,8 +124,9 @@ class Renderer_Foo extends RendererBase<Foo> {
}

String _render_Object(Object context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = Renderer_Object(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer =
Renderer_Object(context, parent, file, partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand All @@ -147,8 +151,9 @@ class Renderer_Object extends RendererBase<Object> {
),
};

Renderer_Object(Object context, RendererBase<Object> parent, File file)
: super(context, parent, file);
Renderer_Object(Object context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<Object> getProperty(String key) {
Expand All @@ -160,19 +165,21 @@ class Renderer_Object extends RendererBase<Object> {
}
}

String renderBar(Bar context, File file) {
String renderBar(Bar context, File file, {PartialResolver partialResolver}) {
try {
var parser = MustachioParser(file.readAsStringSync());
return _render_Bar(context, parser.parse(), file);
return _render_Bar(context, parser.parse(), file,
partialResolver: partialResolver);
} on FileSystemException catch (e) {
throw MustachioResolutionError(
'FileSystemException when reading template "${file.path}": ${e.message}');
}
}

String _render_Bar(Bar context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = Renderer_Bar(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer =
Renderer_Bar(context, parent, file, partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand Down Expand Up @@ -249,8 +256,9 @@ class Renderer_Bar extends RendererBase<Bar> {
...Renderer_Object.propertyMap<CT_>(),
};

Renderer_Bar(Bar context, RendererBase<Object> parent, File file)
: super(context, parent, file);
Renderer_Bar(Bar context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<Bar> getProperty(String key) {
Expand All @@ -262,19 +270,21 @@ class Renderer_Bar extends RendererBase<Bar> {
}
}

String renderBaz(Baz context, File file) {
String renderBaz(Baz context, File file, {PartialResolver partialResolver}) {
try {
var parser = MustachioParser(file.readAsStringSync());
return _render_Baz(context, parser.parse(), file);
return _render_Baz(context, parser.parse(), file,
partialResolver: partialResolver);
} on FileSystemException catch (e) {
throw MustachioResolutionError(
'FileSystemException when reading template "${file.path}": ${e.message}');
}
}

String _render_Baz(Baz context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = Renderer_Baz(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer =
Renderer_Baz(context, parent, file, partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand Down Expand Up @@ -303,8 +313,9 @@ class Renderer_Baz extends RendererBase<Baz> {
...Renderer_Object.propertyMap<CT_>(),
};

Renderer_Baz(Baz context, RendererBase<Object> parent, File file)
: super(context, parent, file);
Renderer_Baz(Baz context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);

@override
Property<Baz> getProperty(String key) {
Expand Down
18 changes: 18 additions & 0 deletions test/mustachio/renderer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,24 @@ void main() {
'Line 2 Partial Section Instance of \'Bar\''));
});

test('Renderer renders a partial using a custom partial renderer', () {
var barTemplate = getFile('/project/bar.mustache')
..writeAsStringSync('Text {{#foo}}{{>_foo.mustache}}{{/foo}}');
getFile('/project/_foo.mustache').writeAsStringSync('Partial {{s1}}');
var bar = Bar()..foo = (Foo()..s1 = 'hello');
String partialResolver(String path) {
var partialPath = resourceProvider.pathContext.isAbsolute(path)
? path
: resourceProvider.pathContext.join('/project', path);
var file = resourceProvider
.getFile(resourceProvider.pathContext.normalize('_$partialPath'));
return file.readAsStringSync();
}

expect(renderBar(bar, barTemplate, partialResolver: partialResolver),
equals('Text Partial hello'));
});

test('Renderer throws when it cannot resolve a variable key', () {
var fooTemplate = getFile('/project/foo.mustache')
..writeAsStringSync('Text {{s2}}');
Expand Down
15 changes: 9 additions & 6 deletions tool/mustachio/codegen_runtime_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,11 @@ String _simpleResolveErrorMessage(List<String> key, String type) =>
if (renderer.publicApiFunctionName != null) {
_buffer.writeln('''
String ${renderer.publicApiFunctionName}${renderer._typeParametersString}(
$typeWithVariables context, File file) {
$typeWithVariables context, File file, {PartialResolver partialResolver}) {
try {
var parser = MustachioParser(file.readAsStringSync());
return ${renderer._renderFunctionName}(context, parser.parse(), file);
return ${renderer._renderFunctionName}(
context, parser.parse(), file, partialResolver: partialResolver);
} on FileSystemException catch (e) {
throw MustachioResolutionError(
'FileSystemException when reading template "\${file.path}": \${e.message}');
Expand All @@ -231,8 +232,9 @@ String ${renderer.publicApiFunctionName}${renderer._typeParametersString}(
_buffer.writeln('''
String ${renderer._renderFunctionName}${renderer._typeParametersString}(
$typeWithVariables context, List<MustachioNode> ast, File file,
{RendererBase<Object> parent}) {
var renderer = ${renderer._rendererClassName}(context, parent, file);
{RendererBase<Object> parent, PartialResolver partialResolver}) {
var renderer = ${renderer._rendererClassName}(
context, parent, file, partialResolver: partialResolver);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}
Expand All @@ -247,8 +249,9 @@ class ${renderer._rendererClassName}${renderer._typeParametersString}
// Write out the constructor.
_buffer.writeln('''
${renderer._rendererClassName}(
$typeWithVariables context, RendererBase<Object> parent, File file)
: super(context, parent, file);
$typeWithVariables context, RendererBase<Object> parent, File file,
{PartialResolver partialResolver})
: super(context, parent, file, partialResolver: partialResolver);
''');
var propertyMapTypeArguments = renderer._typeArgumentsStringWith(typeName);
var propertyMapName = 'propertyMap$propertyMapTypeArguments';
Expand Down