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

Support sveltekit (vite)

Open AndreasHald opened this issue 3 years ago • 43 comments

Intended outcome: Apollo Client to work with sveltekit

Actual outcome: I'm attempting to use Apollo Client with sveltekit, I can run the dev server just fine, but when I build the project and run it using node I get an error

Directory import '/Users/.../node_modules/@apollo/client/core' is not supported resolving ES modules imported from /Users/.../.svelte-kit/output/server/app.js
Did you mean to import @apollo/client/core/core.cjs.js?

Now if I instead import the Apollo Client from @apollo/client instead of @apollo/client/core I don't get this issue, I suspect because of this line in the Apollo Client package.json "module": "./index.js" (but I'm no expert)

However I cannot import the client directly from @apollo/client because it is dependent upon the react module, and since sveltekit does server side rendering the react package explodes in a node environment. here's a related issue discussing this in the Vite repo (sveltekit uses Vite internally)

I guess my best bet would be to attempt to instruct the build process to replace @apollo/client/core with @apollo/client/core/index.js at build time, which I will attempt to look into as a fix.

Are there any other suggested approaches fix this issue?

How to reproduce the issue: Download the sveltekit starter npm init svelte@next my-app Include the Apollo Client package Attempt to build I created a reproducible repo here Versions

System: OS: macOS 11.3 Binaries: Node: 16.0.0 - /usr/local/bin/node Yarn: 1.22.10 - ~/Documents/eddystone/node_modules/.bin/yarn npm: 7.10.0 - /usr/local/bin/npm Browsers: Chrome: 90.0.4430.212 Safari: 14.1 npmPackages: @apollo/client: ^3.3.18 => 3.3.18

AndreasHald avatar May 14 '21 07:05 AndreasHald

CC @brainkim as per email with Alex

AndreasHald avatar May 14 '21 07:05 AndreasHald

@AndreasHald If you look in @apollo/client/core/package.json:

{
  "name": "@apollo/client/core",
  "main": "core.cjs.js",
  "module": "index.js",
  "types": "index.d.ts"
}

you'll see both a CommonJS "main": "core.cjs.js" entry point (which Node.js should be able to use) and an ESM "module": "index.js" entry point, which most web bundlers (Rollup, webpack, Parcel, etc) will use.

My question is… why isn't Node.js automatically using @apollo/client/core/core.cjs.js (as directed by the "main" property)? Has the @apollo/client/core/package.json file been removed (or ignored) somehow?

benjamn avatar May 14 '21 16:05 benjamn

Alternatively, if either sveltekit or Vite has a build option for targeting CommonJS require and exports rather than ESM import and export declarations, you might be able to use that build in Node.js?

benjamn avatar May 14 '21 16:05 benjamn

Related:

  • https://github.com/vitejs/vite/issues/2579
  • https://github.com/sveltejs/kit/issues/503

brainkim avatar May 14 '21 18:05 brainkim

I'm encountering this exact issue. I think that part of the complexity here is that SvelteKit needs the same source code (e.g. import { ApolloClient } from '@apollo/client/core';) to work both server-side and client-side. It seems that in the Node context, the Node code uses the CommonJS entry point for @apollo/client/core, but still attempts to use the ESM syntax in my source code to import the module.

I got a slightly different error than OP:

import { ApolloClient } from "@apollo/client/core";
         ^^^^^^^^^^^^
SyntaxError: Named export 'ApolloClient' not found. The requested module '@apollo/client/core' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@apollo/client/core';
const { ApolloClient } = pkg;

System: Windows 10 Node: 16.2.0 NPM: 7.18.1 @apollo/[email protected] @sveltejs/[email protected] @sveltejs/[email protected]

dennisjlee avatar Jun 23 '21 22:06 dennisjlee

@dennisjlee Try updating to @apollo/client@beta? We've gotten rid of all non-ESM dependencies of @apollo/client/core, and a lot of other things have changed (see #7399), so I'm confident your experience will be… different!

benjamn avatar Jun 25 '21 18:06 benjamn

@benjamn thanks - I won't have time to try that for a few days but I will report back when I can.

dennisjlee avatar Jun 25 '21 21:06 dennisjlee

