diff --git a/fastn-core/src/package/package_doc.rs b/fastn-core/src/package/package_doc.rs index f7786720db..22fafc5332 100644 --- a/fastn-core/src/package/package_doc.rs +++ b/fastn-core/src/package/package_doc.rs @@ -523,6 +523,7 @@ pub(crate) async fn read_ftd_2023( let ssr_body = fastn_js::ssr_with_js_string( &config.package.name, format!("{js_ftd_script}\n{js_document_script}").as_str(), + main.id.as_str(), ); all_packages.extend(lib.config.all_packages.into_inner()); diff --git a/fastn-js/js/dom.js b/fastn-js/js/dom.js index 91f6d15c81..15d85498d8 100644 --- a/fastn-js/js/dom.js +++ b/fastn-js/js/dom.js @@ -958,7 +958,7 @@ class Node2 { attachCss(property, value, createClass, className) { let propertyShort = fastn_dom.propertyMap[property] || property; propertyShort = `__${propertyShort}`; - let cls = `${propertyShort}-${JSON.stringify(fastn_dom.class_count)}`; + let cls = `${propertyShort}-${fastn_dom.class_count}`; if (!!className) { cls = className; } else { diff --git a/fastn-js/src/main.rs b/fastn-js/src/main.rs index 42bcf38d16..e54b1f8327 100644 --- a/fastn-js/src/main.rs +++ b/fastn-js/src/main.rs @@ -1,6 +1,6 @@ fn main() { let start = std::time::Instant::now(); - println!("{}", fastn_js::ssr_str(js())); + println!("{}", fastn_js::ssr_str(js(), "foo")); println!("elapsed: {:?}", start.elapsed()); let start = std::time::Instant::now(); diff --git a/fastn-js/src/ssr.rs b/fastn-js/src/ssr.rs index f4d6cba9e4..b4e70b8136 100644 --- a/fastn-js/src/ssr.rs +++ b/fastn-js/src/ssr.rs @@ -1,4 +1,4 @@ -pub fn ssr_str(js: &str) -> String { +pub fn ssr_str(js: &str, doc_name: &str) -> String { let all_js = fastn_js::all_js_with_test(); let js = format!("{all_js}{js}"); @@ -19,7 +19,10 @@ pub fn ssr_str(js: &str) -> String { ) .build() .unwrap(); - context.eval_as::(js.as_str()).unwrap() + context + .eval_as::(js.as_str()) + .map_err(|e| panic!("SSR Error: {}, doc_id: {}", e, doc_name)) + .unwrap() } } @@ -32,10 +35,10 @@ pub fn ssr(ast: &[fastn_js::Ast]) -> String { }}; fastn_virtual.ssr(main_wrapper);", fastn_js::to_js(ast, "foo")); - ssr_str(&js) + ssr_str(&js, "foo") } -pub fn ssr_with_js_string(package_name: &str, js: &str) -> String { +pub fn ssr_with_js_string(package_name: &str, js: &str, doc_name: &str) -> String { let js = format!(" let __fastn_package_name__ = \"{}\";\n{} let main_wrapper = function(parent) {{ @@ -46,5 +49,5 @@ pub fn ssr_with_js_string(package_name: &str, js: &str) -> String { }}; fastn_virtual.ssr(main_wrapper);", package_name, js); - ssr_str(&js) + ssr_str(&js, doc_name) } diff --git a/fastn-js/src/to_js.rs b/fastn-js/src/to_js.rs index e31e3935ef..a369cf09bc 100644 --- a/fastn-js/src/to_js.rs +++ b/fastn-js/src/to_js.rs @@ -1035,17 +1035,48 @@ impl ExpressionGenerator { if node.operator().get_variable_identifier_read().is_some() && !no_getter { let chain_dot_operator_count = value.matches('.').count(); - // When there are chained dot operator value - // like person.name, person.meta.address - if chain_dot_operator_count > 1 { + let is_local_argument = value.starts_with(fastn_js::LOCAL_VARIABLE_MAP); + let is_global_value = value.contains(fastn_js::GLOBAL_VARIABLE_MAP); + + // If value is internal looping index variable or global value or ftd variable + if value.eq("index") || is_global_value || value.starts_with("ftd") { + return format!("fastn_utils.getter({})", value); + } + + // When there is dot chaining on local argument values + // like __args__.person.name, __args__.person.meta.address + if is_local_argument { + if chain_dot_operator_count > 1 { + return format!( + "fastn_utils.getter({})", + get_chained_getter_string(value.as_str()) + ); + } + + // If the value is local argument variable with no dot chaining + // like __args__.name, __args__.address + return format!("fastn_utils.getter({})", value); + } + + // Otherwise consider the value as global variable + // If dot chaining on global variable + // like person.name, places.0 + if chain_dot_operator_count > 0 { + let global_variable_name = + format!("{}.foo__{}", fastn_js::GLOBAL_VARIABLE_MAP, value.as_str()); return format!( "fastn_utils.getter({})", - get_chained_getter_string(value.as_str()) + get_chained_getter_string(global_variable_name.as_str()) ); } - // When there is no chained dot operator value - format!("fastn_utils.getter({})", value) + // If no dot chaining on global variable + // like x, y (globally defined) + format!( + "fastn_utils.getter({}.foo__{})", + fastn_js::GLOBAL_VARIABLE_MAP, + value + ) } else { value } diff --git a/fastn-js/src/utils.rs b/fastn-js/src/utils.rs index 8998192499..77797a510c 100644 --- a/fastn-js/src/utils.rs +++ b/fastn-js/src/utils.rs @@ -14,7 +14,9 @@ pub fn reference_to_js(s: &str) -> String { let (mut p1, mut p2) = get_doc_name_and_remaining(s.as_str()); p1 = fastn_js::utils::name_to_js_(p1.as_str()); + let mut prefix_attached = false; let mut wrapper_function = None; + let is_asset_reference = p1.contains("assets"); while let Some(ref remaining) = p2 { let (p21, p22) = get_doc_name_and_remaining(remaining); match p21.parse::() { @@ -22,6 +24,15 @@ pub fn reference_to_js(s: &str) -> String { p1 = format!("{}.get({})", p1, num); wrapper_function = Some("fastn_utils.getListItem"); } + Ok(num) if p22.is_some() && !prefix_attached && !is_asset_reference => { + p1 = format!( + "fastn_utils.getListItem({}{}.get({}))", + prefix.map(|v| format!("{v}.")).unwrap_or_default(), + p1, + num + ); + prefix_attached = true; + } _ => { p1 = format!( "{}.get(\"{}\")", @@ -33,10 +44,12 @@ pub fn reference_to_js(s: &str) -> String { } p2 = p22; } - let p1 = format!( - "{}{p1}", - prefix.map(|v| format!("{v}.")).unwrap_or_default() - ); + if !prefix_attached { + p1 = format!( + "{}{p1}", + prefix.map(|v| format!("{v}.")).unwrap_or_default() + ); + } if let Some(func) = wrapper_function { return format!("{}({})", func, p1); } diff --git a/ftd/src/js/ftd_test_helpers.rs b/ftd/src/js/ftd_test_helpers.rs index e19e17403f..9771636d15 100644 --- a/ftd/src/js/ftd_test_helpers.rs +++ b/ftd/src/js/ftd_test_helpers.rs @@ -98,6 +98,7 @@ fn get_dummy_package_data() -> String { #[track_caller] fn p(s: &str, t: &str, fix: bool, manual: bool, script: bool, file_location: &std::path::PathBuf) { let i = interpret_helper("foo", s).unwrap_or_else(|e| panic!("{:?}", e)); + let doc_name = i.name.clone(); let js_ast_data = ftd::js::document_into_js_ast(i); let js_document_script = fastn_js::to_js(js_ast_data.asts.as_slice(), "foo"); let js_ftd_script = fastn_js::to_js(ftd::js::default_bag_into_js_ast().as_slice(), "foo"); @@ -126,6 +127,7 @@ fn p(s: &str, t: &str, fix: bool, manual: bool, script: bool, file_location: &st let ssr_body = fastn_js::ssr_with_js_string( "foo", format!("{js_ftd_script}\n{js_document_script}").as_str(), + doc_name.as_str(), ); ftd::ftd_js_html() diff --git a/ftd/t/js/69-chained-dot-value-in-functions.ftd b/ftd/t/js/69-chained-dot-value-in-functions.ftd index 786ffb81d9..0d8c56a03b 100644 --- a/ftd/t/js/69-chained-dot-value-in-functions.ftd +++ b/ftd/t/js/69-chained-dot-value-in-functions.ftd @@ -1,32 +1,32 @@ --- record Person: +-- record person: caption name: integer age: -Metadata meta: +metadata meta: --- record Metadata: +-- record metadata: string address: string phone-number: -- string list places: Bangalore, Mumbai, Chennai, Kolkata --- Person list people: +-- person list people: --- Person: Sam Ather +-- person: Sam Ather age: 30 --- Person.meta: +-- person.meta: address: Sam Ather City at Some Other House phone-number: +987-654321 --- Person: $r +-- person: $r -- end: people --- Metadata meta: +-- metadata meta: address: Sam City in Some House phone-number: +1234-56789 --- Person r: Sam Wan +-- person r: Sam Wan age: 23 meta: $meta @@ -37,19 +37,19 @@ meta: $meta -- ftd.text: $first-person-details(people = $people) -- string more-details(p): -Person p: +person p: "Person " + p.name + " lives at " + p.meta.address + ". His contact number is " + p.meta.phone-number -- string some-details(person, places): -Person person: +person person: string list places: string date: -"This person named " + person.name + " has first visited " + places.0 + " on " + date +"The person named " + person.name + " has first visited " + places.0 + " on " + date -- string first-person-details(people): -Person list people: +person list people: -"First Person is " + people.0.name + " lives at " + people.0.meta.address + ". His contact number is " + people.0.meta.phone-number +"First person is " + people.0.name + " lives at " + people.0.meta.address + ". His contact number is " + people.0.meta.phone-number diff --git a/ftd/t/js/69-chained-dot-value-in-functions.html b/ftd/t/js/69-chained-dot-value-in-functions.html index bd5811e428..65a8c59ccd 100644 --- a/ftd/t/js/69-chained-dot-value-in-functions.html +++ b/ftd/t/js/69-chained-dot-value-in-functions.html @@ -16,7 +16,7 @@ -
This person named Sam Wan has first visited Bangalore on 27th October
Person Sam Wan lives at Sam City in Some House. His contact number is +1234-56789
First Person is Sam Ather lives at Sam Ather City at Some Other House. His contact number is +987-654321
@@ -59,7 +59,7 @@ let __args__ = fastn_utils.getArgs({ places: fastn.mutableList([]), }, args); - return ("This person named " + fastn_utils.getter(fastn_utils.getterByKey(__args__.person, "name")) + " has first visited " + fastn_utils.getter(fastn_utils.getterByKey(__args__.places, "0")) + " on " + fastn_utils.getter(__args__.date)); + return ("The person named " + fastn_utils.getter(fastn_utils.getterByKey(__args__.person, "name")) + " has first visited " + fastn_utils.getter(fastn_utils.getterByKey(__args__.places, "0")) + " on " + fastn_utils.getter(__args__.date)); } finally { __fastn_package_name__ = __fastn_super_package_name__; } @@ -99,7 +99,7 @@ let __args__ = fastn_utils.getArgs({ people: fastn.mutableList([]), }, args); - return ("First Person is " + fastn_utils.getter(fastn_utils.getterByKey(fastn_utils.getterByKey(__args__.people, "0"), "name")) + " lives at " + fastn_utils.getter(fastn_utils.getterByKey(fastn_utils.getterByKey(fastn_utils.getterByKey(__args__.people, "0"), "meta"), "address")) + ". His contact number is " + fastn_utils.getter(fastn_utils.getterByKey(fastn_utils.getterByKey(fastn_utils.getterByKey(__args__.people, "0"), "meta"), "phone_number"))); + return ("First person is " + fastn_utils.getter(fastn_utils.getterByKey(fastn_utils.getterByKey(__args__.people, "0"), "name")) + " lives at " + fastn_utils.getter(fastn_utils.getterByKey(fastn_utils.getterByKey(fastn_utils.getterByKey(__args__.people, "0"), "meta"), "address")) + ". His contact number is " + fastn_utils.getter(fastn_utils.getterByKey(fastn_utils.getterByKey(fastn_utils.getterByKey(__args__.people, "0"), "meta"), "phone_number"))); } finally { __fastn_package_name__ = __fastn_super_package_name__; } diff --git a/ftd/t/js/70-global-variables-in-functions.ftd b/ftd/t/js/70-global-variables-in-functions.ftd new file mode 100644 index 0000000000..be12c2b49d --- /dev/null +++ b/ftd/t/js/70-global-variables-in-functions.ftd @@ -0,0 +1,42 @@ +-- record person: +caption name: +integer age: + +-- person p: Sam Wan +age: 30 + +-- person list people: + +-- person: Sam Ather +age: 23 + +-- person: $p + +-- end: people + + + + + + +-- ftd.text: $p.name +color: green + +-- ftd.text: $people.0.name +color: green + +-- ftd.text: $get-details(company = FifthTry) +color: blue + +-- ftd.text: $get-first-person-details(company = FifthTry) +color: blue + +-- string get-details(company): +string company: + +"Person " + p.name + " works for " + company + " whose age is " + p.age + +-- string get-first-person-details(company): +string company: + +"Person " + people.0.name + " works for " + company + " whose age is " + people.0.age diff --git a/ftd/t/js/70-global-variables-in-functions.html b/ftd/t/js/70-global-variables-in-functions.html new file mode 100644 index 0000000000..c9fcd66eb8 --- /dev/null +++ b/ftd/t/js/70-global-variables-in-functions.html @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + +
Sam Wan
Sam Ather
Person Sam Wan works for FifthTry whose age is 30
Person Sam Ather works for FifthTry whose age is 23
+ +