Guilherme Carreiro and Alok Swamy
5a210fc8f6
Update Rakefile
...
Co-authored-by: Alok Swamy <[email protected] >
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
8d7bb9b6f8
Update History.md (5.8.8 -> 5.9.0)
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
4f35764d44
Update History.md
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
018442b7d1
Covered changes with more tests, remove redundant cases, and the new with_error_mode(*modes)
...
Most of changes update this:
```
[:lax, :strict].each do |mode|
with_error_mode(mode) do
assert_template_result(...
```
to be this:
```
with_error_mode(:lax, :strict) do
assert_template_result(...
```
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
a23c71e40b
Fix variable to keep it backward-compatible in strict mode
...
* lax_parse - no changes
* strict_parse - uses the `lax_parse_filter_expressions` (as it was doing before)
* rigid_parse - uses the `rigid_parse_filter_expressions`
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
738540a601
Update infrastructure that handles parsing switching:
...
* Remove development helpers from parse context
* Simplify strict_parse_with_error_mode_fallback and update
documentation
* Add unit tests for `Liquid::Expression` and `Liquid::ParseContext`
* Update test helpers to work better with the `:rigid` mode
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
4cd367d971
* Update bin/render script to present an error when no template is passed
...
* Remove `bin/example.liquid` as it's not executable
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
34ab3bdc8e
Fix assert_template_result tests not picking up Liquid::Environment.default.error_mode
...
The `rake test` command gave us the impression that we were running all the tests
on all the error modes, that was false.
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
1be1e36a8d
Fixup cycle rigid parsing to be backwards compatible
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
902ff978a6
Fixup include parsing of with expression
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
2ba81b3f1a
Fix alias parsing
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
5660ce6945
render end of string is not optional
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
5ec3008b37
Add rigid parser to tablerow tag
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
5248025439
Remove redundant tests where rigid and strict modes have the same
...
behavior
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
0bc69b86b7
No longer test ParseContext directly on RigidModeUnitTest as
...
now the `safe: true` calls are considered safe
Test the entire template instead
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
b5fbad08c6
rigid set_attribute in for parsing
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
b8958f626d
Stricter 1:1 refactor of strict_parse for Variable
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
94bbf6ca32
Make it possible to safe_parse subsets of expressions
...
e.g. sometimes you want to only accept strings | lookups.
{% render snippetName %} for example. snippetName is a string right now.
We don't want safe_parse_expression because this would allow snippetName
to be a number, a boolean, etc. But we still want to strict parse this.
So what we'll do is use parse_expression(string, safe: true), this is
an optional opt-in to say "I know what I'm doing". Usually that's
because you're using the output of Parser#something as the input of
parse_expression.
It is true that Parser#expression is subset of Expression.parse, it is
not true of the opposite (e.g. Expression.parse doesn't care about .5
and happily parses that as a global lookup of the variable named "5",
Parser#expression throws for that.)
diff --git a/lib/liquid/condition.rb b/lib/liquid/condition.rb
index e5c321dc..9ab350f0 100644
--- a/lib/liquid/condition.rb
+++ b/lib/liquid/condition.rb
@@ -48,8 +48,8 @@ module Liquid
@@operators
end
- def self.parse_expression(parse_context, markup)
- @@method_literals[markup] || parse_context.parse_expression(markup)
+ def self.parse_expression(parse_context, markup, safe: false)
+ @@method_literals[markup] || parse_context.parse_expression(markup, safe: safe)
end
attr_reader :attachment, :child_condition
diff --git a/lib/liquid/parse_context.rb b/lib/liquid/parse_context.rb
index 1c59fe4a..82cf5768 100644
--- a/lib/liquid/parse_context.rb
+++ b/lib/liquid/parse_context.rb
@@ -51,13 +51,13 @@ module Liquid
end
def safe_parse_expression(parser)
- Expression.safe_parse(parser)
+ Expression.safe_parse(parser, @string_scanner, @expression_cache)
end
- def parse_expression(markup)
+ def parse_expression(markup, safe: false)
# todo(guilherme): remove this once rigid mode is fully using safe_parse_expression
- # raise Liquid::InternalError, "parse_expression is not supported in rigid mode" if @error_mode == :rigid
- puts("🚨 parse_expression used in rigid mode") if @error_mode == :rigid
+ # raise Liquid::InternalError, "parse_expression is not supported in rigid mode" if !safe && @error_mode == :rigid
+ puts("🚨 parse_expression used in rigid mode") if !safe && @error_mode == :rigid
Expression.parse(markup, @string_scanner, @expression_cache)
end
diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb
index 656d2e47..374ee511 100644
--- a/lib/liquid/tag.rb
+++ b/lib/liquid/tag.rb
@@ -72,8 +72,8 @@ module Liquid
parse_context.safe_parse_expression(parser)
end
- def parse_expression(markup)
- parse_context.parse_expression(markup)
+ def parse_expression(markup, safe: false)
+ parse_context.parse_expression(markup, safe: safe)
end
end
end
diff --git a/lib/liquid/tags/for.rb b/lib/liquid/tags/for.rb
index c2be5db1..3182983b 100644
--- a/lib/liquid/tags/for.rb
+++ b/lib/liquid/tags/for.rb
@@ -93,7 +93,7 @@ module Liquid
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
collection_name = p.expression
- @collection_name = parse_expression(collection_name)
+ @collection_name = parse_expression(collection_name, safe: true)
@name = "#{@variable_name}-#{collection_name}"
@reversed = p.id?('reversed')
diff --git a/lib/liquid/tags/if.rb b/lib/liquid/tags/if.rb
index 342374f1..e25d6250 100644
--- a/lib/liquid/tags/if.rb
+++ b/lib/liquid/tags/if.rb
@@ -81,8 +81,8 @@ module Liquid
block.attach(new_body)
end
- def parse_expression(markup)
- Condition.parse_expression(parse_context, markup)
+ def parse_expression(markup, safe: false)
+ Condition.parse_expression(parse_context, markup, safe: safe)
end
def lax_parse(markup)
@@ -124,9 +124,9 @@ module Liquid
end
def parse_comparison(p)
- a = parse_expression(p.expression)
+ a = parse_expression(p.expression, safe: true)
if (op = p.consume?(:comparison))
- b = parse_expression(p.expression)
+ b = parse_expression(p.expression, safe: true)
Condition.new(a, op, b)
else
Condition.new(a)
diff --git a/lib/liquid/tags/include.rb b/lib/liquid/tags/include.rb
index 6cdbfd6f..b72a235b 100644
--- a/lib/liquid/tags/include.rb
+++ b/lib/liquid/tags/include.rb
@@ -87,10 +87,11 @@ module Liquid
def rigid_parse(markup)
p = @parse_context.new_parser(markup)
- template_name = p.expression
+ @template_name_expr = safe_parse_expression(p)
with_or_for = p.id?("for") || p.id?("with") || nil
+ @variable_name_expr = nil
if with_or_for
- variable_name = p.expression
+ @variable_name_expr = parse_expression(p.consume(:id), safe: true)
end
alias_name = nil
@@ -98,8 +99,6 @@ module Liquid
alias_name = p.consume(:id)
end
- @template_name_expr = parse_expression(template_name)
- @variable_name_expr = variable_name ? parse_expression(variable_name) : nil
@alias_name = alias_name
# optional comma
@@ -109,7 +108,7 @@ module Liquid
while p.look(:id)
key = p.consume
p.consume(:colon)
- @attributes[key] = parse_expression(p.expression)
+ @attributes[key] = safe_parse_expression(p)
p.consume?(:comma) # optional comma
end
end
diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb
index 89c11063..4f716b24 100644
--- a/lib/liquid/tags/render.rb
+++ b/lib/liquid/tags/render.rb
@@ -88,10 +88,11 @@ module Liquid
def rigid_parse(markup)
p = @parse_context.new_parser(markup)
- template_name = rigid_template_name(p)
+ @template_name_expr = parse_expression(rigid_template_name(p), safe: true)
+ @variable_name_expr = nil
with_or_for = p.id?("for") || p.id?("with") || nil
if with_or_for
- variable_name = p.expression
+ @variable_name_expr = safe_parse_expression(p)
end
alias_name = nil
@@ -99,8 +100,6 @@ module Liquid
alias_name = p.consume(:id)
end
- @template_name_expr = parse_expression(template_name)
- @variable_name_expr = variable_name ? parse_expression(variable_name) : nil
@alias_name = alias_name
@is_for_loop = (with_or_for == FOR)
@@ -111,7 +110,7 @@ module Liquid
while p.look(:id)
key = p.consume
p.consume(:colon)
- @attributes[key] = parse_expression(p.expression)
+ @attributes[key] = safe_parse_expression(p)
p.consume?(:comma) # optional comma
end
end
diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb
index 20957065..a3623bc5 100644
--- a/lib/liquid/variable.rb
+++ b/lib/liquid/variable.rb
@@ -65,11 +65,11 @@ module Liquid
return if p.look(:end_of_string)
- @name = parse_context.parse_expression(p.expression)
+ @name = parse_context.safe_parse_expression(p)
while p.consume?(:pipe)
filtername = p.consume(:id)
filterargs = p.consume?(:colon) ? parse_filterargs(p) : Const::EMPTY_ARRAY
- @filters << parse_filter_expressions(filtername, filterargs)
+ @filters << parse_filter_expressions(filtername, filterargs, safe: true)
end
p.consume(:end_of_string)
end
@@ -122,15 +122,15 @@ module Liquid
private
- def parse_filter_expressions(filter_name, unparsed_args)
+ def parse_filter_expressions(filter_name, unparsed_args, safe: false)
filter_args = []
keyword_args = nil
unparsed_args.each do |a|
- if (matches = a.match(JustTagAttributes))
+ if (matches = a.match(JustTagAttributes)) # we'll need to fix this
keyword_args ||= {}
- keyword_args[matches[1]] = parse_context.parse_expression(matches[2])
+ keyword_args[matches[1]] = parse_context.parse_expression(matches[2], safe: false)
else
- filter_args << parse_context.parse_expression(a)
+ filter_args << parse_context.parse_expression(a, safe: safe)
end
end
result = [filter_name, filter_args]
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
4f7dafbcac
Use safe_parse_expression instead of parse_expression
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
d77662cd0e
Remove unnecessary skips
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
65a1c167b3
Add rigid_parse to case/when
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
e2a15334f0
Fail with trailing elements in the cycle tag
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
327790cdce
Fix an int the cycle tag, add extra unit tests, and updated parser switcher:
...
- Fixed NoMethod error with .peek (using look instead)
- Add friendlier error message when {% cycle %}
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
75c95d0791
Fix cycle tag
...
- `respond_to?` was returning `false` in the parser switcher
because `rigid_parse` was private
It was working before because `parse_context` was doing
the double-parsing thing, but when we removed that, this
test fairly started breaking
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
e413104e78
Remove ExpressionParser in favor of ParseContext#safe_parse
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
d56e3c50f9
Use ExpressionParser in the ParseContext when parsing in :rigid mode
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
1bff382ebc
Add ExpressionParser and ExpressionConsumer
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
6a9e46dd19
Add rigid_parse method to include
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
e58ac0e75b
Add rigid_parse to render
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
c78bf20010
Add a rigid_parse method to cycle
2025-10-27 16:33:31 +01:00
Charles-P. Clermont and Guilherme Carreiro
6d585a24f1
Add rigid_parse_with_error_context and clarifications
2025-10-27 16:33:31 +01:00
Guilherme Carreiro and Guilherme Carreiro
edf06c2882
Introduce :rigid parsing mode
2025-10-27 16:33:31 +01:00
Michael Go and GitHub
1c1e711906
Merge pull request #1995 from Shopify/avoid-regex-allocation
...
avoid new regex allocation in util functions
2025-10-15 10:43:52 -03:00
Michael Go
b5b36665e6
avoid new regex allocation in util functions
2025-10-15 10:08:13 -03:00
Alok Swamy and GitHub
9942592ea8
Merge pull request #1984 from Shopify/update-loop-named-params
...
Update liquid docs for loop's named params
2025-09-08 06:24:06 -04:00
Alok Swamy
786381c762
Update liquid docs for loop's named params
2025-09-05 16:43:48 -04:00
Gray Gilmore and GitHub
8ad91b5e36
Merge pull request #1952 from Shopify/jus/template.rb-typo-docs
...
template.rb: Correct typo in docs
2025-09-03 09:03:54 -07:00
iain and GitHub
9bb7fbf123
Merge pull request #1968 from Shopify/shopify-dev-docs-formatting
...
Inline some information that previously lived at category level
2025-07-03 10:59:58 -04:00
Iain Campbell
8555fd8a20
inline warning previuosly at category level
2025-06-27 16:57:00 -04:00
James Meng and GitHub
9bd408f5d0
Merge pull request #1965 from Shopify/jm/bump_liquid
...
Bump Liquid to 5.8.7
v5.8.7
2025-06-09 12:42:27 -07:00
James Meng
79b831d96c
Bump Liquid to 5.8.7
2025-06-09 12:38:04 -07:00
James Meng and GitHub
aebd75e5e8
Merge pull request #1954 from Shopify/jm/doc_body
...
Expose tag body in the Doc tag
2025-06-09 12:25:19 -07:00
James Meng
7f2f8a226b
Add tests for new public methods
2025-06-09 12:24:59 -07:00
James Meng
7b2b25fda1
Fix Doc tag blank? method to check body content
...
Previously the blank? method always returned true. Now it properly checks
if the body is empty, making the tag behavior consistent with other tags.
Also updated test to use whitespace control for cleaner assertions.
2025-06-09 11:36:18 -07:00
James Meng
8548b96a97
Remove body attr_reader and initiliaze @body instance variable in parse method
2025-06-06 13:08:06 -07:00
upgrade-umpire[bot] and GitHub
fc96e66e14
Merge pull request #1953 from Shopify/actions-commit
...
Applying Merge
2025-06-05 20:15:14 +00:00
James Meng
79a771d724
Add test for doc tag capturing token before enddoc
2025-06-04 19:30:47 -07:00
James Meng
65b1dedac5
Expose tag body in the Doc tag
2025-06-04 12:04:41 -07:00
Ian Ker-Seymer and GitHub
f375d7b3aa
Add unit test for custom Liquid tag registration ( #1960 )
...
Adds EnvironmentTest to verify custom tag registration and rendering.
2025-05-22 11:38:10 -04:00
James Meng and GitHub
c6f05eaf83
Merge pull request #1955 from Shopify/jm/trim_doc_tag_desc
...
Remove {{ foo }} and {{ bar }} from `doc` tag description
2025-05-06 15:29:29 -07:00