vue-email / vue-email

💌 Write email templates with vue
https://vuemail.net
MIT License
900 stars 33 forks source link

Nuxt compiler bug in production #168

Closed HugoRCD closed 3 months ago

HugoRCD commented 6 months ago

After deploying my project on Vercel, I often encounter this error. Each time, I end up having to redeploy the project to fix it. I was wondering if you might have any insights on what could be causing this issue.

CleanShot 2024-03-18 at 10 51 12@2x

Here is all piece of code related:

resendService.ts

import { useCompiler } from '#vue-email';
import { Resend } from "resend";

const resend = new Resend(process.env.NUXT_PRIVATE_RESEND_API_KEY);

export async function sendOtp(email: string, otp: string) {
  const runtimeConfig = useRuntimeConfig();
  const siteUrl = runtimeConfig.public.siteUrl;
  const template = await useCompiler('verify-otp.vue', {
    props: {
      otp,
      redirectUrl: `${siteUrl}/login?email=${email}&otp=${otp}`,
    }
  });

  try {
    await resend.emails.send({
      from: "HugoRCD <contact@hrcd.fr>",
      to: [email],
      subject: "Welcome to Shelve!",
      html: template.html,
    });
  } catch (error) {
    console.error(error);
  }
}

./emails/verify-otp.vue

<script setup lang="ts">
defineProps({
  otp: {
    type: String,
    required: true
  },
  redirectUrl: {
    type: String,
    required: true
  }
});
</script>

<template>
  <ETailwind>
    <EHtml>
      <EHead />
      <EPreview>
        Here is your code to access Shelve
      </EPreview>
      <EBody class="m-auto bg-white font-sans">
        <EContainer class="mx-auto my-[40px] max-w-[465px] rounded border border-solid border-[#eaeaea] p-[20px] md:p-7">
          <EHeading class="mb-4 text-center text-2xl font-bold text-[#000000] md:text-3xl">
            Welcome to Shelve
          </EHeading>
          <EText class="mb-4 text-center text-[#000000]">
            Your OTP is:
          </EText>
          <EText class="mb-4 text-center text-[36px] font-bold text-[#000000]">
            <span class="text-[#2C3BF5]">{{ otp }}</span>
          </EText>
          <EText class="mb-4 text-center text-[#000000]">
            Use this OTP to verify your account
          </EText>
          <ESection class="my-[32px] text-center">
            <EButton px="20" py="12" class="rounded bg-[#000000] text-center text-[12px] font-semibold text-white no-underline" :href="redirectUrl">
              Go to Shelve
            </EButton>
          </ESection>
          <EText class="text-[14px] leading-[24px] text-black">
            or copy and paste this URL into your browser:
            <ELink :href="redirectUrl" class="text-[#2C3BF5]">
              {{ redirectUrl }}
            </ELink>
          </EText>
          <EHr class="mx-0 my-[26px] w-full border border-solid border-[#eaeaea]" />
          <EText class="text-[12px] leading-[24px] text-[#666666]">
            If you were not expecting this invitation, you can ignore this email. If you are concerned about your account's safety, please reply to this email to get in touch
            with us.
          </EText>
        </EContainer>
      </EBody>
    </EHtml>
  </ETailwind>
</template>

<style scoped>

</style>
letstri commented 6 months ago

Have the same problem but with config from @vue-email/compiler

eliabieri commented 5 months ago

I'm facing the exact same problem

dcln00 commented 5 months ago

I'm also facing the exact same problem

GerryWilko commented 5 months ago

This issue appears to be that the compile function's tries to resolve packages with cjs so the error that comes through is actually:

Error: Cannot find module '/Users/gerard/Developer/ignite/auth-service/apps/auth-service/.output/server/node_modules/vue-email/dist/index.cjs'
    at createEsmNotFoundErr (node:internal/modules/cjs/loader:1181:15)
    at finalizeEsmResolution (node:internal/modules/cjs/loader:1169:15)
    at resolveExports (node:internal/modules/cjs/loader:591:14)
    at Module._findPath (node:internal/modules/cjs/loader:668:31)
    at Module._resolveFilename (node:internal/modules/cjs/loader:1130:27)
    at Module._load (node:internal/modules/cjs/loader:985:27)
    at Module.require (node:internal/modules/cjs/loader:1235:19)
    at require (node:internal/modules/helpers:176:18)
    at /Users/gerard/Developer/ignite/auth-service/apps/auth-service/.output/server/node_modules/import-string/dist/YEEztG6s8DrZXPhaeh8D1.js:36:24
    at Script.runInContext (node:vm:133:12) 

