svelte icon indicating copy to clipboard operation
svelte copied to clipboard

New component `root` property may throw errors

Open CatchABus opened this issue 4 years ago • 52 comments
trafficstars

Read this comment first

The reason why this occurs is that you are likely trying to use a pre-compiled (to JS) component that was compiled with a different Svelte version than the one you use, which is not supported. See this comment for more info and solutions: https://github.com/sveltejs/svelte/issues/6584#issuecomment-1019578529

Describe the bug

I recently experienced issues due to new root property in several svelte plugins. It seems that this line causes issues: https://github.com/sveltejs/svelte/commit/5cfefeb6e72f8085e418150b644cdc4b4f6f260d#diff-da9bae4e28c441de5ba3a074e30775fe69109100b3d921ad8f2592d93cd67b7f

It seems that a null check for parent_component variable is missing at that point.

on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(parent_component ? parent_component.$$.context : options.context || []), // Here, there is a null check for parent_component variable

// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root // Here there is no check for parent_component variable

Reproduction

This suddenly occured on certain svelte plugins.

Logs

No response

System Info

System:
    OS: Linux 5.8 Debian GNU/Linux 10 (buster) 10 (buster)
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
    Memory: 517.19 MB / 7.47 GB
    Container: Yes
    Shell: 5.0.3 - /bin/bash
  Binaries:
    Node: 16.0.0 - ~/.n/bin/node
    npm: 7.10.0 - ~/.n/bin/npm
  npmPackages:
    svelte: ^3.31.2 => 3.38.3 
    webpack: ^5.16.0 => 5.44.0

Severity

blocking an upgrade

CatchABus avatar Jul 27 '21 14:07 CatchABus

Experiencing the same issue. I had svelte app, which includes npm svelte component and it throws this error: image

This is my simplified code snippet:

/** app.js */
<script>
import Example from './example.svelte';

new Example({ target: document.body });
</script>

/** example.svelte **/
<script>
import Svelecte from 'svelecte';
</script>

<Svelecte></Svelecte> <!-- when I add component, it breaks -->

mskocik avatar Jul 27 '21 14:07 mskocik

It's strange that context property will check for existence of parent_component but the new root property won't. @mskocik I had the exact same error from plugins that had bundles using latest svelte version.

CatchABus avatar Jul 27 '21 21:07 CatchABus

This also occurs when sharing components with webpacks module federation. (In the current version of svelte, older versions work). An example is here: https://github.com/micschwarz/svelte-module-federation

micschwarz avatar Jul 29 '21 11:07 micschwarz

I've been racking my brain for the last 3 hours trying to figure out why my app suddenly started exploding... Eventually managed to find this issue. Is there a simple fix to reverting this bug so I can continue development until it's patched? I'm using esbuild-svelte which is pulling in a broken version I guess, so I can't just define an older version of svelte in my package.json as far as I'm aware?

For others trying to Google this issue, the console output for Chrome is:

Uncaught (in promise) TypeError: Cannot read property '$$' of null

And for Firefox:

Uncaught (in promise) TypeError: parent_component is null

nullbio avatar Jul 30 '21 10:07 nullbio

Try downgrading svelte version until it works again. Version 3.39.0 worked fine for me.

On 30. 7. 2021 12:01, nullbio wrote:

I've been racking my brain for the last 3 hours trying to figure out why my app suddenly started exploding... Is there a simple fix to reverting this bug so I can continue development until it's patched?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sveltejs/svelte/issues/6584#issuecomment-889783745, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGCU5FABFDMDYYYU3OTM6PTT2JZ7BANCNFSM5BCL6OBQ.

mskocik avatar Jul 30 '21 10:07 mskocik

Try downgrading svelte version until it works again. Version 3.39.0 worked fine for me. On 30. 7. 2021 12:01, nullbio wrote: I've been racking my brain for the last 3 hours trying to figure out why my app suddenly started exploding... Is there a simple fix to reverting this bug so I can continue development until it's patched? — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#6584 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGCU5FABFDMDYYYU3OTM6PTT2JZ7BANCNFSM5BCL6OBQ.

Thanks. Clearing my package-lock.json, node_modules, and adding "svelte": "3.39.0", to my package.json seems to have worked. Didn't think it would because in my build svelte is a dependency of esbuild-svelte, but I suppose in the land of node it enforces a version for sub-dependencies if you declare one explicitly. News to me. Just leaving this here for anyone else who needs it and is new to all of this like I am.

nullbio avatar Jul 30 '21 10:07 nullbio

