-
Notifications
You must be signed in to change notification settings - Fork 932
feat: support local RN libraries in autolinking #451
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,7 +73,7 @@ function loadConfig(projectRoot: string = process.cwd()): ConfigT { | |
| ? path.resolve(projectRoot, userConfig.reactNativePath) | ||
| : resolveReactNativePath(projectRoot); | ||
| }, | ||
| dependencies: {}, | ||
| dependencies: userConfig.dependencies, | ||
| commands: userConfig.commands, | ||
| get assets() { | ||
| return findAssets(projectRoot, userConfig.assets); | ||
|
|
@@ -97,90 +97,94 @@ function loadConfig(projectRoot: string = process.cwd()): ConfigT { | |
|
|
||
| let depsWithWarnings = []; | ||
|
|
||
| const finalConfig = findDependencies(projectRoot).reduce( | ||
| (acc: ConfigT, dependencyName) => { | ||
| let root; | ||
| let config; | ||
| try { | ||
| root = resolveNodeModuleDir(projectRoot, dependencyName); | ||
| const output = readDependencyConfigFromDisk(root); | ||
| config = output.config; | ||
| const finalConfig = [ | ||
| ...Object.keys(userConfig.dependencies), | ||
| ...findDependencies(projectRoot), | ||
| ].reduce((acc: ConfigT, dependencyName) => { | ||
| const localDependencyRoot = | ||
| userConfig.dependencies[dependencyName] && | ||
| userConfig.dependencies[dependencyName].root; | ||
| let root; | ||
| let config; | ||
| try { | ||
| root = | ||
| localDependencyRoot || | ||
| resolveNodeModuleDir(projectRoot, dependencyName); | ||
| const output = readDependencyConfigFromDisk(root); | ||
| config = output.config; | ||
|
|
||
| if (output.legacy) { | ||
| const pkg = require(path.join(root, 'package.json')); | ||
| const link = | ||
| pkg.homepage || `https://npmjs.com/package/${dependencyName}`; | ||
| depsWithWarnings.push([dependencyName, link]); | ||
| } | ||
| } catch (error) { | ||
| logger.warn( | ||
| inlineString(` | ||
| Package ${chalk.bold( | ||
| dependencyName, | ||
| )} has been ignored because it contains invalid configuration. | ||
|
|
||
| Reason: ${chalk.dim(error.message)} | ||
| `), | ||
| ); | ||
| return acc; | ||
| if (output.legacy && !localDependencyRoot) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because local deps don't have package.json |
||
| const pkg = require(path.join(root, 'package.json')); | ||
| const link = | ||
| pkg.homepage || `https://npmjs.com/package/${dependencyName}`; | ||
| depsWithWarnings.push([dependencyName, link]); | ||
| } | ||
| } catch (error) { | ||
| logger.warn( | ||
| inlineString(` | ||
| Package ${chalk.bold( | ||
| dependencyName, | ||
| )} has been ignored because it contains invalid configuration. | ||
|
|
||
| /** | ||
| * @todo: remove this code once `react-native` is published with | ||
| * `platforms` and `commands` inside `react-native.config.js`. | ||
| */ | ||
| if (dependencyName === 'react-native') { | ||
| if (Object.keys(config.platforms).length === 0) { | ||
| config.platforms = {ios, android}; | ||
| } | ||
| if (config.commands.length === 0) { | ||
| config.commands = [...ios.commands, ...android.commands]; | ||
| } | ||
| Reason: ${chalk.dim(error.message)}`), | ||
| ); | ||
| return acc; | ||
| } | ||
|
|
||
| /** | ||
| * @todo: remove this code once `react-native` is published with | ||
| * `platforms` and `commands` inside `react-native.config.js`. | ||
| */ | ||
| if (dependencyName === 'react-native') { | ||
| if (Object.keys(config.platforms).length === 0) { | ||
| config.platforms = {ios, android}; | ||
| } | ||
| if (config.commands.length === 0) { | ||
| config.commands = [...ios.commands, ...android.commands]; | ||
| } | ||
| } | ||
|
|
||
| const isPlatform = Object.keys(config.platforms).length > 0; | ||
| const isPlatform = Object.keys(config.platforms).length > 0; | ||
|
|
||
| /** | ||
| * Legacy `rnpm` config required `haste` to be defined. With new config, | ||
| * we do it automatically. | ||
| * | ||
| * @todo: Remove this once `rnpm` config is deprecated and all major RN libs are converted. | ||
| */ | ||
| const haste = config.haste || { | ||
| providesModuleNodeModules: isPlatform ? [dependencyName] : [], | ||
| platforms: Object.keys(config.platforms), | ||
| }; | ||
| /** | ||
| * Legacy `rnpm` config required `haste` to be defined. With new config, | ||
| * we do it automatically. | ||
| * | ||
| * @todo: Remove this once `rnpm` config is deprecated and all major RN libs are converted. | ||
| */ | ||
| const haste = config.haste || { | ||
| providesModuleNodeModules: isPlatform ? [dependencyName] : [], | ||
| platforms: Object.keys(config.platforms), | ||
| }; | ||
|
|
||
| return (assign({}, acc, { | ||
| dependencies: assign({}, acc.dependencies, { | ||
| // $FlowExpectedError: Dynamic getters are not supported | ||
| get [dependencyName]() { | ||
| return getDependencyConfig( | ||
| root, | ||
| dependencyName, | ||
| finalConfig, | ||
| config, | ||
| userConfig, | ||
| isPlatform, | ||
| ); | ||
| }, | ||
| }), | ||
| commands: [...acc.commands, ...config.commands], | ||
| platforms: { | ||
| ...acc.platforms, | ||
| ...config.platforms, | ||
| return (assign({}, acc, { | ||
| dependencies: assign({}, acc.dependencies, { | ||
| // $FlowExpectedError: Dynamic getters are not supported | ||
| get [dependencyName]() { | ||
| return getDependencyConfig( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know I am picky, but I actually missed this in the previous PR review. I find extracting
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (especially the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But that's how it is, the finalConfig is not there, unless the whole reduce pass is done. Extracting this helped me to better understand this piece of code and actually type it properly, so I'd leave it even for the type safety sake only. At least in this PR |
||
| root, | ||
| dependencyName, | ||
| finalConfig, | ||
| config, | ||
| userConfig, | ||
| isPlatform, | ||
| ); | ||
| }, | ||
| haste: { | ||
| providesModuleNodeModules: [ | ||
| ...acc.haste.providesModuleNodeModules, | ||
| ...haste.providesModuleNodeModules, | ||
| ], | ||
| platforms: [...acc.haste.platforms, ...haste.platforms], | ||
| }, | ||
| }): ConfigT); | ||
| }, | ||
| initialConfig, | ||
| ); | ||
| }), | ||
| commands: [...acc.commands, ...config.commands], | ||
| platforms: { | ||
| ...acc.platforms, | ||
| ...config.platforms, | ||
| }, | ||
| haste: { | ||
| providesModuleNodeModules: [ | ||
| ...acc.haste.providesModuleNodeModules, | ||
| ...haste.providesModuleNodeModules, | ||
| ], | ||
| platforms: [...acc.haste.platforms, ...haste.platforms], | ||
| }, | ||
| }): ConfigT); | ||
| }, initialConfig); | ||
|
|
||
| if (depsWithWarnings.length) { | ||
| logger.warn( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code has a potential side effect of users defining
react-native-camera.rootand being able to overwrite the root for that library. I don't think this is intentional and can be confusing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also one conceptual thing regarding
userConfigthat I really wanted to avoid. By default, user configuration is deep merged with the configuration we find out. While finding out the proper configuration, we take dependency config into consideration.User provided configuration can overwrite any part of the dependency configuration but that will not affect other properties. For example, you can overwrite the android package import path, but this will not affect other properties.
Taking
rootinto consideration breaks this contract.Also, have we tested for duplicate keys?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we exported a utility function instead and asked users to do:
that would still preserve the merging approach we have settled on so far and actually, give us an opportunity to extract some pieces of this file into reusable functions?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really sure about that.
rootis kinda special, because it lets CLI resolve other values, but you can still override them:resolves to:
That's why I chose this solution. It's "nothing new" and user is still able to override things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I included above in a test so it doesn't go away