797 Commits

Author SHA1 Message Date
Tobi Lutke
ef13b2dfd5
Fix empty? semantics and string first/last for empty strings
- nil is NOT empty (but IS blank) - matches Shopify production
- String first/last returns '' for empty strings, not nil - matches ActiveSupport
- Add test for nil not being empty
2026-01-01 22:06:22 -05:00
Tobi Lutke
af58800c16
Update rubocop-shopify to 2.18.0 and fix new offenses 2026-01-01 20:22:19 -05:00
Tobi Lutke
361d1d52b1
Fix rubocop offenses from 1.82 upgrade 2026-01-01 20:20:50 -05:00
Tobi Lutke
33bac87a5c
Address liquid-spec issues without ActiveSupport loaded
Implement ActiveSupport-compatible behaviors internally so Liquid works
correctly without ActiveSupport being loaded:

1. String first/last via property access (name.first, name.last)
   - VariableLookup now handles string[0] and string[-1] for first/last

2. String first/last via filters (name | first, name | last)
   - StandardFilters#first and #last now handle strings

3. blank?/empty? comparisons for types without these methods
   - Condition now implements liquid_blank? and liquid_empty? internally
   - blank? matches ActiveSupport: nil, false, empty/whitespace strings,
     empty arrays/hashes are all blank
   - empty? checks length == 0 only (whitespace is NOT empty)

This fixes spec failures for templates like:
- {{ name.first }} / {{ name | first }} on strings
- {% if x == blank %} for whitespace strings, empty hashes/arrays
- {% case ' ' %}{% when blank %} matching whitespace
2026-01-01 20:14:21 -05:00
Charles-P. Clermont
cbeff64708 Make new tests serializable to liquid-spec
(No anonymous classes)
2025-12-18 13:56:02 -05:00
Julia Boutin
fa27bfe6e0
Preserve literal semantics in strict2 case/when
Previously, strict2 case/when used `safe_parse_expression`
to parse when expressions causing `blank`/`empty` to be
treated as string literals (Expression::LITERALS maps 'empty' => ''),
rather than method literals

This caused unexpected behavior:

```
{%- case empty_obj -%}
{%- when empty -%}
  previously: doesn't render (empty_obj == '' is false)
  now: renders (empty_obj.empty? is true)
{%- endcase -%}
```

