Skip to content
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

Svelte 5: using var with $state is brittle #12807

Closed
Rich-Harris opened this issue Aug 12, 2024 · 4 comments · Fixed by #12949
Closed

Svelte 5: using var with $state is brittle #12807

Rich-Harris opened this issue Aug 12, 2024 · 4 comments · Fixed by #12949
Labels
Milestone

Comments

@Rich-Harris
Copy link
Member

Describe the bug

State declarations are transformed thusly:

-let count = $state(0);
+let count = $.source(0);

If we tried to console.log(count) before the declaration, we would get a TDZ error:

Can't access lexical declaration 'count' before initialization

But if it's a var instead, we don't — instead we try to get(undefined).

-console.log(count);
+console.log($.get(count));

This errors with

signal is undefined

which is cryptic and unhelpful.

Possible remedies:

  1. add a if (signal === undefined) return signal to the top of get
    /**
    * @template V
    * @param {Value<V>} signal
    * @returns {V}
    */
    export function get(signal) {
    var flags = signal.f;
    if ((flags & DESTROYED) !== 0) {
    return signal.v;
    }
  2. disallow var
  3. hoist the declaration

Option 1 means doing a tiny bit of extra work on every get for something that's a real edge case. Option 2 feels like a bit of a cop-out. So I think we should probably do option 3.

Reproduction

link

Logs

No response

System Info

next

Severity

annoyance

@Rich-Harris Rich-Harris added this to the 5.0 milestone Aug 12, 2024
@trueadm
Copy link
Contributor

trueadm commented Aug 12, 2024

Can't we statically detect it's referenced before and just statically compile the read to undefined?

@Rich-Harris
Copy link
Member Author

Not trivially:

var a = get_b(), b = $state();

function get_b() {
  return b; // we certainly can't compile this to `undefined`
}

With sufficiently sophisticated static analysis we could determine that b could be read before declaration, and replace the read with return b === undefined ? undefined : $.get(b) but that would likely involve a lot more work, and be a lot less reliable, than hoisting

@Conduitry
Copy link
Member

Adjacent: #12900.

dummdidumm added a commit that referenced this issue Aug 21, 2024
- fixes #12807: state declared with var is now retrieved using a new `safe_get` function, which works like `get` but checks for undefined
- fixes #12900: ensure we're not creating another new scope for block statements of function declarations, which resulted in var declarations getting "swallowed" (because they were hoisted to the function declaration scope and never seen by our logic)
@dummdidumm
Copy link
Member

I opted for option 4 in #12949: Add a new safe_get function which works like get but checks for undefined. That way only var declarations get the tiny penalty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants