Skip to content

Commit 7187710

Browse files
committed
Parser: Treat javascript_tag content as foreign content
1 parent a25dec0 commit 7187710

File tree

27 files changed

+589
-43
lines changed

27 files changed

+589
-43
lines changed

ext/herb/extension.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
156156
if (NIL_P(prism_program)) { prism_program = rb_hash_lookup(options, ID2SYM(rb_intern("prism_program"))); }
157157
if (!NIL_P(prism_program) && RTEST(prism_program)) { parser_options.prism_program = true; }
158158

159+
VALUE html = rb_hash_lookup(options, rb_utf8_str_new_cstr("html"));
160+
if (NIL_P(html)) { html = rb_hash_lookup(options, ID2SYM(rb_intern("html"))); }
161+
if (!NIL_P(html) && !RTEST(html)) { parser_options.html = false; }
162+
159163
VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
160164
if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
161165
if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }

java/herb_jni.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ Java_org_herb_Herb_parse(JNIEnv* env, jclass clazz, jstring source, jobject opti
108108
jboolean prismProgram = (*env)->CallBooleanMethod(env, options, getPrismProgram);
109109
parser_options.prism_program = (prismProgram == JNI_TRUE);
110110
}
111+
112+
jmethodID getHtml =
113+
(*env)->GetMethodID(env, optionsClass, "isHtml", "()Z");
114+
115+
if (getHtml != NULL) {
116+
jboolean html = (*env)->CallBooleanMethod(env, options, getHtml);
117+
parser_options.html = (html == JNI_TRUE);
118+
}
111119
}
112120

113121
hb_allocator_T allocator;

java/org/herb/ParserOptions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class ParserOptions {
1010
private boolean prismNodes = false;
1111
private boolean prismNodesDeep = false;
1212
private boolean prismProgram = false;
13+
private boolean html = true;
1314

1415
public ParserOptions() {}
1516

@@ -94,6 +95,15 @@ public boolean isPrismProgram() {
9495
return prismProgram;
9596
}
9697

98+
public ParserOptions html(boolean value) {
99+
this.html = value;
100+
return this;
101+
}
102+
103+
public boolean isHtml() {
104+
return html;
105+
}
106+
97107
public static ParserOptions create() {
98108
return new ParserOptions();
99109
}

javascript/packages/core/src/parser-options.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface ParseOptions {
88
prism_nodes?: boolean
99
prism_nodes_deep?: boolean
1010
prism_program?: boolean
11+
html?: boolean
1112
}
1213

1314
export type SerializedParserOptions = Required<ParseOptions>
@@ -22,6 +23,7 @@ export const DEFAULT_PARSER_OPTIONS: SerializedParserOptions = {
2223
prism_nodes: false,
2324
prism_nodes_deep: false,
2425
prism_program: false,
26+
html: true,
2527
}
2628

2729
/**
@@ -55,6 +57,9 @@ export class ParserOptions {
5557
/** Whether the full Prism ProgramNode was serialized on the DocumentNode. */
5658
readonly prism_program: boolean
5759

60+
/** Whether HTML tag parsing is enabled during parsing. When false, HTML-like content is treated as literal text. */
61+
readonly html: boolean
62+
5863
static from(options: SerializedParserOptions): ParserOptions {
5964
return new ParserOptions(options)
6065
}
@@ -69,5 +74,6 @@ export class ParserOptions {
6974
this.prism_nodes = options.prism_nodes ?? DEFAULT_PARSER_OPTIONS.prism_nodes
7075
this.prism_nodes_deep = options.prism_nodes_deep ?? DEFAULT_PARSER_OPTIONS.prism_nodes_deep
7176
this.prism_program = options.prism_program ?? DEFAULT_PARSER_OPTIONS.prism_program
77+
this.html = options.html ?? DEFAULT_PARSER_OPTIONS.html
7278
}
7379
}

javascript/packages/linter/test/parse-cache.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ describe("ParseCache", () => {
7575
strict: true,
7676
strict_locals: false,
7777
action_view_helpers: false,
78+
html: true,
7879
})
7980
})
8081

@@ -92,6 +93,7 @@ describe("ParseCache", () => {
9293
strict: false,
9394
strict_locals: false,
9495
action_view_helpers: false,
96+
html: true,
9597
})
9698
})
9799

@@ -109,6 +111,7 @@ describe("ParseCache", () => {
109111
strict: true,
110112
strict_locals: false,
111113
action_view_helpers: false,
114+
html: true,
112115
})
113116
})
114117

@@ -126,6 +129,7 @@ describe("ParseCache", () => {
126129
strict: false,
127130
strict_locals: false,
128131
action_view_helpers: false,
132+
html: true,
129133
})
130134
})
131135
})

