I'm working on a collection of Svelte-based components for use in Svelte apps, but am unsure whether it's better to:
1) Transpile the components using rollup (or webpack) to a single JS file
2) Package the components into individual js files using svelte-cli, or
3) Package the svelte .html files with no transpiling
End goal is to (only) use these within Svelte apps, and import like so:
import { Alpha, Bravo, Charlie, Delta } from 'my components';
export default {
components: {
Alpha,
Bravo,
Charlie,
Delta
}
...
Any guidance you can recommend here?
Great question. Not sure we have a ready-made answer for this, but I was wondering about a hybrid approach that allowed components to be used by Svelte users and non-Svelte users:
import { Alpha, Bravo, Charlie, Delta } from 'my-components';
By default, my-components is resolved in the normal way (node_modules/my-components/index.js), and that file re-exports the transpiled components — in other words, there's probably a src/index.js file that looks like this...
// src/index.js
export { default as Alpha } from './Alpha.html';
export { default as Beta } from './Beta.html';
// ...
...and is bundled (probably by Rollup, in this context). But for Svelte users, maybe we have a special "svelte" property in package.json (that rollup-plugin-svelte and svelte-loader etc are aware of) that looks like this...
// package.json
{
// ...
"svelte": {
"Alpha": "src/Alpha.html",
"Beta": "src/Beta.html",
// ...
}
}
...and allows those plugins to go straight to the source, eliminating the overhead of duplicated helpers.
This does pose a tricky question — if you as the author of my-components used a particular version of Svelte, the app developer must be using a compatible version of Svelte. Maybe the "svelte" object has a version property (or maybe bundler integrations check to see which version my-components has listed in its dependencies field) so that it can warn about incompatibilities.
An alternative to doing named imports would be to transpile to individual files:
import Alpha from 'my-components/Alpha';
Though that's arguably a bit less intuitive, and more work for component authors (and these components ought to be fully tree-shakeable, so it should in theory be unnecessary).
Interested to hear people's thoughts.
Thanks Rich, for the time being I have bundled to single file using Rollup, but would also like to hear what svelte users think.
I apologize if my remarks are not relevant to the question.
Except for really small apps, I find the approach of individual js files for each component more interesting. The app can load only the required components according to the route.
It may seem more time-consuming to load multiple components instead of just one, but multiple really-small componentes always load faster than a really-big one.
_(and with HTTP2, these multiple files will possibly be transferred in a single request)_
When the user starts browsing the app, only the components that are not already in memory are downloaded. In my opinion this allows not only fast loading on the first request, but also fast navigation between different routes.
--
After a component has been downloaded, it can be stored in memory (or at least in a local storage), allowing it not to be downloaded again.
With this approach, there will come a time when the user will have all or almost all the necessary components in memory, giving a near real-time navigation.
@paulocoghi the theory is that your bundler would tree-shake away the unused components — so if your app did...
import { Foo } from 'component-library';
new Foo({...});
...then the Bar and Baz components exported by that library would never appear in your bundle. (This assumes you're using Rollup or Webpack 2.) It might get a little bit trickier when you throw dynamic imports into the mix, I'm not totally sure yet.
It probably would make sense to offer individual files as well for people with different workflows, so a component library might look something like this...
| src
| | Foo.html
| | Bar.html
| | Baz.html
| package.json
| index.js
| Foo.js
| Bar.js
| Baz.js
...but that means that some people would write...
import Foo from 'component-library/Foo.js';
...whereas others would have to write...
import Foo from 'component-library/src/Foo.html';
...rather than having a unified approach that the tooling could optimise.
Thanks Rich, agreed - also @paulocoghi important part of question I had is whether we should bundle (transpiled) JS file(s) or untranspiled Svelte HTML files for a Svelte-based library. For time being, I am using JS as it makes usage outside of Svelte (including server side rendering) easier, but happy to adopt/add whatever the Svelte community uses.
@bestguy JS makes more sense, in case there's ever any breaking changes.
Sure, though we could still publish the .html using semver, etc.
With that said, I personally prefer bundling to JS to encapsulate the various JS dependencies/libraries that a Svelte component may use
What I mean is, once a component is turned into JS that code will work the exact same forever. As the svelte compiler changes, the outputted code changes as well. So say svelte 2.0 has a breaking change in component format, svelte 1.0 components that are compiled to JS will still work in 2.0 code.
Gotcha, yes that's a good point
I was searching for how to expose multiple components from one npm package (basically, how to correctly wire up the svelte key in package.json) and came across this issue (hi @bestguy!). Is there more up to date documentation about how to do that as of late 2019 - Svelte 3.12.x?
This is talked about here. This is definitely something we could make more discoverable, but there's not really a good place for this sort of information to live currently.
Most helpful comment
Great question. Not sure we have a ready-made answer for this, but I was wondering about a hybrid approach that allowed components to be used by Svelte users and non-Svelte users:
By default,
my-componentsis resolved in the normal way (node_modules/my-components/index.js), and that file re-exports the transpiled components — in other words, there's probably asrc/index.jsfile that looks like this......and is bundled (probably by Rollup, in this context). But for Svelte users, maybe we have a special
"svelte"property in package.json (that rollup-plugin-svelte and svelte-loader etc are aware of) that looks like this......and allows those plugins to go straight to the source, eliminating the overhead of duplicated helpers.
This does pose a tricky question — if you as the author of
my-componentsused a particular version of Svelte, the app developer must be using a compatible version of Svelte. Maybe the"svelte"object has a version property (or maybe bundler integrations check to see which versionmy-componentshas listed in itsdependenciesfield) so that it can warn about incompatibilities.An alternative to doing named imports would be to transpile to individual files:
Though that's arguably a bit less intuitive, and more work for component authors (and these components ought to be fully tree-shakeable, so it should in theory be unnecessary).
Interested to hear people's thoughts.