facebook / metro

🚇 The JavaScript bundler for React Native
https://metrobundler.dev
MIT License
5.25k stars 627 forks source link

Named and default exports have different resolving strategies/behaviors #1093

Open Yooooomi opened 1 year ago

Yooooomi commented 1 year ago

Problem

Hello! Lately I've been working on optimizing the startup time of our application. I learned a lot about how hermes works and how it incrementally resolves imports as we use the values. I followed this snippet to analyze how many modules were loaded at the Home page of our page and realized it was way too high (5300, which is crazy).

After many attempts at understanding why it would resolve flows and components that were not required in the Home page, I found that Hermes resolves default export by default. Plus the fact that it will initialize data and allocate to memory currently unwanted stuff and execute top level code.

Explanation:

// FileB.tsx
import {View} from "react-native";

console.log("This will log before first render");

export default function ComponentB() {
  return <View />;
}

// FileA.tsx
import ComponentB from "./FileB";

export default function ComponentA() {
  return <ComponentB />;
}

// App.tsx
import ComponentA from "./FileA";

// Insert here the snippet provided above
// The snippet will log that FileB is resolved and loaded

export default function App() {
  return <ComponentA />;
}

Whereas

// FileB.tsx
import {View} from "react-native";

console.log("This will NOT log before first render");

export function ComponentB() {
  return <View />;
}

// FileA.tsx
import {ComponentB} from "./FileB";

export function ComponentA() {
  return <ComponentB />;
}

// App.tsx
import {ComponentA} from "./FileA";

// Insert here the snippet provided above
// The snippet will log that FileA is resolved and loaded but **not** FileB

export default function App() {
  return <ComponentA />;
}

has a completely different behavior.

Is this behavior wanted or not? I feel like I should rewrite all the imports to named imports to work this around. But would be happy to know if there is any other way and if not why it's technically not possible.

Thanks for your answers.

github-actions[bot] commented 1 year ago
:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.
github-actions[bot] commented 1 year ago
:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.
tmikov commented 1 year ago

Hi, when building a RN app, import and export are handled by the bundler (Metro), not by Hermes. The bundler combines all source files together into a single file, transforms the JS to implement import/export (among other things), and only then passes the result to Hermes.

I am moving this issue to React Native.

cortinico commented 1 year ago

Passing over to Metro (cc @robhogan @huntie)

bajormar commented 1 year ago

Facing the same issue. Because of this inline requires does not work, and entire project has to be parsed just to run app. After switching to named exports, problem solved itself. But logically there should be no difference