package yocaml
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Core engine of the YOCaml Static Site Generator
Install
dune-project
Dependency
Authors
Maintainers
Sources
yocaml-2.0.0.tbz
sha256=fddf61500e828ac88d86ba982084cc817299302082a6e797b36787ff18235ec2
sha512=8b71a8cecd3e101df55eef0bba7a24d4dde9d66b5ecedd9f6d55834fcdc8d33fd875092ca73a398e1715664caee06cdc1bdb1b4da85bff0a687faac5c0445023
doc/src/yocaml/action.ml.html
Source file action.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(* YOCaml a static blog generator. Copyright (C) 2024 The Funkyworkers and The YOCaml's developers This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. *) type t = Cache.t -> Cache.t Eff.t type interaction = Create | Nothing | Update let need_update cache has_dynamic_deps deps target = let open Eff.Syntax in let* exists = Eff.file_exists ~on:`Target target in if exists then let* need_shortcut, deps, last_build_date = match Cache.get cache target with | None -> (* If there's no information in the cache, it's annoying and dynamic dependencies will probably have to be rechecked, right? In some cases, a file will be rebuilt, which is a bit of a shame, but it allows you to be less rigorous about cache maintenance, in the event of corruption or concurrent access. So it's a loss that's considered acceptable... sorry about that, but I'm not sure there's a "panacea". *) Eff.return (has_dynamic_deps, deps, None) | Some (_, dynamic_deps, last_build_date) when not (Deps.is_empty dynamic_deps) -> (* If an entry exists in the cache and the target is attached to dynamic dependencies, try to rebuild the file taking into account the dynamic dependencies. *) let+ () = Eff.log ~level:`Debug @@ Lexicon.found_dynamic_dependencies target in (false, Deps.concat deps dynamic_deps, last_build_date) | Some (_, _, last_build_date) -> Eff.return (false, deps, last_build_date) in if need_shortcut then Eff.return Update else let* real_mtime_target = Eff.mtime ~on:`Target target in let mtime_target = Option.fold ~none:real_mtime_target ~some:(fun last_build_date -> Int.max last_build_date real_mtime_target) last_build_date in let+ mtime_deps = Deps.get_mtimes deps in if List.exists (fun mtime_dep -> mtime_dep >= mtime_target) mtime_deps then Update else Nothing else Eff.return Create let perform target task ~when_creation ~when_update cache = let open Eff.Syntax in let deps, eff, has_dynamic_deps = Task.destruct task in let* now = Eff.get_time () in let* interaction = need_update cache has_dynamic_deps deps target in match interaction with | Nothing -> let+ () = Eff.log ~level:`Debug @@ Lexicon.target_already_up_to_date target in cache | Create -> when_creation now target eff cache | Update -> when_update now target eff cache let perform_writing now target cache fc hc dynamic_deps = let open Eff.Syntax in let* () = Eff.log ~level:`Debug @@ Lexicon.target_is_written target in let* () = Eff.write_file ~on:`Target target fc in let+ () = Eff.log ~level:`Info @@ Lexicon.target_was_written target in Cache.update ~deps:dynamic_deps ~now cache target hc let perform_update now target eff cache = let open Eff.Syntax in let* fc, dynamic_deps = eff () in let* hc = Eff.hash fc in match Cache.get cache target with | Some (pred_h, _, _) when String.equal hc pred_h -> let+ () = Eff.log ~level:`Debug @@ Lexicon.target_hash_is_unchanged target in Cache.update ~deps:dynamic_deps ~now cache target pred_h | _ -> let* () = Eff.log ~level:`Debug @@ Lexicon.target_hash_is_changed target in perform_writing now target cache fc hc dynamic_deps let write_dynamic_file target task = perform target task ~when_creation:(fun now target eff cache -> let open Eff.Syntax in let* fc, dynamic_deps = eff () in let* hc = Eff.hash fc in perform_writing now target cache fc hc dynamic_deps) ~when_update:perform_update let write_static_file target task cache = write_dynamic_file target Task.(task ||> no_dynamic_deps) cache let copy_file ?new_name ~into path cache = match Path.basename path with | None -> Eff.raise @@ Eff.Invalid_path (`Source, path) | Some fragment -> let name = Option.value ~default:fragment new_name in let dest = Path.(into / name) in write_static_file dest (Pipeline.read_file path) cache let copy_directory ?new_name ~into source cache = let open Eff.Syntax in let perform_copy _ _ eff cache = let+ (), _ = eff () in cache in let* name = Eff.get_basename source in let name = Option.value new_name ~default:name in let target = Path.(into / name) in let task = Task.( make (Deps.singleton source) (fun () -> Eff.copy_recursive ?new_name ~into source) ||> no_dynamic_deps) in perform target task ~when_creation:perform_copy ~when_update:perform_copy cache let fold ?only ?where ~state path action cache = let open Eff in let* children = read_directory ~on:`Source ?only ?where path in Stdlib.List.fold_left (fun state file -> let* cache, state = state in action file state cache) (return (cache, state)) children let fold_list ~state list action cache = let open Eff in Stdlib.List.fold_left (fun state elt -> let* cache, state = state in action elt state cache) (return (cache, state)) list let batch ?only ?where path action cache = let open Eff in let+ cache, () = fold ?only ?where ~state:() path (fun file () cache -> let+ cache = action file cache in (cache, ())) cache in cache let batch_list list action cache = let open Eff in let+ cache, () = fold_list ~state:() list (fun elt () cache -> let+ cache = action elt cache in (cache, ())) cache in cache let restore_cache ?(on = `Target) path = let open Eff.Syntax in let* exists = Eff.file_exists ~on path in if exists then let* cache_content = Eff.read_file ~on path in let sexp = Sexp.Canonical.from_string cache_content in match sexp with | Error _ -> let+ () = Eff.log ~level:`Warning @@ Lexicon.cache_invalid_csexp path in Cache.empty | Ok sexp -> Result.fold ~ok:(fun cache -> let+ () = Eff.log ~level:`Debug @@ Lexicon.cache_restored path in cache) ~error:(fun _ -> let+ () = Eff.log ~level:`Warning @@ Lexicon.cache_invalid_repr path in Cache.empty) (Cache.from_sexp sexp) else let+ () = Eff.log ~level:`Debug @@ Lexicon.cache_initiated path in Cache.empty let store_cache ?(on = `Target) path cache = let open Eff.Syntax in let sexp_str = cache |> Cache.to_sexp |> Sexp.Canonical.to_string in let* () = Eff.write_file ~on path sexp_str in Eff.log ~level:`Debug @@ Lexicon.cache_stored path let exec_cmd ?is_success cmd target = let action _ _ eff cache = let open Eff in let+ () = eff () in cache in perform target (Pipeline.exec_cmd ?is_success (cmd (Cmd.p target))) ~when_creation:action ~when_update:action module Static = struct let write_file path task = write_static_file path task let write_file_with_metadata path task = write_file path (let open Task in task >>> Static.keep_content ()) end module Dynamic = struct let write_file path task = write_dynamic_file path task let write_file_with_metadata path task = write_file path (let open Task in task >>> Dynamic.keep_content ()) end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>