Commit Graph
1103 Commits
Author SHA1 Message Date
Chris Pak d3e39525f3 BlockBody: byte-walk tag tokens instead of FullToken regex
Add try_parse_tag_token that parses {%...%} tag tokens using
getbyte/byteslice + ByteTables lookup arrays instead of the
FullToken regex with 4 capture groups. Allocates only the 2
strings needed (tag_name, markup) vs 4+ from regex captures.

Uses ByteTables::WORD (no hyphen) for tag name scanning,
matching TagName = /#|\w+/ exactly. Falls back to FullToken
regex when the fast path returns nil.
2026-04-05 20:53:53 -07:00
Chris Pak fd853a3593 VariableLookup: fast path for simple identifier chains
Skip the expensive recursive VariableParser regex for simple
lookups like 'product.title' (~90% of real-world cases).

SIMPLE_LOOKUP_RE validates the input is a plain a.b.c chain
(no brackets, no quotes). On match, byte-walks on dots to split
segments instead of invoking the regex engine. Falls through to
the original VariableParser scan for complex inputs.
2026-04-05 20:53:53 -07:00
Chris Pak aaabfc5017 Expression: byte-walk parse_number and guard strip allocation
parse_number: replace INTEGER_REGEX/FLOAT_REGEX matching and
StringScanner loop with a single byte-walking pass using
ByteTables::DIGIT. Avoids MatchData allocation and StringScanner
reset on every call.

Expression.parse: only call String#strip when leading/trailing
whitespace is actually present (checked via ByteTables::WHITESPACE).
Avoids allocating a new String on ~4,464 calls per compile.
2026-04-05 20:53:53 -07:00
Chris Pak 7cd8df6fa8 Add ByteTables module and bench_quick.rb benchmark harness
ByteTables provides pre-computed 256-entry boolean lookup arrays for
byte classification (IDENT_START, IDENT_CONT, WORD, DIGIT, WHITESPACE)
and named constants for delimiter bytes (NEWLINE, DASH, DOT, HASH).

bench_quick.rb measures parse µs, render µs, and object allocations
for the theme benchmark suite.
2026-04-05 20:53:53 -07:00
Benoit Daloze d824de701c Adapt slice filter to work on non-64 bit platforms 2026-03-25 16:46:47 +01:00
Ian Ker-SeymerandGitHub 59d8d0d22d Add cumulative resource score tracking across partial renders (#2058)
* feat: add cumulative resource score tracking across partial renders

Add cumulative_render_score and cumulative_assign_score counters to
ResourceLimits that accumulate across reset() calls, with optional
cumulative_render_score_limit and cumulative_assign_score_limit to
cap total work across all partial renders.

Also add a reached? check in BlockBody's render loop so that once a
cumulative limit triggers, the parent template stops processing
further nodes.

Bump version to 5.12.0.

* refactor: move cumulative limit enforcement into reset()

Instead of checking reached? in BlockBody's render loop, enforce
cumulative limits in reset() itself. Since reset() is called before
the begin/rescue MemoryError block in Template#render, the raise
propagates to the parent naturally — no changes to BlockBody needed.
2026-03-18 11:54:46 -04:00
Michaël Gallego d589c51697 Add squish filter 2026-02-19 10:03:52 +09:00
Tobi LutkeandClaude Opus 4.5 ccd05e869c Make blank/empty comparisons invariant to ActiveSupport
- Implement liquid_blank? and liquid_empty? methods in Condition
  to emulate ActiveSupport's behavior when it's not loaded
- This ensures templates like `{% if x == blank %}` work identically
  whether ActiveSupport is loaded or not
- Update liquid-spec adapters for new API (ctx parameter)
- Add rake spec task for running liquid-spec matrix

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
2026-01-05 14:54:43 -10:00
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 0e3548d39e Remove redundant else-clause 2026-01-01 20:16:24 -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
Ian Ker-SeymerandGitHub 22e979a6fa Use floating-point format for BigDecimal stringification (#2022)
## Summary

- BigDecimal values now stringify using `to_s("F")` format instead of the default `to_s`

## Why

Ruby's default `BigDecimal#to_s` produces engineering/scientific notation for certain values:

```ruby
BigDecimal("0.00001").to_s      # => "0.1E-4"
BigDecimal("12345678.9").to_s   # => "0.123456789E8"
```

This is rarely the desired output in templates. Using `to_s("F")` produces the expected floating-point format:

```ruby
BigDecimal("0.00001").to_s("F")      # => "0.00001"
BigDecimal("12345678.9").to_s("F")   # => "12345678.9"
```
2025-12-05 15:54:52 -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 CarreiroandGitHub 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 CarreiroandGuilherme Carreiro 248f3a412f Bump to 5.10.0 2025-10-30 12:28:52 +01:00
Guilherme CarreiroandGuilherme 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 BoutinandGuilherme Carreiro 12fd93fbe2 Missing inline snippets should display same error as filebased 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 5ceb0e9cec Raise error on invalid snippet name 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 98fbd985d8 Remove unneeded read method 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro ae05ba071c Add liquid_public_docs yard tag to snippet tag 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 4205131148 Extract snippet resource scoring logic into assign_score_of 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro db350c54ff Allow render tag to recognize drops that respond to to_partial 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 0cc6cdd553 Remove ... syntax references 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 40e45e32ac Raise syntax error on incorrect render identifier type 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro d4d2237b90 Support prop spreading 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 0ceeefba02 Implement resource limits and remove leftover string references 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 65fb80a347 Render arguments should maintain correct precedence 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 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 BoutinandGuilherme Carreiro 9bcfd32e65 Support ... inline snippet syntax 2025-10-30 12:00:44 +01:00
Julia BoutinandGuilherme Carreiro 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 BoutinandGuilherme Carreiro 12bbbc4537 Create SnippetDrop and set in scope 2025-10-30 12:00:44 +01:00
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 <[email protected]>
2025-10-30 12:00:44 +01:00
Josh FaiganandGuilherme Carreiro 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 CarreiroandGuilherme Carreiro c357f91e0c Bump to 5.9.0 2025-10-27 17:25:36 +01:00
Guilherme CarreiroandGuilherme Carreiro 44f0429c08 Extract /\w+:0x\h{8}/ regex to UNNAMED_CYCLE_PATTERN constant 2025-10-27 16:33:31 +01:00
Guilherme CarreiroandGuilherme Carreiro e57b7efe4e Simplify render/include tags following PR review feedback 2025-10-27 16:33:31 +01:00
Guilherme CarreiroandGuilherme 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 CarreiroandGuilherme 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 CarreiroandGuilherme 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. ClermontandGuilherme 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. ClermontandGuilherme Carreiro 1be1e36a8d Fixup cycle rigid parsing to be backwards compatible 2025-10-27 16:33:31 +01:00
Charles-P. ClermontandGuilherme Carreiro 902ff978a6 Fixup include parsing of with expression 2025-10-27 16:33:31 +01:00
Charles-P. ClermontandGuilherme Carreiro 2ba81b3f1a Fix alias parsing 2025-10-27 16:33:31 +01:00
Charles-P. ClermontandGuilherme Carreiro 5660ce6945 render end of string is not optional 2025-10-27 16:33:31 +01:00
Guilherme CarreiroandGuilherme Carreiro 5ec3008b37 Add rigid parser to tablerow tag 2025-10-27 16:33:31 +01:00
Guilherme CarreiroandGuilherme 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. ClermontandGuilherme Carreiro b5fbad08c6 rigid set_attribute in for parsing 2025-10-27 16:33:31 +01:00