How to Create a React-native-cli Plugin in Typescript

React native generator

Typescript app

command line plugin

🚀🚀 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 :

  • Creating the plugin
  • Testing it on a react-native app
  • Typing it with TypeScript

🔧 Creation of the plugin

Generate a node project

First, let's generate a node project called 'react-native-hello-world' :

$ mkdir react-native-hello-world
$ cd react-native-hello-world
$ npm init

Configure the plugin

🔆 How does it work?

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:

  • parses the package.json file of the react-native project.
  • reads the react-native.config.js of all dependencies in node_modules.

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 react-native set-icon and react-native set-splash 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.

💻 Create the configuration file react-native.config.js

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 react-native test-new-plugin in your terminal

// react-native.config.js

module.exports = {
    commands: [
      {
        name: 'test-new-plugin',
        func :  () => console.log('Hello world')
      },
    ],
  };

Let's test that it works!

🔎 Test on a React-Native project

To execute the react-native plugin, let’s create a new react-native project TestProject:

$ react-native init TestProject

⚠️ 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:

// package.json

...
 "dependencies": {
    "react-native-hello-world" : " 0.0.1 "
  }
...

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 : 

  • Export locally the plugin 'react-native-hello-world' : in the root folder of your plugin, run yarn link.
  • Get the new local package in your project : in the folder TestProject, run yarn link react-native-hello-world.

🎉🎉 Finally, run react-native test-new-plugin --> it prints 'Hello World' 🎉🎉

💪 Type the plugin in TypeScript

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.

Add TypeScript and ts-node

You can install these 2 tools into your plugin by running :

$ npm install —dev ts-node typescript 
or 
$ yarn add -D ts-node typescript 

TypeScript and ts-node will be added into your devDependencies of package.json.

Modify react-native.config.js

In react-native.config.js, we indicate that ts-node will be used to compile the following Typescript:

// 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;

The command interface is moved into a ts file rn-plugin.config.ts.

Configure ts-node

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) :

// tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "noImplicitAny": true,
    "outDir": "dist",
    "declaration": true
  },
  "exclude": ["node_modules", "test", "dist"]
}

Define and type the plugin command

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 :

$ npm install --save-dev @types/react-native-community__cli

It will add a devDependencies into your package.json file.

Here is how the plugin is configured :

// 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'); } }, ], };

Here is the link of the types on Definitely Typed : https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-native-community__cli

🔎 You can find the full code here : https://github.com/agathekieny/react-native-hello-world