Search code examples
reactjstypescriptreact-nativetsctsconfig

Typescript including different files depending on compilation command


I used creat-react-app to initialize some code which I want to share between native and web. In my package.json I have two separate commands for starting for each platform using react-scripts-ts and react-native-scripts-ts:

package.json

...,
"scripts": {
    "tsc": "tsc",
    "clean": "rimraf artifacts",
    "build": "npm run clean && npm run tsc --",
    "start-web": "npm run build && react-scripts-ts start",
    "start-native": "npm run build && react-native-scripts start",
},
...

(a detailed description on how to do this can be found here https://medium.com/@yannickdot/write-once-run-anywhere-with-create-react-native-app-and-react-native-web-ad40db63eed0)

This great and I can use react-native components on both platforms. The problem I have is when I try to use external packages such as react-routing.

I include both react-router-native and react-router-dom in my package.json. I am trying to achieve what is described in this article (https://medium.com/@yannickdot/hi-jared-2650fbb2eda1) but using typescript not JS, giving me:

routing.native.tsx

export {
  NativeRouter as Router, // Rename
  Switch,
  Route,
  Link
} from 'react-router-native'

routing.web.tsx

export {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from 'react-router-dom'

However, contrary as described in the article, when using typescript, it is not automatically recognized which file should be included. I get the simple error:

src/App.tsx:10:26 - error TS2307: Cannot find module './routing'.

10 import Router                        from "./routing";

Which makes sense because when I look at the output of the compiler, no module routing exists:

artifacts
   | App.js
   | routing 
        | routing.native.js
        | routing.web.js

How can I tell the typescript compiler to include all *.native.tsx files when running the start-native command and all *.web.tsx files when running the start-web commmand?

Ideally, this should be possible at compile-time, passing additional parameters into the typescript compiler, which override the tsconfig.json. Example:

tsc --exclude="./**/*.native.tsx"

I know this can be done with a hacked solution, e.g. by writing a script to copy the entire source, deleting all unwanted files, keeping the correct ones, and compiling that copied source folder, but I want to know if there is a more neat way to do this.

Thanks in advance!


Solution

  • A possible solution without using external tools :

    1. Create a function to check the platform running

    see this question on Stackoverflow

    export default function getPlatform(): string {
        if (typeof document != 'undefined') {
            // I'm on the web!
            return 'web';
        }
        else if (typeof navigator != 'undefined' && navigator.product == 'ReactNative') {
            // I'm in react-native
            return 'native';
        }
        else {
            // I'm in node js
            return 'node';
        }    
    }
    

    2. Create routing/index.ts

    import getPlatfrom from '../getPlatform';
    
    const platform = getPlatfrom();
    const routing = platform === 'web' ? require('./routing.web') : require('./routing.native');
    
    export const {Router, Switch, Route, Link} = routing;
    

    3. Use the routing

    import { Route } from './routing/index';
    

    You may add an interface IRouting and some type casting in routing/index, so that you don't lose type safety and autocompletion ...