Skip to content

Commit

Permalink
feat: allow adding modules only for the specific build type (react-na…
Browse files Browse the repository at this point in the history
…tive-community#1298)

* feat: allow adding modules only for the specific build type

* Add tests

* Apply requested changes

* Apply suggestions from code review

Co-authored-by: Brent Vatne <[email protected]>

Co-authored-by: Brent Vatne <[email protected]>
  • Loading branch information
lukmccall and brentvatne authored Nov 24, 2020
1 parent aad25b3 commit ecbfbd2
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 16 deletions.
10 changes: 10 additions & 0 deletions docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ type DependencyParamsIOST = {
project?: string;
podspecPath?: string;
sharedLibraries?: string[];
configurations?: string[];
};

type DependencyParamsAndroidT = {
sourceDir?: string;
manifestPath?: string;
packageImportPath?: string;
packageInstance?: string;
buildTypes?: string[];
};
```

Expand Down Expand Up @@ -107,6 +109,10 @@ module.exports = {

See [`script_phase` options](https://www.rubydoc.info/gems/cocoapods-core/Pod/Podfile/DSL#script_phase-instance_method) for a full list of available object keys.

#### platforms.ios.configurations

An array of build configurations which will include the dependency. If the array is empty, your dependency will be installed in all configurations. If you're working on a helper library that should only be included in development, such as a replacement for the React Native development menu, you should set this to `['debug']` to avoid shipping the library in a release build. For more details, see [`build configurations`](https://guides.cocoapods.org/syntax/podfile.html#pod).

#### platforms.android.sourceDir

A relative path to a folder with Android project (Gradle root project), e.g. `./path/to/custom-android`. By default, CLI searches for `./android` as source dir.
Expand All @@ -125,6 +131,10 @@ Custom syntax to instantiate a package. By default, it's a `new AwesomePackage()

For settings applicable on other platforms, please consult their respective documentation.

#### platforms.android.buildTypes

An array of build variants or flavors which will include the dependency. If the array is empty, your dependency will be included in all build types. If you're working on a helper library that should only be included in development, such as a replacement for the React Native development menu, you should set this to `['debug']` to avoid shipping the library in a release build. For more details, see [`build variants`](https://developer.android.com/studio/build/build-variants#dependencies).

### assets

An array of assets folders to glob for files to link.
Expand Down
1 change: 1 addition & 0 deletions packages/cli-types/src/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface AndroidDependencyConfig {
packageInstance: string;
manifestPath: string;
packageName: string;
buildTypes: string[];
}

export type AndroidDependencyParams = Partial<AndroidDependencyConfig>;
8 changes: 6 additions & 2 deletions packages/cli-types/src/ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export interface IOSProjectParams {
scriptPhases?: Array<any>;
}

export interface IOSDependencyParams extends IOSProjectParams {}
export interface IOSDependencyParams extends IOSProjectParams {
configurations?: string[];
}

// The following types are used in untyped-parts of the codebase, so I am leaving them
// until we actually need them.
Expand All @@ -37,7 +39,9 @@ export interface IOSProjectConfig {
plist: Array<any>;
}

export interface IOSDependencyConfig extends IOSProjectConfig {}
export interface IOSDependencyConfig extends IOSProjectConfig {
configurations: string[];
}

/**
* @see https://www.rubydoc.info/gems/cocoapods-core/Pod/Podfile/DSL#script_phase-instance_method
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should apply build types from dependency config 1`] = `
Object {
"assets": Array [],
"hooks": Object {},
"name": "react-native-test",
"params": Array [],
"platforms": Object {
"android": null,
"ios": Object {
"configurations": Array [
"debug",
],
"folder": "<<REPLACED>>/node_modules/react-native-test",
"libraryFolder": "Libraries",
"pbxprojPath": "<<REPLACED>>/node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj",
"plist": Array [],
"podfile": null,
"podspecPath": null,
"projectName": "HelloWorld.xcodeproj",
"projectPath": "<<REPLACED>>/node_modules/react-native-test/ios/HelloWorld.xcodeproj",
"scriptPhases": Array [],
"sharedLibraries": Array [],
"sourceDir": "<<REPLACED>>/node_modules/react-native-test/ios",
},
},
"root": "<<REPLACED>>/node_modules/react-native-test",
}
`;

exports[`should have a valid structure by default 1`] = `
Object {
"assets": Array [],
Expand Down Expand Up @@ -37,6 +66,7 @@ Object {
"platforms": Object {
"android": null,
"ios": Object {
"configurations": Array [],
"folder": "<<REPLACED>>/node_modules/react-native-test",
"libraryFolder": "Libraries",
"pbxprojPath": "<<REPLACED>>/node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj",
Expand All @@ -63,6 +93,7 @@ Object {
"platforms": Object {
"android": null,
"ios": Object {
"configurations": Array [],
"folder": "<<REPLACED>>/node_modules/react-native-test",
"libraryFolder": "Libraries",
"pbxprojPath": "<<REPLACED>>/node_modules/react-native-test/customLocation/customProject.xcodeproj/project.pbxproj",
Expand Down Expand Up @@ -101,6 +132,7 @@ Object {
"platforms": Object {
"android": null,
"ios": Object {
"configurations": Array [],
"folder": "<<REPLACED>>/node_modules/react-native-test",
"libraryFolder": "Libraries",
"pbxprojPath": "<<REPLACED>>/node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj",
Expand All @@ -122,3 +154,32 @@ Object {
exports[`should skip packages that have invalid configuration: dependencies config 1`] = `Object {}`;

exports[`should skip packages that have invalid configuration: logged warning 1`] = `"Package react-native has been ignored because it contains invalid configuration. Reason: \\"dependency.invalidProperty\\" is not allowed"`;

exports[`supports dependencies from user configuration with custom build type 1`] = `
Object {
"assets": Array [],
"hooks": Object {},
"name": "react-native-test",
"params": Array [],
"platforms": Object {
"android": null,
"ios": Object {
"configurations": Array [
"custom_build_type",
],
"folder": "<<REPLACED>>/node_modules/react-native-test",
"libraryFolder": "Libraries",
"pbxprojPath": "<<REPLACED>>/node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj",
"plist": Array [],
"podfile": null,
"podspecPath": null,
"projectName": "HelloWorld.xcodeproj",
"projectPath": "<<REPLACED>>/node_modules/react-native-test/ios/HelloWorld.xcodeproj",
"scriptPhases": Array [],
"sharedLibraries": Array [],
"sourceDir": "<<REPLACED>>/node_modules/react-native-test/ios",
},
},
"root": "<<REPLACED>>/node_modules/react-native-test",
}
`;
63 changes: 63 additions & 0 deletions packages/cli/src/tools/config/__tests__/index-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ module.exports = {
"platforms": Object {
"android": null,
"ios": Object {
"configurations": Array [],
"folder": "<<REPLACED>>/native-libs/local-lib",
"libraryFolder": "Libraries",
"pbxprojPath": "<<REPLACED>>/native-libs/local-lib/ios/LocalRNLibrary.xcodeproj/project.pbxproj",
Expand All @@ -285,3 +286,65 @@ module.exports = {
}
`);
});

test('should apply build types from dependency config', () => {
DIR = getTempDirectory('config_test_apply_dependency_config');
writeFiles(DIR, {
...REACT_NATIVE_MOCK,
'node_modules/react-native-test/package.json': '{}',
'node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj':
'',
'node_modules/react-native-test/react-native.config.js': `module.exports = {
dependency: {
platforms: {
ios: {
configurations: ["debug"]
}
}
}
}`,
'package.json': `{
"dependencies": {
"react-native": "0.0.1",
"react-native-test": "0.0.1"
}
}`,
});
const {dependencies} = loadConfig(DIR);
expect(
removeString(dependencies['react-native-test'], DIR),
).toMatchSnapshot();
});

test('supports dependencies from user configuration with custom build type', () => {
DIR = getTempDirectory('config_test_apply_custom_build_config');
writeFiles(DIR, {
...REACT_NATIVE_MOCK,
'react-native.config.js': `module.exports = {
dependencies: {
'react-native-test': {
platforms: {
ios: {
configurations: ["custom_build_type"]
}
}
},
}
}`,
'node_modules/react-native-test/package.json': '{}',
'node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj':
'',
'node_modules/react-native-test/react-native.config.js': `module.exports = {}`,
'package.json': `{
"dependencies": {
"react-native": "0.0.1",
"react-native-test": "0.0.1"
}
}`,
});

const {dependencies} = loadConfig(DIR);
expect(
removeString(dependencies['react-native-test'], DIR),
).toMatchSnapshot();
});
4 changes: 4 additions & 0 deletions packages/cli/src/tools/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const dependencyConfig = t
sharedLibraries: t.array().items(t.string()),
libraryFolder: t.string(),
scriptPhases: t.array().items(t.object()),
configurations: t.array().items(t.string()).default([]),
})
.default({}),
android: t
Expand All @@ -74,6 +75,7 @@ export const dependencyConfig = t
manifestPath: t.string(),
packageImportPath: t.string(),
packageInstance: t.string(),
buildTypes: t.array().items(t.string()).default([]),
})
.default({}),
})
Expand Down Expand Up @@ -129,6 +131,7 @@ export const projectConfig = t
projectName: t.string(),
libraryFolder: t.string(),
sharedLibraries: t.array().items(t.string()),
configurations: t.array().items(t.string()).default([]),
})
.allow(null),
android: t
Expand All @@ -137,6 +140,7 @@ export const projectConfig = t
folder: t.string(),
packageImportPath: t.string(),
packageInstance: t.string(),
buildTypes: t.array().items(t.string()).default([]),
})
.allow(null),
}),
Expand Down
31 changes: 23 additions & 8 deletions packages/platform-android/native_modules.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,17 @@ class ReactNativeModules {
private String packageName
private File root
private ArrayList<HashMap<String, String>> reactNativeModules
private HashMap<String, ArrayList> reactNativeModulesBuildVariants

private static String LOG_PREFIX = ":ReactNative:"

ReactNativeModules(Logger logger, File root) {
this.logger = logger
this.root = root

def (nativeModules, packageName) = this.getReactNativeConfig()
def (nativeModules, reactNativeModulesBuildVariants, packageName) = this.getReactNativeConfig()
this.reactNativeModules = nativeModules
this.reactNativeModulesBuildVariants = reactNativeModulesBuildVariants
this.packageName = packageName
}

Expand All @@ -104,8 +106,16 @@ class ReactNativeModules {
reactNativeModules.forEach { reactNativeModule ->
def nameCleansed = reactNativeModule["nameCleansed"]
appProject.dependencies {
// TODO(salakar): are other dependency scope methods such as `api` required?
implementation project(path: ":${nameCleansed}")
if (reactNativeModulesBuildVariants.containsKey(nameCleansed)) {
reactNativeModulesBuildVariants
.get(nameCleansed)
.forEach { buildVariant ->
"${buildVariant}Implementation" project(path: ":${nameCleansed}")
}
} else {
// TODO(salakar): are other dependency scope methods such as `api` required?
implementation project(path: ":${nameCleansed}")
}
}
}
}
Expand Down Expand Up @@ -208,7 +218,8 @@ class ReactNativeModules {
if (this.reactNativeModules != null) return this.reactNativeModules

ArrayList<HashMap<String, String>> reactNativeModules = new ArrayList<HashMap<String, String>>()

HashMap<String, ArrayList> reactNativeModulesBuildVariants = new HashMap<String, ArrayList>()

/**
* Resolve the CLI location from Gradle file
*
Expand Down Expand Up @@ -243,22 +254,26 @@ class ReactNativeModules {

if (androidConfig != null && androidConfig["sourceDir"] != null) {
this.logger.info("${LOG_PREFIX}Automatically adding native module '${name}'")

HashMap reactNativeModuleConfig = new HashMap<String, String>()
def nameCleansed = name.replaceAll('[~*!\'()]+', '_').replaceAll('^@([\\w-.]+)/', '$1_')
reactNativeModuleConfig.put("name", name)
reactNativeModuleConfig.put("nameCleansed", name.replaceAll('[~*!\'()]+', '_').replaceAll('^@([\\w-.]+)/', '$1_'))
reactNativeModuleConfig.put("nameCleansed", nameCleansed)
reactNativeModuleConfig.put("androidSourceDir", androidConfig["sourceDir"])
reactNativeModuleConfig.put("packageInstance", androidConfig["packageInstance"])
reactNativeModuleConfig.put("packageImportPath", androidConfig["packageImportPath"])
if (!androidConfig["buildTypes"].isEmpty()) {
reactNativeModulesBuildVariants.put(nameCleansed, androidConfig["buildTypes"])
}
this.logger.trace("${LOG_PREFIX}'${name}': ${reactNativeModuleConfig.toMapString()}")

reactNativeModules.add(reactNativeModuleConfig)
} else {
this.logger.info("${LOG_PREFIX}Skipping native module '${name}'")
}
}

return [reactNativeModules, json["project"]["android"]["packageName"]];
return [reactNativeModules, reactNativeModulesBuildVariants, json["project"]["android"]["packageName"]];
}
}

Expand Down
10 changes: 9 additions & 1 deletion packages/platform-android/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,13 @@ export function dependencyConfig(
const packageInstance =
userConfig.packageInstance || `new ${packageClassName}()`;

return {sourceDir, folder: root, packageImportPath, packageInstance};
const buildTypes = userConfig.buildTypes || [];

return {
sourceDir,
folder: root,
packageImportPath,
packageInstance,
buildTypes,
};
}
5 changes: 2 additions & 3 deletions packages/platform-ios/native_modules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def use_native_modules!(config = nil)
next unless package_config = package["platforms"]["ios"]

podspec_path = package_config["podspecPath"]
configurations = package_config["configurations"]

# Add a warning to the queue and continue to the next dependency if the podspec_path is nil/empty
if podspec_path.nil? || podspec_path.empty?
Expand Down Expand Up @@ -86,9 +87,7 @@ def use_native_modules!(config = nil)
podspec_dir_path = Pathname.new(File.dirname(podspec_path))

relative_path = podspec_dir_path.relative_path_from project_root

pod spec.name, :path => relative_path.to_path

pod spec.name, :path => relative_path.to_path, :configurations => configurations
if package_config["scriptPhases"] && !this_target.abstract?
# Can be either an object, or an array of objects
Array(package_config["scriptPhases"]).each do |phase|
Expand Down
Loading

0 comments on commit ecbfbd2

Please sign in to comment.