Skip to content

NPM package using getContext/setContext doesn't work during SSR dev #360

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

Closed
enyo opened this issue Jun 1, 2022 · 18 comments
Closed

NPM package using getContext/setContext doesn't work during SSR dev #360

enyo opened this issue Jun 1, 2022 · 18 comments
Labels
bug Something isn't working

Comments

@enyo
Copy link

enyo commented Jun 1, 2022

Describe the bug

I have distilled the issue to this:

  • Create an npm package that exposes a function that uses setContext or getContext
  • Make sure that repo does not have svelte as a direct dependency but only as peerDependency (or not even that)
  • Import the package and use the function
  • It crashes with: Function called outside component initialization

This only happens as npm package, using a local dependency works fine.

Reproduction

I tried reproducing in a REPL but it works fine, that's why I assume it's a Svelte Kit issue.

Here is the minimal reproduction repo: https://github.com/enyo/context-issue

See the issue immediately:

git clone https://github.com/enyo/context-issue.git
cd context-issue/svelte-kit-app
npm install
npm run dev

The repo contains the package, where the dist/index.js has this content:

import { setContext } from 'svelte';

const testSetContext = () => setContext('test', 'value');

export { testSetContext };
//# sourceMappingURL=index.js.map

and a svelte kit app that uses it.

Logs

No response

System Info

System:
    OS: macOS 12.3.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 293.67 MB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 17.9.0 - ~/.nvm/versions/node/v17.9.0/bin/node
    Yarn: 1.22.18 - ~/.nvm/versions/node/v17.9.0/bin/yarn
    npm: 8.5.5 - ~/.nvm/versions/node/v17.9.0/bin/npm
  Browsers:
    Chrome: 102.0.5005.61
    Firefox: 100.0.2
    Safari: 15.4
    Safari Technology Preview: 15.4
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.50 
    @sveltejs/kit: next => 1.0.0-next.347 
    svelte: ^3.44.0 => 3.48.0

Severity

blocking all usage of SvelteKit

Additional Information

No response

@dominikg
Copy link
Member

dominikg commented Jun 1, 2022

This error usually happens when there are multiple versions of svelte, which means that the resolve.dedupe config vite-plugin-svelte adds to the vite config has failed.

It's a strange coincidence but a similar error just happened today with alpha testing of vite-3 in a test for vite-plugin-svelte: https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/e2e-tests/kit-node

Some information that can help track this down:

stacktrace from vite-plugin-svelte test