This commit instead calls `Condition.parse_expression`
with `safe: true`, which will correctly handle `blank`
and `empty`
2025-11-28 15:51:48 -07:00
Guilherme Carreiro
32b50ecafe
Bump Liquid to 5.11.0 (#2012)
This commit reverts the Inline Snippets tag (#2001) and bumps
Liquid to 5.11. For now, the inclusion of Inline Snippets
in the latest Liquid release is being treated as a bug.

While #2001 does implement the scope contained in RFC#1916,
we need to take a step back to make sure we’re setting our
sights high enough with this feature, and that we’re truly
supporting theme developers in the ways they need.

If you have any feedback, please leave a comment on RFC#1916.

- Liquid Developer Tools
2025-11-19 18:03:23 +01:00
Gray Gilmore
9973f3399e
Don't raise if no variable found when using context.key?
Previously if you set `strict_variables` to `true` on the context using
`key?('key_name')` would raise a `Liquid::UndefinedVariable` error.
Raising this error makes sense if you're trying to access the variable
directly with something like  `context['key_name']` but by using `key?`
you're safely checking if it exists first.

You should be able to enable `strict_variables` and use `key?` in
combination with each other to ensure code safety.
2025-10-30 15:33:07 -07:00
Guilherme Carreiro
a16ec56a40 Update error handling for keeping backward-compatibility on error messages in the render tag 2025-10-30 12:00:44 +01:00
Julia Boutin
12fd93fbe2 Missing inline snippets should display same error as filebased 2025-10-30 12:00:44 +01:00
Julia Boutin
5ceb0e9cec Raise error on invalid snippet name 2025-10-30 12:00:44 +01:00
Julia Boutin
98fbd985d8 Remove unneeded read method 2025-10-30 12:00:44 +01:00
Julia Boutin
db350c54ff Allow render tag to recognize drops that respond to to_partial 2025-10-30 12:00:44 +01:00
Julia Boutin
0cc6cdd553 Remove ... syntax references 2025-10-30 12:00:44 +01:00
Julia Boutin
40e45e32ac Raise syntax error on incorrect render identifier type 2025-10-30 12:00:44 +01:00
Julia Boutin
d4d2237b90 Support prop spreading 2025-10-30 12:00:44 +01:00
Julia Boutin
0ceeefba02 Implement resource limits and remove leftover string references 2025-10-30 12:00:44 +01:00
Julia Boutin
65fb80a347 Render arguments should maintain correct precedence 2025-10-30 12:00:44 +01:00
Julia Boutin
99116638fd Support with, for, and as inline snippet syntax
This commit updates the render method to share parts
of the snippet and block rendering logic to enable
inline snippets to support `with`, `for`, and `as`
syntax
2025-10-30 12:00:44 +01:00
Julia Boutin
9bcfd32e65 Support ... inline snippet syntax 2025-10-30 12:00:44 +01:00
Julia Boutin
c7ad1c90ca Change inline snippet identifier from string to variable
Currently, snippet files identified by strings. This
PR makes changes to render to allow for new inline
snippets to use variables as identifiers instead
2025-10-30 12:00:44 +01:00
Julia Boutin
12bbbc4537 Create SnippetDrop and set in scope 2025-10-30 12:00:44 +01:00
Julia Boutin
1eca707c4a Update inline snippets syntax
Previously, inline snippets syntax looked a bit
different, they:

- used strings as tag identifiers
- defined tag arguments {% snippet "input" |type| %}

This PR updates snippets to better reflect
the currently proposed syntax

Co-authored-by: Orlando Qiu <orlando.qiu@shopify.com>
2025-10-30 12:00:44 +01:00
Josh Faigan
ed9c4e31c4 Introduce new inline snippets tag
Inline snippets will reduce code duplication and
improve the developer experience, eliminating the
need for one-off snippet files
2025-10-30 12:00:44 +01:00
Guilherme Carreiro
1f58216f48 Add unit test mixing positional and kwargs arguments 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
d38795168b Update test/integration/tags/table_row_test.rb
Co-authored-by: Gray Gilmore <graygilmore@gmail.com>
2025-10-27 16:33:31 +01:00
Guilherme Carreiro
6aa4041c0f Rename with_error_mode(...) to with_error_modes(...) 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
d63dd104de Update test/integration/tags/render_tag_test.rb
Co-authored-by: Alok Swamy <alok.swamy@shopify.com>
2025-10-27 16:33:31 +01:00
Guilherme Carreiro
1733586242 Update test/unit/tags/case_tag_unit_test.rb
Co-authored-by: Alok Swamy <alok.swamy@shopify.com>
2025-10-27 16:33:31 +01:00
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
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
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
Charles-P. Clermont
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
1be1e36a8d Fixup cycle rigid parsing to be backwards compatible 2025-10-27 16:33:31 +01:00
Charles-P. Clermont
5660ce6945 render end of string is not optional 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
5ec3008b37 Add rigid parser to tablerow tag 2025-10-27 16:33:31 +01:00
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
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
b8958f626d Stricter 1:1 refactor of strict_parse for Variable 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
d77662cd0e Remove unnecessary skips 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
65a1c167b3 Add rigid_parse to case/when 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
e2a15334f0 Fail with trailing elements in the cycle tag 2025-10-27 16:33:31 +01:00
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
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
e413104e78 Remove ExpressionParser in favor of ParseContext#safe_parse 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
d56e3c50f9 Use ExpressionParser in the ParseContext when parsing in :rigid mode 2025-10-27 16:33:31 +01:00
Guilherme Carreiro
1bff382ebc Add ExpressionParser and ExpressionConsumer 2025-10-27 16:33:31 +01:00
Charles-P. Clermont
6a9e46dd19 Add rigid_parse method to include 2025-10-27 16:33:31 +01:00
Charles-P. Clermont
e58ac0e75b Add rigid_parse to render 2025-10-27 16:33:31 +01:00
Charles-P. Clermont
c78bf20010 Add a rigid_parse method to cycle 2025-10-27 16:33:31 +01:00