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

3.1.0-pre.0 bundle size regression with rollup #6687

Closed
henryqdineen opened this issue Jul 23, 2020 · 8 comments
Closed

3.1.0-pre.0 bundle size regression with rollup #6687

henryqdineen opened this issue Jul 23, 2020 · 8 comments
Assignees
Milestone

Comments

@henryqdineen
Copy link
Contributor

henryqdineen commented Jul 23, 2020

I know this is prerelease but I just want to give some feedback. This is the same as a comment I made in faa64b3#r40855898 but I created an issue for more visibility.
I tried out 3.1.0.pre.0 and we are seeing a bundle size regression in our rollup bundle. Here is a snippet from dist/core/index.js before and after

export { ApolloClient, } from './ApolloClient';
export { ObservableQuery, } from './ObservableQuery';
export { NetworkStatus } from './networkStatus';
export * from './types';
export { isApolloError, ApolloError } from '../errors';
import '../utilities/graphql/directives.js';
import '../utilities/graphql/fragments.js';
export { isReference, makeReference } from '../utilities/graphql/storeUtils.js';
import '../utilities/graphql/getFromAST.js';
import '../utilities/graphql/transform.js';
export { default as Observable } from 'zen-observable';
import '../utilities/common/mergeDeep.js';
import '../utilities/common/cloneDeep.js';
import '../utilities/common/maybeDeepFreeze.js';

I don't think rollup is able to treeshake the imports with no specifiers. Please let me know if you need any more info if there are any workarounds.

@henryqdineen
Copy link
Contributor Author

henryqdineen commented Jul 23, 2020

My current workaround is just to strip these imports off with a rollup plugin. Would that cause any issues and would it make sense to add something similar to resolveESMImportsAndFileExtensions()?

{
  transform(code, id) {
    const magicString = new MagicString(code);
    const ast = this.parse(code);

    walk(ast, {
      enter: function (node) {
        if (
          node.type === "ImportDeclaration" &&
          node.specifiers.length === 0 &&
          node.source.value !== "symbol-observable"
        ) {
          magicString.remove(node.start, node.end);
        }
      },
    });

    return {
      code: magicString.toString(),
    };
  },
};

henryqdineen referenced this issue Jul 23, 2020
This partially reverts commit 627bd1b
from PR #6656.

PR #6656 removed a function called `prepareESM`, which was no longer
needed for the sake of running `rollup-plugin-invariant`, but which had
another benefit: Rollup would replace imports/exports like

  // Example exports from ./dist/index.js:
  export * from './core'
  export * from './react'

with their fully-expanded (and file-extensioned) equivalents:

  ...
  export { makeVar } from './cache/inmemory/reactiveVars.js';
  export { defaultDataIdFromObject } from './cache/inmemory/policies.js';
  export { InMemoryCache } from './cache/inmemory/inMemoryCache.js';
  export { ApolloClient } from './core/ApolloClient.js';
  ...

Although this may look like more code, it effectively saves the
bundler/browser work by reducing indirection, and including the .js file
extensions makes this code a little more browser- and Rollup-friendly
(that is, you shouldn't have to use @rollup/plugin-node-resolve to bundle
@apollo/client with Rollup).

The expanded imports/exports are also closer to the ESM code shipped with
@apollo/[email protected], so restoring this behavior helps with the goal of
keeping the changes in #6656 backwards-compatible for v3.1.0.

Note that the CommonJS bundles used by Node.js are built before this step,
so this expansion of imports does not affect Node.js.
@benjamn
Copy link
Member

benjamn commented Jul 23, 2020

@henryqdineen This is great feedback, and really validates the decision to publish -pre.0 before 3.1.0!

I was uneasy with those specifier-less imports too, and couldn't find a combination of Rollup options to prevent them. I think they have something to do with import hoisting, which is supposedly disabled when preserveModules: true is set (which we do), but apparently not? Ironically, Rollup seems to be creating a problem that it can't solve (tree-shake) later.

