metro icon indicating copy to clipboard operation
metro copied to clipboard

Require cycle warnings should be opt-in when the source is node_modules

Open brentvatne opened this issue 6 years ago • 103 comments

Do you want to request a feature or report a bug?

Feature

What is the current behavior?

Lots of warnings about require cycles from libraries in node_modules in the react-native ecosystem, coming from the following code: https://github.com/facebook/metro/blob/dc8d85911ff6bf6487b7e3aef6b004df4bdcf17f/packages/metro/src/lib/polyfills/require.js#L158-L174

What is the expected behavior?

Don't warn users about require cycles in libraries in node_modules unless the user opts-in. Warnings about code that you don't own, especially when the warnings are non-essential, create unnecessary noise.

Please provide your exact Metro configuration and mention your Metro, node, yarn/npm version and operating system.

React Native 0.57 default configuration and Metro version (0.47.1). Other information irrelevant.

brentvatne avatar Oct 09 '18 19:10 brentvatne

Any workaround for this issue?

elkinjosetm avatar Oct 24 '18 17:10 elkinjosetm

I get the warn as well. I've tried to ignore this but it didn't help me.

YellowBox.ignoreWarnings([
  'Warning: isMounted(...) is deprecated', // works
  'Module RCTImageLoader', // works
  'Require cycle:', // doesn't work
])

dakiesse avatar Oct 24 '18 23:10 dakiesse

