Merge pull request #140 from drapanjanas/auto-require

Auto-require: infer components in use-figwheel command
This commit is contained in:
Artūr Girenko 2017-09-25 18:31:23 +02:00 committed by GitHub
commit 45712a7627
2 changed files with 94 additions and 22 deletions

View File

@ -224,12 +224,28 @@ And you want to use a component called 'some-library/Component':
```clojure ```clojure
(def Component (js/require "some-library/Component")) (def Component (js/require "some-library/Component"))
``` ```
This would work when you do `lein prod-build` and run your app, but will fail when you run with Figwheel. This works fine when you do `lein prod-build` and run your app.
The React Native packager statically scans for all calls to `require` and prepares the required The React Native packager statically scans for all calls to `require` and prepares the required
code to be available at runtime. But, dynamically loaded (by Figwheel) code bypasses this scan code to be available at runtime. But, dynamically loaded (by Figwheel) code bypasses this scan
and therefore requiring the custom component fails. and therefore requiring the custom component fails.
To overcome this run `use-component`: In re-natal this is solved by adding all dependencies in index.*.js file which is scanned by React Native packager.
#### Using auto-require
To enable auto-require feature you have to run command:
```
$ re-natal enable-auto-require
```
From now on, command `use-figwheel` will scan for all required modules and generate index.*.js with all required dependencies.
You will have to re-run `use-figwheel` command every time you use new modules via `(js/require "...")`
This feature is available since re-natal@0.7.0
#### Manually registering dependencies with use-component command
You can register a single dependency manually by running `use-component` command:
``` ```
$ re-natal use-component some-library/Component $ re-natal use-component some-library/Component
``` ```

View File

@ -33,6 +33,8 @@ ipAddressRx = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/i
figwheelUrlRx = /ws:\/\/[0-9a-zA-Z\.]*:/g figwheelUrlRx = /ws:\/\/[0-9a-zA-Z\.]*:/g
appDelegateRx = /http:\/\/[^:]+/g appDelegateRx = /http:\/\/[^:]+/g
debugHostRx = /host]\s+\?:\s+@".*";/g debugHostRx = /host]\s+\?:\s+@".*";/g
namespaceRx = /\(ns\s+([A-Za-z0-9.-]+)/g
jsRequireRx = /js\/require "(.+)"/g
rnVersion = '0.47.1' rnVersion = '0.47.1'
rnWinVersion = '0.47.0-rc.5' rnWinVersion = '0.47.0-rc.5'
rnPackagerPort = 8081 rnPackagerPort = 8081
@ -210,6 +212,7 @@ generateConfig = (interfaceName, projName) ->
modules: [] modules: []
imageDirs: ["images"] imageDirs: ["images"]
platforms: {} platforms: {}
autoRequire: true
for platform in platforms for platform in platforms
config.platforms[platform] = config.platforms[platform] =
@ -680,7 +683,46 @@ updateIosRCTWebSocketExecutor = (iosHost) ->
RCTWebSocketExecutorPath = "node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.m" RCTWebSocketExecutorPath = "node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.m"
edit RCTWebSocketExecutorPath, [[debugHostRx, "host] ?: @\"#{iosHost}\";"]] edit RCTWebSocketExecutorPath, [[debugHostRx, "host] ?: @\"#{iosHost}\";"]]
platformOfNamespace = (ns) ->
if ns?
possiblePlatforms = Object.keys platformMeta
p = possiblePlatforms.find((p) -> ns.indexOf(".#{p}") > 0);
p ?= "common"
extractRequiresFromSourceFile = (file) ->
content = fs.readFileSync(file, encoding: 'utf8')
requires = []
while match = namespaceRx.exec(content)
ns = match[1]
while match = jsRequireRx.exec(content)
requires.push(match[1])
platform: platformOfNamespace(ns)
requires: requires
buildRequireByPlatformMap = () ->
onlyUserCljs = (item) -> fpath.extname(item.path) == '.cljs' and
item.path.indexOf('/target/') < 0 # ignore target dir
files = klawSync process.cwd(),
nodir: true
filter: onlyUserCljs
filenames = files.map((o) -> o.path)
extractedRequires = filenames.map(extractRequiresFromSourceFile)
extractedRequires.reduce((result, item) ->
platform = item.platform
if result[platform]?
result[platform] = Array.from(new Set(item.requires.concat(result[platform])))
else
result[platform] = Array.from(new Set(item.requires))
result
, {})
platformModulesAndImages = (config, platform) -> platformModulesAndImages = (config, platform) ->
if config.autoRequire? and config.autoRequire
requires = buildRequireByPlatformMap()
requires.common.concat(requires[platform])
else
images = scanImages(config.imageDirs).map (fname) -> './' + fname; images = scanImages(config.imageDirs).map (fname) -> './' + fname;
modulesAndImages = config.modules.concat images; modulesAndImages = config.modules.concat images;
if typeof config.platforms[platform].modules is 'undefined' if typeof config.platforms[platform].modules is 'undefined'
@ -705,6 +747,9 @@ generateDevScripts = () ->
for platform in platforms for platform in platforms
devHost[platform] = config.platforms[platform].host devHost[platform] = config.platforms[platform].host
if config.autoRequire? and config.autoRequire
log 'Auto-require is enabled. Scanning for require() calls in *.cljs files...'
for platform in platforms for platform in platforms
moduleMap = generateRequireModulesCode(platformModulesAndImages(config, platform)) moduleMap = generateRequireModulesCode(platformModulesAndImages(config, platform))
fs.writeFileSync "index.#{platform}.js", "#{moduleMap}require('figwheel-bridge').withModules(modules).start('#{projName}','#{platform}','#{devHost[platform]}');" fs.writeFileSync "index.#{platform}.js", "#{moduleMap}require('figwheel-bridge').withModules(modules).start('#{projName}','#{platform}','#{devHost[platform]}');"
@ -801,30 +846,31 @@ useComponent = (name, platform) ->
logErr message logErr message
inferComponents = () -> inferComponents = () ->
onlyUserCljs = (item) -> fpath.extname(item.path) == '.cljs' and requiresByPlatform = buildRequireByPlatformMap()
item.path.indexOf('/target/') < 0 # ignore target dir
jsRequire = /js\/require \"(.+)\"/g allRequires = []
files = klawSync process.cwd(), for k,v of requiresByPlatform
nodir: true allRequires = Array.from(new Set(allRequires.concat(v)))
filter: onlyUserCljs
filenames = files.map((o) -> o.path)
contents = filenames.map((path) -> fs.readFileSync(path, encoding: 'utf8'))
config = readConfig() # re-natal file config = readConfig() # re-natal file
requires = new Set()
contents.forEach((text) ->
while match = jsRequire.exec(text)
requires.add(match[1]) if match[1].indexOf(config.imageDirs) < 0)
modules = new Set(config.modules) modules = new Set(config.modules)
difference = new Set(Array.from(requires).filter((m) -> !modules.has(m))) difference = new Set(Array.from(allRequires).filter((m) -> !modules.has(m)))
if(difference.size isnt 0) if(difference.size isnt 0)
log "new component import found #{Array.from(difference)}" log "new component import found #{Array.from(difference)}"
config.modules = Array.from(requires) config.modules = Array.from(allRequires)
writeConfig(config) writeConfig(config)
else else
log "no new component was imported, defaulting to #{Array.from(modules)}" log "no new component was imported, defaulting to #{Array.from(modules)}"
autoRequire = (enabled) ->
config = readConfig()
config.autoRequire = enabled
writeConfig(config)
if (enabled)
log "Auto-Require feature is enabled in use-figwheel command"
else
log "Auto-Require feature is disabled in use-figwheel command"
cli._name = 're-natal' cli._name = 're-natal'
cli.version pkgJson.version cli.version pkgJson.version
@ -902,6 +948,16 @@ cli.command 'enable-source-maps'
.action () -> .action () ->
patchReactNativePackager() patchReactNativePackager()
cli.command 'enable-auto-require'
.description 'enables source scanning for automatic required module resolution in use-figwheel command.'
.action () ->
autoRequire(true)
cli.command 'disable-auto-require'
.description 'disables auto-require feature in use-figwheel command'
.action () ->
autoRequire(false)
cli.command 'copy-figwheel-bridge' cli.command 'copy-figwheel-bridge'
.description 'copy figwheel-bridge.js into project' .description 'copy figwheel-bridge.js into project'
.action () -> .action () ->