Error: Function called outside component initialization
    at get_current_component (file:///home/dominikg/develop/vitejs/vite-ecosystem-ci/workspace/svelte/vite-plugin-svelte/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.mjs:953:15)
    at setContext (file:///home/dominikg/develop/vitejs/vite-ecosystem-ci/workspace/svelte/vite-plugin-svelte/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.mjs:985:5)
    at Proxy.setSomeContext (file:///home/dominikg/develop/vitejs/vite-ecosystem-ci/workspace/svelte/vite-plugin-svelte/packages/e2e-tests/_test_dependencies/svelte-api-only/index.js:4:2)
    at index.svelte:35:1
    at Object.$$render (/home/dominikg/develop/vitejs/vite-ecosystem-ci/workspace/svelte/vite-plugin-svelte/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.js:1758:22)
    at Object.default (root.svelte:43:39)
    at eval (/.svelte-kit/runtime/components/layout.svelte:8:41)
    at Object.$$render (/home/dominikg/develop/vitejs/vite-ecosystem-ci/workspace/svelte/vite-plugin-svelte/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.js:1758:22)
    at root.svelte:37:37
    at $$render (/home/dominikg/develop/vitejs/vite-ecosystem-ci/workspace/svelte/vite-plugin-svelte/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.js:1758:22)

in this stacktrace svelte is referenced in node_modules, but another version of svelte exists in vite's optimizedDeps in node_modules/.vite/, so when this second svelte is loaded, it doesn't have the state of the first instance and this error happens.

@enyo
Copy link
Author

enyo commented Jun 1, 2022

@dominikg mh, yes I assumed that it had something to do with deduping not working, I'm just really stumped because the package doesn't have a svelte dependency by itself. So where does the second version come from?

Is there a workaround for this that you know of?

@dominikg
Copy link
Member

dominikg commented Jun 1, 2022

you can try playing with optimizeDeps.exclude and optimizeDeps.include.

vite-plugin-svelte should generate a working configuration for that, you can see it when you run with DEBUG=vite:* npm run dev

thanks for uploading the dependency tmp-context-issue to the npm registry, i was able to reproduce the same error outside of sveltekit: https://stackblitz.com/edit/vitejs-vite-aegr3m?file=package.json,src%2Fmain.js&terminal=dev

see comment by @gtm-nayan
color me stumped too.

@enyo
Copy link
Author

enyo commented Jun 1, 2022

Thanks. I'll try to find a working solution with that and will report back.

I also just realised that running npm run build works fine, so it seems to only be an issue with the dev server.

@enyo
Copy link
Author

enyo commented Jun 1, 2022

I didn't get it to work with either optimizeDeps.exclude or include.

@gtm-nayan
Copy link

In the Vite-only repro, the error is because it is actually being called outside component init, moving it inside App.svelte doesn't throw an error
https://stackblitz.com/edit/vitejs-vite-fhmgtz?file=src%2FApp.svelte,src%2Fmain.js&terminal=dev

As for enyo's SK one, adding the package to ssr.noExternal works around it but that probably has other ramifications for actual packages

@enyo
Copy link
Author

enyo commented Jun 1, 2022

Thanks @gtm-nayan that worked in my case. I assume that this is an issue that should be taken care of so I'm going to leave it open, but great to have a workaround.

Would you mind explaining why that works?

(Edit: this actually caused other issues that I wasn't able to resolve)

@gtm-nayan
Copy link

Unfortunately, I also found it by fiddling around with the config values so I don't have a definite answer as to why it works. My thought process was if it's force bundled the package would reference the same svelte instance as the app.

@enyo
Copy link
Author

enyo commented Jun 2, 2022

I tried the same fix in other projects and it surfaced other bugs that I wasn't able to get rid of, so ssr.noExternal workaround is not viable unfortunately.

@enyo
Copy link
Author

enyo commented Jun 2, 2022

@dominikg so is it safe to say that this issue is cause by vite-plugin-svelte?

@dominikg dominikg transferred this issue from sveltejs/kit Jun 2, 2022
@dominikg dominikg changed the title NPM package using getContext/setContext doesn't work NPM package using getContext/setContext doesn't work during SSR dev Jun 2, 2022
@dominikg dominikg added the bug Something isn't working label Jun 2, 2022
@dominikg
Copy link
Member

dominikg commented Jun 2, 2022

It may be improved by changing this code
https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/vite-plugin-svelte/src/utils/options.ts#L324-L346

to also add js-libraries to ssr.noExternal during dev. Right now the else block of isBuild && ssr removes them.

But you mention that you ran into other issues with noExternal. Can you elaborate on them?

@dominikg
Copy link
Member

dominikg commented Jun 2, 2022

One thing that still confuses me a bit is that this seems to be causing an error only with packages from npm, a local workspace package with a very similar setup is working just fine: https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/e2e-tests/kit-node/src/routes/index.svelte#L26

cc @bluwy

@enyo
Copy link
Author

enyo commented Jun 9, 2022

I am a bit stumped by this. Doesn't that mean that no npm dependency can use a context at the moment? If that's the case I'm surprised this isn't higher on the priority list with more people running into the issue.
Is it because there aren't many packages that are using setContext or is there something wrong with my package?

@dominikg
Copy link
Member

dominikg commented Jun 9, 2022

Adding it to ssr.noExternal fixes it for most people. You didn't share what issues this causes for you.

@bluwy
Copy link
Member

bluwy commented Jun 22, 2022

I'm been checking this bug on and off, and it seems like this is a bug that we've missed in our test, even though we test the context api too, but we're not installing it locally as an npm package which hides the error.

From what I can see, in Vite SSR, your source code that imports setContext (including when you import the package via relative path), Vite resolves the import path to /absolute /path/to/node_modules/svelte/internal/index.js.

When letting Node use it's resolve algorithm, it resolves to /absolute /path/to/node_modules/svelte/internal/index.mjs. Notice the file extension difference, it's the reason why the error appeared as they are two separate modules being loaded.

I can re-confirm that Node uses the .mjs variant via import.meta.resolve('svelte/internal'). So Vite is inconsistent here, and the reason is because Svelte (for some reason) has a package.json at svelte/internal/package.json. Vite reads that package.json, and because it imitates Node's resolution, doesn't read the module exports, so it reads main instead.

So how does Node still get the .mjs version, that because Vite's "get nearest package.json" logic isn't compliant with Node's. See https://nodejs.org/api/esm.html#resolver-algorithm-specification.

If pjson is not null and pjson.exports is not null or undefined, then
Return the result of PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).

svelte/internal/package.json's exports is undefined, it should continue to search for package.json up the parent directory.

So I think this is a Vite issue, and not something vite-plugin-svelte can fix at the moment. Other than the workaorund of ssr.noExternal, which should be fine to apply. I'll hold off closing this until I get a Vite issue/pr up.

@dominikg
Copy link
Member

dominikg commented Jul 20, 2022

@enyo please test this again with @sveltejs/[email protected] and [email protected] the configuration for ssr.noExternal has been updated and we believe it fixed this issue.

Edit: in case of SvelteKit, just update to it's latest version, that uses the above.

@typhonrt
Copy link

I have an alternate confirmation of what @dominikg suggested above is working. I distribute a Svelte component library by NPM and while I'm still getting up to speed on Vite a consumer of my lib tried out Vite w/ slightly older versions. The same getContext / setContext issue described above popped up. In testing I upgraded their repo to @sveltejs/[email protected] and [email protected] and it is fixed. Cheers!

@dominikg
Copy link
Member

Thanks for confirming

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants