refactor: Reuse FilteredValueToken and fix architectural layering#878
Draft
rosomri wants to merge 20 commits into
Draft
refactor: Reuse FilteredValueToken and fix architectural layering#878rosomri wants to merge 20 commits into
rosomri wants to merge 20 commits into
Conversation
Made-with: Cursor
…l-and-Loop-Tags' of https://github.com/skynetigor/liquidjs into 833_Support-Value-Expressions-as-Operands-in-Conditional-and-Loop-Tags Made-with: Cursor # Conflicts: # src/parser/tokenizer.ts
…in-Conditional-and-Loop-Tags
…l-and-Loop-Tags' of https://github.com/skynetigor/liquidjs into 833_Support-Value-Expressions-as-Operands-in-Conditional-and-Loop-Tags
…cenarios for enabled and disabled grouped expressions in case, for, if, unless tags, ensuring proper handling of expressions and error throwing for invalid syntax.
The readGroupedExpression() test suite was duplicated twice in the spec file. Removed the duplicate block to avoid redundant test execution.
Extract inline grouped expression variable extraction logic into a dedicated function for consistency with other extractors (extractFilteredValueVariables, extractPropertyAccessVariable). This addresses PR harttle#863 comment 7 - improves code organization and maintainability.
collection: ValueToken | GroupedExpressionToken Addresses PR harttle#863 comment 5.
…lters Addresses PR review comments 4, 6, 8, 9 - moves grouped expression evaluation from parse-time resolution to render-time lazy evaluation following the generator-based async/sync duality pattern used throughout liquidjs. Key changes: - Replace resolvedValue (Value instance) with resolvedFilters (Filter[]) - Rename resolveGroupedExpressions() to resolveGroupedExpressionFilters() - Move evaluation logic to evalGroupedExpressionToken() at render time - Build Filter instances at parse time (carry liquid reference for render) - Evaluate expression and apply filters lazily via generators - Add support for tablerow tag with grouped expressions - Remove duplicate getFilter() method in Value class Maintains proper layering (tokens → render → templates) and consistency with Value.value() pattern. Filter resolution still happens at parse time since it requires liquid.filters access, but actual evaluation is deferred to render time. Tags that store raw ValueToken (for, case when-values, tablerow) still need explicit resolveGroupedExpressionFilters() calls. Tags that wrap with new Value() get automatic recursive resolution via Value constructor.
Replace GroupedExpressionToken with existing FilteredValueToken to avoid code duplication and fix layering violation where tokens depended on templates (Filter instances). Key changes: - Reuse FilteredValueToken instead of GroupedExpressionToken - Simplify readGroupOrRange() to return FilteredValueToken | RangeToken - Add liquid reference to Context for runtime filter resolution - Build Filter instances at render time in evalFilteredValueToken() - Remove resolveGroupedExpressionFilters() and parse-time resolution - Remove explicit resolution calls from tag constructors This maintains proper architectural layering (tokens → render → templates) with no backward dependencies, as requested in PR review feedback. All 1537 tests pass.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR addresses the architectural concerns raised in review comments on #863 by:
FilteredValueTokeninstead of creating a duplicateGroupedExpressionTokenFilterinstances)This is a proposed architectural improvement built on top of #863 for discussion.
Architectural Changes
1. Reuse
FilteredValueTokeninstead ofGroupedExpressionTokenBefore:
After:
This eliminates code duplication and leverages the existing
FilteredValueTokenclass that was already designed for this purpose, as suggested in review comments.2. Fix Layering Violation
Problem: Tokens had
resolvedFilters?: Filter[], causing tokens to depend on the template layer (violation of tokens → render → templates hierarchy).Solution:
resolvedFiltersfromFilteredValueTokenliquidreference toContextfor runtime filter resolutionFilterinstances at render time inevalFilteredValueToken()Before:
After:
3. Lazy Filter Resolution (Render Time)
Before: Filters were resolved at parse time via
resolveGroupedExpressionFilters():After: Filters are resolved lazily at render time:
Changes by File
Core Architecture
src/context/context.ts: Addedliquid?: anyreference to Context for runtime filter resolutionsrc/liquid.ts: Passliquid: thiswhen creating Context instancessrc/tokens/filtered-value-token.ts: RemovedresolvedFilters(layering fix)src/render/expression.ts: Build Filter instances at render time inevalFilteredValueToken()Simplification
src/parser/tokenizer.ts: SimplifiedreadGroupOrRange()to returnFilteredValueToken | RangeTokensrc/template/value.ts: RemovedresolveGroupedExpressionFilters()function entirelysrc/tags/case.ts,for.ts,tablerow.ts: Removed explicitresolveGroupedExpressionFilters()callsCleanup
src/tokens/grouped-expression-token.ts: Deleted (replaced by FilteredValueToken)src/parser/token-kind.ts: MarkedGroupedExpressionas deprecatedsrc/util/type-guards.ts: UpdatedisGroupedExpressionToken()as deprecated aliasTest Results
✅ All 1537 tests pass
Manual Testing
All grouped expression use cases work correctly:
(foo | upcase) == "BAR"(a | upcase) == (b | upcase)(1..(items | size))(status | downcase)(1..3)(backward compatible)(name | downcase | size)((foo | append: "!") | upcase)Benefits
GroupedExpressionTokenclassresolveGroupedExpressionFilters()functionQuestion for Review
This implementation follows @jg-rp's suggestion to add
liquidtoContext. The alternative mentioned in the PR comments was to passLiquidto theTokenizerconstructor, but that would require changes to filter runtime tokenization support.Does the approach of adding
liquidtoContextalign with your architectural vision? Or would you prefer theTokenizerconstructor approach?Builds on #863