diff --git a/src/main/frontend/fs2/path.cljs b/src/main/frontend/fs2/path.cljs index febdb0e430..6bd62ffcbc 100644 --- a/src/main/frontend/fs2/path.cljs +++ b/src/main/frontend/fs2/path.cljs @@ -11,6 +11,7 @@ (and (string? s) (or (string/starts-with? s "file://") (string/starts-with? s "content://") + (string/starts-with? s "logseq://") ;; reserved for future fs protocl (string/starts-with? s "s3://")))) @@ -59,11 +60,12 @@ (re-find #"[\u0000-\u001f\u0080-\u009f]" fname))))) -(defn path-join +(defn- path-join-internal "Joins the given path segments into a single path, handling relative paths, '..' and '.' normalization." [& segments] (let [segments (remove nil? segments) ;; handle (path-join nil path) + _ (prn ::seg segments) segments (map #(string/replace % #"[/\\]+" "/") segments) ;; a fix for clojure.string/split split-fn (fn [s] @@ -99,17 +101,28 @@ (join-fn)))) (defn url-join + "Segments are not URL-ecoded" [base-url & segments] (let [^js url (.parse Uri base-url) scheme (.getScheme url) domain (.getDomain url) path (.getPath url) - new-path (apply path-join path segments) + new-path (apply path-join-internal path segments) ;; opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, opt_fragment, opt_ignoreCase new-url (.create Uri scheme nil domain nil new-path nil nil nil)] (.toString new-url))) -(defn path-normalize + +(defn path-join + "Join path segments, or URL base and path segments" + [base & segments] + (prn base segments) + (if (is-file-url base) + (apply url-join base segments) + (apply path-join-internal base segments))) + + +(defn- path-normalize-internal "Normalize path using path-join, break into segment and re-join" [path] (path-join path)) @@ -121,23 +134,50 @@ scheme (.getScheme uri) domain (.getDomain uri) path (.getPath uri) - new-path (path-normalize path) + new-path (path-normalize-internal path) ;; opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, opt_fragment, opt_ignoreCase new-uri (.create Uri scheme nil domain nil new-path nil nil nil)] (.toString new-uri))) +(defn path-normalize + "Normalize path or URL" + [path] + (if (is-file-url path) + (url-normalize path) + (path-normalize-internal path))) -(defn relative-path - "Get relative path from base path" - [base path] +(defn trim-dir-prefix + "Trim dir prefix from path" + [base sub] (let [base (path-normalize base) - path (path-normalize path)] + path (path-normalize sub)] (if (string/starts-with? path base) (string/replace (subs path (count base)) #"^/+", "") (do - (js/console.error "unhandled relative path" base path) + (js/console.error "unhandled trim-base" base path) path)))) + +(defn relative-path + "Get relative path from base path. + Works for both path and URL." + [base-path sub-path] + (let [base-path (path-normalize base-path) + is-url (is-file-url base-path) + sub-path (path-normalize sub-path)] + (if (string/starts-with? sub-path base-path) + (string/replace (subs sub-path (count base-path)) #"^/+", "") + ;; append as many .. + (let [base-segs (string/split base-path #"/" -1) + path-segs (string/split sub-path #"/" -1) + common-segs (take-while #(= (first %) (second %)) (map vector base-segs path-segs)) + base-segs (drop (count common-segs) base-segs) + path-segs (drop (count common-segs) path-segs) + base-prefix (repeat (max 0 (dec (count base-segs))) "../")] + #_{:clj-kondo/ignore [:path-invalid-construct/string-join]} + (str (concat base-prefix (string/join "/" path-segs))))))) + + (defn decoded-relative-uri "Get relative uri from base url, url-decoded" [base-url path-url] @@ -152,5 +192,5 @@ (defn parent [path] ;; ugly but works - (path-normalize (str path "/.."))) + (path-normalize-internal (str path "/..")))