Skip to content

Commit

Permalink
Introduce plugin config based on react native version
Browse files Browse the repository at this point in the history
  • Loading branch information
belemaire committed Apr 10, 2020
1 parent 137944f commit 9da9c79
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 23 deletions.
24 changes: 23 additions & 1 deletion docs/platform-parts/manifest/native-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,26 @@ Can be used in case a native module doesn't have yet an available podspec file o

- `extraPods`

Array of extra pod statements that will be injected in the Container Podfile.
Array of extra pod statements that will be injected in the Container Podfile.

#### Configuration based on react native version

A single configuration file can hold more than one configuration for the plugin, based on the react native version being used.\
This can be useful if the `android` or `ios` configuration has to be different for some specific react native version(s).

`ios` and `android` configuration objects allow specifying one or multiple keys following `react-native@version` format. The `version` can be any fixed version or [semver supported range](https://github.com/npm/node-semver#advanced-range-syntax).

For example:

```json
{
"ios": {
"react-native@>=0.61.0": {
}
}
}
```

The exact same configuration directives as the ones used for `ios` and `android` can be used inside these blocks.

If such keys are found in a configuration file, when generating a container, the generator will evaluate these keys, from top to bottom, against the current react native version. If there is a match, it will use the matched version config. Otherwise it will use the top level, default config kept outside of the `react-native@version` blocks.
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ export default class ApiImplAndroidGenerator implements ApiImplGeneratable {
for (pluginPath of pluginsPaths) {
const pluginConfig = await manifest.getPluginConfig(
pluginPath,
'android'
'android',
reactNativeVersion
)
if (pluginConfig) {
log.debug(`Copying ${pluginPath.name} to ${outputDirectory}`)
Expand Down
6 changes: 4 additions & 2 deletions ern-composite-gen/src/GeneratedComposite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class GeneratedComposite implements Composite {
platform: NativePlatform
): Promise<PackagePath[]> {
const dependencies = await this.getResolvedNativeDependencies()
const rnVersion = dependencies.resolved.find(d => d.name === 'react-native')
?.version!
const result: PackagePath[] = []
for (const dependency of dependencies.resolved) {
// Always include react-native
Expand All @@ -78,11 +80,11 @@ export class GeneratedComposite implements Composite {
}

if (platform === 'android') {
if (await manifest.getPluginConfig(dependency, 'android')) {
if (await manifest.getPluginConfig(dependency, 'android', rnVersion)) {
result.push(dependency)
}
} else {
if (await manifest.getPluginConfig(dependency, 'ios')) {
if (await manifest.getPluginConfig(dependency, 'ios', rnVersion)) {
result.push(dependency)
}
}
Expand Down
6 changes: 4 additions & 2 deletions ern-composite-gen/src/WorkspaceComposite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export class WorkspaceComposite implements Composite {
platform: NativePlatform
): Promise<PackagePath[]> {
const dependencies = await this.getResolvedNativeDependencies()
const rnVersion = dependencies.resolved.find(d => d.name === 'react-native')
?.version!
const result: PackagePath[] = []
for (const dependency of dependencies.resolved) {
// Always include react-native
Expand All @@ -79,10 +81,10 @@ export class WorkspaceComposite implements Composite {
}

if (platform === 'android') {
if (await manifest.getPluginConfig(dependency, 'android')) {
if (await manifest.getPluginConfig(dependency, 'android', rnVersion)) {
result.push(dependency)
}
} else if (await manifest.getPluginConfig(dependency, 'ios')) {
} else if (await manifest.getPluginConfig(dependency, 'ios', rnVersion)) {
result.push(dependency)
}
}
Expand Down
13 changes: 11 additions & 2 deletions ern-container-gen-android/src/AndroidGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ export default class AndroidGenerator implements ContainerGenerator {
for (const plugin of config.plugins) {
let pluginConfig:
| PluginConfig<'android'>
| undefined = await manifest.getPluginConfig(plugin, 'android')
| undefined = await manifest.getPluginConfig(
plugin,
'android',
reactNativePlugin.version
)
if (!pluginConfig) {
log.warn(
`Skipping ${plugin.name} as it does not have an Android configuration`
Expand Down Expand Up @@ -559,11 +563,16 @@ export default class AndroidGenerator implements ContainerGenerator {
plugins: PackagePath[],
outDir: string
): Promise<any> {
const rnVersion = plugins.find(p => p.name === 'react-native')?.version!
for (const plugin of plugins) {
if (plugin.name === 'react-native') {
continue
}
const pluginConfig = await manifest.getPluginConfig(plugin, 'android')
const pluginConfig = await manifest.getPluginConfig(
plugin,
'android',
rnVersion
)
if (!pluginConfig) {
log.warn(
`Skipping ${plugin.name} as it does not have an Android configuration`
Expand Down
5 changes: 4 additions & 1 deletion ern-container-gen-ios/src/IosGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,14 @@ export default class IosGenerator implements ContainerGenerator {
plugins: PackagePath[],
outDir: string
): Promise<any> {
const rnVersion = plugins.find(p => p.name === 'react-native')?.version!
for (const plugin of plugins) {
if (plugin.name === 'react-native') {
continue
}
const pluginConfig:
| PluginConfig<'ios'>
| undefined = await manifest.getPluginConfig(plugin, 'ios')
| undefined = await manifest.getPluginConfig(plugin, 'ios', rnVersion)
if (!pluginConfig) {
log.warn(
`${plugin.name} does not have any injection configuration for ios platform`
Expand Down Expand Up @@ -242,10 +243,12 @@ export default class IosGenerator implements ContainerGenerator {
mustacheView: any,
projectSpec: any
) {
const rnVersion = plugins.find(p => p.name === 'react-native')?.version!
for (const plugin of plugins) {
const pluginConfig = await manifest.getPluginConfig(
plugin,
'ios',
rnVersion,
projectSpec.projectName
)
if (!pluginConfig) {
Expand Down
9 changes: 7 additions & 2 deletions ern-container-gen/src/generatePluginsMustacheViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export async function generatePluginsMustacheViews(
platform: 'android' | 'ios'
) {
const pluginsViews: any[] = []
const rnVersion = plugins.find(p => p.name === 'react-native')?.version!
log.debug('Generating plugins mustache views')
for (const plugin of plugins) {
if (plugin.name === 'react-native') {
Expand All @@ -13,9 +14,13 @@ export async function generatePluginsMustacheViews(

let pluginConfig: PluginConfig<'android' | 'ios'> | undefined
if (platform === 'android') {
pluginConfig = await manifest.getPluginConfig(plugin, 'android')
pluginConfig = await manifest.getPluginConfig(
plugin,
'android',
rnVersion
)
} else {
pluginConfig = await manifest.getPluginConfig(plugin, 'ios')
pluginConfig = await manifest.getPluginConfig(plugin, 'ios', rnVersion)
}
if (!pluginConfig) {
log.warn(
Expand Down
78 changes: 66 additions & 12 deletions ern-core/src/Manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,26 @@ import { isDependencyApi, isDependencyApiImpl } from './utils'
import config from './config'
import log from './log'
import { NativePlatform } from './NativePlatform'
import semver from 'semver'

export type PluginConfig<T extends NativePlatform> = T extends 'android'
? AndroidPluginConfig
: IosPluginConfig

export type ManifestPlatformPluginConfig<T extends NativePlatform> =
| AndroidPluginConfig
| { [rnversion: string]: PluginConfig<T> }
| (AndroidPluginConfig & { [rnversion: string]: PluginConfig<T> })

/**
* Represent the plugin structure as seen in the manifest
* i.e the config.json structure
*/
export interface ManifestPluginConfig {
android?: AndroidPluginConfig
ios?: IosPluginConfig
android?: ManifestPlatformPluginConfig<'android'>
ios?: ManifestPlatformPluginConfig<'ios'>
origin?: PluginOrigin
path: string
path?: string
}

/**
Expand Down Expand Up @@ -655,9 +665,39 @@ export class Manifest {
return pluginConfigPath !== undefined
}

public selectPluginConfig<T extends NativePlatform>(
pluginConfig: ManifestPlatformPluginConfig<T>,
reactNativeVersion: string
): PluginConfig<T> {
const rnVersionKeys = Object.keys(pluginConfig).filter(k =>
k.startsWith('react-native@')
)

let selectedVersionKey
if (rnVersionKeys.length > 0) {
for (const rnVersionKey of rnVersionKeys) {
if (
semver.satisfies(
reactNativeVersion,
rnVersionKey.replace('react-native@', '')
)
) {
selectedVersionKey = rnVersionKey
}
}
}

return selectedVersionKey
? ((pluginConfig as { [rnversion: string]: PluginConfig<T> })[
selectedVersionKey
] as PluginConfig<T>)
: (pluginConfig as PluginConfig<T>)
}

public async getPluginConfigFromManifest(
plugin: PackagePath,
platformVersion: string,
reactNativeVersion: string,
projectName: string,
platform: NativePlatform
): Promise<PluginConfig<'android' | 'ios'> | undefined> {
Expand All @@ -681,7 +721,12 @@ export class Manifest {

// Add default value (convention) for Android subsection for missing fields
if (platform === 'android' && result.android) {
result.android.root = result.android.root ?? 'android'
const res: PluginConfig<'android'> = this.selectPluginConfig(
result.android,
reactNativeVersion
)

res.root = res.root ?? 'android'

const matchedFiles = shell
.find(pluginConfigPath)
Expand All @@ -690,7 +735,7 @@ export class Manifest {
})
if (matchedFiles && matchedFiles.length === 1) {
const pluginHookClass = path.basename(matchedFiles[0], '.java')
result.android.pluginHook = {
res.pluginHook = {
configurable: false,
name: pluginHookClass,
}
Expand All @@ -699,13 +744,18 @@ export class Manifest {
.readFileSync(matchedFiles[0], 'utf-8')
.includes('public static class Config')
) {
result.android.pluginHook.configurable = true
res.pluginHook.configurable = true
}
}
result.android.path = pluginConfigPath
return result.android
res.path = pluginConfigPath
return res
} else if (platform === 'ios' && result.ios) {
result.ios.root = result.ios.root ?? 'ios'
const res: PluginConfig<'ios'> = this.selectPluginConfig(
result.ios,
reactNativeVersion
)

res.root = res.root ?? 'ios'

const matchedHeaderFiles = shell
.find(pluginConfigPath)
Expand All @@ -725,13 +775,13 @@ export class Manifest {
matchedSourceFiles.length === 1
) {
const pluginHookClass = path.basename(matchedHeaderFiles[0], '.h')
result.ios.pluginHook = {
res.pluginHook = {
configurable: true,
name: pluginHookClass,
}
}
result.ios.path = pluginConfigPath
return result.ios
res.path = pluginConfigPath
return res
}
}

Expand All @@ -755,20 +805,23 @@ export class Manifest {
public async getPluginConfig(
plugin: PackagePath,
platform: 'android',
reactNativeVersion: string,
projectName?: string,
platformVersion?: string
): Promise<PluginConfig<'android'> | undefined>

public async getPluginConfig(
plugin: PackagePath,
platform: 'ios',
reactNativeVersion: string,
projectName?: string,
platformVersion?: string
): Promise<PluginConfig<'ios'> | undefined>

public async getPluginConfig(
plugin: PackagePath,
platform: NativePlatform,
reactNativeVersion: string,
projectName: string = 'ElectrodeContainer',
platformVersion: string = Platform.currentVersion
): Promise<PluginConfig<'android' | 'ios'> | undefined> {
Expand All @@ -791,6 +844,7 @@ export class Manifest {
result = await this.getPluginConfigFromManifest(
plugin,
platformVersion,
reactNativeVersion,
projectName,
platform
)
Expand Down
2 changes: 2 additions & 0 deletions ern-core/src/iosUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export async function fillProjectHull(

const injectPluginsTaskMsg = 'Injecting Native Dependencies'
const injectPluginsKaxTask = kax.task(injectPluginsTaskMsg)
const rnVersion = plugins.find(p => p.name === 'react-native')?.version!
const additionalPods = []
const destPodfilePath = path.join(pathSpec.outputDir, 'Podfile')

Expand All @@ -93,6 +94,7 @@ export async function fillProjectHull(
| undefined = await manifest.getPluginConfig(
plugin,
'ios',
rnVersion,
projectSpec.projectName
)
if (!pluginConfig) {
Expand Down

0 comments on commit 9da9c79

Please sign in to comment.