Compare commits

..
1 Commits
Author SHA1 Message Date
msyds de61e45637 feat: html 캐시
build / build (push) Failing after 1m33s
2026-03-27 18:45:46 -06:00
92 changed files with 667 additions and 642 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
((nil . ((cider-clojure-cli-aliases . ":dev:test"))))
+2
View File
@@ -9,3 +9,5 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: build doerg - name: build doerg
run: nix build -L .#doerg run: nix build -L .#doerg
- name: build publisher
run: nix build -L .#publisher
+1
View File
@@ -9,6 +9,7 @@ result-*
.lsp .lsp
.nrepl .nrepl
.direnv/ .direnv/
resources/vendor/*
node_modules node_modules
.cljs_node_repl .cljs_node_repl
build/ build/
+29 -1
View File
@@ -1,6 +1,14 @@
{ {
"lock-version": 4, "lock-version": 4,
"git-deps": [], "git-deps": [
{
"lib": "io.github.msyds/spec-dict",
"url": "https://github.com/msyds/spec-dict.git",
"rev": "531d629b7f05f37232261cf9e8927a4b5915714f",
"git-dir": "https/github.com/msyds/spec-dict",
"hash": "sha256-5hMdPsB8OhOCtByPZS+CHXzVLq0H+OBKKnXec21xwmg="
}
],
"mvn-deps": [ "mvn-deps": [
{ {
"mvn-path": "aero/aero/1.1.6/aero-1.1.6.jar", "mvn-path": "aero/aero/1.1.6/aero-1.1.6.jar",
@@ -137,6 +145,11 @@
"mvn-repo": "https://repo1.maven.org/maven2/", "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-ccrFOSFR4qUozJoJF58KM0F58FxS+OWWz1jd8Suyfys=" "hash": "sha256-ccrFOSFR4qUozJoJF58KM0F58FxS+OWWz1jd8Suyfys="
}, },
{
"mvn-path": "com/fasterxml/jackson/core/jackson-core/2.20.0/jackson-core-2.20.0.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-vAz0YHWHcgH4QG7n3idBrn32wGb18EV72AYypxjAbnI="
},
{ {
"mvn-path": "com/fasterxml/jackson/core/jackson-core/2.20.0/jackson-core-2.20.0.pom", "mvn-path": "com/fasterxml/jackson/core/jackson-core/2.20.0/jackson-core-2.20.0.pom",
"mvn-repo": "https://repo1.maven.org/maven2/", "mvn-repo": "https://repo1.maven.org/maven2/",
@@ -357,6 +370,11 @@
"mvn-repo": "https://repo.clojars.org/", "mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-OaawIvv0GgkuZwK6MAFtmuyw3zNcYiZVd66K/IipB2A=" "hash": "sha256-OaawIvv0GgkuZwK6MAFtmuyw3zNcYiZVd66K/IipB2A="
}, },
{
"mvn-path": "fipp/fipp/0.6.26/fipp-0.6.26.jar",
"mvn-repo": "https://repo.clojars.org/",
"hash": "sha256-98tpbM5Vr9dMg41UQUGcfl9tSRrxhajlY9+nl5aFcoM="
},
{ {
"mvn-path": "fipp/fipp/0.6.26/fipp-0.6.26.pom", "mvn-path": "fipp/fipp/0.6.26/fipp-0.6.26.pom",
"mvn-repo": "https://repo.clojars.org/", "mvn-repo": "https://repo.clojars.org/",
@@ -952,6 +970,11 @@
"mvn-repo": "https://repo1.maven.org/maven2/", "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-NnHYN2UlIwq6Ah8fYmx54g86ELYrXfgXIiWJDsSv4EU=" "hash": "sha256-NnHYN2UlIwq6Ah8fYmx54g86ELYrXfgXIiWJDsSv4EU="
}, },
{
"mvn-path": "org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-UfmOunss1C7jDzgmkl3N6HkRZ/dvcSMprlG4gkToE44="
},
{ {
"mvn-path": "org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2.pom", "mvn-path": "org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2.pom",
"mvn-repo": "https://repo1.maven.org/maven2/", "mvn-repo": "https://repo1.maven.org/maven2/",
@@ -1102,6 +1125,11 @@
"mvn-repo": "https://repo1.maven.org/maven2/", "mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-E15H98p5wKoYG2kJPML50aYyKt1qgli2aXxQCNIwv00=" "hash": "sha256-E15H98p5wKoYG2kJPML50aYyKt1qgli2aXxQCNIwv00="
}, },
{
"mvn-path": "org/clojure/tools.reader/1.3.6/tools.reader-1.3.6.jar",
"mvn-repo": "https://repo1.maven.org/maven2/",
"hash": "sha256-EdGzHyxlwzVbKSu5tEuPyv2lS0TaY+NKuXt5qKs7uOA="
},
{ {
"mvn-path": "org/clojure/tools.reader/1.3.6/tools.reader-1.3.6.pom", "mvn-path": "org/clojure/tools.reader/1.3.6/tools.reader-1.3.6.pom",
"mvn-repo": "https://repo1.maven.org/maven2/", "mvn-repo": "https://repo1.maven.org/maven2/",
-24
View File
@@ -1,24 +0,0 @@
{:deps {aero/aero #:mvn{:version "1.1.6"}
babashka/fs #:mvn{:version "0.5.24"}
babashka/process #:mvn{:version "0.6.25"}
ch.qos.logback/logback-classic #:mvn{:version "1.1.3"}
cheshire/cheshire #:mvn{:version "6.1.0"}
com.github.seancorfield/next.jdbc #:mvn{:version "1.3.1070"}
com.rpl/specter #:mvn{:version "1.1.6"}
hiccup/hiccup #:mvn{:version "2.0.0-RC4"}
http-kit/http-kit #:mvn{:version "2.8.0"}
instaparse/instaparse #:mvn{:version "1.5.0"}
metosin/reitit #:mvn{:version "0.10.1"}
mvxcvi/clj-cbor #:mvn{:version "1.1.1"}
org.clojars.pntblnk/clj-ldap #:mvn{:version "0.0.17"}
org.clojure/clojure #:mvn{:version "1.12.0"}
org.clojure/core.match #:mvn{:version "1.1.0"}
org.clojure/test.check #:mvn{:version "1.1.3"}
org.clojure/tools.logging #:mvn{:version "1.3.0"}
org.xerial/sqlite-jdbc #:mvn{:version "3.47.1.0"}}
:paths ["src" "resources"]
:aliases
{:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
:extra-paths ["test"]
:main-opts ["-m" "kaocha.runner"]}
:dev {:extra-paths ["dev"]}}}
-17
View File
@@ -1,17 +0,0 @@
(ns user
(:require [net.deertopia.doerg.server :as server]
[net.deertopia.doerg.config :as cfg]
[net.deertopia.doerg.cached-file :as cached-file]
[babashka.fs :as fs]))
(cfg/load-config! :profile :dev)
(when (not= :running (server/status))
(server/start!))
(defn invalidate-html-cache! []
(fs/delete-tree (server/html-dir))
nil)
(defn toggle-html-cache! []
(alter-var-root #'cached-file/*use-cache?* not))
+6
View File
@@ -0,0 +1,6 @@
#+title: Doerg specification
#+author: Guppy
* Footnotes
- A bunch of metadata should be read into a node of type =doerg-data=
+6 -19
View File
@@ -4,9 +4,8 @@
, doerg-parser , doerg-parser
, doerg-temml-worker , doerg-temml-worker
, ibm-plex-web , ibm-plex-web
, test-emacs ? callPackage ./test-emacs.nix {}
, fake-git , fake-git
, our-tex , our-tex ? callPackage ./our-tex.nix {}
, makeWrapper , makeWrapper
, writeText , writeText
}: }:
@@ -33,8 +32,8 @@ let
]; ];
doerg-config = writeText "doerg-extra-config.edn" '' doerg-config = writeText "doerg-extra-config.edn" ''
#:net.deertopia.doerg.config #:net.deertopia.doerg
{:ibm-plex-web "${ibm-plex-web}/share/ibm-plex-web" {:ibm-plex-web "${ibm-plex-web}"
:latex "${lib.getExe' our-tex "xelatex"}" :latex "${lib.getExe' our-tex "xelatex"}"
:dvisvgm "${lib.getExe' our-tex "dvisvgm"}" :dvisvgm "${lib.getExe' our-tex "dvisvgm"}"
:doerg-temml-worker "${lib.getExe doerg-temml-worker}" :doerg-temml-worker "${lib.getExe doerg-temml-worker}"
@@ -43,21 +42,15 @@ let
in mkCljBin' { in mkCljBin' {
name = "net.deertopia/doerg"; name = "net.deertopia/doerg";
version = "0.1.0"; version = "0.1.0";
projectSrc = lib.cleanSourceWith { projectSrc = lib.cleanSource ./.;
filter = path: type: lockfile = ../deps-lock.json;
lib.sources.cleanSourceFilter path type
|| lib.hasSuffix ".nix" path;
src = ./.;
};
lockfile = ./deps-lock.json;
main-ns = "net.deertopia.doerg.main"; main-ns = "net.deertopia.doerg.main";
nativeBuildInputs = [ nativeBuildInputs = [
plex plex
makeWrapper makeWrapper
]; ];
propagatedBuildInputs = [ buildInputs = [
doerg-parser doerg-parser
ibm-plex-web
doerg-temml-worker doerg-temml-worker
plex plex
our-tex our-tex
@@ -65,7 +58,6 @@ in mkCljBin' {
nativeCheckInputs = [ nativeCheckInputs = [
doerg-parser doerg-parser
doerg-temml-worker doerg-temml-worker
test-emacs
plex plex
our-tex our-tex
]; ];
@@ -74,11 +66,6 @@ in mkCljBin' {
''; '';
doCheck = true; doCheck = true;
checkPhase = '' checkPhase = ''
export \
EMACS=test-emacs \
XDG_STATE_HOME=$(mktemp -d "state-home-XXXXXX")
clojure -M:test clojure -M:test
''; '';
passthru = { inherit plex our-tex test-emacs doerg-config; };
meta.mainProgram = "doerg";
} }
+18
View File
@@ -0,0 +1,18 @@
{:deps {org.clojure/tools.logging {:mvn/version "1.3.0"}
babashka/fs {:mvn/version "0.5.24"}
org.clojure/core.match {:mvn/version "1.1.0"}
cheshire/cheshire {:mvn/version "6.1.0"}
babashka/process {:mvn/version "0.6.25"}
io.github.msyds/spec-dict
{:git/sha "531d629b7f05f37232261cf9e8927a4b5915714f"}
hiccup/hiccup {:mvn/version "2.0.0-RC4"}
com.rpl/specter {:mvn/version "1.1.6"}
mvxcvi/clj-cbor {:mvn/version "1.1.1"}
ch.qos.logback/logback-classic {:mvn/version "1.1.3"}
org.clojure/test.check {:mvn/version "1.1.3"}
aero/aero {:mvn/version "1.1.6"}}
:paths ["src" "resources"]
:aliases
{:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
:extra-paths ["test"]
:main-opts ["-m" "kaocha.runner"]}}}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
@@ -0,0 +1,11 @@
#:net.deertopia.doerg
{:ibm-plex-web #or [#xdg-data-dir "ibm-plex-web"
#env IBM_PLEX_WEB]
:latex "xelatex"
:dvisvgm "dvisvgm"
:doerg-temml-worker
#profile {:dev #file "../../../../doerg-temml-worker/index.js"
:default "doerg-temml-worker"}
:doerg-parser
#profile {:dev #file "../../../../doerg-parser/index.js"
:default "doerg-parser"}}
@@ -438,7 +438,7 @@ label.margin-toggle:not(.sidenote-number) {
} }
} }
.org-link.external::after .link.external::after
{ content: "↗" { content: "↗"
; vertical-align: super ; vertical-align: super
; font-size: 1rem ; font-size: 1rem
@@ -446,8 +446,8 @@ label.margin-toggle:not(.sidenote-number) {
} }
.center .center
{ align-items: center { align-items: "center"
; justify-content: center ; justify-content: "center"
; display: flex ; display: flex
; max-width: 55% ; max-width: 55%
} }
+27
View File
@@ -0,0 +1,27 @@
(ns scratch
(:require [clojure.spec.alpha :as s]
[spec-dict.main :refer [dict]]
[clojure.test.check.generators :as gen]))
(defmulti node-spec :type)
(s/def ::node
(s/multi-spec node-spec :type))
(s/def ::children (s/coll-of ::node :kind vector?))
(s/def ::value nat-int?)
(defmethod node-spec :branch [_]
(dict {:children ::children})
#_(s/keys :req-un [::children]))
(defmethod node-spec :leaf [_]
(dict {:value ::value})
#_
(s/keys :req-un [::value]))
(comment
(binding [s/*recursion-limit* 1]
(gen/generate (s/gen ::node))))
File renamed without changes.
@@ -1,25 +1,25 @@
(ns net.deertopia.doerg.config (ns net.deertopia.doerg.config
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[babashka.fs :as fs] [babashka.fs :as fs]
[spec-dict.main :refer [dict]]
[aero.core :as aero] [aero.core :as aero]
[net.deertopia.doerg :as-alias d]
[clojure.java.io :as io])) [clojure.java.io :as io]))
(s/def ::config (s/def ::d/config
(s/keys :req [::ibm-plex-web (s/keys :req [::d/ibm-plex-web
::latex ::d/latex
::dvisvgm ::d/dvisvgm
::doerg-temml-worker ::d/doerg-temml-worker
::doerg-parser ::d/doerg-parser]))
::state-directory
::org-roam-db-path]))
(s/def ::directory (s/def ::d/directory
(s/conformer #(fs/file %))) (s/conformer #(fs/file %)))
(s/def ::file (s/def ::d/file
(s/conformer #(-> % fs/expand-home fs/file))) (s/conformer #(-> % fs/expand-home fs/file)))
(s/def ::executable (s/def ::d/executable
(s/conformer (s/conformer
;; I'd love to use `fs/which` here, but it's fairly problematic to ;; I'd love to use `fs/which` here, but it's fairly problematic to
;; check `fs/executable?` at… build time (which `fs/which` does)? ;; check `fs/executable?` at… build time (which `fs/which` does)?
@@ -28,26 +28,22 @@
(some-> % fs/expand-home fs/file) (some-> % fs/expand-home fs/file)
::s/invalid))) ::s/invalid)))
(s/def ::ibm-plex-web ::directory) (s/def ::d/ibm-plex-web ::d/directory)
(s/def ::latex ::executable) (s/def ::d/latex ::d/executable)
(s/def ::dvisvgm ::executable) (s/def ::d/dvisvgm ::d/executable)
(s/def ::doerg-temml-worker ::executable) (s/def ::d/doerg-temml-worker ::d/executable)
(s/def ::doerg-parser ::executable) (s/def ::d/doerg-parser ::d/executable)
(s/def ::state-directory ::file)
(s/def ::org-roam-db-path ::file)
(defmethod aero/reader 'xdg-data-dir (defmethod aero/reader 'xdg-data-dir
[_opts tag value] [_opts tag value]
"Aero tag to search for a directory on $XDG_DATA_DIRS." "Aero tag to search for a directory on $XDG_DATA_DIRS."
(some #(let [x (fs/path % value)] (some #(let [x (fs/path % value)]
(and (fs/exists? x) x)) (and (fs/exists? x) x))
(some-> (System/getenv "XDG_DATA_DIRS") (fs/split-paths (System/getenv "XDG_DATA_DIRS"))))
fs/split-paths)))
(defmethod aero/reader 'file (defmethod aero/reader 'file
[{:keys [source]} tag value] [{:keys [source]} tag value]
@@ -56,16 +52,19 @@
(-> (aero/relative-resolver source value) (-> (aero/relative-resolver source value)
fs/file)) fs/file))
(defn read-config [files & {:as opts}] (defn read-config [spec files & {:as opts}]
(let [r (->> files (let [r (->> files
(filter identity) (filter identity)
(map #(aero/read-config % opts)) (map #(aero/read-config % opts))
(apply merge)) (apply merge))
conformed (s/conform ::config r)] conformed (s/conform spec r)]
(if-not (s/invalid? conformed) (if-not (s/invalid? conformed)
conformed conformed
(throw (ex-info "Failed to conform config" (throw (ex-info "Failed to conform config"
(s/explain-data ::config r)))))) (s/explain-data spec r))))))
(defn load-config! [var spec files & {:as opts}]
(alter-var-root var (constantly (read-config spec files opts))))
(def sources (def sources
[;; Default config. [;; Default config.
@@ -75,13 +74,6 @@
;; Config set at runtime. ;; Config set at runtime.
(System/getenv "DOERG_CONFIG")]) (System/getenv "DOERG_CONFIG")])
(def default (read-config sources)) (def default (read-config ::d/config sources))
(def ^:dynamic *cfg* default) (def ^:dynamic *cfg* default)
(defn load-config!
([& {:as opts :keys [files]
:or {files sources}}]
(let [passthru-opts (-> opts (dissoc :files))]
(alter-var-root
#'*cfg* (constantly (read-config files passthru-opts))))))
@@ -4,8 +4,10 @@
[babashka.process :as p] [babashka.process :as p]
[cheshire.core :as json] [cheshire.core :as json]
[clojure.core.match :refer [match]] [clojure.core.match :refer [match]]
[net.deertopia.doerg :as-alias doerg]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.set :as set] [clojure.set :as set]
[clojure.spec.alpha :as s]
[clojure.string :as str] [clojure.string :as str]
[clojure.test.check.generators :as gen] [clojure.test.check.generators :as gen]
[clojure.tools.logging.readable :as lr] [clojure.tools.logging.readable :as lr]
@@ -14,6 +16,7 @@
[com.rpl.specter.zipper :as sz] [com.rpl.specter.zipper :as sz]
[net.deertopia.doerg.common :as common] [net.deertopia.doerg.common :as common]
[net.deertopia.doerg.config :as cfg] [net.deertopia.doerg.config :as cfg]
[spec-dict.main :refer [dict]]
[clojure.tools.logging :as l]) [clojure.tools.logging :as l])
(:import (:import
(java.util UUID))) (java.util UUID)))
@@ -34,7 +37,7 @@
:or {in *in*}}] :or {in *in*}}]
(let [r (-> (p/process (let [r (-> (p/process
{:in in :out :string} {:in in :out :string}
(-> cfg/*cfg* ::cfg/doerg-parser str)) (-> cfg/*cfg* ::doerg/doerg-parser str))
(common/deref-with-timeout *uniorg-timeout-duration*))] (common/deref-with-timeout *uniorg-timeout-duration*))]
(when (zero? (:exit r)) (when (zero? (:exit r))
(-> r :out (json/parse-string (comp keyword camel->kebab)))))) (-> r :out (json/parse-string (comp keyword camel->kebab))))))
@@ -62,6 +65,8 @@
(and (map? e) (contains? e :children))) (and (map? e) (contains? e :children)))
(defn org-element? [element] (defn org-element? [element]
#_
(s/valid? ::org-element element)
(and (map? element) (and (map? element)
(contains? element :type))) (contains? element :type)))
@@ -94,6 +99,16 @@
(and (of-type? element "latex-fragment") (and (of-type? element "latex-fragment")
(-> element :contents (str/starts-with? "\\["))))) (-> element :contents (str/starts-with? "\\[")))))
;;; Spec
(s/def ::org-element
(dict {:type string?}
^:opt {:contents-begin nat-int?
:contents-end nat-int?
:children (s/coll-of ::org-element
:kind seq?)}))
;;; Zipper ;;; Zipper
@@ -300,3 +315,149 @@
(recur (conj acc c) rest) (recur (conj acc c) rest)
[] acc)))))) [] acc))))))
;;; Specs (top-level)
;; Data taken from uniorg/index.d.ts
(comment
(defn- typescript-enum->set [s]
(as-> s
it
(str/split it #" \| ")
(map camel->kebab it)
(into #{} it))))
(def greater-element-types
#{"org-data" "section" "property-drawer" "drawer" "plain-list"
"list-item" "quote-block" "verse-block" "center-block"
"special-block" "footnote-definition" "table"})
(def element-types
#{"list-item-tag" "src-block" "comment-block" "latex-environment"
"keyword" "paragraph" "node-property" "example-block" "clock"
"planning" "diary-sexp" "fixed-width" "export-block"
"horizontal-rule" "comment" "table-row" "headline"})
(def recursive-object-types
#{"citation" "footnote-reference" "superscript" "table-cell" "link"
"italic" "citation-common-prefix" "subscript" "citation-prefix"
"citation-common-suffix" "strike-through" "citation-reference"
"bold" "underline"})
(def object-types
#{"line-break" "citation-suffix" "statistics-cookie" "timestamp"
"text" "verbatim" "citation-key" "export-snippet" "latex-fragment"
"entity" "code"})
(s/def ::greater-element-type greater-element-types)
(s/def ::element-type element-types)
(s/def ::object-type (set/union recursive-object-types object-types))
(s/def ::recursive-object-type recursive-object-types)
(s/def ::contents-begin nat-int?)
(s/def ::contents-end nat-int?)
(defmulti node-spec :type)
(defn- unimplemented-spec [x]
(lr/warnf "unimplemented method for %s" x)
(s/with-gen any?
(constantly (gen/return {}))))
(defmethod node-spec :default [x] (unimplemented-spec x))
(def ^:private nfe
"NFE — “no further expectations.” Used in sub-specs of `::element`
et al. for elements with no additional structure beyond that
provided by their parents."
(s/with-gen any?
(constantly (gen/return {}))))
(s/def ::node
(s/multi-spec node-spec :type))
(s/def :object/type ::object-type)
(s/def ::object
(s/keys :req-un [:object/type]))
(s/def :element/children (s/coll-of ::object :kind vector?))
(s/def :element/type ::element-type)
(s/def ::element
(s/keys :opt-un [::contents-begin ::contents-end]
:req-un [:element/children :element/type]))
(s/def :greater-element/children
(s/coll-of (s/merge
(dict {:type (set/union greater-element-types
element-types)})
::node)
:kind vector?))
(s/def :greater-element/type ::greater-element-type)
(s/def ::greater-element
(s/keys :req-un [::contents-begin ::contents-end
:greater-element/children
:greater-element/type]))
(s/def :recursive-object/children
(s/coll-of
(s/merge
(dict {:type ::object-type})
::node)
:kind vector?))
(s/def :recursive-object/type ::object-type)
(s/def ::recursive-object
(s/keys :opt-un [::contents-begin ::contents-end]
:req-un [:recursive-object/children
:recursive-object/type]))
(comment
(use 'net.deertopia.doerg.repl)
(def doc (-> "test/net/deertopia/doerg/element_test/paragraph-with-multiple-latex.org"
slurp
(read-string :post-processors
[gather-first-section])))
(s/explain ::node doc)
(binding [s/*recursion-limit* 1]
(gen/generate (s/gen ::node)))
(sp/select [postorder-walker (sp/must :children)
(sp/continuous-subseqs)]
doc))
(s/def ::todo-keyword string?)
(s/def ::priority string?)
(s/def ::commented boolean?)
(s/def ::level nat-int?)
(s/def ::tags (s/coll-of string? :kind vector?))
;;; Specs (specific elements)
(def ^:private string-value (dict {:value string?}))
(defmethod node-spec "text" [_] (s/merge ::object string-value))
(defmethod node-spec "verbatim" [_] (s/merge ::object string-value))
(defmethod node-spec "code" [_] (s/merge ::object string-value))
(defmethod node-spec "bold" [_] ::recursive-object)
(defmethod node-spec "italic" [_] ::recursive-object)
(defmethod node-spec "headline" [_]
(s/merge ::element
(dict {:todo-keyword (s/nilable ::todo-keyword)
:priority (s/nilable ::priority)
:level ::level
:commented ::commented
:raw-value string?
:tags ::tags})))
(defmethod node-spec "org-data" [_]
::greater-element)
(defmethod node-spec "section" [_]
::greater-element)
File renamed without changes.
+5
View File
@@ -0,0 +1,5 @@
(ns net.deertopia.doerg.main
(:gen-class))
(defn -main []
(println "hello from doerg"))
@@ -12,8 +12,7 @@
[net.deertopia.doerg.tex :as tex] [net.deertopia.doerg.tex :as tex]
[net.deertopia.doerg.tex.temml :as tex-temml] [net.deertopia.doerg.tex.temml :as tex-temml]
[clojure.zip :as z] [clojure.zip :as z]
[babashka.fs :as fs] [babashka.fs :as fs]))
[clojure.edn :as edn]))
;;; Top-level API ;;; Top-level API
@@ -40,7 +39,7 @@
#(do (assert (element/of-type? % "keyword")) #(do (assert (element/of-type? % "keyword"))
(:key %))) (:key %)))
(def ^:dynamic ^:private *opts*) (def ^:dynamic ^:private *document-info*)
(declare ^:private gather-footnotes render-renderer-error (declare ^:private gather-footnotes render-renderer-error
view-children-as-seq render-tex-snippets) view-children-as-seq render-tex-snippets)
@@ -63,31 +62,23 @@
(defn org-document (defn org-document
"Recursively render an Org-mode document to Hiccup." "Recursively render an Org-mode document to Hiccup."
[doc & {:as opts :keys [postamble header title]}] [doc]
(binding [*opts* opts] (tex-temml/binding-worker
(tex-temml/binding-worker (let [rendered (-> doc gather-footnotes render-tex-snippets
(let [rendered (-> doc gather-footnotes render-tex-snippets org-element-recursive)]
org-element-recursive)] [:html
[:html [:head
[:head [:title "org document"]
(when title doerg-html/head]
[:title title]) [:body {:lang default-language}
doerg-html/head] [:article
[:body {:lang default-language} rendered]]])))
(when header
[:header header])
[:article
rendered
(when postamble
[:footer
[:hr]
postamble])]]]))))
(defn to-html (defn to-html
"Read `f` with `slurp` as an Org document and return a string of "Read `f` with `slurp` as an Org document and return a string of
rendered HTML. See `org-document` for opts." rendered HTML."
[f & {:as opts}] [f]
(str (hiccup/html {} (-> f slurp element/read-string (org-document opts))))) (str (hiccup/html {} (-> f slurp element/read-string org-document))))
;;; Further dispatching on `org-element` ;;; Further dispatching on `org-element`
@@ -111,19 +102,6 @@
(sp/view #(update % :children seq)) (sp/view #(update % :children seq))
sp/STAY)) sp/STAY))
(defn center [& es]
[:div.center es])
(defn doerg-attrs [e]
(->> e :affiliated :attr_doerg (str/join " ")
(format "{%s}") edn/read-string))
(defn em [x]
(format "%.4fem" x))
(defn wrap-if [x c f]
(if c (f x) x))
(defn- contains-footnote-refs? [node] (defn- contains-footnote-refs? [node]
(some #(element/of-type? % "footnote-reference") (some #(element/of-type? % "footnote-reference")
(:children node))) (:children node)))
@@ -276,14 +254,11 @@
(defmethod org-element "bold" [{:keys [children]}] (defmethod org-element "bold" [{:keys [children]}]
[:b children]) [:b children])
(defmethod org-element "strike-through" [{:keys [children]}]
[:s children])
(defmethod org-element "subscript" [{:keys [children]}] (defmethod org-element "subscript" [{:keys [children]}]
[:sub children]) [:sub children])
(defmethod org-element "superscript" [{:keys [children]}] (defmethod org-element "superscript" [{:keys [children]}]
[:sup children]) [:super children])
(defmethod org-element "italic" [{:keys [children]}] (defmethod org-element "italic" [{:keys [children]}]
[:em children]) [:em children])
@@ -354,52 +329,23 @@
[:span.latex-fragment.display-math [:span.latex-fragment.display-math
(-> e ::rendered deref)]) (-> e ::rendered deref)])
(defmethod org-element "example-block" [{:keys [value] :as e}] (defmethod org-element "example-block" [{:keys [value]}]
(let [{:keys [center? alt scale img?]} (doerg-attrs e)] [:pre value])
(-> [:pre (merge (and img? {:role "img"
:aria-label alt
:title alt})
(and scale {:style {:font-size (em scale)}}))
value]
(wrap-if center? center))))
(defmethod org-element "src-block" [{:keys [value]}] (defmethod org-element "src-block" [{:keys [value]}]
[:pre [:code value]]) [:pre [:code value]])
(defn- split-quote-block-children [children] (defmethod org-element "quote-block" [{:keys [children]}]
(match (split-with #(not= % [:hr]) children) [:blockquote children])
[x ([[:hr] & ys] :seq)] [x (strip-paragraphs ys)]
x x))
(defmethod org-element "quote-block" [{:keys [children] :as e}]
(let [{:keys [epigraph?]} (doerg-attrs e)
[content footer] (split-quote-block-children children)]
(-> [:blockquote
content
(when footer
[:footer footer])]
(wrap-if epigraph? (fn [c] [:div.epigraph c])))))
(defmethod org-element "horizontal-rule" [_]
[:hr])
(defmethod org-element "comment" [_] nil) (defmethod org-element "comment" [_] nil)
(defmethod org-keyword "TITLE" [{:keys [value]}] (defmethod org-keyword "TITLE" [{:keys [value]}]
[:h1 value]) [:h1 value])
(defmethod org-keyword "SUBTITLE" [{:keys [value]}]
[:p.subtitle value])
(defmethod org-keyword "LATEX_COMPILER" [_] nil) (defmethod org-keyword "LATEX_COMPILER" [_] nil)
(defmethod org-keyword "LATEX_HEADER" [_] nil) (defmethod org-keyword "LATEX_HEADER" [_] nil)
;; TODO: A bunch of things I'd rather hide for now, but should be
;; implemented eventually.
(defmethod org-keyword "FILETAGS" [_] nil)
(defmethod org-element "planning" [_] nil)
(defmethod org-element "timestamp" [_] nil)
;; Not sure how to deal with this one yet. ;; Not sure how to deal with this one yet.
(defmethod org-keyword "AUTHOR" [_] nil) (defmethod org-keyword "AUTHOR" [_] nil)
@@ -35,7 +35,7 @@
(fs/create-dirs dest) (fs/create-dirs dest)
(fs/create-dirs resource-dir) (fs/create-dirs resource-dir)
(force-create-sym-link (fs/file resource-dir "ibm-plex-web") (force-create-sym-link (fs/file resource-dir "ibm-plex-web")
(-> cfg/*cfg* ::cfg/ibm-plex-web)) (-> cfg/*cfg* ::doerg/ibm-plex-web))
(doseq [x #{"Temml-Plex.css" "tuftesque.css" "deerstar.css"}] (doseq [x #{"Temml-Plex.css" "tuftesque.css" "deerstar.css"}]
(force-create-sym-link (force-create-sym-link
(fs/file resource-dir x) (fs/file resource-dir x)
File renamed without changes.
@@ -83,14 +83,14 @@
acc)))) acc))))
(defn- invoke-latex [& {:keys [file output-dir]}] (defn- invoke-latex [& {:keys [file output-dir]}]
(let [latex (-> cfg/*cfg* ::cfg/latex)] (let [latex (-> cfg/*cfg* ::doerg/latex)]
(invoke (invoke
{:dir output-dir} {:dir output-dir}
latex "-no-pdf" "-interaction" "nonstopmode" latex "-no-pdf" "-interaction" "nonstopmode"
"-output-directory" output-dir file))) "-output-directory" output-dir file)))
(defn- invoke-dvisvgm [& {:keys [file output-dir]}] (defn- invoke-dvisvgm [& {:keys [file output-dir]}]
(let [dvisvgm (-> cfg/*cfg* ::cfg/dvisvgm)] (let [dvisvgm (-> cfg/*cfg* ::doerg/dvisvgm)]
(invoke (invoke
{:dir output-dir} {:dir output-dir}
dvisvgm "--page=1-" "--optimize" "--clipjoin" dvisvgm "--page=1-" "--optimize" "--clipjoin"
@@ -25,7 +25,7 @@
fs/file)) fs/file))
(defn worker [] (defn worker []
(let [doerg-temml-worker (-> cfg/*cfg* ::cfg/doerg-temml-worker)] (let [doerg-temml-worker (-> cfg/*cfg* ::doerg/doerg-temml-worker)]
(when (or (not (fs/exists? prelude-file)) (when (or (not (fs/exists? prelude-file))
(zero? (fs/size prelude-file))) (zero? (fs/size prelude-file)))
(-> "net/deertopia/doerg/prelude.tex" (-> "net/deertopia/doerg/prelude.tex"
View File
File renamed without changes.
Generated
+3 -3
View File
@@ -111,11 +111,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1774386573, "lastModified": 1760038930,
"narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=", "narHash": "sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9", "rev": "0b4defa2584313f3b781240b29d61f6f9f7e0df3",
"type": "github" "type": "github"
}, },
"original": { "original": {
+40 -49
View File
@@ -14,19 +14,16 @@
"x86_64-linux" "x86_64-linux"
]; ];
overlays = [
inputs.sydpkgs.overlays.default
clj-nix.overlays.default
];
each-system = f: nixpkgs.lib.genAttrs supportedSystems (system: f rec { each-system = f: nixpkgs.lib.genAttrs supportedSystems (system: f rec {
pkgs = import nixpkgs { pkgs = import nixpkgs {
inherit system; inherit system;
overlays = overlays ++ [ overlays = [
self.overlays.default inputs.sydpkgs.overlays.default
]; clj-nix.overlays.default
}; self.overlays.default
inherit (pkgs) lib; ];
};
inherit (pkgs) lib;
inherit system; inherit system;
}); });
in { in {
@@ -34,51 +31,45 @@
_pkgs = each-system ({ pkgs, ... }: pkgs); _pkgs = each-system ({ pkgs, ... }: pkgs);
packages = each-system ({ pkgs, ... }: { packages = each-system ({ pkgs, ... }: {
inherit (pkgs) doerg doerg-parser doerg-temml-worker; inherit (pkgs) publisher doerg doerg-parser doerg-temml-worker;
default = pkgs.doerg; default = pkgs.publisher;
}); });
overlays.default = final: prev: overlays.default = final: prev:
let let graal = x: final.mkGraalBin { cljDrv = x; };
# is this really the correct way to satisfy our vendored = final.callPackage ./vendor {};
# dependencies? lmfao in {
final' = final.appendOverlays overlays; inherit (vendored) ibm-plex-web;
graal = x: final'.mkGraalBin { cljDrv = x; }; publisher = final.callPackage ./publisher {};
in { doerg = final.callPackage ./doerg {};
ibm-plex-web = final'.callPackage ./ibm-plex-web.nix {}; doerg-parser = final.callPackage ./doerg/doerg-parser {};
doerg = final'.callPackage ./. {}; doerg-temml-worker = final.callPackage ./doerg/doerg-temml-worker {};
doerg-parser = final'.callPackage ./doerg-parser {}; };
doerg-temml-worker = final'.callPackage ./doerg-temml-worker {};
our-tex = final'.callPackage ./our-tex.nix {};
};
checks = each-system ({ pkgs, system, ... }: { checks = each-system ({ pkgs, system, ... }: {
build-all = build-all =
pkgs.linkFarmFromDrvs pkgs.linkFarmFromDrvs
"all" "all"
(pkgs.lib.attrValues self.packages.${system}); (pkgs.lib.attrValues self.packages.${system});
}); });
nixosModules.default = import ./module.nix { inherit self; };
devShells = each-system ({ pkgs, system, ... }: { devShells = each-system ({ pkgs, system, ... }: {
default = pkgs.mkShell { default = pkgs.mkShell {
inputsFrom = [ inputsFrom = [
pkgs.doerg pkgs.doerg
pkgs.doerg-parser pkgs.doerg-parser
pkgs.doerg-temml-worker pkgs.doerg-temml-worker
]; ];
packages = with pkgs; [ packages = with pkgs; [
clojure-lsp clojure-lsp
doerg-parser doerg-parser
doerg-temml-worker doerg-temml-worker
zprint zprint
clojure clojure
babashka babashka
sqlite-web sqlite-web
pkgs.doerg.test-emacs ];
]; };
};
}); });
}; };
} }
-107
View File
@@ -1,107 +0,0 @@
{ self, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.services.doerg;
doerg-config = pkgs.writeText "doerg-config.edn" ''
#:net.deertopia.doerg.config
{:org-roam-db-path "${cfg.databasePath}"
:state-directory "${cfg.stateDir}"
:port ${builtins.toString cfg.port}}
'';
inherit (lib) types;
org-roam-db-sync = pkgs.writeText "org-roam-db-sync.el" ''
#!/usr/bin/env -S emacs -Q -x
(require 'org-roam)
(setq org-roam-directory (expand-file-name (car command-line-args-left)))
(setq org-roam-db-location (expand-file-name (cadr command-line-args-left)))
(org-roam-db-sync)
'';
in {
options.services.doerg = {
enable = lib.mkEnableOption "Doerg";
org-roam-db-sync.enable = lib.mkEnableOption "Org-roam db sync";
port = lib.mkOption {
default = 21984;
type = lib.types.port;
description = ''
The port on which Doerg will listen.
'';
};
stateDir = lib.mkOption {
type = types.path;
default = "/var/lib/private/doerg";
description = "Daemon's state directory.";
};
orgDir = lib.mkOption {
type = types.path;
description = "Org roam directory.";
};
package = lib.mkPackageOption pkgs "doerg" {};
databasePath = lib.mkOption {
type = types.path;
description = "Org roam database path";
default = cfg.orgDir + "org-roam.db";
};
openFirewall = lib.mkOption {
type = types.bool;
description = "Open doerg ports?";
default = false;
};
};
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [ self.overlays.default ];
systemd.services.org-roam-db-sync = lib.mkIf cfg.org-roam-db-sync.enable {
script = lib.escapeShellArgs [
(lib.getExe cfg.package.test-emacs)
"-Q" "-x" org-roam-db-sync cfg.orgDir cfg.databasePath
];
serviceConfig = {
Type = "oneshot";
ReadOnlyBindPaths = [
cfg.orgDir
];
};
};
systemd.timers.org-roam-db-sync = lib.mkIf cfg.org-roam-db-sync.enable {
unitConfig.StopWhenUnneeded = true;
timerConfig = {
OnActiveSec = "1h";
RandomizedDelaySec = "30m";
Persistent = true;
};
};
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
cfg.port
];
systemd.services.doerg = {
after = [ "network-online.target" ];
wants = [ "network-online.target" "org-roam-db-sync.timer" ];
wantedBy = [ "multi-user.target" ];
environment.DOERG_CONFIG = doerg-config;
serviceConfig = {
# WorkingDirectory = cfg.stateDir;
StateDirectory = "doerg";
ExecStart = lib.getExe cfg.package;
DynamicUser = true;
ProtectSystem = "strict";
PrivateTmp = true;
BindReadOnlyPaths = [
cfg.orgDir
# cfg.databasePath
"/nix"
];
};
};
};
}
+12
View File
@@ -0,0 +1,12 @@
((nil
. ((cider-clojure-cli-aliases . ":dev:test")
;; (eval
;; . (progn
;; (defun start-deertopia-server ()
;; (let ((n (cider-format-connection-params "%j" cider-launch-params)))
;; (when (equal n "net-deertopia")
;; (cider-interactive-eval
;; "(do (require '[net.deertopia.publisher.server :as server])
;; (server/start!))"))))
;; (add-hook 'cider-connected-hook #'start-deertopia-server)))
)))
+27
View File
@@ -0,0 +1,27 @@
{ mkCljBin
, doerg
, babashka
}:
mkCljBin {
name = "net.deertopia/publisher";
version = "0.1.0";
projectSrc = ./.;
lockfile = ../deps-lock.json;
main-ns = "net.deertopia.publisher.main";
buildInputs = [
doerg
];
nativeBuildInputs = [
babashka
];
postPatch = ''
mv deps.edn deps.edn.old
bb -cp . -m override-deps < deps.edn.old > deps.edn \
net.deertopia/doerg '{:local/root "${doerg.lib}/${doerg.name}.jar"}'
'';
doCheck = true;
checkPhase = ''
clojure -M:test
'';
}
+21
View File
@@ -0,0 +1,21 @@
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/tools.logging {:mvn/version "1.3.0"}
hiccup/hiccup {:mvn/version "2.0.0-RC4"}
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"}
babashka/fs {:mvn/version "0.5.24"}
org.clojure/core.match {:mvn/version "1.1.0"}
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1070"}
org.xerial/sqlite-jdbc {:mvn/version "3.47.1.0"}
cheshire/cheshire {:mvn/version "6.1.0"}
org.clojure/test.check {:mvn/version "1.1.3"}
net.deertopia/doerg {:local/root "../doerg"}
metosin/reitit {:mvn/version "0.10.1"}
http-kit/http-kit {:mvn/version "2.8.0"}
instaparse/instaparse {:mvn/version "1.5.0"}
aero/aero {:mvn/version "1.1.6"}}
:paths ["src" "resources"]
:aliases
{:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
:extra-paths ["test"]
:main-opts ["-m" "kaocha.runner"]}
:dev {:extra-paths ["dev"]}}}
+24
View File
@@ -0,0 +1,24 @@
(ns user
(:require [net.deertopia.publisher.server :as server]
[net.deertopia.doerg :as-alias doerg]
[net.deertopia.publisher :as-alias publisher]
[net.deertopia.doerg.config :as doerg-config]
[net.deertopia.publisher.config :as publisher-config]
[babashka.fs :as fs]))
(doerg-config/load-config! #'doerg-config/*cfg*
::doerg/config
doerg-config/sources
:profile :dev)
(doerg-config/load-config! #'publisher-config/*cfg*
::publisher/config
publisher-config/sources
:profile :dev)
(when (not= :running (server/status))
(server/start!))
(defn invalidate-html-cache! []
(fs/delete-tree (server/html-dir))
nil)
+56
View File
@@ -0,0 +1,56 @@
;;; -*- mode:clojure -*-
;;;
;;; USAGE:
;;;
;;; bb -cp . -m override-deps -- [DEP-NAME INFO]…
;;;
;;; This script takes a series of deps substitutions on the command
;;; line, applies them to a deps.edn file (read on stdin), and spits
;;; the result to stdout.
;;;
;;; It is used to build the Doerg server with Nix, since Clj-nix does
;;; not resolve the local deps itself.
(ns override-deps
(:require [rewrite-clj.zip :as z]
[babashka.fs :as fs]
[clojure.edn :as edn]))
(defn apply-overrides [zloc overrides]
(loop [os (seq overrides)
loc zloc]
(if-some [[[k v] & xs] os]
(do (binding [*out* *err*]
(printf "override dep %s with %s\n"
(pr-str k) (pr-str v)))
(recur xs (z/assoc loc k v)))
loc)))
(defn args->overrides [args]
(assert (even? (count args)))
(->> args (map edn/read-string) (apply hash-map)))
(defn -main [& args]
(let [zloc (-> (slurp *in*) z/of-string)
overrides (args->overrides args)]
(-> zloc
z/down
(z/find-value :deps) z/right
(apply-overrides overrides)
z/root-string
print)))
(comment
"Example overrides"
(def overrides '{net.deertopia/doerg "blah!!!!"
ring/ring-defaults {:mvn/version "xxxxx"}}))
(comment
"Behaviour of `args->overrides`."
(= (args->overrides ["net.deertopia/doerg" "{:mvn/version \"abc\"}"
"ring/ring-defaults" "{:local/root \"/path/to/jar\"}"])
'{ring/ring-defaults {:local/root "/path/to/jar"}
net.deertopia/doerg {:mvn/version "abc"}}))
@@ -0,0 +1,8 @@
#:net.deertopia.publisher
{:state-directory #join [#or [#env XDG_STATE_HOME
#envf ["%s/.local/share" HOME]]
"/doerg-publisher"]
:org-roam-db-path
#profile {:default #join [#env HOME "/.cache/emacs/org-roam.db"]
:test #file "../../../../test/net/deertopia/publisher/roam-test.db"}
:port #profile {:default 8080}}
@@ -1,4 +1,4 @@
(ns net.deertopia.doerg.cached-file (ns net.deertopia.publisher.cached-file
(:require [babashka.fs :as fs])) (:require [babashka.fs :as fs]))
(defn newer-than? (defn newer-than?
@@ -18,10 +18,8 @@
calling `compute` with no arguments only if stale? is logical true." calling `compute` with no arguments only if stale? is logical true."
[& {:keys [file stale? compute]}] [& {:keys [file stale? compute]}]
(when (or (not *use-cache?*) stale?) (when (or (not *use-cache?*) stale?)
(let [r (compute) (let [r (compute)]
dir (fs/parent file)]
(assert (string? r)) (assert (string? r))
(when-not (fs/exists? dir) (fs/create-dirs (fs/parent file))
(fs/create-dirs dir))
(spit file r))) (spit file r)))
file) file)
@@ -0,0 +1,28 @@
(ns net.deertopia.publisher.config
(:require [babashka.fs :as fs]
[clojure.spec.alpha :as s]
[net.deertopia.doerg.config :as doerg-config]
[net.deertopia.doerg :as-alias doerg]
[net.deertopia.publisher :as-alias p]
[aero.core :as aero]
[clojure.java.io :as io]))
(s/def ::p/config
(s/keys :req [::p/state-directory
::p/org-roam-db-path]))
(s/def ::p/state-directory ::doerg/file)
(s/def ::p/org-roam-db-path ::doerg/file)
(def sources
[;; Default config.
(io/resource "net/deertopia/publisher/default-config.edn")
;; Defaults set at build time, if any.
(io/resource "net/deertopia/publisher/extra-config.edn")
;; Config set at runtime.
(System/getenv "DOERG_PUBLISHER_CONFIG")])
(def default
(doerg-config/read-config ::p/config sources))
(def ^:dynamic *cfg* default)
@@ -1,4 +1,4 @@
(ns net.deertopia.doerg.elisp (ns net.deertopia.publisher.elisp
(:require [clojure.core.match :refer [match]] (:require [clojure.core.match :refer [match]]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
@@ -6,7 +6,7 @@
(:refer-clojure :exclude [print read read-string])) (:refer-clojure :exclude [print read read-string]))
(ip/defparser read* (ip/defparser read*
(io/resource "net/deertopia/doerg/elisp/grammar")) (io/resource "net/deertopia/publisher/elisp/grammar"))
(defn- transform-string [s] (defn- transform-string [s]
(let [s* (loop [s (seq s) (let [s* (loop [s (seq s)
@@ -106,9 +106,9 @@
(defn print [x] (defn print [x]
;; TODO: this is really not how it should be done lol. at the ;; TODO: this is really not how it should be done lol. at the
;; moment, `print` is only used in `net.deertopia.doerg.roam` ;; moment, `print` is only used in `net.deertopia.publisher.roam`
;; and only to serialise uuids, so it's not a /massive/ priority. ;; and only to serialise uuids, so it's not a /massive/ priority.
(cond (or (string? x) (uuid? x)) (str \" x \") (cond (string? x) (str \" x \")
:else (throw (ex-info "`print` is unimplemented lol" :else (throw (ex-info "`print` is unimplemented lol"
{:x x})))) {:x x}))))
@@ -0,0 +1,7 @@
(ns net.deertopia.publisher.main
(:require [net.deertopia.doerg.main :as doerg]
[net.deertopia.publisher.server :as server])
(:gen-class))
(defn -main []
(server/start!))
@@ -1,8 +1,9 @@
(ns net.deertopia.doerg.roam (ns net.deertopia.publisher.roam
(:require [babashka.fs :as fs] (:require [babashka.fs :as fs]
[net.deertopia.doerg.config :as cfg] [net.deertopia.publisher.config :as cfg]
[net.deertopia.doerg.elisp :as elisp] [net.deertopia.publisher.elisp :as elisp]
[net.deertopia.doerg.slug :as slug] [net.deertopia.publisher.slug :as slug]
[net.deertopia.publisher :as-alias publisher]
[next.jdbc :as sql]) [next.jdbc :as sql])
(:import (java.util UUID))) (:import (java.util UUID)))
@@ -11,10 +12,13 @@
(defonce ^:dynamic *use-db-cache?* true) (defonce ^:dynamic *use-db-cache?* true)
(defn ds [] (def db-path (-> cfg/*cfg* ::publisher/org-roam-db-path
(sql/get-datasource fs/expand-home str))
{:dbtype "sqlite"
:dbname (-> cfg/*cfg* ::cfg/org-roam-db-path str)})) (def db {:dbtype "sqlite"
:dbname db-path})
(def ds (sql/get-datasource db))
;;; Elisp sexp (de)serialisation ;;; Elisp sexp (de)serialisation
@@ -26,23 +30,21 @@
(-> node :id slug/from-uuid)) (-> node :id slug/from-uuid))
(defn- print-id [node] (defn- print-id [node]
(-> node id elisp/print)) (-> node id print))
;;; Node ;;; Node
(defrecord Node [id cache]) (defrecord Node [id cache])
(defn uuid-exists? [uuid] (defn- uuid-exists? [uuid]
(sql/execute-one! (ds) (sql/execute-one! ds
["select 1 from nodes where id = ? limit 1" ["select 1 from nodes where id = ? limit 1"
(-> uuid str elisp/print)])) (-> uuid str elisp/print)]))
(defn make-node (defn make-node [uuid]
([uuid] (make-node uuid {})) (and (uuid-exists? uuid)
([uuid props] (->Node uuid (atom {}))))
(and (uuid-exists? uuid)
(->Node uuid (atom props)))))
(defn- fetch-with-cache [node field fetch] (defn- fetch-with-cache [node field fetch]
(if *use-db-cache?* (if *use-db-cache?*
@@ -57,20 +59,11 @@
node :org-file node :org-file
(fn [node] (fn [node]
(when-some [r (sql/execute-one! (when-some [r (sql/execute-one!
(ds) ds
["select file from nodes where id = ?" ["select file from nodes where id = ?"
(-> node :id str elisp/print)])] (-> node :id str elisp/print)])]
(-> r :nodes/file elisp/read-string))))) (-> r :nodes/file elisp/read-string)))))
(defn title [node]
(fetch-with-cache
node :title
#(when-some [r (sql/execute-one!
(ds)
["select title from nodes where id = ?"
(print-id %)])]
(-> r :nodes/title elisp/read-string))))
(defprotocol GetNode (defprotocol GetNode
(get-node [this] (get-node [this]
"Return the node associated with `this` or nil.")) "Return the node associated with `this` or nil."))
@@ -85,7 +78,7 @@
java.util.UUID java.util.UUID
(get-node [this] (get-node [this]
(make-node this)) (make-node this))
net.deertopia.doerg.slug.Slug net.deertopia.publisher.slug.Slug
(get-node [this] (get-node [this]
(-> this slug/to-uuid make-node)) (-> this slug/to-uuid make-node))
Node Node
@@ -97,7 +90,7 @@
(fetch-with-cache (fetch-with-cache
node :title node :title
#(do (println "fetch") #(do (println "fetch")
(sql/execute-one! (ds) ["select title from nodes where id = ?" (sql/execute-one! ds ["select title from nodes where id = ?"
(elisp/print (:id %))])))) (elisp/print (:id %))]))))
@@ -107,7 +100,7 @@
(fetch-with-cache (fetch-with-cache
node :level node :level
#(-> (sql/execute-one! #(-> (sql/execute-one!
(ds) ["select level from nodes where id = ?" ds ["select level from nodes where id = ?"
(print-id %)]) (print-id %)])
:nodes/level))) :nodes/level)))
@@ -118,7 +111,7 @@
(fetch-with-cache (fetch-with-cache
node :file node :file
#(-> (sql/execute-one! #(-> (sql/execute-one!
(ds) ["select file from nodes where id = ?" ds ["select file from nodes where id = ?"
(print-id %)]) (print-id %)])
:nodes/file :nodes/file
elisp/read-string))) elisp/read-string)))
@@ -127,35 +120,31 @@
(fetch-with-cache (fetch-with-cache
node :properties node :properties
#(-> (sql/execute-one! #(-> (sql/execute-one!
(ds) ["select properties from nodes where id = ?" ds ["select properties from nodes where id = ?"
(print-id %)]) (print-id %)])
:nodes/properties :nodes/properties
elisp/read-alist))) elisp/read-alist)))
(defn public? [node] (defn public? [node]
(some-> (properties node) (get "DEERTOPIAVISIBILITY") (= "public"))) (-> node properties (get "DEERTOPIAVISIBILITY") (= "public")))
(defn get-public-node [node]
(when-let [n (get-node node)]
(when (public? n) n)))
(defn graph-visible? [node] (defn graph-visible? [node]
(#{"public" "graphonly"} (#{"public" "graphonly"}
(some-> (properties node) (get "DEERTOPIAVISIBILITY")))) (-> node properties (get "DEERTOPIAVISIBILITY"))))
(defn backlinks (defn backlinks
"Returns a collection of nodes linking to `node`." "Returns a collection of maps {:id …, :title …}."
[node] [node]
(for [{id :nodes/id title :nodes/title} (for [{id :nodes/id, title :nodes/title}
(sql/execute! (ds) ["select distinct nodes.id, nodes.title from links (sql/execute! ds ["select distinct nodes.id, nodes.title from links
inner join nodes inner join nodes
on nodes.id = links.source on nodes.id = links.source
where links.dest = ?" where links.dest = ?"
(elisp/print (str (:id node)))]) (elisp/print (:id node))])
:let [id' (elisp/read-string id)] :let [id (elisp/read-string id)]
:when (some-> (-> id' parse-uuid get-node) :when (graph-visible? (get-node (UUID/fromString id)))]
public?)] {:id id
(make-node id' {:title (elisp/read-string title)}))) :title (elisp/read-string title)}))
;;; Graph support ;;; Graph support
@@ -167,9 +156,9 @@
(-> uuid parse-uuid get-node graph-visible?)) (-> uuid parse-uuid get-node graph-visible?))
(defn get-graph [] (defn get-graph []
(let [nodes (sql/execute! (ds) ["select id, title from nodes"]) (let [nodes (sql/execute! ds ["select id, title from nodes"])
links (sql/execute! links (sql/execute!
(ds) ds
["select n1.id as source, nodes.id as target from ["select n1.id as source, nodes.id as target from
((nodes as n1) join links on n1.id = links.source) ((nodes as n1) join links on n1.id = links.source)
join (nodes as n2) on links.dest = nodes.id join (nodes as n2) on links.dest = nodes.id
@@ -1,12 +1,12 @@
(ns net.deertopia.doerg.server (ns net.deertopia.publisher.server
(:require [clojure.pprint :refer [pprint]] (:require [clojure.pprint :refer [pprint]]
[clojure.tools.logging :as l] [clojure.tools.logging :as l]
[hiccup2.core :as hiccup] [hiccup2.core :as hiccup]
[net.deertopia.doerg.html :as doerg-html] [net.deertopia.doerg.html :as doerg-html]
[net.deertopia.doerg.config :as-alias cfg] [net.deertopia.publisher :as-alias publisher]
[net.deertopia.doerg.slug :as slug] [net.deertopia.publisher.slug :as slug]
[net.deertopia.doerg.config :as cfg] [net.deertopia.publisher.config :as cfg]
[net.deertopia.doerg.roam :as roam] [net.deertopia.publisher.roam :as roam]
[org.httpkit.server :as http] [org.httpkit.server :as http]
[reitit.coercion] [reitit.coercion]
[reitit.coercion.spec] [reitit.coercion.spec]
@@ -20,10 +20,12 @@
[reitit.dev.pretty] [reitit.dev.pretty]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[net.deertopia.doerg.render :as doerg-render] [net.deertopia.doerg.render :as doerg-render]
[net.deertopia.doerg.cached-file :as cached-file] [net.deertopia.publisher.cached-file :as cached-file]
[babashka.fs :as fs] [babashka.fs :as fs]
[aero.core :as aero] [aero.core :as aero]
[clojure.string :as str])) [clojure.string :as str]
[net.deertopia.doerg :as-alias doerg]
[net.deertopia.doerg.config :as doerg-config]))
;;; Routes ;;; Routes
@@ -47,15 +49,10 @@
(response/content-type "text/html"))) (response/content-type "text/html")))
(defn html-dir [] (defn html-dir []
(-> cfg/*cfg* ::cfg/state-directory (fs/file "html"))) (-> cfg/*cfg* ::publisher/state-directory (fs/file "html")))
(declare node-by-slug) (defn not-found [req]
(response/not-found "not found"))
;; This could infinitely loop if the 404 page can't be found. lmfao.
(defn not-found [_req]
(-> (node-by-slug {:path-params {:slug not-found-slug}})
(assoc :status 404))
#_(response/not-found "not found"))
(defn org-file->html-file [org-file] (defn org-file->html-file [org-file]
(fs/file (html-dir) (fs/file (html-dir)
@@ -64,56 +61,20 @@
(fs/strip-ext {:ext "org"}) (fs/strip-ext {:ext "org"})
(str ".html")))) (str ".html"))))
(defn slug-link [slug & contents]
[:a {:href (str "/n/" slug)}
contents])
(defmethod doerg-render/org-link "id" (defmethod doerg-render/org-link "id"
[{:keys [path raw-link children]}] [{:keys [path raw-link children]}]
[:span.org-link [:span.org-link
(slug-link (slug/from-uuid path) [:a {:href (str "/n/" (slug/from-uuid path))}
(or (seq children) raw-link))
#_[:a {:href (str "/n/" (slug/from-uuid path))}
(or (seq children) raw-link)]]) (or (seq children) raw-link)]])
(def navbar
"Hiccup element for Deertopia.net's navbar."
[:nav.navbar
[:ol.navbar-list
[:li
[:a.home-link {:href "/"}
"🦌 deertopia.net"]]
#_[:li
[:a.home-link {:href "/graph"}
"graph"]]
#_
[:li
[:a.home-link {:onclick "alert('unimplemented }:(')"}
"search"]]]])
(defn backlinks-postamble [node]
(let [backlinks (roam/backlinks node)]
(when-not (empty? backlinks)
[:section#backlinks
[:h2 "Backlinks"]
[:ul
(for [n (->> backlinks
(sort-by (comp str/lower-case roam/title)))]
[:li (slug-link (roam/slug n)
(roam/title n))])]])))
(defn node-by-slug [{{:keys [slug]} :path-params :as req}] (defn node-by-slug [{{:keys [slug]} :path-params :as req}]
(if-some [node (some-> slug slug/from-string roam/get-public-node)] (if-some [node (some-> slug slug/from-string roam/get-node)]
(let [org-file (roam/org-file node) (let [org-file (roam/org-file node)
html-file (org-file->html-file org-file)] html-file (org-file->html-file org-file)]
(cached-file/cached-file (cached-file/cached-file
:file html-file :file html-file
:stale? (cached-file/newer-than? org-file html-file) :stale? (cached-file/newer-than? org-file html-file)
:compute #(doerg-render/to-html :compute #(doerg-render/to-html org-file))
org-file
:postamble (backlinks-postamble node)
:header navbar
:title (roam/title node)))
(-> (str html-file) (-> (str html-file)
response/file-response response/file-response
(response/content-type "text/html"))) (response/content-type "text/html")))
@@ -136,11 +97,14 @@
(assoc-in [:path-params :slug] homepage-slug) (assoc-in [:path-params :slug] homepage-slug)
node-by-slug)) node-by-slug))
(def doerg-resources
#{"Temml-Plex.css" "tuftesque.css" "deerstar.css"})
(defn handle-resource [{:keys [uri]}] (defn handle-resource [{:keys [uri]}]
(if-some [[_ resource] (re-matches #"^/resource/ibm-plex-web/(.*)" uri)] (if-some [[_ resource] (re-matches #"^/resource/ibm-plex-web/(.*)" uri)]
(-> resource (-> resource
(response/file-response (response/file-response
{:root (-> cfg/*cfg* ::cfg/ibm-plex-web str)})) {:root (-> doerg-config/*cfg* ::doerg/ibm-plex-web str)}))
(-> uri (-> uri
(str/replace-first #"^/resource/" "") (str/replace-first #"^/resource/" "")
(response/resource-response (response/resource-response
@@ -191,7 +155,7 @@
(throw (IllegalStateException. "Server already started")) (throw (IllegalStateException. "Server already started"))
(do (reset! server (do (reset! server
(http/run-server (bound-fn* #'app) (http/run-server (bound-fn* #'app)
{:port (-> cfg/*cfg* ::cfg/port) {:port (-> cfg/*cfg* ::publisher/port)
:legacy-return-value? false})) :legacy-return-value? false}))
;; For some reason, the log messages are not flushed before ;; For some reason, the log messages are not flushed before
;; the JVM shuts dowm. Nevertheless, the server /does/ come ;; the JVM shuts dowm. Nevertheless, the server /does/ come
@@ -202,7 +166,7 @@
(ex-message e)) (ex-message e))
(throw e)))) (throw e))))
(l/infof "Server started on port %d" (l/infof "Server started on port %d"
(-> cfg/*cfg* ::cfg/port))))) (-> cfg/*cfg* ::publisher/port)))))
(defn status [] (defn status []
(if @server (if @server
@@ -1,4 +1,4 @@
(ns net.deertopia.doerg.slug (ns net.deertopia.publisher.slug
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[spec-tools.core :as st]) [spec-tools.core :as st])
(:import (java.nio ByteBuffer) (:import (java.nio ByteBuffer)
Binary file not shown.
@@ -1,16 +1,24 @@
(ns net.deertopia.doerg.server-test (ns net.deertopia.publisher.server-test
(:require [net.deertopia.doerg.server :as sut] (:require [net.deertopia.publisher.server :as sut]
[reitit.ring] [reitit.ring]
[clojure.test :as t] [clojure.test :as t]
[net.deertopia.doerg.config :as cfg] [net.deertopia.publisher.config :as publisher-cfg]
[net.deertopia.doerg.roam :as roam] [net.deertopia.doerg.config :as doerg-cfg]
[babashka.fs :as fs] [net.deertopia.publisher :as-alias publisher]
[clojure.java.io :as io] [net.deertopia.doerg :as-alias doerg]
[net.deertopia.doerg.roam-test :refer [test-db-fixture]] [net.deertopia.publisher.roam :as roam]))
[net.deertopia.doerg.config-test :refer [test-config-fixture]]))
(t/use-fixtures (t/use-fixtures
:once (t/join-fixtures [test-config-fixture test-db-fixture])) :once (fn [f]
(binding [doerg-cfg/*cfg*
(doerg-cfg/read-config
::doerg/config doerg-cfg/sources
:profile :test)
publisher-cfg/*cfg*
(doerg-cfg/read-config
::publisher/config publisher-cfg/sources
:profile :test)]
(f))))
(defn with-server [f] (defn with-server [f]
(let [was-already-running? (= :running (sut/status))] (let [was-already-running? (= :running (sut/status))]
@@ -24,8 +32,6 @@
(sut/app {:request-method :get (sut/app {:request-method :get
:uri uri})) :uri uri}))
(t/deftest server-is-running (t/deftest server-is-running
;; 서버는 벌써 시작한 다음에 이 테스트 하면 잘못됩니다. ;; 서버는 벌써 시작한 다음에 이 테스트 하면 잘못됩니다.
;; (assert (not= :running (sut/status))) ;; (assert (not= :running (sut/status)))
@@ -33,9 +39,9 @@
(fn [] (fn []
(t/is (= :running (sut/status))) (t/is (= :running (sut/status)))
(t/is (->> (format "http://localhost:%d" (t/is (->> (format "http://localhost:%d"
(::cfg/port cfg/*cfg*)) (::publisher/port publisher-cfg/*cfg*))
slurp slurp
string?))))) string?)))))
(t/deftest get-nonexistent-node (t/deftest get-nonexistent-node
(let [slug "3Lxvxnb0QrivoU3DX-l_5w"] (let [slug "3Lxvxnb0QrivoU3DX-l_5w"]
+1
View File
@@ -0,0 +1 @@
#kaocha/v1 {}
+25
View File
@@ -0,0 +1,25 @@
{ fetchzip
, fetchurl
}:
{
ibm-plex-serif = fetchzip {
url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-serif%401.1.0/ibm-plex-serif.zip";
hash = "sha256-8ygaAeMKygYS4GCub4YUQmkh87pYHfi3s0PQ6AbaeGw=";
};
ibm-plex-math = fetchzip {
url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-math%401.1.0/ibm-plex-math.zip";
hash = "sha256-dJA6uqxa/yb3eLY4l39NeP0yIl2NfrbaRpf6h0/F7Xc=";
};
ibm-plex-sans-kr = fetchzip {
url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-sans-kr%401.1.0/ibm-plex-sans-kr.zip";
hash = "sha256-FsHxMvLlI4yylgG96DOZIdW2DYpk7I+c5QgkVIkNZIE=";
};
"d3.v7.min.js" = fetchurl {
url = "https://d3js.org/d3.v7.min.js";
hash = "sha256-8glLv2FBs1lyLE/kVOtsSw8OQswQzHr5IfwVj864ZTk=";
};
}
@@ -1,18 +0,0 @@
#:net.deertopia.doerg.config
{:ibm-plex-web #or [#env IBM_PLEX_WEB
#xdg-data-dir "ibm-plex-web"]
:latex "xelatex"
:dvisvgm "dvisvgm"
:doerg-temml-worker
#profile {:dev #file "../../../../doerg-temml-worker/index.js"
:default "doerg-temml-worker"}
:doerg-parser
#profile {:dev #file "../../../../doerg-parser/index.js"
:default "doerg-parser"}
:state-directory #join [#or [#env XDG_STATE_HOME
#envf ["%s/.local/share" HOME]]
"/doerg-server"]
:org-roam-db-path
#profile {:default #join [#env HOME "/.cache/emacs/org-roam.db"]
:test #join [#or [#env TMP "/tmp"] "/doerg-org-roam-test.db"]}
:port #profile {:default 8080}}
-8
View File
@@ -1,8 +0,0 @@
(ns net.deertopia.doerg.main
(:require [net.deertopia.doerg.server :as server]
[net.deertopia.doerg.config :as cfg])
(:gen-class))
(defn -main [& _]
(binding [cfg/*cfg* (cfg/read-config cfg/sources)]
(server/start!)))
-10
View File
@@ -1,10 +0,0 @@
{ emacsPackages
, symlinkJoin
, writeScriptBin
, lib
}:
let emacs = emacsPackages.emacsWithPackages (epkgs: [ epkgs.org-roam ]);
in writeScriptBin "test-emacs" ''
exec ${lib.getExe emacs} "$@"
''
-9
View File
@@ -1,9 +0,0 @@
(ns net.deertopia.doerg.config-test
(:require [clojure.test :as t]
[net.deertopia.doerg.config :as cfg]))
(defn test-config-fixture
"`clojure.test` fixture to run tests with the :test configuration."
[f]
(binding [cfg/*cfg* (cfg/read-config cfg/sources :profile :test)]
(f)))
@@ -1,8 +0,0 @@
#!/usr/bin/env -S emacs -Q -x
(require 'org-roam)
(setq org-roam-directory (expand-file-name (car command-line-args-left)))
(setq org-roam-db-location (expand-file-name (cadr command-line-args-left)))
(org-roam-db-sync)
@@ -1,23 +0,0 @@
:PROPERTIES:
:ID: 3c60c74e-f533-43ad-89b3-563975bf80f2
:DeertopiaVisibility: public
:END:
#+title: page not found
the page you're looking for doesn't exist, or you lack the permissions necessary to view it........
#+attr_doerg: :center? true :alt "A doe sitting and sobbing" :scale 1.4
#+begin_example
\{ / \ }/
\/ . . \/
.. \Y` `Y/ ..
\ `\|~==~|/' /
'". T T ."`
{`^' }
| |
/</3 \
|| ||
_| || |_
.,_) ) || ( (_,.
|__ /_||_\ __|
#+end_example
@@ -1,7 +0,0 @@
:PROPERTIES:
:ID: 90f23e03-f746-42cb-862f-1af2d4bde3cc
:DeertopiaVisibility: public
:END:
#+title: Category theory
*Category theory* is the mathematical study of categories and functors between them.
@@ -1,7 +0,0 @@
:PROPERTIES:
:ID: ebc5ea84-77ab-4d60-9b13-ef9160b11d1f
:DeertopiaVisibility: public
:END:
#+title: deertopia.net!!!!!!!!
homeee
@@ -1,22 +0,0 @@
:PROPERTIES:
:ID: 897bfc9d-94ce-4c58-8d21-93f13372b17b
:END:
#+title: Monomorphisms and epimorphisms
In [[id:90f23e03-f746-42cb-862f-1af2d4bde3cc][category theory]], *monomorphisms* and *epimorphisms* are types of cancellative morphisms generalising injective and surjective functions, respectively.[fn:1]
\begin{tikzcd}
% https://q.uiver.app/#q=WzAsNixbMCwwLCJYIl0sWzEsMCwiWSJdLFsyLDAsIloiXSxbMSwxLCJZIl0sWzIsMSwiWiJdLFswLDEsIlgiXSxbMCwxLCJnXzEiLDAseyJvZmZzZXQiOi0yfV0sWzAsMSwiZ18yIiwyLHsib2Zmc2V0IjoyfV0sWzEsMiwiZiIsMl0sWzMsNCwiZ18xIiwwLHsib2Zmc2V0IjotMn1dLFszLDQsImdfMiIsMix7Im9mZnNldCI6Mn1dLFs1LDMsImYiLDJdXQ==
X & Y & Z \\
X & Y & Z
\arrow["{g_1}", shift left=2, from=1-1, to=1-2]
\arrow["{g_2}"', shift right=2, from=1-1, to=1-2]
\arrow["f"', from=1-2, to=1-3]
\arrow["f"', from=2-1, to=2-2]
\arrow["{g_1}", shift left=2, from=2-2, to=2-3]
\arrow["{g_2}"', shift right=2, from=2-2, to=2-3]
\end{tikzcd}
* Footnotes
[fn:1] blahahahahah blah blah
-63
View File
@@ -1,63 +0,0 @@
(ns net.deertopia.doerg.roam-test
(:require [net.deertopia.doerg.roam :as sut]
[clojure.test :as t]
[clojure.java.io :as io]
[net.deertopia.doerg.config :as cfg]
[babashka.fs :as fs]
[babashka.process :as p]
[net.deertopia.doerg.config-test :refer [test-config-fixture]]
[next.jdbc :as sql]
[clojure.string :as str]
[net.deertopia.doerg.elisp :as elisp]
[com.rpl.specter :as sp]))
(def org-roam-directory
(fs/file "test/net/deertopia/doerg/roam-test"))
(defn org-roam-db-sync [db-file]
(let [script-file (fs/create-temp-file {:prefix "org-roam-db-sync-"
:suffix ".el"})
emacs (->> [(System/getenv "EMACS") "test-emacs" "emacs"]
(some #(some-> % fs/which)))]
(io/copy (-> "net/deertopia/doerg/org-roam-db-sync.el"
io/resource io/reader)
(fs/file script-file))
(p/shell {:out :string :err :string}
emacs "-Q" "-x" script-file org-roam-directory db-file)
(fs/delete script-file)))
(defn test-db-fixture [f]
(let [db-file (-> cfg/*cfg* ::cfg/org-roam-db-path)]
(assert (->> db-file fs/canonicalize str
(re-matches #"^/(build|tmp)/.*"))
(format "i'm scared to delete a non-test database... %s"
(str db-file)))
(fs/delete-if-exists db-file)
(org-roam-db-sync db-file)
(f)
(fs/delete db-file)))
(t/use-fixtures
:once (t/join-fixtures [test-config-fixture test-db-fixture]))
(t/deftest all-nodes-exist
(let [known-node-files (->> (fs/list-dir org-roam-directory)
(map (comp str fs/canonicalize))
(into #{}))
database-nodes
(->> (sql/execute!
(sut/ds) ["select file, id from nodes"])
(map (fn [x]
{:file (-> x :nodes/file elisp/read-string)
:id (-> x :nodes/id elisp/read-string parse-uuid)}))
(into #{}))]
(t/testing "database has a node for each file?"
(t/is (= known-node-files (sp/transform
[sp/ALL]
#(:file %)
database-nodes))))
(t/testing "each uuid exists?"
(t/is (every? (comp sut/uuid-exists? :id)
database-nodes)))))
+8
View File
@@ -0,0 +1,8 @@
{ fetchzip
, fetchurl
, callPackage
}:
{
ibm-plex-web = callPackage ./ibm-plex-web.nix {};
}
View File
File renamed without changes.