Modules required from outside of root directory does not find node_modules
Explanation of the problem
The following is a technical description of a file importing issue for a web and native app being developed. The developer is attempting to import shared code files from outside the root directory of the project. While the developer has configured the project to allow such imports using the “rn-cli.config.js” file, the imported files are unable to find required modules such as React, which are present in the “node_modules” directory located in a parent directory to the root.
The project has a folder structure that includes a “common” directory containing shared code files and separate “web” and “native” directories for the web and native apps respectively. The “rn-cli.config.js” file contains code that defines the project roots by resolving paths to the “common” and “node_modules” directories. However, the imported files are not able to find modules such as React that are located in the “node_modules” directory, which is a sibling directory to the “common” directory.
Troubleshooting with the Lightrun Developer Observability Platform
Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.
- Instantly add logs to, set metrics in, and take snapshots of live applications
- Insights delivered straight to your IDE or CLI
- Works where you do: dev, QA, staging, CI/CD, and production
Start for free today
Problem solution for Modules required from outside of root directory does not find node_modules
To resolve this issue, the developer added a “package.json” file to the “common” directory and installed React there, which allowed the imported files to find the required module. The issue seems to be related to the packager walking the file upwards and not finding the “node_modules” directory in a sibling directory, as it is not located in a parent directory.
The developer has tried clearing the cache but has been unable to find a solution. The project is using the latest version of Expo, which is believed to be using RN 0.43.
Here is the relevant code from the “rn-cli.config.js” file:
const path = require('path');
module.exports = {
getProjectRoots() {
return [
path.resolve(__dirname, '../common'),
path.resolve(__dirname, 'node_modules'),
path.resolve('.')
]
}
};
Other popular problems with Metro
Problem 1: Metro Bundler has encountered an internal error, please check your terminal error output for more details
This error is a common issue faced by developers when using Metro. It usually occurs when Metro is unable to find or load the required dependencies. One of the reasons for this error is an incorrect configuration in the “metro.config.js” file.
Solution:
To fix this error, developers can try clearing the Metro cache by running the following command in the project directory:
rm -rf $TMPDIR/metro-*
Problem: ENOSPC: System limit for number of file watchers reached
This error occurs when a developer has too many files being watched by Metro, and the system limit for the number of file watchers is exceeded. This can happen when developers use a large number of dependencies or when their project directory has many files.
Solution:
To fix this error, developers can increase the number of file watchers available on their system by running the following command:
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
Problem: Error: TransformError: /path/to/file.js: Invalid call at line x: require(…) must be called with a single string literal argument
This error occurs when developers attempt to use a dynamic variable as an argument for the “require” function instead of a static string. This can happen when developers try to import modules conditionally based on some runtime logic.
Solution:
To fix this error, developers should refactor their code to use a static string as the argument for the “require” function. For example, instead of:
const myModule = require(`./${moduleName}`);
A brief introduction to Metro
Metro is a JavaScript bundler used for building React Native applications. It is a popular tool among developers due to its fast and efficient bundling process. Metro uses a modular architecture, allowing developers to extend its functionality by adding custom transformers or middleware. This flexibility makes it easy to integrate with various types of projects and workflows.
Metro also includes several features that optimize the development process, such as hot module replacement, which allows developers to see changes in their code immediately without restarting the application. Metro’s fast bundling process and optimized caching mechanism also reduce build times, making the development process more efficient. Additionally, Metro includes support for various file types, including JavaScript, TypeScript, and CSS. It also supports various platforms, such as Android, iOS, and web. Metro’s flexibility, speed, and wide range of features make it a popular choice for React Native developers.
Most popular use cases for Metro
- Building React Native Applications: Metro is primarily used for building React Native applications. It is optimized for bundling JavaScript code and assets for React Native projects. It uses a modular architecture, allowing developers to extend its functionality by adding custom transformers or middleware. Here is an example of a basic Metro configuration file for a React Native project:
const path = require('path');
const { getDefaultConfig } = require('metro-config');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig(__dirname);
return {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
extraNodeModules: new Proxy(
{},
{
get: (target, name) =>
path.join(__dirname, `node_modules/${name}`),
}
),
},
};
})();
- Optimizing the Development Process: Metro includes several features that optimize the development process. One of the most notable features is Hot Module Replacement (HMR), which allows developers to see changes in their code immediately without restarting the application. Metro also includes optimized caching mechanisms that reduce build times, making the development process more efficient. Additionally, Metro supports various file types and platforms, making it a versatile tool for different types of projects.
- Customizing the Bundling Process: Metro’s modular architecture allows developers to customize the bundling process by adding custom transformers or middleware. For example, a developer can use a custom transformer to preprocess files before they are bundled. Here is an example of a custom transformer that uses Babel to transpile TypeScript code:
const { transformSync } = require('@babel/core');
const babelPluginTransformTypescript = require('@babel/plugin-transform-typescript');
function transform({ src, filename, options }) {
const transformed = transformSync(src, {
filename,
ast: false,
babelrc: false,
configFile: false,
plugins: [babelPluginTransformTypescript],
...options,
});
return transformed.code;
}
module.exports.transform = transform;
It’s Really not that Complicated.
You can actually understand what’s going on inside your live applications.