nuxt-og-image 1.0.0-beta.3 → 1.0.0-beta.5

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.
Files changed (30) hide show
  1. package/README.md +1 -0
  2. package/dist/client/200.html +2 -2
  3. package/dist/client/404.html +2 -2
  4. package/dist/client/_nuxt/{Icon.be9fb21b.js → Icon.881857b6.js} +1 -1
  5. package/dist/client/_nuxt/{entry.a0824e7e.js → entry.db5a4449.js} +2 -2
  6. package/dist/client/_nuxt/{error-404.15b637ef.js → error-404.1c1c2700.js} +1 -1
  7. package/dist/client/_nuxt/{error-500.037a45db.js → error-500.6a615525.js} +1 -1
  8. package/dist/client/_nuxt/{error-component.541b3e05.js → error-component.834f3c34.js} +2 -2
  9. package/dist/client/index.html +2 -2
  10. package/dist/module.d.ts +8 -8
  11. package/dist/module.json +1 -1
  12. package/dist/module.mjs +45 -29
  13. package/dist/runtime/components/{OgImageTemplate.island.vue → OgImageBasic.island.vue} +0 -0
  14. package/dist/runtime/components/OgImageDynamic.d.ts +2 -2
  15. package/dist/runtime/components/OgImageScreenshot.d.ts +2 -2
  16. package/dist/runtime/components/OgImageStatic.d.ts +2 -2
  17. package/dist/runtime/composables/defineOgImage.d.ts +5 -5
  18. package/dist/runtime/composables/defineOgImage.mjs +11 -6
  19. package/dist/runtime/nitro/providers/browser.mjs +2 -6
  20. package/dist/runtime/nitro/providers/satori.mjs +21 -25
  21. package/dist/runtime/nitro/routes/__og_image__/html.mjs +6 -8
  22. package/dist/runtime/nitro/routes/__og_image__/index.mjs +3 -3
  23. package/dist/runtime/nitro/routes/__og_image__/og.png.mjs +7 -11
  24. package/dist/runtime/nitro/routes/__og_image__/options.d.ts +5 -0
  25. package/dist/runtime/nitro/routes/__og_image__/{payload.mjs → options.mjs} +18 -20
  26. package/dist/runtime/nitro/routes/__og_image__/svg.mjs +4 -4
  27. package/dist/runtime/nitro/utils.d.ts +3 -3
  28. package/dist/runtime/nitro/utils.mjs +4 -4
  29. package/package.json +2 -2
  30. package/dist/runtime/nitro/routes/__og_image__/payload.d.ts +0 -5
@@ -1 +1 @@
1
- import{s as n,v as a,o as r,l,x as e,t as s,y as d,z as c,A as p,B as f,C as x,D as h}from"./entry.a0824e7e.js";const m=t=>(x("data-v-18337f8d"),t=t(),h(),t),u={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},g=m(()=>e("div",{class:"fixed left-0 right-0 spotlight z-10"},null,-1)),_={class:"max-w-520px text-center z-20"},b=["textContent"],y=["textContent"],w={class:"w-full flex items-center justify-center"},S={__name:"error-404",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:404},statusMessage:{type:String,default:"Not Found"},description:{type:String,default:"Sorry, the page you are looking for could not be found."},backHome:{type:String,default:"Go back home"}},setup(t){const o=t;return a({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}a{color:inherit;text-decoration:inherit}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(k,v)=>{const i=p;return r(),l("div",u,[g,e("div",_,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,b),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,y),e("div",w,[d(i,{to:"/",class:"gradient-border text-md sm:text-xl py-2 px-4 sm:py-3 sm:px-6 cursor-pointer"},{default:c(()=>[f(s(t.backHome),1)]),_:1})])])])}}},z=n(S,[["__scopeId","data-v-18337f8d"]]);export{z as default};
1
+ import{s as n,v as a,o as r,l,x as e,t as s,y as d,z as c,A as p,B as f,C as x,D as h}from"./entry.db5a4449.js";const m=t=>(x("data-v-18337f8d"),t=t(),h(),t),u={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},g=m(()=>e("div",{class:"fixed left-0 right-0 spotlight z-10"},null,-1)),_={class:"max-w-520px text-center z-20"},b=["textContent"],y=["textContent"],w={class:"w-full flex items-center justify-center"},S={__name:"error-404",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:404},statusMessage:{type:String,default:"Not Found"},description:{type:String,default:"Sorry, the page you are looking for could not be found."},backHome:{type:String,default:"Go back home"}},setup(t){const o=t;return a({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}a{color:inherit;text-decoration:inherit}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(k,v)=>{const i=p;return r(),l("div",u,[g,e("div",_,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,b),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,y),e("div",w,[d(i,{to:"/",class:"gradient-border text-md sm:text-xl py-2 px-4 sm:py-3 sm:px-6 cursor-pointer"},{default:c(()=>[f(s(t.backHome),1)]),_:1})])])])}}},z=n(S,[["__scopeId","data-v-18337f8d"]]);export{z as default};
@@ -1 +1 @@
1
- import{s as i,v as a,o as r,l as n,x as e,t as s,C as l,D as d}from"./entry.a0824e7e.js";const c=t=>(l("data-v-428e244b"),t=t(),d(),t),p={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},h=c(()=>e("div",{class:"fixed -bottom-1/2 left-0 right-0 h-1/2 spotlight"},null,-1)),f={class:"max-w-520px text-center"},g=["textContent"],m=["textContent"],x={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:500},statusMessage:{type:String,default:"Server error"},description:{type:String,default:"This page is temporarily unavailable."}},setup(t){const o=t;return a({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(b,u)=>(r(),n("div",p,[h,e("div",f,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,g),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,m)])]))}},w=i(x,[["__scopeId","data-v-428e244b"]]);export{w as default};
1
+ import{s as i,v as a,o as r,l as n,x as e,t as s,C as l,D as d}from"./entry.db5a4449.js";const c=t=>(l("data-v-428e244b"),t=t(),d(),t),p={class:"font-sans antialiased bg-white dark:bg-black text-black dark:text-white grid min-h-screen place-content-center overflow-hidden"},h=c(()=>e("div",{class:"fixed -bottom-1/2 left-0 right-0 h-1/2 spotlight"},null,-1)),f={class:"max-w-520px text-center"},g=["textContent"],m=["textContent"],x={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},statusCode:{type:Number,default:500},statusMessage:{type:String,default:"Server error"},description:{type:String,default:"This page is temporarily unavailable."}},setup(t){const o=t;return a({title:`${o.statusCode} - ${o.statusMessage} | ${o.appName}`,script:[],style:[{children:'*,:before,:after{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:#e0e0e0}*{--tw-ring-inset:var(--tw-empty, );--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(14, 165, 233, .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{margin:0;font-family:inherit;line-height:inherit}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}h1,p{margin:0}h1{font-size:inherit;font-weight:inherit}'}]}),(b,u)=>(r(),n("div",p,[h,e("div",f,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,g),e("p",{class:"text-xl px-8 sm:px-0 sm:text-4xl font-light mb-16 leading-tight",textContent:s(t.description)},null,8,m)])]))}},w=i(x,[["__scopeId","data-v-428e244b"]]);export{w as default};
@@ -1,3 +1,3 @@
1
- import{d as n,_ as o,o as _,c as f,n as g,g as E,u as r}from"./entry.a0824e7e.js";const k={__name:"nuxt-error-page",props:{error:Object},setup(t){(t.error.stack||"").split(`
1
+ import{d as n,_ as o,o as _,c as f,n as g,g as E,u as r}from"./entry.db5a4449.js";const k={__name:"nuxt-error-page",props:{error:Object},setup(t){(t.error.stack||"").split(`
2
2
  `).splice(1).map(e=>({text:e.replace("webpack:/","").replace(".vue",".js").trim(),internal:e.includes("node_modules")&&!e.includes(".cache")||e.includes("internal")||e.includes("new Promise")})).map(e=>`<span class="stack${e.internal?" internal":""}">${e.text}</span>`).join(`
3
- `);const s=Number(t.error.statusCode||500),a=s===404,c=t.error.statusMessage??(a?"Page Not Found":"Internal Server Error"),u=t.error.message||t.error.toString(),i=void 0,d=n(()=>o(()=>import("./error-404.15b637ef.js"),["./error-404.15b637ef.js","./entry.a0824e7e.js","./entry.0827acf4.css","./error-404.68aa58b4.css"],import.meta.url).then(e=>e.default||e)),l=n(()=>o(()=>import("./error-500.037a45db.js"),["./error-500.037a45db.js","./entry.a0824e7e.js","./entry.0827acf4.css","./error-500.dc5710d1.css"],import.meta.url).then(e=>e.default||e)),m=a?d:l;return(e,p)=>(_(),f(r(m),g(E({statusCode:r(s),statusMessage:r(c),description:r(u),stack:r(i)})),null,16))}},v=k;export{v as default};
3
+ `);const s=Number(t.error.statusCode||500),a=s===404,c=t.error.statusMessage??(a?"Page Not Found":"Internal Server Error"),u=t.error.message||t.error.toString(),i=void 0,d=n(()=>o(()=>import("./error-404.1c1c2700.js"),["./error-404.1c1c2700.js","./entry.db5a4449.js","./entry.0827acf4.css","./error-404.68aa58b4.css"],import.meta.url).then(e=>e.default||e)),l=n(()=>o(()=>import("./error-500.6a615525.js"),["./error-500.6a615525.js","./entry.db5a4449.js","./entry.0827acf4.css","./error-500.dc5710d1.css"],import.meta.url).then(e=>e.default||e)),m=a?d:l;return(e,p)=>(_(),f(r(m),g(E({statusCode:r(s),statusMessage:r(c),description:r(u),stack:r(i)})),null,16))}},v=k;export{v as default};
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html >
3
3
  <head><meta charset="utf-8">