playground/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,17 @@
490490
/>
491491
<span class="select-none">Prism nodes (deep)</span>
492492
</label>
493+
494+
<label class="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)">
495+
<input
496+
type="checkbox"
497+
data-option="html"
498+
data-action="change->playground#onOptionChange"
499+
class="rounded border-gray-600 text-green-600 focus:ring-green-500 bg-gray-700"
500+
checked
501+
/>
502+
<span class="select-none">HTML</span>
503+
</label>
493504
</div>
494505

495506
<pre

playground/src/controllers/playground_controller.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,7 @@ export default class extends Controller {
12711271
prism_program: false,
12721272
prism_nodes: false,
12731273
prism_nodes_deep: false,
1274+
html: true,
12741275
}
12751276

12761277
const nonDefaultOptions = {}

rust/src/herb.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct ParserOptions {
1414
pub prism_nodes: bool,
1515
pub prism_nodes_deep: bool,
1616
pub prism_program: bool,
17+
pub html: bool,
1718
}
1819

1920
impl Default for ParserOptions {
@@ -28,6 +29,7 @@ impl Default for ParserOptions {
2829
prism_nodes: false,
2930
prism_nodes_deep: false,
3031
prism_program: false,
32+
html: true,
3133
}
3234
}
3335
}
@@ -109,6 +111,7 @@ pub fn parse_with_options(source: &str, options: &ParserOptions) -> Result<Parse
109111
prism_program: options.prism_program,
110112
prism_nodes: options.prism_nodes,
111113
prism_nodes_deep: options.prism_nodes_deep,
114+
html: options.html,
112115
};
113116

114117
let ast = crate::ffi::herb_parse(c_source.as_ptr(), &c_parser_options, &mut allocator);

src/analyze/action_view/tag_helpers.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "../../include/analyze/action_view/tag_helper_node_builders.h"
55
#include "../../include/analyze/analyze.h"
66
#include "../../include/ast_nodes.h"
7+
#include "../../include/herb.h"
78
#include "../../include/html_util.h"
89
#include "../../include/parser_helpers.h"
910
#include "../../include/position.h"
@@ -687,23 +688,26 @@ static AST_NODE_T* transform_erb_block_to_tag_helper(
687688
AST_NODE_T* close_tag = (AST_NODE_T*) block_node->end_node;
688689

689690
if (tag_name && parser_is_foreign_content_tag(hb_string_from_c_string(tag_name)) && context->source
690-
&& block_node->body && hb_array_size(block_node->body) > 0) {
691+
&& block_node->body && hb_array_size(block_node->body) > 0 && block_node->end_node
692+
&& block_node->end_node->tag_opening) {
691693
size_t start_offset = block_node->tag_closing->range.to;
692694
size_t end_offset = block_node->end_node->tag_opening->range.from;
693695

694696
if (end_offset > start_offset) {
695697
position_T body_start = block_node->tag_closing->location.end;
696-
position_T body_end = block_node->end_node->tag_opening->location.start;
697698

698699
size_t content_length = end_offset - start_offset;
699700
char* raw_copy = hb_allocator_strndup(allocator, context->source + start_offset, content_length);
700-
hb_string_T raw_content = { .data = raw_copy, .length = (uint32_t) content_length };
701701

702-
AST_LITERAL_NODE_T* literal_node =
703-
ast_literal_node_init(raw_content, body_start, body_end, hb_array_init(0, allocator), allocator);
702+
parser_options_T body_options = HERB_DEFAULT_PARSER_OPTIONS;
703+
body_options.html = false;
704+
body_options.analyze = false;
705+
body_options.strict = false;
706+
body_options.start_line = body_start.line;
707+
body_options.start_column = body_start.column;
704708

705-
body = hb_array_init(1, allocator);
706-
hb_array_append(body, literal_node);
709+
AST_DOCUMENT_NODE_T* body_document = herb_parse(raw_copy, &body_options, allocator);
710+
body = body_document->children;
707711
}
708712
}
709713

src/herb.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ HERB_EXPORTED_FUNCTION AST_DOCUMENT_NODE_T* herb_parse(
4444
parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
4545
if (options != NULL) { parser_options = *options; }
4646

47+
if (parser_options.start_line > 0) {
48+
lexer.current_line = parser_options.start_line;
49+
lexer.previous_line = parser_options.start_line;
50+
}
51+
52+
if (parser_options.start_column > 0) {
53+
lexer.current_column = parser_options.start_column;
54+
lexer.previous_column = parser_options.start_column;
55+
}
56+
4757
herb_parser_init(&parser, &lexer, parser_options);
4858

4959
AST_DOCUMENT_NODE_T* document = herb_parser_parse(&parser);

0 commit comments

Comments
 (0)