However the vue-email that is copied to the built output contains only the .mjs index file so this error is thrown.

Just investigating how it ends up only pulling over the .mjs file now or perhaps trying to force resolution to be ESModule.

xlanex6 commented 5 months ago

So I'am

GerryWilko commented 5 months ago

So in my case this issue was caused by the imports in my email component. It seems you are unable to import vue-email inside here. When removing the vue-email imports e.g.

import {
  EBody,
  EButton,
  EColumn,
  EContainer,
  EHead,
  EHeading,
  EHtml,
  EImg,
  EPreview,
  ERow,
  ESection,
  EText,
} from 'vue-email'

Then I no longer have this error in production. Hope this helps someone!

jonatandorozco commented 5 months ago

@GerryWilko if you remove the imports, how do get the components? If you can share details about how you were able to solve than can be helpful

GerryWilko commented 5 months ago

They appear to be auto imported. Looking inside the compile function i noticed that they seemed to be part of the auto imported code. If you check the examples on the Vue-Email site it does show them without the explicit imports. I have a feeling this may be a recent change as I think I took one of the examples and modified it for my own use case and now they seem to have no imports in the examples. Or my IDE was trying to be helpful and added them automatically.

To be clear my email templates work fine in the IDE and in production with no imports so they arent needed and so seem to be the source of the error listed in this issue.

Gugustinette commented 5 months ago

Removing the imports didn't work on my side 😕 Still getting the exact same error on production.

Btw I'm on "@vue-email/nuxt": "^0.8.19"

Also, I'm really surprised this could have an impact on the error, as the components not being found are the Vue Mails we are writing, not the components imported inside.

dcln00 commented 5 months ago

What worked for me was deleting the node modules directory with package-lock.json file and running npm install again.

Gugustinette commented 5 months ago

So I digged some of the code :

The first esbuild-linux-64 error appears because of the catch here :

https://github.com/vue-email/compiler/blob/1267312f408b63b6d0ef1a27a94e890f932893fd/src/template.ts#L120-L133

async function loadComponent(name: string, source: string, verbose = false) {
  try {
    name = correctName(name)
    const compiledComponent = compile(name, source, verbose)
    const componentCode: Component = (await importModule(compiledComponent)).default

    return componentCode
  }
  catch (error) {
    console.error('Error loading component', error)
  }

  return null
}

Then the second error appears because the previous function returned null after the error catched :

https://github.com/vue-email/compiler/blob/1267312f408b63b6d0ef1a27a94e890f932893fd/src/template.ts#L45-L51

    const component = await loadComponent(name, code.source, verbose)

    if (verbose)
      console.warn(`${lightGreen('💌')} ${bold(blue('Generating output'))}`)

    if (!component)
      throw new Error(`Component ${name} not found`)

This seems to be related to the compile function from @vue/compiler-sfc throwing an error when not finding the esbuild-linux-64 (because esbuild is used here at runtime to compile the template). So this is not directly related to a vue-email behavior. But we still don't know why is esbuild-linux-64 not installed in the vercel context.

Gugustinette commented 5 months ago

Sooo I didn't sleep BUT I FIXED IT.

esbuild Source Code

Here is the piece of code from esbuild that crashes when vue is rendering the template. Obviously, it tries to resolve the package dynamically, and it just isn't found in the vercel context. However, it's pretty weird to me as it should be something like ${pkg}-${subpath} in this version but anyway. https://www.npmjs.com/package/esbuild/v/0.15.18?activeTab=code

Capture d’écran 2024-04-13 à 07 22 14

The issue

I was digging a lot on how the package couldn't be found, even when we were trying to force install the esbuild-linux-64 package. Even adding it locally on your repo won't fix the issue.

This is basically because Nuxt won't include it in the built node_modules folder when running nuxt build with the vercel preset, that compiles to serverless functions. I digged a lot about how that damn node_modules folder should look (and where it should be) in serverless functions and found some information here : https://vercel.com/docs/build-output-api/v3/primitives#config-example

Capture d’écran 2024-04-13 à 07 29 00

Every serverless function should have the node_modules folder at its root. However, Nitro (which powers Nuxt) compiles to only one serverless function called __nitro.func. Which means esbuild-linux-64 package was just missing from .vercel/output/functions/__nitro.func/node_modules.

So esbuild-linux-64 is correctly installed when running npm install during Vercel build time. It is just not included by Nitro on build step.

The solution

I still don't know how to solve it correctly using Nitro config. But here is the dirty solution I used during my debugging time : package.json

