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

feat!: support Svelte 5 #51

Merged
merged 21 commits into from
Jan 23, 2025
Merged

feat!: support Svelte 5 #51

merged 21 commits into from
Jan 23, 2025

Conversation

kvangrae
Copy link
Contributor

This pull request provides support for Svelte 5 runes mode. It is not backwards compatible for Svelte 4.

Summary of changes:

  • Dependencies have been upgraded to support Svelte 5.
  • Compiler has been set to Runes mode.
  • Code updated to use runes.

Closes #48

src/Time.svelte Outdated

import { dayjs } from "./dayjs";
import { onMount } from "svelte";

/** @type {undefined | NodeJS.Timeout} */
let interval = undefined;
let liveUpdate = $state(0);

const DEFAULT_INTERVAL = 60 * 1_000;

onMount(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this should be called onDestroy https://svelte.dev/docs/svelte/lifecycle-hooks

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually would be better to move it into $effect

$effect(() => {
		// This will be recreated whenever `milliseconds` changes
		const interval = setInterval(() => {
			count += 1;
		}, milliseconds);

		return () => {
			// if a callback is provided, it will run
			// a) immediately before the effect re-runs
			// b) when the component is destroyed
			clearInterval(interval);
		};
	});
	```

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. that should be easy. i'll have a look tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have updated the code to return the clearInterval in $effect return statement.

Copy link

@niemyjski niemyjski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I think we could clean up the onmount and use the function return of $effect

@niemyjski
Copy link

cc @metonym

src/Time.svelte Outdated

import { dayjs } from "./dayjs";
import { onMount } from "svelte";

/** @type {undefined | NodeJS.Timeout} */
let interval = undefined;
let liveUpdate = $state(0);

const DEFAULT_INTERVAL = 60 * 1_000;

onMount(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. that should be easy. i'll have a look tomorrow.

src/Time.svelte Outdated
Comment on lines 62 to 65
let formatted = $derived.by(() => {
liveUpdate; // no-op. this is a dependency trigger for live updates
return relative ? dayjs(timestamp).from() : dayjs(timestamp).format(format);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that we need the liveUpdate $state rune b/c if any of the props change, formatted should get updated (i think...). it is something i can test. if not, this would simplify some things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have removed liveUpdate as it does not have any adverse impacts on live updates.

Comment on lines +39 to 51
$effect(() => {
/** @type {undefined | NodeJS.Timeout} */
let interval;
if (relative && live !== false) {
interval = setInterval(
() => {
formatted = dayjs(timestamp).from();
},
Math.abs(typeof live === "number" ? live : DEFAULT_INTERVAL),
);
}
return () => clearInterval(interval);
});
Copy link

@niemyjski niemyjski Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait how does this even work. If this is setting formatted, but formatted is derived. Guessing this one takes over the derived instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call. it does work, but i agree, it doesn't makes sense for me to update a derived value. i'm thinking formatted should be a $state instead of a $derived. if that makes sense, let me know and i will update.

but, in this instance, the derived will only be updated if relative, live, or timestamp ever change, which, most likely, they won't change.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either is fine, I think that if we update it the initial state needs to be set in the effect?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i found that in $effect any values that are read asynchronously, for example in a setInterval, will not be tracked.

https://svelte.dev/docs/svelte/$effect#Understanding-dependencies

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, it doesn't look like it is recommended to set the initial state inside of the $effect. my intellij complains with the following:
image

Copy link
Contributor Author

@kvangrae kvangrae Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with what i found out above, i'm thinking to either keep it the way it is or change formatted to use $state instead of $derived. let me know your thoughts.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably using state would be good, whatever keeps it simple and easy to understand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link

@niemyjski niemyjski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, left one comment.

@niemyjski
Copy link

@metonym can we get a new release and this merged? Is there anything else that should be updated like Dayjs?

@metonym
Copy link
Owner

metonym commented Dec 15, 2024

Nice work, thanks both.

Will review – I think one thing that'll need updating is the demo, for which it breaks entirely for Svelte 5. It could be a nice opportunity to switch to Vite instead of Rollup.

@niemyjski
Copy link

@metonym can we get this merged and that gets followed up on?

@kvangrae
Copy link
Contributor Author

Nice work, thanks both.

Will review – I think one thing that'll need updating is the demo, for which it breaks entirely for Svelte 5. It could be a nice opportunity to switch to Vite instead of Rollup.

I've updated and ran the examples. I could successfully run and verify sveltekit and vite. I ran into issues with the following examples:

  • rollup - npm run dev I couldn't find what port it was running on to verify. I was expecting it to be running on default port 5000.
  • webpack - npm run dev complains "Module not found: Error: Package path . is not exported from package". I'm not good with webpack and wasn't sure exactly how to resolve it.

Any help would be appreciated.

package.json Outdated
".": {
"svelte": "./src/index.js"
}
},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exports condition is not needed, as it's dynamically generated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed. i originally added it due to a compiler warning

@niemyjski
Copy link

@metonym can we get this merged?

@metonym
Copy link
Owner

metonym commented Jan 23, 2025

@niemyjski See my comment: #48 (comment)

@metonym
Copy link
Owner

metonym commented Jan 23, 2025

I'm reviewing this now, and pushing my changes:

  • Fix some "self-closing" tag warnings emitted in the tests: 7470d8c
  • SvelteComponentTyped is also deprecated. I've followed the migration guide and replaced it with Component cdb585b

My todo:

  • Make sure demo can run
  • Make sure each example works with Svelte 5

@metonym
Copy link
Owner

metonym commented Jan 23, 2025

Another change b40a4bf

I've updated the Svelte action to use $effect as recommended by the docs:

Prior to the $effect rune, actions could return an object with update and destroy methods, where update would be called with the latest value of the argument if it changed. Using effects is preferred.

The file extension was updated to svelte.js because it uses a Rune. No changes in the barrel export or usage.

Copy link
Owner

@metonym metonym left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also got the demo working again using Astro, as another affirmation that the established examples work with Svelte 5 + Runes mode.

I went through each example, linking it locally and making sure it could run. I wasn't able to quite link the Webpack 5 example, so I ended up testing it manually via copy/paste.

I also cut a stable v1 release on the existing master branch. This PR will be a v2.

I also updated the README in this PR, making an explicit note that going v2+ is Svelte 5 only, while Svelte 3/4 should use v1.

@metonym metonym changed the title Support Svelte 5 Runes mode feat!: support Svelte 5 Jan 23, 2025
@metonym metonym merged commit f986943 into metonym:master Jan 23, 2025
1 check passed
@metonym
Copy link
Owner

metonym commented Jan 23, 2025

@kvangrae @niemyjski Thanks for your patience and all the work that went into this!

Released in v2.0.0

After release, I've done the following:

@niemyjski
Copy link

Thank you!!!

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

Successfully merging this pull request may close these issues.

Cannot use $$restProps in runes mode (Svelte 5)
3 participants