svelte icon indicating copy to clipboard operation
svelte copied to clipboard

feat: allow runes in POJO properties

Open Ocean-OS opened this issue 8 months ago • 7 comments

This is currently an experiment to see how well runes could work as properties in POJOs. This is similar to #11210, but much more seamless. This would enable explicit fine-grained reactivity in objects, without proxying them. Here's an example:

let counter = {
    count: $state(0);
}

This would get compiled to an IIFE, returning something like this:

let counter = (() => {
    let $$1 = $.state(0);
    return {
        get count() {
            return $.get($$1);
        },
        set count($$value) {
            $.set($$1, $$value, true);
        }
    }
})();

Before submitting the PR, please make sure you do the following

  • [ ] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • [x] Prefix your PR title with feat:, fix:, chore:, or docs:.
  • [x] This message body should clearly illustrate what problems it solves.
  • [ ] Ideally, include a test that fails without this PR but passes with it.
  • [x] If this PR changes code within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • [x] Run the tests with pnpm test and lint the project with pnpm lint

Ocean-OS avatar Mar 22 '25 18:03 Ocean-OS

Preview: https://svelte-dev-git-preview-svelte-15593-svelte.vercel.app/

svelte-docs-bot[bot] avatar Mar 22 '25 18:03 svelte-docs-bot[bot]

Playground

pnpm add https://pkg.pr.new/svelte@15593

github-actions[bot] avatar Mar 22 '25 18:03 github-actions[bot]

🦋 Changeset detected

Latest commit: 1b0918c31c7dceead1fdf493222b0cdd06c2e22c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar Mar 22 '25 18:03 changeset-bot[bot]

I think this references should be working, try to break them to see if there's any issues.

Ocean-OS avatar Mar 22 '25 19:03 Ocean-OS

I apparently forgot how this works in POJOs, so unfortunately this doesn't work anymore, you would have to do something like this instead. It was a nice shortcut, but we probably wouldn't want to confuse people by not following JavaScript's rules.

Ocean-OS avatar Mar 23 '25 23:03 Ocean-OS

If wrapping is needed, then there's a simpler solution using closures, using the currently available Svelte features.

notramo avatar Apr 15 '25 13:04 notramo

I have two examples that I would use this in the real world, and would love to see this happen! Thanks for your work on it @Ocean-OS!

Sveltekit-Superforms Scenario

I has creating a wrapper function around the superForm() function, so I could return a separate open state along side my form (for forms in collapse and modal components).

function newSuperForm(form, formOptions) {
    const newForm = superForm(form, formOptions)
    return {
        ...newForm,
        open: $state(false),
    }
}

As this syntax isn't avaliable atm, I'm making due with this.

function newSuperForm(form, formOptions) {
    const newForm = superForm(form, formOptions)
    return {
        ...newForm,
        get open() {
            return open
        },
        set open(newOpen) {
            open = newOpen
        }
    }
}

Passing values as $derived to functions

When passing values to functions its helpful to declare them explicitly as $derived or there was reactivity issues (could definitely be a skill issue though).

const addressForm = formBuilder(
    () => $derived({ ...reactiveAddressObject, _id: databaseId });,
    addressFormSchema,
);

I'm currently using a temp variable for the $derived call atm:

let addressFormInitialData = $derived({ ...reactiveAddressObject, _id: databaseId });
const addressForm = formBuilder(
    () => addressFormInitialData,
    addressFormSchema,
);

Edit: After some digging this example actually doesn't change anything, but the point still stands (even if the code doesn't).

Conclusion

I hope this comment adds constructively to the conversation, and not destructively to the chaos.

Tyler-Petrov avatar Jun 16 '25 14:06 Tyler-Petrov