steveninety / svelte-email-tailwind

Code, preview and test-send email templates with Svelte and Tailwind classes and render them to HTML or plain text.
MIT License
10 stars 1 forks source link

Error: Missing <Head /> component! at substituteHead #8

Open socketopp opened 3 weeks ago

socketopp commented 3 weeks ago

Invite.svelte

<script>
    import { Button, Hr, Html, Text, Head } from 'svelte-email-tailwind';

    export let name = 'World';
</script>

<Html lang="en">
    <Head />
    <Text class="md:text-[18px] text-[24px]">
        Hello, {name}!
    </Text>
    <Hr />
    <Button href="https://svelte.dev">Visit Svelte</Button>
</Html>

I have been using it like this

import { render as svelteRender } from 'svelte/server';
import type { SvelteComponent } from 'svelte';
import InviteTemplate from '$lib/emails/Invite.svelte';

export async function sendSvelteEmail({ from, to, subject, svelteEmail: { props, template } }) {
    type InviteProps = {
        name: string;
    };

    const componentProps: InviteProps = {
        name: 'Steven3'
    };

    const { html } = svelteRender(InviteTemplate as unknown as typeof SvelteComponent, { props: componentProps });
    ....

However, I receive the following error in Svelte 5. Is there anything I can do to fix this?

Error: Missing <Head /> component!
    at substituteHead (file:/node_modules/svelte-email-tailwind/vite/utils/inline-tailwind.js:139:15)
    at inlineTailwind (file:/node_modules/svelte-email-tailwind/vite/utils/inline-tailwind.js:17:25)
    at TransformContext.transform (file:/node_modules/svelte-email-tailwind/vite/index.js:8:30)
    at Object.transform (file:/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:51139:62)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async loadAndTransform (file:/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:53894:29)
    at async instantiateModule (file:/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:54954:10) {
  plugin: 'vite:inline-tw',

Also the config require parameters


const emailTwConfig: TailwindConfig = {};
...
svelteEmailTailwind({ pathToEmailFolder: '/src/lib/emails', tailwindConfig: emailTwConfig })

Expected 1 arguments, but got 0.ts(2554)
index.d.ts(2, 45): An argument matching this binding pattern was not provided.
(alias) svelteEmailTailwind({ tailwindConfig, pathToEmailFolder }: {
    tailwindConfig?: TailwindConfig | undefined;
    pathToEmailFolder?: string | undefined;
}): {
    name: string;
    transform(src: string, id: string): Promise<{
        code: string;
    } | undefined>;
}
import svelteEmailTailwind
steveninety commented 3 weeks ago

I haven't looked into the Svelte 5 generated server JS output, but I suspect it has breaking changes. So that would make this plugin incompatible with Svelte 5. I will look into making it compatible. I'm afraid there will be a lot that needs changing!

socketopp commented 3 weeks ago

I haven't looked into the Svelte 5 generated server JS output, but I suspect it has breaking changes. So that would make this plugin incompatible with Svelte 5. I will look into making it compatible. I'm afraid there will be a lot that needs changing!

What parts in svelte-email-tailwind is related to the breaking changes in Svelte 5? I helped svelty-email with some breaking changes and it was only concerning Server API changes.

The error originates from:

function substituteHead(code, twClean) {
    // 3. Handle responsive head styles
    const headStyle = `<style>${getMediaQueryCss(twClean)}</style>`;
    // const hasResponsiveStyles = /@media[^{]+\{(?<content>[\s\S]+?)\}\s*\}/gm.test(headStyle)
    const startStringPre = '${validate_component(Head, "Head").$$render($$result,';
    const iS = code.indexOf(startStringPre);
    if (iS === -1) {
        throw new Error('Missing <Head /> component!');
    }

I am not sure why this error occurs or what the code is doing. The inline-tailwind.jsfile is quite complex. I couldn't imagine adding tailwind to svelte-email would be this complicated?

steveninety commented 3 weeks ago

What parts in svelte-email-tailwind is related to the breaking changes in Svelte 5?

This Vite plugin takes an email component's SSR code as a string, and manipulates the string. It looks for all 'class' properties, transforms those classes to styles, then put them back into the original stringified SSR code as a 'style' prop.

In Svelte 4, it would look something like this: ${validate_component(MyComponent, "MyComponent").$$render($$result, { classes: "text-sm" }, {}, {})} This plugin then looks for the pattern $$result, {, and subsequently looks for class properties. It's turned into: ${validate_component(MyComponent, "MyComponent").$$render($$result, { style: "font-size: 14px" }, {}, {})}

In Svelte 5, the SSR JS output is different: MyComponent($$payload, { classes: "text-sm" });

If we're lucky, the only syntax change is that '$$result' becomes '$$payload'. But I doubt that's all - I have to look into it.

PS: You can find these outputs on the Svelte REPL: in the JS "Output tab", with "Compiler options - generate" set to "SSR".

socketopp commented 3 weeks ago

Interesting approach! Hope your migration to Svelte 5 goes well.