Disclaimer: our plugin React Native Make has been archived on Oct 7, 2021. It is now read-only, but you can read our Tech Radar New Edition to discover the RN technologies we recommand.
🚀🚀 At BAM, we are creating Make, an in-house react-native app generator to automate the launch of our projects.
In this context, we wanted to make it possible to set an icon and a splash screen on a react-native application in one command line by creating a react-native-cli plugin.
This article explains how we managed to create a new react-native-cli plugin using TypeScript in 3 steps :
First, let's generate a node project called 'react-native-hello-world' :
++pre>++code>$ mkdir react-native-hello-world
$ cd react-native-hello-world
$ npm init
++/code>++/pre>
To configure our plugin, we use the new configuration of plugins of react-native-cli.
To be interpreted as a plugin by the CLI, each package needs to have a react-native.config.js file at the root folder.
At the execution of the command line, react-native-cli:
At the end, an array of commands for all plugins is passed to the CLI.
User can access it by typing react-native <command> in terminal.
Here is an example of a react-native plugin: @bam.tech/react-native-make.
After installing the package, from the command line with ++code>react-native set-icon++/code> and ++code>react-native set-splash++/code> user can set icon and splash screen for iOS and Android.
⚠️ The configuration of react-native-cli plugin has been migrated recently from rnpm configuration into the configuration explained above.
🔎 Check https://github.com/react-native-community/cli/blob/master/docs/plugins.md for more details about plugin configuration.
Now, let's see how to configure the plugin.
In react-native.config.js, we need to provide React Native CLI with a command interface that contains the name of the command, its action and options.
Here is an example of a plugin which exports the command 'test-new-plugin' that will be accessible by running ++code>react-native test-new-plugin++/code> in your terminal
++pre>++code>// react-native.config.js
module.exports = {
commands: [
{
name: 'test-new-plugin',
func : () => console.log('Hello world')
},
],
};
++/code>++/pre>
Let's test that it works!
To execute the react-native plugin, let's create a new react-native project TestProject:
++pre>++code>$ react-native init TestProject
++/code>++/pre>
⚠️ Make sure that the version of react-native is higher than 0.60. ⚠️
In the package.json of this new project, add a dependency to the plugin we just created.
Remember, it will be parsed by the CLI at the execution of the command which will make the command available in your terminal.
To do so, in package.json, add:
++pre>++code>// package.json
...
"dependencies": {
"react-native-hello-world" : " 0.0.1 "
}
...
++/code>++/pre>
The value of the version does not matter.
Since our new package is not public, we are going to link it locally with yarn link :++code> ++/code>
🎉🎉 Finally, run react-native test-new-plugin --> it prints 'Hello World' 🎉🎉
What was crucial for us was to type our plugin.
To do so, we chose to use TypeScript (and not flow which is the typing tools used in react-native-cli) and ts-node to compile it at runtime.
To type the command in TypeScript, we converted the flow types of react-native-cli into TypeScript.
The types used have been published on Definitely Typed. I will explain below how to use it.
Here is how we managed to type the plugin in a few steps.
You can install these 2 tools into your plugin by running :
++pre>++code>$ npm install -dev ts-node typescript
or
$ yarn add -D ts-node typescript
++/code>++/pre>
TypeScript and ts-node will be added into your devDependencies of package.json.
In react-native.config.js, we indicate that ts-node will be used to compile the following Typescript:
++pre>++code>// react-native.config.js
const { resolve } = require('path');
require('ts-node').register({ project: resolve(__dirname, `tsconfig.json`) });
const { rnPluginConfig } = require('./rn-plugin.config');
module.exports = rnPluginConfig;
++/code>++/pre>
The command interface is moved into a ts file rn-plugin.config.ts.
The configuration options of TypeScript Node are defined in tsconfig.json. This file is loaded automatically by ts-node.
Here is how we configured ts-node (I won't detail this configuration, you will find info here) :
++pre>++code>// tsconfig.json
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"resolveJsonModule": true,
"noImplicitAny": true,
"outDir": "dist",
"declaration": true
},
"exclude": ["node_modules", "test", "dist"]
}
++/code>++/pre>
The command interface is defined as previously, but is here typed.
You can either define types in rn-plugin.types.ts or use types published on DefinitelyTyped.
To install the DefinitelyTyped types, run :
++pre>++code>$ npm install --save-dev @types/react-native-community__cli
++/code>++/pre>
It will add a devDependencies into your package.json file.
Here is how the plugin is configured :
++pre>++code>// rn-plugin.config.ts
import { UserDependencyConfig } from '@react-native-community/cli'
// OR use types defined in rn-plugin.types
// import { UserDependencyConfig } from './rn-plugin.types';
export const rnPluginConfig: UserDependencyConfig = {
commands: [
{
name: 'test-new-plugin',
func : async () => { console.log('Hello world'); }
},
],
};
++/code>++/pre>
🔍 You can find the full code here : https://github.com/agathekieny/react-native-hello-world
Read our Tech Radar to discover the technologies we recommand!