From b423f76ab739c12f477ff3f338894d0e86e1d1b3 Mon Sep 17 00:00:00 2001 From: Artur Girenko Date: Sun, 24 Sep 2017 21:39:47 +0200 Subject: [PATCH 1/6] refactor infer-components command to use it in use-figwheel for auto-require --- re-natal.coffee | 51 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/re-natal.coffee b/re-natal.coffee index 693e97a..41cd965 100644 --- a/re-natal.coffee +++ b/re-natal.coffee @@ -33,6 +33,8 @@ ipAddressRx = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/i figwheelUrlRx = /ws:\/\/[0-9a-zA-Z\.]*:/g appDelegateRx = /http:\/\/[^:]+/g debugHostRx = /host]\s+\?:\s+@".*";/g +namespaceRx = /\(ns\s+([A-Za-z0-9.-]+)/g +jsRequireRx = /js\/require "(.+)"/g rnVersion = '0.47.1' rnWinVersion = '0.47.0-rc.5' rnPackagerPort = 8081 @@ -800,27 +802,54 @@ useComponent = (name, platform) -> catch {message} logErr message -inferComponents = () -> +platformOfNamespace = (ns) -> + if ns? + platforms = Object.keys platformMeta + platform = platforms.find((p) -> ns.indexOf(".#{p}") > 0); + platform ?= "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 - jsRequire = /js\/require \"(.+)\"/g + item.path.indexOf('/target/') < 0 # ignore target dir files = klawSync process.cwd(), nodir: true filter: onlyUserCljs filenames = files.map((o) -> o.path) - contents = filenames.map((path) -> fs.readFileSync(path, encoding: 'utf8')) + 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 + , {}) + +inferComponents = () -> + requiresByPlatform = buildRequireByPlatformMap() + + allRequires = [] + for k,v of requiresByPlatform + allRequires = Array.from(new Set(allRequires.concat(v))) 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) - 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) log "new component import found #{Array.from(difference)}" - config.modules = Array.from(requires) + config.modules = Array.from(allRequires) writeConfig(config) else log "no new component was imported, defaulting to #{Array.from(modules)}" From d4c45e8777240860613839d1bf29418aec182477 Mon Sep 17 00:00:00 2001 From: Artur Girenko Date: Sun, 24 Sep 2017 21:47:52 +0200 Subject: [PATCH 2/6] add commands to enable auto-require feature --- re-natal.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/re-natal.coffee b/re-natal.coffee index 41cd965..df5c7d3 100644 --- a/re-natal.coffee +++ b/re-natal.coffee @@ -854,6 +854,15 @@ inferComponents = () -> else 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.version pkgJson.version @@ -931,6 +940,16 @@ cli.command 'enable-source-maps' .action () -> patchReactNativePackager() +cli.command 'enable-auto-require' + .description 'Enables scanning for requires in cljs files and automatically add them in use-figwheel' + .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' .description 'copy figwheel-bridge.js into project' .action () -> From b37334a3dfab3715d1be019d2d545ff1aff60c72 Mon Sep 17 00:00:00 2001 From: Artur Girenko Date: Sun, 24 Sep 2017 23:08:46 +0200 Subject: [PATCH 3/6] infer components when auto-require is enabled --- re-natal.coffee | 87 ++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/re-natal.coffee b/re-natal.coffee index df5c7d3..67f74e1 100644 --- a/re-natal.coffee +++ b/re-natal.coffee @@ -682,13 +682,52 @@ updateIosRCTWebSocketExecutor = (iosHost) -> RCTWebSocketExecutorPath = "node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.m" 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) -> - images = scanImages(config.imageDirs).map (fname) -> './' + fname; - modulesAndImages = config.modules.concat images; - if typeof config.platforms[platform].modules is 'undefined' - modulesAndImages + if config.autoRequire? and config.autoRequire + requires = buildRequireByPlatformMap() + requires.common.concat(requires[platform]) else - modulesAndImages.concat(config.platforms[platform].modules) + images = scanImages(config.imageDirs).map (fname) -> './' + fname; + modulesAndImages = config.modules.concat images; + if typeof config.platforms[platform].modules is 'undefined' + modulesAndImages + else + modulesAndImages.concat(config.platforms[platform].modules) generateDevScripts = () -> try @@ -707,6 +746,9 @@ generateDevScripts = () -> for platform in platforms 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 moduleMap = generateRequireModulesCode(platformModulesAndImages(config, platform)) fs.writeFileSync "index.#{platform}.js", "#{moduleMap}require('figwheel-bridge').withModules(modules).start('#{projName}','#{platform}','#{devHost[platform]}');" @@ -802,41 +844,6 @@ useComponent = (name, platform) -> catch {message} logErr message -platformOfNamespace = (ns) -> - if ns? - platforms = Object.keys platformMeta - platform = platforms.find((p) -> ns.indexOf(".#{p}") > 0); - platform ?= "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 - , {}) - inferComponents = () -> requiresByPlatform = buildRequireByPlatformMap() From 606f66add7410b9b0ed287c6e920572bd78e86b8 Mon Sep 17 00:00:00 2001 From: Artur Girenko Date: Sun, 24 Sep 2017 23:16:53 +0200 Subject: [PATCH 4/6] update command descriptions --- re-natal.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/re-natal.coffee b/re-natal.coffee index 67f74e1..8f42eb6 100644 --- a/re-natal.coffee +++ b/re-natal.coffee @@ -948,12 +948,12 @@ cli.command 'enable-source-maps' patchReactNativePackager() cli.command 'enable-auto-require' - .description 'Enables scanning for requires in cljs files and automatically add them in use-figwheel' + .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' + .description 'disables auto-require feature in use-figwheel command' .action () -> autoRequire(false) From b0bbdb7696f4ff288224d2d960bd080feb9bb72e Mon Sep 17 00:00:00 2001 From: Artur Girenko Date: Mon, 25 Sep 2017 17:52:06 +0200 Subject: [PATCH 5/6] add info about auto-require to README --- README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a53d0a8..e2258a8 100644 --- a/README.md +++ b/README.md @@ -224,12 +224,28 @@ And you want to use a component called 'some-library/Component': ```clojure (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 code to be available at runtime. But, dynamically loaded (by Figwheel) code bypasses this scan 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 ``` From 9fde539929dba33f9ad8d381c70febf36987ff5e Mon Sep 17 00:00:00 2001 From: Artur Girenko Date: Mon, 25 Sep 2017 18:08:47 +0200 Subject: [PATCH 6/6] enable auto-require by default --- re-natal.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/re-natal.coffee b/re-natal.coffee index 8f42eb6..15ba9ce 100644 --- a/re-natal.coffee +++ b/re-natal.coffee @@ -212,6 +212,7 @@ generateConfig = (interfaceName, projName) -> modules: [] imageDirs: ["images"] platforms: {} + autoRequire: true for platform in platforms config.platforms[platform] =