package server-reason-react
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Rendering React components on the server natively
Install
dune-project
Dependency
Authors
Maintainers
Sources
server-reason-react-0.5.1.tbz
sha256=3972f64a3ec22b120b40137f74e7862629b2164e5bbf8b85489d4c7b8bc13288
sha512=24a52537c091c4b278ea5e468aa6786378f8b559ed2128e824b6f84f26c283856a30e7ef01aab6101087bd53b9bdc2ecd8152c00db10bccff67221b9c67318a0
doc/src/server-reason-react.js/Js_string.ml.html
Source file Js_string.ml
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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356(** JavaScript String API *) type t = string let make _whatever = Js_internal.notImplemented "Js.String" "make" let fromCharCode code = Quickjs.String.from_char_code [| code |] let fromCharCodeMany codes = Quickjs.String.from_char_code codes let fromCodePoint code_point = Quickjs.String.from_code_point [| code_point |] let fromCodePointMany code_points = Quickjs.String.from_code_point code_points let length str = Quickjs.String.Prototype.length str let get str index = Quickjs.String.Prototype.char_at index str let charAt ~index str = Quickjs.String.Prototype.char_at index str let charCodeAt ~index str = match Quickjs.String.Prototype.char_code_at index str with Some code -> float_of_int code | None -> nan let codePointAt ~index str = Quickjs.String.Prototype.code_point_at index str let concat ~other:str2 str1 = str1 ^ str2 let concatMany ~strings:many original = let many_list = Stdlib.Array.to_list many in Stdlib.String.concat "" (original :: many_list) let endsWith ~suffix ?len str = match len with | None -> Quickjs.String.Prototype.ends_with suffix str | Some len -> Quickjs.String.Prototype.ends_with_at suffix len str let includes ~search ?start str = match start with | None -> Quickjs.String.Prototype.includes search str | Some start -> Quickjs.String.Prototype.includes_from search start str let indexOf ~search ?start str = match start with | None -> Quickjs.String.Prototype.index_of search str | Some start -> Quickjs.String.Prototype.index_of_from search start str let lastIndexOf ~search ?start str = match start with | None -> Quickjs.String.Prototype.last_index_of search str | Some start -> Quickjs.String.Prototype.last_index_of_from search start str (* No locale-aware collation (ICU) on the server: byte-wise comparison. Diverges from JS for case-mixed and non-ASCII strings. *) let localeCompare ~other str = Stdlib.float_of_int (Stdlib.compare (Stdlib.String.compare str other) 0) (* Removes the given flag from a JavaScript flags string, e.g. [remove_flag 'g' "gi"] is ["i"]. *) let remove_flag flag flags = Stdlib.String.to_seq flags |> Stdlib.Seq.filter (fun c -> c <> flag) |> Stdlib.String.of_seq let match_ ~regexp str = if Js_re.global regexp then ( (* JavaScript's String.prototype.match with a global regexp returns every full match (without capture groups) and leaves lastIndex at 0. *) let flags = remove_flag 'g' (Js_re.flags regexp) in let matches = Quickjs.String.Prototype.match_global ~flags (Js_re.source regexp) str in Js_re.setLastIndex regexp 0; if Stdlib.Array.length matches = 0 then None else Some (Stdlib.Array.map (fun m -> Some m) matches)) else match Js_re.exec ~str regexp with None -> None | Some result -> Some (Js_re.captures result) let normalize ?(form = `NFC) str = let normalization = match form with | `NFC -> Quickjs.String.NFC | `NFD -> Quickjs.String.NFD | `NFKC -> Quickjs.String.NFKC | `NFKD -> Quickjs.String.NFKD in match Quickjs.String.Prototype.normalize normalization str with Some s -> s | None -> str let repeat ~count str = Quickjs.String.Prototype.repeat count str let replace ~search ~replacement str = Quickjs.String.Prototype.replace search replacement str let add_source_range buf ~prepared ~source ~start ~end_ = match Js_re.Prepared.byte_range prepared ~start ~end_ with | Some (start_byte, end_byte) -> Buffer.add_substring buf source start_byte (end_byte - start_byte) | None -> Buffer.add_string buf (Js_re.Prepared.substring prepared ~start ~end_) (* Expands the replacement patterns of String.prototype.replace ($$, $&, $`, $', $1..$99) directly into [buf], as specified by GetSubstitution (ECMA-262 22.1.3.20.1). *) let process_replacement ~buf ~prepared ~source ~source_length ~replacement ~matches ~match_start ~match_end = let len = Stdlib.String.length replacement in let i = ref 0 in while !i < len do if replacement.[!i] = '$' && !i + 1 < len then ( let next = replacement.[!i + 1] in match next with | '$' -> (* $$ -> literal $ *) Buffer.add_char buf '$'; i := !i + 2 | '&' -> (* $& -> the matched substring *) let matched = Stdlib.Array.get matches 0 |> Stdlib.Option.value ~default:"" in Buffer.add_string buf matched; i := !i + 2 | '`' -> (* $` -> portion before the match *) add_source_range buf ~prepared ~source ~start:0 ~end_:match_start; i := !i + 2 | '\'' -> (* $' -> portion after the match *) add_source_range buf ~prepared ~source ~start:match_end ~end_:source_length; i := !i + 2 | '0' .. '9' -> (* $n or $nn -> capturing group *) let start_digit = !i + 1 in (* Check for two-digit group number *) let group_num, advance = if !i + 2 < len then match replacement.[!i + 2] with | '0' .. '9' -> let two_digit = int_of_string (Stdlib.String.sub replacement start_digit 2) in if two_digit < Stdlib.Array.length matches then (two_digit, 3) else (Stdlib.Char.code next - Stdlib.Char.code '0', 2) | _ -> (Stdlib.Char.code next - Stdlib.Char.code '0', 2) else (Stdlib.Char.code next - Stdlib.Char.code '0', 2) in if group_num > 0 && group_num < Stdlib.Array.length matches then let group_value = Stdlib.Array.get matches group_num |> Stdlib.Option.value ~default:"" in Buffer.add_string buf group_value else ( (* Invalid group reference, keep as literal *) Buffer.add_char buf '$'; Buffer.add_char buf next; if advance = 3 then Buffer.add_char buf replacement.[!i + 2]); i := !i + advance | _ -> (* Unknown $ sequence, keep as literal *) Buffer.add_char buf '$'; incr i) else ( Buffer.add_char buf replacement.[!i]; incr i) done let is_full_unicode regexp = let flags = Js_re.flags regexp in Stdlib.String.contains flags 'u' || Stdlib.String.contains flags 'v' (* Shared driver for RegExp.prototype[Symbol.replace] (ECMA-262 22.2.6.11). Global matches are collected before replacements are evaluated, so callback mutations of lastIndex cannot affect which matches are replaced. *) let replace_driver ~regexp str ~add_replacement = let str_byte_length = Stdlib.String.length str in let str_length = Quickjs.String.Prototype.length str in let prepared = Js_re.Prepared.make str in let render matches = match matches with | [] -> str | _ -> let buf = Buffer.create str_byte_length in let previous_end = ref 0 in Stdlib.List.iter (fun match_ -> let match_start, match_end = Js_re.Prepared.range match_ in add_source_range buf ~prepared ~source:str ~start:!previous_end ~end_:match_start; add_replacement buf ~prepared ~source_length:str_length ~match_ ~match_start ~match_end; previous_end := match_end) matches; add_source_range buf ~prepared ~source:str ~start:!previous_end ~end_:str_length; Buffer.contents buf in if Js_re.global regexp then ( (* RegExp.prototype[Symbol.replace] (ECMA-262 22.2.6.11): with the global flag, start from 0 and iterate with exec (which advances lastIndex to the end of each match); on an empty match, advance lastIndex manually with AdvanceStringIndex. The final failing exec resets the caller's lastIndex to 0, as in JavaScript. *) Js_re.setLastIndex regexp 0; let unicode = is_full_unicode regexp in let rec collect acc = match Js_re.Prepared.exec prepared regexp with | None -> Stdlib.List.rev acc | Some match_ -> let match_start, match_end = Js_re.Prepared.range match_ in if match_end = match_start then Js_re.setLastIndex regexp (Js_re.Prepared.advance_index prepared ~unicode match_start); collect (match_ :: acc) in render (collect [])) else (* Without the global flag only the first match is replaced. exec is used directly on the caller's regexp so sticky (y) lastIndex semantics are preserved. *) match Js_re.Prepared.exec prepared regexp with | None -> str | Some match_ -> render [ match_ ] let replaceByRe ~regexp ~replacement str = replace_driver ~regexp str ~add_replacement:(fun buf ~prepared ~source_length ~match_ ~match_start ~match_end -> process_replacement ~buf ~prepared ~source:str ~source_length ~replacement ~matches:(Js_re.Prepared.captures match_) ~match_start ~match_end) (* The unsafeReplaceByN functions pass the matched text, the first N capture groups, the UTF-16 offset of the match, and the whole string to [f]. Like JavaScript, a capture group that did not participate in the match is passed as an "empty" value; Melange leaks [undefined], natively it is [""]. *) let capture matches n = if n >= Stdlib.Array.length matches then "" else match Stdlib.Array.get matches n with Some c -> c | None -> "" let unsafeReplaceBy0 ~regexp ~f str = replace_driver ~regexp str ~add_replacement:(fun buf ~prepared:_ ~source_length:_ ~match_ ~match_start ~match_end:_ -> Buffer.add_string buf (f (capture (Js_re.Prepared.captures match_) 0) match_start str)) let unsafeReplaceBy1 ~regexp ~f str = replace_driver ~regexp str ~add_replacement:(fun buf ~prepared:_ ~source_length:_ ~match_ ~match_start ~match_end:_ -> let matches = Js_re.Prepared.captures match_ in Buffer.add_string buf (f (capture matches 0) (capture matches 1) match_start str)) let unsafeReplaceBy2 ~regexp ~f str = replace_driver ~regexp str ~add_replacement:(fun buf ~prepared:_ ~source_length:_ ~match_ ~match_start ~match_end:_ -> let matches = Js_re.Prepared.captures match_ in Buffer.add_string buf (f (capture matches 0) (capture matches 1) (capture matches 2) match_start str)) let unsafeReplaceBy3 ~regexp ~f str = replace_driver ~regexp str ~add_replacement:(fun buf ~prepared:_ ~source_length:_ ~match_ ~match_start ~match_end:_ -> let matches = Js_re.Prepared.captures match_ in Buffer.add_string buf (f (capture matches 0) (capture matches 1) (capture matches 2) (capture matches 3) match_start str)) let search ~regexp str = (* RegExp.prototype[Symbol.search] (ECMA-262 22.2.6.12): search from 0 and restore the caller's lastIndex afterwards. *) let saved_last_index = Js_re.lastIndex regexp in Js_re.setLastIndex regexp 0; let result = match Js_re.exec ~str regexp with Some result -> Js_re.index result | None -> -1 in Js_re.setLastIndex regexp saved_last_index; result let slice ?start ?end_ str = match (start, end_) with | None, None -> str | Some start, None -> Quickjs.String.Prototype.slice_from start str | None, Some end_ -> Quickjs.String.Prototype.slice ~start:0 ~end_ str | Some start, Some end_ -> Quickjs.String.Prototype.slice ~start ~end_ str (* String.prototype.split coerces limit with ToUint32: negative values wrap to huge positive values and behave as "no limit". *) let to_uint32 limit = limit land 0xFFFFFFFF let split ?sep ?limit str = match sep with (* JavaScript's str.split(undefined, limit) never splits: it is [str] alone, or [] when the limit is 0. *) | None -> ( match limit with Some limit when to_uint32 limit = 0 -> [||] | _ -> [| str |]) | Some sep -> ( if str = "" && sep <> "" then (* JavaScript: "".split(sep) is [""] for any non-empty separator. *) match limit with | Some limit when to_uint32 limit = 0 -> [||] | _ -> [| str |] else match limit with | None -> Quickjs.String.Prototype.split sep str | Some limit -> Quickjs.String.Prototype.split_limit sep limit str) let splitByRe ~regexp ?limit str = (* RegExp.prototype[Symbol.split] (ECMA-262 22.2.6.14). The regexp is recompiled with the global flag added (JavaScript clones it with the sticky flag), so the caller's lastIndex is never touched. *) let limit = match limit with None -> 0xFFFFFFFF | Some limit -> to_uint32 limit in if limit = 0 then [||] else (* The spec clones with the sticky flag and probes every position one by one; scanning forward with a global, non-sticky regexp is equivalent. *) let flags = remove_flag 'y' (Js_re.flags regexp) in let flags = if Stdlib.String.contains flags 'g' then flags else flags ^ "g" in let splitter = Js_re.fromStringWithFlags (Js_re.source regexp) ~flags in let unicode = is_full_unicode regexp in let size = Quickjs.String.Prototype.length str in let prepared = Js_re.Prepared.make str in if size = 0 then match Js_re.Prepared.exec prepared splitter with Some _ -> [||] | None -> [| Some str |] else let substring_between start_u16 end_u16 = Js_re.Prepared.substring prepared ~start:start_u16 ~end_:end_u16 in let out = ref [] in let count = ref 0 in let exception Limit_reached in let push entry = out := entry :: !out; incr count; if !count = limit then raise Limit_reached in let segment_start = ref 0 in let reached_limit = try let rec loop () = match Js_re.Prepared.exec prepared splitter with | None -> () | Some match_ -> let match_start, match_end = Js_re.Prepared.range match_ in (* The loop in the spec only probes positions before the end of the string: a match starting at the very end is ignored. *) if match_start < size then let match_end = Stdlib.min match_end size in if match_end = !segment_start then ( (* Empty match at the current segment start: no split here, advance and keep searching. *) Js_re.setLastIndex splitter (Js_re.Prepared.advance_index prepared ~unicode match_start); loop ()) else ( push (Some (substring_between !segment_start match_start)); let captures = Js_re.Prepared.captures match_ in for i = 1 to Stdlib.Array.length captures - 1 do push (Stdlib.Array.get captures i) done; segment_start := match_end; loop ()) in loop (); false with Limit_reached -> true in if not reached_limit then out := Some (substring_between !segment_start size) :: !out; Stdlib.Array.of_list (Stdlib.List.rev !out) let startsWith ~prefix ?start str = match start with | None -> Quickjs.String.Prototype.starts_with prefix str | Some start -> Quickjs.String.Prototype.starts_with_from prefix start str let substr ?start ?len str = match (start, len) with | None, None -> str | Some start, None -> Quickjs.String.Prototype.substr_from start str | None, Some len -> Quickjs.String.Prototype.substr ~start:0 ~length:len str | Some start, Some len -> Quickjs.String.Prototype.substr ~start ~length:len str let substring ?start ?end_ str = match (start, end_) with | None, None -> str | Some start, None -> Quickjs.String.Prototype.substring_from start str | None, Some end_ -> Quickjs.String.Prototype.substring ~start:0 ~end_ str | Some start, Some end_ -> Quickjs.String.Prototype.substring ~start ~end_ str let toLowerCase str = Quickjs.String.Prototype.to_lower_case str (* Locale-insensitive: aliased to toLowerCase (no ICU on the server). *) let toLocaleLowerCase str = Quickjs.String.Prototype.to_lower_case str let toUpperCase str = Quickjs.String.Prototype.to_upper_case str (* Locale-insensitive: aliased to toUpperCase (no ICU on the server). *) let toLocaleUpperCase str = Quickjs.String.Prototype.to_upper_case str let trim str = Quickjs.String.Prototype.trim str (* String.prototype.anchor/link (ECMA-262 B.2.2, CreateHTML): double quotes in the attribute value are replaced with ". *) let escape_html_attribute value = let buf = Buffer.create (Stdlib.String.length value) in Stdlib.String.iter (fun c -> if c = '"' then Buffer.add_string buf """ else Buffer.add_char buf c) value; Buffer.contents buf let anchor ~name str = "<a name=\"" ^ escape_html_attribute name ^ "\">" ^ str ^ "</a>" let link ~href str = "<a href=\"" ^ escape_html_attribute href ^ "\">" ^ str ^ "</a>"
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>