diff --git a/.gitmodules b/.gitmodules index 2d53210f6498b..d64b459e63985 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,6 +6,3 @@ url = git@github.com:expo/react-native.git branch = exp-latest update = checkout -[submodule "packages/react-native-unimodules"] - path = packages/react-native-unimodules - url = git@github.com:unimodules/react-native-unimodules.git diff --git a/packages/react-native-unimodules b/packages/react-native-unimodules deleted file mode 160000 index 68313e6a30159..0000000000000 --- a/packages/react-native-unimodules +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 68313e6a3015975428ef5e9914ba4587669c9516 diff --git a/packages/react-native-unimodules/.eslintrc.js b/packages/react-native-unimodules/.eslintrc.js new file mode 100644 index 0000000000000..2720197860feb --- /dev/null +++ b/packages/react-native-unimodules/.eslintrc.js @@ -0,0 +1,2 @@ +// @generated by expo-module-scripts +module.exports = require('expo-module-scripts/eslintrc.base.js'); diff --git a/packages/react-native-unimodules/.npmignore b/packages/react-native-unimodules/.npmignore new file mode 100644 index 0000000000000..85e65a234bcb8 --- /dev/null +++ b/packages/react-native-unimodules/.npmignore @@ -0,0 +1,4 @@ +# @generated by expo-module-scripts +babel.config.js +__tests__ +__mocks__ diff --git a/packages/react-native-unimodules/CHANGELOG.md b/packages/react-native-unimodules/CHANGELOG.md new file mode 100644 index 0000000000000..218986d173602 --- /dev/null +++ b/packages/react-native-unimodules/CHANGELOG.md @@ -0,0 +1,104 @@ +# Changelog + +## 0.10.0 + +### 📚 Library updates + +- Updated dependencies to match versions included in Expo SDK38. + +## 0.9.0 + +### 📚 Library updates + +- Updated `@unimodules/react-native-adapter` to version `5.2.0`. + +## 0.8.1 + +### 🎉 New features + +- Added `unimodules-app-loader` to dependencies. + +## 0.8.0 + +### 🛠 Breaking changes + +- Updated core packages, please refer to [their changelogs](https://github.com/expo/expo/blob/master/CHANGELOG.md) to see the diff. +- Removed `expo-app-loader-provider` from dependencies. + +### 🎉 New features + +- Added `expo-image-loader` to dependencies. + +### 🐛 Bug fixes + +- Fix `pod install --deployment` failing due to pathname object being used instead of a string. ([#96](https://github.com/unimodules/react-native-unimodules/pull/96) by [@tsapeta](https://github.com/tsapeta)) + +## 0.7.0 + +### 📚 Library updates + +- Updated `@unimodules/react-native-adapter` to version `5.0.0`. + +### 🛠 Breaking changes + +- Updated core packages, please refer to [their changelogs](https://github.com/expo/expo/blob/master/CHANGELOG.md) to see the diff. + +### 🎉 New features + +- Allow passing custom pod flags to the unimodules. + +## 0.6.0 + +### 🛠 Breaking changes + +- Updated core packages, please refer to [their changelogs](https://github.com/expo/expo/blob/master/CHANGELOG.md) to see the diff. + +## 0.5.3 + +## 0.5.2 + +### 🐛 Bug fixes + +- Updated `@unimodules/core` to version `3.0.2` including proper ProGuard rules. + +## 0.5.0 + +### 🛠 Breaking changes + +- Updated core packages, please refer to [their changelogs](https://github.com/expo/expo/blob/master/CHANGELOG.md) to see the diff + +## 0.4.2 + +### 🐛 Bug fixes + +- Fixed MainApplication.kt not being recognized correctly. ([#46](https://github.com/unimodules/react-native-unimodules/pull/46) by [@geovannimp](https://github.com/geovannimp)) + +## 0.4.1 + +### 🐛 Bug fixes + +- Added support for Kotlin ([#39](https://github.com/unimodules/react-native-unimodules/pull/39) by [@bbarthec](https://github.com/bbarthec)) + +## 0.4.0 + +### 🛠 Breaking changes + +- Updated core packages, please refer to [their changelogs](https://github.com/expo/expo/blob/master/CHANGELOG.md) to see the diff + +### 🐛 Bug fixes + +- Support version tags when adding dependencies for unimodules + +## 0.3.1 + +### 🐛 Bug fixes + +- Fixed TypeScript definitions of common unimodules not being exported. Thanks [@saadq](https://github.com/saadq)! ([#24](https://github.com/unimodules/react-native-unimodules/pull/24)) +- Fixed automatic installation script not finding unimodules when using CocoaPods' `--project-directory` flag. ([#31](https://github.com/unimodules/react-native-unimodules/pull/31)) + +## 0.3.0 + +### 🎉 New features + +- Automatically generated list of Android packages ([#28](https://github.com/unimodules/react-native-unimodules/pull/28)) + As of this version, you no longer need to add new packages to your `MainApplication.java` file. Just use `new BasePackageList().getPackageList()` instead 🎉. `BasePackageList` is auto-generated with a list of installed unimodules found in your `node_modules` folder during Gradle's Sync operation. diff --git a/packages/react-native-unimodules/LICENSE.md b/packages/react-native-unimodules/LICENSE.md new file mode 100644 index 0000000000000..c68dc36280ae8 --- /dev/null +++ b/packages/react-native-unimodules/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-present 650 Industries, Inc. (aka Expo) + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/packages/react-native-unimodules/README.md b/packages/react-native-unimodules/README.md new file mode 100644 index 0000000000000..7c2e9013d924d --- /dev/null +++ b/packages/react-native-unimodules/README.md @@ -0,0 +1,109 @@ +# react-native-unimodules + +This library contains the core unimodule infrastructure and a collection of unimodules and interfaces that are commonly depended on by other unimodules. You only need to install `react-native-unimodules` once and then you will be able to use [most of the packages from the Expo SDK](https://docs.expo.io/versions/latest/bare/unimodules-full-list/), like [expo-camera](https://docs.expo.io/versions/latest/sdk/camera/), [expo-media-library](https://docs.expo.io/versions/latest/sdk/media-library/) and many more, [in any React Native project](https://blog.expo.io/you-can-now-use-expo-apis-in-any-react-native-app-7c3a93041331). + +The easiest way to use the library is to initialize a project with it already installed: `npx create-react-native-app my-app`. + +> You can also use `expo-cli` to do this - run `npm i -g expo-cli` and then `expo init --template bare-minimum` or `expo init --template expo-template-bare-typescript` if you prefer TypeScript. + +If you have an existing project you'd like to install it into, please read the following instructions. + +## 📦 Installation + +**This project requires that you use CocoaPods on iOS**, to set it up see [this gist](https://gist.github.com/brentvatne/b0ea11a36dc423e441b7d36e36eb5a26), or relevant parts of the [this guide](https://facebook.github.io/react-native/docs/integration-with-existing-apps#3-install-cocoapods). React Native >= 0.60 ships with CocoaPods support by default, so this should be easy if you're already on that version. + +```bash +npm install react-native-unimodules +``` + +Now you need to configure the library for iOS and/or Android. + +## 🍎 Configure iOS + +- Open `ios/Podfile` in your editor and make it look [like this one on react-native <= 0.59](https://gist.github.com/sjchmiela/6c079f2173938a9a61a7c6f053c45000) or [like this one on react-native >= 0.60](https://gist.github.com/brentvatne/d093e440698404803bd9c29d962949b0/revisions#diff-4a25b996826623c4a3a4910f47f10c30). +- Run `npx pod-install` again +- Update your `AppDelegate.h` and `AppDelegate.m` according to [to look like these](https://gist.github.com/brentvatne/1ece8c32a3c5c9d0ac3a470460c65603). + - If you use [`react-native-navigation`](https://github.com/wix/react-native-navigation), you will need to use its `bridgeManagerDelegate` option [like in this gist](https://gist.github.com/brentvatne/67909ec442121de22c9b81c629a99aa6). + +### Advanced configuration + +
Need to customize node_modules path? +