I can't deal with these warnings right now in my app. I mute them via a npm postintall script including this mess:

  const codeToObscure = /console.warn\([\s\S].*"Require cycle: "/;
  const problemFilePath = './node_modules/metro-config/node_modules/metro/src/lib/polyfills/require.js';
  const problemFileContent = fs.readFileSync(problemFilePath,'utf8');
  fs.writeFileSync(problemFilePath,problemFileContent.replace(codeToObscure,'const noConsoleWarn = (""'),'utf8');

brianephraim avatar Nov 05 '18 15:11 brianephraim

Would love to be able to opt-in to this warning. In the meantime, similar to @defualt, I've used patch-package to apply the below patch to ignore the warning only if its an external dependency:

Patch
patch-package
--- a/node_modules/metro/src/lib/polyfills/require.js
+++ b/node_modules/metro/src/lib/polyfills/require.js
@@ -111,21 +111,29 @@ function metroRequire(moduleId) {
     var initializingIndex = initializingModuleIds.indexOf(
       moduleIdReallyIsNumber
     );
+
     if (initializingIndex !== -1) {
       var cycle = initializingModuleIds
         .slice(initializingIndex)
         .map(function(id) {
           return modules[id].verboseName;
         });
-      // We want to show A -> B -> A:
-      cycle.push(cycle[0]);
-      console.warn(
-        "Require cycle: " +
-          cycle.join(" -> ") +
-          "\n\n" +
-          "Require cycles are allowed, but can result in uninitialized values. " +
-          "Consider refactoring to remove the need for a cycle."
-      );
+
+      var isExternalOnly = cycle.every(function(cycleWarning) {
+        return cycleWarning.includes("node_modules")
+      })
+      
+      if (!isExternalOnly) {
+        // We want to show A -> B -> A:
+        cycle.push(cycle[0]);
+        console.warn(
+          "Require cycle: " +
+            cycle.join(" -> ") +
+            "\n\n" +
+            "Require cycles are allowed, but can result in uninitialized values. " +
+            "Consider refactoring to remove the need for a cycle."
+        );
+      }
     }
   }

codybrouwers avatar Nov 07 '18 04:11 codybrouwers

@CodyBrouwers I have tried to use your patch but I received the following error from patch-package

**ERROR** Failed to apply patch for package metro

  This error was caused because Git cannot apply the following patch file:

    patches/metro+0.45.6.patch

  This is usually caused by inconsistent whitespace in the patch file.

toioski avatar Nov 07 '18 09:11 toioski

@toioski Ah yes, I've encountered that issue with patch-package as well. Better to just make the same changes and patch it yourself I think.

codybrouwers avatar Nov 10 '18 19:11 codybrouwers

I'd like to add that require cycle may be intentional inside your app code and is fine in many cases if you know what you are doing and don't start calling cycle methods on initialization . I'd like to be able to disable those warnings even for my own code, is there a way?

slorber avatar Nov 13 '18 10:11 slorber

@slorber In those cases the warning should be displayed. The problem like the OP said is:

Lots of warnings about require cycles from libraries in node_modules in the react-native ecosystem

luissmg avatar Nov 14 '18 09:11 luissmg

Hi! We added this warning since we got many complains from developers at FB that it was hard to debug the errors caused by cyclic dependencies (since a cyclic dependency causes the returned value of a require statement to be undefined without hard throwing).

The YellowBox.ignoreWarnings method should allow to mute these errors, @dakiesse are you adding it at the top of the entry point of your app?

YellowBox.ignoreWarnings([
  'Require cycle:',
]);

rafeca avatar Nov 14 '18 11:11 rafeca

@rafeca I added that in my entry point and it does not work. Like @dakiesse said, the other two warnings don't show up but this one does.

luissmg avatar Nov 14 '18 13:11 luissmg

@rafeca yes, immediately after imports in entry point

dakiesse avatar Nov 15 '18 08:11 dakiesse

Today I updated versions to:

    "react": "16.6.3",
    "react-native": "0.57.5",
    "metro-react-native-babel-preset": "0.49.1",

And I became to get new warning ListView is deprecated and will be removed in a future release. See https://fb.me/nolistview for more information. I've tried to ignore it.

YellowBox.ignoreWarnings([
  'Require cycle:',
  'ListView is deprecated',
])

It doesn't work as well.

dakiesse avatar Nov 15 '18 09:11 dakiesse

This is so annoying, any updates here?

Every time I load the app I get like a million require cycle warnings which I really don't care about and muting them as suggested doesn't work.

Is there any other way of silencing this?

vrgimael avatar Nov 17 '18 17:11 vrgimael

@vrgimael the solution was suggested by @defualt

dakiesse avatar Nov 18 '18 07:11 dakiesse

YellowBox.ignoreWarnings(["Require cycle:"]); worked for me 👍

Here is my index.js:

/** @format */

import { AppRegistry, YellowBox } from "react-native";
import App from "./App";
import { name as appName } from "./app.json";

// ignore specific yellowbox warnings
YellowBox.ignoreWarnings(["Require cycle:", "Remote debugger"]);

AppRegistry.registerComponent(appName, () => App);

abegehr avatar Nov 22 '18 14:11 abegehr

YellowBox.ignoreWarnings unfortunately does not hide them in the console :/

KylePalko avatar Nov 26 '18 20:11 KylePalko

IMO this should be opt-in, not opt out. Many people also don't understand what it is and think they need to fix this warning even if nothing is wrong with the code. There are also third-party libs with the warning that you can do nothing about.

Also to find require cycles, you have to go through hundreds of warnings, doesn't seem very efficient to find an issue. Enabling/disabling all of the warnings don't seem ideal either, it'll probably be better to enable this per folder rather than for whole project.

satya164 avatar Nov 26 '18 20:11 satya164

Okaaaay, so if this warning is annoying the hell out of you, and you can't refactor your app because it's too much work, here's how I "suppressed" those warnings until a proper solution shows up :

  • Add a scripts folder in your project
  • Create a stfu.js file with the following content (edited from @dakiesse) :
const fs = require('fs');

const codeToObscure = /console.warn\([\s\S].*"Require cycle: "/;
const problemFilePath = './node_modules/metro/src/lib/polyfills/require.js';
const problemFileContent = fs.readFileSync(problemFilePath,'utf8');
fs.writeFileSync(problemFilePath,problemFileContent.replace(codeToObscure,'const noConsoleWarn = (""'),'utf8');
  • Add the following line to your package.json in your scripts : "postinstall": "node ./scripts/stfu.js",

When it's done, just rm -rf node_modules && yarn to reinstall everything, and make sure these damn warnings are now gone.

This is really really tricky for us since we have LOTS of require cycles because of how we use redux in our app.

I hope this is helpful.

Exilz avatar Dec 04 '18 17:12 Exilz

When i have a warning like that on my console, even if is not in node_modules, my assets loading be very slow.

As a temporary fix to disable the warning and have assets load quicker, you can comment out line 109 in node_modules/metro/src/lib/polyfills/require.js.

Check it out temporary solution here: https://github.com/expo/expo/issues/2578#issuecomment-435713216

mauriciord avatar Dec 11 '18 12:12 mauriciord

can someone please explain how to patch this? @Exilz answer is not working

cory-baker avatar Dec 31 '18 00:12 cory-baker

Can always mute it by patching console.warn. This is my index.js file:

if (__DEV__) {
  const IGNORED_WARNINGS = [
    'Remote debugger is in a background tab which may cause apps to perform slowly',
    'Require cycle:',
  ]
  const oldConsoleWarn = console.warn

  console.warn = (...args) => {
    if (
      typeof args[0] === 'string' &&
      IGNORED_WARNINGS.some(ignoredWarning =>
        args[0].startsWith(ignoredWarning),
      )
    ) {
      return
    }

    return oldConsoleWarn.apply(console, args)
  }
}

module.exports = require('./src')

Note, if it isn't obvious, that this is a temporary fix until a more ideal solution is released. At which point you should delete this monkey patch!

IMO, YellowBox.ignoreWarnings should result in a similar output than the above monkey patch.

tazsingh avatar Jan 02 '19 16:01 tazsingh

@tazsingh Your solution works great! YellowBox.ignoreWarnings can not mute the console logs. May need to use both ways to mute all.

sunnylqm avatar Jan 03 '19 09:01 sunnylqm

Require cycles are perfectly fine. Well-organized code will undoubtedly have cycles. I don't understand the rationale to warn about them... Is the goal to put all the code in a single file?

alamothe avatar Jan 06 '19 02:01 alamothe

How to mute this in a Typescript project? None of the suggested workarounds are working.

ivnsch avatar Feb 10 '19 11:02 ivnsch

@i-schuetz I created the following patch file (in my case named metro+0.45.6.patch) to apply with patch-package and is working fine for me

patch-package
--- a/node_modules/metro/src/lib/polyfills/require.js
+++ b/node_modules/metro/src/lib/polyfills/require.js
@@ -106,7 +106,7 @@ function metroRequire(moduleId) {
       });
       // We want to show A -> B -> A:
       cycle.push(cycle[0]);
-      console.warn('Require cycle: ' + cycle.join(' -> ') + '\n\n' + 'Require cycles are allowed, but can result in uninitialized values. ' + 'Consider refactoring to remove the need for a cycle.');
+      // console.warn('Require cycle: ' + cycle.join(' -> ') + '\n\n' + 'Require cycles are allowed, but can result in uninitialized values. ' + 'Consider refactoring to remove the need for a cycle.');
     }
   }

