drapanjanas / re-natal

Bootstrap ClojureScript React Native apps
MIT License
1.22k stars 100 forks source link

use ios/android only module in same project #81

Closed ghost closed 7 years ago

ghost commented 7 years ago

When use ios or android only modules in the same project, the index.ios.js and index.android.js will include wrong modules, for example:

"modules": [ "react-native-qr-decoder", // ios only "react-native-barcodescanner" // android only ],

the generated index.*.js file will include all modules, which causes loading errors. Maybe add more directives in the .re-natal file to differentiate this kind of case.

drapanjanas commented 7 years ago

True, need to extend command re-natal use-component to accept platform option, if provided it would add to platform specific list in .re-natal file. Then, index.*.js could be generated combining common and platform-specific components

seantempesta commented 7 years ago

Any chance we could change direction on this? I recently came back to cljsrn development after a two month break and spent at least two hours banging my head against the wall because I forgot to issue a use-component command.

What about creating a react-requires.cljs file with require statements in it? We could have figwheel generate the index.ios.js and index.android.js files from it AND separately build the rest of the project with it just being required.

(ns future-app.react-requires)

(defonce React (js/require "react"))
(defonce ReactNative (js/require "react-native"))
(defonce Platform (aget ReactNative "Platform"))

(declare IOSModule)
(declare AndroidModule)

(if (aget Platform "ios")
  (defonce IOSModule (js/require "react-native-ios-specific"))
  (defonce AndroidModule (js/require "react-native-android-specific")))

The goal being to stop using a separate process to require modules. We can also inject other code as necessary (including images files in the images directory?).

Because the current way of doing things is confusing to new developers ... and my poor memory. :)

drapanjanas commented 7 years ago

Sure @seantempesta, I like the idea. Getting rid of use-component would be the best. But I do not fully understand what technically you mean by

figwheel generate the index.ios.js and index.android.js from it

The problem is that, say, I have compiled namespace future-app.react-requires (which is google closure code) I somehow need this js code to be required from my generated index.*.js file so that packager prepares all required modules.

seantempesta commented 7 years ago

Please correct me if I'm wrong (as I'm uncertain about how things are actually working), but if you had something like this (I'm just writing pseudo code):

(ns future-app.react-requires)

(defonce React (js/require "react"))
(defonce ReactNative (js/require "react-native"))
(defonce OtherModule (js/require "react-native-other-module"))
(ns future-app.ios.core
 (:require  [future-app.react-requires]
            [reagent.core :as r]))

;;; normal re-natal app code
(defn init
....
(ns future-app.figwheel-loader
  (:require  [future-app.react-requires :refer [ReactNative]]
             [reagent.core :as r]))

(defonce AppRegistry (aget ReactNative "AppRegistry"))
(defonce Text (aget ReactNative "Text"))
(defonce View (aget ReactNative "View"))

(defn app-root []
  (fn []
    [:> View {}
     [:> Text {} "Figwheel Loading..."]]))

(.registerComponent AppRegistry "FutureApp" #(r/reactify-component app-root))

;; Additional code to launch figwheel and load the app ...

So if you have figwheel compiling index.ios.js using only figwheel-loader.cljs with simple compilation, it would produce a working index.ios.js file that the react packager could consume, parse out the requires and bootstrap the app.

As a separate figwheel target you compile future-app.ios.core with optimizations: none which builds the whole app (and in this app you also require react-requires.cljs.

Okay, so this is the part I don't fully understand, but somehow you are loading figwheel and dynamically loading the app. This part would go after the AppRegistry section.

Basically, if we wrote the figwheel-bridge.js code in clojure (or maybe you can load it from future-app.figwheel-loader?) you could share the react-requires namespace. Then if the user changes it, it would trigger a rebuild of both the index.ios.js file and the app. The packager would be happy and so would the app.

drapanjanas commented 7 years ago

With optimizations :simple its all good since compilation produces a single file with all deps included. And there are several problems with optimizations :none.

One problem is that produced target file (in re-natal it is named not-used.js) is not what can be used with RN. So instead of that, in re-natal we generate a static index.ios.js file to bootstrap the app and load all dependencies (in cljs_deps.js) dynamically using just eval. Currently I see no way of producing usable index.*.js with just clojusecript compiler, I have tried playing around withtarget :nodejs but wasn't able to find a way of making it work so far.

Second problem is that RN packager does not understand closure module/dependency system. So, the bundle prepared by packager does not contain the dependencies. In other words, packager is supposed to do something similar to what is the output when we compile code with optimizations :simple - resolve all deps and produce one js bundle with all deps. This is why re-natal need to load all js files dynamically even when loading app for the first time (and not only when figwheel needs to reload something). Since RN packager can not follow the dependencies of the app itself, it also does not know what additional modules which are require'd by the app. So the current workaround is to put those requires directly in index.ios.js which is served by packager.

I like the improvement idea that these requires could be taken from some namespace instead of adding them manually (to .re-natal config) with use-component command. I will try to make it work when I have enough time. But again, I do not see it working the way you described it.

Anyways, probably the best solution would be is to teach somehow the RN packager to build the bundle out of the google closure compiled files in optimizations :none. This would remove most of the current hacks. But, I am not feeling capable of learning JS and the packager that deep to do that :) I think BRN tool is much closer to this than re-natal, need to look deeper how things are done there.

seantempesta commented 7 years ago

I tried to mock up what I was talking about and realized this is a lot more complicated than I thought. I think you're right that our best bet is to get the react native package manager consuming google closure modules, and until then your solution seems to be the best alternative.