+ +If you need to customize the path to node_modules, for example because you are using yarn workspaces, then you can pass in a param for this: `use_unimodules!(modules_paths: ['./path/to/node_modules'])` + +

+
+ +
Need to exclude some unimodules that are being automatically linked? +

+ +If you need to exclude some of the unimodules that you are not using but they got installed by your other dependencies (like `expo`), then you can pass in `exclude` param for this. For example, if you want to exclude `expo-face-detector`, you may want to use this: `use_unimodules!(exclude: ['expo-face-detector'])` + +

+
+ +## 🤖 Configure Android + +**In `android/settings.gradle`** + +1. At the top add `apply from: '../node_modules/react-native-unimodules/gradle.groovy'` +1. Then call `includeUnimodulesProjects()` on the next line. + +**In `android/app/build.gradle`** + +1. Add `apply from: '../../node_modules/react-native-unimodules/gradle.groovy'` anywhere before the `dependencies {}` block. +1. Add `addUnimodulesDependencies()` inside `dependencies {}` block. +1. We recommend you use Java 1.8, you can set this [like this](https://github.com/expo/expo/commit/e175f870418fc69e8c129168118264439d73d7cc). + +**In `android/build.gradle`** + +1. Update `minSdkVersion` to `21`. + +**In `MainApplication.java`** +Make the changes outlined in the diff that correspondes to your react-native version. + +- [this diff for react-native <= 0.59](https://gist.github.com/mczernek/0670ec16ca6071796853a66d589b49a5/revisions#diff-a2e7ff8a82f1c4be06f8b8163f2afefa) +- [this diff for react-native >= 0.60](https://gist.github.com/mczernek/9de9e184abc430e9e3508d26738c8a14/revisions#diff-a2e7ff8a82f1c4be06f8b8163f2afefa) + +### Advanced configuration + +
Need to customize node_modules path? +

