svelte-apollo icon indicating copy to clipboard operation
svelte-apollo copied to clipboard

Order of import between apollo dependencies and svelte-apollo somehow matters

Open zzgvh opened this issue 3 years ago • 2 comments

Hello, I've been learning Svelte and one of the small apps I have built is a POC for using Svelte with a graphql backend. For this I have of course used the fine svelte-apollo library along with bits and bobs from apollo itself. I'm posting this issue as a "note" as much as a question/bug report, since I cannot say this necessarily is an issue with svelte-apollo, but may be my somewhat inept way of setting things up or the general messiness of the javascript universe, but I thought someone might either have some insight into what's going on or maybe have a similar problem and then this might be a fix. I'm using rollup to get Svelte to build and I am guessing there is a connection there.

So. I started out creating my test app with the setup of ApolloClient in the

App.svelte

<script>
  import { ApolloClient, HttpLink, InMemoryCache, split } from "apollo-boost";
  import { WebSocketLink } from "apollo-link-ws";
  import { getMainDefinition } from "apollo-utilities";
  import { setClient } from "svelte-apollo";
  import { Container, Row, Col } from "sveltestrap";

  import Batches from "./Batches.svelte";

  const headers = {
    "content-type": "application/json",
    "x-hasura-admin-secret": "notSecret",
  };
  const getHeaders = () => {
    return headers;
  };

  const wsLink = new WebSocketLink({
    uri: "ws://localhost:8080/v1/graphql",
    options: {
      reconnect: true,
      lazy: true,
      connectionParams: () => {
        return { headers: getHeaders() };
      },
    },
  });

  const httpLink = new HttpLink({
    uri: "http://localhost:8080/v1/graphql",
    headers: getHeaders(),
  });

  const link = split(
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === "OperationDefinition" && operation === "subscription";
    },
    wsLink,
    httpLink
  );
  const cache = new InMemoryCache();

  const client = new ApolloClient({
    link,
    cache,
    onError: ({ networkError, graphQLErrors }) => {
      console.log("graphQLErrors", graphQLErrors);
      console.log("networkError", networkError);
    },
  });

  setClient(client);
</script>

<Container>
  <Row>
    <Col>
      <Batches />
    </Col>
  </Row>
</Container>

Batches.svelte

<script>
  import { getClient, query } from "svelte-apollo";
  import { gql } from "apollo-boost";

  const GET_BATCHES = gql`
    {
      poni_batch {
        id
        poni_seed {
          name
        }
        poni_supplier {
          name
        }
      }
    }
  `;
  
  const client = getClient();
  const batches = query(GET_BATCHES);
</script>

{#if $batches.loading}
  Loading..
{:else if $batches.error}
  Error: {$batches.error.message}
{:else}
  <ul>
    {#each $batches.data.poni_batch as batch (batch.id)}
      <li>
        {batch.id}, {batch.poni_seed.name}
      </li>
    {/each}
  </ul>
{/if}

Works fine and I get a list of batches. But here's the curious thing. I extracted most of the apollo-related code that creates the client into its own .js file and used it as an import in my App.svelte. And then I get this error in the console: ReferenceError: process is not defined with references to the last line of bundle.js and to line 7 of graphql/jsutils/instanceOf.mjs.

instanceOf.mjs looks like this (I have indicated the line with the error):

/**
 * A replacement for instanceof which includes an error warning when multi-realm
 * constructors are detected.
 */
// See: https://expressjs.com/en/advanced/best-practice-performance.html#set-node_env-to-production
// See: https://webpack.js.org/guides/production/
ERROR HERE --> export default process.env.NODE_ENV === 'production' ? // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317')
// eslint-disable-next-line no-shadow
function instanceOf(value, constructor) {
  return value instanceof constructor;
} : // eslint-disable-next-line no-shadow
function instanceOf(value, constructor) {
  if (value instanceof constructor) {
    return true;
  }

  if (value) {
    var valueClass = value.constructor;
    var className = constructor.name;

    if (className && valueClass && valueClass.name === className) {
      throw new Error("Cannot use ".concat(className, " \"").concat(value, "\" from another module or realm.\n\nEnsure that there is only one instance of \"graphql\" in the node_modules\ndirectory. If different versions of \"graphql\" are the dependencies of other\nrelied on modules, use \"resolutions\" to ensure only one version is installed.\n\nhttps://yarnpkg.com/en/docs/selective-version-resolutions\n\nDuplicate \"graphql\" modules cannot be used at the same time since different\nversions may have different capabilities and behavior. The data from one\nversion used in the function from another could produce confusing and\nspurious results."));
    }
  }

  return false;
};

Now I'm not super well informed on how JS importing and modules work but I'm guessing the last line of bundle.js should look like this: }(process)); instead of }()); and all goes south when it doesn't!

After quite a bit of fiddling I found that I could solve my problem by changing the order of my imports! Usually I import external dependencies first followed by "local" stuff:

App.svelte:

<script>
  import { setClient } from "svelte-apollo";

  import { client } from "./apollo";
  import Batches from "./Batches.svelte";

  setClient(client);
</script>
...

apollo.js:

import ApolloClient from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { WebSocketLink } from "apollo-link-ws";
import { split } from "apollo-link";
import { HttpLink } from "apollo-link-http";
import { getMainDefinition } from "apollo-utilities";
...

and this is when I get the error above. But if I swap the order in App.svelte to

<script>
  import { client } from "./apollo";
  import { setClient } from "svelte-apollo";

  import Batches from "./Batches.svelte";

  setClient(client);
</script>

everything works again! Say whaaat?!?

Final note: It seems I'm using an "old" way of importing apollo, as the docs now refer to @apollo/client and sub parts thereof. I tried changing my imports to that with the result that I get the error above regardless of order of importing!

zzgvh avatar Apr 08 '21 15:04 zzgvh

  1. Yes, you should import as follows:
import { ApolloClient } from "@apollo/client/core";
  1. Do you use vite?

flashspys avatar Apr 11 '21 21:04 flashspys

window.process = {
  env: { 'NODE_ENV': 'production' }
};

added this to graphql/jsutils/instanceOf.mjs for a temp fix

rkhatkhede avatar May 19 '21 20:05 rkhatkhede