mirror of
https://github.com/status-im/re-natal.git
synced 2025-02-28 04:00:34 +00:00
commit
498de825d2
57
README.md
57
README.md
@ -11,7 +11,7 @@ setting up a React Native app running on ClojureScript.
|
||||
It stands firmly on the shoulders of giants, specifically those of
|
||||
[Mike Fikes](http://blog.fikesfarm.com) who created
|
||||
[Ambly](https://github.com/omcljs/ambly) and the
|
||||
[documentation](https://github.com/omcljs/ambly/wiki/ClojureScript-React-Native-Quick-Start)
|
||||
[documentation](http://cljsrn.org/ambly.html)
|
||||
on setting up a ClojureScript React Native app.
|
||||
|
||||
|
||||
@ -26,31 +26,34 @@ Then, install the CLI using npm:
|
||||
$ npm install -g natal
|
||||
```
|
||||
|
||||
Then run `natal` with your app's name as the first argument:
|
||||
To bootstrap a new app, run `natal init` with your app's name as an argument:
|
||||
|
||||
```
|
||||
$ natal FutureApp
|
||||
$ natal init FutureApp
|
||||
```
|
||||
|
||||
If your app is more than a single word, be sure to type it in CamelCase.
|
||||
If your app's name is more than a single word, be sure to type it in CamelCase.
|
||||
A corresponding hyphenated Clojure namespace will be created.
|
||||
|
||||
When Xcode appears, click the play button (or ⌘-R) to run the app on the simulator.
|
||||
If all goes well your app should compile and boot in the simulator.
|
||||
|
||||
Then run the following for an interactive workflow:
|
||||
From there you can begin an interactive workflow by starting the REPL.
|
||||
|
||||
```
|
||||
$ cd future-app
|
||||
$ ./start.sh
|
||||
$ natal repl
|
||||
```
|
||||
|
||||
First, choose the correct device (probably `[1]`). At the REPL prompt type this:
|
||||
Choose your app from the list the REPL outputs (probably `1`) so Ambly can connect.
|
||||
|
||||
At the prompt, try loading your app's namespace:
|
||||
|
||||
```clojure
|
||||
(in-ns 'future-app.core)
|
||||
```
|
||||
|
||||
Changes you make via the REPL or by changing your .cljs files should appear live.
|
||||
Changes you make via the REPL or by changing your `.cljs` files should appear live
|
||||
in the simulator.
|
||||
|
||||
Try this command as an example:
|
||||
|
||||
@ -58,8 +61,8 @@ Try this command as an example:
|
||||
(swap! app-state assoc :text "Hello Native World")
|
||||
```
|
||||
|
||||
When the REPL starts it will print the location of its compilation log.
|
||||
It's useful to tail it to see any errors, like so:
|
||||
When the REPL connects to the simulator it will print the location of its
|
||||
compilation log. It's useful to tail it to see any errors, like so:
|
||||
|
||||
```
|
||||
$ tail -f /Volumes/Ambly-81C53995/watch.log
|
||||
@ -67,11 +70,27 @@ $ tail -f /Volumes/Ambly-81C53995/watch.log
|
||||
|
||||
|
||||
## Tips
|
||||
- Having `rlwrap` installed is optional but recommended since it makes the REPL
|
||||
a much nicer experience with arrow keys
|
||||
- Having `rlwrap` installed is optional but highly recommended since it makes
|
||||
the REPL a much nicer experience with arrow keys.
|
||||
|
||||
- Don't press ⌘-R in the simulator; code changes should be reflected automatically.
|
||||
See [this issue](https://github.com/omcljs/ambly/issues/97) in Ambly for details
|
||||
- Running multiple React Native apps at once can cause problems
|
||||
See [this issue](https://github.com/omcljs/ambly/issues/97) in Ambly for details.
|
||||
|
||||
- Running multiple React Native apps at once can cause problems with the React
|
||||
Packager so try to avoid doing so.
|
||||
|
||||
- You can launch your app on the simulator without opening Xcode by running
|
||||
`natal launch` in your app's root directory.
|
||||
|
||||
- By default new Natal projects will launch on the iPhone 6 simulator. To change
|
||||
which device `natal launch` uses, you can run `natal listdevices` to see a list
|
||||
of available simulators, then select one by running `natal setdevice` with the
|
||||
index of the device on the list.
|
||||
|
||||
- To change advanced settings run `natal xcode` to quickly open the Xcode project.
|
||||
|
||||
- The Xcode-free workflow is for convenience. If you're encountering app crashes,
|
||||
you should open the Xcode project and run it from there to view errors.
|
||||
|
||||
|
||||
## Dependencies
|
||||
@ -86,17 +105,21 @@ tools.
|
||||
- [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
|
||||
- [CocoaPods](https://cocoapods.org) `>=0.38.2`
|
||||
- [Ruby](https://www.ruby-lang.org) `>=2.0.0`
|
||||
- [Xcode](https://developer.apple.com/xcode) `>=6.3`
|
||||
- [Xcode](https://developer.apple.com/xcode) (+ Command Line Tools) `>=6.3`
|
||||
- [OS X](http://www.apple.com/osx) `>=10.10`
|
||||
- [Watchman](https://facebook.github.io/watchman) `>=3.7.0`
|
||||
|
||||
|
||||
## Aspirations
|
||||
- [x] Xcode-free workflow with CLI tools
|
||||
- [ ] Automatic wrapping of all React Native component functions for ClojureScript
|
||||
- [ ] Xcode-free development with CLI tools
|
||||
- [ ] Automatically run React packager in background
|
||||
- [ ] Automatically tail cljs build log and report compile errors
|
||||
- [ ] Templates for other ClojureScript React wrappers
|
||||
- [ ] Automatic bundling for offline device usage and App Store distribution
|
||||
- [ ] Android support
|
||||
|
||||
|
||||
Contributions are welcome.
|
||||
|
||||
For more ClojureScript React Native resources visit [cljsrn.org](http://cljsrn.org).
|
||||
|
2
index.js
2
index.js
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('coffee-script/register');
|
||||
require('./main');
|
||||
require('./natal');
|
||||
|
176
main.coffee
176
main.coffee
@ -1,176 +0,0 @@
|
||||
# Natal
|
||||
# Bootstrap ClojureScript React Native apps
|
||||
# Dan Motzenbecker
|
||||
# http://oxism.com
|
||||
# MIT License
|
||||
|
||||
fs = require 'fs'
|
||||
crypto = require 'crypto'
|
||||
{execSync} = require 'child_process'
|
||||
chalk = require 'chalk'
|
||||
semver = require 'semver'
|
||||
reactInit = require 'react-native/local-cli/init'
|
||||
rnVersion = require(__dirname + '/package.json').dependencies['react-native']
|
||||
|
||||
resources = __dirname + '/resources/'
|
||||
camelRx = /([a-z])([A-Z])/g
|
||||
projNameRx = /\$PROJECT_NAME\$/g
|
||||
projNameHyphRx = /\$PROJECT_NAME_HYPHENATED\$/g
|
||||
projNameUnderRx = /\$PROJECT_NAME_UNDERSCORED\$/g
|
||||
|
||||
log = (s, color = 'green') ->
|
||||
console.log chalk[color] s
|
||||
|
||||
|
||||
logErr = (err, color = 'red') ->
|
||||
console.error chalk[color] err
|
||||
|
||||
|
||||
editSync = (path, pairs) ->
|
||||
fs.writeFileSync path, pairs.reduce (contents, [rx, replacement]) ->
|
||||
contents.replace rx, replacement
|
||||
, fs.readFileSync path, encoding: 'ascii'
|
||||
|
||||
|
||||
init = (projName) ->
|
||||
projNameHyph = projName.replace(camelRx, '$1-$2').toLowerCase()
|
||||
projNameUs = projName.replace(camelRx, '$1_$2').toLowerCase()
|
||||
|
||||
try
|
||||
log "Creating #{ projName }", 'bgMagenta'
|
||||
log ''
|
||||
|
||||
if fs.existsSync projNameHyph
|
||||
throw new Error "Directory #{ projNameHyph } already exists"
|
||||
|
||||
execSync 'type lein'
|
||||
execSync 'type pod'
|
||||
podVersion = execSync('pod --version').toString().trim()
|
||||
unless semver.satisfies podVersion, '>=0.36.4'
|
||||
throw new Error "Natal requires CocoaPods 0.36.4 or higher (you have #{ podVersion }).
|
||||
\nRun [sudo] gem update cocoapods and try again."
|
||||
|
||||
log 'Creating Leiningen project'
|
||||
execSync "lein new #{ projNameHyph }"
|
||||
|
||||
log 'Updating Leiningen project'
|
||||
process.chdir projNameHyph
|
||||
execSync "cp #{ resources }project.clj project.clj"
|
||||
editSync 'project.clj', [[projNameHyphRx, projNameHyph]]
|
||||
corePath = "src/#{ projNameUs }/core.clj"
|
||||
fs.unlinkSync corePath
|
||||
corePath += 's'
|
||||
execSync "cp #{ resources }core.cljs #{ corePath }"
|
||||
editSync corePath, [[projNameHyphRx, projNameHyph], [projNameRx, projName]]
|
||||
execSync "cp #{ resources }ambly.sh start.sh"
|
||||
editSync 'start.sh', [[projNameUnderRx, projNameUs]]
|
||||
|
||||
log 'Compiling ClojureScript'
|
||||
execSync 'lein cljsbuild once dev'
|
||||
|
||||
log 'Creating React Native skeleton'
|
||||
fs.mkdirSync 'iOS'
|
||||
process.chdir 'iOS'
|
||||
_log = console.log
|
||||
global.console.log = ->
|
||||
reactInit '.', projName
|
||||
global.console.log = _log
|
||||
fs.writeFileSync 'package.json', JSON.stringify
|
||||
name: projName
|
||||
version: '0.0.1'
|
||||
private: true
|
||||
scripts:
|
||||
start: 'node_modules/react-native/packager/packager.sh'
|
||||
dependencies:
|
||||
'react-native': rnVersion
|
||||
, null, 2
|
||||
execSync 'npm i', stdio: 'ignore'
|
||||
|
||||
log 'Installing Pod dependencies'
|
||||
process.chdir 'iOS'
|
||||
execSync "cp #{ resources }Podfile ."
|
||||
execSync 'pod install', stdio: 'ignore'
|
||||
|
||||
log 'Updating Xcode project'
|
||||
for ext in ['m', 'h']
|
||||
path = "#{ projName }/AppDelegate.#{ ext }"
|
||||
execSync "cp #{ resources }AppDelegate.#{ ext } #{ path }"
|
||||
editSync path, [[projNameRx, projName], [projNameHyphRx, projNameHyph]]
|
||||
|
||||
uuid1 = crypto
|
||||
.createHash 'md5'
|
||||
.update projName, 'utf8'
|
||||
.digest('hex')[...24]
|
||||
.toUpperCase()
|
||||
|
||||
uuid2 = uuid1.split ''
|
||||
uuid2.splice 7, 1, ((parseInt(uuid1[7], 16) + 1) % 16).toString(16).toUpperCase()
|
||||
uuid2 = uuid2.join ''
|
||||
|
||||
editSync \
|
||||
"#{ projName }.xcodeproj/project.pbxproj",
|
||||
[
|
||||
[
|
||||
/OTHER_LDFLAGS = "-ObjC";/g
|
||||
'OTHER_LDFLAGS = "${inherited}";'
|
||||
]
|
||||
[
|
||||
/\/\* End PBXBuildFile section \*\//
|
||||
"\t\t#{ uuid2 } /* out in Resources */ =
|
||||
{isa = PBXBuildFile; fileRef = #{ uuid1 } /* out */; };
|
||||
\n/* End PBXBuildFile section */"
|
||||
]
|
||||
[
|
||||
/\/\* End PBXFileReference section \*\//
|
||||
"\t\t#{ uuid1 } /* out */ = {isa = PBXFileReference; lastKnownFileType
|
||||
= folder; name = out; path = ../../../target/out;
|
||||
sourceTree = \"<group>\"; };\n/* End PBXFileReference section */"
|
||||
]
|
||||
[
|
||||
/main.jsbundle \*\/\,/
|
||||
"main.jsbundle */,\n\t\t\t\t#{ uuid1 } /* out */,"
|
||||
]
|
||||
[
|
||||
/\/\* LaunchScreen.xib in Resources \*\/\,/
|
||||
"/* LaunchScreen.xib in Resources */,
|
||||
\n\t\t\t\t#{ uuid2 } /* out in Resources */,"
|
||||
]
|
||||
]
|
||||
|
||||
execSync "open #{ projName }.xcworkspace"
|
||||
log '\nWhen Xcode appears, click the play button to run the app on the simulator.', 'yellow'
|
||||
log 'Then run the following for an interactive workflow:', 'yellow'
|
||||
log "cd #{ projNameHyph }", 'inverse'
|
||||
log './start.sh', 'inverse'
|
||||
log 'First, choose the correct device (Probably [1]).', 'yellow'
|
||||
log 'At the REPL prompt type this:', 'yellow'
|
||||
log "(in-ns '#{ projNameHyph }.core)", 'inverse'
|
||||
log 'Changes you make via the REPL or by changing your .cljs files should appear live.', 'yellow'
|
||||
log 'Try this command as an example:', 'yellow'
|
||||
log '(swap! app-state assoc :text "Hello Native World")', 'inverse'
|
||||
log ''
|
||||
log '✔ Done', 'bgMagenta'
|
||||
log ''
|
||||
|
||||
|
||||
catch e
|
||||
if e.message.match /type\:.+lein/i
|
||||
logErr 'Leiningen is required (http://leiningen.org/)'
|
||||
|
||||
else if e.message.match /type\:.+pod/i
|
||||
logErr 'CocoaPods is required (https://cocoapods.org/)'
|
||||
|
||||
else
|
||||
logErr e.message
|
||||
|
||||
process.exit 1
|
||||
|
||||
|
||||
[_, _, name] = process.argv
|
||||
|
||||
unless name
|
||||
logErr 'You must pass a project name as the first argument.'
|
||||
logErr 'e.g. natal HelloWorld'
|
||||
process.exit 1
|
||||
|
||||
init name
|
438
natal.coffee
Normal file
438
natal.coffee
Normal file
@ -0,0 +1,438 @@
|
||||
# Natal
|
||||
# Bootstrap ClojureScript React Native apps
|
||||
# Dan Motzenbecker
|
||||
# http://oxism.com
|
||||
# MIT License
|
||||
|
||||
fs = require 'fs'
|
||||
crypto = require 'crypto'
|
||||
child = require 'child_process'
|
||||
cli = require 'commander'
|
||||
chalk = require 'chalk'
|
||||
semver = require 'semver'
|
||||
pkgJson = require __dirname + '/package.json'
|
||||
|
||||
nodeVersion = pkgJson.engines.node
|
||||
resources = __dirname + '/resources/'
|
||||
camelRx = /([a-z])([A-Z])/g
|
||||
projNameRx = /\$PROJECT_NAME\$/g
|
||||
projNameHyphRx = /\$PROJECT_NAME_HYPHENATED\$/g
|
||||
projNameUnderRx = /\$PROJECT_NAME_UNDERSCORED\$/g
|
||||
rnVersion = '0.13.0-rc'
|
||||
podMinVersion = '0.38.2'
|
||||
process.title = 'natal'
|
||||
|
||||
|
||||
log = (s, color = 'green') ->
|
||||
console.log chalk[color] s
|
||||
|
||||
|
||||
logErr = (err, color = 'red') ->
|
||||
console.error chalk[color] err
|
||||
process.exit 1
|
||||
|
||||
|
||||
exec = (cmd, keepOutput) ->
|
||||
if keepOutput
|
||||
child.execSync cmd
|
||||
else
|
||||
child.execSync cmd, stdio: 'ignore'
|
||||
|
||||
|
||||
readFile = (path) ->
|
||||
fs.readFileSync path, encoding: 'ascii'
|
||||
|
||||
|
||||
edit = (path, pairs) ->
|
||||
fs.writeFileSync path, pairs.reduce (contents, [rx, replacement]) ->
|
||||
contents.replace rx, replacement
|
||||
, readFile path
|
||||
|
||||
|
||||
pluckUuid = (line) ->
|
||||
line.match(/\[(.+)\]/)[1]
|
||||
|
||||
|
||||
toUnderscored = (s) ->
|
||||
s.replace(camelRx, '$1_$2').toLowerCase()
|
||||
|
||||
|
||||
writeConfig = (config) ->
|
||||
try
|
||||
fs.writeFileSync '.natal', JSON.stringify config, null, 2
|
||||
catch {message}
|
||||
logErr \
|
||||
if message.match /EACCES/i
|
||||
'Invalid write permissions for creating .natal config file'
|
||||
else
|
||||
message
|
||||
|
||||
|
||||
readConfig = ->
|
||||
try
|
||||
JSON.parse readFile '.natal'
|
||||
catch {message}
|
||||
logErr \
|
||||
if message.match /ENOENT/i
|
||||
'No Natal config was found in this directory (.natal)'
|
||||
else if message.match /EACCES/i
|
||||
'No read permissions for .natal'
|
||||
else if message.match /Unexpected/i
|
||||
'.natal contains malformed JSON'
|
||||
else
|
||||
message
|
||||
|
||||
|
||||
getBundleId = (name) ->
|
||||
try
|
||||
if line = readFile "native/ios/#{name}.xcodeproj/project.pbxproj"
|
||||
.match /PRODUCT_BUNDLE_IDENTIFIER = (.+);/
|
||||
|
||||
line[1]
|
||||
|
||||
else if line = readFile "native/ios/#{name}/Info.plist"
|
||||
.match /\<key\>CFBundleIdentifier\<\/key\>\n?\s*\<string\>(.+)\<\/string\>/
|
||||
|
||||
rfcIdRx = /\$\(PRODUCT_NAME\:rfc1034identifier\)/
|
||||
|
||||
if line[1].match rfcIdRx
|
||||
line[1].replace rfcIdRx, name
|
||||
else
|
||||
line[1]
|
||||
|
||||
else
|
||||
throw new Error 'Cannot find bundle identifier in project.pbxproj or Info.plist'
|
||||
|
||||
catch {message}
|
||||
logErr message
|
||||
|
||||
|
||||
init = (projName) ->
|
||||
projNameHyph = projName.replace(camelRx, '$1-$2').toLowerCase()
|
||||
projNameUs = toUnderscored projName
|
||||
|
||||
try
|
||||
log "Creating #{projName}", 'bgMagenta'
|
||||
log ''
|
||||
|
||||
if fs.existsSync projNameHyph
|
||||
throw new Error "Directory #{projNameHyph} already exists"
|
||||
|
||||
exec 'type lein'
|
||||
exec 'type pod'
|
||||
exec 'type watchman'
|
||||
exec 'type xcodebuild'
|
||||
|
||||
podVersion = exec('pod --version', true).toString().trim()
|
||||
unless semver.satisfies podVersion, ">=#{podMinVersion}"
|
||||
throw new Error """
|
||||
Natal requires CocoaPods #{podMinVersion} or higher (you have #{podVersion}).
|
||||
Run [sudo] gem update cocoapods and try again.
|
||||
"""
|
||||
|
||||
log 'Creating Leiningen project'
|
||||
exec "lein new #{projNameHyph}"
|
||||
|
||||
log 'Updating Leiningen project'
|
||||
process.chdir projNameHyph
|
||||
exec "cp #{resources}project.clj project.clj"
|
||||
edit 'project.clj', [[projNameHyphRx, projNameHyph]]
|
||||
corePath = "src/#{projNameUs}/core.clj"
|
||||
fs.unlinkSync corePath
|
||||
corePath += 's'
|
||||
exec "cp #{resources}core.cljs #{corePath}"
|
||||
edit corePath, [[projNameHyphRx, projNameHyph], [projNameRx, projName]]
|
||||
|
||||
log 'Compiling ClojureScript'
|
||||
exec 'lein cljsbuild once dev'
|
||||
|
||||
log 'Creating React Native skeleton'
|
||||
fs.mkdirSync 'native'
|
||||
process.chdir 'native'
|
||||
|
||||
fs.writeFileSync 'package.json', JSON.stringify
|
||||
name: projName
|
||||
version: '0.0.1'
|
||||
private: true
|
||||
scripts:
|
||||
start: 'node_modules/react-native/packager/packager.sh'
|
||||
dependencies:
|
||||
'react-native': rnVersion
|
||||
, null, 2
|
||||
|
||||
exec 'npm i'
|
||||
exec "
|
||||
node -e
|
||||
\"process.argv[3]='#{projName}';
|
||||
require('react-native/local-cli/init')('.', '#{projName}')\"
|
||||
"
|
||||
|
||||
exec 'rm -rf android'
|
||||
fs.unlinkSync 'index.android.js'
|
||||
|
||||
log 'Installing Pod dependencies'
|
||||
process.chdir 'ios'
|
||||
exec "cp #{resources}Podfile ."
|
||||
exec 'pod install'
|
||||
|
||||
log 'Updating Xcode project'
|
||||
for ext in ['m', 'h']
|
||||
path = "#{projName}/AppDelegate.#{ext}"
|
||||
exec "cp #{resources}AppDelegate.#{ext} #{path}"
|
||||
edit path, [[projNameRx, projName], [projNameHyphRx, projNameHyph]]
|
||||
|
||||
uuid1 = crypto
|
||||
.createHash 'md5'
|
||||
.update projName, 'utf8'
|
||||
.digest('hex')[...24]
|
||||
.toUpperCase()
|
||||
|
||||
uuid2 = uuid1.split ''
|
||||
uuid2.splice 7, 1, ((parseInt(uuid1[7], 16) + 1) % 16).toString(16).toUpperCase()
|
||||
uuid2 = uuid2.join ''
|
||||
|
||||
edit \
|
||||
"#{projName}.xcodeproj/project.pbxproj",
|
||||
[
|
||||
[
|
||||
/OTHER_LDFLAGS = "-ObjC";/g
|
||||
'OTHER_LDFLAGS = "${inherited}";'
|
||||
]
|
||||
[
|
||||
/\/\* End PBXBuildFile section \*\//
|
||||
"\t\t#{uuid2} /* out in Resources */ =
|
||||
{isa = PBXBuildFile; fileRef = #{uuid1} /* out */; };
|
||||
\n/* End PBXBuildFile section */"
|
||||
]
|
||||
[
|
||||
/\/\* End PBXFileReference section \*\//
|
||||
"\t\t#{uuid1} /* out */ = {isa = PBXFileReference; lastKnownFileType
|
||||
= folder; name = out; path = ../../target/out;
|
||||
sourceTree = \"<group>\"; };\n/* End PBXFileReference section */"
|
||||
]
|
||||
[
|
||||
/main.jsbundle \*\/\,/
|
||||
"main.jsbundle */,\n\t\t\t\t#{uuid1} /* out */,"
|
||||
]
|
||||
[
|
||||
/\/\* LaunchScreen.xib in Resources \*\/\,/
|
||||
"/* LaunchScreen.xib in Resources */,
|
||||
\n\t\t\t\t#{uuid2} /* out in Resources */,"
|
||||
]
|
||||
]
|
||||
|
||||
testId = readFile("#{projName}.xcodeproj/project.pbxproj")
|
||||
.match(new RegExp "([0-9A-F]+) \/\\* #{projName}Tests \\*\/ = \\{")[1]
|
||||
|
||||
edit \
|
||||
"#{projName}.xcodeproj/xcshareddata/xcschemes/#{projName}.xcscheme",
|
||||
[
|
||||
[
|
||||
/\<Testables\>\n\s*\<\/Testables\>/
|
||||
"""
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "#{testId}"
|
||||
BuildableName = "#{projName}Tests.xctest"
|
||||
BlueprintName = "#{projName}Tests"
|
||||
ReferencedContainer = "container:#{projName}.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
"""
|
||||
]
|
||||
]
|
||||
|
||||
log 'Creating Natal config'
|
||||
process.chdir '../..'
|
||||
config =
|
||||
name: projName
|
||||
device: pluckUuid getDeviceList().find (line) -> /iPhone 6/.test line
|
||||
|
||||
writeConfig config
|
||||
launch config
|
||||
|
||||
log ''
|
||||
log 'To get started with your new app, first cd into its directory:', 'yellow'
|
||||
log "cd #{projNameHyph}", 'inverse'
|
||||
log ''
|
||||
log 'Boot the REPL by typing:', 'yellow'
|
||||
log 'natal repl', 'inverse'
|
||||
log 'Then choose the correct device to connect to (probably 1).', 'yellow'
|
||||
log ''
|
||||
log 'At the REPL prompt type this:', 'yellow'
|
||||
log "(in-ns '#{projNameHyph}.core)", 'inverse'
|
||||
log ''
|
||||
log 'Changes you make via the REPL or by changing your .cljs files should appear live.', 'yellow'
|
||||
log ''
|
||||
log 'Try this command as an example:', 'yellow'
|
||||
log '(swap! app-state assoc :text "Hello Native World")', 'inverse'
|
||||
log ''
|
||||
log '✔ Done', 'bgMagenta'
|
||||
log ''
|
||||
|
||||
catch {message}
|
||||
logErr \
|
||||
if message.match /type\:.+lein/i
|
||||
'Leiningen is required (http://leiningen.org)'
|
||||
else if message.match /type\:.+pod/i
|
||||
'CocoaPods is required (https://cocoapods.org)'
|
||||
else if message.match /type\:.+watchman/i
|
||||
'Watchman is required (https://facebook.github.io/watchman)'
|
||||
else if message.match /type\:.+xcodebuild/i
|
||||
'Xcode Command Line Tools are required'
|
||||
else
|
||||
message
|
||||
|
||||
|
||||
launch = ({name, device}) ->
|
||||
log 'Compiling Xcode project'
|
||||
try
|
||||
exec "
|
||||
xcodebuild
|
||||
-workspace native/ios/#{name}.xcworkspace
|
||||
-scheme #{name}
|
||||
-destination platform='iOS Simulator',OS=latest,id='#{device}'
|
||||
test
|
||||
"
|
||||
|
||||
log 'Launching simulator'
|
||||
exec "xcrun simctl launch #{device} #{getBundleId name}"
|
||||
|
||||
catch {message}
|
||||
logErr message
|
||||
|
||||
|
||||
openXcode = (name) ->
|
||||
try
|
||||
exec "open native/ios/#{name}.xcworkspace"
|
||||
catch {message}
|
||||
logErr \
|
||||
if message.match /ENOENT/i
|
||||
"""
|
||||
Cannot find #{name}.xcworkspace in native/ios.
|
||||
Run this command from your project's root directory.
|
||||
"""
|
||||
else if message.match /EACCES/i
|
||||
"Invalid permissions for opening #{name}.xcworkspace in native/ios"
|
||||
else
|
||||
message
|
||||
|
||||
|
||||
getDeviceList = ->
|
||||
try
|
||||
exec 'xcrun instruments -s devices', true
|
||||
.toString()
|
||||
.split '\n'
|
||||
.filter (line) -> /^i/.test line
|
||||
catch {message}
|
||||
logErr 'Device listing failed: ' + message
|
||||
|
||||
|
||||
startRepl = (name) ->
|
||||
log 'Starting REPL'
|
||||
hasRlwrap =
|
||||
try
|
||||
exec 'type rlwrap'
|
||||
true
|
||||
catch
|
||||
log '
|
||||
Warning: rlwrap is not installed.\nInstall it to make the REPL a much
|
||||
better experience with arrow key support.
|
||||
', 'red'
|
||||
false
|
||||
|
||||
try
|
||||
lein = child.spawn (if hasRlwrap then 'rlwrap' else 'lein'),
|
||||
"#{if hasRlwrap then 'lein ' else ''}trampoline run -m clojure.main -e"
|
||||
.split(' ').concat(
|
||||
"""
|
||||
(require '[cljs.repl :as repl])
|
||||
(require '[ambly.core :as ambly])
|
||||
(let [repl-env (ambly.core/repl-env)]
|
||||
(cljs.repl/repl repl-env
|
||||
:watch \"src\"
|
||||
:watch-fn
|
||||
(fn []
|
||||
(cljs.repl/load-file repl-env
|
||||
\"src/#{toUnderscored name}/core.cljs\"))
|
||||
:analyze-path \"src\"))
|
||||
"""),
|
||||
cwd: process.cwd()
|
||||
env: process.env
|
||||
stdio: 'inherit'
|
||||
|
||||
catch {message}
|
||||
logErr message
|
||||
|
||||
|
||||
cli._name = 'natal'
|
||||
cli.version pkgJson.version
|
||||
|
||||
cli.command 'init <name>'
|
||||
.description 'Create a new ClojureScript React Native project'
|
||||
.action (name) ->
|
||||
if typeof name isnt 'string'
|
||||
logErr '''
|
||||
natal init requires a project name as the first argument.
|
||||
e.g.
|
||||
natal init HelloWorld
|
||||
'''
|
||||
|
||||
init name
|
||||
|
||||
|
||||
cli.command 'launch'
|
||||
.description 'Compile project and run in simulator'
|
||||
.action ->
|
||||
launch readConfig()
|
||||
|
||||
|
||||
cli.command 'repl'
|
||||
.description 'Launch a ClojureScript REPL with background compilation'
|
||||
.action ->
|
||||
startRepl readConfig().name
|
||||
|
||||
|
||||
cli.command 'listdevices'
|
||||
.description 'List available simulator devices by index'
|
||||
.action ->
|
||||
console.log (getDeviceList()
|
||||
.map (line, i) -> "#{i}\t#{line.replace /\[.+\]/, ''}"
|
||||
.join '\n')
|
||||
|
||||
|
||||
cli.command 'setdevice <index>'
|
||||
.description 'Choose simulator device by index'
|
||||
.action (index) ->
|
||||
unless device = getDeviceList()[parseInt index, 10]
|
||||
logErr 'Invalid device index. Run natal listdevices for valid indexes.'
|
||||
|
||||
config = readConfig()
|
||||
config.device = pluckUuid device
|
||||
writeConfig config
|
||||
|
||||
|
||||
cli.command 'xcode'
|
||||
.description 'Open Xcode project'
|
||||
.action ->
|
||||
openXcode readConfig().name
|
||||
|
||||
|
||||
cli.on '*', (command) ->
|
||||
logErr "Unknown command #{command[0]}. See natal --help for valid commands"
|
||||
|
||||
|
||||
unless semver.satisfies process.version[1...], nodeVersion
|
||||
logErr """
|
||||
Natal requires Node.js version #{nodeVersion}
|
||||
You have #{process.version[1...]}
|
||||
"""
|
||||
|
||||
if process.argv.length <= 2
|
||||
cli.outputHelp()
|
||||
else
|
||||
cli.parse process.argv
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "natal",
|
||||
"version": "0.0.3",
|
||||
"version": "0.1.0",
|
||||
"description": "Bootstrap ClojureScript React Native apps",
|
||||
"main": "index.js",
|
||||
"author": {
|
||||
@ -12,12 +12,11 @@
|
||||
"dependencies": {
|
||||
"chalk": "^1.1.1",
|
||||
"coffee-script": "^1.9.3",
|
||||
"react-native": "^0.10.1",
|
||||
"commander": "^2.8.1",
|
||||
"semver": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12.x",
|
||||
"iojs": ">=3.1.x"
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -97,7 +97,7 @@ RCT_EXPORT_MODULE()
|
||||
* on the same Wi-Fi network.
|
||||
*/
|
||||
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"];
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
|
||||
|
||||
/**
|
||||
* OPTION 2
|
||||
@ -131,7 +131,8 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
// Set up a root view using the bridge defined above
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"$PROJECT_NAME$"];
|
||||
moduleName:@"$PROJECT_NAME$"
|
||||
initialProperties:nil];
|
||||
|
||||
// Set up to be notified when the React Native UI is up
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
|
@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if hash rlwrap 2>/dev/null; then
|
||||
COMMAND="rlwrap lein"
|
||||
else
|
||||
COMMAND="lein"
|
||||
fi
|
||||
|
||||
$COMMAND trampoline run -m clojure.main -e \
|
||||
"(require '[cljs.repl :as repl])
|
||||
(require '[ambly.core :as ambly])
|
||||
(let [repl-env (ambly.core/repl-env)]
|
||||
(cljs.repl/repl repl-env
|
||||
:watch \"src\"
|
||||
:watch-fn
|
||||
(fn []
|
||||
(cljs.repl/load-file repl-env
|
||||
\"src/$PROJECT_NAME_UNDERSCORED$/core.cljs\"))
|
||||
:analyze-path \"src\"))"
|
@ -4,7 +4,7 @@
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:dependencies [[org.clojure/clojure "1.7.0"]
|
||||
[org.clojure/clojurescript "1.7.122"]
|
||||
[org.clojure/clojurescript "1.7.145"]
|
||||
[org.omcljs/om "0.9.0"]
|
||||
[org.omcljs/ambly "0.6.0"]]
|
||||
:plugins [[lein-cljsbuild "1.1.0"]]
|
||||
|
Loading…
x
Reference in New Issue
Block a user