1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use vize_carton::{String, ToCompactString};
use crate::script::ScriptCompileContext;
use super::super::helpers::extract_const_name;
/// Separate hoisted consts (literal consts that can be module-level) from setup code.
///
/// Returns (hoisted_lines, setup_body_lines).
pub(super) fn separate_hoisted_consts(
transformed_setup: &str,
ctx: &ScriptCompileContext,
) -> (Vec<String>, Vec<String>) {
let mut hoisted_lines: Vec<String> = Vec::new();
let mut setup_body_lines: Vec<String> = Vec::new();
let mut in_multiline_value = false;
for line in transformed_setup.lines() {
let trimmed = line.trim();
// Track multi-line template literals / strings - don't hoist individual lines
if in_multiline_value {
setup_body_lines.push(line.to_compact_string());
// Count unescaped backticks to detect end of template literal
let backticks = trimmed
.chars()
.fold((0usize, false), |(count, escaped), c| {
if escaped {
(count, false)
} else if c == '\\' {
(count, true)
} else if c == '`' {
(count + 1, false)
} else {
(count, false)
}
})
.0;
if backticks % 2 == 1 {
in_multiline_value = false;
}
continue;
}
// Check if this is a literal const that should be hoisted
if trimmed.starts_with("const ") && !trimmed.starts_with("const {") {
// Check for multi-line const where value is on the next line (e.g., `const x =\n 'value'`)
if let Some(eq_pos) = trimmed.find('=') {
let value_part = trimmed[eq_pos + 1..].trim();
// If value part is empty, the value is on the next line - don't hoist
if value_part.is_empty() {
setup_body_lines.push(line.to_compact_string());
continue;
}
// Check for multi-line template literal (unclosed backtick)
let backticks = value_part
.chars()
.fold((0usize, false), |(count, escaped), c| {
if escaped {
(count, false)
} else if c == '\\' {
(count, true)
} else if c == '`' {
(count + 1, false)
} else {
(count, false)
}
})
.0;
if backticks % 2 == 1 {
// Unclosed template literal - don't hoist, mark as multi-line
in_multiline_value = true;
setup_body_lines.push(line.to_compact_string());
continue;
}
}
// Extract variable name and check if it's LiteralConst
if let Some(name) = extract_const_name(trimmed) {
if matches!(
ctx.bindings.bindings.get(name.as_str()),
Some(crate::types::BindingType::LiteralConst)
) {
hoisted_lines.push(line.to_compact_string());
continue;
}
}
}
setup_body_lines.push(line.to_compact_string());
}
(hoisted_lines, setup_body_lines)
}