Hi, @benjamn! I've checked @apollo/client@beta and have the same problem. Thinking this issue is related to https://github.com/sveltejs/kit/issues/612. If we need an import scope submodule, we need to add some export information, like https://github.com/ReactiveX/rxjs/pull/6192/files do.

In my case, adding these lines to @apollo/client package.json solved the problem:

"exports": {
    ".": {
      "node": "./main.cjs.js",
      "default": "./index.js"
    },
    "./cache": {
      "node": "./cache/cache.cjs.js",
      "default": "./cache/index.js"
    },
    "./core": {
      "node": "./core/core.cjs.js",
      "default": "./core/index.js"
    },
    "./link/schema": {
      "node": "./link/schema/schema.cjs.js",
      "default": "./link/schema/index.js"
    },
    "./link/http": {
      "node": "./link/http/http.cjs.js",
      "default": "./link/http/index.js"
    }
  },

What is your opinion on this?

cudr avatar Jul 06 '21 11:07 cudr

Are there any more thoughts on @cudr's solution now that 3.4 has dropped and this issue is still surfacing? (3.4.x is otherwise awesome by the way!)

qstearns avatar Aug 05 '21 22:08 qstearns

I'm using the latest beta of apollo-client ("@apollo/client": "^3.5.0-beta.6") and unfortunately this issue still remains, which is especially curious because thanks to #8396, the package.json for node_modules/@apollo/client/core/package.json now looks like this:

{
  "name": "@apollo/client/core",
  "type": "module",
  "main": "core.cjs",
  "module": "index.js",
  "types": "index.d.ts",
  "sideEffects": false
}

That is, it has type=module, which I thought was the thing that's required here.

eminence avatar Aug 23 '21 00:08 eminence

Adding the exports map above as @cudr suggested would be a good solution. One tweak to that: you should also export package.json so that various build tools such as vite-pugin-svelte can read it

benmccann avatar Aug 23 '21 16:08 benmccann

Sorry to dig up a stale thread, but wondering if there's been any update to a solution for using apollo/client with svelte-kit? Still getting the Directory import '.../node_modules/@apollo/client/core' is not supported resolving ES modules imported from... error.

Have tried importing directly from the core.cjs.js file based on this stack overflow post

import { ApolloClient, InMemoryCache } from '@apollo/client/core/core.cjs.js'

For some reason I'm still getting the same directory import error even though I'm specifying a file to import?

How are people adding the "exports" object to the package.json file? Is this a script that needs to run before the application gets built on something like Vercel or Netlify?

cchyung avatar Oct 12 '21 18:10 cchyung

@cchyung yes. I solved this problem by adding a pre-build step that reads @apollo-client package.json, adds common exports lines to it, and overwrites the existing file. But this is a trick

cudr avatar Oct 16 '21 22:10 cudr

@cudr Thanks for getting back to me :)

For others who are curious, here's line I used in the script we set up. We were deploying to vercel which fortunately let's you define a custom install script.

#!/bin/bash

yarn # install dependencies first

sed -i.bak "s/\"dependencies\":/\"exports\":{\".\":{\"node\":\".\/main.cjs.js\",\"default\":\".\/index.js\"},\".\/cache\":{\"node\":\".\/cache\/cache.cjs.js\",\"default\":\".\/cache\/index.js\"},\".\/core\":{\"node\":\".\/core\/core.cjs.js\",\"default\":\".\/core\/index.js\"},\".\/link\/schema\":{\"node\":\".\/link\/schema\/schema.cjs.js\",\"default\":\".\/link\/schema\/index.js\"},\".\/link\/context\":{\"node\":\".\/link\/context\/context.cjs.js\",\"default\":\".\/link\/context\/index.js\"},\".\/link\/http\":{\"node\":\".\/link\/http\/http.cjs.js\",\"default\":\".\/link\/http\/index.js\"}},\n\"dependencies\":/" ../node_modules/@apollo/client/package.json

cchyung avatar Oct 18 '21 03:10 cchyung

Svelte-kit and all this stuff will work if apollo would add exports map. Same as firebase did for example: https://github.com/firebase/firebase-js-sdk/pull/4767 Or rxjs: https://github.com/ReactiveX/rxjs/pull/6192

