Skip to content
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

[RFC 0702]: Autolinking local modules #702

Conversation

szymonrybczak
Copy link
Contributor

@szymonrybczak szymonrybczak commented Aug 21, 2023

This RFC proposes a new feature to React Native CLI where we can create a folder containing local native modules and views and have them linked automatically. Proposed solution will simplify the space of creating local native modules and views and using them later in the app.

Rendered RFC can be found here.

@szymonrybczak szymonrybczak changed the title [RFC 700: Autolinking local modules [RFC 700]: Autolinking local modules Aug 21, 2023
@szymonrybczak szymonrybczak marked this pull request as draft August 21, 2023 13:22
@szymonrybczak szymonrybczak changed the title [RFC 700]: Autolinking local modules [RFC 702]: Autolinking local modules Aug 21, 2023
@szymonrybczak szymonrybczak marked this pull request as ready for review August 21, 2023 13:34
@szymonrybczak szymonrybczak changed the title [RFC 702]: Autolinking local modules [RFC 0702]: Autolinking local modules Aug 21, 2023
Copy link
Member

@cortinico cortinico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really appreciate your time for drafting this one @szymonrybczak 👍 I've left some comments for you

## Detailed design

To automate the process of creating native modules or views we need to implement a few things.
First we need to introduce a new feature to the `create-react-native-library` package to create local modules. `create-react-native-library` will detect if the command has been run under React Native project if yes, it will create a local module under `local_modules` directory. CRNL will contain templates for creating native modules and views, also templates for turbo modules and fabric views.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: What's the difference from this approach from just using create-react-native-library inside the packages/ folder and yarn add-ing it?

Wouldn't it make it easier to open-source actually, as you're creating a proper NPM package?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other word: do we need this? What problem is this solving that CRNL isn't solving already?

Copy link
Member

@satya164 satya164 Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cortinico right now, the code generated by create-react-native-library includes an example folder which isn't necessary for local modules, additional dependencies in the package.json, build scripts for publishing etc. that need to be maintained separately. the idea with local_modules is that we remove the additional unnecessary stuff to give a smaller amount of stuff to maintain. It's harder to open source than a regular lib, but easier to mainatin.

yarn adding an repo like this also has other issues. it doesn't behave exactly like installing a package from npm, it could result in duplicate dependencies etc. due to the additional node_modules which could cause issues.

The other issue is that every time there's a change in the package, user will need to yarn add, which is not good DX.

Yarn also caches packages by their version, so for every change, users will need to update the version number as well or get the cached package, which is another DX issue. yarnpkg/yarn#2165

## Alternatives

- [react-native-coco-loco](https://github.com/jamonholmgren/react-native-colo-loco), it's a great solution - but it integrates with native code, proposed solution will autolink local modules and won't touch native code at all - which makes upgrades simpler and faster.
- [Expo Local Modules](https://docs.expo.dev/modules/get-started/#creating-the-local-expo-module) Expo has a similar autolinking directive for modules that are using Expo Modules API.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you mentioned, this approach looks inspired from Local Expo Modules.

Can we get someone from Expo to chime in here and help design this solution? (example: the fact that the folder on Expo is called modules and on your proposal is called local_modules is an unnecessary misalignment, to name one thing).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to clarify that I had no idea that about expo modules before we came up with the proposal. But yeah, we could align the naming if they behave similarly, e.g. expo modules would work with auto-linking in the CLI.

Comment on lines +19 to +23
1. The new modules become part of the `android` and `ios` source code—which means upgrading the project to a newer React Native version is harder as it also needs to consider those changes,
2. The module boilerplate is written manually, that's the place where users can make mistakes and it takes a lot of time to write one,
3. The code for registering the modules is additional boilerplate which is not required for third party packages (because they're automatically linked),
4. The workflow for creating new architecture modules without creating packages is not streamlined,
5. It's challenging to open-source the local module, as it requires creating a dedicated library setup that adds extra friction to the process.
Copy link

@tido64 tido64 Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I may be missing something because I'm struggling to understand the scenarios this is supposed to address. I have some questions for each of your numbered points:

  1. How does having local native modules work around the fact that you need to deal with React Native upgrades? If your modules are written as Turbo/Native Modules, you still need to consider changes in React Native?
    1. If the generated modules don't have a package.json, what's the difference between this vs. putting the files directly in your project?
  2. I can see the benefit of having the modules automatically generated, but isn't this already resolved by TurboModule generation?
  3. On iOS/macOS, modules are statically linked when you define them. There is no registration boilerplate. On Android, we do need to register them manually. Is this what you're trying to solve?
  4. Is this true? Doesn't codegen address this? If codegen is missing anything, I think it makes more sense to address those gaps instead. @cortinico?
  5. I'm struggling to see how this is better. See comments on point 1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't codegen address this? If codegen is missing anything, I think it makes more sense to address those gaps instead. @cortinico?

Practically codegen can consume local modules already. You'll have to define at the app level where they're placed and they will be consumed, so I don't see any significant gaps.

I would refrain from extending codegen to search also into local_modules as that will complicate the infra, and I would just re-use the infra that we already have (i.e. let the module pass through node_modules or via the app project).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would refrain from extending codegen to search also into local_modules as that will complicate the infra, and I would just re-use the infra that we already have (i.e. let the module pass through node_modules or via the app project).

Agreed. If codegen is already addressing these needs, I am not sure why having local_modules is beneficial.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does having local native modules work around the fact that you need to deal with React Native upgrades? If your modules are written as Turbo/Native Modules, you still need to consider changes in React Native?

It's not about upgrading the modules, it's about upgrading the app. If your custom native code is outside of android/ios folders, you can just recreate android/ios folders of your app as you don't need to account for other changes related to your custom native code and carefully review the diff.

I can see the benefit of having the modules automatically generated, but isn't this already resolved by TurboModule generation

Not sure what you mean here. Do you mean codegen? The point is about the initial boilerplate that we write manually before we start working on the module.

Is this true? Doesn't codegen address this? If codegen is missing anything, I think it makes more sense to address those gaps instead.

It's more about polishing the workflow. In this case, having same setup as a library means we can have the same workflow and any improvements would apply in both cases.
reactwg/react-native-new-architecture#142 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The main point that local modules are solving is when it comes to upgrades we have less custom code to take into account, because the most common scenario is that users put modules code under ios/ or android/ directories.
  2. What I described in this point is not role of codegen. What we're trying to achieve with CRNL is that it will contain templates for all kinds of local modules, so we'll automate process of creating modules from scratch. So user will be able to create module with just one command, not as right now there's need to create all files manually (that's where people make mistakes).
  3. Yes, exactly. With dependencies logic, that we'll reuse for autolinking we don't need to integrate with code under android directory at all.
  4. So right now this is doable, but it isn't documented anywhere. (Usage of Turbo Native Modules within existing iOS and Android apps reactwg/react-native-new-architecture#142 (comment)) To do so you need specify path to local modules in react-native.config.js under dependencies field. And codegen will take into account these dependencies, because of this logic. What we would like to do is to CLI look under local_modules directory and automatically under the hood link modules as dependencies. But the only thing that we need to implement is to codegen look also under local_modules directory, because if not we loose the whole point of autolinking modules (because then we would need to manually specify them under dependencies field in react-native.config.js). I think it wouldn't complicate logic behind the codegen. I don't quite understand how would it work with "let the module pass through node_modules or via the app project" 🤔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for answering my questions folks. ❤️

You're talking about "the app", but it's not to clear to me whether you mean the user's app or the library's example app. If you mean the latter and all of this is to remove the need to keep an example app up-to-date, why is local modules better than just putting your modules directly in the project? I think you can still streamline this (e.g. with code generation) without introducing the concept of local modules.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it's not to clear to me whether you mean the user's app or the library's example app

the local modules are for using in the user's app. there won't be a separate example app here as the user would be using the library in their app. the modifications to CRNL would consist of removing the example app and any other library specific stuff.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

… why is local modules better than just putting your modules directly in the project? I think you can still streamline this (e.g. with code generation) without introducing the concept of local modules.

What about this? I think if CRNL can just output those files directly into your project, you've already solved a pain point. I don't see why the concept of local modules is necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Together with @satya164 we thought that it would be great to create related sections for this in RFC, to make this sound clear why creating local modules inside app is not a great idea. This section should answer your question.

satya164 added a commit to callstack/react-native-builder-bob that referenced this pull request Sep 29, 2023
### Summary

Currently, when we have local native modules and views, they become part of the `android` & `ios` source code, this makes upgrading the project to a newer React Native version harder as we also need to consider those changes.

This change lets us create a local library which is not part of the android & ios folders, but a separate directory in the repo, making it self contained. It also adds a new `--no-example` flag to generate the project without example.

More details in this RFC: react-native-community/discussions-and-proposals#702

### How it works

- If we detect a `package.json` in the current folder, we ask user whether they want to create a local library
- If the project is a react-native project, we add entries to `package.json` based on the package manager to link the native library (which creates a symlink in `node_modules`)
- If the project is not a react-native project, we don't modify package.json, but ask user to link based on the project setup - this is relevant for monorepo scenario

### Test plan

- Create a new project with `npx react-native init`
- Run `create-react-native-library` in the project to create a local library
- Import the library in `App.tsx` and use the exported method to verify functionality
- Install dependencies and pods, then run the app on Android & iOS to verify that it works

https://github.com/callstack/react-native-builder-bob/assets/1174278/d1da73f7-8a57-4911-9200-07c937d3b940
@satya164
Copy link
Member

satya164 commented Oct 2, 2023

Support for creating local library is now available in latest version of create-react-native-library

https://x.com/satya164/status/1708769857309720652

@szymonrybczak
Copy link
Contributor Author

As @satya164 mentioned create-react-native-library supports creating local modules right now - so to add local module user need to run npx create-react-native-library@latest, and it will:

  • detect if the command was run inside React Native project,
  • ask for name, directory and other important informations,
  • add "my_module": "link:./directory" line to package.json which will handle linking so no additional code in Core and CLI is needed.

After running command, user will need to rebuild app and local module is ready to use. To make it possible there's no need for changes in Core or CLI. So I think we can close this PR.

💡 One thing that I'm thinking of is to mention this technique in docs, so when it comes to creating modules users will now that CRNL has templates for that and with just one command they can create the module. cc. @kelset @cortinico

@cortinico
Copy link
Member

  • add "my_module": "link:./directory" line to package.json which will handle linking so no additional code in Core and CLI is needed.

That's amazing news 👍
Yup we can close this one, and update the website to mention that CRNL is the recommended alternative to create local modules. @szymonrybczak @satya164 are you up for sending a PR against the website?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants