What not to do when publishing a Svelte component locally

Summary
  • Using local dependency like "your-package": "file:../some-path" or npm link doesn't work with Svelte because you will have duplicated Svelte runtimes.
  • What works is to use npm pack and then npm install <path_to_tgz>
  • Bundlers look for Svelte runtime in the node_modules folder (or package.json; I didn't dive deeper) that belongs to the component folder's ancestor tree. If there are 2 node_modules folders involved, then there will be duplicated Svelte runtimes.
  • Having duplicated Svelte runtimes would result in obscure errors like: Cannot read properties of undefined (reading 'call') within get_first_child or Uncaught TypeError: Cannot read properties of null (reading 'f') on parent_effect.f

I was just stuck at publishing a Svelte component locally for almost a whole day.

I was looking to publish a Svelte component as an NPM package and reading this guide. All is good until I read this section:

Well, that is a great idea. Let's test it locally first.

I made a test folder with App.svelte which used my new component. Then, I configured Rollup to build App.svelte. It worked. Great!

Now I wanted to try it in my new project, Backdoor (Postgres Data Querying and Editing Tool that you can embed into your JVM app). I included the dependency by adding the line to package.json as shown below:

{
  ....
  "devDependencies": {
    "svelte-virtual-table-by-tanin": "file:../svelte-virtual-table"
  }
  ...
}

And it didn't work. The error happened inside Svelte itself and looked totally obscure: Cannot read properties of undefined (reading 'call')

At some point, I also encountered this error: Uncaught TypeError: Cannot read properties of null (reading 'f') because parent_effect is null in the parent_effect.f statement.

This happened within Svelte itself.

After googling it around for hours, nobody encountered the same issue before. There were only a few Svelte issues that had a similar error but not quite match my situation. However, I stumbled upon this comment:

This is an interesting theory. Maybe I had duplicated Svelte runtime in my bundle somehow. So I added the below to my Rollup config:

{
  ...,
  plugins: [
    resolve({
      ...
      dedupe: ['svelte']
      ...
    })
  ]
  ...
}

It worked! But why would I even need this?

The clue was actually in the experiment I did before where the component and the test project were in the same folder vs. in different folders.

When an obscure error like this happens, I often resort to deleting random code to see what impacts the error. So, I deleted the node_modules of the component folder. Then, I ran Rollup. Now it failed when bundling. Rollup complained that the component couldn't find Svelte. But why? It was just a component, not the whole project.

As it turns out, when Rollup builds a component, it will try to find the Svelte runtime by walking up the directory tree in some way (it could be looking for node_modules or package.json; I didn't dive deeper.). If the component and the test project are on 2 different folders, then there are 2 separate node_modules and 2 separate Svelte runtimes (even if they are of identical versions). That was how 2 Svelte runtimes were bundled.

This means we cannot use a local package that links to a folder. npm link also doesn't work because it goes through the same mechanism.

What works is npm pack which generates a tgz file. Then, you can do npm install <tgz_file> to install the package from the compressed file instead.

I've learned something new about how Svelte and a bundler work. Now you've learned it too 😄

Subscribe to tanin

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe