diff --git a/CHANGELOG.md b/CHANGELOG.md index e124b45..b0e267c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,10 @@ - `UndefinedError` exception - `UndefinedFactory` typedef - `Environment`: - - `Environment({undefined})` argument - - `undefined` field + - `Environment({UndefinedFactory undefined})` argument + - `UndefinedFactory undefined` field - `Template`: - - `Template({undefined})` argument + - `Template({UndefinedFactory undefined})` argument - Filters: - `null` (`none` alias) diff --git a/README.md b/README.md index 8b82866..92ca039 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,22 @@ See also examples with [conduit][conduit_example] and - `stream` method - Relative template paths - Async Support +- Template Inheritance + - Super Blocks + - `{{ super.super() }}` +- List of Control Structures + - Extends + - Execute non-`block` statements and expressions + ```jinja + {% extends 'base.html' %} + {% set title = 'Index' %} + {% macro header() %} +

{{ title }}

+ {% endmacro %} ``` + {% block body %} + {{ header() }} + {% endblock %} + ``` - Expressions - Dart Methods and Properties - `!.`/`?.` @@ -73,7 +89,6 @@ See also examples with [conduit][conduit_example] and - ... - List of Global Functions - `lipsum` - - `dict` - `cycler` - `joiner` - Extensions @@ -165,6 +180,7 @@ See also examples with [conduit][conduit_example] and - `{{ string.toUpperCase() }}` - `{{ list.add(item) }}` - List of Global Functions + - ~~`dict`~~ - `print` - `range` - `list` diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart index 11ca430..08476c4 100644 --- a/lib/src/compiler.dart +++ b/lib/src/compiler.dart @@ -3,16 +3,18 @@ import 'package:jinja/src/nodes.dart'; import 'package:jinja/src/visitor.dart'; import 'package:meta/meta.dart'; +// TODO(renderer): Rename to `StringSinkRendererCompiler` +// and move to `renderer.dart`. Add `ContextNode` for `ContextCallback`s. @doNotStore class RuntimeCompiler implements Visitor { RuntimeCompiler() : _imports = {}, - _macroses = {}, + _macros = {}, _inMacro = false; final Set _imports; - final Set _macroses; + final Set _macros; bool _inMacro; @@ -46,8 +48,8 @@ class RuntimeCompiler implements Visitor { @override Call visitCall(Call node, void context) { - // Modifies Template AST from `loop.cycle(first, second, *list)` - // to `loop['cycle']([first, second], list)`, which matches + // Modifies Template AST from `loop.cycle(first, second)` + // to `loop['cycle']([first, second])`, which matches // [LoopContext.cycle] definition. // // TODO(compiler): check name arguments @@ -59,7 +61,7 @@ class RuntimeCompiler implements Visitor { : [Array(values: calling.arguments)]; return node.copyWith( - value: visitNode(node.value, context), + value: Item(key: Constant(value: 'cycle'), value: value), calling: Calling(arguments: visitNodes(arguments, context)), ); } @@ -94,7 +96,7 @@ class RuntimeCompiler implements Visitor { ); } - if (_inMacro && name == 'caller' || _macroses.contains(name)) { + if (_inMacro && name == 'caller' || _macros.contains(name)) { var calling = visitNode(node.calling, context); var arguments = calling.arguments; var keywords = calling.keywords; @@ -344,7 +346,7 @@ class RuntimeCompiler implements Visitor { @override FromImport visitFromImport(FromImport node, void context) { for (var (name, alias) in node.names) { - _macroses.add(alias ?? name); + _macros.add(alias ?? name); } return node.copyWith(template: visitNode(node.template, context)); @@ -378,7 +380,7 @@ class RuntimeCompiler implements Visitor { @override Macro visitMacro(Macro node, void context) { _inMacro = true; - _macroses.add(node.name); + _macros.add(node.name); var positional = []; var named = <(Expression, Expression)>[]; @@ -420,17 +422,29 @@ class RuntimeCompiler implements Visitor { } @override - Output visitOutput(Output node, void context) { + Node visitOutput(Output node, void context) { + if (node.nodes.isEmpty) { + return Data(); + } + + if (node.nodes.length == 1) { + return visitNode(node.nodes[0], context); + } + return node.copyWith(nodes: visitNodes(node.nodes, context)); } @override TemplateNode visitTemplateNode(TemplateNode node, void context) { - return node.copyWith( - blocks: visitNodes(node.blocks, context), - macros: visitNodes(node.macros, context), - body: visitNode(node.body, context), - ); + var body = visitNode(node.body, context) as Node; + var blocks = [if (body is Block) body, ...body.findAll()]; + var macros = [if (body is Macro) body, ...body.findAll()]; + + if (body is Output && body.nodes.first is Extends) { + body = body.nodes.first; + } + + return node.copyWith(blocks: blocks, macros: macros, body: body); } @override diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 0b5c921..d34cd65 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -342,10 +342,8 @@ base class Environment { } /// Parse the source code and return the AST node. - /// - /// This can be useful for debugging or to extract information from templates. Node parse(String source, {String? path}) { - return scan(lex(source), path: path); + return Parser(this, path: path).parse(source); } /// Load a template from a source string without using [loader]. @@ -564,7 +562,7 @@ base class Template { /// The global variables for this template. final Map globals; - /// Template body node. + // @nodoc @internal final TemplateNode body; diff --git a/lib/src/loop.dart b/lib/src/loop.dart index b529809..b504e10 100644 --- a/lib/src/loop.dart +++ b/lib/src/loop.dart @@ -1,197 +1,5 @@ -final class LoopContext extends Iterable { - LoopContext(this.values, this.depth0, this.recurse) - : length = values.length, - index0 = -1; +@Deprecated('Use `package:jinja/src/runtime.dart` instead') +library; - final List values; - - @override - final int length; - - final int depth0; - - final String Function(Object? data, [int depth]) recurse; - - int index0; - - @override - LoopIterator get iterator { - return LoopIterator(this); - } - - int get index { - return index0 + 1; - } - - int get depth { - return depth0 + 1; - } - - int get revindex0 { - return length - index; - } - - int get revindex { - return length - index0; - } - - @override - bool get first { - return index0 == 0; - } - - @override - bool get last { - return index == length; - } - - Object? get next { - if (last) { - return null; - } - - return values[index0 + 1]; - } - - Object? get prev { - if (first) { - return null; - } - - return values[index0 - 1]; - } - - Object? operator [](String key) { - switch (key) { - case 'length': - return length; - case 'index0': - return index0; - case 'depth0': - return depth0; - case 'index': - return index; - case 'depth': - return depth; - case 'revindex0': - return revindex0; - case 'revindex': - return revindex; - case 'first': - return first; - case 'last': - return last; - case 'prev': - case 'previtem': - return prev; - case 'next': - case 'nextitem': - return next; - case 'call': - return call; - case 'cycle': - return cycle; - case 'changed': - return changed; - default: - var invocation = Invocation.getter(Symbol(key)); - throw NoSuchMethodError.withInvocation(this, invocation); - } - } - - String call(Object? data) { - return recurse(data, depth); - } - - Object? cycle(Iterable values) { - var list = values.toList(); - - if (list.isEmpty) { - // TODO(loop): update error - throw TypeError(); - } - - return list[index0 % list.length]; - } - - bool changed(Object? item) { - if (index0 == 0) { - return true; - } - - if (item == prev) { - return false; - } - - return true; - } -} - -final class LoopIterator implements Iterator { - LoopIterator(this.context); - - final LoopContext context; - - @override - Object? get current { - return context.values[context.index0]; - } - - @override - bool moveNext() { - if (context.index < context.length) { - context.index0 += 1; - return true; - } - - return false; - } -} - -final class Cycler extends Iterable { - Cycler(Iterable values) - : values = List.of(values), - length = values.length, - index = 0; - - final List values; - - @override - final int length; - - int index; - - Object? get current { - return values[index]; - } - - @override - Iterator get iterator { - return CyclerIterator(this); - } - - Object? next() { - var result = current; - index = (index + 1) % length; - return result; - } - - void reset() { - index = 0; - } -} - -final class CyclerIterator implements Iterator { - CyclerIterator(this.cycler); - - final Cycler cycler; - - @override - Object? current; - - @override - bool moveNext() { - current = cycler.next(); - return true; - } -} +export 'package:jinja/src/runtime.dart' + show LoopContext, LoopIterator, Cycler, CyclerIterator; diff --git a/lib/src/nodes.dart b/lib/src/nodes.dart index a05b4b3..c333562 100644 --- a/lib/src/nodes.dart +++ b/lib/src/nodes.dart @@ -137,7 +137,7 @@ final class Output extends Node { } final class TemplateNode extends Node { - const TemplateNode({ + TemplateNode({ this.blocks = const [], this.macros = const [], required this.body, @@ -169,14 +169,6 @@ final class TemplateNode extends Node { @override Iterable findAll() sync* { - for (var block in blocks) { - if (block case T block) { - yield block; - } - - yield* block.findAll(); - } - if (body case T body) { yield body; } diff --git a/lib/src/nodes/statements.dart b/lib/src/nodes/statements.dart index edc5f77..8948ffb 100644 --- a/lib/src/nodes/statements.dart +++ b/lib/src/nodes/statements.dart @@ -1,6 +1,6 @@ part of '../nodes.dart'; -abstract class ImportContext { +abstract interface class ImportContext { bool get withContext; } diff --git a/lib/src/optimizer.dart b/lib/src/optimizer.dart index 30b9d9d..7137e08 100644 --- a/lib/src/optimizer.dart +++ b/lib/src/optimizer.dart @@ -413,10 +413,7 @@ class Optimizer implements Visitor { @override TemplateNode visitTemplateNode(TemplateNode node, Context context) { - return node.copyWith( - blocks: visitNodes(node.blocks, context), - body: visitNode(node.body, context), - ); + return node.copyWith(body: visitNode(node.body, context)); } @override diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 96734b9..6490080 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -9,7 +9,7 @@ final class Parser { Parser(this.environment, {this.path}) : endTokensStack = >[], tagStack = [], - blocks = []; + blocks = {}; final Environment environment; @@ -19,7 +19,7 @@ final class Parser { final List tagStack; - final List blocks; + final Set blocks; Extends? extendsNode; @@ -322,7 +322,7 @@ final class Parser { var token = reader.next(); var name = reader.expect('name'); - if (blocks.any((block) => block.name == name.value)) { + if (!blocks.add(name.value)) { fail("Block '${name.value}' defined twice.", reader.current.line); } @@ -350,15 +350,12 @@ final class Parser { reader.next(); } - var block = Block( + return Block( name: name.value, scoped: scoped, required: required, body: body, ); - - blocks.add(block); - return block; } Extends parseExtends(TokenReader reader) { @@ -1319,20 +1316,7 @@ final class Parser { Node scan(Iterable tokens) { var reader = TokenReader(tokens); var nodes = subParse(reader); - - if (extendsNode case var extendsNode?) { - nodes = [extendsNode]; - } - - if (blocks.isEmpty) { - if (nodes.length == 1) { - return nodes.first; - } - - return Output(nodes: nodes); - } - - return TemplateNode(blocks: blocks.toList(), body: Output(nodes: nodes)); + return Output(nodes: nodes); } List subParse( @@ -1397,6 +1381,6 @@ final class Parser { Node parse(String template) { var tokens = environment.lex(template, path: path); - return scan(tokens); + return TemplateNode(body: scan(tokens)); } } diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart index ca0a9e3..24a0fba 100644 --- a/lib/src/renderer.dart +++ b/lib/src/renderer.dart @@ -2,7 +2,6 @@ import 'dart:math' as math; import 'package:jinja/src/environment.dart'; import 'package:jinja/src/exceptions.dart'; -import 'package:jinja/src/loop.dart'; import 'package:jinja/src/nodes.dart'; import 'package:jinja/src/runtime.dart'; import 'package:jinja/src/tests.dart'; @@ -19,13 +18,6 @@ abstract base class RenderContext extends Context { super.data, }); - @override - RenderContext derived({String? template, Map? data}); - - Object? finalize(Object? object) { - return environment.finalize(this, object); - } - void assignTargets(Object? target, Object? current) { if (target is String) { set(target, current); @@ -46,15 +38,22 @@ abstract base class RenderContext extends Context { } else if (target is NamespaceValue) { var value = resolve(target.name); - if (value is Namespace) { - value[target.item] = current; - } else { - throw TemplateRuntimeError('Non-namespace object'); + if (value is! Namespace) { + throw TemplateRuntimeError('Non-namespace object.'); } + + value[target.item] = current; } else { throw TypeError(); } } + + @override + RenderContext derived({String? template, Map? data}); + + Object? finalize(Object? object) { + return environment.finalize(this, object); + } } base class StringSinkRenderContext extends RenderContext { @@ -87,7 +86,7 @@ base class StringSinkRenderContext extends RenderContext { return StringSinkRenderContext( environment, sink ?? this.sink, - template: template, + template: template ?? this.template, blocks: blocks, parent: parent, data: data, @@ -105,11 +104,11 @@ base class StringSinkRenderer const StringSinkRenderer(); Map getDataForTargets(Object? targets, Object? current) { - if (targets case String string) { - return {string: current}; + if (targets is String) { + return {targets: current}; } - if (targets case List targets) { + if (targets is List) { var names = targets.cast(); var values = list(current); @@ -140,8 +139,8 @@ base class StringSinkRenderer String macro(List positional, Map named) { var buffer = StringBuffer(); var derived = context.derived(sink: buffer); - var index = 0; + var index = 0; var length = node.positional.length; for (; index < length; index += 1) { @@ -258,7 +257,7 @@ base class StringSinkRenderer buffer.write(value.accept(this, context)); } - return '$buffer'; + return buffer.toString(); } @override @@ -387,7 +386,7 @@ base class StringSinkRenderer var derived = context.derived(sink: buffer); node.body.accept(this, derived); - Object? value = '$buffer'; + Object? value = buffer.toString(); var filters = node.filters; @@ -448,7 +447,7 @@ base class StringSinkRenderer var derived = context.derived(sink: buffer); node.body.accept(this, derived); - Object? value = '$buffer'; + Object? value = buffer.toString(); for (var Filter(name: name, calling: calling) in node.filters) { var (positional, named) = calling.accept(this, context) as Parameters; @@ -482,6 +481,7 @@ base class StringSinkRenderer orElse.accept(this, context); } + // Empty string prevents calling `finalize` on `null`. return ''; } @@ -510,6 +510,7 @@ base class StringSinkRenderer node.body.accept(this, forContext); } + // Empty string prevents calling `finalize` on `null`. return ''; } @@ -643,35 +644,57 @@ base class StringSinkRenderer @override void visitTemplateNode(TemplateNode node, StringSinkRenderContext context) { + // TODO(renderer): add `TemplateReference` var self = Namespace(); for (var block in node.blocks) { + var blockName = block.name; + + // TODO(compiler): switch to `ContextCallback` String render() { - var blocks = context.blocks[block.name]; + var blocks = context.blocks[blockName]; - if (blocks == null || blocks.isEmpty) { - throw UndefinedError("Block '${block.name}' is not defined."); + if (blocks == null) { + throw UndefinedError("Block '$blockName' is not defined."); } - blocks.last(context); + // TODO(renderer): check if empty + blocks[0](context); return ''; } - self[block.name] = render; + self[blockName] = render; - var blocks = context.blocks[block.name] ??= []; + var blocks = context.blocks[blockName] ??= []; if (block.required) { - String callback(Context context) { + Never callback(Context context) { throw TemplateRuntimeError( "Required block '${block.name}' not found."); } blocks.add(callback); } else { - String callback(Context context) { + var parentIndex = blocks.length + 1; + + void callback(Context context) { + var current = context.get('super'); + + // TODO(renderer): add `BlockReference` + String parent() { + var blocks = context.blocks[blockName]!; + + if (parentIndex >= blocks.length) { + throw TemplateRuntimeError("Super block '$blockName' not found."); + } + + blocks[parentIndex](context); + return ''; + } + + context.set('super', parent); block.body.accept(this, context); - return ''; + context.set('super', current); } blocks.add(callback); diff --git a/lib/src/runtime.dart b/lib/src/runtime.dart index 07ac9a9..88979d0 100644 --- a/lib/src/runtime.dart +++ b/lib/src/runtime.dart @@ -70,7 +70,11 @@ base class Context { return parent[name]; } - return environment.undefined(name, template); + return undefined(name, template); + } + + Object? get(String key) { + return context[key]; } void set(String key, Object? value) { @@ -115,6 +119,204 @@ base class Context { } } +final class LoopContext extends Iterable { + LoopContext(this.values, this.depth0, this.recurse) + : length = values.length, + index0 = -1; + + final List values; + + @override + final int length; + + final int depth0; + + final String Function(Object? data, [int depth]) recurse; + + int index0; + + @override + LoopIterator get iterator { + return LoopIterator(this); + } + + int get index { + return index0 + 1; + } + + int get depth { + return depth0 + 1; + } + + int get revindex0 { + return length - index; + } + + int get revindex { + return length - index0; + } + + @override + bool get first { + return index0 == 0; + } + + @override + bool get last { + return index == length; + } + + Object? get next { + if (last) { + return null; + } + + return values[index0 + 1]; + } + + Object? get prev { + if (first) { + return null; + } + + return values[index0 - 1]; + } + + Object? operator [](String key) { + switch (key) { + case 'length': + return length; + case 'index0': + return index0; + case 'depth0': + return depth0; + case 'index': + return index; + case 'depth': + return depth; + case 'revindex0': + return revindex0; + case 'revindex': + return revindex; + case 'first': + return first; + case 'last': + return last; + case 'prev': + case 'previtem': + return prev; + case 'next': + case 'nextitem': + return next; + case 'call': + return call; + case 'cycle': + return cycle; + case 'changed': + return changed; + default: + var invocation = Invocation.getter(Symbol(key)); + throw NoSuchMethodError.withInvocation(this, invocation); + } + } + + String call(Object? data) { + return recurse(data, depth); + } + + Object? cycle(Iterable values) { + var list = values.toList(); + + if (list.isEmpty) { + // TODO(loop): update error + throw TypeError(); + } + + return list[index0 % list.length]; + } + + bool changed(Object? item) { + if (index0 == 0) { + return true; + } + + if (item == prev) { + return false; + } + + return true; + } +} + +final class LoopIterator implements Iterator { + LoopIterator(this.context); + + final LoopContext context; + + @override + Object? get current { + return context.values[context.index0]; + } + + @override + bool moveNext() { + if (context.index < context.length) { + context.index0 += 1; + return true; + } + + return false; + } +} + +final class Cycler extends Iterable { + Cycler(Iterable values) + : values = List.of(values), + length = values.length, + index = 0; + + final List values; + + @override + final int length; + + int index; + + Object? get current { + return values[index]; + } + + @override + Iterator get iterator { + return CyclerIterator(this); + } + + Object? next() { + var result = current; + index = (index + 1) % length; + return result; + } + + void reset() { + index = 0; + } +} + +final class CyclerIterator implements Iterator { + CyclerIterator(this.cycler); + + final Cycler cycler; + + @override + Object? current; + + @override + bool moveNext() { + current = cycler.next(); + return true; + } +} + base class Namespace { Namespace([Map? data]) : context = {...?data}; diff --git a/lib/src/visitor.dart b/lib/src/visitor.dart index b28918d..11ca044 100644 --- a/lib/src/visitor.dart +++ b/lib/src/visitor.dart @@ -87,183 +87,183 @@ class ThrowingVisitor implements Visitor { @override R visitArray(Array node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitAttribute(Attribute node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitCall(Call node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitCalling(Calling node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitCompare(Compare node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitConcat(Concat node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitCondition(Condition node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitConstant(Constant node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitDict(Dict node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitFilter(Filter node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitItem(Item node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitLogical(Logical node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitName(Name node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitNamespaceRef(NamespaceRef node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitScalar(Scalar node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitTest(Test node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitTuple(Tuple node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitUnary(Unary node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } // Statements @override R visitAssign(Assign node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitAssignBlock(AssignBlock node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitBlock(Block node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitCallBlock(CallBlock node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitData(Data node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitDo(Do node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitExtends(Extends node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitFilterBlock(FilterBlock node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitFor(For node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitFromImport(FromImport node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitIf(If node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitImport(Import node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitInclude(Include node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitInterpolation(Interpolation node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitMacro(Macro node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitOutput(Output node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitTemplateNode(TemplateNode node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } @override R visitWith(With node, C context) { - throw UnimplementedError('$node'); + throw UnimplementedError(); } } diff --git a/pubspec.yaml b/pubspec.yaml index db1ede8..a4dac83 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: jinja -version: 0.6.1-dev.4 +version: 0.6.1-dev.5 description: >- Jinja2 template engine for Dart. Variables, expressions, control structures and template inheritance. diff --git a/test/api_test.dart b/test/api_test.dart index f1b4f34..a6cec19 100644 --- a/test/api_test.dart +++ b/test/api_test.dart @@ -2,7 +2,6 @@ library; import 'package:jinja/jinja.dart'; -import 'package:jinja/src/loop.dart'; import 'package:jinja/src/runtime.dart'; import 'package:test/test.dart'; diff --git a/test/statements/set_test.dart b/test/statements/set_test.dart index 7a95936..bb3d66c 100644 --- a/test/statements/set_test.dart +++ b/test/statements/set_test.dart @@ -30,7 +30,7 @@ void main() { expect( () => tmpl.render({'foo': emptyMap}), throwsA(predicate( - (error) => error.message == 'Non-namespace object'))); + (error) => error.message == 'Non-namespace object.'))); }); test('namespace redefined', () { @@ -39,7 +39,7 @@ void main() { expect( () => tmpl.render({'namespace': () => emptyMap}), throwsA(predicate( - (error) => error.message == 'Non-namespace object'))); + (error) => error.message == 'Non-namespace object.'))); }); test('namespace', () { diff --git a/test/test.dart b/test/test.dart deleted file mode 100644 index 8102a69..0000000 --- a/test/test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// ignore_for_file: avoid_print - -import 'dart:convert'; - -import 'package:jinja/jinja.dart'; - -const Map sources = { - 'include': '{% macro test(foo) %}[{{ foo }}]{% endmacro %}', -}; - -void main() { - var loader = MapLoader(sources); - var env = Environment(loader: loader); - var tmpl = env.fromString(''' -{% from "include" import test -%} -{{ test("foo") }}'''); - print(JsonEncoder.withIndent(' ').convert(env.getTemplate('include').body.toJson())); - print(tmpl.render()); -}