You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parser: Treat javascript_tag content as foreign content (#1434)
This pull request fixes how the parser handles `javascript_tag do`
blocks when the body contains HTML-like syntax such as `<` operators in
JavaScript expressions (e.g. `n <o.length, for (let i = 0;
i<items.length; i++)`).
Previously, the outer HTML parser would interpret `<` inside a
`javascript_tag do` body as an HTML tag opening, creating a broken
`HTMLOpenTagNode` that swallowed the `<% end %>` closing tag. This
caused a null dereference in the analyzer when it tried to access
`block_node->end_node`.
The fix has two parts:
1. `html` parser option and foreign content re-parsing
A new `html` parser option (defaults to `true`) controls whether the
parser runs in HTML mode or foreign content mode. When `html: false`,
the parser treats everything as literal text and ERB tags, no HTML tag
parsing. The analyzer now re-parses `javascript_tag do` body content
with `html: false` to produce clean `LiteralNode`s, regardless of what
the outer HTML parse produced.
New `start_line` and `start_column` options allow the re-parsed body to
carry correct source locations from the original template.
2. Swallowed `<% end %>` recovery
When the outer HTML parse swallows `<% end %>` inside a broken
`HTMLOpenTagNode`, the analyzer walks the AST to find the swallowed
`ERBContentNode` with `end` content. It creates a proper `ERBEndNode`
from it and uses it as the `close_tag`, with stale
`ERBControlFlowScopeError` cleared.
For example, the following template with `action_view_helpers: true`:
```erb
<%= javascript_tag do %>
n <o.length
<% end %>
```
Now correctly produces:
```js
@ DocumentNode (location: (1:0)-(3:9))
└── children: (1 item)
└── @ HTMLElementNode (location: (1:0)-(3:9))
├── open_tag:
│ └── @ ERBOpenTagNode (location: (1:0)-(1:24))
│ ├── tag_opening: "<%=" (location: (1:0)-(1:3))
│ ├── content: " javascript_tag do " (location: (1:3)-(1:22))
│ ├── tag_closing: "%>" (location: (1:22)-(1:24))
│ ├── tag_name: "script" (location: (1:4)-(1:18))
│ └── children: []
│
├── tag_name: "script" (location: (1:4)-(1:18))
├── body: (1 item)
│ └── @ LiteralNode (location: (1:24)-(3:0))
│ └── content: "\n n <o.length\n"
│
├── close_tag:
│ └── @ ERBEndNode (location: (3:0)-(3:9))
│ ├── tag_opening: "<%" (location: (3:0)-(3:2))
│ ├── content: " end " (location: (3:2)-(3:7))
│ └── tag_closing: "%>" (location: (3:7)-(3:9))
│
├── is_void: false
└── element_source: "ActionView::Helpers::JavaScriptHelper#javascript_tag"
```
Resolves#1426
<labelclass="flex items-center gap-1.5 text-gray-300 text-sm" title="Enable HTML tag parsing — uncheck to treat all HTML-like content as literal text (ERB is still parsed)">
0 commit comments