4
- <meta name="viewport" content="width=device-width, initial-scale=1"><link rel="modulepreload" as="script" crossorigin href="/__nuxt_og_image__/client/_nuxt/entry.a0824e7e.js"><link rel="preload" as="style" href="/__nuxt_og_image__/client/_nuxt/entry.0827acf4.css"><link rel="prefetch" as="script" crossorigin href="/__nuxt_og_image__/client/_nuxt/error-component.541b3e05.js"><link rel="stylesheet" href="/__nuxt_og_image__/client/_nuxt/entry.0827acf4.css"><script>"use strict";const w=window,de=document.documentElement,knownColorSchemes=["dark","light"],preference=window.localStorage.getItem("nuxt-color-mode")||"system";let value=preference==="system"?getColorScheme():preference;const forcedColorMode=de.getAttribute("data-color-mode-forced");forcedColorMode&&(value=forcedColorMode),addColorScheme(value),w["__NUXT_COLOR_MODE__"]={preference,value,getColorScheme,addColorScheme,removeColorScheme};function addColorScheme(e){const o=""+e+"",t="";de.classList?de.classList.add(o):de.className+=" "+o,t&&de.setAttribute("data-"+t,e)}function removeColorScheme(e){const o=""+e+"",t="";de.classList?de.classList.remove(o):de.className=de.className.replace(new RegExp(o,"g"),""),t&&de.removeAttribute("data-"+t)}function prefersColorScheme(e){return w.matchMedia("(prefers-color-scheme"+e+")")}function getColorScheme(){if(w.matchMedia&&prefersColorScheme("").media!=="not all"){for(const e of knownColorSchemes)if(prefersColorScheme(":"+e).matches)return e}return"light"}
4
+ <meta name="viewport" content="width=device-width, initial-scale=1"><link rel="modulepreload" as="script" crossorigin href="/__nuxt_og_image__/client/_nuxt/entry.db5a4449.js"><link rel="preload" as="style" href="/__nuxt_og_image__/client/_nuxt/entry.0827acf4.css"><link rel="prefetch" as="script" crossorigin href="/__nuxt_og_image__/client/_nuxt/error-component.834f3c34.js"><link rel="stylesheet" href="/__nuxt_og_image__/client/_nuxt/entry.0827acf4.css"><script>"use strict";const w=window,de=document.documentElement,knownColorSchemes=["dark","light"],preference=window.localStorage.getItem("nuxt-color-mode")||"system";let value=preference==="system"?getColorScheme():preference;const forcedColorMode=de.getAttribute("data-color-mode-forced");forcedColorMode&&(value=forcedColorMode),addColorScheme(value),w["__NUXT_COLOR_MODE__"]={preference,value,getColorScheme,addColorScheme,removeColorScheme};function addColorScheme(e){const o=""+e+"",t="";de.classList?de.classList.add(o):de.className+=" "+o,t&&de.setAttribute("data-"+t,e)}function removeColorScheme(e){const o=""+e+"",t="";de.classList?de.classList.remove(o):de.className=de.className.replace(new RegExp(o,"g"),""),t&&de.removeAttribute("data-"+t)}function prefersColorScheme(e){return w.matchMedia("(prefers-color-scheme"+e+")")}function getColorScheme(){if(w.matchMedia&&prefersColorScheme("").media!=="not all"){for(const e of knownColorSchemes)if(prefersColorScheme(":"+e).matches)return e}return"light"}
5
5
  </script></head>
6
- <body ><div id="__nuxt"></div><script>window.__NUXT__={serverRendered:false,config:{public:{},app:{baseURL:"\u002F__nuxt_og_image__\u002Fclient",buildAssetsDir:"\u002F_nuxt\u002F",cdnURL:""}},data:{},state:{}}</script><script type="module" src="/__nuxt_og_image__/client/_nuxt/entry.a0824e7e.js" crossorigin></script></body>
6
+ <body ><div id="__nuxt"></div><script>window.__NUXT__={serverRendered:false,config:{public:{},app:{baseURL:"\u002F__nuxt_og_image__\u002Fclient",buildAssetsDir:"\u002F_nuxt\u002F",cdnURL:""}},data:{},state:{}}</script><script type="module" src="/__nuxt_og_image__/client/_nuxt/entry.db5a4449.js" crossorigin></script></body>
7
7
  </html>
package/dist/module.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { SatoriOptions } from 'satori';
2
3
 
