f / graphql.js

A Simple and Isomorphic GraphQL Client for JavaScript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Webpack breaks isomorphic code - graphql.js adds 25kb gzipped!

laurencedorman opened this issue · comments

I've discovered that Webpack's default production build minifies graphql.js in a strange way, and breaks the isomorphic nature of this library. It might be a webpack problem but I wanted to see if others had encountered this issue. It seems that the problem relates to a specific part of graphql.js's code.

When Webpack's minifier processes this condition starting line 34:

if (typeof XMLHttpRequest !== 'undefined') {
// browser compatible __doRequest function
} else if (typeof require === 'function') {
// node compatible __doRequest function
}

It turns it into:

if ("undefined" != typeof XMLHttpRequest);
else {}

And it removes the browser-compatible definition of __doRequest, so we're only left with the Node-compatible one that depends on the native libraries http, https, and url. It doesn't actually break a build because Webpack automatically pulls in node polyfills. But this means that graphql.js adds far more than 4kb to a bundle, in fact it bloats the bundle by about 25kb gzipped!

Would you have any idea why it's doing this? This only happens when minifying.

To reproduce:

  1. Clone this repo
  2. Run npm install
  3. Run npm build-prod
  4. Open dist/main.beautified.js and go to line 2296.

You should see this code:

        if ("undefined" != typeof XMLHttpRequest); // if condition appears here, empty.
        else {}

        function o(t, e, n, o, s, a, u, f) {
            if (n) {
                if (a) var h = JSON.stringify({
                    query: s.query,
                    variables: s.variables
                });
                else h = "query=" + encodeURIComponent(s.query) + "&variables=" + encodeURIComponent(JSON.stringify(s.variables));
                for (var c in t && (console.groupCollapsed("[graphql]: " + e.toUpperCase() + " " + n + ": " + s.query.split(/\n/)[0].substr(0, 50) + "... with " + JSON.stringify(s.variables).substr(0, 50) + "..."), console.log("QUERY: %c%s", "font-weight: bold", s.query), console.log("VARIABLES: %c%s\n\nsending as " + (a ? "json" : "form url-data"), "font-weight: bold", JSON.stringify(s.variables, null, 2), s.variables), console.groupEnd()), o) "function" == typeof o[c] && (o[c] = o[c]());
                ! function(t, e, n, o, s, a, u, f) { // this is the node-compatible __doRequest function
                    var h = r(9), // http node library
                        c = r(42), // https node library
                        l = r(8).parse(e), // url node library
                        p = ("https:" === l.protocol ? c : h).request({
                            protocol: l.protocol,
                            hostname: l.hostname,
                            port: l.port,
                            path: l.path,
                            method: t.toUpperCase(),
                            headers: i({
                                "Content-type": n,
                                Accept: o
                            }, s)
                        }, (function(t) {
                            var e = "";
                            t.setEncoding("utf8"), t.on("data", (function(t) {
                                e += t
                            })), t.on("end", (function() {
                                f(JSON.parse(e), t.statusCode)
                            }))
                        }));
                    "function" == typeof u && p.on("error", (function(t) {
                        u(t)
                    })), p.write(a), p.end()
                }(e, n, a ? "application/json" : "application/x-www-form-urlencoded", "application/json", o, h, u, f)
            }
        }

Hmm, I think I've worked out that the problem is here.

} else if (typeof require === 'function') {

This is going to return true as require is a function in Webpack and I'm pretty sure it will be the same for Browserify and Rollup. What is happening in Webpack in production mode is that this condition evaluates to simply else if (true) { and so the other condition is stripped out as it's seen as dead code. Surely a better test, to be sure that we are in a Node environment would be:

} else if (typeof window !== 'object' && typeof require === 'function') {

Could I open a PR to this end?

Nope, that's not it. 🤔

@laurencedorman Did you manage to get to the bottom of this? I am observing that 100kb is added when I import graphql.js in an almost out-of-the-box create-react-app. I don't see a clear link.

Before importing graphql.js After importing graphql.js
image image

It is still 60kb less than Apollo Client, but I am a little puzzled by this impact.

@egeriis I forked the library and added an extra check on the function that verifies if the code is being run in a browser environment or not. You can see the commit here: https://github.com/laurencedorman/graphql.js/commit/6f73084dea546420c38e5485da99fa2521d808a9