sveltejs / sapper

The next small thing in web development, powered by Svelte
https://sapper.svelte.dev
MIT License
7k stars 434 forks source link

Document on how to build packages containing components for SSR and Sapper #1679

Closed yukipastelcat closed 3 years ago

yukipastelcat commented 3 years ago

Is your feature request related to a problem? Please describe.

I'm trying to build components library for Svelte with SSR support in this branch. However I stumbled upon several problems related to lack of documentation at these points.

Problem 1

It's unclear which package.json fields are used on import - I have to specify full path to cjs bundle in my import string:

<script>
  import { faApple } from '@fortawesome/free-brands-svg-icons'
  // while using SSR it seems to resolve module from the 'module' field of package.json
  import { FontAwesomeIcon } from 'fontawesome-svelte'
</script>
<script>
  import { faApple } from '@fortawesome/free-brands-svg-icons'
  // importing correct bundle for SSR, containing SSR methods
  import { FontAwesomeIcon } from 'fontawesome-svelte/dist/fontawesome-svelte.cjs.js'
</script>

Problem 2

It looks like the problem is that SSR components can't be used on client-side and vice-versa. I'm getting this error while trying to use my library in Sapper app:

Uncaught (in promise) TypeError: fontawesomeSvelte_cjs.FontAwesomeIcon is not a constructor

It looks like while component is trying to mount on client it attempts to create new instance, which is impossible because Svelte compiler with generate: 'ssr' option outputs constant, which cannot be used with new:

const FontAwesomeIcon = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
  // ...
}

And if I try to import esm module, it doesn't contain methods needed for SSR.

Describe the solution you'd like Provide a documentation for building SSR components with an example/template.

Describe alternatives you've considered Alternative solution currently is to include .svelte files into package and importing them, in which case if you're planning to use library outside of Svelte/Sapper without bundler configuration for handling .svelte files you'll end up with something like that:

import { FontAwesomeIcon as FontAwesomeIconCSR } from 'fontawesome-svelte/dist/fontawesome-svelte.esm.js'
import { FontAwesomeIcon as FontAwesomeIconSSR } from 'fontawesome-svelte/dist/fontawesome-svelte.cjs.js'

// SSR
const { html } = FontAwesomeSSR.render()
// append html somewhere

// CSR
const target = document.querySelector(/* find target element */)
const icon = new FontAwesomeIconCSR({
  target,
})

Additional context You can reproduce problems mentioned here by doing following:

Clone this branch, then run:

npm i
npm run build
npm pack

This will produce tarball, containing library package. Next up create Sapper app:

# for Rollup
npx degit "sveltejs/sapper-template#rollup" my-app
# for webpack
npx degit "sveltejs/sapper-template#webpack" my-app
cd my-app
npm i
npm i --save <path_to_library_tarball>
npm i --save @fortawesome/free-brands-svg-icons

Then change contents of src/routes/index.svelte to following:

<script>
    import { faApple } from '@fortawesome/free-brands-svg-icons'
    import { FontAwesomeIcon } from 'fontawesome-svelte/dist/fontawesome-svelte.cjs.js'
</script>
<FontAwesomeIcon icon={faApple} />

and launch project.

stephane-vanraes commented 3 years ago

Does https://github.com/sveltejs/sapper/issues/774 help you in any way for now ?

Svelte components are commonly added to the devDependencies instead.

With Sapper being discontinued in favour of SvelteKit it is unlikely this will get better documentation, but probably something that can be expanded upon more in upcoming documentation for SvelteKit.

yukipastelcat commented 3 years ago

@stephane-vanraes so it looks like I have to include .svelte files to package distribution and specify "svelte" field in the package.json since rollup-plugin-svelte and svelte-loader are seem to be using this field.