nuxt-generation-emails 1.0.7 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,7 +46,7 @@ const props = withDefaults(defineProps<{
46
46
 
47
47
  /**
48
48
  * useNgeTemplate auto-loads the sibling .mjml file, compiles it with
49
- * Handlebars, and returns a reactive ComputedRef<string> of rendered HTML.
49
+ * Handlebars, and sets the render function \u2014 no <template> block needed.
50
50
  * MJML components from components/ are registered automatically.
51
51
  */
52
52
  useNgeTemplate('${templatePath}', props)
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "1.0.7",
7
+ "version": "1.1.1",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -623,7 +623,7 @@ export default defineEventHandler(async (event) => {
623
623
  const mjmlSource = readFileSync(join(emailsDir, '${emailPath}.mjml'), 'utf-8')
624
624
  const compiledTemplate = Handlebars.compile(mjmlSource)
625
625
  const mjmlString = compiledTemplate(templateData)
626
- const { html, errors } = mjml2html(mjmlString)
626
+ const { html, errors } = await mjml2html(mjmlString)
627
627
 
628
628
  if (errors && errors.length > 0) {
629
629
  throw new Error('MJML compilation errors: ' + errors.map(e => e.formattedMessage).join('; '))
@@ -789,15 +789,18 @@ export function registerMjmlComponents(): void {
789
789
  addTemplate({
790
790
  filename: "nge/use-template.ts",
791
791
  write: true,
792
- getContents: () => `import { computed, h, getCurrentInstance } from 'vue'
793
- import type { ComputedRef } from 'vue'
792
+ getContents: () => `import { h, ref, watch, toRef, getCurrentInstance } from 'vue'
793
+ import type { Ref, VNode } from 'vue'
794
794
  import Handlebars from 'handlebars'
795
795
  import { registerMjmlComponents } from './register-components'
796
796
 
797
- // Lazy-load mjml-browser only on the client to avoid "window is not defined" during SSR
798
- let mjml2html: ((mjml: string) => { html: string; errors: unknown[] }) | null = null
797
+ // Lazy-load mjml-browser only on the client to avoid "window is not defined" during SSR.
798
+ // mjml-browser v5+ returns a Promise, so we store the convert function in a ref
799
+ // and resolve the HTML asynchronously via watchEffect.
800
+ type Mjml2Html = (mjml: string) => Promise<{ html: string; errors: unknown[] }>
801
+ const mjml2html: Ref<Mjml2Html | null> = ref(null)
799
802
  if (import.meta.client) {
800
- mjml2html = (await import('mjml-browser')).default
803
+ import('mjml-browser').then(m => { mjml2html.value = m.default })
801
804
  }
802
805
 
803
806
  const mjmlTemplates: Record<string, string> = import.meta.glob(
@@ -820,51 +823,56 @@ let _componentsRegistered = false
820
823
  /**
821
824
  * Load and render an MJML email template by name.
822
825
  * Registers MJML components (Handlebars partials) automatically on first call.
823
- * Sets the component's render function so no <template> block is needed.
826
+ * Sets the current component's render function \u2014 no <template> block needed.
824
827
  *
825
828
  * @param name - Template name relative to the emails directory (e.g. 'example', 'v1/test')
826
829
  * @param props - Reactive props object from defineProps
827
- * @returns ComputedRef<string> containing the rendered HTML
828
830
  */
829
- export function useNgeTemplate(name: string, props: Record<string, unknown>): ComputedRef<string> {
831
+ export function useNgeTemplate(name: string, props: Record<string, unknown>): void {
830
832
  if (!_componentsRegistered) {
831
833
  registerMjmlComponents()
832
834
  _componentsRegistered = true
833
835
  }
834
836
 
837
+ const instance = getCurrentInstance()
838
+ const renderedHtml = ref('')
839
+
835
840
  const source = templateMap[name]
836
841
  if (!source) {
837
842
  const available = Object.keys(templateMap).join(', ')
838
843
  console.error(\`[nuxt-generation-emails] Template "\${name}" not found. Available: \${available}\`)
839
- const fallback = computed(() => \`<pre style="color:red;">Template "\${name}" not found</pre>\`)
840
- const instance = getCurrentInstance()
841
- if (instance) instance.render = () => h('div', { innerHTML: fallback.value })
842
- return fallback
843
- }
844
-
845
- const compiled = Handlebars.compile(source)
846
-
847
- const renderedHtml = computed(() => {
848
- // mjml-browser requires window \u2014 skip rendering during SSR
849
- if (!mjml2html) return ''
850
- try {
851
- const mjmlString = compiled({ ...props })
852
- const result = mjml2html(mjmlString)
853
- return result.html
854
- }
855
- catch (e: unknown) {
856
- console.error(\`[\${name}] Error rendering MJML:\`, e)
857
- return \`<pre style="color:red;">\${e instanceof Error ? e.message : String(e)}\\n\${e instanceof Error ? e.stack : ''}</pre>\`
844
+ renderedHtml.value = \`<pre style="color:red;">Template "\${name}" not found</pre>\`
845
+ if (instance) {
846
+ instance.render = (): VNode => h('div', { innerHTML: renderedHtml.value })
858
847
  }
859
- })
848
+ return
849
+ }
860
850
 
861
- // Set render function on the component instance so no <template> block is needed
862
- const instance = getCurrentInstance()
851
+ // Set the render function on the component instance so Vue renders the HTML directly.
863
852
  if (instance) {
864
- instance.render = () => h('div', { innerHTML: renderedHtml.value })
853
+ instance.render = (): VNode => h('div', { innerHTML: renderedHtml.value })
865
854
  }
866
855
 
867
- return renderedHtml
856
+ const compiled = Handlebars.compile(source)
857
+
858
+ // mjml-browser v5 returns a Promise \u2014 use watch to resolve async results reactively.
859
+ // Tracks both the mjml2html loader ref AND all accessed props (via the Handlebars compile spread).
860
+ watch(
861
+ [mjml2html, toRef(() => ({ ...props }))],
862
+ async ([convert]) => {
863
+ if (!convert) { renderedHtml.value = ''; return }
864
+ try {
865
+ const mjmlString = compiled({ ...props })
866
+ const result = await convert(mjmlString)
867
+ renderedHtml.value = result.html
868
+ }
869
+ catch (e: unknown) {
870
+ console.error(\`[\${name}] Error rendering MJML:\`, e)
871
+ renderedHtml.value = \`<pre style="color:red;">\${e instanceof Error ? e.message : String(e)}\\n\${e instanceof Error ? e.stack : ''}</pre>\`
872
+ }
873
+ },
874
+ { immediate: true, deep: true },
875
+ )
868
876
  }
869
877
  `
870
878
  });
@@ -21,7 +21,7 @@ declare module 'mjml-browser' {
21
21
  preprocessors?: Array<(xml: string) => string>
22
22
  }
23
23
 
24
- function mjml2html(mjml: string, options?: MjmlOptions): MjmlResult
24
+ function mjml2html(mjml: string, options?: MjmlOptions): Promise<MjmlResult>
25
25
  export default mjml2html
26
26
  }
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-generation-emails",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "description": "A Nuxt module for authoring, previewing, and sending transactional email templates with MJML and Handlebars.",
5
5
  "author": "nullcarry@icloud.com",
6
6
  "repository": {
@@ -66,8 +66,8 @@
66
66
  "citty": "^0.2.1",
67
67
  "consola": "^3.4.2",
68
68
  "handlebars": "^4.7.8",
69
- "mjml": "^4.18.0",
70
- "mjml-browser": "^4.18.0",
69
+ "mjml": "^5.0.0-alpha.11",
70
+ "mjml-browser": "^5.0.0-alpha.11",
71
71
  "pathe": "^2.0.3"
72
72
  },
73
73
  "peerDependencies": {