What's `re-start`?

Edit on GitHub

React Native is a framework derived from React that allow to program mobile native apps using Javascript. It's only focused on Android and iOS, but its popularity has lead to other implementations of its API for other platforms like Windows, macOS or also web. Thing is, although they share the same APIs and source code is (almost) compatible between them, they are not integrated so it would surface difficulties to add a new platform to an existing code, or forcing to have several different projects that could lead to duplicated efforts or diverge the features of them.

re-start is a set of pre-configured templates to create React Native projects that can run both on Android, iOS, web, Windows and Electron from a single source code base, unifying the ports for the different React Native platforms currently available:

Advantages and disadvantages

The main advantages of this perspective is to allow to have a wider difussion of your project with almost the same effort of developing for just one single platform, while at the same time you can just use standard React Native idioms instead of third-party frameworks. By having a single codebase you can also be sure that all platforms have the same features and behaviour, decreasing the incompatibilities and costs that could arise by having independent projects for each platform (with potentially independent teams, too).

This has some disadvantages, being the obvious one the need to fix the minor diferences between the different platforms and their support of the React Native APIs or particular platform features. The main problem though, is the fact that most React Native components that include native code are targeted only for Android or iOS, not taking in account other platforms like web or Windows. This requires to search for alternatives that include support for the additional platforms or add them ourselves, or if possible, find alternatives written in vanilla Javascript and using the React Native standard components, that have the advantage of being more secure by running inside the Javascript virtual machine instead of executing platform native code. In this aspect, the safest bet to solve these problems is to define the web platform as the "canonical" one and develop your project targeting it first. This platform don't also makes the development and debug cycle easier and faster (you can use your web browser to debug your app instead of an Android or iOS devices or an emulator), but also force you to find this "pure Javascript" components that will make you sure your project work on all the platforms.

Update process

re-start was created by Amogh Banta, but in last weeks project development has slowed down and needed some maintenance, so I decided to update and clean-up it. The main areas I have been working on were

  • removal of duplicated and useless files
  • unify code entrypoints
  • fix usage of placeholders
  • ensure projects work unmodified once they are generated
  • and improve support of Windows platform

removal of duplicated and useless files

The diferent templates had the same or similar files duplicated between them, that make maintenance more dificult. react-native-cli don't support to use inherited templates, nor define a template as dependency of another (a hack to define a "parent" template) due to usage of the dependencies.json file, nor execution of scripts to fix the inheritance by hand. Only solutions that have been found are to install several templates by hand one on top of previous ones, generate and publish them with copied files, or use a custom CLI tool (long term solution planned by Amogh Banta).

The solution that I've implemented is the stacked templates. To do so, I removed the duplicated files, so the devs install first the re-base template and later apply the other ones. After that, devs just only need to exec the finishInstall.js script at the root of the generated project to do the final tune-ups and clean the autogenerated useless files like App.windows.js that could lead to confussion.

Installing a template using git:// fails due to how npm works with git repositories and multirepos (first time in my life I've desired it support them...). Using file:// fails when pointing to a directory since files are not copied to node_modules dir but instead used directly, so react-native cli fails due to not being able to solve the paths. This also happen when using a npm pack tarfile or a local web server pointing to the generated tarfile or the project package.json file. It seems to be due to react-native cli doesn't clean-up the template name when installing from other place than a npm registry, like a tarfile or a directory or a scoped package.

Trying to publish the template and install it as a scoped package doesn't work because react-native cli just append the package name to the react-native-template- string, fetching it as react-native-template-@piranna/react-native-template-re-base instead of @piranna/react-native-template-re-base, making it to wrongly think that's a Github repository due to the slash and not finding the @ at the beggining. I could finally install the re-base template by publishing it on npm as a new package, named react-native-template-re-base_piranna, helping to polute the npm registry thanks to this. I've open a pull-request to propose a fix for this.

unify code entrypoints