This breaks on Snowpack's streaming imports where you don't have the svelte/compiler available and it just downloads ESM modules... so, for componentes that were already generated this way then there's no solution.

Sticking to v3.39.0 is fine if you're bundling all way long, but CDN-based approaches are failing due this.

Until #6646 gets merged CDN users are just blocked.

Thank you!

pateketrueke avatar Aug 22 '21 03:08 pateketrueke

does this issue solved? the same error occurs to me.

robin-shine avatar Aug 23 '21 12:08 robin-shine

Why is this code ever getting called with neither a target passed in to the constructor nor current_component/parent_component being set?

If it's one Svelte component trying to use another pre-compiled pre-bundled Svelte component, there are already other known issues with that (problems with the transitions scheduler, context not inheriting, probably others), and it's not something that's supported.

What's the use case here? Would the change in #6646 prevent this immediate crash, but still leave the other less-visible issues with apps being bundled with multiple copies of Svelte's internals?

Conduitry avatar Aug 27 '21 19:08 Conduitry

@Conduitry

What's the use case here?

Microfrontends are the use case :)

Would the change in #6646 prevent this immediate crash, but still leave the other less-visible issues with apps being bundled with multiple copies of Svelte's internals?

The change in #6646 would prevent this, yes. image Of course the other issues would still exist, but if you use svelte this way, you might be aware of them.

micschwarz avatar Aug 30 '21 11:08 micschwarz

A possibly related post on Reddit: https://www.reddit.com/r/sveltejs/comments/pjo902/svelte_microfrontend_module_federation_bug/?utm_medium=android_app&utm_source=share

dummdidumm avatar Sep 07 '21 19:09 dummdidumm

I'm getting same error and I've tried downgrading to 3.39.0 and using 3.43.0 latest but still get it:

image

My use case is I have a ui component library I'm attempting to prepare for publishing -- I've npm linked to a ui library which I've built out a dist/index.js via rollup -c and when I try to use it with following I get said error:

<script>
	import { Button } from 'agnosticui-svelte';
</script>

<main>
	<Button mode="primary" isBordered>Testing 1234</Button>
</main>
<style></style>

I've used https://github.com/joeattardi/svelte-tabs and built it out a similar way and it doesn't have the issue but it's locked back at 3.7.1:

    "svelte": {
      "version": "3.7.1",
      "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.7.1.tgz",
      "integrity": "sha512-MKjFy3YZ2pNUVPTwLNQ9JAVx6KVwfYnm/vbUF/pKLrfDDZiCBKfWPwnffpwGlnIAn7aS+aJoKV0soj464DOs4Q==",
      "dev": true
    },

I tried rolling all the way back to 3.7.1 in my project, and I still have same exact error. So that tells me perhaps there's something different (and wrong) between what I'm doing and what svelte-tabs.

UPDATE: I just deleted all my components and copied over the very simple svelte-tabs components and updated my example app to look exactly like his. I can reproduce same exact error with svelte v3.43.0.

