All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
Unreleased - ReleaseDate
0.26.9 - 2024-08-01
- Don't panic when generating parser error message
0.26.8 - 2024-07-25
0.26.7 - 2024-07-25
0.26.6 - 2024-06-06
- Support for jekyll's
sort
thanks to emmyoh
0.26.5 - 2024-06-03
Bump MSRV to 1.70
- Provided an example of
liquid::partials
0.26.4 - 2023-06-09
- Initial support for
render
tag
0.26.3 - 2023-06-02
- Update dependencies
0.26.2 - 2023-05-28
Bump MSRV to 1.65
- Serialization leaves off subseconds when not needed
- Deserialiation supports with and without subseconds
0.26.1 - 2023-02-23
Bump MSRV to 1.60
- Don't lose millisecond precision on datetimes
0.26.0 - 2022-04-01
- Upgraded kstring for real this time
0.25.1 - 2022-04-01
- More datetime string formats are supported
0.25.0 - 2022-03-29
- Upgraded kstring
0.24.0 - 2022-03-09
- Use
time
instead ofchrono
0.23.1 - 2021-12-23
- Ensure
LazyStore
actually caches
0.23.0 - 2021-08-24
- Upgraded from
anymap
toanymap2
which is slightly better maintained (and removed it from the API)
0.22.0 - 2021-02-27
For the most part, only plugin authors should be impacted by these changes.
core::runtime
went through significant changesRenderable::render_to
now takes&dyn Runtime
instead of&Runtime<'_>
- Adding a new stack frame is now a
StackFrame::new
instead ofRuntime.run_in_scope
- This opens up taking references to layers lower in the stack.
runtime.
to access stack functions instead ofruntime.stack_mut()
InterruptState
is nowInterruptRegister
and accessed viaruntime.registers()
- Functions were renamed while at it.
core::model
has been flattenedderive(ValueView)
now requires being used withimpl ObjectView
liquid-core
users now need to opt-in to thederive
feature for derive macros
API
- Allow
#[derive(liquid_core::ObjectView, liquid_core::ValueView)]
(previously only worked fromliquid
, making it unusable for thelib
crate)
- Remove
serde
requirement forderive(ValueView)
, making it work with more types (likefield: &dyn ValueView
).
- Reduce allocations for for-loop variables
- Reduce overhead from
derive(ValueView)
generatingto_value
Benchmarks:
- Baseline was Liquid 0.21.5
- Variability tended to be high when in the low
us
range - For the most part, this release brings us in line with Tera's performance (when we weren't already faster).
- This is with correcting for a bug in Tera's benchmarks
- Something is off about
bench_big_loop_big_object/render
, it was hardly impacted by the changes and yet that should have been case that greatly improved. Further investigation is needed.
handlebars_bench_template/parse/handlebars
time: [24.068 us 24.265 us 24.561 us]
change: [-19.750% -17.343% -14.831%] (p = 0.00 < 0.05)
Performance has improved.
handlebars_bench_template/parse/liquid
time: [19.382 us 19.438 us 19.503 us]
change: [-1.7723% -0.6484% +0.3359%] (p = 0.25 > 0.05)
No change in performance detected.
handlebars_bench_template/render/handlebars
time: [17.542 us 17.644 us 17.768 us]
change: [+0.2705% +0.9005% +1.5616%] (p = 0.01 < 0.05)
Change within noise threshold.
handlebars_bench_template/render/liquid
time: [9.0979 us 9.1362 us 9.1840 us]
change: [-29.397% -28.804% -28.079%] (p = 0.00 < 0.05)
Performance has improved.
handlebars_bench_large_loop/render/handlebars
time: [2.1674 ms 2.1748 ms 2.1828 ms]
change: [-1.6970% -0.9970% -0.3973%] (p = 0.00 < 0.05)
Change within noise threshold.
handlebars_bench_large_loop/render/liquid
time: [971.57 us 1.0048 ms 1.0330 ms]
change: [-25.328% -22.623% -20.133%] (p = 0.00 < 0.05)
Performance has improved.
liquid_bench_fixtures/parse/Hello World
time: [1.5103 us 1.5228 us 1.5364 us]
change: [+0.9648% +2.0790% +3.2025%] (p = 0.00 < 0.05)
Change within noise threshold.
liquid_bench_fixtures/render/Hello World
time: [325.45 ns 327.42 ns 329.17 ns]
change: [+66.006% +66.928% +67.840%] (p = 0.00 < 0.05)
Performance has regressed.
bench_big_loop_big_object/render/tera
time: [9.7588 us 10.082 us 10.483 us]
change: [+16.670% +19.302% +22.469%] (p = 0.00 < 0.05)
Performance has regressed.
bench_big_loop_big_object/render/liquid
time: [243.01 us 244.43 us 245.82 us]
change: [-3.1405% -2.6344% -2.1172%] (p = 0.00 < 0.05)
Performance has improved.
bench_big_table/render/tera
time: [3.6955 ms 3.7093 ms 3.7226 ms]
change: [-14.427% -12.151% -9.8055%] (p = 0.00 < 0.05)
Performance has improved.
bench_big_table/render/liquid
time: [5.4149 ms 5.4978 ms 5.5944 ms]
change: [-56.570% -55.904% -55.143%] (p = 0.00 < 0.05)
Performance has improved.
bench_teams/render/tera time: [8.9949 us 9.0961 us 9.2245 us]
change: [+11.039% +14.026% +16.983%] (p = 0.00 < 0.05)
Performance has regressed.
bench_teams/render/liquid
time: [9.0989 us 9.1398 us 9.1854 us]
change: [-30.075% -29.750% -29.403%] (p = 0.00 < 0.05)
Performance has improved.
bench_parsing_basic_template/render/tera
time: [29.527 us 29.732 us 29.979 us]
change: [-0.2179% +0.8213% +2.0881%] (p = 0.16 > 0.05)
No change in performance detected.
bench_parsing_basic_template/render/liquid
time: [16.676 us 16.724 us 16.772 us]
change: [-1.2453% -0.7338% -0.3215%] (p = 0.00 < 0.05)
Change within noise threshold.
bench_rendering_only_variable/render/tera
time: [1.4250 us 1.4302 us 1.4351 us]
change: [-14.768% -12.240% -9.6021%] (p = 0.00 < 0.05)
Performance has improved.
bench_rendering_only_variable/render/liquid
time: [851.04 ns 859.36 ns 867.64 ns]
change: [+26.841% +28.148% +29.436%] (p = 0.00 < 0.05)
Performance has regressed.
bench_rendering_basic_templates/render/tera
time: [7.9776 us 8.2825 us 8.5379 us]
change: [+8.1702% +11.263% +14.601%] (p = 0.00 < 0.05)
Performance has regressed.
bench_rendering_basic_templates/render/liquid
time: [3.9680 us 3.9832 us 3.9992 us]
change: [+15.551% +16.062% +16.660%] (p = 0.00 < 0.05)
Performance has regressed.
bench_huge_loop/render/tera
time: [850.76 us 857.75 us 865.69 us]
change: [+0.4801% +0.9708% +1.5712%] (p = 0.00 < 0.05)
Change within noise threshold.
bench_huge_loop/render/liquid
time: [803.23 us 808.50 us 814.27 us]
change: [-32.892% -32.340% -31.777%] (p = 0.00 < 0.05)
Performance has improved.
bench_access_deep_object/render/tera
time: [5.5409 us 5.5652 us 5.5925 us]
change: [-3.5414% -2.1804% -1.0822%] (p = 0.00 < 0.05)
Performance has improved.
bench_access_deep_object/render/liquid
time: [4.2440 us 4.2770 us 4.3158 us]
change: [-43.376% -42.898% -42.391%] (p = 0.00 < 0.05)
Performance has improved.
bench_access_deep_object_with_literal/render/tera
time: [7.7474 us 7.7802 us 7.8141 us]
change: [+0.9487% +1.4115% +1.9242%] (p = 0.00 < 0.05)
Change within noise threshold.
bench_access_deep_object_with_literal/render/liquid
time: [6.2592 us 6.2853 us 6.3135 us]
change: [-48.596% -47.174% -45.635%] (p = 0.00 < 0.05)
Performance has improved.
0.21.5 - 2021-02-03
{% include %}
: Support for variable passing for (#310 closed by #424){% forloop %}
: Support forparentloop
variable (#271 closed by #425)
- Conformance: support looping over
nil
(Fixes #294)
- Fix date serialization
- Fix compilation error in lib "extras"
where
filter is now being included when stdlib is requested.
- Switched from
ScalarCow
usingi32
toi64
.
API
- Switched from
ScalarCow
usingi32
toi64
. - Added
from_value(&dyn ValueView)
to complementto_value(...) -> Value
ValueView
support was added to all integer types
- Don't crash on bad date formats (see #409)
- Support
split
onnil
(see #403)
- Fix overflow in truncate (see #402)
- Don't panic on divide-by-zero (see #404)
This release resolves several planned breaking changes we've been holding off on. This doesn't make us ready for 1.0 yet but this closes the gap significantly.
We're striving to match the liquid-ruby's behavior and this release gets us closer:
where
filter implemented by or17191- Improvements to
sort
,sort_natural
,compact
, and other filters by or17191 - Improvements to
include
s conformance. Before, it was a weird hybrid of jekyll and stdlib styles. - Support for
{{ var.size }}
- Improved equality of values
In addition, we've made it more clear what filters, tags, and blocks are a part of core liquid, Jekyll's extensions, Shopify's extensions, or our own extensions.
The liquid
crate has been stripped down to what is needed for parsing and rendering a template.
liquid_core
was created as a convenience for plugin authors.liquid_lib
has all plugins soliquid
can focus on providing thestdlib
while non-stdlib
plugins can more easily evolve.
Previously, you had to construct a liquid::value::Object
(a newtype for a HashMap
) to pass to render
. Now, you can create a struct
that implements ObjectView
and ValueView
instead and pass it in:
#[derive(liquid::ObjectView, liquid::ValueView, serde::Serialize, serde::Deserialize, Debug)]
struct Data {
foo: i32,
bar: String,
}
let data = Data::default();
let template = todo!();
let s = template.render(&data)?;
In addition to the ergonomic improvements, this can help squeeze out the most performance:
- Can reuse borrowed data rather than having to switch everything to an owned type.
- Avoid allocating for the
HashMap
entries.
These improvements will be in the caller of liquid
and don't show up in our benchmarks.
There multiple convenient ways to construct your data
, depending on your application:
let template = todo!();
// `Object` is a newtype for `HashMap` and has a similar interface.
let object = liquid::Object::new();
let s = template.render(&object)?;
let object = liquid::object!({
"foo" => 0,
"bar" => "Hello World",
});
let s = template.render(&object)?;
// Requires your struct implements `serde::Serialize`
let data = todo!();
let object = liquid::to_object(&data)?;
let s = template.render(&object)?;
// Using the aforementioned derive.
let data = Data::default();
let s = template.render(&data)?;
A core data type in liquid is an Object
, a mapping of strings to Value
s. Strings used as keys within a template engine are:
- Immutable, not needing separate
size
andcapacity
fields of aString
.Box<str>
is more appropriate. - Generally short, gaining a lot from small-string optimizations
- Depending on the application,
'static
. Something like aCow<'static, str>
. Even better if it can preserve'static
getting a reference and going back to an owned value.
Combining these together gives us the new kstring
crate. Some quick benchmarking suggests
- Equality is faster than
String
(as a gauge of access time). - Cloning takes 1/5 the time when using
'static
or small string optimization.
- String types have been switched to
kstring
types for small string and'static
optimizations. - Plugins (tags, filters, and blocks)
- Reflection traits are no longer a super trait but instead a getter is used.
- Filter API changed to accept a
&dyn ValueView
liquid
is stripped down to being about to parse and render. For tag, filter, and block plugins,liquid_core
will have everything you need.
- Value:
- Functionality has moved from
Value
toValueView
,ArrayView
, andObjectView
. Date
was renamed toDateTime
.DateTime
is now a newtype.
- Functionality has moved from
- Library:
liquid
no longer exposes filters, tags, or blocks. Depend onliquid_lib
and enable the relevant features to get them.ParserBuilder
sextra_filters
andjekyll_filters
are no more. Instead depend onliquid_lib
, enable theextras
,shopify
, orjekyll
features and manually add them.ParserBuilder
swith_liquid
andliquid
have been renamed towith_stdlib
andstdlib
.include
tag was a hybrid of jekyll and liquid styles. Now there are separate jekyll and luquid plugins.
- Value
- Scalar extended with a date-only type (#253 fixed in #363).
- Support structs being
ObjectView
(#379). to_scalar
andto_object
functions along with existingto_value
(#381).scalar
,array
, andobject
macros along with existingvalue
(#381).- derive macros for
ObjectView
/ValueView
(#385). - Support
.size
(#136 fixed in #390).
- Parser:
- Initial reflection support (#357).
- Render:
- Make accessing variables faster (#386).
- Filters:
- Support the
where
filter (#291 fixed in #349) sort
,sort_natural
,compact
now acceptproperty
parameter (#333, #334, #335 fixed in #352).
- Support the
- Reflection
'static
lifetimes were relaxed (#367).
- Filters:
sort
order ofnil
was incorrect (#262 fixed in #352).sort
should work on scalars (#250 fixed in #352).
- jekyll-filter: slugify filter (21a5be0b)
- comment: parse tags inside comment, but ignore their content (a153b127)
- grammar: allow unmatched
}}
and%}
as valid liquid (1889c7b0, closes #320) - parser: blocks can accept invalid liquid (3b2b5fcc, closes #277)
- Indexing by variable (c216a439, closes #209)
- array: indexing with
.first
and.last
(36d79cf2) - case_block: support comma separated values in
when
(0e56f772) - errors: Report available tags blocks (04e486a8, closes #183)
- filters:
- for-block:
- if_block: support multiple conditions with
and
andor
(fb16a066) - tablerow: add tablerow object for its tag (6b95cca5)
- unless_block: support
else
(8577eb1e) - tags: add more shopify tags (18660736, closes #163)
- grammar:
- Improve error reporting (e373b1e1)
- Deeply nested array indexes (51c3a853, closes #230)
- Support more expressive indexing (e579dd3d
- for_block: make ranges inclusive. (42055c35)
- if: Improve accuracy of contains op (07452cd3)
- newlines_to_br: should preserve newlines (01904edb)
- comment_block: allow nesting (8d1f64e7)
- errors:
- dbg: Fix --help for debug tool (45c5d397)
- grammar:
- Slight change for if-existence (92aaadf5)
- if-existence bypass error reporting cost (c7fde6f4)
- Improve for-loop (f4500fdf)
- Slight speed up for for-over-hash (8e2ce0e6)
- Speed up variable accesses (f7392486
- Reduce allocations (cbb1d254, closes #188
- render:
- value:
- parser: accept newlines as
WHITESPACE
(7bec9871, closes #286, #280) - interpreter:
- Runtime partials (0ef46a17)
- Support runtime include for tags (5a0854fa
- Allow named stack frames (4c378178)
- Create dedicated Path for indexing into a Value (a936ba5290130d1fe53ccb4b59c2d04744406674)
- New caching policies (d2ba7a61c0af0600bfd8841fc6f12951ae53557c)
- Support arbitrary state (033c9b75
- error:
- value:
- Speed up variable accesses (f7392486
- Allow slicing Paths (9601e30a
- Rich Gobals API (385a62fd
- Support more expressive indexing (e579dd3d
- Force serde to always be on (7f1e2027
- Cleanup each crate's API (2e4ab661
- Isolate context creation (00cac8cb
- Isolate Stack state (3f8e9432
- Isolate cycle state (34dc950a
- Isolate interrupt state (06596643
- Reduce allocations (cbb1d254, closes #188
- compiler:
- context: Reduce scope of public API (866eb0cb
- error:
- errors: List alternative filters (406187bc
- filter: Switch to standard error type (3d18b718
- interpreter:
- perf: Don't clone globals (fbc1c153
- plugins:
- render: Use a Write (0093a595, closes #187
- value:
- array:
- blocks:
- Indexing by variable (c216a439, closes #209)
- for_block: support parameters with variables (7376ccf5, closes #162)
- filters:
- tags:
- for_block: make ranges inclusive. (42055c35)
- Force serde to always be on (7f1e2027)
- context: Reduce scope of public API (866eb0cb)
- error: Clean up error API (6a950048)
- filter: Switch to standard error type (3d18b718)
- Cleanup each crate's API (2e4ab661)
- interpreter: Clarify names (6b96f92b)
- perf: Don't clone globals (fbc1c153)
- render: Use a Write (0093a595, closes #187)
- value:
- Reduce allocations (cbb1d254, closes #188)
- render: Use a Write (0093a595, closes #187)
- value: Support
&'static str
's (e3aae68d) (7fd1e62d)
- Upgrade from f32 to f64 (3eddded2)
- Expose filters/tags (027a67cc)
- Upgrade from f32 to f64 (3eddded2)
- date: Support today/now (6a1e0a0f, closes #181)
- if: Bare if is an existence check (7ab091ca)
API
Users
- errors:
- filters: Implement basic
compact
support (c0eadd5c)
API
- Reduce string cloning (3d93928b
Users
- Reduce string cloning (3d93928b
API
- Remove warning when no-default-features (8c43de87)
Users
- filters: date_in_tz can't parse cobalt date (1dae5276)
- value: Improve value coercion practices (ebb4f40e, closes #99
- filters: date can parse YYYY-MM-DD HH:MM:SS TTTT (59ab76dc)
- Update dependencies
- parse: Error on empty expressions (5cffe44a, closes #139)
- raw: Stop swapping the text's order (bd45c14b, closes #79)
- for: Re-enable support for object.access (cc9998b5)
- api: Add missing traits (e0f82705)
- nil: Equality logic missed a case (111d10a6)
Minor docs change.
- api: Make Renderable debuggable (802b0af0)
- for: Remove non-standard for_loop variable (0d9515fe)
- Make LiquidOptions cloneable (838e5261)
- Make TemplateRepository cloneable (94f337ae)
- Make ParseBlock cloneable (472fb638)
- Make ParseTag cloneable (ec59839d)
- Make TemplateRepository cloneable (94f337ae)
- Make ParseBlock cloneable (472fb638)
- Make ParseTag cloneable (ec59839d)
- syntax: Add
arr[0]
andobj["name"]
indexing (PR #141, fixes #127) - value: Add nil value to support foreign data (PR #140, 89f6660d)
- value: Add nil value to support foreign data (PR #140, 89f6660d)
- Technically will break anyone matching on
liquid::Value
.
- Technically will break anyone matching on
- Stop recompiling everytime due to Skeptic.