Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSR #98

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft

SSR #98

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8b68e55
playing with ssr
lilactown Apr 22, 2022
fcbab07
remove other ppls code, simple e2e stream
lilactown May 16, 2022
5e5f74e
closing tags, styles, handle deferreds, fragments
lilactown May 16, 2022
c960967
linting for helix.server.core defnc & fnc
lilactown May 16, 2022
a537b57
string keys in styles
lilactown May 16, 2022
bd78927
render all components before streaming
lilactown May 18, 2022
382a316
throwing a special error renders the fallback
lilactown May 18, 2022
1504c7d
stream template when deferred succeeds
lilactown May 19, 2022
cc6a3e8
replace suspense boundary with content
lilactown May 19, 2022
1759fef
works with multiple boundaries
lilactown May 20, 2022
dac4611
parallel fallbacks work, but waterfall does not
lilactown May 21, 2022
ddd00cb
fan out
lilactown May 28, 2022
7911bca
render-to-stream
lilactown May 28, 2022
3d80829
ssr cljc demo init
lilactown May 28, 2022
4584d1f
setup frontend
lilactown May 28, 2022
68e04c1
render key in suspense on client
lilactown May 28, 2022
fa9f0ab
fix style generation, add id prop, put boundaries when no fallback
lilactown May 28, 2022
f78fb70
hide div with suspense content
lilactown May 28, 2022
822bbb1
suspense-counter, dont double render boundary comment
lilactown May 28, 2022
42d6597
fetching during SSR works!
lilactown May 28, 2022
da55184
helix.server.hooks
lilactown May 28, 2022
ac2f21e
setting state works while hydrating!
lilactown May 28, 2022
f7175fc
suspend on both client and server
lilactown May 28, 2022
37ccd1d
fetch waterfalls work!
lilactown May 28, 2022
a953e88
hydration errors due to text nodes not being right
lilactown May 28, 2022
8677bbb
split sequential text nodes
lilactown May 28, 2022
b0e43e8
stateful component within suspense boundary
lilactown May 28, 2022
90f3fa4
remove color binding
lilactown May 30, 2022
1293e94
simplify -render! impl, with more concurrency!
lilactown May 30, 2022
9406e7f
move dfor kondo config into project config
lilactown May 30, 2022
8a89ceb
server.impl.dom
lilactown Jun 4, 2022
bfba1cb
SSR test
lilactown Jun 5, 2022
ef3a51b
fix default-checked, default-value & boolean attrs
lilactown Jun 6, 2022
9b69825
handle dangerously-set-inner-HTML
lilactown Jun 6, 2022
28c6f8f
lots
lilactown Jun 6, 2022
c6c3d39
Merge branch 'master' into server
lilactown Apr 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .clj-kondo/config.edn
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
{:config-paths ["../resources/clj-kondo.exports/lilactown/helix"]
:lint-as {devcards.core/defcard clojure.core/def}}
:lint-as {devcards.core/defcard clojure.core/def
manifold.deferred/loop clojure.core/doseq
helix.server.dom/dfor clojure.core/for}}
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
/public/benchmark/js
/public/test/js
node_modules
/.shadow-cljs
/package-lock.json
.shadow-cljs
package-lock.json
/pom.xml
/target
.clj-kondo/.cache
Expand Down
2 changes: 1 addition & 1 deletion bin/kaocha
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env sh
clojure -M:test "$@"
clojure -M:test:ci "$@"
6 changes: 6 additions & 0 deletions demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.shadow-cljs
.cpcache
resources/public/js
package-lock.json
.nrepl-port
6 changes: 6 additions & 0 deletions demo/deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{:paths ["src" "resources"] ; include resources so that we have resources on classpath
:deps {lilactown/helix {:local/root "../"}
manifold/manifold {:mvn/version "0.2.3"}
aleph/aleph {:mvn/version "0.4.7"}
thheller/shadow-cljs {:mvn/version "2.19.0"}
metosin/reitit-ring {:mvn/version "0.5.5"}}}
18 changes: 18 additions & 0 deletions demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"shadow-cljs": "^2.19.0"
},
"dependencies": {
"react": "^18.1.0",
"react-dom": "^18.1.0"
}
}
8 changes: 8 additions & 0 deletions demo/shadow-cljs.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{:deps true
:builds
{:app {:target :browser
:output-dir "resources/public/js"
:asset-path "assets/js"
:modules {:main {:entries [helix.demo.ssr]
:init-fn helix.demo.ssr/start-client!}}
:dev {:compiler-options {:output-feature-set :es6}}}}}
116 changes: 116 additions & 0 deletions demo/src/helix/demo/ssr.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
(ns helix.demo.ssr
(:require
#?@(:clj [[aleph.http :as http]
[helix.server.core :as hx :refer [defnc $ <> suspense]]
[helix.server.dom-test :as dom :refer [$d]]
[helix.server.hooks :as hooks]
[manifold.deferred :as d]
[manifold.stream :as s]
[reitit.ring :as ring]]
:cljs [[helix.core :as hx :refer [defnc $ <> suspense]]
[helix.dom :as dom :refer [$d]]
[helix.hooks :as hooks]
["react-dom/client" :as rdom]]))
;; makes shadow-cljs reload our clj ns
#?(:cljs (:require-macros [helix.demo.ssr])))