Instead of having platform specific ones, all the project entrypoints have been unified except the ones for Electron and web platform, that need their special loading process. react-native init creates an App.js file, so it's not needed to add it and can be used as platforms common example entrypoint. Electron requires to work that the main field of package.json file points to its entrypoint, so I added some checkings of the platform and in case the file is not used as Electron entrypoint, return instead the content of App.js. On the other hand, react-scripts start uses the file at src/index.js as default entrypoint for web platform, and trying to use the App.js file in the web platform fails because react-scripts can't use files outside src folder: https://gist.github.com/d62cbe56a14f809a127da2c4a20f1943

As proposed by the error message, solution has been to just move App.js to src/App.js as part of the tasks done by the finishInstall.js script, and change the references of the other platforms entrypoints to use it, making this file the app common entrypoint for all the platforms, and the one devs should only worry about.

fix usage of placeholders

re-start templates had some hardcoded values that otherwise could be replaced by the final project ones. After looking for them, I replaced that values for the correct React Native templates placeholders (HelloWorld strings everywhere) so now the correct project names will appear instead of references to re-start project.

ensure projects work unmodified once they are generated

The main leit-motiv of re-start is to create projects that can run on several platforms, but this was not fully true for the generated projects, providing some basic config and needing some tune-ups to make this true. I've paid special attention to this point so generated projects can run in all the re-start platform unmodified, allowing this to be a reference milestone for devs and also works as basis to implement some tests and allow auto-upgrade of the template dependencies. In this aspect, I've also fixed Electron platform to don't need to have web server running to serve the app files but instead access them from the filesystem, and also added some npm run scripts so all the build steps can be done by executing npm run <platform> and also it's available npm run <platform>:release to build release outputs (although Electron needs to have wine installed to set the Windows executable icon), or you can run npm run release to build all the releases at once, so there's no need to learn any third party tool.

improve support of Windows platform

As part of checking all platforms are ready to run, Windows platform showed to need to extra love. This is due to not being one of the core React Native platforms like Android and iOS nor have its own bundler like web platform (that uses webpack under the hood), so it's not just a matter of adding the react-native-windows package as happens with web platform and its react-native-web package, but instead Windows platform needs to be registered first in React Native as one of the available platforms.

To do so, what's needed to be added instead is the rnpm-plugin-windows package as a devDependency, that will enable the Windows platform support in React Native for that project. After that, exec react-native windows in the project, so the Windows runtime files gets generated and the react-native-windows dependency gets installed. All these steps are already done automatically by re-start when you create a new project, so it's ready to run on Windows platform just by executing npm run windows. This command has the problem that react-native-windows includes between its dependencies files the nuget.exe file but without execution permissions. Enabling them by hand and run it on Linux crashes with some errors that seems related to the lack of some .NET/Mono libraries, so I needed to exec the npm run windows command to build the Windows binary from a Windows virtual machine.

To build React Native apps for the Windows platform, you'll need to have installed in your Windows system not only VisualStudio 2017 tools but also the Windows 10 SDK Build 14393. Windows binaries need to be signed, so after doing that, if you try to build it you'll probably get an error about the included certificate has expired. To fix that issue, open the project solution file stored at windows/<projectName>.sln with VisualStudio to update the signing certificate. After creating the new certificate, when you'll try to build the Windows binary for the first time it will ask you to install the generated certificate in your system, so just accept it and let the build process to end.

Future work

The most inmediate work is regarding development of unit tests and integration with Greekeeper to keep the dependencies upgraded and be sure that the templates are working after that. Since currently it's not possible to use local filesystem to serve the templates files (both directly or as a tarfile), tests for re-start could make use of nock to intercept the HTTP requests for the packages install or start a local npm proxy.

Another future task is to check what libraries are needed to make nuget.exe to work on Linux, so generation of Windows binaries would not need to be done in a Windows host, and to add support for react-native-macos to be able to generate native OSX binaries. And from a personal joice, create a new React Native port to render UI components in the terminal console. Why not? :-)

Written on April 15, 2020

Comment on Twitter

You can leave a comment by replying this tweet.