├── examples └── re-frame │ ├── uiexplorer │ ├── src │ │ ├── cljsjs │ │ │ ├── react.cljs │ │ │ ├── react │ │ │ │ ├── dom.cljs │ │ │ │ └── dom │ │ │ │ │ └── server.cljs │ │ │ └── create_react_class.cljs │ │ ├── uiexplorer │ │ │ ├── subs.cljs │ │ │ ├── db.cljs │ │ │ ├── core.cljs │ │ │ ├── scenes │ │ │ │ └── login.cljs │ │ │ ├── react_requires.cljs │ │ │ ├── events.cljs │ │ │ └── routing.cljs │ │ └── reagent │ │ │ ├── dom │ │ │ └── server.cljs │ │ │ └── dom.cljs │ ├── .babelrc │ ├── .projectile │ ├── env │ │ ├── prod │ │ │ └── env │ │ │ │ └── main.cljs │ │ └── dev │ │ │ ├── env │ │ │ └── main.cljs │ │ │ ├── externs.clj │ │ │ └── user.clj │ ├── assets │ │ ├── icons │ │ │ ├── app.png │ │ │ └── loading.png │ │ └── images │ │ │ ├── cljs.png │ │ │ ├── cljs@2x.png │ │ │ └── cljs@3x.png │ ├── .gitignore │ ├── package.json │ ├── README.md │ ├── app.json │ ├── project.clj │ ├── js │ │ └── figwheel-bridge.js │ └── LICENSE │ └── expo-example │ ├── src │ ├── cljsjs │ │ ├── react.cljs │ │ ├── react │ │ │ ├── dom.cljs │ │ │ └── dom │ │ │ │ └── server.cljs │ │ └── create_react_class.cljs │ ├── expo_example │ │ ├── subs.cljs │ │ ├── db.cljs │ │ ├── handlers.cljs │ │ └── core.cljs │ └── reagent │ │ ├── dom │ │ └── server.cljs │ │ └── dom.cljs │ ├── .babelrc │ ├── .projectile │ ├── env │ ├── prod │ │ └── env │ │ │ └── main.cljs │ └── dev │ │ ├── env │ │ └── main.cljs │ │ ├── externs.clj │ │ └── user.clj │ ├── assets │ ├── icons │ │ ├── app.png │ │ └── loading.png │ └── images │ │ ├── cljs.png │ │ ├── cljs@2x.png │ │ └── cljs@3x.png │ ├── .gitignore │ ├── package.json │ ├── app.json │ ├── readme.md │ ├── build.boot │ ├── project.clj │ ├── js │ └── figwheel-bridge.js │ └── LICENSE ├── CHANGELOG.md ├── doc └── intro.md ├── .gitignore ├── test └── cljs_react_navigation │ └── core_test.clj ├── project.clj ├── LICENSE ├── src └── cljs_react_navigation │ ├── reagent.cljs │ ├── re_frame.cljs │ └── base.cljs └── README.md /examples/re-frame/uiexplorer/src/cljsjs/react.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.react) 2 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/cljsjs/react.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.react) 2 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/cljsjs/react/dom.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.react.dom) 2 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/cljsjs/react/dom.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.react.dom) 2 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | ## [0.1.1] - 2017-05-15 3 | ### Changed 4 | - Initial check-in 5 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/cljsjs/react/dom/server.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.react.dom.server) 2 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/cljsjs/create_react_class.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.create-react-class) 2 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/cljsjs/react/dom/server.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.react.dom.server) 2 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/cljsjs/create_react_class.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsjs.create-react-class) 2 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/.projectile: -------------------------------------------------------------------------------- 1 | -/node_modules 2 | -/target 3 | -/.git 4 | -/.exponent 5 | -/figwheel_server.log 6 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/.projectile: -------------------------------------------------------------------------------- 1 | -/node_modules 2 | -/target 3 | -/.git 4 | -/.exponent 5 | -/figwheel_server.log 6 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/env/prod/env/main.cljs: -------------------------------------------------------------------------------- 1 | (ns env.main 2 | (:require [expo-example.core :as core])) 3 | 4 | (core/init) 5 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/env/prod/env/main.cljs: -------------------------------------------------------------------------------- 1 | (ns env.main 2 | (:require [uiexplorer.core :as core])) 3 | 4 | (core/init) 5 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to cljs-react-navigation 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/assets/icons/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/uiexplorer/assets/icons/app.png -------------------------------------------------------------------------------- /examples/re-frame/expo-example/assets/icons/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/expo-example/assets/icons/app.png -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/assets/images/cljs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/uiexplorer/assets/images/cljs.png -------------------------------------------------------------------------------- /examples/re-frame/expo-example/assets/images/cljs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/expo-example/assets/images/cljs.png -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/assets/icons/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/uiexplorer/assets/icons/loading.png -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/assets/images/cljs@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/uiexplorer/assets/images/cljs@2x.png -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/assets/images/cljs@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/uiexplorer/assets/images/cljs@3x.png -------------------------------------------------------------------------------- /examples/re-frame/expo-example/assets/icons/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/expo-example/assets/icons/loading.png -------------------------------------------------------------------------------- /examples/re-frame/expo-example/assets/images/cljs@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/expo-example/assets/images/cljs@2x.png -------------------------------------------------------------------------------- /examples/re-frame/expo-example/assets/images/cljs@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seantempesta/cljs-react-navigation/HEAD/examples/re-frame/expo-example/assets/images/cljs@3x.png -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.subs 2 | (:require [re-frame.core :refer [reg-sub]])) 3 | 4 | (reg-sub 5 | :get-greeting 6 | (fn [db _] 7 | (:greeting db))) -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/expo_example/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns expo-example.subs 2 | (:require [re-frame.core :refer [reg-sub]])) 3 | 4 | (reg-sub 5 | :get-greeting 6 | (fn [db _] 7 | (:greeting db))) 8 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/reagent/dom/server.cljs: -------------------------------------------------------------------------------- 1 | (ns reagent.dom.server) 2 | ;; Shimmed namespace to make reagent 0.6.0 work with react native packager 3 | 4 | (defn render-to-string [_]) 5 | 6 | (defn render-to-static-markup [_]) 7 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/reagent/dom/server.cljs: -------------------------------------------------------------------------------- 1 | (ns reagent.dom.server) 2 | ;; Shimmed namespace to make reagent 0.6.0 work with react native packager 3 | 4 | (defn render-to-string [_]) 5 | 6 | (defn render-to-static-markup [_]) 7 | -------------------------------------------------------------------------------- /test/cljs_react_navigation/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns cljs-react-navigation.core-test 2 | (:require [clojure.test :refer :all] 3 | [cljs-react-navigation.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/reagent/dom.cljs: -------------------------------------------------------------------------------- 1 | (ns reagent.dom) 2 | ;; Shimmed namespace to make reagent 0.6.0 work with react native packager 3 | 4 | (defn render 5 | ([_ _]) 6 | ([_ _ _])) 7 | (defn unmount-component-at-node [_]) 8 | 9 | (defn dom-node [_]) 10 | 11 | (defn force-update-all []) 12 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/reagent/dom.cljs: -------------------------------------------------------------------------------- 1 | (ns reagent.dom) 2 | ;; Shimmed namespace to make reagent 0.6.0 work with react native packager 3 | 4 | (defn render 5 | ([_ _]) 6 | ([_ _ _])) 7 | (defn unmount-component-at-node [_]) 8 | 9 | (defn dom-node [_]) 10 | 11 | (defn force-update-all []) 12 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/expo_example/db.cljs: -------------------------------------------------------------------------------- 1 | (ns expo-example.db 2 | (:require [clojure.spec.alpha :as s])) 3 | 4 | ;; spec of app-db 5 | (s/def ::greeting string?) 6 | (s/def ::app-db 7 | (s/keys :req-un [::greeting])) 8 | 9 | ;; initial state of app-db 10 | (def app-db {:greeting "Hello Clojurescript in Expo!"}) 11 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/db.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.db 2 | (:require [clojure.spec.alpha :as s])) 3 | 4 | ;; spec of app-db 5 | (s/def ::greeting string?) 6 | (s/def ::app-db 7 | (s/keys :req-un [::greeting])) 8 | 9 | ;; initial state of app-db 10 | (def app-db {:greeting "Hello Clojure in iOS and Android!"}) 11 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | /target 5 | /classes 6 | /checkouts 7 | pom.xml 8 | pom.xml.asc 9 | *.jar 10 | *.class 11 | /.lein-* 12 | /.nrepl-port 13 | .hgignore 14 | .hg/ 15 | figwheel_server.log 16 | main.js 17 | .js-modules.edn 18 | /env/dev/env/dev.cljs 19 | /env/dev/env/index.cljs 20 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | /target 5 | /classes 6 | /checkouts 7 | pom.xml 8 | pom.xml.asc 9 | *.jar 10 | *.class 11 | /.lein-* 12 | /.nrepl-port 13 | .hgignore 14 | .hg/ 15 | figwheel_server.log 16 | main.js 17 | .js-modules.edn 18 | /env/dev/env/dev.cljs 19 | /env/dev/env/index.cljs 20 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expo-example", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "main": "main.js", 8 | "dependencies": { 9 | "expo": "^22.0.0", 10 | "react": "16.0.0-beta.5", 11 | "react-native": "https://github.com/expo/react-native/archive/sdk-22.0.1.tar.gz", 12 | "create-react-class": "15.6.0", 13 | "react-navigation": "1.0.0-beta.12" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uiexplorer", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "main": "main.js", 8 | "dependencies": { 9 | 10 | 11 | "expo": "^22.0.0", 12 | "react": "16.0.0-beta.5", 13 | "react-native": "https://github.com/expo/react-native/archive/sdk-22.0.1.tar.gz", 14 | 15 | 16 | 17 | "create-react-class": "15.6.0", 18 | 19 | 20 | "@expo/vector-icons": "6.1.0", 21 | 22 | "react-navigation": "^1.0.0-beta.12" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/env/dev/env/main.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-no-load env.main 2 | (:require [reagent.core :as r] 3 | [uiexplorer.core :as core] 4 | [figwheel.client :as figwheel :include-macros true] 5 | [env.dev])) 6 | 7 | (enable-console-print!) 8 | 9 | (def cnt (r/atom 0)) 10 | (defn reloader [] @cnt [core/app-root]) 11 | (def root-el (r/as-element [reloader])) 12 | 13 | (figwheel/watch-and-reload 14 | :websocket-url (str "ws://" env.dev/ip ":3449/figwheel-ws") 15 | :heads-up-display false 16 | :jsload-callback #(swap! cnt inc)) 17 | 18 | (core/init) 19 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/env/dev/env/main.cljs: -------------------------------------------------------------------------------- 1 | (ns ^:figwheel-no-load env.main 2 | (:require [reagent.core :as r] 3 | [expo-example.core :as core] 4 | [figwheel.client :as figwheel :include-macros true] 5 | [env.dev])) 6 | 7 | (enable-console-print!) 8 | 9 | (def cnt (r/atom 0)) 10 | (defn reloader [] @cnt [core/app-root]) 11 | (def root-el (r/as-element [reloader])) 12 | 13 | (figwheel/watch-and-reload 14 | :websocket-url (str "ws://" env.dev/ip ":3449/figwheel-ws") 15 | :heads-up-display false 16 | :jsload-callback #(swap! cnt inc)) 17 | 18 | (core/init) 19 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/README.md: -------------------------------------------------------------------------------- 1 | ## Re-frame demo of cljs-react-navigation 2 | 3 | ### To run 4 | 5 | #### Install [Lein](http://leiningen.org/#install) 6 | 7 | #### Install [Expo cli client](https://docs.expo.io/versions/latest/introduction/installation.html) 8 | ``` shell 9 | yarn global add exp 10 | ``` 11 | 12 | #### Install npm modules 13 | 14 | ``` shell 15 | yarn install 16 | ``` 17 | 18 | #### Start the figwheel server and cljs repl 19 | ``` shell 20 | lein figwheel 21 | ``` 22 | 23 | #### In a new console tab, run expo 24 | ``` shell 25 | exp start --host lan --ios 26 | # or 27 | exp start --host lan --android 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/core.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.core 2 | (:require [reagent.core :as r :refer [atom]] 3 | [re-frame.core :refer [subscribe dispatch dispatch-sync]] 4 | [uiexplorer.react-requires :refer [Expo]] 5 | [uiexplorer.routing :as routing] 6 | [uiexplorer.events] 7 | [uiexplorer.subs])) 8 | 9 | (def app-root routing/app-root) 10 | 11 | (defn init [] 12 | (aset js/console "disableYellowBox" true) 13 | (dispatch-sync [:initialize-db]) 14 | (.registerRootComponent Expo (r/reactify-component routing/app-root)) 15 | ;; (.registerComponent AppRegistry "UIExplorer" #(r/reactify-component routing/app-root)) 16 | ) 17 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject cljs-react-navigation "0.1.3" 2 | :description "CLJS Wrappers for react-navigation" 3 | :url "https://github.com/seantempesta/cljs-react-navigation" 4 | :license {:name "MIT" 5 | :url "https://opensource.org/licenses/MIT"} 6 | :dependencies [[org.clojure/clojure "1.9.0"] 7 | [org.clojure/clojurescript "1.9.946"] 8 | [reagent "0.7.0" :exclusions [cljsjs/react 9 | cljsjs/react-dom 10 | cljsjs/react-dom-server 11 | cljsjs/create-react-class]] 12 | [re-frame "0.10.4"]] 13 | :plugins [[lein-codox "0.10.3"]] 14 | :codox {:language :clojurescript} 15 | :source-paths ["src"]) 16 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "uiexplorer", 4 | "description": "No description", 5 | "slug": "uiexplorer", 6 | "sdkVersion": "22.0.0", 7 | "version": "1.0.0", 8 | "orientation": "portrait", 9 | "primaryColor": "#cccccc", 10 | "privacy": "public", 11 | "icon": "./assets/icons/app.png", 12 | "notification": { 13 | "icon": "./assets/icons/loading.png", 14 | "color": "#000000" 15 | }, 16 | "loading": { 17 | "icon": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png", 18 | "hideExponentText": false 19 | }, 20 | "packagerOpts": { 21 | "assetExts": [ 22 | "ttf", 23 | "otf" 24 | ], 25 | "nonPersistent": "" 26 | }, 27 | "ios": { 28 | "supportsTablet": true 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo-example", 4 | "description": "No description", 5 | "slug": "expo-example", 6 | "sdkVersion": "22.0.0", 7 | "version": "1.0.0", 8 | "orientation": "portrait", 9 | "primaryColor": "#cccccc", 10 | "privacy": "public", 11 | "icon": "./assets/icons/app.png", 12 | "notification": { 13 | "icon": "./assets/icons/loading.png", 14 | "color": "#000000" 15 | }, 16 | "loading": { 17 | "icon": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png", 18 | "hideExponentText": false 19 | }, 20 | "packagerOpts": { 21 | "assetExts": [ 22 | "ttf", 23 | "otf" 24 | ], 25 | "nonPersistent": "" 26 | }, 27 | "ios": { 28 | "supportsTablet": true 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Sean Tempesta 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/expo_example/handlers.cljs: -------------------------------------------------------------------------------- 1 | (ns expo-example.handlers 2 | (:require 3 | [re-frame.core :refer [reg-event-db ->interceptor]] 4 | [clojure.spec.alpha :as s] 5 | [expo-example.db :as db :refer [app-db]])) 6 | 7 | ;; -- Interceptors ---------------------------------------------------------- 8 | ;; 9 | ;; See https://github.com/Day8/re-frame/blob/develop/docs/Interceptors.md 10 | ;; 11 | (defn check-and-throw 12 | "Throw an exception if db doesn't have a valid spec." 13 | [spec db] 14 | (when-not (s/valid? spec db) 15 | (let [explain-data (s/explain-data spec db)] 16 | (throw (ex-info (str "Spec check failed: " explain-data) explain-data))))) 17 | 18 | (def validate-spec 19 | (if goog.DEBUG 20 | (->interceptor 21 | :id :validate-spec 22 | :after (fn [context] 23 | (let [db (-> context :effects :db)] 24 | (check-and-throw ::db/app-db db) 25 | context))) 26 | ->interceptor)) 27 | 28 | ;; -- Handlers -------------------------------------------------------------- 29 | 30 | (reg-event-db 31 | :initialize-db 32 | [validate-spec] 33 | (fn [_ _] 34 | app-db)) 35 | 36 | (reg-event-db 37 | :set-greeting 38 | [validate-spec] 39 | (fn [db [_ value]] 40 | (assoc db :greeting value))) 41 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/src/expo_example/core.cljs: -------------------------------------------------------------------------------- 1 | (ns expo-example.core 2 | (:require [reagent.core :as r :refer [atom]] 3 | [re-frame.core :refer [subscribe dispatch dispatch-sync]] 4 | [expo-example.handlers] 5 | [expo-example.subs] 6 | [cljs-react-navigation.base :as base])) 7 | 8 | (def ReactNative (js/require "react-native")) 9 | (def ReactNavigation (js/require "react-navigation")) 10 | 11 | (def app-registry (.-AppRegistry ReactNative)) 12 | (def text (r/adapt-react-class (.-Text ReactNative))) 13 | (def view (r/adapt-react-class (.-View ReactNative))) 14 | (def image (r/adapt-react-class (.-Image ReactNative))) 15 | (def touchable-highlight (r/adapt-react-class (.-TouchableHighlight ReactNative))) 16 | (def Alert (.-Alert ReactNative)) 17 | 18 | (defn alert [title] 19 | (.alert Alert title)) 20 | 21 | (defn app-root [] 22 | (let [greeting (subscribe [:get-greeting])] 23 | (fn [] 24 | [view {:style {:flex-direction "column" :margin 40 :align-items "center"}} 25 | [image {:source (js/require "./assets/images/cljs.png") 26 | :style {:width 200 27 | :height 200}}] 28 | [text {:style {:font-size 30 :font-weight "100" :margin-bottom 20 :text-align "center"}} @greeting] 29 | [touchable-highlight {:style {:background-color "#999" :padding 10 :border-radius 5} 30 | :on-press #(alert "HELLO!")} 31 | [text {:style {:color "white" :text-align "center" :font-weight "bold"}} "press me!"]]]))) 32 | 33 | (defn init [] 34 | (dispatch-sync [:initialize-db]) 35 | (.registerComponent app-registry "main" #(r/reactify-component app-root))) 36 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/readme.md: -------------------------------------------------------------------------------- 1 | ## expo-example 2 | 3 | ### Usage 4 | 5 | #### Install Expo [XDE and mobile client](https://docs.expo.io/versions/v15.0.0/introduction/installation.html) 6 | If you don't want to use XDE (not IDE, it stands for Expo Development Tools), you can use [exp CLI](https://docs.expo.io/versions/v15.0.0/guides/exp-cli.html). 7 | 8 | ``` shell 9 | yarn global add exp 10 | ``` 11 | 12 | #### Install [Lein](http://leiningen.org/#install) or [Boot](https://github.com/boot-clj/boot) 13 | 14 | #### Install npm modules 15 | 16 | ``` shell 17 | yarn install 18 | ``` 19 | 20 | #### Signup using exp CLI 21 | 22 | ``` shell 23 | exp signup 24 | ``` 25 | 26 | #### Start the figwheel server and cljs repl 27 | 28 | ##### leiningen users 29 | ``` shell 30 | lein figwheel 31 | ``` 32 | 33 | ##### boot users 34 | ``` shell 35 | boot dev 36 | 37 | ;; then input (cljs-repl) in the connected clojure repl to connect to boot cljs repl 38 | ``` 39 | 40 | #### Start Exponent server (Using `exp`) 41 | 42 | ##### Also connect to Android device 43 | 44 | ``` shell 45 | exp start -a --lan 46 | ``` 47 | 48 | ##### Also connect to iOS Simulator 49 | 50 | ``` shell 51 | exp start -i --lan 52 | ``` 53 | 54 | ### Add new assets or external modules 55 | 1. `require` module: 56 | 57 | ``` clj 58 | (def cljs-logo (js/require "./assets/images/cljs.png")) 59 | (def FontAwesome (js/require "@expo/vector-icons/FontAwesome")) 60 | ``` 61 | 2. Reload simulator or device 62 | 63 | ### Make sure you disable live reload from the Developer Menu, also turn off Hot Module Reload. 64 | Since Figwheel already does those. 65 | 66 | ### Production build (generates js/externs.js and main.js) 67 | 68 | #### leiningen users 69 | ``` shell 70 | lein prod-build 71 | ``` 72 | 73 | #### boot users 74 | ``` shell 75 | boot prod 76 | ``` 77 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/scenes/login.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.scenes.login 2 | (:require [uiexplorer.react-requires :refer [ActivityIndicator Platform Button TouchableOpacity Ionicons InteractionManager View ScrollView Text TouchableHighlight]] 3 | [cljs-react-navigation.re-frame :refer [stack-navigator tab-navigator stack-screen tab-screen router]] 4 | [reagent.core :as r] 5 | [re-frame.core :refer [subscribe dispatch dispatch-sync]])) 6 | 7 | (def static-navigationOptions {:headerTitle "Login" 8 | :headerRight (fn [] 9 | [:> Button {:title "Sign In" 10 | :onPress #(dispatch [:login])}])}) 11 | 12 | (defn dynamic-navigationOptions [{:keys [navigation] :as props}] 13 | (let [navigate (:navigate navigation)] 14 | {:headerTitle "Login" 15 | :headerRight (fn [] 16 | [:> Button {:title "Sign In" 17 | :onPress #(do (dispatch [:login]) 18 | (navigate "Loading"))}])})) 19 | 20 | (defn login1 [{:keys [screenProps navigation] :as props}] 21 | (let [navigate (:navigate navigation)] 22 | (fn [props] 23 | [:> View {:style {:flex 1 24 | :alignItems "center" 25 | :justifyContent "center"}} 26 | [:> Button {:style {:fontSize 17} 27 | :onPress #(do (dispatch [:login]) 28 | (navigate "Loading")) 29 | :title "Click to Login!"}]]))) 30 | 31 | (defn loading [props] 32 | (fn [props] 33 | [:> View {:style {:flex 1 34 | :backgroundColor "#333333" 35 | :alignItems "center" 36 | :justifyContent "center"}} 37 | [:> ActivityIndicator 38 | {:animating true 39 | :style {:alignItems "center" 40 | :justifyContent "center" 41 | :height 80} 42 | :size "large"}]])) -------------------------------------------------------------------------------- /examples/re-frame/expo-example/build.boot: -------------------------------------------------------------------------------- 1 | (set-env! 2 | :source-paths #{"src" "env/dev"} 3 | :dependencies '[[ajchemist/boot-figwheel "0.5.4-6" :scope "test"] ;; latest release 4 | [org.clojure/tools.nrepl "0.2.12" :scope "test"] 5 | [com.cemerick/piggieback "0.2.1" :scope "test"] 6 | [figwheel-sidecar "0.5.4-7" :scope "test"] 7 | [react-native-externs "0.0.2-SNAPSHOT" :scope "test"] 8 | 9 | [org.clojure/clojure "1.9.0-alpha16"] 10 | [org.clojure/clojurescript "1.9.542"] 11 | [org.clojure/core.async "0.3.442"] 12 | [reagent "0.6.1" :exclusions [cljsjs/react cljsjs/react-dom cljsjs/react-dom-server]] 13 | [re-frame "0.9.3"]]) 14 | 15 | (require 16 | '[boot-figwheel :refer [figwheel cljs-repl]] 17 | '[cljs.build.api :as b] 18 | '[user :as user] 19 | '[externs :as externs]) 20 | 21 | (require 'boot.repl) 22 | (swap! boot.repl/*default-middleware* 23 | conj 'cemerick.piggieback/wrap-cljs-repl) 24 | 25 | (deftask dev 26 | "boot dev, then input (cljs-repl)" 27 | [] 28 | (user/prepare) 29 | 30 | (comp 31 | (figwheel 32 | :build-ids ["main"] 33 | :all-builds [{:id "main" 34 | :source-paths ["src" "env/dev"] 35 | :figwheel true 36 | :compiler {:output-to "not-used.js" 37 | :main "env.main" 38 | :optimizations :none 39 | :output-dir "."}}] 40 | :figwheel-options {:open-file-command "emacsclient" 41 | :validate-config false}) 42 | (repl))) 43 | 44 | (deftask prod 45 | [] 46 | (externs/-main) 47 | 48 | (println "Start to compile clojurescript ...") 49 | (let [start (System/nanoTime)] 50 | (b/build ["src" "env/prod"] 51 | {:output-to "main.js" 52 | :main "env.main" 53 | :output-dir "target" 54 | :static-fns true 55 | :externs ["js/externs.js"] 56 | :parallel-build true 57 | :optimize-constants true 58 | :optimizations :advanced 59 | :closure-defines {"goog.DEBUG" false}}) 60 | (println "... done. Elapsed" (/ (- (System/nanoTime) start) 1e9) "seconds"))) 61 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/react_requires.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.react-requires) 2 | 3 | ; react-native 4 | (set! js/ReactNative (js/require "react-native")) 5 | (defonce Modal (.-Modal js/ReactNative)) 6 | (defonce Animated ^js/ReactNative.Animated (.-Animated js/ReactNative)) 7 | (defonce AnimatedValue ^js/ReactNative.Animated.Value (.-Value Animated)) 8 | (defonce AnimatedImage (aget Animated "Image")) 9 | (defonce Dimensions (aget js/ReactNative "Dimensions")) 10 | (defonce ListView (aget js/ReactNative "ListView")) 11 | (defonce DataSource (aget ListView "DataSource")) 12 | (defonce Image (aget js/ReactNative "Image")) 13 | (defonce AppRegistry (aget js/ReactNative "AppRegistry")) 14 | (defonce InteractionManager (aget js/ReactNative "InteractionManager")) 15 | (defonce Platform (aget js/ReactNative "Platform")) 16 | (defonce Linking (aget js/ReactNative "Linking")) 17 | (defonce LayoutAnimation (aget js/ReactNative "LayoutAnimation")) 18 | (defonce Keyboard (aget js/ReactNative "Keyboard")) 19 | (defonce KeyboardAvoidingView (aget js/ReactNative "KeyboardAvoidingView")) 20 | (defonce Picker (aget js/ReactNative "Picker")) 21 | (defonce PickerItem (aget Picker "Item")) 22 | (defonce Text (aget js/ReactNative "Text")) 23 | (defonce TextInput (aget js/ReactNative "TextInput")) 24 | (defonce Button (aget js/ReactNative "Button")) 25 | (defonce View (aget js/ReactNative "View")) 26 | (defonce ScrollView (aget js/ReactNative "ScrollView")) 27 | (defonce Image (aget js/ReactNative "Image")) 28 | (defonce AnimatedImage (aget js/ReactNative "AnimatedImage")) 29 | (defonce TouchableOpacity (aget js/ReactNative "TouchableOpacity")) 30 | (defonce TouchableHighlight (aget js/ReactNative "TouchableHighlight")) 31 | (defonce AsyncStorage (aget js/ReactNative "AsyncStorage")) 32 | (defonce ListView (aget js/ReactNative "ListView")) 33 | (defonce TouchableOpacity (aget js/ReactNative "TouchableOpacity")) 34 | (defonce Alert (aget js/ReactNative "Alert")) 35 | (defonce AppState ^js/ReactNative.AppState (.-AppState js/ReactNative)) 36 | (defonce ActivityIndicator (aget js/ReactNative "ActivityIndicator")) 37 | (defonce VirtualizedList (.-VirtualizedList js/ReactNative)) 38 | (defonce DatePickerIOS (aget js/ReactNative "DatePickerIOS")) 39 | (defonce DatePickerAndroid (aget js/ReactNative "DatePickerAndroid")) 40 | (defonce Expo (js/require "expo")) 41 | 42 | ; react-native-vector-icons 43 | (defonce Ionicons (.-Ionicons (js/require "@expo/vector-icons"))) 44 | 45 | ; react-navigation 46 | (defonce ReactNavigation (js/require "react-navigation")) 47 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/project.clj: -------------------------------------------------------------------------------- 1 | (defproject expo-example "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.9.0-alpha16"] 7 | [org.clojure/clojurescript "1.9.854"] 8 | [reagent "0.7.0" :exclusions [cljsjs/react cljsjs/react-dom cljsjs/react-dom-server cljsjs/create-react-class]] 9 | [re-frame "0.9.3"] 10 | [react-native-externs "0.1.0"]] 11 | :plugins [[lein-cljsbuild "1.1.4"] 12 | [lein-figwheel "0.5.11"]] 13 | :clean-targets ["target/" "main.js"] 14 | :aliases {"figwheel" ["run" "-m" "user" "--figwheel"] 15 | "externs" ["do" "clean" 16 | ["run" "-m" "externs"]] 17 | "rebuild-modules" ["run" "-m" "user" "--rebuild-modules"] 18 | "prod-build" ^{:doc "Recompile code with prod profile."} 19 | ["externs" 20 | ["with-profile" "prod" "cljsbuild" "once" "main"]]} 21 | :jvm-opts ["--add-modules" "java.xml.bind"] 22 | :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.10"] 23 | [com.cemerick/piggieback "0.2.1"]] 24 | :source-paths ["src" "env/dev"] 25 | :cljsbuild {:builds [{:id "main" 26 | :source-paths ["src" "env/dev" "../../../src/"] 27 | :figwheel true 28 | :compiler {:output-to "target/not-used.js" 29 | :main "env.main" 30 | :output-dir "target" 31 | :optimizations :none}}]} 32 | :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}} 33 | :prod {:cljsbuild {:builds [{:id "main" 34 | :source-paths ["src" "env/prod" "../../../src/"] 35 | :compiler {:output-to "main.js" 36 | :main "env.main" 37 | :output-dir "target" 38 | :static-fns true 39 | :externs ["js/externs.js"] 40 | :parallel-build true 41 | :optimize-constants true 42 | :optimizations :advanced 43 | :closure-defines {"goog.DEBUG" false}}}]}}}) 44 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/project.clj: -------------------------------------------------------------------------------- 1 | (defproject uiexplorer "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.9.0-alpha16"] 7 | [org.clojure/clojurescript "1.9.854"] 8 | [reagent "0.7.0" :exclusions [cljsjs/react cljsjs/react-dom cljsjs/react-dom-server cljsjs/create-react-class]] 9 | [re-frame "0.9.3"] 10 | [cljs-react-navigation "0.1.0"] 11 | [react-native-externs "0.1.0"]] 12 | :plugins [[lein-cljsbuild "1.1.4"] 13 | [lein-figwheel "0.5.11"]] 14 | :clean-targets ["target/" "main.js"] 15 | :aliases {"figwheel" ["run" "-m" "user" "--figwheel"] 16 | "externs" ["do" "clean" 17 | ["run" "-m" "externs"]] 18 | "rebuild-modules" ["run" "-m" "user" "--rebuild-modules"] 19 | "prod-build" ^{:doc "Recompile code with prod profile."} 20 | ["externs" 21 | ["with-profile" "prod" "cljsbuild" "once" "main"]]} 22 | 23 | :jvm-opts ["--add-modules" "java.xml.bind"] 24 | 25 | :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.10"] 26 | [com.cemerick/piggieback "0.2.1"]] 27 | :source-paths ["src" "env/dev"] 28 | :cljsbuild {:builds [{:id "main" 29 | :source-paths ["src" "env/dev"] 30 | :figwheel true 31 | :compiler {:output-to "target/not-used.js" 32 | :main "env.main" 33 | :output-dir "target" 34 | :optimizations :none}}]} 35 | :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}} 36 | :prod {:cljsbuild {:builds [{:id "main" 37 | :source-paths ["src" "env/prod"] 38 | :compiler {:output-to "main.js" 39 | :main "env.main" 40 | :output-dir "target" 41 | :static-fns true 42 | :externs ["js/externs.js"] 43 | :parallel-build true 44 | :optimize-constants true 45 | :optimizations :advanced 46 | :closure-defines {"goog.DEBUG" false}}}]}}}) 47 | -------------------------------------------------------------------------------- /src/cljs_react_navigation/reagent.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-react-navigation.reagent 2 | (:require [cljs-react-navigation.base :as base] 3 | [reagent.core :as r] 4 | [reagent.impl.component :as ric] 5 | [cljs.spec.alpha :as s :include-macros true])) 6 | 7 | 8 | (defn react-component? 9 | "Same as base, but now accepts a reagent component fn" 10 | [c] 11 | (cond 12 | (ric/react-class? c) c 13 | (fn? c) (r/reactify-component 14 | (fn [props & children] 15 | [c (js->clj props :keywordize-keys true) children])) 16 | :else :cljs.spec.alpha/invalid)) 17 | 18 | (defn react-element? 19 | "Same as base, but now accepts a reagent component fn" 20 | [e] 21 | (cond 22 | (base/isValidElement e) e 23 | (ric/react-class? e) (r/create-element e) 24 | (fn? e) (r/create-element 25 | (r/reactify-component 26 | (fn [props & children] 27 | [e (js->clj props :keywordize-keys true) children]))) 28 | :else :cljs.spec.alpha/invalid)) 29 | 30 | (defn fn-or-react-component? 31 | "Same as base, but now *expects* a reagent component if a fn is supplied" 32 | [fn-or-c] 33 | (cond 34 | (ric/react-class? fn-or-c) fn-or-c 35 | (fn? fn-or-c) (fn [props & children] 36 | (let [clj-props (js->clj props :keywordize-keys true)] 37 | (r/reactify-component (fn-or-c clj-props children)))) 38 | :else :cljs.spec.alpha/invalid)) 39 | 40 | (defn fn-or-react-element? 41 | "Same as base, but now *expects* a reagent component if a fn is supplied" 42 | [fn-or-e] 43 | (cond 44 | (base/isValidElement fn-or-e) fn-or-e 45 | (ric/react-class? fn-or-e) (r/create-element fn-or-e) 46 | (fn? fn-or-e) (fn [props & children] 47 | (let [clj-props (js->clj props :keywordize-keys true)] 48 | (r/as-element [fn-or-e clj-props children]))) 49 | :else :cljs.spec.alpha/invalid)) 50 | 51 | (defn string-or-react-element? [s-or-e] 52 | (cond 53 | (base/isValidElement s-or-e) s-or-e 54 | (ric/react-class? s-or-e) (r/create-element s-or-e) 55 | (fn? s-or-e) (r/as-element [(fn [props & children] 56 | (let [clj-props (js->clj props :keywordize-keys true)] 57 | [s-or-e clj-props children]))]) 58 | (string? s-or-e) s-or-e 59 | :else :cljs.spec.alpha/invalid)) 60 | 61 | ;; Spec overrides for Reagent Components 62 | (s/def :react/component (s/conformer react-component?)) 63 | (s/def :react/element (s/conformer react-element?)) 64 | (s/def :react-navigation.navigationOptions/headerTitle (s/conformer string-or-react-element?)) 65 | (s/def :react-navigation.navigationOptions/headerLeft (s/conformer string-or-react-element?)) 66 | (s/def :react-navigation.navigationOptions/headerRight (s/conformer string-or-react-element?)) 67 | (s/def :react-navigation.navigationOptions/tabBarIcon (s/conformer fn-or-react-element?)) 68 | (s/def :react-navigation.RouteConfigs.route/screen (s/conformer fn-or-react-component?)) 69 | 70 | ;; API 71 | (def NavigationActionsMap base/NavigationActionsMap) 72 | (def stack-screen base/stack-screen) 73 | (def tab-screen base/tab-screen) 74 | (def drawer-component base/drawer-component) 75 | (def stack-navigator base/stack-navigator) 76 | (def tab-navigator base/tab-navigator) 77 | (def drawer-navigator base/drawer-navigator) 78 | (def switch-navigator base/switch-navigator) 79 | -------------------------------------------------------------------------------- /src/cljs_react_navigation/re_frame.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-react-navigation.re-frame 2 | (:require [cljs-react-navigation.base :as base] 3 | [cljs-react-navigation.reagent :as reagent] 4 | [reagent.core :as r] 5 | [re-frame.core :refer [subscribe dispatch dispatch-sync reg-event-db trim-v reg-sub]])) 6 | 7 | (def ref-getStateForAction (atom nil)) ;; HACK 8 | 9 | (reg-event-db 10 | ::swap-routing-state 11 | [trim-v] 12 | (fn [app-db [new-routes]] 13 | (assoc app-db :routing new-routes))) 14 | 15 | (reg-event-db 16 | ::dispatch 17 | [trim-v] 18 | (fn [app-db [dispatch-args]] 19 | (let [routing-state (get app-db :routing) 20 | type (aget dispatch-args "type") 21 | action-fn (get reagent/NavigationActionsMap type) 22 | action (action-fn dispatch-args) 23 | new-state (@ref-getStateForAction action routing-state)] 24 | (assoc app-db :routing new-state)))) 25 | 26 | (reg-event-db 27 | ::navigate 28 | [trim-v] 29 | (fn [app-db [routeName params]] 30 | (let [routing-state (get app-db :routing) 31 | action-fn (get reagent/NavigationActionsMap "Navigation/NAVIGATE") 32 | action (action-fn #js {:routeName routeName :params params}) 33 | new-state (@ref-getStateForAction action routing-state)] 34 | (assoc app-db :routing new-state)))) 35 | 36 | 37 | (reg-event-db 38 | ::goBack 39 | [trim-v] 40 | (fn [app-db [routeName]] 41 | (let [routing-state (get app-db :routing) 42 | action-fn (get reagent/NavigationActionsMap "Navigation/BACK") 43 | action (action-fn #js {:routeName routeName}) 44 | new-state (@ref-getStateForAction action routing-state)] 45 | (assoc app-db :routing new-state)))) 46 | 47 | (reg-sub 48 | ::routing-state 49 | (fn [app-db] 50 | (get-in app-db [:routing]))) 51 | 52 | 53 | ;; API 54 | (def stack-screen reagent/stack-screen) 55 | (def tab-screen reagent/tab-screen) 56 | (def drawer-component reagent/drawer-component) 57 | (def stack-navigator reagent/stack-navigator) 58 | (def tab-navigator reagent/tab-navigator) 59 | (def drawer-navigator reagent/drawer-navigator) 60 | (def switch-navigator reagent/switch-navigator) 61 | 62 | (def init-state 63 | (fn [main key] 64 | (-> main 65 | .-router 66 | (as-> router 67 | (.getStateForAction router 68 | (.getActionForPathAndParams router (name key))))))) 69 | 70 | (def nil-fn (fn [_])) 71 | 72 | (defn router [{:keys [root-router init-route-name add-listener] 73 | :or {add-listener nil-fn init-route-name :start-route} 74 | :as props}] 75 | (let [routing-sub (subscribe [::routing-state]) 76 | getStateForAction (aget root-router "router" "getStateForAction")] 77 | (reset! ref-getStateForAction getStateForAction) 78 | (fn [props] 79 | (let [routing-state (or @routing-sub 80 | (init-state root-router init-route-name))] 81 | [:> root-router {:navigation 82 | (addNavigationHelpers 83 | (clj->js {:state routing-state 84 | :addListener add-listener 85 | :dispatch (fn [action] 86 | (let [next-state (getStateForAction action routing-state)] 87 | (dispatch [::swap-routing-state next-state])))}))}])))) 88 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/events.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.events 2 | (:require 3 | [re-frame.core :refer [reg-event-db after dispatch]] 4 | [clojure.spec.alpha :as s] 5 | [uiexplorer.db :as db :refer [app-db]])) 6 | 7 | ;; -- Interceptors ------------------------------------------------------------ 8 | ;; 9 | ;; See https://github.com/Day8/re-frame/blob/master/docs/Interceptors.md 10 | ;; 11 | (defn check-and-throw 12 | "Throw an exception if db doesn't have a valid spec." 13 | [spec db [event]] 14 | (when-not (s/valid? spec db) 15 | (let [explain-data (s/explain-data spec db)] 16 | (throw (ex-info (str "Spec check after " event " failed: " explain-data) explain-data))))) 17 | 18 | (def validate-spec 19 | (if goog.DEBUG 20 | (after (partial check-and-throw ::db/app-db)) 21 | [])) 22 | 23 | ;; -- Handlers -------------------------------------------------------------- 24 | 25 | (reg-event-db 26 | :initialize-db 27 | validate-spec 28 | (fn [_ _] 29 | app-db)) 30 | 31 | (reg-event-db 32 | :reset-routing-state 33 | validate-spec 34 | (fn [db _] 35 | (assoc db :routing (clj->js {:index 1, 36 | :routes [{:routes [{:index 0, 37 | :routes [{:routeName "Home", :key "HomeInit"}], 38 | :key "HomeTab", 39 | :routeName "HomeTab"} 40 | {:index 0, 41 | :routes [{:routeName "Search", :key "SearchInit"}], 42 | :key "SearchTabInit", 43 | :routeName "SearchTab"}], 44 | :index 0, 45 | :key "InitTabs", 46 | :routeName "Tabs"} 47 | {:index 0, 48 | :routes [{:routeName "Login1", :key "Login1Init"}], 49 | :key "LoginStackInit", 50 | :routeName "LoginStack"}]})))) 51 | 52 | (reg-event-db 53 | :login 54 | validate-spec 55 | (fn [db [_ value]] 56 | (js/setTimeout #(dispatch [:login-success]) 2000) 57 | db)) 58 | 59 | (reg-event-db 60 | :login-success 61 | validate-spec 62 | (fn [db [_ value]] 63 | (assoc db :routing (clj->js {:index 0, 64 | :routes [{:routes [{:index 0, 65 | :routes [{:routeName "Home", :key "HomeInit"}], 66 | :key "HomeTab", 67 | :routeName "HomeTab"} 68 | {:index 0, 69 | :routes [{:routeName "Search", :key "SearchInit"}], 70 | :key "SearchTabInit", 71 | :routeName "SearchTab"}], 72 | :index 0, 73 | :key "TabsInit", 74 | :routeName "Tabs"}]})))) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cljs-react-navigation 2 | 3 | This library is my attempt to unify all `cljs` `react-navigation` libraries under a shared code base. Right now it only includes `Reagent/Re-Frame` libraries, but I'm hoping other people will help me add `om-next` and `rum` libraries. 4 | 5 | ![](https://clojars.org/cljs-react-navigation/latest-version.svg) 6 | 7 | ![](https://media.giphy.com/media/3o7bu14rq4AVqTK1Nu/giphy.gif) 8 | 9 | ## Tested with 10 | 11 | Clojurescript 12 | ```clojure 13 | [org.clojure/clojurescript "1.9.854"] ;; using cljs.spec.alpha namespace 14 | [reagent "0.7.0"] ;; Import the cljs-react-navigation.reagent namespace 15 | [re-frame "0.9.3"] ;; Import the cljs-react-navigation.re-frame namespace 16 | ``` 17 | 18 | Javascript 19 | ```js 20 | "react": "16.0.0-alpha.12", 21 | "react-native": "0.44.0", 22 | "react-navigation": "^1.0.0-beta.9" 23 | ``` 24 | 25 | ## Do this first 26 | 27 | Add `react-navigation` to your project. 28 | ``` 29 | npm install react-navigation --save 30 | ``` 31 | 32 | If you're using re-natal, also add it to re-natal 33 | ```js 34 | re-natal use-component react-navigation 35 | re-natal use-figwheel 36 | ``` 37 | 38 | If you're using my [cljs expo template](https://github.com/seantempesta/expo-cljs-template), make sure to require react-navigation somewhere in your code. 39 | ```clojure 40 | ; react-navigation 41 | (defonce ReactNavigation (js/require "react-navigation")) 42 | ``` 43 | 44 | ## Documentation 45 | 46 | Anything `react-navigation` related is documented on [their site](https://reactnavigation.org/docs/getting-started.html). The idea for this library isn't to re-invent the wheel, but make it cljs friendly. 47 | 48 | Specifically: 49 | - Conforming whatever cljs wrapper components into the correct react components/elements 50 | - Converting props to cljs data structures to avoid `clj->js` & `js->clj`ing everything. 51 | 52 | 53 | ## Usage for base 54 | 55 | Yeah, you really don't use this directly. Try extending this set of specs and and functions. See the `reagent` version. 56 | 57 | That being said, I'm literally just spec'ing out the javascript API, with some minor tweaks: 58 | - `screen` function isn't overloaded (there's a `stack-screen` and a `tab-screen`) 59 | - `screen` functions accepts the `navigationOptions` as a second param (god knows why they insist on doing ES6 style initializations) 60 | 61 | ## Usage for reagent 62 | 63 | First import the reagent version of the library library: 64 | ```clojure 65 | (ns uiexplorer.routing 66 | (:require [cljs-react-navigation.reagent :refer [stack-navigator tab-navigator stack-screen tab-screen router]])) 67 | 68 | ``` 69 | 70 | Then create reagent-components that accept props like this: 71 | 72 | ```clojure 73 | (defn home 74 | "Each Screen will receive two props: 75 | - screenProps - Extra props passed down from the router (rarely used) 76 | - navigation - The main navigation functions in a map as follows: 77 | {:state - routing state for this screen 78 | :dispatch - generic dispatch fn 79 | :goBack - pop's the current screen off the stack 80 | :navigate - most common way to navigate to the next screen 81 | :setParams - used to change the params for the current screen}" 82 | [props] 83 | (fn [{:keys [screenProps navigation] :as props}] 84 | (let [{:keys [navigate goBack]} navigation] 85 | [:> View {:style {:flex 1 86 | :alignItems "center" 87 | :justifyContent "center"}} 88 | [:> Button {:style {:fontSize 17} 89 | :onPress #(navigate "Modal") 90 | :title "Modal Me!"}] 91 | [:> Button {:style {:fontSize 17} 92 | :onPress #(goBack) 93 | :title "Go Back!"}] 94 | [:> Button {:style {:fontSize 17} 95 | :onPress #(navigate "Placeholder")}]]))) 96 | ``` 97 | 98 | And then you can create stacks and screens for them like this: 99 | ```clojure 100 | (def HomeStack (stack-navigator {:Home {:screen (stack-screen home {:title "Home"})}})) 101 | 102 | ``` 103 | 104 | And then you can just render that Stack like a normal reagent component: 105 | 106 | ```clojure 107 | (defn app-root [] 108 | (fn [] 109 | [:> HomeStack {}])) 110 | 111 | (defn init [] 112 | (.registerComponent AppRegistry "UIExplorer" #(r/reactify-component app-root))) 113 | ``` 114 | 115 | 116 | TODO: Create an example 117 | 118 | 119 | ## Usage for re-frame 120 | 121 | Pretty much the same as the Reagent version, but you can store the routing state in re-frame's app-db (which is great for serializing state to AsyncStorage). Also, dispatch routing changes from anywhere. 122 | 123 | See the [examples folder](https://github.com/seantempesta/cljs-react-navigation/tree/master/examples/re-frame/uiexplorer). 124 | 125 | ## TODO 126 | 127 | - Finish spec'ing all functions and parameters (I think I've got most of them) 128 | - Reagent example 129 | - Rum example 130 | - Om-Next example 131 | 132 | 133 | ## License 134 | 135 | Copyright © 2017 Sean Tempesta 136 | 137 | Distributed under the MIT license. 138 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/src/uiexplorer/routing.cljs: -------------------------------------------------------------------------------- 1 | (ns uiexplorer.routing 2 | (:require [uiexplorer.react-requires :refer [Platform Button TouchableOpacity Ionicons InteractionManager View ScrollView Text TouchableHighlight]] 3 | [uiexplorer.scenes.login :as login] 4 | [cljs-react-navigation.re-frame :refer [stack-navigator tab-navigator stack-screen tab-screen router] :as nav] 5 | [reagent.core :as r] 6 | [re-frame.core :refer [subscribe dispatch dispatch-sync]])) 7 | 8 | ;; Colors 9 | ;; 10 | (def darkGrey "#484848") 11 | (def blue "#348BFE") 12 | 13 | (defn home 14 | "Each Screen will receive two props: 15 | - screenProps - Extra props passed down from the router (rarely used) 16 | - navigation - The main navigation functions in a map as follows: 17 | {:state - routing state for this screen 18 | :dispatch - generic dispatch fn 19 | :goBack - pop's the current screen off the stack 20 | :navigate - most common way to navigate to the next screen 21 | :setParams - used to change the params for the current screen}" 22 | [props] 23 | (fn [{:keys [screenProps navigation] :as props}] 24 | (let [{:keys [navigate goBack]} navigation] 25 | [:> View {:style {:flex 1 26 | :alignItems "center" 27 | :justifyContent "center"}} 28 | [:> Button {:style {:fontSize 17} 29 | :onPress #(navigate "Modal") 30 | :title "Modal Me!"}] 31 | [:> Button {:style {:fontSize 17} 32 | :onPress #(goBack) 33 | :title "Go Back!"}] 34 | [:> Button {:style {:fontSize 17} 35 | :onPress #(navigate "Placeholder") 36 | :title "Push Placeholder"}] 37 | [:> Button {:style {:fontSize 17} 38 | :onPress #(dispatch [:reset-routing-state]) 39 | :title "Logout"}]]))) 40 | 41 | (defn search [props] 42 | (fn [{:keys [screenProps navigation] :as props}] 43 | (let [{:keys [state setParams]} navigation 44 | {:keys [params]} state] 45 | [:> View {:style {:flex 1 46 | :alignItems "center" 47 | :justifyContent "center"}} 48 | [:> Text {} (str "Searching for " (:search params))] 49 | 50 | [:> Button {:style {:fontSize 17} 51 | :onPress #(setParams (clj->js {:search (rand-int 100)})) 52 | :title "Search!"}]]))) 53 | 54 | (defn placeholder [props] 55 | (fn [{:keys [screenProps navigation] :as props}] 56 | (let [{:keys [goBack]} navigation] 57 | [:> View {:style {:flex 1 58 | :alignItems "center" 59 | :justifyContent "center"}} 60 | [:> Text {} "Nothing to see here!"] 61 | [:> Button {:style {:fontSize 17} 62 | :onPress #(goBack) 63 | :title "Go Back!"}]]))) 64 | 65 | (def home-tabBar {:tabBarLabel "Home" 66 | :tabBarIcon (fn [{:keys [tintColor focused] :as props}] 67 | [:> Ionicons {:name (if focused "ios-home" "ios-home-outline") 68 | :color (if focused blue darkGrey) 69 | :size 26}])}) 70 | 71 | (def search-tabBar {:tabBarLabel "Search" 72 | :tabBarIcon (fn [{:keys [tintColor focused] :as props}] 73 | [:> Ionicons {:name (if focused "ios-search" "ios-search-outline") 74 | :color (if focused blue darkGrey) 75 | :size 26}])}) 76 | 77 | 78 | (def LoginStack (stack-navigator {:Login1 {:screen (stack-screen login/login1 login/dynamic-navigationOptions)} 79 | :Loading {:screen (stack-screen login/loading {:header nil})}})) 80 | (def HomeStack (stack-navigator {:Home {:screen (stack-screen home {:title "Home"})} 81 | :Placeholder {:screen (stack-screen placeholder {:title "Placeholder"})}})) 82 | (def SearchStack (stack-navigator {:Search {:screen (stack-screen search {:title "Search"})}})) 83 | 84 | (def navbar-marginTop (if (= (aget Platform "OS") "android") 24 0)) 85 | (def Tabs (tab-navigator {:HomeTab {:path "home" 86 | :screen (tab-screen HomeStack home-tabBar)} 87 | :SearchTab {:path "search" 88 | :screen (tab-screen SearchStack search-tabBar)}} 89 | {:tabBarOptions {:style {:marginTop navbar-marginTop}}})) 90 | 91 | (def AllRoutesStack (stack-navigator {:Tabs {:screen Tabs} 92 | :Modal {:screen (stack-screen placeholder {:title "Modal"})} 93 | :LoginStack {:screen LoginStack}} 94 | {:headerMode "none" 95 | :mode "modal"})) 96 | 97 | (defn app-root [] 98 | (r/create-class 99 | (let [nav-state (subscribe [::nav/routing-state])] 100 | {:component-will-mount (fn [] 101 | (when-not @nav-state 102 | (dispatch-sync [:reset-routing-state]))) 103 | :reagent-render (fn [] 104 | [router {:root-router AllRoutesStack}])}))) 105 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/env/dev/externs.clj: -------------------------------------------------------------------------------- 1 | (ns externs 2 | (:require [cljs.compiler.api :as compiler] 3 | [cljs.analyzer.api :as analyzer] 4 | [cljs.analyzer :as ana] 5 | [clojure.walk :refer [prewalk]] 6 | [clojure.pprint :refer [pprint]] 7 | [clojure.java.io :as io] 8 | [clojure.string :as str] 9 | [cljs.env :as env] 10 | [clojure.tools.reader :as r] 11 | [clojure.tools.reader.reader-types :refer [string-push-back-reader]] 12 | [cljs.tagged-literals :as tags]) 13 | (:import (clojure.lang LineNumberingPushbackReader))) 14 | 15 | ;; Idea from https://gist.github.com/Chouser/5796967 16 | 17 | ;; TODO (NAMESPACE/MODULE.method ...args) or NAMESPACE/MODULE.field not work 18 | ;; For example, (ui/Facebook.logInWithReadPermissionsAsync ...args) 19 | ;; or ui/Permissions.REMOTE_NOTIFICATIONS, 20 | ;; we already know logInWithReadPermissionsAsync is `invoke' op 21 | ;; and REMOTE_NOTIFICATIONS is a property, need to dig deeper to the ast 22 | 23 | ;; TODO ana/analyze is slow 24 | 25 | (defonce cenv (analyzer/empty-state)) 26 | 27 | (defn compile-project 28 | [src target] 29 | (analyzer/with-state cenv 30 | (compiler/with-core-cljs 31 | (compiler/compile-root src target)))) 32 | 33 | (defn get-namespaces 34 | [] 35 | (:cljs.analyzer/namespaces @cenv)) 36 | 37 | (defn get-namespace 38 | ([] 39 | (get-namespace ana/*cljs-ns*)) 40 | ([k] 41 | (get (get-namespaces) k))) 42 | 43 | (defn get-alias 44 | [ns] 45 | (apply merge 46 | ((juxt :requires :require-macros) 47 | (get-namespace ns)))) 48 | 49 | (defn print-ast [ast] 50 | (pprint ;; pprint indents output nicely 51 | (prewalk ;; rewrite each node of the ast 52 | (fn [x] 53 | (if (map? x) 54 | (select-keys x [:children :name :form :op]) ;; return selected entries of each map node 55 | x)) ;; non-map nodes are left unchanged 56 | ast))) 57 | 58 | (defn get-ns 59 | [s] 60 | (some->> 61 | (re-find #"\(ns[\s]+([^\s]+)" s) 62 | (last) 63 | (symbol))) 64 | 65 | (defn read-file 66 | [filename] 67 | (try 68 | (let [form-str (slurp filename) 69 | current-ns (get-ns form-str) 70 | reader (string-push-back-reader form-str) 71 | endof (gensym)] 72 | (binding [r/*read-eval* false 73 | r/*data-readers* tags/*cljs-data-readers* 74 | r/*alias-map* (try 75 | (get-alias (ns-name current-ns)) 76 | (catch Exception e 77 | {}))] 78 | (->> #(r/read reader false endof) 79 | (repeatedly) 80 | (take-while #(not= % endof)) 81 | (doall)))) 82 | (catch Exception e 83 | (println e) 84 | '()))) 85 | 86 | (defn file-ast 87 | "Return the ClojureScript AST for the contents of filename. Tends to 88 | be large and to contain cycles -- be careful printing at the REPL." 89 | [filename] 90 | (binding [ana/*cljs-ns* 'cljs.user ;; default namespace 91 | ana/*cljs-file* filename] 92 | (mapv 93 | (fn [form] 94 | (try (ana/no-warn (ana/analyze (ana/empty-env) form {:cache-analysis true})) 95 | (catch Exception e 96 | (prn filename e)))) 97 | (read-file filename)))) 98 | 99 | (defn flatten-ast [ast] 100 | (mapcat #(tree-seq :children :children %) ast)) 101 | 102 | (defn get-interop-used 103 | "Return a set of symbols representing the method and field names 104 | used in interop forms in the given sequence of AST nodes." 105 | [flat-ast] 106 | (keep #(let [ret (and (map? %) 107 | (when-let [sym (some % [:method :field])] 108 | (when-not (str/starts-with? sym "cljs") 109 | sym)))] 110 | (if ret 111 | ret 112 | nil)) flat-ast)) 113 | 114 | (defn externs-for-interop [syms] 115 | (apply str 116 | "var DummyClass={};\n" 117 | (map #(str "DummyClass." % "=function(){};\n") 118 | syms))) 119 | 120 | (defn var-defined? 121 | "Returns true if the given fully-qualified symbol is known by the 122 | ClojureScript compiler to have been defined, based on its mutable set 123 | of namespaces." 124 | [sym] 125 | (contains? (let [ns (get (get-namespaces) (symbol (namespace sym)))] 126 | (merge (:defs ns) 127 | (:macros ns))) 128 | (symbol (name sym)))) 129 | 130 | (defn get-vars-used 131 | "Return a set of symbols representing all vars used or referenced in 132 | the given sequence of AST nodes." 133 | [requires flat-ast] 134 | (->> flat-ast 135 | (filter #(let [ns (-> % :info :ns)] 136 | (and (= (:op %) :var) 137 | ns 138 | (not= ns 'js)))) 139 | (map #(let [sym (-> % :info :name) 140 | sym-namespace (get requires (symbol (namespace sym))) 141 | sym-name (name sym)] 142 | (if sym-namespace 143 | (symbol (str sym-namespace) sym-name) 144 | sym))))) 145 | 146 | (defn extern-for-var [[str-namespace symbols]] 147 | (let [symbols-str (->> symbols 148 | (map (fn [sym] (format "%s.%s={};\n" (namespace sym) (name sym)))) 149 | (apply str))] 150 | (format "var %s={};\n%s" 151 | str-namespace symbols-str))) 152 | 153 | (defn externs-for-vars [grouped-syms] 154 | (apply str (map extern-for-var grouped-syms))) 155 | 156 | (defn get-undefined-vars [requires flat-ast] 157 | (->> (get-vars-used requires flat-ast) 158 | (remove var-defined?))) 159 | 160 | (defn get-undefined-vars-and-interop-used [file] 161 | (let [ast (file-ast file) 162 | ns-name (:name (first ast)) 163 | ns-requires (:requires (first ast)) 164 | flat-ast (flatten-ast ast)] 165 | [(get-undefined-vars ns-requires flat-ast) 166 | (get-interop-used flat-ast)])) 167 | 168 | ;; copy from https://github.com/ejlo/lein-externs/blob/master/src/leiningen/externs.clj 169 | (defn cljs-file? 170 | "Returns true if the java.io.File represents a normal Clojurescript source 171 | file." 172 | [^java.io.File file] 173 | (and (.isFile file) 174 | (.endsWith (.getName file) ".cljs"))) 175 | 176 | (defn get-source-paths [build-type builds] 177 | (or 178 | (when build-type 179 | (:source-paths 180 | (or ((keyword build-type) builds) 181 | (first (filter #(= (name (:id %)) build-type) builds))))) 182 | ["src" "cljs"])) 183 | 184 | (defn -main 185 | "Generate an externs file" 186 | [] 187 | ;; TODO configurable 188 | (println "Start to generate externs...") 189 | (compile-project (io/file "src") (io/file "target")) 190 | 191 | (let [source-paths ["src" "env/prod"] 192 | 193 | files (->> source-paths 194 | (map io/file) 195 | (mapcat file-seq) 196 | (filter cljs-file?)) 197 | col (apply concat (doall (pmap get-undefined-vars-and-interop-used files))) 198 | vars (->> (take-nth 2 col) 199 | (remove empty?) 200 | (flatten) 201 | (set) 202 | (sort) 203 | (group-by namespace) 204 | ;; remove goog dependencies, need to dig deeper(TODO) 205 | (remove (fn [[ns _]] (str/starts-with? ns "goog"))) 206 | (externs-for-vars)) 207 | interop (->> (take-nth 2 (rest col)) 208 | (remove empty?) 209 | (flatten) 210 | (set) 211 | (sort) 212 | (externs-for-interop)) 213 | result (str vars interop)] 214 | (spit "js/externs.js" result) 215 | (println "Generated externs to js/externs.js") 216 | 217 | ;; prevent jvm hang after this task, maybe Clojurescript uses pmap for parallel compilation. 218 | (shutdown-agents))) 219 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/env/dev/externs.clj: -------------------------------------------------------------------------------- 1 | (ns externs 2 | (:require [cljs.compiler.api :as compiler] 3 | [cljs.analyzer.api :as analyzer] 4 | [cljs.analyzer :as ana] 5 | [clojure.walk :refer [prewalk]] 6 | [clojure.pprint :refer [pprint]] 7 | [clojure.java.io :as io] 8 | [clojure.string :as str] 9 | [cljs.env :as env] 10 | [clojure.tools.reader :as r] 11 | [clojure.tools.reader.reader-types :refer [string-push-back-reader]] 12 | [cljs.tagged-literals :as tags]) 13 | (:import (clojure.lang LineNumberingPushbackReader))) 14 | 15 | ;; Idea from https://gist.github.com/Chouser/5796967 16 | 17 | ;; TODO (NAMESPACE/MODULE.method ...args) or NAMESPACE/MODULE.field not work 18 | ;; For example, (ui/Facebook.logInWithReadPermissionsAsync ...args) 19 | ;; or ui/Permissions.REMOTE_NOTIFICATIONS, 20 | ;; we already know logInWithReadPermissionsAsync is `invoke' op 21 | ;; and REMOTE_NOTIFICATIONS is a property, need to dig deeper to the ast 22 | 23 | ;; TODO ana/analyze is slow 24 | 25 | (defonce cenv (analyzer/empty-state)) 26 | 27 | (defn compile-project 28 | [src target] 29 | (analyzer/with-state cenv 30 | (compiler/with-core-cljs 31 | (compiler/compile-root src target)))) 32 | 33 | (defn get-namespaces 34 | [] 35 | (:cljs.analyzer/namespaces @cenv)) 36 | 37 | (defn get-namespace 38 | ([] 39 | (get-namespace ana/*cljs-ns*)) 40 | ([k] 41 | (get (get-namespaces) k))) 42 | 43 | (defn get-alias 44 | [ns] 45 | (apply merge 46 | ((juxt :requires :require-macros) 47 | (get-namespace ns)))) 48 | 49 | (defn print-ast [ast] 50 | (pprint ;; pprint indents output nicely 51 | (prewalk ;; rewrite each node of the ast 52 | (fn [x] 53 | (if (map? x) 54 | (select-keys x [:children :name :form :op]) ;; return selected entries of each map node 55 | x)) ;; non-map nodes are left unchanged 56 | ast))) 57 | 58 | (defn get-ns 59 | [s] 60 | (some->> 61 | (re-find #"\(ns[\s]+([^\s]+)" s) 62 | (last) 63 | (symbol))) 64 | 65 | (defn read-file 66 | [filename] 67 | (try 68 | (let [form-str (slurp filename) 69 | current-ns (get-ns form-str) 70 | reader (string-push-back-reader form-str) 71 | endof (gensym)] 72 | (binding [r/*read-eval* false 73 | r/*data-readers* tags/*cljs-data-readers* 74 | r/*alias-map* (try 75 | (get-alias (ns-name current-ns)) 76 | (catch Exception e 77 | {}))] 78 | (->> #(r/read reader false endof) 79 | (repeatedly) 80 | (take-while #(not= % endof)) 81 | (doall)))) 82 | (catch Exception e 83 | (println e) 84 | '()))) 85 | 86 | (defn file-ast 87 | "Return the ClojureScript AST for the contents of filename. Tends to 88 | be large and to contain cycles -- be careful printing at the REPL." 89 | [filename] 90 | (binding [ana/*cljs-ns* 'cljs.user ;; default namespace 91 | ana/*cljs-file* filename] 92 | (mapv 93 | (fn [form] 94 | (try (ana/no-warn (ana/analyze (ana/empty-env) form {:cache-analysis true})) 95 | (catch Exception e 96 | (prn filename e)))) 97 | (read-file filename)))) 98 | 99 | (defn flatten-ast [ast] 100 | (mapcat #(tree-seq :children :children %) ast)) 101 | 102 | (defn get-interop-used 103 | "Return a set of symbols representing the method and field names 104 | used in interop forms in the given sequence of AST nodes." 105 | [flat-ast] 106 | (keep #(let [ret (and (map? %) 107 | (when-let [sym (some % [:method :field])] 108 | (when-not (str/starts-with? sym "cljs") 109 | sym)))] 110 | (if ret 111 | ret 112 | nil)) flat-ast)) 113 | 114 | (defn externs-for-interop [syms] 115 | (apply str 116 | "var DummyClass={};\n" 117 | (map #(str "DummyClass." % "=function(){};\n") 118 | syms))) 119 | 120 | (defn var-defined? 121 | "Returns true if the given fully-qualified symbol is known by the 122 | ClojureScript compiler to have been defined, based on its mutable set 123 | of namespaces." 124 | [sym] 125 | (contains? (let [ns (get (get-namespaces) (symbol (namespace sym)))] 126 | (merge (:defs ns) 127 | (:macros ns))) 128 | (symbol (name sym)))) 129 | 130 | (defn get-vars-used 131 | "Return a set of symbols representing all vars used or referenced in 132 | the given sequence of AST nodes." 133 | [requires flat-ast] 134 | (->> flat-ast 135 | (filter #(let [ns (-> % :info :ns)] 136 | (and (= (:op %) :var) 137 | ns 138 | (not= ns 'js)))) 139 | (map #(let [sym (-> % :info :name) 140 | sym-namespace (get requires (symbol (namespace sym))) 141 | sym-name (name sym)] 142 | (if sym-namespace 143 | (symbol (str sym-namespace) sym-name) 144 | sym))))) 145 | 146 | (defn extern-for-var [[str-namespace symbols]] 147 | (let [symbols-str (->> symbols 148 | (map (fn [sym] (format "%s.%s={};\n" (namespace sym) (name sym)))) 149 | (apply str))] 150 | (format "var %s={};\n%s" 151 | str-namespace symbols-str))) 152 | 153 | (defn externs-for-vars [grouped-syms] 154 | (apply str (map extern-for-var grouped-syms))) 155 | 156 | (defn get-undefined-vars [requires flat-ast] 157 | (->> (get-vars-used requires flat-ast) 158 | (remove var-defined?))) 159 | 160 | (defn get-undefined-vars-and-interop-used [file] 161 | (let [ast (file-ast file) 162 | ns-name (:name (first ast)) 163 | ns-requires (:requires (first ast)) 164 | flat-ast (flatten-ast ast)] 165 | [(get-undefined-vars ns-requires flat-ast) 166 | (get-interop-used flat-ast)])) 167 | 168 | ;; copy from https://github.com/ejlo/lein-externs/blob/master/src/leiningen/externs.clj 169 | (defn cljs-file? 170 | "Returns true if the java.io.File represents a normal Clojurescript source 171 | file." 172 | [^java.io.File file] 173 | (and (.isFile file) 174 | (.endsWith (.getName file) ".cljs"))) 175 | 176 | (defn get-source-paths [build-type builds] 177 | (or 178 | (when build-type 179 | (:source-paths 180 | (or ((keyword build-type) builds) 181 | (first (filter #(= (name (:id %)) build-type) builds))))) 182 | ["src" "cljs"])) 183 | 184 | (defn -main 185 | "Generate an externs file" 186 | [] 187 | ;; TODO configurable 188 | (println "Start to generate externs...") 189 | (compile-project (io/file "src") (io/file "target")) 190 | 191 | (let [source-paths ["src" "env/prod"] 192 | 193 | files (->> source-paths 194 | (map io/file) 195 | (mapcat file-seq) 196 | (filter cljs-file?)) 197 | col (apply concat (doall (pmap get-undefined-vars-and-interop-used files))) 198 | vars (->> (take-nth 2 col) 199 | (remove empty?) 200 | (flatten) 201 | (set) 202 | (sort) 203 | (group-by namespace) 204 | ;; remove goog dependencies, need to dig deeper(TODO) 205 | (remove (fn [[ns _]] (str/starts-with? ns "goog"))) 206 | (externs-for-vars)) 207 | interop (->> (take-nth 2 (rest col)) 208 | (remove empty?) 209 | (flatten) 210 | (set) 211 | (sort) 212 | (externs-for-interop)) 213 | result (str vars interop)] 214 | (spit "js/externs.js" result) 215 | (println "Generated externs to js/externs.js") 216 | 217 | ;; prevent jvm hang after this task, maybe Clojurescript uses pmap for parallel compilation. 218 | (shutdown-agents))) 219 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/env/dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require [figwheel-sidecar.repl-api :as ra] 3 | [clojure.java.io :as io] 4 | [clojure.string :as str] 5 | [hawk.core :as hawk] 6 | [clojure.tools.reader.edn :as edn] 7 | [clojure.set :as set])) 8 | ;; This namespace is loaded automatically by nREPL 9 | 10 | (defn get-cljs-builds 11 | [] 12 | (let [project-config (->> "project.clj" 13 | slurp 14 | read-string 15 | (drop 1) 16 | (apply hash-map)) 17 | profiles (:profiles project-config)] 18 | (get-in profiles [:dev :cljsbuild :builds]))) 19 | 20 | (defn enable-source-maps 21 | [] 22 | (println "Source maps enabled.") 23 | (let [path "node_modules/metro-bundler/src/Server/index.js"] 24 | (spit path 25 | (str/replace (slurp path) "/\\.map$/" "/main.map$/")))) 26 | 27 | (defn write-main-js 28 | [] 29 | (-> "'use strict';\n\n// cljsbuild adds a preamble mentioning goog so hack around it\nwindow.goog = {\n provide() {},\n require() {},\n};\nrequire('./target/env/index.js');\n" 30 | ((partial spit "main.js")))) 31 | 32 | (defn get-lan-ip 33 | [] 34 | (cond 35 | (= "Mac OS X" (System/getProperty "os.name")) 36 | (.getHostAddress (java.net.InetAddress/getLocalHost)) 37 | 38 | :else 39 | (->> (java.net.NetworkInterface/getNetworkInterfaces) 40 | (enumeration-seq) 41 | (filter #(not (or (str/starts-with? (.getName %) "docker") 42 | (str/starts-with? (.getName %) "vboxnet") 43 | (str/starts-with? (.getName %) "br-")))) 44 | (map #(.getInterfaceAddresses %)) 45 | (map 46 | (fn [ip] 47 | (seq (filter #(instance? 48 | java.net.Inet4Address 49 | (.getAddress %)) 50 | ip)))) 51 | (remove nil?) 52 | (first) 53 | (filter #(instance? 54 | java.net.Inet4Address 55 | (.getAddress %))) 56 | (first) 57 | (.getAddress) 58 | (.getHostAddress)))) 59 | 60 | (defn write-env-dev 61 | [] 62 | (let [hostname (.getHostName (java.net.InetAddress/getLocalHost)) 63 | ip (get-lan-ip)] 64 | (-> "(ns env.dev)\n(def hostname \"%s\")\n(def ip \"%s\")" 65 | (format 66 | hostname 67 | ip) 68 | ((partial spit "env/dev/env/dev.cljs"))))) 69 | 70 | (defn rebuild-env-index 71 | [js-modules] 72 | (let [modules (->> (file-seq (io/file "assets")) 73 | (filter #(and (not (re-find #"DS_Store" (str %))) 74 | (.isFile %))) 75 | (map (fn [file] (when-let [path (str file)] 76 | (str "../../" path)))) 77 | (concat js-modules ["react" "react-native" "expo" "create-react-class"]) 78 | (distinct)) 79 | modules-map (zipmap 80 | (->> modules 81 | (map #(str "\"" 82 | (if (str/starts-with? % "../../assets") 83 | (-> % 84 | (str/replace "../../" "./") 85 | (str/replace "@2x" "") 86 | (str/replace "@3x" "")) 87 | %) 88 | "\""))) 89 | (->> modules 90 | (map #(format "(js/require \"%s\")" 91 | (-> % 92 | (str/replace "@2x" "") 93 | (str/replace "@3x" ""))))))] 94 | (try 95 | (-> "(ns env.index\n (:require [env.dev :as dev]))\n\n;; undo main.js goog preamble hack\n(set! js/window.goog js/undefined)\n\n(-> (js/require \"figwheel-bridge\")\n (.withModules %s)\n (.start \"main\"))\n" 96 | (format 97 | (str "#js " (with-out-str (println modules-map)))) 98 | ((partial spit "env/dev/env/index.cljs"))) 99 | 100 | (catch Exception e 101 | (println "Error: " e))))) 102 | 103 | ;; Each file maybe corresponds to multiple modules. 104 | (defn watch-for-external-modules 105 | [] 106 | (let [path ".js-modules.edn"] 107 | (hawk/watch! [{:paths ["src"] 108 | :filter hawk/file? 109 | :handler (fn [ctx {:keys [kind file] :as event}] 110 | (let [m (edn/read-string (slurp path)) 111 | file-name (-> (.getPath file) 112 | (str/replace (str (System/getProperty "user.dir") "/") ""))] 113 | 114 | ;; file is deleted 115 | (when (= :delete kind) 116 | (let [new-m (dissoc m file-name)] 117 | (spit path new-m) 118 | (rebuild-env-index (flatten (vals new-m))))) 119 | 120 | (when (.exists file) 121 | (let [content (slurp file) 122 | js-modules (some->> 123 | content 124 | (re-seq #"\(js/require \"([^\"]+)\"\)") 125 | (map last) 126 | (vec)) 127 | commented-modules (some->> 128 | content 129 | (re-seq #"[;]+[\s]*\(js/require \"([^\"]+)\"\)") 130 | (map last) 131 | (set)) 132 | js-modules (if commented-modules 133 | (vec (remove commented-modules js-modules)) 134 | js-modules)] 135 | (let [old-js-modules (get m file-name)] 136 | (when (not= old-js-modules js-modules) 137 | (let [new-m (if (seq js-modules) 138 | (assoc m file-name js-modules) 139 | (dissoc m file-name))] 140 | (spit path new-m) 141 | 142 | (rebuild-env-index (flatten (vals new-m))))))))) 143 | ctx)}]))) 144 | 145 | (defn rebuild-modules 146 | [] 147 | (let [path ".js-modules.edn" 148 | m (atom {})] 149 | ;; delete path 150 | (when (.exists (java.io.File. path)) 151 | (clojure.java.io/delete-file path)) 152 | 153 | (doseq [file (file-seq (java.io.File. "src"))] 154 | (when (.isFile file) 155 | (let [file-name (-> (.getPath file) 156 | (str/replace (str (System/getProperty "user.dir") "/") "")) 157 | content (slurp file) 158 | js-modules (some->> 159 | content 160 | (re-seq #"\(js/require \"([^\"]+)\"\)") 161 | (map last) 162 | (vec)) 163 | commented-modules (some->> 164 | content 165 | (re-seq #"[;]+[\s]*\(js/require \"([^\"]+)\"\)") 166 | (map last) 167 | (set)) 168 | js-modules (if commented-modules 169 | (vec (remove commented-modules js-modules)) 170 | js-modules)] 171 | (if js-modules 172 | (swap! m assoc file-name (vec js-modules)))))) 173 | (spit path @m) 174 | (rebuild-env-index (flatten (vals @m))))) 175 | 176 | (defn init-external-modules 177 | [] 178 | (rebuild-modules)) 179 | 180 | ;; Lein 181 | (defn start-figwheel 182 | "Start figwheel for one or more builds" 183 | [& build-ids] 184 | (init-external-modules) 185 | (enable-source-maps) 186 | (write-main-js) 187 | (write-env-dev) 188 | (watch-for-external-modules) 189 | (ra/start-figwheel! 190 | {:figwheel-options {} 191 | :build-ids (if (seq build-ids) 192 | build-ids 193 | ["main"]) 194 | :all-builds (get-cljs-builds)}) 195 | (ra/cljs-repl)) 196 | 197 | (defn stop-figwheel 198 | "Stops figwheel" 199 | [] 200 | (ra/stop-figwheel!)) 201 | 202 | (defn -main 203 | [args] 204 | (case args 205 | "--figwheel" 206 | (start-figwheel) 207 | 208 | "--rebuild-modules" 209 | (rebuild-modules) 210 | 211 | (prn "You can run lein figwheel or lein rebuild-modules."))) 212 | 213 | ;; Boot 214 | (defn prepare 215 | [] 216 | (init-external-modules) 217 | (enable-source-maps) 218 | (write-main-js) 219 | (write-env-dev) 220 | (watch-for-external-modules)) 221 | -------------------------------------------------------------------------------- /examples/re-frame/expo-example/env/dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require [figwheel-sidecar.repl-api :as ra] 3 | [clojure.java.io :as io] 4 | [clojure.string :as str] 5 | [hawk.core :as hawk] 6 | [clojure.tools.reader.edn :as edn] 7 | [clojure.set :as set])) 8 | ;; This namespace is loaded automatically by nREPL 9 | 10 | (defn get-cljs-builds 11 | [] 12 | (let [project-config (->> "project.clj" 13 | slurp 14 | read-string 15 | (drop 1) 16 | (apply hash-map)) 17 | profiles (:profiles project-config)] 18 | (get-in profiles [:dev :cljsbuild :builds]))) 19 | 20 | (defn enable-source-maps 21 | [] 22 | (println "Source maps enabled.") 23 | (let [path "node_modules/metro-bundler/src/Server/index.js"] 24 | (spit path 25 | (str/replace (slurp path) "/\\.map$/" "/main.map$/")))) 26 | 27 | (defn write-main-js 28 | [] 29 | (-> "'use strict';\n\n// cljsbuild adds a preamble mentioning goog so hack around it\nwindow.goog = {\n provide() {},\n require() {},\n};\nrequire('./target/env/index.js');\n" 30 | ((partial spit "main.js")))) 31 | 32 | (defn get-lan-ip 33 | [] 34 | (cond 35 | (= "Mac OS X" (System/getProperty "os.name")) 36 | (.getHostAddress (java.net.InetAddress/getLocalHost)) 37 | 38 | :else 39 | (->> (java.net.NetworkInterface/getNetworkInterfaces) 40 | (enumeration-seq) 41 | (filter #(not (or (str/starts-with? (.getName %) "docker") 42 | (str/starts-with? (.getName %) "vboxnet") 43 | (str/starts-with? (.getName %) "br-")))) 44 | (map #(.getInterfaceAddresses %)) 45 | (map 46 | (fn [ip] 47 | (seq (filter #(instance? 48 | java.net.Inet4Address 49 | (.getAddress %)) 50 | ip)))) 51 | (remove nil?) 52 | (first) 53 | (filter #(instance? 54 | java.net.Inet4Address 55 | (.getAddress %))) 56 | (first) 57 | (.getAddress) 58 | (.getHostAddress)))) 59 | 60 | (defn write-env-dev 61 | [] 62 | (let [hostname (.getHostName (java.net.InetAddress/getLocalHost)) 63 | ip (get-lan-ip)] 64 | (-> "(ns env.dev)\n(def hostname \"%s\")\n(def ip \"%s\")" 65 | (format 66 | hostname 67 | ip) 68 | ((partial spit "env/dev/env/dev.cljs"))))) 69 | 70 | (defn rebuild-env-index 71 | [js-modules] 72 | (let [modules (->> (file-seq (io/file "assets")) 73 | (filter #(and (not (re-find #"DS_Store" (str %))) 74 | (.isFile %))) 75 | (map (fn [file] (when-let [path (str file)] 76 | (str "../../" path)))) 77 | (concat js-modules ["react" "react-native" "expo" "create-react-class"]) 78 | (distinct)) 79 | modules-map (zipmap 80 | (->> modules 81 | (map #(str "\"" 82 | (if (str/starts-with? % "../../assets") 83 | (-> % 84 | (str/replace "../../" "./") 85 | (str/replace "@2x" "") 86 | (str/replace "@3x" "")) 87 | %) 88 | "\""))) 89 | (->> modules 90 | (map #(format "(js/require \"%s\")" 91 | (-> % 92 | (str/replace "@2x" "") 93 | (str/replace "@3x" ""))))))] 94 | (try 95 | (-> "(ns env.index\n (:require [env.dev :as dev]))\n\n;; undo main.js goog preamble hack\n(set! js/window.goog js/undefined)\n\n(-> (js/require \"figwheel-bridge\")\n (.withModules %s)\n (.start \"main\"))\n" 96 | (format 97 | (str "#js " (with-out-str (println modules-map)))) 98 | ((partial spit "env/dev/env/index.cljs"))) 99 | 100 | (catch Exception e 101 | (println "Error: " e))))) 102 | 103 | ;; Each file maybe corresponds to multiple modules. 104 | (defn watch-for-external-modules 105 | [] 106 | (let [path ".js-modules.edn"] 107 | (hawk/watch! [{:paths ["src"] 108 | :filter hawk/file? 109 | :handler (fn [ctx {:keys [kind file] :as event}] 110 | (let [m (edn/read-string (slurp path)) 111 | file-name (-> (.getPath file) 112 | (str/replace (str (System/getProperty "user.dir") "/") ""))] 113 | 114 | ;; file is deleted 115 | (when (= :delete kind) 116 | (let [new-m (dissoc m file-name)] 117 | (spit path new-m) 118 | (rebuild-env-index (flatten (vals new-m))))) 119 | 120 | (when (.exists file) 121 | (let [content (slurp file) 122 | js-modules (some->> 123 | content 124 | (re-seq #"\(js/require \"([^\"]+)\"\)") 125 | (map last) 126 | (vec)) 127 | commented-modules (some->> 128 | content 129 | (re-seq #"[;]+[\s]*\(js/require \"([^\"]+)\"\)") 130 | (map last) 131 | (set)) 132 | js-modules (if commented-modules 133 | (vec (remove commented-modules js-modules)) 134 | js-modules)] 135 | (let [old-js-modules (get m file-name)] 136 | (when (not= old-js-modules js-modules) 137 | (let [new-m (if (seq js-modules) 138 | (assoc m file-name js-modules) 139 | (dissoc m file-name))] 140 | (spit path new-m) 141 | 142 | (rebuild-env-index (flatten (vals new-m))))))))) 143 | ctx)}]))) 144 | 145 | (defn rebuild-modules 146 | [] 147 | (let [path ".js-modules.edn" 148 | m (atom {})] 149 | ;; delete path 150 | (when (.exists (java.io.File. path)) 151 | (clojure.java.io/delete-file path)) 152 | 153 | (doseq [file (file-seq (java.io.File. "src"))] 154 | (when (.isFile file) 155 | (let [file-name (-> (.getPath file) 156 | (str/replace (str (System/getProperty "user.dir") "/") "")) 157 | content (slurp file) 158 | js-modules (some->> 159 | content 160 | (re-seq #"\(js/require \"([^\"]+)\"\)") 161 | (map last) 162 | (vec)) 163 | commented-modules (some->> 164 | content 165 | (re-seq #"[;]+[\s]*\(js/require \"([^\"]+)\"\)") 166 | (map last) 167 | (set)) 168 | js-modules (if commented-modules 169 | (vec (remove commented-modules js-modules)) 170 | js-modules)] 171 | (if js-modules 172 | (swap! m assoc file-name (vec js-modules)))))) 173 | (spit path @m) 174 | (rebuild-env-index (flatten (vals @m))))) 175 | 176 | (defn init-external-modules 177 | [] 178 | (rebuild-modules)) 179 | 180 | ;; Lein 181 | (defn start-figwheel 182 | "Start figwheel for one or more builds" 183 | [& build-ids] 184 | (init-external-modules) 185 | (enable-source-maps) 186 | (write-main-js) 187 | (write-env-dev) 188 | (watch-for-external-modules) 189 | (ra/start-figwheel! 190 | {:figwheel-options {} 191 | :build-ids (if (seq build-ids) 192 | build-ids 193 | ["main"]) 194 | :all-builds (get-cljs-builds)}) 195 | (ra/cljs-repl)) 196 | 197 | (defn stop-figwheel 198 | "Stops figwheel" 199 | [] 200 | (ra/stop-figwheel!)) 201 | 202 | (defn -main 203 | [args] 204 | (case args 205 | "--figwheel" 206 | (start-figwheel) 207 | 208 | "--rebuild-modules" 209 | (rebuild-modules) 210 | 211 | (prn "You can run lein figwheel or lein rebuild-modules."))) 212 | 213 | ;; Boot 214 | (defn prepare 215 | [] 216 | (init-external-modules) 217 | (enable-source-maps) 218 | (write-main-js) 219 | (write-env-dev) 220 | (watch-for-external-modules)) 221 | -------------------------------------------------------------------------------- /examples/re-frame/uiexplorer/js/figwheel-bridge.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Originally taken from https://github.com/decker405/figwheel-react-native 3 | * 4 | * @providesModule figwheel-bridge 5 | */ 6 | 7 | var CLOSURE_UNCOMPILED_DEFINES = null; 8 | var debugEnabled = false; 9 | 10 | var config = { 11 | basePath: "target/", 12 | googBasePath: 'goog/', 13 | serverPort: 19001 14 | }; 15 | 16 | var React = require('react'); 17 | var ReactNative = require('react-native'); 18 | var WebSocket = require('WebSocket'); 19 | var createReactClass = require('create-react-class'); 20 | var self; 21 | var scriptQueue = []; 22 | var serverHost = null; // will be set dynamically 23 | var fileBasePath = null; // will be set dynamically 24 | var evaluate = eval; // This is needed, direct calls to eval does not work (RN packager???) 25 | var externalModules = {}; 26 | var evalListeners = [ // Functions to be called after each js file is loaded and evaluated 27 | function (url) { 28 | if (url.indexOf('jsloader') > -1) { 29 | shimJsLoader(); 30 | } 31 | }, 32 | function (url) { 33 | if (url.indexOf('/figwheel/client/socket') > -1) { 34 | setCorrectWebSocketImpl(); 35 | } 36 | }]; 37 | 38 | var figwheelApp = function (platform, devHost) { 39 | return createReactClass({ 40 | getInitialState: function () { 41 | return {loaded: false} 42 | }, 43 | render: function () { 44 | if (!this.state.loaded) { 45 | var plainStyle = {flex: 1, alignItems: 'center', justifyContent: 'center'}; 46 | return ( 47 | 48 | Waiting for Figwheel to load files. 49 | 50 | ); 51 | } 52 | return this.state.root; 53 | }, 54 | componentDidMount: function () { 55 | var app = this; 56 | if (typeof goog === "undefined") { 57 | var url = this.props.url || 58 | this.props.exp.manifest.bundleUrl; 59 | var hostPort = url.split('/')[2].split(':'); 60 | devHost = hostPort[0]; 61 | 62 | if (hostPort[1]) { 63 | config.serverPort = hostPort[1]; 64 | } 65 | 66 | loadApp(platform, devHost, function (appRoot) { 67 | app.setState({root: appRoot, loaded: true}) 68 | }); 69 | } 70 | } 71 | }) 72 | }; 73 | 74 | function logDebug(msg) { 75 | if (debugEnabled) { 76 | console.log(msg); 77 | } 78 | } 79 | 80 | // evaluates js code ensuring proper ordering 81 | function customEval(url, javascript, success, error) { 82 | if (scriptQueue.length > 0) { 83 | if (scriptQueue[0] === url) { 84 | try { 85 | evaluate(javascript); 86 | logDebug('Evaluated: ' + url); 87 | scriptQueue.shift(); 88 | evalListeners.forEach(function (listener) { 89 | listener(url) 90 | }); 91 | success(); 92 | } catch (e) { 93 | console.error(e); 94 | error(); 95 | } 96 | } else { 97 | setTimeout(function () { 98 | customEval(url, javascript, success, error) 99 | }, 5); 100 | } 101 | } else { 102 | console.error('Something bad happened...'); 103 | error() 104 | } 105 | } 106 | 107 | var isChrome = function () { 108 | return typeof importScripts === "function" 109 | }; 110 | 111 | function asyncImportScripts(url, success, error) { 112 | logDebug('(asyncImportScripts) Importing: ' + url); 113 | scriptQueue.push(url); 114 | fetch(url) 115 | .then(function (response) { 116 | return response.text() 117 | }) 118 | .then(function (responseText) { 119 | return customEval(url, responseText, success, error); 120 | }) 121 | .catch(function (error) { 122 | console.error(error); 123 | return error(); 124 | }); 125 | } 126 | 127 | function syncImportScripts(url, success, error) { 128 | try { 129 | importScripts(url); 130 | logDebug('Evaluated: ' + url); 131 | evalListeners.forEach(function (listener) { 132 | listener(url) 133 | }); 134 | success(); 135 | } catch (e) { 136 | console.error(e); 137 | error() 138 | } 139 | } 140 | 141 | // Loads js file sync if possible or async. 142 | function importJs(src, success, error) { 143 | if (typeof success !== 'function') { 144 | success = function () { 145 | }; 146 | } 147 | if (typeof error !== 'function') { 148 | error = function () { 149 | }; 150 | } 151 | 152 | var file = fileBasePath + '/' + src; 153 | 154 | logDebug('(importJs) Importing: ' + file); 155 | if (isChrome()) { 156 | syncImportScripts(serverBaseUrl("localhost") + '/' + file, success, error); 157 | } else { 158 | asyncImportScripts(serverBaseUrl(serverHost) + '/' + file, success, error); 159 | } 160 | } 161 | 162 | function interceptRequire() { 163 | var oldRequire = window.require; 164 | console.info("Shimming require"); 165 | window.require = function (id) { 166 | console.info("Requiring: " + id); 167 | if (externalModules[id]) { 168 | return externalModules[id]; 169 | } 170 | return oldRequire(id); 171 | }; 172 | } 173 | 174 | function compileWarningsToYellowBox() { 175 | var log = window.console.log; 176 | var compileWarningRx = /Figwheel: Compile/; 177 | var compileExceptionRx = /Figwheel: Compile Exception/; 178 | var errorInFileRx = /Error on file/; 179 | var isBuffering = false; 180 | var compileExceptionBuffer = ""; 181 | window.console.log = function (msg) { 182 | log.apply(window.console, arguments); 183 | if (compileExceptionRx.test(msg)) { // enter buffering mode to get all the messages for exception 184 | isBuffering = true; 185 | compileExceptionBuffer = msg + "\n"; 186 | } else if (errorInFileRx.test(msg) && isBuffering) { // exit buffering mode and log buffered messages to YellowBox 187 | isBuffering = false; 188 | console.warn(compileExceptionBuffer + msg); 189 | compileExceptionBuffer = ""; 190 | } else if (isBuffering) { //log messages buffering mode 191 | compileExceptionBuffer += msg + "\n"; 192 | } else if (compileWarningRx.test(msg)) { 193 | console.warn(msg); 194 | } 195 | }; 196 | } 197 | 198 | function serverBaseUrl(host) { 199 | return "http://" + host + ":" + config.serverPort 200 | } 201 | 202 | function setCorrectWebSocketImpl() { 203 | figwheel.client.socket.get_websocket_imp = function () { 204 | return WebSocket; 205 | }; 206 | } 207 | 208 | function loadApp(platform, devHost, onLoadCb) { 209 | serverHost = devHost; 210 | fileBasePath = config.basePath; 211 | 212 | // callback when app is ready to get the reloadable component 213 | var mainJs = '/env/main.js'; 214 | evalListeners.push(function (url) { 215 | if (url.indexOf(mainJs) > -1) { 216 | onLoadCb(env.main.root_el); 217 | console.info('Done loading Clojure app'); 218 | } 219 | }); 220 | 221 | if (typeof goog === "undefined") { 222 | console.info('Loading Closure base.'); 223 | interceptRequire(); 224 | compileWarningsToYellowBox(); 225 | importJs('goog/base.js', function () { 226 | shimBaseGoog(); 227 | importJs('cljs_deps.js'); 228 | importJs('goog/deps.js', function () { 229 | // This is needed because of RN packager 230 | // seriously React packager? why. 231 | var googreq = goog.require; 232 | 233 | googreq('figwheel.connect.build_main'); 234 | }); 235 | }); 236 | } 237 | } 238 | 239 | function startApp(appName, platform, devHost) { 240 | ReactNative.AppRegistry.registerComponent( 241 | appName, () => figwheelApp(platform, devHost)); 242 | } 243 | 244 | function withModules(moduleById) { 245 | externalModules = moduleById; 246 | return self; 247 | } 248 | 249 | // Goog fixes 250 | function shimBaseGoog() { 251 | console.info('Shimming goog functions.'); 252 | goog.basePath = 'goog/'; 253 | goog.writeScriptSrcNode = importJs; 254 | goog.writeScriptTag_ = function (src, optSourceText) { 255 | importJs(src); 256 | return true; 257 | }; 258 | } 259 | 260 | // Figwheel fixes 261 | // Used by figwheel - uses importScript to load JS rather than