+ +If you need to customize the path to node_modules, for example because you are using yarn workspaces, then you can pass in a param `modulesPaths` for both of these functions: `includeUnimodulesProjects([modulesPaths: ['./path/to/node_modules']])`, `addUnimodulesDependencies([modulesPaths: ['./path/to/node_modules']])` + +

+
+ +
Need to exclude some unimodules that are being automatically linked? +

+ +If you need to exclude some of the unimodules that you are not using but they got installed by your other dependencies (like `expo`), then you can pass in `exclude` param for this. For example, if you want to exclude `expo-face-detector`, you may want to use this: `addUnimodulesDependencies([exclude: ['expo-face-detector']])` + +

+
+ +
Need to customize configuration of unimodule dependencies? +

+ +You can also customize the configuration of the unimodules dependencies (the default is `implementation`, if you're using Gradle older than 3.0, you will need to set `configuration: "compile"` in `addUnimodulesDependencies`, like: `addUnimodulesDependencies([configuration: "compile"])`) + +

+
+ +# API + +It's possible that you will not have to use any of the code provided by this package directly, it may be used only by other Unimodules that you install. + +But it's likely that you will want to use something like FileSystem or Permissions, and to do that you can import the following modules like so: + +```js +import { Asset, Constants, FileSystem, Permissions } from 'react-native-unimodules'; +``` + +You can import them directly from the specific Unimodule package if you like, but your linter may complain about importing a transitive dependency. + +```js +import * as Permissions from 'expo-permissions'; +``` diff --git a/packages/react-native-unimodules/babel.config.js b/packages/react-native-unimodules/babel.config.js new file mode 100644 index 0000000000000..68c7d3192262e --- /dev/null +++ b/packages/react-native-unimodules/babel.config.js @@ -0,0 +1,2 @@ +// @generated by expo-module-scripts +module.exports = require('expo-module-scripts/babel.config.base'); diff --git a/packages/react-native-unimodules/build/index.d.ts b/packages/react-native-unimodules/build/index.d.ts new file mode 100644 index 0000000000000..fbf40a5e30c51 --- /dev/null +++ b/packages/react-native-unimodules/build/index.d.ts @@ -0,0 +1,5 @@ +import { Asset } from 'expo-asset'; +import Constants from 'expo-constants'; +import * as FileSystem from 'expo-file-system'; +import * as Permissions from 'expo-permissions'; +export { Asset, Constants, FileSystem, Permissions }; diff --git a/packages/react-native-unimodules/build/index.js b/packages/react-native-unimodules/build/index.js new file mode 100644 index 0000000000000..5555e4f69b5aa --- /dev/null +++ b/packages/react-native-unimodules/build/index.js @@ -0,0 +1,7 @@ +// Override React Native's asset resolution for `Image` components +import { Asset } from 'expo-asset'; +import Constants from 'expo-constants'; +import * as FileSystem from 'expo-file-system'; +import * as Permissions from 'expo-permissions'; +export { Asset, Constants, FileSystem, Permissions }; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/react-native-unimodules/build/index.js.map b/packages/react-native-unimodules/build/index.js.map new file mode 100644 index 0000000000000..b66e60a6a7c20 --- /dev/null +++ b/packages/react-native-unimodules/build/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC","sourcesContent":["// Override React Native's asset resolution for `Image` components\nimport { Asset } from 'expo-asset';\nimport Constants from 'expo-constants';\nimport * as FileSystem from 'expo-file-system';\nimport * as Permissions from 'expo-permissions';\n\nexport { Asset, Constants, FileSystem, Permissions };\n"]} \ No newline at end of file diff --git a/packages/react-native-unimodules/cocoapods.rb b/packages/react-native-unimodules/cocoapods.rb new file mode 100644 index 0000000000000..108b923f1d57d --- /dev/null +++ b/packages/react-native-unimodules/cocoapods.rb @@ -0,0 +1,127 @@ +require 'json' +require 'pathname' +require 'optparse' + +def use_unimodules!(custom_options = {}) + options = { + modules_paths: ['../node_modules'], + target: 'react-native', + exclude: [], + flags: {}, + }.deep_merge(custom_options) + + modules_paths = options.fetch(:modules_paths) + modules_to_exclude = options.fetch(:exclude) + target = options.fetch(:target) + flags = options.fetch(:flags) + + unimodules = {} + unimodules_duplicates = [] + + project_directory = Pod::Config.instance.project_root + + modules_paths.each { |module_path| + canonical_module_path = Pathname.new(File.join(project_directory, module_path)).cleanpath + glob_pattern = File.join(canonical_module_path, '**/*/**', 'unimodule.json') + + Dir.glob(glob_pattern) { |module_config_path| + unimodule_json = JSON.parse(File.read(module_config_path)) + directory = File.dirname(module_config_path) + platforms = unimodule_json['platforms'] || ['ios'] + targets = unimodule_json['targets'] || ['react-native'] + + if unimodule_supports_platform(platforms, 'ios') && unimodule_supports_target(targets, target) + package_json_path = File.join(directory, 'package.json') + package_json = JSON.parse(File.read(package_json_path)) + package_name = unimodule_json['name'] || package_json['name'] + + if !modules_to_exclude.include?(package_name) + unimodule_config = { 'subdirectory' => 'ios' }.merge(unimodule_json.fetch('ios', {})) + unimodule_version = package_json['version'] + + if unimodules[package_name] + unimodules_duplicates.push(package_name) + end + + if !unimodules[package_name] || Gem::Version.new(unimodule_version) >= Gem::Version.new(unimodules[package_name][:version]) + unimodules[package_name] = { + name: package_name, + directory: directory, + version: unimodule_version, + config: unimodule_config, + warned: false, + } + end + end + end + } + } + + if unimodules.values.length > 0 + puts brown 'Installing unimodules:' + + unimodules.values.sort! { |x,y| x[:name] <=> y[:name] }.each { |unimodule| + directory = unimodule[:directory] + config = unimodule[:config] + + subdirectory = config['subdirectory'] + pod_name = config.fetch('podName', find_pod_name(directory, subdirectory)) + podspec_directory = Pathname.new("#{directory}/#{subdirectory}").relative_path_from(project_directory) + + puts " #{green unimodule[:name]}#{cyan "@"}#{magenta unimodule[:version]} from #{blue podspec_directory}" + + pod_options = flags.merge({ path: podspec_directory.to_s }) + + pod "#{pod_name}", pod_options + } + + if unimodules_duplicates.length > 0 + puts + puts brown "Found some duplicated unimodule packages. Installed the ones with the highest version number." + puts brown "Make sure following dependencies of your project are resolving to one specific version:" + + puts ' ' + unimodules_duplicates + .uniq + .map { |package_name| green(package_name) } + .join(', ') + end + else + puts + puts brown "No unimodules found. Are you sure you've installed JS dependencies before installing pods?" + end + + puts +end + +def find_pod_name(package_path, subdirectory) + podspec_path = Dir.glob(File.join(package_path, subdirectory, '*.podspec')).first + return podspec_path && File.basename(podspec_path).chomp('.podspec') +end + +def unimodule_supports_platform(platforms, platform) + return platforms.class == Array && platforms.include?(platform) +end + +def unimodule_supports_target(targets, target) + return targets.class == Array && targets.include?(target) +end + +def green(message) + return "\e[32m#{message}\e[0m" +end + +def brown(message) + return "\e[33m#{message}\e[0m" +end + +def blue(message) + return "\e[34m#{message}\e[0m" +end + +def magenta(message) + return "\e[35m#{message}\e[0m" +end + +def cyan(message) + return "\e[36m#{message}\e[0m" +end diff --git a/packages/react-native-unimodules/gradle.groovy b/packages/react-native-unimodules/gradle.groovy new file mode 100644 index 0000000000000..ef69c4b7e9f58 --- /dev/null +++ b/packages/react-native-unimodules/gradle.groovy @@ -0,0 +1,260 @@ +import groovy.json.JsonSlurper +import org.gradle.util.VersionNumber + +import java.util.regex.Pattern + +class Unimodule { + String name + List platforms + List targets + List androidPackages + String directory + String version + String androidGroup + String androidSubdirectory + + boolean supportsPlatform(String platform) { + return platforms instanceof List && platforms.contains(platform) + } + + boolean supportsTarget(String target) { + return targets.size() == 0 || targets.contains(target) + } +} + +def readPackageFromJavaOrKotlinFile(String filePath) { + def file = new File(filePath) + def fileReader = new BufferedReader(new FileReader(file)) + def fileContent = "" + while ((fileContent = fileReader.readLine()) != null) { + def match = fileContent =~ /^package ([0-9a-zA-Z._]*);?$/ + if (match.size() == 1 && match[0].size() == 2) { + fileReader.close() + return match[0][1] + } + } + fileReader.close() + + throw new GradleException("Java or Kotlin file $file does not include package declaration") +} + +def readFromBuildGradle(String file) { + def gradleFile = new File(file) + if (!gradleFile.exists()) { + return [:] + } + def fileReader = new BufferedReader(new FileReader(gradleFile)) + def result = [:] + for (def line = fileReader.readLine(); line != null; line = fileReader.readLine()) { + def versionMatch = line.trim() =~ /^version ?= ?'([\w.-]+)'$/ + def groupMatch = line.trim() =~ /^group ?= ?'([\w.]+)'$/ + if (versionMatch.size() == 1 && versionMatch[0].size() == 2) { + result.version = versionMatch[0][1] + } + if (groupMatch.size() == 1 && groupMatch[0].size() == 2) { + result.group = groupMatch[0][1] + } + } + fileReader.close() + return result +} + +def findDefaultBasePackage(String packageDir) { + def pathsJava = new FileNameFinder().getFileNames(packageDir, "android/src/**/*Package.java") + def pathsKt = new FileNameFinder().getFileNames(packageDir, "android/src/**/*Package.kt") + def paths = pathsJava + pathsKt + + if (paths.size != 1) { + return [] + } + + def packageName = readPackageFromJavaOrKotlinFile(paths[0]) + def className = new File(paths[0]).getName().split(Pattern.quote("."))[0] + return ["$packageName.$className"] +} + +def generateBasePackageList(List unimodules) { + def findMainJavaApp = new FileNameFinder().getFileNames(rootProject.getProjectDir().getPath(), '**/MainApplication.java', '') + def findMainKtApp = new FileNameFinder().getFileNames(rootProject.getProjectDir().getPath(), '**/MainApplication.kt', '') + + if (findMainJavaApp.size() != 1 && findMainKtApp.size() != 1) { + throw new GradleException("You need to have MainApplication in your project") + } + + def findMainApp = (findMainJavaApp.size() == 1) ? findMainJavaApp : findMainKtApp + def mainAppDirectory = new File(findMainApp[0]).parentFile + def packageName = readPackageFromJavaOrKotlinFile(findMainApp[0]) + + def fileBuilder = new StringBuilder() + fileBuilder.append("package ${packageName}.generated;\n\n") + + fileBuilder.append("import java.util.Arrays;\n") + fileBuilder.append("import java.util.List;\n") + fileBuilder.append("import org.unimodules.core.interfaces.Package;\n\n") + + fileBuilder.append("public class BasePackageList {\n") + fileBuilder.append(" public List getPackageList() {\n") + fileBuilder.append(" return Arrays.asList(\n") + def isEmptyList = true + for (module in unimodules) { + for (pkg in module.androidPackages) { + fileBuilder.append(" new $pkg(),\n") + isEmptyList = false + } + } + if (!isEmptyList) { + fileBuilder.deleteCharAt(fileBuilder.length() - 2) // remove last comma in a list + } + fileBuilder.append(" );\n") + fileBuilder.append(" }\n") + fileBuilder.append("}\n") + + + new File(mainAppDirectory, "generated").mkdirs() + def javaFile = new File(mainAppDirectory, "generated/BasePackageList.java") + javaFile.createNewFile() + def javaFileWriter = new BufferedWriter(new FileWriter(javaFile)) + javaFileWriter.write(fileBuilder.toString()) + javaFileWriter.close() +} + + +def findUnimodules(String target, List exclude, List modulesPaths) { + def unimodules = [:] + def unimodulesDuplicates = [] + + for (modulesPath in modulesPaths) { + def baseDir = new File(rootProject.getBuildFile(), modulesPath).toString() + def moduleConfigPaths = new FileNameFinder().getFileNames(baseDir, '**/unimodule.json', '') + + for (moduleConfigPath in moduleConfigPaths) { + def unimoduleConfig = new File(moduleConfigPath) + def unimoduleJson = new JsonSlurper().parseText(unimoduleConfig.text) + def directory = unimoduleConfig.getParent() + def buildGradle = readFromBuildGradle(new File(directory, "android/build.gradle").toString()) + def packageJsonFile = new File(directory, 'package.json') + def packageJson = new JsonSlurper().parseText(packageJsonFile.text) + + def unimodule = new Unimodule() + unimodule.name = unimoduleJson.name ?: packageJson.name + unimodule.directory = directory + unimodule.version = buildGradle.version ?: packageJson.version ?: "UNVERSIONED" + unimodule.androidGroup = buildGradle.group ?: "org.unimodules" + unimodule.androidSubdirectory = unimoduleJson.android?.subdirectory ?: "android" + unimodule.platforms = unimoduleJson.platforms != null ? unimoduleJson.platforms : [] + assert unimodule.platforms instanceof List + unimodule.targets = unimoduleJson.targets != null ? unimoduleJson.targets : [] + assert unimodule.targets instanceof List + unimodule.androidPackages = unimoduleJson.android?.packages != null ? + unimoduleJson.android.packages : findDefaultBasePackage(directory) + assert unimodule.androidPackages instanceof List + + if (unimodule.supportsPlatform('android') && unimodule.supportsTarget(target)) { + if (!exclude.contains(unimodule.name)) { + if (unimodules[unimodule.name]) { + unimodulesDuplicates.add(unimodule.name) + } + + if (!unimodules[unimodule.name] || + VersionNumber.parse(unimodule.version) >= VersionNumber.parse(unimodules[unimodule.name].version)) { + unimodules[unimodule.name] = unimodule + } + } + } + } + } + return [ + unimodules: unimodules.collect { entry -> entry.value }, + duplicates: unimodulesDuplicates.unique() + ] +} + + +class Colors { + static final String NORMAL = "\u001B[0m" + static final String RED = "\u001B[31m" + static final String GREEN = "\u001B[32m" + static final String YELLOW = "\u001B[33m" + static final String MAGENTA = "\u001B[35m" +} + +def addUnimodulesDependencies(String target, List exclude, List modulesPaths, Closure addUnimodule) { + if (!(new File(project.rootProject.projectDir.parentFile, 'package.json').exists())) { + // There's no package.json + throw new GradleException( + "'addUnimodulesDependencies()' is being used in a project that doesn't seem to be a React Native project." + ) + } + + def results = findUnimodules(target, exclude, modulesPaths) + def unimodules = results.unimodules + def duplicates = results.duplicates + generateBasePackageList(unimodules) + + if (unimodules.size() > 0) { + println() + println Colors.YELLOW + 'Installing unimodules:' + Colors.NORMAL + for (unimodule in unimodules) { + println ' ' + Colors.GREEN + unimodule.name + Colors.YELLOW + '@' + Colors.RED + unimodule.version + Colors.NORMAL + ' from ' + Colors.MAGENTA + unimodule.directory + Colors.NORMAL + addUnimodule(unimodule) + } + + if (duplicates.size() > 0) { + println() + println Colors.YELLOW + 'Found some duplicated unimodule packages. Installed the ones with the highest version number.' + Colors.NORMAL + println Colors.YELLOW + 'Make sure following dependencies of your project are resolving to one specific version:' + Colors.NORMAL + + println ' ' + duplicates + .collect { unimoduleName -> Colors.GREEN + unimoduleName + Colors.NORMAL } + .join(', ') + } + } else { + println() + println Colors.YELLOW + "No unimodules found. Are you sure you've installed JS dependencies?" + Colors.NORMAL + } +} + +ext.addUnimodulesDependencies = { Map customOptions = [:] -> + def options = [ + modulesPaths : ['../../node_modules'], + configuration: 'implementation', + target : 'react-native', + exclude : [], + ] << customOptions + + addUnimodulesDependencies(options.target, options.exclude, options.modulesPaths, {unimodule -> + Object dependency = project.project(':' + unimodule.name) + project.dependencies.add(options.configuration, dependency) + }) +} + +ext.addMavenUnimodulesDependencies = { Map customOptions = [:] -> + def options = [ + modulesPaths : ['../../node_modules'], + configuration: 'implementation', + target : 'react-native', + exclude : [], + ] << customOptions + + addUnimodulesDependencies(options.target, options.exclude, options.modulesPaths, {unimodule -> + project.dependencies.add( + options.configuration, + "${unimodule.androidGroup}:${unimodule.name}:${unimodule.version}" + ) + }) +} + +ext.includeUnimodulesProjects = { Map customOptions = [:] -> + def options = [ + modulesPaths: ['../../node_modules'], + target : 'react-native', + exclude : [], + ] << customOptions + + def unimodules = findUnimodules(options.target, options.exclude, options.modulesPaths).unimodules + + for (unimodule in unimodules) { + include ":${unimodule.name}" + project(":${unimodule.name}").projectDir = new File(unimodule.directory, unimodule.androidSubdirectory) + } +} diff --git a/packages/react-native-unimodules/package.json b/packages/react-native-unimodules/package.json new file mode 100644 index 0000000000000..1add21bee0bf7 --- /dev/null +++ b/packages/react-native-unimodules/package.json @@ -0,0 +1,62 @@ +{ + "name": "react-native-unimodules", + "version": "0.10.0", + "description": "This library contains the core unimodule infrastructure and a collection of unimodules and interfaces that are commonly depended on by other unimodules.", + "main": "build/index.js", + "types": "build/index.d.ts", + "private": false, + "scripts": { + "build": "expo-module build", + "clean": "expo-module clean", + "lint": "expo-module lint", + "test": "expo-module test", + "prepare": "expo-module prepare", + "prepublishOnly": "expo-module prepublishOnly", + "expo-module": "expo-module", + "postinstall": "node ./scripts/postinstall.js" + }, + "keywords": [ + "react-native", + "expo", + "modules", + "unimodules" + ], + "repository": { + "type": "git", + "url": "https://github.com/expo/expo.git", + "directory": "packages/expo-sms" + }, + "bugs": { + "url": "https://github.com/expo/expo/issues" + }, + "author": "650 Industries, Inc.", + "license": "MIT", + "homepage": "https://docs.expo.io/versions/latest/sdk/sms/", + "jest": { + "preset": "expo-module-scripts/ios" + }, + "dependencies": { + "@unimodules/core": "~5.2.0", + "@unimodules/react-native-adapter": "~5.3.0", + "chalk": "^2.4.2", + "expo-asset": "~8.1.6", + "expo-constants": "~9.1.0", + "expo-file-system": "~9.0.0", + "expo-image-loader": "~1.1.0", + "expo-permissions": "~8.2.0", + "unimodules-app-loader": "~1.1.0", + "unimodules-barcode-scanner-interface": "~5.2.0", + "unimodules-camera-interface": "~5.2.0", + "unimodules-constants-interface": "~5.2.0", + "unimodules-face-detector-interface": "~5.2.0", + "unimodules-file-system-interface": "~5.2.0", + "unimodules-font-interface": "~5.2.0", + "unimodules-image-loader-interface": "~5.2.0", + "unimodules-permissions-interface": "~5.2.0", + "unimodules-sensors-interface": "~5.2.0", + "unimodules-task-manager-interface": "~5.2.0" + }, + "devDependencies": { + "expo-module-scripts": "~1.2.0" + } +} diff --git a/packages/react-native-unimodules/scripts/postinstall.js b/packages/react-native-unimodules/scripts/postinstall.js new file mode 100644 index 0000000000000..1bfdeca50b748 --- /dev/null +++ b/packages/react-native-unimodules/scripts/postinstall.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +const chalk = require('chalk'); +const packageJson = require('../package.json'); + +// Don't run the script when doing `yarn` or `npm install` in this package. +if (process.env.PWD === process.env.INIT_CWD) { + process.exit(0); + return; +} + +console.info(chalk.green(` +Successfully installed ${chalk.red(packageJson.name)}. This package contains core unimodules that are commonly depended on by other unimodules. You will need to configure your project before using other unimodules like ${chalk.red('expo-camera')}, ${chalk.red('expo-media-library')} and others. +See configuration guide: + ${chalk.blue(`https://www.npmjs.com/package/${packageJson.name}/v/${packageJson.version}`)} +`)); diff --git a/packages/react-native-unimodules/src/index.ts b/packages/react-native-unimodules/src/index.ts new file mode 100644 index 0000000000000..4b698a41cde81 --- /dev/null +++ b/packages/react-native-unimodules/src/index.ts @@ -0,0 +1,7 @@ +// Override React Native's asset resolution for `Image` components +import { Asset } from 'expo-asset'; +import Constants from 'expo-constants'; +import * as FileSystem from 'expo-file-system'; +import * as Permissions from 'expo-permissions'; + +export { Asset, Constants, FileSystem, Permissions }; diff --git a/packages/react-native-unimodules/tsconfig.json b/packages/react-native-unimodules/tsconfig.json new file mode 100644 index 0000000000000..a24ec0ff2f600 --- /dev/null +++ b/packages/react-native-unimodules/tsconfig.json @@ -0,0 +1,9 @@ +// @generated by expo-module-scripts +{ + "extends": "expo-module-scripts/tsconfig.base", + "compilerOptions": { + "outDir": "./build" + }, + "include": ["./src"], + "exclude": ["**/__mocks__/*", "**/__tests__/*"] +} diff --git a/tools/expotools/src/publish-packages/tasks/prepareParcels.ts b/tools/expotools/src/publish-packages/tasks/prepareParcels.ts index 20973c20394be..ee465cd54a186 100644 --- a/tools/expotools/src/publish-packages/tasks/prepareParcels.ts +++ b/tools/expotools/src/publish-packages/tasks/prepareParcels.ts @@ -8,7 +8,6 @@ import { Task } from '../../TasksRunner'; import { CommandOptions, Parcel, TaskArgs } from '../types'; const { green } = chalk; -const IGNORED_PACKAGES = ['react-native-unimodules']; /** * Gets a list of public packages in the monorepo, downloads `npm view` result of them, @@ -38,7 +37,7 @@ export const prepareParcels = new Task( const filteredPackages = allPackages.filter((pkg) => { const isPrivate = pkg.packageJson.private; const isIncluded = packageNames.length === 0 || packageNames.includes(pkg.packageName); - return !isPrivate && isIncluded && !IGNORED_PACKAGES.includes(pkg.packageName); + return !isPrivate && isIncluded; }); parcels.push(...(await Promise.all(filteredPackages.map(createParcelAsync))));