So, my project is monorepo. There are folders for web application, mobile application, etc. And there is also a common
folder, where gathered all the reusable components and utils.
<repo-name>
├── app // mobile app
│ ├── node_modules
│ ├── src
│ │ ├── views
│ │ │ ├── Homepage
│ │ │ │ └── index.tsx
│ │ └── App.tsx
│ ├── babel.config.js
│ ├── tsconfig.json
│ └── ...
├── common
│ ├── utils
│ │ └── format.ts
├── web
│ ├── app
│ │ ├── tsconfig.json
│ │ └── ...
│ ├── landing
│ │ ├── tsconfig.json
│ │ └── ...
App.tsx
has schematically the next content:
import React from "react"
import Homepage from "views/Homepage"
import { someFunction } from "@common/utils/format"
export default
class App
extends React.Component<any, any> {
render() {
return <>
<h1>{someFunction("kappa")}</h1>
<Homepage />
<>
}
}
Mobile app's tsconfig.json
looks like this:
{
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"experimentalDecorators": true,
"useDefineForClassFields": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"paths": {
"@common/*": [
"../common/*",
],
"*": [
"./src/*",
"node_modules",
],
}
},
}
and the babel.config.js
is this:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
"@babel/plugin-transform-flow-strip-types",
{ legacy: true },
],
[
"@babel/plugin-proposal-decorators",
{ legacy: true },
],
[
"@babel/plugin-proposal-class-properties",
{ "loose": false }
],
[
'module-resolver',
{
root: ['./src'],
extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
alias: {
"@common": "../common",
}
}
]
]
};
Basically, what's going on.
I've created alias for common
folder content to be imported as @common/...
Also, all content from src
folder can be imported without relative paths.
This aliases are configured in both tsconfig.json
and babel.config.json
. I'm using VSCode, and autocomplete for @common
alias works flawlessly.
But in runtime (inside Metro CLI) I'm getting this error:
Error: Unable to resolve module ../../common/utils/format from /home/<username>/projects/<project>/<repo-name>/app/src/App.tsx:
None of these files exist:
* ../common/utils/format(.native|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
* ../common/utils/format/index(.native|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
As you can see, path for @common
is resolved perfectly, but for some reason babel does not see format.ts
file.
What I'm doing wrong? Is there any caveats, any workarounds? Any help appreciated. Spent abount 4 hours on this problem and found nothing useful. Thanks in advance
Okay, so, basically, this is not a problem of the Babel. Turns out that Metro does not know a thing about folders outside the project.
Well, all you need is to add watchFolders
property to metro.config.js
:
/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/
const path = require("path")
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
// here we should set folders outside of the project folder
// to be able to import modules from 'em
watchFolders: [
path.resolve(__dirname + "/../common")
]
};
Two days searching and the solution is, as always, just 4 rows
The beautiful world of javascript bundlers.
That's all folks!
¯\_(ツ)_/¯