Then, I backed down my package.json for the library and for the example app the was npm linked to "svelte": "3.39.0"`, removed both lock files and reinstalled. I was able to run the example. So, there's something specific in my components that still was causing the error even after downgrading; so I'll have to just add my components back one at a time to figure that out. However, it looks like this is definitely a bug on the latest version.

This seems like a blocker for anyone trying to develop a component library.

roblevintennis avatar Sep 24 '21 22:09 roblevintennis

Hello,

I do have the same issue in my app while trying to test a component which contains an "if" statement and a mock module. I did not have the issue back when I was using Svelte 3.35.0. I tried to upgrade yesterday to 3.43.0, but did not succeed. I had to rollback to 3.39.0.

To ease a bit the debug, here's a simplified repo to reproduce the issue: https://github.com/gcruchon/tests/tree/main/svelte-null-parent-component

I'm using last version of all libraries (svelte, jest, svelte-jester, babel, testing-library, ...). I used the svelte template and just added the bug (i.e. testing a mocked "Link" within an "if" statement)

Feel free to comment if I missed something.

gcruchon avatar Sep 25 '21 11:09 gcruchon

Re @mskocik I tested your example in a REPL and I don't see any errors.

Re streaming imports @pateketrueke I'm not familiar with it, but usually using CDN or pre-bundled code for Svelte components, they should always be instantiated with a target to mount to. Svelte doesn't guarantee components to work cross version AFAIK, so target would help "isolate" it.

Re jest mocks @gcruchon that seems like a bug of how Svelte components are mocked and interacting with the component initialization phase. I'm not familiar with how it works under-the-hood, but probably somewhere multiple svelte instances are created when being bundled (?).

I agree with Conduitry here. While the PR that fixes this works, I don't see any reason why the error would happen in the first place. Either options.target is defined for the component to mount to, or it has a parent_component. It shouldn't be dangling around without a reference. If anything, versions above 3.39.0 have surfaced many implementation bugs around the ecosystem, and we should fix them instead. And they largely seem to be components not being compiled/bundled correctly.

bluwy avatar Oct 21 '21 16:10 bluwy

@bluwy Cannot duplicate in the REPL neither. But it was happening. It was quite hard to replicate back then. But since when I tried 3.43.1 it worked for me since then.

mskocik avatar Oct 21 '21 18:10 mskocik

Re @mskocik that seems like a bug of how Svelte components are mocked and interacting with the component initialization phase. I'm not familiar with how it works under-the-hood, but probably somewhere multiple svelte instances are created when being bundled (?).

Thanks for acknowledging this bug.

If I’m not mocking svelte component in the right way, how would you recommend to mock a svelte component? Using an import of a much simpler .svelte file seems to me pretty simple and straightforward… how come this works without if statement and suddenly fails with an if?

I’m not a specialist of the svelte compiler, so I need more info to investigate. Can someone help me understand and improve the maturity of Svelte to embrace such software craftsmanship practices?

Do you have all what is necessary to reproduce the bug? (I see a label “need repro”)

gcruchon avatar Oct 24 '21 11:10 gcruchon

If I’m not mocking svelte component in the right way, how would you recommend to mock a svelte component?

The way you mock it now is correct, but I'm guessing there's a bug when transforming svelte files in jest. Might be an issue in https://github.com/mihar-22/svelte-jester.

Do you have all what is necessary to reproduce the bug? (I see a label “need repro”)

I'd say a no since the repros given so far are Svelte + [third-party integration] repros. Maybe it's fair as it only happens in these very specific scenarios, but it makes it hard to nail down what actually is the bug in Svelte. Or the real bug resides in that third-party integration.

bluwy avatar Oct 25 '21 02:10 bluwy

@nullbio I was also getting this error with Esbuild and it seemed to happen when I linked other libraries that also included svelte. Esbuild wasn't de-duping the svelte import, so I was getting multiple copies inside my bundle. I couldn't find a plugin to de-dupe svelte so added this inline one.

// part of esbuild config
plugins: [
    {
      name: 'dedupe-svelte',
      setup({ onResolve }) {
        const svelte = require.resolve('svelte')
        onResolve({ filter: /^svelte/ }, args => {
          let path = svelte.replace(/svelte\/[^\.]+.js$/,args.path+"/index.mjs")// .mjs for browser path
          return { path }
        })
      }
    },
    sveltePlugin({compileOptions: {css: true}})
  ],

Hope that helps.

crisward avatar Nov 28 '21 13:11 crisward

Just adding another data point for why this error occurred for me — I was consuming an internal design system where svelte was listed as a dependency rather than peerDependency, resulting in multiple instances existing in my app (verified by running yarn why svelte). Fixing that so only one svelte instance (regardless of version) was installed fixed the issue for me.

I agree with the sentiment that if anything recent versions of Svelte have just surfaced ecosystem issues. Could svelte throw a more useful error? From reading this thread seems it's often due to mismatching/several versions of svelte existing at once.

EDIT: This also happened when I yarn linked said design system for local development, presumably for the same reason, which is frustrating. I got around it by also linking the svelte instance in the design system, which feels hacky.

madeleineostoja avatar Dec 03 '21 01:12 madeleineostoja

Could svelte throw a more useful error?

Yes, we could do that as it's detectable.

This also happened when I yarn linked said design system for local development, presumably for the same reason, which is frustrating. I got around it by also linking the svelte instance in the design system, which feels hacky.

I have a design system component library as well and have not encountered this issue. Are you compiling the library into JS? Make sure to export raw Svelte files instead so that the consumer of that library can share the Svelte instance.

bluwy avatar Dec 04 '21 08:12 bluwy

For what it's worth, I'm going to add my boring story of how I came across this error 🤷 In a progressively-enhanced PHP app, I used to create svelte components regardless of whether the target existed or not, because one main.js file creates components on different pages that don't always use all of them. Svelte used to handle this gracefully, skipping those components that didn't have a valid target element. After I updated svelte from 3.38.3 to 3.44.2, I came across this error. Either I missed it while browsing through or this “change” in behavior wasn't mentioned in the changelog. 🤭

I added a conditional checking for the element to exist before creating a component, which seems more reasonable anyways.

Having gone through the debugging process, I can however confirm that catching the error and printing a more specific error message could be nice if both, target and parent are undefined for one or another reason.

te-online avatar Dec 04 '21 11:12 te-online

I've been having issues with this recently, such as when using svelte-loader + svelte with electron-forge image

FunctionalMetatable avatar Dec 18 '21 16:12 FunctionalMetatable

https://github.com/sveltejs/svelte/pull/6646 What about this pr? Its open for month now

micschwarz avatar Jan 14 '22 11:01 micschwarz

So this was breaking components that couldn’t be server-side rendered and so had to be instantiated using <svelte:component> in NodeKit.

In case anyone else comes across this Svelte bug while using their Svelte plugin with esbuild, you can apply the patch in #6646 in your plugin’s build onEnd() handler:

await esbuild.build({
  //…
  plugins: [
    sveltePlugin(route),
    {
      name: 'Apply patches',
      setup(build) {
        build.onEnd(result => {
          // Apply PR: https://github.com/sveltejs/svelte/pull/6646/files
          // by https://github.com/hgiesel
          if (result.outputFiles) {
            result.outputFiles.forEach((outputFile, index) => {
              // Note $$ is escaped as $$$$.
              const patchedResult = outputFile.text.replace(/root: options\.target \|\| parent_component\.\$\$\.root/g, 'root: options.target || (parent_component ? parent_component.$$$$.root : document)')
              result.outputFiles[index].contents = Buffer.from(patchedResult, 'utf-8')
            })
          }
        })
      }
    }
  ]
})

This does, however, slow down the esbuild build so I do hope that #6646 will be accepted.

In case it helps, I was testing with the svelte-boring-avatars and tsParticles components. Both break without the patch.

With the patch, svelte-boring-avatars works but, since the distribution build for tsParticles is minified, the patch doesn’t apply and that’s still failing.

So I guess the only option left is to patch Svelte itself.

aral avatar Jan 23 '22 19:01 aral

Esbuild wasn't de-duping the svelte import, so I was getting multiple copies inside my bundle.

It’s odd because I’m using a custom resolver in my esbuild to ensure that all Svelte references get mapped to the single instance of Svelte that’s in my app. And I’m still seeing this error (see above).

aral avatar Jan 23 '22 20:01 aral

The problem here is instantiating a component compiled with Svelte version X inside an app compiled with Svelte version Y in a declarative way.

The following is not guaranteed to work:

<CompiledComponent />
<svelte:element this={CompiledComponent} />

The following will work:

<script>
   //..import
   let el;
   onMount(() => new CompiledComponent({target: el, props: {..}));
</script>
<div bind:this={el} />

If using a component from a library, ensure that an uncompiled version exists and is used by your build tool. SvelteKit's package command ensures this for example.

dummdidumm avatar Jan 23 '22 22:01 dummdidumm

@dummdidumm Hey Simon, thank you, that does indeed work. I’m going to see how I can incorporate checking if the source exists for a component and using that in my loader’s resolution. I’m assuming that’s what the svelte entries in the package.json files are for. Appreciate your help.

aral avatar Jan 24 '22 10:01 aral

If it's one Svelte component trying to use another pre-compiled pre-bundled Svelte component, there are already other known issues with that (problems with the transitions scheduler, context not inheriting, probably others), and it's not something that's supported.

@Conduitry There should be a big warning in the Svelte doc that using pre-bundled Svelte components is not supported. It's a blocker for our project and probably for some others too.

jindrahm avatar Apr 01 '22 12:04 jindrahm

@jindrahm Agreed; @Conduitry this could also be mentioned in the Svelte FAQ, or Svelte Society. I'm trying to downgrade as well; I'm just trying to re-spin up nodemon real quick.

ghost avatar May 24 '22 02:05 ghost

:heavy_check_mark: Got it on my end; make sure to run:

> npm i -s svelte@^3.39.0
# for yarn:
> yarn add svelte@^3.39.0

So npm/yarn knows to downgrade.

However, I can see this tripping other newbies up as there's nothing mentioned in the Svelte docs/FAQ/Society; I think this is very essential for the Svelte ecosystem/community to be aware of as an error like this can hinder the modularity of a Svelte app.

ghost avatar May 24 '22 03:05 ghost