(def *cached? (atom #{}))


(defn fetch! [i]
(when-not (get @*cached? i)
(throw
#?(:clj (ex-info
"dunno"
{::hx/deferred (d/future
(Thread/sleep (* 100 i))
(swap! *cached? conj i))})
:cljs (js/Promise.
(fn [res _]
(prn :suspending)
(js/setTimeout
(fn []
(swap! *cached? conj i)
(res))
(* 100 i))))))))


(defnc counter [_]
(let [[counter set-counter] (hooks/use-state 0)]
(<> ($d "button" {:on-click #(set-counter inc)} "+") counter)))


(defnc item [{:keys [i]}]
(if (zero? (mod i 10))
(do (fetch! i)
($d "div"
"hello" i
($ counter)))
($d "div" "hi" i)))


(defnc app [{:keys [count] :or {count 10}}]
($d "div"
($ counter)
(suspense
{:fallback ($d "div" "Loading all....")}
(for [i (range 0 count)]
#_($ item {:i i :key i})
(suspense
{:fallback ($d "div" "Loading..")
:key i}
($ item {:i i}))))))


(defnc page [_]
($d "html"
($d "head"
($d "title" "Streaming test"))
($d "body" {:style {:color "blue"}}
($d "div" {:id "app"}
($ app {:count 30}))
#?(:clj "<script src=\"/assets/js/main.js\"></script>"
:cljs ($d "script" {:src "/assets/js/main.js"})))))

(def client-root)

#?(:cljs
(defn ^:dev/after-load render!
[]
(.render client-root ($ page))
#_(.render client-root ($ app))))

#?(:cljs
(defn start-client!
[]
(set! client-root (rdom/hydrateRoot js/document
($ page)))
#_(set! client-root (rdom/createRoot (js/document.getElementById "app")))
#_(render!)))



#?(:clj
(defn page-handler [req]
{:status 200
:headers {"content-type" "text/html"}
:body (doto (dom/render-to-stream ($ page))
(s/on-closed #(reset! *cached? #{})))}))


#?(:clj
(def router
(ring/router
[["/" {:get page-handler}]
["/assets/*" (ring/create-resource-handler)]])))



(comment
#?(:clj (def server (http/start-server
(ring/ring-handler router)
{:port 9090})))

#?(:clj (.close server)))

10 changes: 6 additions & 4 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{:paths ["src" "resources"]
:deps {cljs-bean/cljs-bean {:mvn/version "1.5.0"}}
:deps {cljs-bean/cljs-bean {:mvn/version "1.5.0"}
manifold/manifold {:mvn/version "0.2.3"}
aleph/aleph {:mvn/version "0.4.7"}}
:aliases {:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.66.1034"}
org.clojure/clojurescript {:mvn/version "LATEST"}}
:main-opts ["-m" "kaocha.runner"]}}}
:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.51"}}}
:ci {:extra-deps {lambdaisland/kaocha {:mvn/version "1.66.1034"}}
:main-opts ["-m" "kaocha.runner"]}}}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"author": "",
"license": "EPL-2.0",
"dependencies": {
"@cljs-oss/module-deps": "^1.1.1",
"karma": "^5.0.2",
"karma-chrome-launcher": "^3.1.0",
"karma-cljs-test": "^0.1.0",
Expand Down
4 changes: 3 additions & 1 deletion resources/clj-kondo.exports/lilactown/helix/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@
helix.dom/video clj-kondo.lilactown.helix/dom
helix.dom/wbr clj-kondo.lilactown.helix/dom}}
:lint-as {helix.core/defhook clojure.core/defn
helix.core/fnc clojure.core/fn}
helix.core/fnc clojure.core/fn
helix.server.core/defnc clojure.core/defn
helix.server.core/fnc clojure.core/fn}
:linters {:helix/invalid-children {:level :error}
:helix/wrap-after-args {:level :warning}}}
5 changes: 3 additions & 2 deletions src/helix/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@

(defmacro suspense
"Creates a React Suspense boundary."
[{:keys [fallback]} & children]
[{:keys [fallback key]} & children]
`^js/React.Element ($ Suspense
~@(when fallback
`({:fallback ~fallback}))
`({:fallback ~fallback
:key ~key}))
~@children))


Expand Down
60 changes: 60 additions & 0 deletions src/helix/server/core.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
(ns helix.server.core)


(defprotocol IRenderable
(-render [c props]))


(extend-protocol IRenderable
clojure.lang.IFn
(-render [f props]
(f props)))


(defrecord Element [type props key])


(defn $
[type & args]
(if (and (map? (first args)) (not (instance? Element (first args))))
(let [props (first args)
children (rest args)
key (:key props)]
(->Element type (-> props (dissoc :key) (assoc :children children)) key))
(->Element type {:children args} nil)))


(defn <>
[& children]
(->Element 'react/Fragment {:children children} nil))


(defn provider
[{:keys [context value]} & children]
(->Element
'react/Provider
{:context context :value value :children children}
nil))


(defn suspense
[{:keys [fallback]} & children]
(->Element
'react/Suspense
{:fallback fallback :children children}
nil))


(defmacro fnc
[& body]
`(fn ~@body))


(defmacro defnc
[& body]
`(defn ~@body))


(defmacro defhook
[& body]
`(defn ~@body))
Loading