Repository: next.js
Issue: Wrapped styled-components don't update on Fast Refresh of imported modules
Labels: bug,Webpack

# Bug report

## Describe the bug

When a styled component is created by wrapping another styled component, as in:
```
const Wrapper = styled(AnotherStyledComponent)``;
```
updates to the wrapped component aren't properly applied during Fast Refresh, when `AnotherStyledComponent` is imported from a separate module, and that module exports only React components. More info (and theories) at the end of this issue.

## To Reproduce

Minimal reproduction at: https://gitlab.com/andrew.gies.axiom/next-sc-bug
The reproduction repo is based on the [official example for using styled-components](https://github.com/vercel/next.js/tree/canary/examples/with-styled-components).

Can be run using `yarn dev` or `npm run dev` and opening `localhost:9000`. Open `lib/components.js` and change the background color of the `StyledThing` component - notice that `components.js` is reloaded and re-evaluated by checking the browser console, yet the new styles aren't applied to the page.

Uncommenting the `export const RandomNumber = 5` line will cause the Fast Refresh/HMR behavior to work correctly upon changes to `StyledThing`. 

## Expected behavior

When the styles are changed in code, they are updated in the browser after a Fast Refresh/HMR without needing to reload the page.

## System information

- OS: macOS Mojave 10.14.6
- Browser: Chrome 84.0.4147.125 
- Version of Next.js: 9.5.2
- Version of Node.js: 12.18.2

## Additional context

This bug was introduced in next.js `9.3.7-canary.19` (not reproducible in `9.3.7-canary.18`), which
leads me to believe that it is an issue with Fast Refresh. (Fast Refresh was enabled by default in canary 19). It is reproducible on the current latest version of next as well (9.5.2).

Styled components can "wrap" other styled components to create extensions of their styles. This doesn't actually create
a component tree that contains a wrapper component and a wrapped component; instead, styled-components creates a new
component that duplicates the old component, but with the new styles added. When one of the "wrapped" components changes, the changes are not properly updated if only the "wrapper" is actually being used in component tree.

**The below refers specifically to the example in my minimal reproduction repo, above.**

For example, running in dev mode and changing the background of `StyledThing` in `components.js` will not properly update
on the webpage. In the browser console, we can see that `components.js` is being reloaded and re-evaluated, but 
`index.jsx` is never reloaded, and so the updated component is never re-wrapped into `Wrapper` in `index.jsx` and inserted
into the React tree. If anything that _isn't_ a React component is exported by `components.js`, then this issue no longer
exists. (However, multiple react components can be exported with the same issue).

My guess is that roughly the following logic is being applied to cause the bug:
 - Fast Refresh is noticing that `components.js` needs to be reloaded, grabs the new file and re-evaluates it.
 - It sees that the only things exported by this file are react components, so it'll attempt to update only parts of
 the existing React tree that are using these components.
 - Since `StyledThing` doesn't appear anywhere in the React tree, nothing is updated and `index.jsx` is not re-run, so
 the changes are never applied.

However, when something else (like a number) is exported from `components.js`, the following happens:
 - Fast Refresh is noticing that `components.js` needs to be reloaded, grabs the new file and re-evaluates it.
 - At least one non-React-component thing is exported, which means it may have effects on files that import it beyond
 just changes to the React tree. 
 - To play it safe, Fast Refresh falls back to the more "traditional" HMR behavior of re-evaluating all modules that 
 import `components.js`, which includes `index.jsx`.

The mistake is in the assumption that the only effect a React component can have on a program is if it is inserted into
the component tree, and that the component can be entirely ignored if not used in that way. While a mostly reasonable assumption, this breaks in situations like styled-components wrappers where components are used indirectly, having an effect on the program execution without ever being inserted into the tree.