bkiers / liqp Goto Github PK
View Code? Open in Web Editor NEWAn ANTLR based 'Liquid Template' parser and rendering engine.
License: MIT License
An ANTLR based 'Liquid Template' parser and rendering engine.
License: MIT License
Hi, in the develop branch the IncludeTag uses the Template.parse method without preserving the Flavor.JEKYLL, so if you use recursive includes the flavor will flip.
Also, will you merge the develop branch to master?
Parsing a template with an escaped quote in an assign tag fails. So the following template does not work.
{% assign test = "f\"oo" %} Test: {{test}}
I have tested with the following java code:
Template.parse("{% assign test = \"f\\\"oo\" %} Test: {{test}}");
I would expect it to be possible to escape quotes in the assign tag. Please let me know if I have misunderstood something or you need more information.
Filter.registerFilter()
and Tag.registerTag()
are static methods. Not able to plug different implementations during runtime as any change affects globally across the JVM.
First of all, love the library.
In our use case, we have a two-pass rendering. The first pass has a certain set of models, and the second pass has a different set. All the other filters are the same, but I could foresee a setup that used different filters based on the rendering mode.
What would be awesome is if we could tell the renderer/parser which items should be purposely ignored. If the parser encountered these items, it would leave those liquid expression in place.
Is this possible today? And could this be done without introducing a lot of complexity?
JSoup before version 1.8.3 has a vulnerability. Any chance you can bump the version and do a release?
☝️
Hi @mosabua, could you create a new release to Maven Central from master
?
Hi.
The "round" filter (Round.java
) uses the default rounding in DecimalFormat.class
which is HALF_EVEN
.
I believe that HALF_UP
may be the correct, but I have no data/reference to back this up.
HALF_UP
rounds all numbers up when the decimal is higher or equal to .5
, whereas HALF_EVEN
rounds to the nearest neighbour (as do HALF_UP
), but when equal distance to the neighbour, it rounds to the value that is even. So 10.5 becomes 10, and 11.5 becomes 12.
At least in Denmark the default rounding is always to round upwards when reached .5 in decimal.
Although with the current version that always removes all whitespaces it might not be a problem, for correctness, I think it would be better to also test Windows specific cases too.
This means doubling all tests that use \n
with similar ones using \r\n
.
In my fork that removes only one line terminator, the test file looks like this:
The adjusted parser that passes these tests is:
I'd like to somehow specify the locale which is used by the date filter.
In Date
there is a function setLocale()
, which is not in use. Also, as Date
isn't public, I can't call the function myself.
Template.render() method is creating a separate thread which is not getting closed explicitly. Method should ideally call executorService.shutdown() explicitly to properly close the thread.
Not closing threads explicitly is causing multiple threads to be alive in the system and causes problems at the time of closing the process.
@mosabua could you once again cut a new release to Maven Central from when you have the time? master
is ready for a new release.
I have some custom formatting tags that I'm working with, that would look something like this
{%red%}{%bold%}This is red, and {%yellow%} this is yellow{%endyellow%}{%endbold%}{%endred%}
This is the tree that's generated, which seems off to me, but I'm wondering if I'm not understanding something. Specifically, I'm wondering why the "yellow" atom was embedded in the {%yellow%}
tag, but the green atom wasn't.
'- Parse
|- Block
| |- Atom_tag
| | '- Tag
| | '- Other_tag
| | |- TagStart
| | | '- TerminalNodeImpl='{%'
| | |- TerminalNodeImpl='red'
| | |- TerminalNodeImpl='%}'
| | '- Other_tag_block
| | |- Atom_tag
| | | '- Tag
| | | '- Other_tag
| | | |- TagStart
| | | | '- TerminalNodeImpl='{%'
| | | |- TerminalNodeImpl='bold'
| | | |- TerminalNodeImpl='%}'
| | | '- Other_tag_block
| | | |- Atom_others
| | | | '- Other
| | | | |- TerminalNodeImpl='R'
| | | | |- TerminalNodeImpl='e'
| | | | |- TerminalNodeImpl='d'
| | | | |- TerminalNodeImpl=' '
| | | | |- TerminalNodeImpl='a'
| | | | |- TerminalNodeImpl='n'
| | | | |- TerminalNodeImpl='d'
| | | | '- TerminalNodeImpl=' '
| | | |- Atom_tag
| | | | '- Tag
| | | | '- Other_tag
| | | | |- TagStart
| | | | | '- TerminalNodeImpl='{%'
| | | | |- TerminalNodeImpl='yellow'
| | | | |- TerminalNodeImpl='%}'
| | | | '- Other_tag_block
| | | | |- Atom_others
| | | | | '- Other
| | | | | |- TerminalNodeImpl='y'
| | | | | |- TerminalNodeImpl='e'
| | | | | |- TerminalNodeImpl='l'
| | | | | |- TerminalNodeImpl='l'
| | | | | |- TerminalNodeImpl='o'
| | | | | '- TerminalNodeImpl='w'
| | | | |- TagStart
| | | | | '- TerminalNodeImpl='{%'
| | | | |- TerminalNodeImpl='endyellow'
| | | | '- TerminalNodeImpl='%}'
| | | |- Atom_others
| | | | '- Other
| | | | |- TerminalNodeImpl=' '
| | | | |- TerminalNodeImpl='a'
| | | | |- TerminalNodeImpl='n'
| | | | |- TerminalNodeImpl='d'
| | | | '- TerminalNodeImpl=' '
| | | |- Atom_tag
| | | | '- Tag
| | | | '- Other_tag
| | | | |- TagStart
| | | | | '- TerminalNodeImpl='{%'
| | | | |- TerminalNodeImpl='green'
| | | | '- TerminalNodeImpl='%}'
| | | |- Atom_others
| | | | '- Other
| | | | |- TerminalNodeImpl='g'
| | | | |- TerminalNodeImpl='r'
| | | | |- TerminalNodeImpl='e'
| | | | |- TerminalNodeImpl='e'
| | | | '- TerminalNodeImpl='n'
| | | |- TagStart
| | | | '- TerminalNodeImpl='{%'
| | | |- TerminalNodeImpl='endgreen'
| | | '- TerminalNodeImpl='%}'
| | |- TagStart
| | | '- TerminalNodeImpl='{%'
| | |- TerminalNodeImpl='endbold'
| | '- TerminalNodeImpl='%}'
| '- Atom_tag
| '- Tag
| '- Other_tag
| |- TagStart
| | '- TerminalNodeImpl='{%'
| |- TerminalNodeImpl='red'
| '- TerminalNodeImpl='%}'
'- TerminalNodeImpl='<EOF>'
We have a layout that works in Jekyll where there is an include that uses a variable defined in the front-matter to select which navigation to include. Here's an example:
---
layout: default
---
<div class="row margin-bottom-50">
{% include {{page.side-navigation}} %}
</div>
Seems like the ANTRL grammar needs to be altered to allow {{XXX}}
, and do a variable lookup in the include tag.
@mosabua could you cut a new release to Maven Central from master
when you have the time?
Jekyll has a synatx for highlighting . see https://jekyllrb.com/docs/posts/#highlighting-code-snippets
What would it take to implement this? Just add a highlight tag implementation in https://github.com/bkiers/Liqp/tree/develop/src/main/java/liqp/tags or some other stuff as well? I am not sure if this tag is liquid standard or jekyll only to be honest.
Currently {{x | default: 1}}
will fail if the strictVariables
setting is enabled. I'm not sure if this is the intended behavior, since the template should work as expected when x
is undefined.
It might be a PITA to refactor the code to support this, though.
What's your opinion on this?
The following code throws an exception parsing the template:
Template template = Template.parse("{% assign v = 1 | minus: 1 | plus: 1 %}");
The exception is:
MismatchedTokenException(85!=95)
at liqp.parser.LiquidParser.reportError(LiquidParser.java:182)
at liqp.parser.LiquidParser.assignment(LiquidParser.java:5187)
at liqp.parser.LiquidParser.atom(LiquidParser.java:478)
at liqp.parser.LiquidParser.block(LiquidParser.java:307)
at liqp.parser.LiquidParser.parse(LiquidParser.java:213)
at liqp.Template.<init>(Template.java:78)
at liqp.Template.parse(Template.java:127)
Caused by: MismatchedTokenException(85!=95)
at org.antlr.runtime.BaseRecognizer.recoverFromMismatchedToken(BaseRecognizer.java:617)
at org.antlr.runtime.BaseRecognizer.match(BaseRecognizer.java:115)
at liqp.parser.LiquidParser.assignment(LiquidParser.java:5139)
This template does parse with the Ruby version of Liquid.
A simple workaround is to use multiple assign statements for every filter you want to apply.
I'm not super familiar with ANTLR, but I assume the following assignment definition would fix the problem:
assignment
: tagStart Assign Id EqSign expr filter? TagEnd
;
assignment
: tagStart Assign Id EqSign expr filter* TagEnd
;
Thanks for the great library, the ANTLRv4 version is a great improvement too!
I'm not sure what the effort is to adopt this library to the Java module system, but in doing this with my own work, I've found it's good to follow this advice and reserve a module name before even moving a library to be modular. This is as simple as modifying the main pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Automatic-Module-Name>liqp</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
This allows consumers using Java 9+ modules to depend on this library before it even adopts modules without concern that the module name will change.
I originally thought nl.big-o.liqp
would be good a module name, but the hyphen is an invalid character in module names. Also, using just liqp
would mean that if anybody is already using this library in Java 9 as an automatic module, they wouldn't break should you decide to add this change.
I'm also glad to help modularize the library if you need any assistance. This library has been great for me, so always happy to contribute back. 👍
The latest release seems slower, I benchmarked with our project:
0.7.2: 20.49 secs
0.7.3: 21.162 secs
0.7.4: 48.954 secs
Anyone else experienced this? We don't use any advanced features.
We implemented a custom tag "snippet" to allow users to define reusable chunks of HTML. Works like a charm... Until there are > 20 tags present in the same content. Then it starts to drastically slow down the more tags are present.
Wrote the following unit test to demonstrate:
@Test
public void testCustomTags() {
final StopWatch stopWatch = new StopWatch();
for (int n=0; n<30; n++) {
final String content = StringUtils.repeat("{% snippet \"face\" %}", n);
stopWatch.start();
Template.parse(content);
stopWatch.stop();
LOG.debug("{} tags -> {} milliseconds", n, stopWatch.getTime());
stopWatch.reset();
}
}
Here is some sample output when using custom tags:
1 tags -> 0 milliseconds
2 tags -> 0 milliseconds
3 tags -> 0 milliseconds
4 tags -> 0 milliseconds
5 tags -> 1 milliseconds
6 tags -> 6 milliseconds
7 tags -> 1 milliseconds
8 tags -> 2 milliseconds
9 tags -> 4 milliseconds
10 tags -> 6 milliseconds
11 tags -> 9 milliseconds
12 tags -> 11 milliseconds
13 tags -> 11 milliseconds
14 tags -> 17 milliseconds
15 tags -> 33 milliseconds
16 tags -> 61 milliseconds
17 tags -> 79 milliseconds
18 tags -> 134 milliseconds
19 tags -> 230 milliseconds
20 tags -> 464 milliseconds
21 tags -> 887 milliseconds
22 tags -> 1727 milliseconds
23 tags -> 3393 milliseconds
24 tags -> 6865 milliseconds
25 tags -> 13781 milliseconds
26 tags -> 28804 milliseconds
27 tags -> 56767 milliseconds
Fortunately, standard tags do not have the same problem. Wrote the following unit test to demonstrate:
@Test
public void testStandardTags() {
final StopWatch stopWatch = new StopWatch();
for (int n=0; n<30; n++) {
final String content = StringUtils.repeat("{% if data.face == \"happy\" %}{% endif %}", n);
stopWatch.start();
Template.parse(content);
stopWatch.stop();
LOG.debug("{} tags -> {} milliseconds", n * 2, stopWatch.getTime());
stopWatch.reset();
}
}
And here is some sample output when using standard tags:
2 tags -> 1 milliseconds
4 tags -> 0 milliseconds
6 tags -> 0 milliseconds
8 tags -> 6 milliseconds
10 tags -> 1 milliseconds
12 tags -> 1 milliseconds
14 tags -> 1 milliseconds
16 tags -> 1 milliseconds
18 tags -> 1 milliseconds
20 tags -> 1 milliseconds
22 tags -> 1 milliseconds
24 tags -> 1 milliseconds
26 tags -> 1 milliseconds
28 tags -> 2 milliseconds
30 tags -> 2 milliseconds
32 tags -> 1 milliseconds
34 tags -> 1 milliseconds
36 tags -> 1 milliseconds
38 tags -> 1 milliseconds
40 tags -> 1 milliseconds
42 tags -> 1 milliseconds
44 tags -> 1 milliseconds
46 tags -> 1 milliseconds
48 tags -> 1 milliseconds
50 tags -> 1 milliseconds
52 tags -> 1 milliseconds
54 tags -> 1 milliseconds
I am checking out this library trying to call it directly from clojure. I noticed a that there is scope leaked into the parsing of a template for example parsing a template with a java keyword will result in something like:
(liqp.Template/parse "%{{case}}")
NoViableAltException liqp.parser.LiquidParser.term (LiquidParser.java:5469)
java.lang.RuntimeException: NoViableAltException(21@[])
at liqp.parser.LiquidParser.reportError(LiquidParser.java:163)
at liqp.parser.LiquidParser.term(LiquidParser.java:5688)
at liqp.parser.LiquidParser.rel_expr(LiquidParser.java:5307)
at liqp.parser.LiquidParser.eq_expr(LiquidParser.java:5211)
at liqp.parser.LiquidParser.contains_expr(LiquidParser.java:5128)
at liqp.parser.LiquidParser.and_expr(LiquidParser.java:5039)
at liqp.parser.LiquidParser.or_expr(LiquidParser.java:4950)
at liqp.parser.LiquidParser.expr(LiquidParser.java:4894)
at liqp.parser.LiquidParser.output(LiquidParser.java:4400)
at liqp.parser.LiquidParser.atom(LiquidParser.java:445)
at liqp.parser.LiquidParser.block(LiquidParser.java:288)
at liqp.parser.LiquidParser.parse(LiquidParser.java:194)
at liqp.Template.<init>(Template.java:65)
at liqp.Template.parse(Template.java:111)
at user$eval12727.invokeStatic(form-init1027659148715222265.clj:1)
at user$eval12727.invoke(form-init1027659148715222265.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6890)
at clojure.core$eval.invokeStatic(core.clj:3105)
at clojure.core$eval.invoke(core.clj:3101)
at clojure.main$repl$read_eval_print__7408$fn__7411.invoke(main.clj:240)
at clojure.main$repl$read_eval_print__7408.invoke(main.clj:240)
at clojure.main$repl$fn__7417.invoke(main.clj:258)
at clojure.main$repl.invokeStatic(main.clj:258)
at clojure.main$repl.doInvoke(main.clj:174)
at clojure.lang.RestFn.invoke(RestFn.java:1523)
at clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__12023.invoke(interruptible_eval.clj:87)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.core$apply.invokeStatic(core.clj:646)
at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1881)
at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1881)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at clojure.tools.nrepl.middleware.interruptible_eval$evaluate.invokeStatic(interruptible_eval.clj:85)
at clojure.tools.nrepl.middleware.interruptible_eval$evaluate.invoke(interruptible_eval.clj:55)
at clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__12068$fn__12071.invoke(interruptible_eval.clj:222)
at clojure.tools.nrepl.middleware.interruptible_eval$run_next$fn__12063.invoke(interruptible_eval.clj:190)
at clojure.lang.AFn.run(AFn.java:22)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: NoViableAltException(21@[])
at liqp.parser.LiquidParser.term(LiquidParser.java:5469)
... 39 more
I cant really trace this because the parser code seems to be generated so I was wondering if you had any idea what is going on. I cannot think that this would be safe to use without a way to it exploding on templates with legit var names like case
Can you help me understand the code base and how it prevents the untrusted user writing the template from calling methods like class loader methods, reflection, etc.
Based on the definition of a Comment tag, it appears that anything between a {% comment %}
and {% endcomment %}
should simply be omitted from the output:
Allows you to leave un-rendered code inside a Liquid template. Any text within the opening and closing comment blocks will not be output, and any Liquid code within will not be executed.
However, the following code fails to parse:
{% comment %}
{% if true %}
hello
{% endif %}
{% endcomment %}
The exception generated:
at liquid.parser.v4.LiquidParser.comment_tag(LiquidParser.java:847)
at liquid.parser.v4.LiquidParser.tag(LiquidParser.java:443)
at liquid.parser.v4.LiquidParser.atom(LiquidParser.java:332)
at liquid.parser.v4.LiquidParser.block(LiquidParser.java:211)
at liquid.parser.v4.LiquidParser.parse(LiquidParser.java:154)
at liqp.Template.parse(Template.java:138)
at liqp.Template.<init>(Template.java:77)
I think the problem is the comment_body
definition in LiquidParser.g4
:
comment_tag
: tagStart CommentStart TagEnd comment_body tagStart CommentEnd TagEnd
;
comment_body
: other_than_tag_start
;
other_than_tag_start
: ~( TagStart | TagStart2 )*
;
I would think this might be the correct definition:
comment_tag
: tagStart CommentStart TagEnd other? tagStart CommentEnd TagEnd
;
I don't know enough about ANTLR to be confident in that answer though. 😂
Ultimately where I'm trying to get is to have the equivalent of:
https://jekyllrb.com/docs/templates/#includes
In my implementation of Jekyll using Liqp I only needed to support the Jekyll case and not the general case which is what Jekyll does overriding the standard include tag. I had to change your grammar slightly which allows me to use standard Jekyll in Ruby and my Java implementation which is super handy. But I'm not sure if this will break your standard use case. Jekyll allows things like:
{% include header.html %}
Where you don't need quotes around the included resource.
I'm happy to implement a way to selectively register tags with the default behaviour as it currently is. I can also make the include grammar bits more flexible. But I do need to change the grammar to support the Jekyll style. I can do whatever you prefer.
When an assign tag tries to assign a value from an object, but the name of the objects property matches the name of another liquid tag, a NoViableAltException is thrown.
Easy to replicate with the following AssignTag test case
@Test
public void assignTestWithOtherTagName() throws Exception {
assertThat(
Template.parse("var2:{{var2}} {%assign var2 = var.comment%} var2:{{var2}}")
.render(" { \"var\": { \"comment\": \"content\" } } "),
is("var2: var2:content"));
assertThat(
Template.parse("var2:{{var2}} {%assign var2 = var.end%} var2:{{var2}}")
.render(" { \"var\": { \"end\": \"content\" } } "),
is("var2: var2:content"));
}
Presumably some form of escaping needs to happen to not treat that value as a tag? Or maybe its something else entirely?
{{ 'general.meta.tags' | t: tags: meta_tags }}
Seeing exceptions on any tags with that t: tags: meta_tags
sorta structure.
Any ideas?
Should you check for "canBeInteger" here as well and other places where arithmetic operators are used? It doesn't seem to match the original liquid implementation of Shopify where integer - integer => integer
https://github.com/bkiers/Liqp/blob/master/src/main/java/liqp/filters/Minus.java#L21
Some of the older filters are not public classes - this means that we cannot use them for testing, nor extend their functionality.
In our case, we have a String-like class (called "SafeString"), which sometimes is used and passed into the filters. We'd like to accommodate for this by extending some of the currently non-public filter classes by extracting the "non-safe" (and original String.class) value (mySafeString.getRaw() -> String.class) and pass it further down to a parent filter.
public class OurSlizeFilter extends Slice {
@Override
public final Object apply(Object value, Object... params) {
if(value instanceof SafeString){
super.apply(((SafeString) value).getRaw(), params);
} else {
super.apply(value, params);
}
}
[...]
But currently above is not possible.
Non-public classes are:
Append.java
Capitalize.java
Divided_By.java
Downcase.java
Escape.java
Escape_Once.java
First.java
H.java
Join.java
Last.java
Map.java
Minus.java
Modulo.java
Newline_To_Br.java
Plus.java
Prepend.java
Remove.java
Remove_First.java
Replace.java
Replace_First.java
Size.java
Sort.java
Split.java
Strip_HTML.java
Strip_Newlines.java
Times.java
Truncate.java
Truncatewords.java
Upcase.java
I forked this project a long while back to add support for a Java implementation of Jekyll, and I'm trying to sync up my changes but you have the ANTRL grammars in duplicated in two places. Is IDEA using them in a different place than the Maven plugin? Just wanted to check before making pull requests as I have grammar changes and don't want to alter the wrong set.
@mosabua could you cut a new release to Maven Central from master
?
I've seen this gem for the official Liquid engine in Ruby https://github.com/danwrong/liquid-inheritance which provides such functionality. I would like to implement it in this library. Is that possible?
I think it is possible but I'm not sure. To implement this we need 2 new tags: extends
and block
. The block tag will be just a custom block to mark inheritable blocks and provide the ability to call the parent block (we also would need to support the {{ block.super }}
syntax somehow. Maybe by injecting a special variable in the context). With the extends tag we need to parse the parent template, render any content outside of an inheritable block, and inject the inheritable blocks in the current template.
I think that just using Tag.registerTag()
with the mentioned new tags will not be enough. Probably we would need to change something else in the codebase.
☝️
Exception in thread "main" java.lang.RuntimeException: could not parse input: {% include wmt/html-head.html %}
{% include wmt/footer.html %}
I took a cursory look at the grammar and appears that allowing the '/' in there would fix this?
One of the additions I made in my fork was to create an IncludeJekyll tag that took the source directory of the includes from the context and didn't assume a default extension.
Would you mind a pull request that uses a source directory for the includes if one is provided in the context and uses the included resources name as-is if it has an extension? I will preserve the existing behaviour of using the snippets
directory if a source directory is not provided and defaulting to the .liquid
extension for included resources if one isn't provided.
The contents inside a capture
tag is evaluated as a block
, causing it to become a string. But the following:
{% capture n %}{{ username | size }}{% endcapture %}
should be captured as a number.
A peculiar issue here. I see that there is #66 on a similar topic. However, I want to pass in a Map<String,Object>
instead, as there might be plenty of variables I want to access.. In this case, using dot notation does not work for me.
Example:
-snip-
public static Site site;
public String buildPage(Request request, Response response) throws SQLException, ClassNotFoundException {
Page build_target = Page.get(request.pathInfo(), site.id);
Map<String, Object> params = new HashMap<String, Object>();
params.put("site", site);
Template template;
try {
template = Template.parse(new File("sites/"+site.id+"/templates/"+build_target.template+".liquid"));
return template.render(params);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "Template rendering failed";
}
-snip-
When I then try doing {{site.name}}
in the template, I get out nothing. Doing {{site}}
gives me the string representation of the Site object.
I have a include file in Jekyll that add breadcrumbs which looks like this
{% assign crumbs = page.url | split: '/' %}
<div style="font-size: 12px;">
<p><a href="/">Home</a> >
{% for crumb in crumbs offset: 1 %}
{% if forloop.last %}
<span style="font-weight: bold;">{{ page.title }}</span>
{% else %}
<a href="{% assign crumb_limit = forloop.index | plus: 1 %}{% for crumb in crumbs limit: crumb_limit %}{{ crumb | append: '/' }}{% endfor %}"
style="text-transform: capitalize"
>{{ crumb | replace:'-',' '}}</a> >
{% endif %}
{% endfor %}</p>
</div>
This works fine with Jekyll 3.3.1. When using my liqp based parsing in a java jekyll impl the order of the crumbs is messed up. E.g. instead of
Home > Docs > Getting Started > Getting Started
I get
Home > Getting Started > /Docs >
Any idea where to start looking to figure out where the problem is?
Is it possible to define a LiqpException class from which I can get details of template parsing errors?
Whitespace processing was always tricky in template engines, and the Shopify Liquid makes no exception.
The specifications (https://shopify.github.io/liquid/basics/whitespace/) mention that adding hyphen in the tag syntax will strip whitespace, but it does not give more details.
The first approach to this rule is to strip as many whitespaces (spaces, tabs, CR, LF) as possible.
Unfortunately this behaviour is too greedy, and makes impossible to generate some content (for example conditional lines that need to preserve empty lines at the end or at the beginning).
In my opinion, after working with various template engines, striping should not go past the next line terminator, i.e. it should not eat previous/following empty lines.
I made a fork of the project and experimented with different strategies. The one I ended with was to alter the behaviour when the stripSpacesAroundTags
option is enabled; the new behaviour is:
When explicit hyphens are used, the behaviour is the one you implemented, i.e. a very greedy strip of all whitespaces.
My commit with these changes is eclipse-embed-cdt@98ef5b2
An actual template file using this new configuration is https://github.com/gnu-mcu-eclipse/eclipse-plugins/blob/templates/bundles/ilg.gnumcueclipse.templates.sifive/templates/sifive_exe_c_project/src/main-liquid.c.cpp
As you can see, there are no explicit hyphens, and the syntax is natural, and the empty lines are passed as expected.
I'm happy to coordinate and do releases to Maven Central if you don't have time. I'm about to do a first release of my Jekyll-clone and I'd like to get Liqp into Maven Central so Idiom will build. Thanks.
Date
filter uses English locale always, but DateTest lines 25-45 does not check what locale is set on computer running test.
It is only a bug in tests. Everywhere
new SimpleDateFormat(pattern)
is used
new SimpleDateFormat(pattern, Locale.ENGLISH)
should be used.
I'm planning of using Liqp for a user-facing app where users can create templates and I've been thinking about security and I couldn't really see how the following cases are handled in the codebase:
Also, are there any other scenarios that I left out?
@mosabua could you cut a new release to Maven Central from master
?
See the official Liquid documentation. Whitespace around rendered blocks should be able to be removed by adding a dash to each tag. Would also be nice to support a global option to never render that whitespace.
In liqp 0.6 it was possible to use a custom tag foo
like this:
{% foo a.b.c %}
But since #46 the parameter is always parsed as the string "a.b.c", and there seems to be no built-in way to parse and evaluate the expression in this string. The following does not work either (parsing fails with a NoViableAltException):
{% foo {{ a.b.c }} %}
Is there an alternative to this? The documentation generator of the next-gen Scala compiler currently relies on this: scala/scala3#3859
Maybe some build/test dependencies are accidentally being included in compile scope? This has implications for dependency convergence, especially for people on newer versions of maven.
tree generated using mvn dependency:tree
0.7.3 tree:
[INFO] +- nl.big-o:liqp:jar:0.7.3:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.0:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-core:jar:2.9.6:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.6:compile
[INFO] | \- org.jsoup:jsoup:jar:1.10.3:compile
0.7.4 tree:
[INFO] +- nl.big-o:liqp:jar:0.7.4:compile
[INFO] | +- org.antlr:antlr4-runtime:jar:4.7.1:compile
[INFO] | +- org.antlr:antlr4-maven-plugin:jar:4.7.1:compile
[INFO] | | +- org.apache.maven:maven-plugin-api:jar:3.0.5:compile
[INFO] | | | +- org.apache.maven:maven-model:jar:3.0.5:compile
[INFO] | | | +- org.apache.maven:maven-artifact:jar:3.0.5:compile
[INFO] | | | \- org.sonatype.sisu:sisu-inject-plexus:jar:2.3.0:compile
[INFO] | | | +- org.codehaus.plexus:plexus-component-annotations:jar:1.5.5:compile
[INFO] | | | +- org.codehaus.plexus:plexus-classworlds:jar:2.4:compile
[INFO] | | | \- org.sonatype.sisu:sisu-inject-bean:jar:2.3.0:compile
[INFO] | | | \- org.sonatype.sisu:sisu-guice:jar:no_aop:3.1.0:compile
[INFO] | | | \- org.sonatype.sisu:sisu-guava:jar:0.9.9:compile
[INFO] | | +- org.codehaus.plexus:plexus-compiler-api:jar:2.2:compile
[INFO] | | | \- org.codehaus.plexus:plexus-utils:jar:3.0.8:compile
[INFO] | | +- org.sonatype.plexus:plexus-build-api:jar:0.0.7:compile
[INFO] | | +- org.antlr:antlr4:jar:4.7.1:compile
[INFO] | | | +- org.abego.treelayout:org.abego.treelayout.core:jar:1.0.3:compile
[INFO] | | | +- org.glassfish:javax.json:jar:1.0.4:compile
[INFO] | | | \- com.ibm.icu:icu4j:jar:58.2:compile
[INFO] | | \- org.apache.maven:maven-project:jar:2.2.1:compile
[INFO] | | +- org.apache.maven:maven-settings:jar:2.2.1:compile
[INFO] | | +- org.apache.maven:maven-profile:jar:2.2.1:compile
[INFO] | | +- org.apache.maven:maven-artifact-manager:jar:2.2.1:compile
[INFO] | | | +- org.apache.maven:maven-repository-metadata:jar:2.2.1:compile
[INFO] | | | +- org.apache.maven.wagon:wagon-provider-api:jar:1.0-beta-6:compile
[INFO] | | | \- backport-util-concurrent:backport-util-concurrent:jar:3.1:compile
[INFO] | | +- org.apache.maven:maven-plugin-registry:jar:2.2.1:compile
[INFO] | | +- org.codehaus.plexus:plexus-interpolation:jar:1.11:compile
[INFO] | | \- org.codehaus.plexus:plexus-container-default:jar:1.0-alpha-9-stable-1:compile
[INFO] | | +- junit:junit:jar:4.12:compile
[INFO] | | | \- org.hamcrest:hamcrest-core:jar:1.3:compile
[INFO] | | \- classworlds:classworlds:jar:1.1-alpha-2:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.9.0:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-core:jar:2.9.6:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.9.6:compile
[INFO] | \- org.jsoup:jsoup:jar:1.10.3:compile
We're trying to directly put an object into the render context like this:
template.render("some_top_level_name", someComplexObject);
And in the actual template, we can access the root object via {{ some_top_level_name }}
but not any of its attributes, like {{ some_top_level_name.snake_case_attribute }}
or {{ some_top_level_name.camelCaseAttribute }}
Does this work? Are we doing it wrong?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.