Hope apollo will fix this soon. Until I would use @cudr and @cchyung workaround (but it's not .cjs.js but just .cjs for me). It doesn't contain all exports that my project requires but I just added some more manually. Also it required me to escape some more characters so I could paste this in Dockerfile (this wasn't as easy).

I would like to open PR by myself and try to propose it as solution to this issue but I am not really into how apollo works under the hood, I opened @apollo/client in my node_modules and was terrified of amount of exports, files and all these things.

Note: I've killed hours to make apollo (and svelte-apollo) work with svelte. If you are struggling too, make sure to clean-up old "solutions" and workarounds you had before (for example optimizeDeps in your svelte.config.js or some weird not common imports as they will conflict. And I recommend you to import things directly from where it belongs (e. g. import client from @apollo/client/core, not @apollo/client). Also clean-up build and .svelte-kit folders every time you run your project until you make it work. And of course when you are done do note forget to add this sed command to Dockerfile if you have one.


My imports:

import { ApolloClient, InMemoryCache, split } from "@apollo/client/core";
import { HttpLink } from "@apollo/client/link/http";
import { setContext } from "@apollo/client/link/context";
import { WebSocketLink } from "@apollo/client/link/ws";

My sed command for imports above: sed -i.bak "s/\"dependencies\":/\"exports\":{\".\":{\"node\":\".\/main.cjs\",\"default\":\".\/index.js\"},\".\/cache\":{\"node\":\".\/cache\/cache.cjs\",\"default\":\".\/cache\/index.js\"},\".\/core\":{\"node\":\".\/core\/core.cjs\",\"default\":\".\/core\/index.js\"},\".\/link\/schema\":{\"node\":\".\/link\/schema\/schema.cjs\",\"default\":\".\/link\/schema\/index.js\"},\".\/link\/context\":{\"node\":\".\/link\/context\/context.cjs\",\"default\":\".\/link\/context\/index.js\"},\".\/link\/http\":{\"node\":\".\/link\/http\/http.cjs\",\"default\":\".\/link\/http\/index.js\"},\".\/link\/ws\":{\"node\":\".\/link\/ws\/ws.cjs\",\"default\":\".\/link\/ws\/index.js\"}},\n\"dependencies\":/" ./node_modules/@apollo/client/package.json (this one is escaped and can be pasted directly to your Dockerfile)

ZerdoX-x avatar Nov 24 '21 22:11 ZerdoX-x

@cchyung @cudr thanks for the "tricks"/"hack".

However, they did not work for me with Apollo version (3.5.5) and latest Sveltekit (1.0.0-next.201), but removing the .js in .cjs.js filenames did it for me, like this:

"exports": {
  ".": {
    "node": "./main.cjs",
    "default": "./index.js"
  },
  "./core": {
    "node": "./core/core.cjs",
    "default": "./core/index.js"
  },
  "./link/http": {
    "node": "./link/http/http.cjs",
    "default": "./link/http/index.js"
  },
  "./package.json": "./package.json"
}

A modified sed command that worked for me in a build step (I'm using this in a Dockerfile):

sed -i.bak "s/\"dependencies\":/\"exports\":{\n\".\":{\"node\":\".\/main.cjs\",\"default\":\".\/index.js\"},\n\".\/core\":{\"node\":\".\/core\/core.cjs\",\"default\":\".\/core\/index.js\"},\n\".\/link\/http\":{\"node\":\".\/link\/http\/http.cjs\",\"default\":\".\/link\/http\/index.js\"},\n\".\/package.json\": \".\/package.json\"\n},\n  \"dependencies\":/" node_modules/@apollo/client/package.json

With the above, I'm able to have imports like this in the Sveltekit app:

import { ApolloClient, InMemoryCache, } from '@apollo/client/core';
import { HttpLink } from '@apollo/client/link/http';
import type { NormalizedCacheObject } from '@apollo/client';

dlebech avatar Dec 07 '21 10:12 dlebech

Following Hasura's tutorial ( https://hasura.io/learn/graphql/svelte-apollo/apollo-client/ ) I'm also having some issues with apollo-client

Doing yarn run dev with the setup from Hasura's tutorial I get several errors like the following

 > node_modules/@apollo/client/react/hooks/useReactiveVar.js:1:36: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    1 │ import { useEffect, useState } from 'react';
      ╵                                     ~~~~~~~
      
 > node_modules/@apollo/client/react/context/ApolloContext.js:1:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    1 │ import * as React from 'react';

What can I do about this?

ItzaMi avatar Jan 14 '22 14:01 ItzaMi

@ItzaMi

What can I do about this?

The solution is hinted at in the issue description above. Use @apollo/client/core rather than @apollo/client for the client import. E.g. see the imports in my note above.

This issue and comment talks about it as well. They mention React will not be a hard dependency like this in Apollo 4.

But fundamentally, it's a separate issue from what this issue describes -- The React error has nothing to do with Sveltekit :slightly_smiling_face:

dlebech avatar Jan 14 '22 14:01 dlebech

@cchyung @cudr thanks for the "tricks"/"hack".

However, they did not work for me with Apollo version (3.5.5) and latest Sveltekit (1.0.0-next.201), but removing the .js in .cjs.js filenames did it for me ...

Weirdly enough - in my first project I needed to include .js but in a new project that I am working on I needed to leave it as .cjs. Something else must be strange about my project setup (mono repo using node workspaces).

Regardless, thanks for that clarification!

cchyung avatar Jan 15 '22 20:01 cchyung

@dennisjlee Try updating to @apollo/client@beta? We've gotten rid of all non-ESM dependencies of @apollo/client/core, and a lot of other things have changed (see #7399), so I'm confident your experience will be… different!

This made it for me!

Stack: Svelte + Vite + Routify

Lucasmiguelmac avatar Jan 30 '22 15:01 Lucasmiguelmac

Here's the issue for removing React: https://github.com/apollographql/apollo-client/issues/8190

benmccann avatar Feb 04 '22 17:02 benmccann

Hi @benjamn, I'm a maintainer of SvelteKit and contributor to Vite. I'd like to work with you or someone on the Apollo team to fix this issue if we can

SvelteKit expects code to be written in an isomorphic manner - meaning it will run on both the client and server. The issue here is that there's currently no way to do that with Apollo Client because Node.js expects to either have an exports map or expects you to include the file extension. The file extension in this case is .cjs and that doesn't work on the browser.

So I believe there are two possible solutions to this issue:

  • the most user-friendly would be to add an exports map as it would simply allow to import @apollo/client/core. However, this is technically a breaking change because it would disallow deep imports of entry points not included in the exports map. But often consumers should not be reaching into the internals of a library in an undocumented way and so many libraries wouldn't consider adding this to be backwards incompatible
  • the other potential solution would be to create a core.js living alongside core.cjs so that users can import @apollo/client/core/core.js. Or is this already done with @apollo/client/core/index.js? This is a bit uglier, but should unblock people. The reason it works is that you can import ESM on both the client and server unlike CJS

Are you open to a PR to add an exports map or is that too braking of a change?

Regarding the second potential solution...

@dennisjlee Try updating to @apollo/client@beta? We've gotten rid of all non-ESM dependencies of @apollo/client/core, and a lot of other things have changed (see https://github.com/apollographql/apollo-client/pull/7399), so I'm confident your experience will be… different!

Unfortunately, I'm not sure this is true. I tried importing @apollo/client/core/index.js and got the error message:

file://node_modules/@apollo/client/utilities/globals/fix-graphql.js:1 import { remove } from "ts-invariant/process/index.js"; ^^^^^^ SyntaxError: Named export 'remove' not found. The requested module 'ts-invariant/process/index.js' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export, for example using:

import pkg from 'ts-invariant/process/index.js'; const { remove } = pkg;

ts-invariant/process lacks ESM support (https://github.com/apollographql/invariant-packages/issues/227). Additionally, it's not really valid to do a deep import like this without an exports map for the same reasons users can't do it in this issue. If adding an exports map to @apollo/client might be too difficult in terms of breakingness, perhaps it could be done more easily in ts-invariant?

P.S. @dennisjlee any chance you're the DJ Lee that used to work at Google on Spreadsheets? :smile:

benmccann avatar Feb 04 '22 18:02 benmccann

Update: I believe I have a SvelteKit / Vite project with Apollo client successfully building locally after https://github.com/apollographql/invariant-packages/pull/254 is merged

benmccann avatar Feb 04 '22 19:02 benmccann

@benmccann Thanks for the offer to help!

It sounds like the ts-invariant/process problem is solvable, especially because ts-invariant/process/index.js is an ECMAScript module, so the problem is more about getting build tools to interpret it as such, not anything deeper. In this case, putting "type": "module" in ts-invariant/process/package.json and changing file extensions may be all that's needed, unless that violates your sense of "validity" by importing from a nested subdirectory.

I have a lot more to say about your notions of "isomorphism" and "validity" and where to draw the line when supporting Node.js features, if we need to get into that, but could you maybe imagine the ts-invariant/process issue already solved, and reread your post in that light? The rest of it may not matter all that much, and adding an exports map may not be all that urgent.

benjamn avatar Feb 04 '22 19:02 benjamn

Looks like we're on the same page! (Finished my comment just as yours appeared)

benjamn avatar Feb 04 '22 19:02 benjamn

@benmccann Your changes to the ts-invariant package should be testable now in @apollo/[email protected]. If everything looks good there, we can backport this to v3.5.x.

benjamn avatar Feb 04 '22 20:02 benjamn

@benmccann @benjamn thanks for your help in getting this fixed! @benmccann yes that's me 😄 I DMed you on linkedin.

dennisjlee avatar Feb 04 '22 20:02 dennisjlee

Thanks for all your help! And yes I can confirm it's working now. Here's is a fork of the repo posted above where it's working: https://github.com/benmccann/sveltekit-apollo-build-bug/tree/working-version

All that is needed is an updated version of @apollo/client and then change the import from @apollo/client/core to @apollo/client/core/index.js

What do you think about a PR to add an exports map to support importing from @apollo/client/core as a slightly nicer API? Or would that be too breaking of a change?

benmccann avatar Feb 04 '22 20:02 benmccann

Hi, all. So, having done all, this error shows in the terminal, even though page seems to load:

00:38:37 [vite] Error when evaluating SSR module /src/routes/index.svelte:
file:///Users/macbook/code.ly/exchange/node_modules/@apollo/client/utilities/globals/fix-graphql.js:1
import { remove } from "ts-invariant/process/index.js";
         ^^^^^^
SyntaxError: Named export 'remove' not found. The requested module 'ts-invariant/process/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'ts-invariant/process/index.js';
const { remove } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:181:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async nodeImport (/Users/macbook/code.ly/exchange/node_modules/vite/dist/node/chunks/dep-971d9e33.js:56246:21)
    at async eval (/src/routes/index.svelte:11:31)
    at async instantiateModule (/Users/macbook/code.ly/exchange/node_modules/vite/dist/node/chunks/dep-971d9e33.js:56177:9)
Named export 'remove' not found. The requested module 'ts-invariant/process/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'ts-invariant/process/index.js';
const { remove } = pkg;

Any idea(s) on how to fix? Thank you.

power-f-GOD avatar Feb 24 '22 23:02 power-f-GOD

Hi, all. So, having done all, this error shows in the terminal, even though page seems to load:

00:38:37 [vite] Error when evaluating SSR module /src/routes/index.svelte:
file:///Users/macbook/code.ly/exchange/node_modules/@apollo/client/utilities/globals/fix-graphql.js:1
import { remove } from "ts-invariant/process/index.js";
         ^^^^^^
SyntaxError: Named export 'remove' not found. The requested module 'ts-invariant/process/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'ts-invariant/process/index.js';
const { remove } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:181:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async nodeImport (/Users/macbook/code.ly/exchange/node_modules/vite/dist/node/chunks/dep-971d9e33.js:56246:21)
    at async eval (/src/routes/index.svelte:11:31)
    at async instantiateModule (/Users/macbook/code.ly/exchange/node_modules/vite/dist/node/chunks/dep-971d9e33.js:56177:9)
Named export 'remove' not found. The requested module 'ts-invariant/process/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'ts-invariant/process/index.js';
const { remove } = pkg;

Any idea(s) on how to fix? Thank you.

Same problem with me on dev mode: when loading page for first time this error appears, after reloading page the error is gone. But the error is critical when running build command using node adapter.

aramisromero89 avatar Mar 03 '22 19:03 aramisromero89