In any case, I am planning to replace resolveESMImportsAndFileExtensions with a step that simply resolves all existing module identifiers, without injecting any new imports or exports.

@benjamn benjamn added this to the Post 3.0 milestone Jul 23, 2020
@benjamn benjamn self-assigned this Jul 23, 2020
@henryqdineen
Copy link
Contributor Author

Thanks! Feel free to reach out if you need help testing out alternate solutions.

@benjamn
Copy link
Member

benjamn commented Jul 24, 2020

@henryqdineen Give @apollo/[email protected] (which includes #6694) a try when you have a chance!

@henryqdineen
Copy link
Contributor Author

henryqdineen commented Jul 25, 2020

Thanks @benjamn! Nice solution in #6694. I just checked it out 3.1.0-pre.2 and it does seem to treeshake much better but I did notice one thing. It's having trouble treeshaking graphql-tag, which we wish to omit to reduce bundle size (we want to parse the graphql at build time). I think it has something to do with this no longer being in index.js:

export { default as gql } from 'graphql-tag';
export { disableExperimentalFragmentVariables, disableFragmentWarnings, enableExperimentalFragmentVariables, resetCaches } from './core/index.js';

Instead we have:

export * from "./core/index.js";

And in core/index.js:

import gql from 'graphql-tag';
export var resetCaches = gql.resetCaches, disableFragmentWarnings = gql.disableFragmentWarnings, enableExperimentalFragmentVariables = gql.enableExperimentalFragmentVariables, disableExperimentalFragmentVariables = gql.disableExperimentalFragmentVariables;
export { gql };

My assumption is that rollup is considering stuff like gql.resetCaches a potential side effect. We can figure out a workaround but definitely let us know if you have any ideas or if I'm missing anything obvious. If there is a future where using the graphql-tag at runtime is mandatory or if you think parsing at build time might be an anti-pattern I'd love to hear more about it.

@henryqdineen
Copy link
Contributor Author

I noticed one other small regression from 3.0.0 regarding treeshaking. We aren't using subscriptions but I noticed that 3.1.0-pre.2 would cause SubscriptionData to be bundled. I think this is more of a issue with rollup and the way TypeScript transforms classes but I wanted to mention it anyway.

In 3.0.0:

import { QueryData } from '../../data/QueryData';

In 3.1.0-pre
import { QueryData } from '../../data';

@benjamn
Copy link
Member

benjamn commented Jul 27, 2020

@henryqdineen Do you think it would help to replace

import gql from 'graphql-tag';
export var resetCaches = gql.resetCaches, disableFragmentWarnings = gql.disableFragmentWarnings, enableExperimentalFragmentVariables = gql.enableExperimentalFragmentVariables, disableExperimentalFragmentVariables = gql.disableExperimentalFragmentVariables;
export { gql };

with

export { default as gql } from 'graphql-tag'

or is it more a matter of declaring the graphql-tag package as not having side-effects?

benjamn added a commit that referenced this issue Jul 27, 2020
#6687 (comment)

Methods specific to the gql template tag should be accessed via the gql
function, rather than mixed in with the exports of @apollo/client.
@henryqdineen
Copy link
Contributor Author

@benjamn I just tried it out by adding "sideEffects": false to node_modules/graphql-tag/package.json and that did the trick! Are there plans to add that flag to that project? The other solution would work also but seems like a breaking change since we are removing named exports. Thanks as usual!

hwillson added a commit to apollographql/graphql-tag that referenced this issue Jul 28, 2020
This will help tree-shaking when used with projects that have a
dependency on `graphql-tag` (like `@apollo/client`).

Reference: apollographql/apollo-client#6687
hwillson added a commit to apollographql/graphql-tag that referenced this issue Jul 28, 2020
This will help tree-shaking when used with projects that have a
dependency on `graphql-tag` (like `@apollo/client`).

Reference: apollographql/apollo-client#6687
@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

No branches or pull requests

2 participants