{
...
"scripts": {
    "build": "nuxt build && cp -r ./node_modules/esbuild-linux-64 ./.vercel/output/functions/__nitro.func/node_modules/esbuild-linux-64",
  },
...
}

Basically modify your build command to manually copy the missing package from the "build node_modules" to the "serverless node_modules".

I'm sure there is a way to just tell Nitro to include the package, but for now I'm too lazy and need to sleep. 🥹

CernyMatej commented 5 months ago

I feel like Tailwind doesn't really work with this solution, does it?

Gugustinette commented 5 months ago

I feel like Tailwind doesn't really work with this solution, does it?

who tf uses Tailwind anyway

Joke aside, are you facing the issue even with the fix ? Did you try to remove Tailwind to see if it was really breaking it ? Any errors ?

Maybe there's also an error with Tailwind not being included in the node_modules, which would require to also cp -r your Tailwind dependencies. But I don't know much about how Tailwind source/build will behave in this context as I've never used it.

CernyMatej commented 5 months ago

Well if I understand it correctly, this module converts tailwind classes to inline styles at some point so I don't know whether it actually needs the tw source or not. Maybe it is missing from the node_modules...

When I removed the explicit component imports & used your build copy command the mails do send. It's only the styles that are missing.

I'll experiment with it when I have some time

xlanex6 commented 5 months ago

I feel like Tailwind doesn't really work with this solution, does it?

who tf uses Tailwind anyway

Joke aside, are you facing the issue even with the fix ? Did you try to remove Tailwind to see if it was really breaking it ? Any errors ?

Maybe there's also an error with Tailwind not being included in the node_modules, which would require to also cp -r your Tailwind dependencies. But I don't know much about how Tailwind source/build will behave in this context as I've never used it.

@Gugustinette

I try to modify the build command but it still not work I use Tailwind also

JeremyMees commented 4 months ago

I get the same error using Netlify.

Gugustinette commented 4 months ago

@JeremyMees

Can you provide logs ?

I don't know much about Netlify, if you are deploying to a serverless environment, the solution could be similar to Vercel :

{
...
"scripts": {
    "build": "nuxt build && cp -r ./node_modules/esbuild-linux-64 ./.vercel/output/functions/__nitro.func/node_modules/esbuild-linux-64",
  },
...
}

(Of course, directories will probably be named differently)

JeremyMees commented 4 months ago

@Gugustinette

These are the logs i get from Netlify

May 29, 12:55:05 PM: 1b92f110 ERROR  Error loading component Error: The package "esbuild-linux-64" could not be found, and is needed by esbuild.

If you are installing esbuild with npm, make sure that you don't specify the
"--no-optional" or "--omit=optional" flags. The "optionalDependencies" feature
of "package.json" is used by esbuild to install the correct binary executable
for your current platform.
    at generateBinPath (/var/task/node_modules/esbuild/lib/main.js:1819:15)
    at esbuildCommandAndArgs (/var/task/node_modules/esbuild/lib/main.js:1886:33)
    at ensureServiceIsRunning (/var/task/node_modules/esbuild/lib/main.js:2051:25)
    at startSyncServiceWorker (/var/task/node_modules/esbuild/lib/main.js:2261:19)
    at Object.<anonymous> (/var/task/node_modules/esbuild/lib/main.js:2302:3)
    at Module._compile (node:internal/modules/cjs/loader:1358:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
    at Module.load (node:internal/modules/cjs/loader:1208:32)
    at Module._load (node:internal/modules/cjs/loader:1024:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12)May 29, 12:55:05 PM: 1b92f110 ERROR  [nuxt] [request error] [unhandled] [500] Error rendering template ReservationSuccess: Error: Component ReservationSuccess not found
  at Object.handler (./chunks/routes/api/index.post.mjs:61:11)  
  at async Object.handler (./chunks/runtime.mjs:3084:19)  
  at async toNodeHandle (./chunks/runtime.mjs:3350:7)  
  at async lambda (./chunks/runtime.mjs:16990:13)
Gugustinette commented 4 months ago

@JeremyMees

As far as I know from Netlify, this could be solved with this build command :

nuxt build && cp -r ./node_modules/esbuild-linux-64 ./.netlify/functions-internal/server/node_modules/esbuild-linux-64
IsraelOrtuno commented 3 months ago

I am experiencing the same issue in Vercel. I could not make it work with the build script

Flowko commented 3 months ago

we just merged a new project rewrite, please do check the docs and the updated logic https://vuemail.net/