sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.19k stars 4.09k forks source link

Possibility of writing snippets in .svelte.ts files #12713

Open webJose opened 1 month ago

webJose commented 1 month ago

Describe the problem

AFAIK, writing snippets outside of Svelte component files is not possible. However, unit testing would greatly benefit from being able to define snippets in .svelte.ts files. Right now, we either create a helper component for the test, or use createRawSnippet API.

Describe the proposed solution

Be capable of writing snippets in codefiles anywhere (top level, inside function bodies, etc.), as if they were functions or similar.

Importance

nice to have

paoloricciuti commented 1 month ago

Isn't createRawSnippet exactly this?

Conduitry commented 1 month ago

createRawSnippet is the API for this, yes. I don't know what we would want to do differently in a .svelte.js file. If you want to write something that looks like a component, use a .svelte file. If you want to programmatically create a snippet, use createRawSnippet.

Rich-Harris commented 1 month ago

In the medium term it would be nice to be able to export snippets from Svelte components, perhaps like this:

<script context="module">
  export { foo };
</script>

{#snippet foo()}...{/snippet}

There's no intention to create a way to define snippets declaratively outside .svelte files though. Creating them programmatically with createRawSnippet is the way to go.

webJose commented 1 month ago

I understand. It is just that the {#snippet} construct is easier than the raw API. Also, a question (or maybe a point in favor of allowing this): How does one create a snippet with createRawSnippet that includes a component, not just HTML elements? How would that look like?

And finally, the one other ask I have in my mind is the ability to unit-test the effects of bind:. I think libraries like @testing-library/svelte would require a public API that would allow this. I know, different topic, but since I got your attention, I figured I should ask.

webJose commented 1 month ago

Rich's medium term solution would be very good: One could then create .test.svelte component files that define and export the snippets for testing. Then it is a matter of importing them in the .ts/.svelte.ts test file.

rChaoz commented 1 month ago

Also, I believe this is a duplicate of #11261, which I opened a long time ago, and got downvoted into oblivion. I personally really like the idea but from the community's response it's really not good

Antonio-Bennett commented 1 month ago

@rChaoz I think ppl disliked the syntax maybe not so much the idea

rChaoz commented 1 month ago

I understand, but the issue has the syntax as a suggestion. The issue itself was to add some way to create snippets in *.svelte.js files. Anyhow, what would a good syntax be, that's better that the current API?

webJose commented 1 month ago

As Rich is proposing is good enough for me. No need for new fancy syntax, IMHO. Adding the ability to export snippets is an excellent midpoint since component files are not required to have templates or script tags. One can create, today, a snippet-only component file. All we need is the export part.

rChaoz commented 1 month ago

But can't snippets depend on component props? How to handle that case?

Antonio-Bennett commented 1 month ago

@rChaoz Since snippets are pretty much functions in my head you just have to ensure where you are using that exported snippet just pass the required params

webJose commented 1 month ago

I'm with Antonio: Regular snippets are not for exporting. Exported snippets are exported for a reason. Let it be part of the requirements that they need to be self-contained. Any dependencies, via the parameters.

rChaoz commented 1 month ago

I understand, my question is: should the following be prohibited? It feels like it would be a huge loss to me. Or, how should svelte figure out whether it can be exported?

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAA0WOzQrCMBCEXyVEDy0V6rm2RY_evVkPNd1iMN2EZCNIyLub_oC3meGbYQIfpQLHq3vg2E_AK34xhh84fc1s3AcUQfJOeyvmpHbCSkNthx0pICa0R2IN2zvqCbJjfuqwLv8Qhp1DaUxCXZbHDhm7vWCrScfColIeyo2LS-tsAQewWyltPj2RRqZRKCneTchy1rTrTlHE5c8VhYUJkOYLK9-m75Me5Chh4BVZD_ERfwu31dD2AAAA

Antonio-Bennett commented 1 month ago

My perspective is that context module is for the purpose of having shared state so therefore create a shared state to be used. Not sure if then you can use the new $state.link rune that’s coming to keep shared state in sync with a parent value passed into the component?

webJose commented 1 month ago

Why would you want to export that snippet? What would be the use case for that? I can only imagine exporting for reuse, if at all. After all, components are superior. I just want exported snippets for unit testing because one consume them in TS/JS files. For application development, I consume from .svelte files, so why would I go the route of snippets when I can go the route of components?

7nik commented 1 month ago

I understand, my question is: should the following be prohibited? It feels like it would be a huge loss to me. Or, how should svelte figure out whether it can be exported?

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAA0WOzQrCMBCEXyVEDy0V6rm2RY_evVkPNd1iMN2EZCNIyLub_oC3meGbYQIfpQLHq3vg2E_AK34xhh84fc1s3AcUQfJOeyvmpHbCSkNthx0pICa0R2IN2zvqCbJjfuqwLv8Qhp1DaUxCXZbHDhm7vWCrScfColIeyo2LS-tsAQewWyltPj2RRqZRKCneTchy1rTrTlHE5c8VhYUJkOYLK9-m75Me5Chh4BVZD_ERfwu31dD2AAAA

It's evident that the snippet references a variable defined in the script, so it cannot be hoisted to the context script and exported. The compiler can easily see it.


For curious ones, a close to the proposed solution workaround. Plus, the solution is already proposed in #10350.

paoloricciuti commented 1 month ago

Btw i started fiddling around with the changes to allow exporting snippets from the module and i think there's a decent blocker for it: the module is parsed by acorn that errors out if you are trying to export something that is not there. This is also true for the normal script.

So either one limitation is that you can only export a getter for the snippets in the form of function or we should find a way around this error (which seems pretty annoying because you want acorn to error out if you are exporting something that is not there and at the parsing phase we still don't know if the snippet could be hoisted or not).

KevsRepos commented 4 weeks ago

Wild idea:

{#snippet export foo()}
{/snippet}

Or

{@export foo}

{#snippet foo()}
{/snippet}

Besides the fact that even I dont like the syntax, could the language server even recognize foo being exported? Another idea:

<script>
export let snippets = $snippets(foo); // exports object containing passed snippets
</script>

{#snippet foo()}
{/snippet}