Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@apollo/client v3 broken in Rollup & Snowpack #6352

Closed
FredKSchott opened this issue May 28, 2020 · 16 comments · Fixed by #6361
Closed

@apollo/client v3 broken in Rollup & Snowpack #6352

FredKSchott opened this issue May 28, 2020 · 16 comments · Fixed by #6361
Assignees
Milestone

Comments

@FredKSchott
Copy link

Tracked it back to this item:

https://github.com/apollographql/apollo-client/blob/master/src/react/react.ts#L8

The require function doesn't exist inside an ES Module (any file with import or export syntax), which is causing the package to fail in some bundlers.

Happy to help brainstorm ideas on how to solve if you'd like, but would strongly recommend against shipping 3.0 with broken support in any major bundlers.

@FredKSchott
Copy link
Author

related: #6035

@hwillson
Copy link
Member

@FredKSchott can you post an example of the failures you're seeing? I haven't tried Snowpack, but AC3 definitely works with Rollup. Thanks!

@benjamn
Copy link
Member

benjamn commented May 29, 2020

Good catch @FredKSchott.

It's disappointing that the only way to defer an ESM import is to use dynamic import(), which introduces asynchrony into the codebase, and is implemented differently by just about every major JS bundler/runtime.

Please do give us your thoughts on how we might avoid importing React when it isn't needed!

@FredKSchott
Copy link
Author

FredKSchott commented May 29, 2020

Yup, 100% agreed and wish this was better supported in ESM. But even Webpack is limited by how much it can statically analyze. Bundlers can see that @apollo/client is importing & requiring React, but they can't tell at build-time wether that runtime function that does it will or won't ever be run, so a bundler will normally just bundle "react" into your app just to be safe.

Based on the above, I'm 99% sure that Webpack is bundling "react" into every application that uses @apollo/client v3 regardless of whether the that code path ever gets hit. I don't think it's actually buying you that much.

I'd recommend:

  1. Decide that @apollo/client is for React, and remove the layer of indirection / just import react directly. Webpack is already including React anyway, so this isn't too different from the way it is behaving today, but you'd be able to support Rollup, Snowpack, etc, etc.
  2. Split out anything React-specific from @apollo/client into a new @apollo/react or @apollo/client-react. I'd actually recommend this regardless of this issue, since it would help you scale to @apollo/client-{FRAMEWORK} without much thrash, but that's just my outsiders opinion.
  3. Move anything React-specific into a @apollo/client/react sub-path within the package, and have users import from there directly. Everyone who npm installs the package will still get react as a dependency, but if the user never imports @apollo/client/react in their project, bundlers should be smart enough to handle that.

@hwillson
Copy link
Member

Thanks @FredKSchott, we'll look into this further. Just a small note though - AC3's current lazy react require approach does work with Rollup. You can see it in action here.

@ashubham
Copy link

@hwillson FWIW, the example you are citing is no-react, where the lazy require is not even needed.

I did a workaround though to make it work with Rollup/React:

I created a rollup plugin which replaces the lazy require with a static import.

export function patchApolloReactRequire() {
    return {
        name: 'patch-apollo-react-require',
        transform(code, id) {
            if (id.endsWith('@apollo/client/react/react.js')) {
                return `import React from 'react';export function requireReactLazily(){return React}`;
            }
        }
    }
}

@FredKSchott
Copy link
Author

Yup, that's correct, the lazy require isn't called in that example. ApolloProvider is an example of something that calls the broken lazy require

@abdonrd
Copy link
Contributor

abdonrd commented May 29, 2020

  1. Split out anything React-specific from @apollo/client into a new @apollo/react or @apollo/client-react. I'd actually recommend this regardless of this issue, since it would help you scale to @apollo/client-{FRAMEWORK} without much thrash, but that's just my outsiders opinion.

I really like the second option from @FredKSchott!

@abdonrd
Copy link
Contributor

abdonrd commented May 29, 2020

Also about ES Modules related, @apollo/client still uses three dependencies exported as CommonJS:

I have noticed this trying to make @apollo/client work directly in the browser, without using any build/bundled process during development.

@ashubham
Copy link

ashubham commented May 29, 2020

There is a 4th way too, which can achieve conditional bundling without asking users to use a different namespace to import from:

Have 2 modules:

// src/react/react.ts

let React: typeof import('react');

export function requireReactLazily(): typeof import('react') {
  if(!React) {
    throw new Error('React not loaded, consider putting `import @apollo/client/react/load`');
  }
  return React;
}

export function setReact(_React) {
  React = _React;
}

And another,

// src/react/load.ts

import React from 'react';
import { setReact } from './react';

setReact(React);

Now, have the users when using apollo-client in a react app do:

// app.tsx

import { ApolloClient, ApolloProvider, useQuery } from '@apollo/client';
import '@apollo/client/react/load'; // This is required only once.

... Rest of the App ...

Instead of load.ts if we name it index.ts, we can have users just import '@apollo/client/react'.

@hwillson
Copy link
Member

hwillson commented May 29, 2020

Thanks all - sorry, I misinterpreted the original problem regarding the example app I shared (I was focused on the use case of not needing React).

We had discussed a lot of the ideas in this thread back when we decided to integrate our React hooks into the @apollo/client core. The thing is, we're very focused on providing an as easy to use out of the box Apollo Client + React experience as possible. React is the only view layer we officially support ourselves, which means it gets higher priority in our design decisions and usability goals. We'll put some more thought into this, but the most likely outcome here (for the short term) is that we'll just remove the lazy load / require for the AC 3.0 release. We'll then investigate a way to make React more detachable, but that method will not be part of the default out of the box @apollo/client package install experience. TBD though, more details shortly.

@FredKSchott
Copy link
Author

@hwillson I think that's a good call 👍

hwillson added a commit that referenced this issue May 29, 2020
to help prevent modern bundlers from requiring React when used
with `@apollo/client/core` (in other words, without any of
Apollo Client's React components). While this approach works
well for applications that aren't using React, it introduces
problems for bundlers and applications that are using React
(as outlined in #6035 and #6352). There are several different ways
we can address this, and we might do something more substantial
in the future, but for now this commit manipulates Apollo Client's
core CJS bundle at build time, to make the React require optional.

Fixes #6035.
Fixes #6352.
hwillson added a commit that referenced this issue May 29, 2020
PR #5577 introduced a new way of lazily loading React
to help prevent modern bundlers from requiring React when used
with `@apollo/client/core` (in other words, without any of
Apollo Client's React components). While this approach works
well for applications that aren't using React, it introduces
problems for bundlers and applications that are using React
(as outlined in #6035 and #6352). There are several different ways
we can address this, and we might do something more substantial
in the future, but for now this commit manipulates Apollo Client's
core CJS bundle at build time, to make the React require optional.

Fixes #6035.
Fixes #6352.
@FredKSchott
Copy link
Author

Excellent! Thanks for the quick fix. What's the best way to test this in Snowpack/Rollup?

@hwillson
Copy link
Member

@FredKSchott we'll publish a new beta shortly (I'll post back here when it's ready). Thanks!

@hwillson
Copy link
Member

@FredKSchott we just published @apollo/client@3.0.0-beta.53 which should hopefully fix this 🤞.

@FredKSchott
Copy link
Author

That worked! Thanks again

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants