fix: handle string in input transform strings

This commit is contained in:
Ruben Fiszel
2025-11-13 12:26:50 +00:00
parent 0639623af6
commit 2ee17aea8b
+141 -3
View File
@@ -171,6 +171,74 @@ const DOT_PATTERN: &'static str = ".";
const START_BRACKET_PATTERN: &'static str = "[\"";
const END_BRACKET_PATTERN: &'static str = "\"]";
/// Determines if we should prepend "return" to the expression
fn should_add_return(expr: &str) -> bool {
// Trim whitespace
let trimmed = expr.trim();
// If it's empty, add return
if trimmed.is_empty() {
return true;
}
// Check if it already starts with 'return' keyword (as a statement)
// Use word boundary to avoid matching "return" in variable names
if trimmed.starts_with("return ") || trimmed.starts_with("return;") || trimmed == "return" {
return false;
}
// Check for common statement patterns that shouldn't have return prepended
let statement_prefixes = [
"const ", "let ", "var ", "if ", "if(", "for ", "for(",
"while ", "while(", "switch ", "switch(", "try ", "try{",
"throw ", "function ", "class ", "async ", "await "
];
for prefix in &statement_prefixes {
if trimmed.starts_with(prefix) {
return false;
}
}
// Check for multiple statements (contains semicolon not in a string)
// This is still not perfect but better than current logic
if contains_semicolon_outside_strings(trimmed) {
return false;
}
// Default: assume it's an expression that needs return
true
}
/// Checks if the expression contains a semicolon outside of strings
fn contains_semicolon_outside_strings(expr: &str) -> bool {
let mut in_single_quote = false;
let mut in_double_quote = false;
let mut in_template = false;
let mut prev_char = '\0';
for ch in expr.chars() {
match ch {
'\'' if prev_char != '\\' && !in_double_quote && !in_template => {
in_single_quote = !in_single_quote;
}
'"' if prev_char != '\\' && !in_single_quote && !in_template => {
in_double_quote = !in_double_quote;
}
'`' if prev_char != '\\' && !in_single_quote && !in_double_quote => {
in_template = !in_template;
}
';' if !in_single_quote && !in_double_quote && !in_template => {
return true;
}
_ => {}
}
prev_char = ch;
}
false
}
fn try_exact_property_access(
expr: &str,
flow_input: Option<&mappable_rc::Marc<HashMap<String, Box<RawValue>>>>,
@@ -574,10 +642,10 @@ async function resource(path) {{
(String::new(), String::new())
};
let f = if expr.contains("return ") {
expr.to_string()
} else {
let f = if should_add_return(expr) {
format!("return {expr}")
} else {
expr.to_string()
};
let ctx_str = ctx
@@ -1492,4 +1560,74 @@ multiline template`";
// assert_eq!(res.0.get(), "\"\"");
// Ok(())
// }
#[test]
fn test_should_add_return() {
// Simple expressions should get return added
assert_eq!(should_add_return("5"), true);
assert_eq!(should_add_return("x + y"), true);
assert_eq!(should_add_return("foo()"), true);
assert_eq!(should_add_return("obj.property"), true);
// Object literals should get return added
assert_eq!(should_add_return("{ foo: 'bar' }"), true);
assert_eq!(should_add_return("{ a: 1, b: 2 }"), true);
assert_eq!(should_add_return("{}"), true);
// Already has return
assert_eq!(should_add_return("return 5"), false);
assert_eq!(should_add_return("return x + y"), false);
assert_eq!(should_add_return("return;"), false);
assert_eq!(should_add_return("return"), false);
// Should NOT add return for statements
assert_eq!(should_add_return("const x = 5"), false);
assert_eq!(should_add_return("let y = 10"), false);
assert_eq!(should_add_return("var z = 15"), false);
assert_eq!(should_add_return("if (x > 5) { return x; }"), false);
assert_eq!(should_add_return("for (let i = 0; i < 10; i++) {}"), false);
assert_eq!(should_add_return("while (true) {}"), false);
assert_eq!(should_add_return("function foo() {}"), false);
assert_eq!(should_add_return("throw new Error('test')"), false);
// Multiple statements with semicolons (including block statements)
assert_eq!(should_add_return("let x = 5; x + 1"), false);
assert_eq!(should_add_return("{ const x = 5; return x; }"), false);
// Edge case: "return" in a string should still get return prepended
assert_eq!(should_add_return("\"return this string\""), true);
assert_eq!(should_add_return("'return in single quotes'"), true);
assert_eq!(should_add_return("`return in template literal`"), true);
// Semicolons in strings should not trigger multi-statement detection
assert_eq!(should_add_return("\"hello; world\""), true);
assert_eq!(should_add_return("'test; string'"), true);
assert_eq!(should_add_return("`template; literal`"), true);
}
#[test]
fn test_contains_semicolon_outside_strings() {
// Semicolons outside strings
assert_eq!(contains_semicolon_outside_strings("let x = 5; x + 1"), true);
assert_eq!(contains_semicolon_outside_strings("x; y"), true);
// Semicolons inside strings (should NOT be detected)
assert_eq!(contains_semicolon_outside_strings("\"hello; world\""), false);
assert_eq!(contains_semicolon_outside_strings("'test; string'"), false);
assert_eq!(contains_semicolon_outside_strings("`template; literal`"), false);
// Mixed cases
assert_eq!(
contains_semicolon_outside_strings("let x = 'hello; world'; x"),
true
);
assert_eq!(
contains_semicolon_outside_strings("console.log(\"test; string\")"),
false
);
// No semicolons
assert_eq!(contains_semicolon_outside_strings("x + y"), false);
assert_eq!(contains_semicolon_outside_strings("foo()"), false);
}
}