mirror of
https://github.com/Shopify/liquid.git
synced 2026-10-02 00:25:12 -07:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4a6987a30 | ||
|
|
eab457f8ca | ||
|
|
99b0b38806 | ||
|
|
d109bc9242 | ||
|
|
47288ccca0 | ||
|
|
de23ed82d5 | ||
|
|
1069b944a0 | ||
|
|
c6c342ab32 | ||
|
|
3556c3371e | ||
|
|
96a44927b5 | ||
|
|
fb6cf17099 | ||
|
|
9409dd8f4a | ||
|
|
b5ecd4d0f8 | ||
|
|
ba5aa0abf6 | ||
|
|
a384e229d8 | ||
|
|
db474d5b57 | ||
|
|
3a13ac7e6c | ||
|
|
4d13f030f8 | ||
|
|
8c6149782d |
@@ -67,6 +67,7 @@ require 'liquid/i18n'
|
|||||||
require 'liquid/drop'
|
require 'liquid/drop'
|
||||||
require 'liquid/tablerowloop_drop'
|
require 'liquid/tablerowloop_drop'
|
||||||
require 'liquid/forloop_drop'
|
require 'liquid/forloop_drop'
|
||||||
|
require 'liquid/snippet_drop'
|
||||||
require 'liquid/extensions'
|
require 'liquid/extensions'
|
||||||
require 'liquid/errors'
|
require 'liquid/errors'
|
||||||
require 'liquid/interrupts'
|
require 'liquid/interrupts'
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
block_tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: {% %{tag} %}{% end%{tag} %}"
|
block_tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: {% %{tag} %}{% end%{tag} %}"
|
||||||
assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]"
|
assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]"
|
||||||
capture: "Syntax Error in 'capture' - Valid syntax: capture [var]"
|
capture: "Syntax Error in 'capture' - Valid syntax: capture [var]"
|
||||||
|
snippet: "Syntax Error in 'snippet' - Valid syntax: snippet [var]"
|
||||||
case: "Syntax Error in 'case' - Valid syntax: case [condition]"
|
case: "Syntax Error in 'case' - Valid syntax: case [condition]"
|
||||||
case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}"
|
case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}"
|
||||||
case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) "
|
case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) "
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
||||||
invalid_template_encoding: "Invalid template encoding"
|
invalid_template_encoding: "Invalid template encoding"
|
||||||
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
||||||
|
render_invalid_template_name: "Syntax error in tag 'render' - Expected a string or identifier, found %{found}"
|
||||||
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
||||||
table_row_invalid_attribute: "Invalid attribute '%{attribute}' in tablerow loop. Valid attributes are cols, limit, offset, and range"
|
table_row_invalid_attribute: "Invalid attribute '%{attribute}' in tablerow loop. Valid attributes are cols, limit, offset, and range"
|
||||||
tag_never_closed: "'%{block_name}' tag was never closed"
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
||||||
@@ -29,5 +31,6 @@
|
|||||||
variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
|
variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
|
||||||
argument:
|
argument:
|
||||||
include: "Argument error in tag 'include' - Illegal template name"
|
include: "Argument error in tag 'include' - Illegal template name"
|
||||||
|
render: "Argument error in tag 'render' - Dynamically chosen templates are not allowed"
|
||||||
disabled:
|
disabled:
|
||||||
tag: "usage is not allowed in this context"
|
tag: "usage is not allowed in this context"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Liquid
|
||||||
|
class SnippetDrop < Drop
|
||||||
|
attr_reader :body, :name, :filename
|
||||||
|
|
||||||
|
def initialize(body, name, filename)
|
||||||
|
super()
|
||||||
|
@body = body
|
||||||
|
@name = name
|
||||||
|
@filename = filename
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_partial
|
||||||
|
@body
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
'SnippetDrop'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -20,6 +20,7 @@ require_relative "tags/raw"
|
|||||||
require_relative "tags/render"
|
require_relative "tags/render"
|
||||||
require_relative "tags/cycle"
|
require_relative "tags/cycle"
|
||||||
require_relative "tags/doc"
|
require_relative "tags/doc"
|
||||||
|
require_relative "tags/snippet"
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
module Tags
|
module Tags
|
||||||
@@ -44,6 +45,7 @@ module Liquid
|
|||||||
'echo' => Echo,
|
'echo' => Echo,
|
||||||
'tablerow' => TableRow,
|
'tablerow' => TableRow,
|
||||||
'doc' => Doc,
|
'doc' => Doc,
|
||||||
|
'snippet' => Snippet,
|
||||||
}.freeze
|
}.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+20
-14
@@ -27,7 +27,7 @@ module Liquid
|
|||||||
# @liquid_syntax_keyword filename The name of the snippet to render, without the `.liquid` extension.
|
# @liquid_syntax_keyword filename The name of the snippet to render, without the `.liquid` extension.
|
||||||
class Render < Tag
|
class Render < Tag
|
||||||
FOR = 'for'
|
FOR = 'for'
|
||||||
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
SYNTAX = /(#{QuotedString}+|#{VariableSegment}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||||
|
|
||||||
disable_tags "include"
|
disable_tags "include"
|
||||||
|
|
||||||
@@ -47,21 +47,23 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_tag(context, output)
|
def render_tag(context, output)
|
||||||
# The expression should be a String literal, which parses to a String object
|
template = context.evaluate(@template_name_expr)
|
||||||
template_name = @template_name_expr
|
|
||||||
raise ::ArgumentError unless template_name.is_a?(String)
|
|
||||||
|
|
||||||
partial = PartialCache.load(
|
if template.respond_to?(:to_partial)
|
||||||
template_name,
|
partial = template.to_partial
|
||||||
context: context,
|
template_name = template.filename
|
||||||
parse_context: parse_context,
|
context_variable_name = @alias_name || template.name
|
||||||
)
|
elsif @template_name_expr.is_a?(String)
|
||||||
|
partial = PartialCache.load(template, context: context, parse_context: parse_context)
|
||||||
context_variable_name = @alias_name || template_name.split('/').last
|
template_name = partial.name
|
||||||
|
context_variable_name = @alias_name || template_name.split('/').last
|
||||||
|
else
|
||||||
|
raise ::ArgumentError
|
||||||
|
end
|
||||||
|
|
||||||
render_partial_func = ->(var, forloop) {
|
render_partial_func = ->(var, forloop) {
|
||||||
inner_context = context.new_isolated_subcontext
|
inner_context = context.new_isolated_subcontext
|
||||||
inner_context.template_name = partial.name
|
inner_context.template_name = template_name
|
||||||
inner_context.partial = true
|
inner_context.partial = true
|
||||||
inner_context['forloop'] = forloop if forloop
|
inner_context['forloop'] = forloop if forloop
|
||||||
|
|
||||||
@@ -101,14 +103,18 @@ module Liquid
|
|||||||
key = p.consume
|
key = p.consume
|
||||||
p.consume(:colon)
|
p.consume(:colon)
|
||||||
@attributes[key] = safe_parse_expression(p)
|
@attributes[key] = safe_parse_expression(p)
|
||||||
p.consume?(:comma)
|
p.consume?(:comma) # optional comma
|
||||||
end
|
end
|
||||||
|
|
||||||
p.consume(:end_of_string)
|
p.consume(:end_of_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
def rigid_template_name(p)
|
def rigid_template_name(p)
|
||||||
p.consume(:string)
|
return p.consume(:string) if p.look(:string)
|
||||||
|
return p.consume(:id) if p.look(:id)
|
||||||
|
|
||||||
|
found = p.consume || "nothing"
|
||||||
|
raise SyntaxError, options[:locale].t("errors.syntax.render_invalid_template_name", found: found)
|
||||||
end
|
end
|
||||||
|
|
||||||
def strict_parse(markup)
|
def strict_parse(markup)
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category variable
|
||||||
|
# @liquid_name snippet
|
||||||
|
# @liquid_summary
|
||||||
|
# Creates a new inline snippet.
|
||||||
|
# @liquid_description
|
||||||
|
# You can create inline snippets to make your Liquid code more modular.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% snippet snippet_name %}
|
||||||
|
# value
|
||||||
|
# {% endsnippet %}
|
||||||
|
class Snippet < Block
|
||||||
|
def initialize(tag_name, markup, options)
|
||||||
|
super
|
||||||
|
p = @parse_context.new_parser(markup)
|
||||||
|
if p.look(:id)
|
||||||
|
@to = p.consume(:id)
|
||||||
|
p.consume(:end_of_string)
|
||||||
|
else
|
||||||
|
raise SyntaxError, options[:locale].t("errors.syntax.snippet")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_to_output_buffer(context, output)
|
||||||
|
snippet_drop = SnippetDrop.new(@body, @to, context.template_name)
|
||||||
|
context.scopes.last[@to] = snippet_drop
|
||||||
|
context.resource_limits.increment_assign_score(assign_score_of(snippet_drop))
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def blank?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def assign_score_of(snippet_drop)
|
||||||
|
snippet_drop.body.nodelist.sum { |node| node.to_s.bytesize }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -101,10 +101,6 @@ class RenderTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_dynamically_choosen_templates_are_not_allowed
|
|
||||||
assert_syntax_error("{% assign name = 'snippet' %}{% render name %}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_rigid_parsing_errors
|
def test_rigid_parsing_errors
|
||||||
with_error_modes(:lax, :strict) do
|
with_error_modes(:lax, :strict) do
|
||||||
assert_template_result(
|
assert_template_result(
|
||||||
@@ -294,6 +290,13 @@ class RenderTagTest < Minitest::Test
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_tag_with_snippet_drop
|
||||||
|
assert_template_result(
|
||||||
|
"Hello from snippet",
|
||||||
|
"{% snippet my_snippet %}Hello from snippet{% endsnippet %}{% render my_snippet %}",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def test_render_tag_renders_error_with_template_name
|
def test_render_tag_renders_error_with_template_name
|
||||||
assert_template_result(
|
assert_template_result(
|
||||||
'Liquid error (foo line 1): standard error',
|
'Liquid error (foo line 1): standard error',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user