toioski avatar Feb 11 '19 10:02 toioski

Today I encountered the following require cycle node_modules/react-native/Libraries/Network/fetch.js -> node_modules/react-native/Libraries/vendor/core/whatwg-fetch.js -> node_modules/react-native/Libraries/Network/fetch.js. I have a single fetch() invocation in my whole codebase.

React Native Environment Info: System: OS: macOS 10.14.3 CPU: (4) x64 Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz Memory: 54.78 MB / 8.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 8.12.0 - /usr/local/bin/node Watchman: 4.9.0 - /usr/local/bin/watchman SDKs: iOS SDK: Platforms: iOS 12.1, macOS 10.14, tvOS 12.1, watchOS 5.1 Android SDK: API Levels: 27, 28 Build Tools: 27.0.3, 28.0.2, 28.0.3 System Images: android-28 | Google APIs Intel x86 Atom IDEs: Android Studio: 3.2 AI-181.5540.7.32.5056338 Xcode: 10.1/10B61 - /usr/bin/xcodebuild npmPackages: react: 16.6.3 => 16.6.3 react-native: 0.57.8 => 0.57.8

Screenshot

tsvetan-ganev avatar Feb 26 '19 13:02 tsvetan-ganev

Okaaaay, so if this warning is annoying the hell out of you, and you can't refactor your app because it's too much work, here's how I "suppressed" those warnings until a proper solution shows up :

  • Add a scripts folder in your project
  • Create a stfu.js file with the following content (edited from @dakiesse) :
const fs = require('fs');

const codeToObscure = /console.warn\([\s\S].*"Require cycle: "/;
const problemFilePath = './node_modules/metro/src/lib/polyfills/require.js';
const problemFileContent = fs.readFileSync(problemFilePath,'utf8');
fs.writeFileSync(problemFilePath,problemFileContent.replace(codeToObscure,'const noConsoleWarn = (""'),'utf8');
  • Add the following line to your package.json in your scripts : "postinstall": "node ./scripts/stfu.js",

When it's done, just rm -rf node_modules && yarn to reinstall everything, and make sure these damn warnings are now gone.

This is really really tricky for us since we have LOTS of require cycles because of how we use redux in our app.

I hope this is helpful.

Use

const codeToObscure = /console.warn\([\s]*`Require cycle[^;]*;/;

and

fs.writeFileSync(problemFilePath, problemFileContent.replace(codeToObscure, ''), { encoding: 'utf8' });

if the Regex mentioned above by @Exilz doesn't work, as in my case (Metro 0.49.2)

alexverbitsky avatar Mar 06 '19 09:03 alexverbitsky

@leshanative

const codeToObscure = /console.warn\([\s]*`Require cycle[^;]*;/;

should be

const codeToObscure = /console.warn\([\s]*'Require cycle[^;]*;/;

(note the ')

JulianKingman avatar Apr 24 '19 17:04 JulianKingman

Honestly, would it be possible to remove all "Require cycle" warnings, put them in the bin, and throw the bin over the side (preferably when anchored over the nearest ocean trench)? I really don't see the need for them at all. Which programming languages can deal with cyclic dependencies? These days, pretty much all of them: from C to Python to JavaScript. Yes, it would be nice not to have cyclic dependencies, but it sounds unavoidable, especially in third-party code. By the sounds of it, "Require cycle" warning are less information and more noise, wasting the time of programmers.

For me, the "Require cycle" warnings occurs with existing Redux code that works on vanilla React (React original? React plain? The non-native React, I mean) and works quite well. There is a little bit of cyclic dependency between actions.js (the Redux actions code), and httpreq.js (Redux thunk code that sends HTTP requests and fires actions on responses), but I never got any warning on that platform. So why is there a warning on React Native?

peterkmurphy avatar Jun 12 '19 03:06 peterkmurphy

This warning is making me do ugly acrobatics to suppress it when it's ostensibly for code quality. Really unfortunate decision to bury an optional warning deep inside a tool without a configuration option.

This might be showing my ignorance when it comes to bundlers, but why do we need this warning at that level instead of a linter like https://www.npmjs.com/package/tslint-no-circular-imports?

coffenbacher avatar Jun 16 '19 15:06 coffenbacher