3
4
  interface ScreenshotOptions {
4
5
  colorScheme?: 'dark' | 'light';
@@ -21,7 +22,7 @@ interface ScreenshotOptions {
21
22
  */
22
23
  delay?: number;
23
24
  }
24
- interface OgImagePayload extends Partial<ScreenshotOptions> {
25
+ interface OgImageOptions extends Partial<ScreenshotOptions> {
25
26
  provider?: 'browser' | 'satori';
26
27
  prerender?: boolean;
27
28
  title?: string;
@@ -30,19 +31,18 @@ interface OgImagePayload extends Partial<ScreenshotOptions> {
30
31
  alt?: string;
31
32
  [key: string]: any;
32
33
  }
33
- type OgImageScreenshotPayload = Omit<OgImagePayload, 'component'>;
34
- declare module 'nitropack' {
35
- interface NitroRouteRules {
36
- ogImage?: false | OgImageScreenshotPayload | OgImagePayload;
37
- }
38
- }
39
34
 
40
- interface ModuleOptions extends ScreenshotOptions {
35
+ interface ModuleOptions {
41
36
  /**
42
37
  * The hostname of your website.
43
38
  */
44
39
  host: string;
40
+ defaults: OgImageOptions;
45
41
  experimentalNitroBrowser: boolean;
42
+ satoriFonts: Array<Partial<SatoriOptions['fonts'][number]> & {
43
+ publicPath: string;
44
+ }>;
45
+ satoriOptions: Partial<SatoriOptions>;
46
46
  forcePrerender: boolean;
47
47
  }
48
48
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
package/dist/module.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "bridge": false
6
6
  },
7
7
  "configKey": "ogImage",
8
- "version": "1.0.0-beta.3"
8
+ "version": "1.0.0-beta.5"
9
9
  }
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdir, writeFile } from 'node:fs/promises';
2
2
  import { existsSync } from 'fs';
3
- import { useNuxt, addTemplate, defineNuxtModule, createResolver, addServerHandler, addImports, addComponent } from '@nuxt/kit';
3
+ import { useNuxt, addTemplate, defineNuxtModule, createResolver, getNuxtVersion, useLogger, addServerHandler, addImports, addComponent } from '@nuxt/kit';
4
4
  import { execa } from 'execa';
5
5
  import chalk from 'chalk';
6
6
  import defu from 'defu';
@@ -57,13 +57,8 @@ async function screenshot(browser, url, options) {
57
57
  return await page.screenshot();
58
58
  }
59
59
 
60
- const PayloadScriptId = "nuxt-og-image-payload";
61
- const Constants = {
62
- PayloadScriptId
63
- };
64
-
65
60
  function exposeConfig(alias, filename, config) {
66
- const exports = Object.entries(config).map(([k, v]) => `export const ${k} = '${v}'`).join("\n");
61
+ const exports = Object.entries(config).map(([k, v]) => `export const ${k} = ${JSON.stringify(v)}`).join("\n");
67
62
  useNuxt().options.alias[alias] = addTemplate({
68
63
  filename,
69
64
  getContents: () => exports
@@ -72,6 +67,13 @@ function exposeConfig(alias, filename, config) {
72
67
  nitroConfig.virtual[alias] = exports;
73
68
  });
74
69
  }
70
+ function extractOgImageOptions(html) {
71
+ const options = html.match(/<script id="nuxt-og-image-options" type="application\/json">(.+?)<\/script>/)?.[1];
72
+ return options ? JSON.parse(options) : false;
73
+ }
74
+ function stripOgImageOptions(html) {
75
+ return html.replace(/<script id="nuxt-og-image-options" type="application\/json">(.*?)<\/script>/, "");
76
+ }
75
77
 
76
78
  function setupPlaygroundRPC(nuxt, config) {
77
79
  const serverFunctions = {
@@ -155,13 +157,6 @@ function getBodyJson(req) {
155
157
  });
156
158
  }
157
159
 
158
- function extractOgPayload(html) {
159
- const payload = html.match(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.+?)<\/script>`))?.[1];
160
- if (payload) {
161
- return JSON.parse(payload);
162
- }
163
- return false;
164
- }
165
160
  const PATH = "/__nuxt_og_image__";
166
161
  const PATH_ENTRY = `${PATH}/entry`;
167
162
  const PATH_PLAYGROUND = `${PATH}/client`;
@@ -179,8 +174,26 @@ const module = defineNuxtModule({
179
174
  experimentalNitroBrowser: false,
180
175
  forcePrerender: !nuxt.options.dev && nuxt.options._generate,
181
176
  host: nuxt.options.runtimeConfig.public?.siteUrl,
182
- width: 1200,
183
- height: 630
177
+ defaults: {
178
+ component: "OgImageBasic",
179
+ width: 1200,
180
+ height: 630
181
+ },
182
+ satoriFonts: [
183
+ {
184
+ name: "Inter",
185
+ weight: 400,
186
+ style: "normal",
187
+ publicPath: "/inter-latin-ext-400-normal.woff"
188
+ },
189
+ {
190
+ name: "Inter",
191
+ weight: 700,
192
+ style: "normal",
193
+ publicPath: "/inter-latin-ext-700-normal.woff"
194
+ }
195
+ ],
196
+ satoriOptions: {}
184
197
  };
185
198
  },
186
199
  async setup(config, nuxt) {
@@ -193,6 +206,10 @@ const module = defineNuxtModule({
193
206
  };
194
207
  nuxt.options.experimental.componentIslands = true;
195
208
  const isEdge = (process.env.NITRO_PRESET || "").includes("edge");
209
+ const hasIslandSupport = getNuxtVersion(nuxt) !== "3.0.0";
210
+ const logger = useLogger("nuxt-og-image");
211
+ if (!hasIslandSupport)
212
+ logger.warn("You are using Nuxt 3.0.0 with `nuxt-og-image`, which only supports screenshots.\nPlease upgrade to Nuxt 3.0.1 or the edge channel: https://nuxt.com/docs/guide/going-further/edge-channel.");
196
213
  addTemplate({
197
214
  filename: "nuxt-og-image.d.ts",
198
215
  getContents: () => {
@@ -211,7 +228,7 @@ export {}
211
228
  nuxt.hooks.hook("prepare:types", ({ references }) => {
212
229
  references.push({ path: resolve(nuxt.options.buildDir, "nuxt-og-image.d.ts") });
213
230
  });
214
- ["html", "payload", "svg", "og.png"].forEach((type) => {
231
+ ["html", "options", "svg", "og.png"].forEach((type) => {
215
232
  addServerHandler({
216
233
  handler: resolve(`./runtime/nitro/routes/__og_image__/${type}`)
217
234
  });
@@ -238,8 +255,8 @@ export {}
238
255
  });
239
256
  });
240
257
  await addComponent({
241
- name: "OgImageTemplate",
242
- filePath: resolve("./runtime/components/OgImageTemplate.island.vue"),
258
+ name: "OgImageBasic",
259
+ filePath: resolve("./runtime/components/OgImageBasic.island.vue"),
243
260
  global: true,
244
261
  island: true
245
262
  });
@@ -252,7 +269,6 @@ export {}
252
269
  });
253
270
  const runtimeDir = resolve("./runtime");
254
271
  nuxt.options.build.transpile.push(runtimeDir);
255
- exposeConfig("#nuxt-og-image/constants", "nuxt-og-image-constants.mjs", Constants);
256
272
  exposeConfig("#nuxt-og-image/config", "nuxt-og-image-config.mjs", config);
257
273
  nuxt.hooks.hook("nitro:config", (nitroConfig) => {
258
274
  nitroConfig.externals = defu(nitroConfig.externals || {}, {
@@ -292,19 +308,19 @@ export {}
292
308
  const html = ctx.contents;
293
309
  if (!html)
294
310
  return;
295
- const extractedPayload = extractOgPayload(html);
296
- ctx.contents = html.replace(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.*?)<\/script>`), "");
311
+ const extractedOptions = extractOgImageOptions(html);
312
+ ctx.contents = stripOgImageOptions(html);
297
313
  const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
298
- if (!extractedPayload || routeRules.ogImage === false)
314
+ if (!extractedOptions || routeRules.ogImage === false)
299
315
  return;
300
- const payload = {
316
+ const options = {
301
317
  path: ctx.route,
302
- ...extractedPayload,
318
+ ...extractedOptions,
303
319
  ...routeRules.ogImage || {},
304
320
  ctx
305
321
  };
306
- if ((nuxt.options._generate || payload.prerender) && payload.provider === "browser")
307
- screenshotQueue.push(payload);
322
+ if ((nuxt.options._generate || options.prerender) && options.provider === "browser")
323
+ screenshotQueue.push(options);
308
324
  });
309
325
  if (nuxt.options.dev)
310
326
  return;
@@ -333,8 +349,8 @@ export {}
333
349
  const filename = joinURL(dirname, "/og.png");
334
350
  try {
335
351
  const imgBuffer = await screenshot(browser, `${host}${entry.path}`, {
336
- ...config,
337
- ...entry
352
+ ...config.defaults || {},
353
+ ...entry || {}
338
354
  });
339
355
  try {
340
356
  await mkdir(dirname, { recursive: true });
@@ -1,5 +1,5 @@
1
- import type { OgImagePayload } from '../../types';
2
- declare const _default: import("vue").DefineComponent<OgImagePayload, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImagePayload>>, {
1
+ import type { OgImageOptions } from '../../types';
2
+ declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageOptions>>, {
3
3
  [x: string]: any;
4
4
  }>;
5
5
  export default _default;
@@ -1,5 +1,5 @@
1
- import type { OgImageScreenshotPayload } from '../../types';
2
- declare const _default: import("vue").DefineComponent<OgImageScreenshotPayload, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageScreenshotPayload>>, {
1
+ import type { OgImageScreenshotOptions } from '../../types';
2
+ declare const _default: import("vue").DefineComponent<OgImageScreenshotOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageScreenshotOptions>>, {
3
3
  [x: string]: any;
4
4
  [x: number]: any;
5
5
  }>;
@@ -1,5 +1,5 @@
1
- import type { OgImagePayload } from '../../types';
2
- declare const _default: import("vue").DefineComponent<OgImagePayload, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImagePayload>>, {
1
+ import type { OgImageOptions } from '../../types';
2
+ declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageOptions>>, {
3
3
  [x: string]: any;
4
4
  }>;
5
5
  export default _default;
@@ -1,5 +1,5 @@
1
- import type { OgImagePayload, OgImageScreenshotPayload } from '../../types';
2
- export declare function defineOgImageScreenshot(options?: OgImageScreenshotPayload): void;
3
- export declare function defineOgImageDynamic(options?: OgImageScreenshotPayload): void;
4
- export declare function defineOgImageStatic(options?: OgImageScreenshotPayload): void;
5
- export declare function defineOgImage(options?: OgImagePayload): void;
1
+ import type { OgImageOptions, OgImageScreenshotOptions } from '../../types';
2
+ export declare function defineOgImageScreenshot(options?: OgImageScreenshotOptions): void;
3
+ export declare function defineOgImageDynamic(options?: OgImageOptions): void;
4
+ export declare function defineOgImageStatic(options?: OgImageOptions): void;
5
+ export declare function defineOgImage(options?: OgImageOptions): void;
@@ -2,8 +2,7 @@ import { useServerHead } from "@vueuse/head";
2
2
  import { withBase } from "ufo";
3
3
  import { useRequestEvent } from "#app";
4
4
  import { useRouter } from "#imports";
5
- import { PayloadScriptId } from "#nuxt-og-image/constants";
6
- import { forcePrerender, height, host, width } from "#nuxt-og-image/config";
5
+ import { defaults, forcePrerender, host } from "#nuxt-og-image/config";
7
6
  export function defineOgImageScreenshot(options = {}) {
8
7
  const router = useRouter();
9
8
  const route = router?.currentRoute?.value?.path || "";
@@ -45,11 +44,11 @@ export function defineOgImage(options = {}) {
45
44
  },
46
45
  {
47
46
  property: "og:image:width",
48
- content: width
47
+ content: options.width || defaults.width
49
48
  },
50
49
  {
51
50
  property: "og:image:height",
52
- content: height
51
+ content: options.height || defaults.height
53
52
  }
54
53
  ];
55
54
  if (options.alt) {
@@ -62,9 +61,15 @@ export function defineOgImage(options = {}) {
62
61
  meta,
63
62
  script: [
64
63
  {
65
- id: PayloadScriptId,
64
+ id: "nuxt-og-image-options",
66
65
  type: "application/json",
67
- children: () => JSON.stringify(options)
66
+ children: () => {
67
+ const payload = {};
68
+ Object.entries(options).forEach(([key, val]) => {
69
+ payload[key.replace(/-([a-z])/g, (g) => g[1].toUpperCase())] = val;
70
+ });
71
+ return payload;
72
+ }
68
73
  }
69
74
  ]
70
75
  });
@@ -1,16 +1,12 @@
1
1
  import { screenshot } from "../../browserUtil.mjs";
2
- import { height, width } from "#nuxt-og-image/config";
3
2
  import { createBrowser } from "#nuxt-og-image/browser";
4
3
  export default {
5
4
  name: "browser",
6
5
  createSvg: function createSvg() {
7
6
  throw new Error("Browser provider isn't able to create SVGs.");
8
7
  },
9
- createPng: async function createPng(basePath) {
8
+ createPng: async function createPng(basePath, options) {
10
9
  const browser = await createBrowser();
11
- return screenshot(browser, basePath, {
12
- width: Number(width),
13
- height: Number(height)
14
- });
10
+ return screenshot(browser, basePath, options);
15
11
  }
16
12
  };
@@ -2,47 +2,43 @@ import { fileURLToPath } from "node:url";
2
2
  import { promises as fsp } from "node:fs";
3
3
  import { html as convertHtmlToSatori } from "satori-html";
4
4
  import satori from "satori";
5
- import { parseURL } from "ufo";
5
+ import { parseURL, withBase } from "ufo";
6
6
  import { Resvg } from "@resvg/resvg-js";
7
7
  import { dirname, resolve } from "pathe";
8
- import { height, width } from "#nuxt-og-image/config";
9
8
  import { getAsset } from "#internal/nitro/virtual/public-assets";
9
+ import { satoriFonts, satoriOptions } from "#nuxt-og-image/config";
10
10
  export default {
11
11
  name: "satori",
12
- createPng: async function createPng(baseUrl) {
13
- const svg = await this.createSvg(baseUrl);
12
+ createPng: async function createPng(baseUrl, options) {
13
+ const svg = await this.createSvg(baseUrl, options);
14
14
  const resvg = new Resvg(svg, {});
15
15
  const pngData = resvg.render();
16
16
  return pngData.asPng();
17
17
  },
18
- createSvg: async function createSvg(baseUrl) {
18
+ createSvg: async function createSvg(baseUrl, options) {
19
19
  const url = parseURL(baseUrl);
20
20
  const html = await $fetch(url.pathname);
21
21
  const body = html.match(/<body[^>]*>([\s\S]*)<\/body>/)?.[1];
22
- const font = async (weight) => {
23
- let fontData;
24
- const file = getAsset(`/inter-latin-ext-${weight}-normal.woff`);
25
- if (file) {
26
- const serverDir = dirname(fileURLToPath(import.meta.url));
27
- fontData = await fsp.readFile(resolve(serverDir, file.path));
22
+ const parseFont = async (font) => {
23
+ if (typeof font.publicPath === "string") {
24
+ const file = getAsset(font.publicPath);
25
+ if (file) {
26
+ const serverDir = dirname(fileURLToPath(import.meta.url));
27
+ font.data = await fsp.readFile(resolve(serverDir, file.path));
28
+ }
29
+ if (!font.data)
30
+ font.data = await (await $fetch(withBase(font.publicPath, `${url.protocol}//${url.host}`))).arrayBuffer();
28
31
  }
29
- if (!fontData)
30
- fontData = await (await $fetch(`${url.protocol}//${url.host}/inter-latin-ext-${weight}-normal.woff`)).arrayBuffer();
31
- return {
32
- name: "Inter",
33
- weight,
34
- style: "normal",
35
- data: fontData
36
- };
32
+ return font;
37
33
  };
34
+ satoriOptions.fonts = satoriOptions.fonts || [];
35
+ for (const font of satoriFonts)
36
+ satoriOptions.fonts.push(await parseFont(font));
38
37
  const satoriTree = convertHtmlToSatori(body);
39
38
  return await satori(satoriTree, {
40
- width,
41
- height,
42
- fonts: [
43
- await font(400),
44
- await font(700)
45
- ]
39
+ ...satoriOptions,
40
+ width: options.width,
41
+ height: options.height
46
42
  });
47
43
  }
48
44
  };
@@ -2,19 +2,17 @@ import { parseURL, withBase, withoutTrailingSlash } from "ufo";
2
2
  import { renderSSRHead } from "@unhead/ssr";
3
3
  import { createHeadCore } from "@unhead/vue";
4
4
  import { defineEventHandler, sendRedirect } from "h3";
5
- import { fetchPayload, renderIsland, useHostname } from "../../utils.mjs";
6
- import { height, width } from "#nuxt-og-image/config";
5
+ import { fetchOptions, renderIsland, useHostname } from "../../utils.mjs";
6
+ import { defaults } from "#nuxt-og-image/config";
7
7
  export default defineEventHandler(async (e) => {
8
8
  const path = parseURL(e.path).pathname;
9
9
  if (!path.endsWith("__og_image__/html"))
10
10
  return;
11
11
  const basePath = withoutTrailingSlash(path.replace("__og_image__/html", ""));
12
- const payload = await fetchPayload(basePath);
13
- if (payload.provider === "browser")
12
+ const options = await fetchOptions(basePath);
13
+ if (options.provider === "browser")
14
14
  return sendRedirect(e, withBase(basePath, useHostname(e)));
15
- const component = payload.component || "OgImageTemplate";
16
- delete payload.component;
17
- const island = await renderIsland(component, payload);
15
+ const island = await renderIsland(options);
18
16
  const head = createHeadCore();
19
17
  head.push(island.head);
20
18
  head.push({
@@ -38,6 +36,6 @@ export default defineEventHandler(async (e) => {
38
36
  return `<!DOCTYPE html>
39
37
  <html ${headChunk.htmlAttrs}>
40
38
  <head>${headChunk.headTags}</head>
41
- <body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div style="width: ${width}px; height: ${height}px; display: flex; margin: 0 auto;">${island.html}</div>${headChunk.bodyTags}</body>
39
+ <body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div style="width: ${defaults.width}px; height: ${defaults.height}px; display: flex; margin: 0 auto;">${island.html}</div>${headChunk.bodyTags}</body>
42
40
  </html>`;
43
41
  });
@@ -1,13 +1,13 @@
1
1
  import { defineEventHandler } from "h3";
2
2
  import { parseURL, withoutTrailingSlash } from "ufo";
3
- import { fetchPayload, useHostname } from "../../utils.mjs";
3
+ import { fetchOptions, useHostname } from "../../utils.mjs";
4
4
  export default defineEventHandler(async (e) => {
5
5
  const path = parseURL(e.path).pathname;
6
6
  if (!path.endsWith("/__og_image__"))
7
7
  return;
8
8
  const basePath = withoutTrailingSlash(path.replace("__og_image__", ""));
9
- const payload = await fetchPayload(basePath);
10
- if (!payload)
9
+ const options = await fetchOptions(basePath);
10
+ if (!options)
11
11
  return `The route ${basePath} has not been set up for og:image generation.`;
12
12
  return `
13
13
  <style>
@@ -1,6 +1,6 @@
1
1
  import { defineEventHandler, setHeader } from "h3";
2
2
  import { joinURL, parseURL, withBase, withoutTrailingSlash } from "ufo";
3
- import { fetchPayload, useHostname } from "../../utils.mjs";
3
+ import { fetchOptions, useHostname } from "../../utils.mjs";
4
4
  import { useProvider } from "#nuxt-og-image/provider";
5
5
  export default defineEventHandler(async (e) => {
6
6
  const path = parseURL(e.path).pathname;
@@ -9,15 +9,11 @@ export default defineEventHandler(async (e) => {
9
9
  const basePath = withoutTrailingSlash(
10
10
  path.replace("__og_image__/og.png", "")
11
11
  );
12
- const { provider: providerName, prerender } = await fetchPayload(basePath);
12
+ const options = await fetchOptions(basePath);
13
13
  setHeader(e, "Content-Type", "image/png");
14
- if (prerender && !process.dev) {
15
- setHeader(e, "Cache-Control", "public, max-age=86400");
16
- } else {
17
- setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
18
- setHeader(e, "Pragma", "no-cache");
19
- setHeader(e, "Expires", "0");
20
- }
21
- const provider = await useProvider(providerName);
22
- return provider.createPng(withBase(joinURL(basePath, "/__og_image__/html"), useHostname(e)));
14
+ setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
15
+ setHeader(e, "Pragma", "no-cache");
16
+ setHeader(e, "Expires", "0");
17
+ const provider = await useProvider(options.provider);
18
+ return provider.createPng(withBase(joinURL(basePath, "/__og_image__/html"), useHostname(e)), options);
23
19
  });
@@ -0,0 +1,5 @@
1
+ import type { OgImageOptions } from '../../../../types';
2
+ export declare function extractOgImageOptions(html: string): any;
3
+ export declare const inferOgImageOptions: (html: string) => OgImageOptions;
4
+ declare const _default: import("h3").EventHandler<false | OgImageOptions | undefined>;
5
+ export default _default;
@@ -1,35 +1,32 @@
1
1
  import { parseURL, withoutTrailingSlash } from "ufo";
2
2
  import { defineEventHandler, getQuery } from "h3";
3
- import { PayloadScriptId } from "#nuxt-og-image/constants";
4
3
  import { getRouteRules } from "#internal/nitro";
5
- export const extractOgPayload = (html) => {
6
- const payload = html.match(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.+?)<\/script>`))?.[1];
7
- if (payload) {
8
- return JSON.parse(payload);
9
- }
10
- return false;
11
- };
12
- export const inferOgPayload = (html) => {
13
- const payload = {};
4
+ import { defaults } from "#nuxt-og-image/config";
5
+ export function extractOgImageOptions(html) {
6
+ const options = html.match(/<script id="nuxt-og-image-options" type="application\/json">(.+?)<\/script>/)?.[1];
7
+ return options ? JSON.parse(options) : false;
8
+ }
9
+ export const inferOgImageOptions = (html) => {
10
+ const options = {};
14
11
  const title = html.match(/<meta property="og:title" content="(.*?)">/)?.[1];
15
12
  if (title)
16
- payload.title = title;
13
+ options.title = title;
17
14
  else
18
- payload.title = html.match(/<title>(.*?)<\/title>/)?.[1];
15
+ options.title = html.match(/<title>(.*?)<\/title>/)?.[1];
19
16
  const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
20
17
  if (description)
21
- payload.description = description;
18
+ options.description = description;
22
19
  else
23
- payload.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
24
- return payload;
20
+ options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
21
+ return options;
25
22
  };
26
23
  export default defineEventHandler(async (e) => {
27
24
  const path = parseURL(e.path).pathname;
28
- if (!path.endsWith("__og_image__/payload"))
25
+ if (!path.endsWith("__og_image__/options"))
29
26
  return;
30
- const basePath = withoutTrailingSlash(path.replace("__og_image__/payload", ""));
27
+ const basePath = withoutTrailingSlash(path.replace("__og_image__/options", ""));
31
28
  const html = await $fetch(basePath);
32
- const extractedPayload = extractOgPayload(html);
29
+ const extractedPayload = extractOgImageOptions(html);
33
30
  if (!extractedPayload)
34
31
  return false;
35
32
  e.node.req.url = basePath;
@@ -40,9 +37,10 @@ export default defineEventHandler(async (e) => {
40
37
  return false;
41
38
  return {
42
39
  path: basePath,
43
- ...inferOgPayload(html),
40
+ ...defaults,
41
+ ...inferOgImageOptions(html),
44
42
  ...routeRules || {},
45
- ...extractOgPayload(html),
43
+ ...extractedPayload,
46
44
  ...getQuery(e)
47
45
  };
48
46
  });
@@ -1,6 +1,6 @@
1
1
  import { defineEventHandler, setHeader } from "h3";
2
2
  import { joinURL, parseURL, withBase, withoutTrailingSlash } from "ufo";
3
- import { fetchPayload, useHostname } from "../../utils.mjs";
3
+ import { fetchOptions, useHostname } from "../../utils.mjs";
4
4
  import { useProvider } from "#nuxt-og-image/provider";
5
5
  export default defineEventHandler(async (e) => {
6
6
  const path = parseURL(e.path).pathname;
@@ -9,8 +9,8 @@ export default defineEventHandler(async (e) => {
9
9
  const basePath = withoutTrailingSlash(
10
10
  path.replace("__og_image__/svg", "")
11
11
  );
12
- const { provider: providerName } = await fetchPayload(basePath);
12
+ const options = await fetchOptions(basePath);
13
13
  setHeader(e, "Content-Type", "image/svg+xml");
14
- const provider = await useProvider(providerName);
15
- return provider.createSvg(withBase(joinURL(basePath, "/__og_image__/html"), useHostname(e)));
14
+ const provider = await useProvider(options.provider);
15
+ return provider.createSvg(withBase(joinURL(basePath, "/__og_image__/html"), useHostname(e)), options);
16
16
  });
@@ -1,7 +1,7 @@
1
1
  import type { H3Event } from 'h3';
2
- import type { OgImagePayload } from '../../types';
3
- export declare function fetchPayload(path: string): Promise<OgImagePayload>;
4
- export declare function renderIsland(island: string, payload: Record<string, any>): Promise<{
2
+ import type { OgImageOptions } from '../../types';
3
+ export declare function fetchOptions(path: string): Promise<OgImageOptions>;
4
+ export declare function renderIsland(payload: OgImageOptions): Promise<{
5
5
  html: string;
6
6
  head: any;
7
7
  }>;