nuxt-og-image 3.0.0-rc.0 → 3.0.0-rc.10
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.
- package/README.md +0 -4
- package/dist/client/200.html +5 -5
- package/dist/client/404.html +5 -5
- package/dist/client/_nuxt/{IconCSS.9f95294e.js → IconCSS.b32ce867.js} +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/bb3bd895-b72a-4970-a205-33afc62f06bc.json +1 -0
- package/dist/client/_nuxt/entry.47125dcd.css +1 -0
- package/dist/client/_nuxt/{entry.c5e613a6.js → entry.ecf0d33f.js} +47 -47
- package/dist/client/_nuxt/{error-404.e1564a51.js → error-404.b9a11c8e.js} +1 -1
- package/dist/client/_nuxt/{error-500.90b12af8.js → error-500.eb8225d3.js} +1 -1
- package/dist/client/index.html +5 -5
- package/dist/module.d.mts +5 -0
- package/dist/module.d.ts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +37 -10
- package/dist/runtime/cache.d.ts +2 -1
- package/dist/runtime/cache.mjs +33 -18
- package/dist/runtime/components/Templates/Community/NuxtSeo.vue +1 -1
- package/dist/runtime/composables/defineOgImage.mjs +8 -10
- package/dist/runtime/core/font/fetch.mjs +4 -1
- package/dist/runtime/core/html/devIframeTemplate.mjs +3 -2
- package/dist/runtime/core/html/fetchIsland.mjs +1 -1
- package/dist/runtime/core/options/extract.d.ts +1 -0
- package/dist/runtime/core/options/extract.mjs +3 -0
- package/dist/runtime/core/options/fetch.mjs +7 -1
- package/dist/runtime/core/renderers/chromium/index.mjs +2 -1
- package/dist/runtime/core/renderers/satori/index.mjs +6 -5
- package/dist/runtime/core/renderers/satori/vnodes.mjs +2 -0
- package/dist/runtime/core/utils/resolveRendererContext.mjs +14 -16
- package/dist/runtime/nitro/kit.d.ts +3 -0
- package/dist/runtime/nitro/kit.mjs +23 -0
- package/dist/runtime/nitro/plugins/prerender.d.ts +1 -1
- package/dist/runtime/nitro/plugins/prerender.mjs +5 -12
- package/dist/runtime/nitro/utils.mjs +2 -3
- package/dist/runtime/nuxt/utils.mjs +10 -3
- package/dist/runtime/server/routes/__og-image__/debug.json.d.ts +0 -4
- package/dist/runtime/server/routes/__og-image__/debug.json.mjs +0 -6
- package/dist/runtime/server/routes/__og-image__/font-[name]-[weight].[extension].mjs +31 -5
- package/dist/runtime/server/routes/__og-image__/image.mjs +8 -5
- package/dist/runtime/types.d.ts +5 -0
- package/dist/runtime/utils.pure.d.ts +2 -0
- package/dist/runtime/utils.pure.mjs +10 -1
- package/package.json +16 -15
- package/dist/client/_nuxt/builds/meta/01a72661-4cae-4637-8364-242cd0ee1a35.json +0 -1
- package/dist/client/_nuxt/entry.a30f63d0.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as a,u as n,o as r,c as l,a as e,t as s,b as d,w as c,d as p,e as f,p as x,f as h}from"./entry.
|
|
1
|
+
import{_ as a,u as n,o as r,c as l,a as e,t as s,b as d,w as c,d as p,e as f,p as x,f as h}from"./entry.ecf0d33f.js";const m=t=>(x("data-v-05a2b8a3"),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)),b={class:"max-w-520px text-center z-20"},_=["textContent"],w=["textContent"],y={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 n({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=f;return r(),l("div",u,[g,e("div",b,[e("h1",{class:"text-8xl sm:text-10xl font-medium mb-8",textContent:s(t.statusCode)},null,8,_),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,w),e("div",y,[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(()=>[p(s(t.backHome),1)]),_:1})])])])}}},z=a(S,[["__scopeId","data-v-05a2b8a3"]]);export{z as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as i,u as a,o as r,c as n,a as e,t as s,p as l,f as d}from"./entry.
|
|
1
|
+
import{_ as i,u as a,o as r,c as n,a as e,t as s,p as l,f as d}from"./entry.ecf0d33f.js";const c=t=>(l("data-v-c967d9a9"),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}'}]}),(u,b)=>(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-c967d9a9"]]);export{w as default};
|
package/dist/client/index.html
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
<html >
|
|
3
3
|
<head><meta charset="utf-8">
|
|
4
4
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
-
<link rel="stylesheet" href="/__nuxt-og-image/_nuxt/entry.
|
|
6
|
-
<link rel="modulepreload" as="script" crossorigin href="/__nuxt-og-image/_nuxt/entry.
|
|
5
|
+
<link rel="stylesheet" href="/__nuxt-og-image/_nuxt/entry.47125dcd.css">
|
|
6
|
+
<link rel="modulepreload" as="script" crossorigin href="/__nuxt-og-image/_nuxt/entry.ecf0d33f.js">
|
|
7
7
|
<link rel="prefetch" as="script" crossorigin href="/__nuxt-og-image/_nuxt/vanilla-picker-NKbIFE8h.23409a58.js">
|
|
8
8
|
<link rel="prefetch" as="style" href="/__nuxt-og-image/_nuxt/error-404.b751fa02.css">
|
|
9
|
-
<link rel="prefetch" as="script" crossorigin href="/__nuxt-og-image/_nuxt/error-404.
|
|
9
|
+
<link rel="prefetch" as="script" crossorigin href="/__nuxt-og-image/_nuxt/error-404.b9a11c8e.js">
|
|
10
10
|
<link rel="prefetch" as="style" href="/__nuxt-og-image/_nuxt/error-500.69009e70.css">
|
|
11
|
-
<link rel="prefetch" as="script" crossorigin href="/__nuxt-og-image/_nuxt/error-500.
|
|
12
|
-
<script type="module" src="/__nuxt-og-image/_nuxt/entry.
|
|
11
|
+
<link rel="prefetch" as="script" crossorigin href="/__nuxt-og-image/_nuxt/error-500.eb8225d3.js">
|
|
12
|
+
<script type="module" src="/__nuxt-og-image/_nuxt/entry.ecf0d33f.js" crossorigin></script><script>"use strict";(()=>{const a=window,e=document.documentElement,m=["dark","light"],c=window&&window.localStorage&&window.localStorage.getItem&&window.localStorage.getItem("nuxt-color-mode")||"system";let n=c==="system"?d():c;const l=e.getAttribute("data-color-mode-forced");l&&(n=l),i(n),a["__NUXT_COLOR_MODE__"]={preference:c,value:n,getColorScheme:d,addColorScheme:i,removeColorScheme:f};function i(o){const t=""+o+"",s="";e.classList?e.classList.add(t):e.className+=" "+t,s&&e.setAttribute("data-"+s,o)}function f(o){const t=""+o+"",s="";e.classList?e.classList.remove(t):e.className=e.className.replace(new RegExp(t,"g"),""),s&&e.removeAttribute("data-"+s)}function r(o){return a.matchMedia("(prefers-color-scheme"+o+")")}function d(){if(a.matchMedia&&r("").media!=="not all"){for(const o of m)if(r(":"+o).matches)return o}return"light"}})();
|
|
13
13
|
</script></head>
|
|
14
14
|
<body ><div id="__nuxt"></div><script type="application/json" id="__NUXT_DATA__" data-ssr="false">[{"_errors":1,"serverRendered":2,"data":3,"state":4},{},false,{},{}]</script>
|
|
15
15
|
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-og-image",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body>
|
package/dist/module.d.mts
CHANGED
|
@@ -80,9 +80,14 @@ interface OgImageOptions<T extends keyof OgImageComponents = 'NuxtSeo'> {
|
|
|
80
80
|
sharp?: SharpOptions;
|
|
81
81
|
fonts?: InputFontConfig[];
|
|
82
82
|
cacheMaxAgeSeconds?: number;
|
|
83
|
+
/**
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
_query?: Record<string, any>;
|
|
83
87
|
}
|
|
84
88
|
interface FontConfig {
|
|
85
89
|
name: string;
|
|
90
|
+
style?: string;
|
|
86
91
|
weight?: string | number;
|
|
87
92
|
path?: string;
|
|
88
93
|
key?: string;
|
package/dist/module.d.ts
CHANGED
|
@@ -80,9 +80,14 @@ interface OgImageOptions<T extends keyof OgImageComponents = 'NuxtSeo'> {
|
|
|
80
80
|
sharp?: SharpOptions;
|
|
81
81
|
fonts?: InputFontConfig[];
|
|
82
82
|
cacheMaxAgeSeconds?: number;
|
|
83
|
+
/**
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
_query?: Record<string, any>;
|
|
83
87
|
}
|
|
84
88
|
interface FontConfig {
|
|
85
89
|
name: string;
|
|
90
|
+
style?: string;
|
|
86
91
|
weight?: string | number;
|
|
87
92
|
path?: string;
|
|
88
93
|
key?: string;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { createHash } from 'node:crypto';
|
|
|
14
14
|
import { execa } from 'execa';
|
|
15
15
|
import terminate from 'terminate';
|
|
16
16
|
|
|
17
|
-
const version = "3.0.0-rc.
|
|
17
|
+
const version = "3.0.0-rc.9";
|
|
18
18
|
|
|
19
19
|
const autodetectableProviders = {
|
|
20
20
|
azure_static: "azure",
|
|
@@ -205,6 +205,9 @@ export {}
|
|
|
205
205
|
references.push({ path: resolve(nuxt.options.buildDir, `module/${module}.d.ts`) });
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
|
+
function isNuxtGenerate(nuxt = useNuxt()) {
|
|
209
|
+
return nuxt.options._generate || nuxt.options.nitro.static || nuxt.options.nitro.preset === "static";
|
|
210
|
+
}
|
|
208
211
|
|
|
209
212
|
const DEVTOOLS_UI_ROUTE = "/__nuxt-og-image";
|
|
210
213
|
const DEVTOOLS_UI_LOCAL_PORT = 3030;
|
|
@@ -295,7 +298,9 @@ function setupGenerateHandler(options, resolve, nuxt = useNuxt()) {
|
|
|
295
298
|
function setupPrerenderHandler(options, resolve, nuxt = useNuxt()) {
|
|
296
299
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
297
300
|
nitro.hooks.hook("prerender:config", async (nitroConfig) => {
|
|
298
|
-
|
|
301
|
+
nitroConfig.serverAssets = nitroConfig.serverAssets || [];
|
|
302
|
+
nitroConfig.serverAssets.push({ baseName: "nuxt-og-image:fonts", dir: resolve("./runtime/server/assets") });
|
|
303
|
+
applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve });
|
|
299
304
|
nitroConfig.wasm = nitroConfig.wasm || {};
|
|
300
305
|
nitroConfig.wasm.esmImport = false;
|
|
301
306
|
const prerenderingPages = (nuxt.options.nitro.prerender?.routes || []).some((r) => r && (!r.includes(".") || r.includes("*")));
|
|
@@ -365,7 +370,7 @@ function normaliseFontInput(fonts) {
|
|
|
365
370
|
return {
|
|
366
371
|
cacheKey: f,
|
|
367
372
|
name,
|
|
368
|
-
weight: weight ||
|
|
373
|
+
weight: weight || 400,
|
|
369
374
|
style: "normal",
|
|
370
375
|
path: void 0
|
|
371
376
|
};
|
|
@@ -395,6 +400,7 @@ const module = defineNuxtModule({
|
|
|
395
400
|
emojis: "noto",
|
|
396
401
|
renderer: "satori",
|
|
397
402
|
component: "NuxtSeo",
|
|
403
|
+
extension: "png",
|
|
398
404
|
width: 1200,
|
|
399
405
|
height: 600,
|
|
400
406
|
// default is to cache the image for 3 day (72 hours)
|
|
@@ -423,7 +429,6 @@ const module = defineNuxtModule({
|
|
|
423
429
|
const hasSharpDependency = !!await tryResolveModule("sharp");
|
|
424
430
|
const userConfiguredExtension = config.defaults.extension;
|
|
425
431
|
const hasConfiguredJpegs = userConfiguredExtension && ["jpeg", "jpg"].includes(userConfiguredExtension);
|
|
426
|
-
config.defaults.extension = userConfiguredExtension || (hasSharpDependency && targetCompatibility.sharp ? "jpg" : "png");
|
|
427
432
|
if (hasConfiguredJpegs && config.defaults.renderer !== "chromium") {
|
|
428
433
|
if (hasSharpDependency && !targetCompatibility.sharp) {
|
|
429
434
|
logger.warn(`Rendering JPEGs requires sharp which does not work with ${preset}. Images will be rendered as PNG at runtime.`);
|
|
@@ -468,6 +473,18 @@ const module = defineNuxtModule({
|
|
|
468
473
|
} else if (!hasChromeLocally && nuxt.options.dev && config.compatibility?.dev?.chromium === "node") {
|
|
469
474
|
await ensureChromium(logger);
|
|
470
475
|
}
|
|
476
|
+
await import('@resvg/resvg-js').catch(() => {
|
|
477
|
+
logger.warn("ReSVG is missing dependencies for environment. Falling back to WASM version, this may slow down PNG rendering.");
|
|
478
|
+
config.compatibility = defu(config.compatibility, {
|
|
479
|
+
dev: { resvg: "wasm-fs" },
|
|
480
|
+
prerender: { resvg: "wasm-fs" }
|
|
481
|
+
});
|
|
482
|
+
if (targetCompatibility.resvg === "node") {
|
|
483
|
+
config.compatibility = defu(config.compatibility, {
|
|
484
|
+
runtime: { resvg: "wasm" }
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
});
|
|
471
488
|
await installNuxtSiteConfig();
|
|
472
489
|
if (hasNuxtModule("@nuxt/content"))
|
|
473
490
|
addServerPlugin(resolve("./runtime/nitro/plugins/nuxt-content"));
|
|
@@ -616,7 +633,7 @@ declare module 'nitropack' {
|
|
|
616
633
|
}
|
|
617
634
|
interface NitroRuntimeHooks {
|
|
618
635
|
'nuxt-og-image:context': (ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
|
|
619
|
-
'nuxt-og-image:satori:vnodes': (vnodes: import('${typesPath}').VNode, ctx:
|
|
636
|
+
'nuxt-og-image:satori:vnodes': (vnodes: import('${typesPath}').VNode, ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
|
|
620
637
|
}
|
|
621
638
|
}
|
|
622
639
|
|
|
@@ -629,16 +646,26 @@ ${componentImports}
|
|
|
629
646
|
});
|
|
630
647
|
const cacheEnabled = typeof config.runtimeCacheStorage !== "undefined" && config.runtimeCacheStorage !== false;
|
|
631
648
|
const runtimeCacheStorage = typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver;
|
|
632
|
-
let baseCacheKey = runtimeCacheStorage === "default" ? `/cache/nuxt-og-image@${version}` : `/nuxt-og-image
|
|
649
|
+
let baseCacheKey = runtimeCacheStorage === "default" ? `/cache/nuxt-og-image@${version}` : `/nuxt-og-image/${version}`;
|
|
633
650
|
if (!cacheEnabled)
|
|
634
651
|
baseCacheKey = false;
|
|
652
|
+
if (!nuxt.options.dev && config.runtimeCacheStorage && typeof config.runtimeCacheStorage === "object") {
|
|
653
|
+
nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
|
|
654
|
+
nuxt.options.nitro.storage["nuxt-og-image"] = config.runtimeCacheStorage;
|
|
655
|
+
}
|
|
635
656
|
nuxt.hooks.hook("modules:done", async () => {
|
|
636
657
|
const normalisedFonts = normaliseFontInput(config.fonts);
|
|
637
|
-
if (!
|
|
658
|
+
if (!isNuxtGenerate() && nuxt.options.build) {
|
|
659
|
+
nuxt.options.nitro = nuxt.options.nitro || {};
|
|
638
660
|
nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
|
|
639
661
|
nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
|
|
640
|
-
normalisedFonts.filter((f) => !f.path && !f.key).forEach((
|
|
641
|
-
|
|
662
|
+
normalisedFonts.filter((f) => !f.path && !f.key).forEach((entry, key) => {
|
|
663
|
+
const { name, weight } = entry;
|
|
664
|
+
entry.path = `/__og-image__/font/${name}/${weight}.ttf`;
|
|
665
|
+
nuxt.options.nitro.prerender.routes.unshift(entry.path);
|
|
666
|
+
if (name === "Inter" && [400, 700].includes(Number(weight)))
|
|
667
|
+
entry.key = `nuxt-og-image:fonts:inter-latin-ext-${weight}-normal.woff`;
|
|
668
|
+
normalisedFonts[key] = entry;
|
|
642
669
|
});
|
|
643
670
|
}
|
|
644
671
|
const hasColorModeModule = hasNuxtModule("@nuxtjs/color-mode");
|
|
@@ -669,7 +696,7 @@ ${componentImports}
|
|
|
669
696
|
if (nuxt.options.dev) {
|
|
670
697
|
setupDevHandler(config, resolve);
|
|
671
698
|
setupDevToolsUI(config, resolve);
|
|
672
|
-
} else if (
|
|
699
|
+
} else if (isNuxtGenerate()) {
|
|
673
700
|
setupGenerateHandler(config, resolve);
|
|
674
701
|
} else if (nuxt.options.build) {
|
|
675
702
|
await setupBuildHandler(config, resolve);
|
package/dist/runtime/cache.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { type H3Error } from 'h3';
|
|
1
2
|
import type { OgImageRenderEventContext } from './types';
|
|
2
3
|
export declare function useOgImageBufferCache(ctx: OgImageRenderEventContext, options: {
|
|
3
4
|
baseCacheKey: string | false;
|
|
4
5
|
cacheMaxAgeSeconds?: number;
|
|
5
|
-
}): Promise<void | {
|
|
6
|
+
}): Promise<void | H3Error | {
|
|
6
7
|
cachedItem: false | BufferSource;
|
|
7
8
|
enabled: boolean;
|
|
8
9
|
update: (image: BufferSource) => Promise<void>;
|
package/dist/runtime/cache.mjs
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
import { prefixStorage } from "unstorage";
|
|
2
|
-
import { getQuery, handleCacheHeaders, setHeader, setHeaders } from "h3";
|
|
2
|
+
import { createError, getQuery, handleCacheHeaders, setHeader, setHeaders } from "h3";
|
|
3
3
|
import { withTrailingSlash } from "ufo";
|
|
4
4
|
import { hash } from "ohash";
|
|
5
5
|
import { useStorage } from "#imports";
|
|
6
6
|
export async function useOgImageBufferCache(ctx, options) {
|
|
7
7
|
const maxAge = Number(options.cacheMaxAgeSeconds);
|
|
8
|
-
|
|
8
|
+
let enabled = !import.meta.dev && import.meta.env.MODE !== "test" && maxAge > 0;
|
|
9
9
|
const cache = prefixStorage(useStorage(), withTrailingSlash(options.baseCacheKey || "/"));
|
|
10
10
|
const key = ctx.key;
|
|
11
11
|
let cachedItem = false;
|
|
12
|
-
if (enabled
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (handleCacheHeaders(ctx.e, {
|
|
20
|
-
modifiedTime: new Date(headers["last-modified"]),
|
|
21
|
-
etag: headers.etag,
|
|
22
|
-
maxAge
|
|
23
|
-
}))
|
|
24
|
-
return;
|
|
25
|
-
setHeaders(ctx.e, headers);
|
|
26
|
-
} else {
|
|
27
|
-
await cache.removeItem(key).catch(() => {
|
|
12
|
+
if (enabled) {
|
|
13
|
+
const hasItem = await cache.hasItem(key).catch((e) => {
|
|
14
|
+
enabled = false;
|
|
15
|
+
return createError({
|
|
16
|
+
cause: e,
|
|
17
|
+
statusCode: 500,
|
|
18
|
+
statusMessage: `[Nuxt OG Image] Failed to connect to cache ${options.baseCacheKey}. Response from cache: ${e.message}`
|
|
28
19
|
});
|
|
20
|
+
});
|
|
21
|
+
if (hasItem instanceof Error)
|
|
22
|
+
return hasItem;
|
|
23
|
+
if (hasItem) {
|
|
24
|
+
const { value, expiresAt, headers } = await cache.getItem(key).catch(() => ({
|
|
25
|
+
value: null,
|
|
26
|
+
expiresAt: Date.now()
|
|
27
|
+
}));
|
|
28
|
+
if (typeof getQuery(ctx.e).purge !== "undefined") {
|
|
29
|
+
await cache.removeItem(key).catch(() => {
|
|
30
|
+
});
|
|
31
|
+
} else if (expiresAt > Date.now()) {
|
|
32
|
+
cachedItem = Buffer.from(value, "base64");
|
|
33
|
+
if (handleCacheHeaders(ctx.e, {
|
|
34
|
+
modifiedTime: new Date(headers["last-modified"]),
|
|
35
|
+
etag: headers.etag,
|
|
36
|
+
maxAge
|
|
37
|
+
}))
|
|
38
|
+
return;
|
|
39
|
+
setHeaders(ctx.e, headers);
|
|
40
|
+
} else {
|
|
41
|
+
await cache.removeItem(key).catch(() => {
|
|
42
|
+
});
|
|
43
|
+
}
|
|
29
44
|
}
|
|
30
45
|
}
|
|
31
46
|
if (!enabled) {
|
|
@@ -117,7 +117,7 @@ if (typeof props.icon === 'string' && !runtimeConfig.hasNuxtIcon && process.dev)
|
|
|
117
117
|
{{ description }}
|
|
118
118
|
</p>
|
|
119
119
|
</div>
|
|
120
|
-
<div v-if="icon" style="width: 30%;" class="flex justify-end">
|
|
120
|
+
<div v-if="Boolean(icon)" style="width: 30%;" class="flex justify-end">
|
|
121
121
|
<IconComponent :name="icon" size="250px" style="margin: 0 auto; opacity: 0.7;" />
|
|
122
122
|
</div>
|
|
123
123
|
</div>
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { defu } from "defu";
|
|
2
2
|
import { appendHeader } from "h3";
|
|
3
|
-
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
4
|
-
import { withoutBase } from "ufo";
|
|
5
3
|
import { getOgImagePath, separateProps, useOgImageRuntimeConfig } from "../utils.mjs";
|
|
6
4
|
import { createOgImageMeta, normaliseOptions } from "../nuxt/utils.mjs";
|
|
7
|
-
import {
|
|
5
|
+
import { createNitroRouteRuleMatcher } from "../nitro/kit.mjs";
|
|
6
|
+
import { useNuxtApp, useRequestEvent, useRoute } from "#imports";
|
|
8
7
|
export function defineOgImage(_options = {}) {
|
|
9
8
|
if (!import.meta.server)
|
|
10
9
|
return;
|
|
11
10
|
const nuxtApp = useNuxtApp();
|
|
12
11
|
const ogImageInstances = nuxtApp.ssrContext._ogImageInstances || [];
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
);
|
|
17
|
-
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(
|
|
18
|
-
withoutBase(basePath.split("?")[0], useRuntimeConfig().app.baseURL)
|
|
19
|
-
).reverse()).ogImage;
|
|
12
|
+
const route = useRoute();
|
|
13
|
+
const basePath = route.path || "/";
|
|
14
|
+
const routeRuleMatcher = createNitroRouteRuleMatcher();
|
|
15
|
+
const routeRules = routeRuleMatcher(basePath).ogImage;
|
|
20
16
|
if (!_options || nuxtApp.ssrContext?.event.context._nitro?.routeRules?.ogImage === false || typeof routeRules !== "undefined" && routeRules === false) {
|
|
21
17
|
ogImageInstances.forEach((e) => {
|
|
22
18
|
e.dispose();
|
|
@@ -27,6 +23,8 @@ export function defineOgImage(_options = {}) {
|
|
|
27
23
|
const options = normaliseOptions({
|
|
28
24
|
..._options
|
|
29
25
|
});
|
|
26
|
+
if (route.query)
|
|
27
|
+
options._query = route.query;
|
|
30
28
|
const { defaults } = useOgImageRuntimeConfig();
|
|
31
29
|
const resolvedOptions = normaliseOptions(defu(separateProps(_options), separateProps(routeRules), defaults));
|
|
32
30
|
if (_options.url) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHeadCore } from "@unhead/vue";
|
|
2
2
|
import { renderSSRHead } from "@unhead/ssr";
|
|
3
|
-
import { useOgImageRuntimeConfig } from "../../utils.mjs";
|
|
3
|
+
import { normaliseFontInput, useOgImageRuntimeConfig } from "../../utils.mjs";
|
|
4
4
|
import { applyEmojis } from "./applyEmojis.mjs";
|
|
5
5
|
import { fetchIsland } from "./fetchIsland.mjs";
|
|
6
6
|
import { theme } from "#nuxt-og-image/unocss-config.mjs";
|
|
@@ -11,7 +11,8 @@ export async function devIframeTemplate(ctx) {
|
|
|
11
11
|
const head = createHeadCore();
|
|
12
12
|
head.push(island.head);
|
|
13
13
|
let defaultFontFamily = "sans-serif";
|
|
14
|
-
const
|
|
14
|
+
const normalisedFonts = normaliseFontInput([...options.fonts || [], ...fonts]);
|
|
15
|
+
const firstFont = normalisedFonts[0];
|
|
15
16
|
if (firstFont)
|
|
16
17
|
defaultFontFamily = firstFont.name;
|
|
17
18
|
await applyEmojis(ctx, island);
|
|
@@ -4,7 +4,7 @@ export function fetchIsland({ options, e }) {
|
|
|
4
4
|
if (!options.component) {
|
|
5
5
|
throw createError({
|
|
6
6
|
statusCode: 500,
|
|
7
|
-
statusMessage: `Nuxt OG Image
|
|
7
|
+
statusMessage: `[Nuxt OG Image] Rendering an invalid component. Received options: ${JSON.stringify(options)}.`
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
const hashId = hash([options.component, options]);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { OgImageOptions } from '../../types';
|
|
2
|
+
export declare function htmlDecodeQuotes(html: string): string;
|
|
2
3
|
export declare function decodeHtml(html: string): string;
|
|
3
4
|
export declare function extractAndNormaliseOgImageOptions(html: string): OgImageOptions | false;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export function htmlDecodeQuotes(html) {
|
|
2
|
+
return html.replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'");
|
|
3
|
+
}
|
|
1
4
|
export function decodeHtml(html) {
|
|
2
5
|
return html.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/¢/g, "\xA2").replace(/£/g, "\xA3").replace(/¥/g, "\xA5").replace(/€/g, "\u20AC").replace(/©/g, "\xA9").replace(/®/g, "\xAE").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(///g, "/").replace(/&#([0-9]+);/g, (full, int) => {
|
|
3
6
|
return String.fromCharCode(Number.parseInt(int));
|
|
@@ -11,7 +11,13 @@ export async function fetchPathHtmlAndExtractOptions(e, path, key) {
|
|
|
11
11
|
} catch (err) {
|
|
12
12
|
return createError({
|
|
13
13
|
statusCode: 500,
|
|
14
|
-
statusMessage: `Failed to read the path ${path} for og-image extraction. ${err.message}.`
|
|
14
|
+
statusMessage: `[Nuxt OG Image] Failed to read the path ${path} for og-image extraction. ${err.message}.`
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (typeof html !== "string") {
|
|
18
|
+
return createError({
|
|
19
|
+
statusCode: 500,
|
|
20
|
+
statusMessage: `[Nuxt OG Image] Got invalid response from ${path} for og-image extraction.`
|
|
15
21
|
});
|
|
16
22
|
}
|
|
17
23
|
const payload = extractAndNormaliseOgImageOptions(html);
|
|
@@ -14,7 +14,8 @@ const ChromiumRenderer = {
|
|
|
14
14
|
if (screenshot instanceof Error) {
|
|
15
15
|
return createError({
|
|
16
16
|
statusCode: 400,
|
|
17
|
-
|
|
17
|
+
cause: screenshot,
|
|
18
|
+
statusMessage: `[Nuxt OG Image] Failed to create screenshot ${screenshot.message}.`
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
return screenshot;
|
|
@@ -7,10 +7,10 @@ import { useResvg, useSatori, useSharp } from "./instances.mjs";
|
|
|
7
7
|
import { theme } from "#nuxt-og-image/unocss-config.mjs";
|
|
8
8
|
export async function createSvg(event) {
|
|
9
9
|
const { options } = event;
|
|
10
|
-
const { fonts, satoriOptions } = useOgImageRuntimeConfig();
|
|
10
|
+
const { fonts, satoriOptions: _satoriOptions } = useOgImageRuntimeConfig();
|
|
11
11
|
const vnodes = await createVNodes(event);
|
|
12
12
|
await event._nitro.hooks.callHook("nuxt-og-image:satori:vnodes", vnodes, event);
|
|
13
|
-
const normalisedFonts = normaliseFontInput([...
|
|
13
|
+
const normalisedFonts = normaliseFontInput([...event.options.fonts || [], ...fonts]);
|
|
14
14
|
const localFontPromises = [];
|
|
15
15
|
const preloadedFonts = [];
|
|
16
16
|
for (const font of normalisedFonts) {
|
|
@@ -30,15 +30,16 @@ export async function createSvg(event) {
|
|
|
30
30
|
}
|
|
31
31
|
const awaitedFonts = await Promise.all(localFontPromises);
|
|
32
32
|
const satori = await useSatori();
|
|
33
|
-
|
|
33
|
+
const satoriOptions = defu(options.satori, _satoriOptions, {
|
|
34
34
|
fonts: [...preloadedFonts, ...awaitedFonts].map((_f) => {
|
|
35
|
-
return {
|
|
35
|
+
return { name: _f.name, data: _f.data, style: _f.style, weight: Number(_f.weight) };
|
|
36
36
|
}),
|
|
37
37
|
tailwindConfig: { theme },
|
|
38
38
|
embedFont: true,
|
|
39
39
|
width: options.width,
|
|
40
40
|
height: options.height
|
|
41
|
-
})
|
|
41
|
+
});
|
|
42
|
+
return satori(vnodes, satoriOptions);
|
|
42
43
|
}
|
|
43
44
|
async function createPng(event) {
|
|
44
45
|
const { resvgOptions } = useOgImageRuntimeConfig();
|
|
@@ -2,6 +2,7 @@ import { html as convertHtmlToSatori } from "satori-html";
|
|
|
2
2
|
import { fetchIsland } from "../../html/fetchIsland.mjs";
|
|
3
3
|
import { applyInlineCss } from "../../html/applyInlineCss.mjs";
|
|
4
4
|
import { applyEmojis } from "../../html/applyEmojis.mjs";
|
|
5
|
+
import { htmlDecodeQuotes } from "../../options/extract.mjs";
|
|
5
6
|
import { walkSatoriTree } from "./utils.mjs";
|
|
6
7
|
import unocss from "./plugins/unocss.mjs";
|
|
7
8
|
import emojis from "./plugins/emojis.mjs";
|
|
@@ -13,6 +14,7 @@ export async function createVNodes(ctx) {
|
|
|
13
14
|
let html = ctx.options.html;
|
|
14
15
|
if (!html) {
|
|
15
16
|
const island = await fetchIsland(ctx);
|
|
17
|
+
island.html = htmlDecodeQuotes(island.html);
|
|
16
18
|
await applyInlineCss(ctx, island);
|
|
17
19
|
await applyEmojis(ctx, island);
|
|
18
20
|
html = island.html;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { parseURL,
|
|
1
|
+
import { parseURL, withQuery, withoutTrailingSlash } from "ufo";
|
|
2
2
|
import { createError, getQuery } from "h3";
|
|
3
3
|
import { defu } from "defu";
|
|
4
|
-
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
5
4
|
import { fetchPathHtmlAndExtractOptions } from "../options/fetch.mjs";
|
|
6
5
|
import { prerenderOptionsCache } from "../cache/prerender.mjs";
|
|
7
6
|
import { useChromiumRenderer, useSatoriRenderer } from "../renderers/satori/instances.mjs";
|
|
8
7
|
import { separateProps, useOgImageRuntimeConfig } from "../../utils.mjs";
|
|
9
8
|
import { resolvePathCacheKey } from "../../nitro/utils.mjs";
|
|
10
|
-
import {
|
|
9
|
+
import { createNitroRouteRuleMatcher } from "../../nitro/kit.mjs";
|
|
10
|
+
import { useNitroApp } from "#internal/nitro/app";
|
|
11
11
|
export async function resolveRendererContext(e) {
|
|
12
12
|
const runtimeConfig = useOgImageRuntimeConfig();
|
|
13
13
|
const path = parseURL(e.path).pathname;
|
|
@@ -15,21 +15,23 @@ export async function resolveRendererContext(e) {
|
|
|
15
15
|
if (!extension) {
|
|
16
16
|
return createError({
|
|
17
17
|
statusCode: 400,
|
|
18
|
-
statusMessage: `Missing OG Image type.`
|
|
18
|
+
statusMessage: `[Nuxt OG Image] Missing OG Image type.`
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
if (!["png", "jpeg", "jpg", "svg", "html", "json"].includes(extension)) {
|
|
22
22
|
return createError({
|
|
23
23
|
statusCode: 400,
|
|
24
|
-
statusMessage: `Unknown OG Image type ${extension}.`
|
|
24
|
+
statusMessage: `[Nuxt OG Image] Unknown OG Image type ${extension}.`
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
const basePath = withoutTrailingSlash(
|
|
28
|
-
path.replace(`/__og-image__/image`, "").replace(`/og.${extension}`, "")
|
|
29
|
-
);
|
|
30
27
|
let queryParams = { ...getQuery(e) };
|
|
31
28
|
queryParams.props = JSON.parse(queryParams.props || "{}");
|
|
32
29
|
queryParams = separateProps(queryParams);
|
|
30
|
+
let basePath = withoutTrailingSlash(
|
|
31
|
+
path.replace(`/__og-image__/image`, "").replace(`/og.${extension}`, "")
|
|
32
|
+
);
|
|
33
|
+
if (queryParams._query)
|
|
34
|
+
basePath = withQuery(basePath, JSON.parse(queryParams._query));
|
|
33
35
|
const isDebugJsonPayload = extension === "json" && runtimeConfig.debug;
|
|
34
36
|
const key = resolvePathCacheKey(e, basePath);
|
|
35
37
|
let options = queryParams.options;
|
|
@@ -44,12 +46,8 @@ export async function resolveRendererContext(e) {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
delete queryParams.options;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(
|
|
51
|
-
withoutBase(basePath.split("?")[0], useRuntimeConfig().app.baseURL)
|
|
52
|
-
).reverse());
|
|
49
|
+
const routeRuleMatcher = createNitroRouteRuleMatcher();
|
|
50
|
+
const routeRules = routeRuleMatcher(basePath);
|
|
53
51
|
if (typeof routeRules.ogImage === "undefined" && !options) {
|
|
54
52
|
return createError({
|
|
55
53
|
statusCode: 400,
|
|
@@ -61,7 +59,7 @@ export async function resolveRendererContext(e) {
|
|
|
61
59
|
if (!options) {
|
|
62
60
|
return createError({
|
|
63
61
|
statusCode: 404,
|
|
64
|
-
statusMessage: "OG Image not found."
|
|
62
|
+
statusMessage: "[Nuxt OG Image] OG Image not found."
|
|
65
63
|
});
|
|
66
64
|
}
|
|
67
65
|
let renderer;
|
|
@@ -76,7 +74,7 @@ export async function resolveRendererContext(e) {
|
|
|
76
74
|
if (!renderer || renderer.__unenv__) {
|
|
77
75
|
throw createError({
|
|
78
76
|
statusCode: 400,
|
|
79
|
-
statusMessage: `Renderer ${options.renderer} is missing.`
|
|
77
|
+
statusMessage: `[Nuxt OG Image] Renderer ${options.renderer} is missing.`
|
|
80
78
|
});
|
|
81
79
|
}
|
|
82
80
|
const ctx = {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
2
|
+
import { defu } from "defu";
|
|
3
|
+
import { withoutBase, withoutTrailingSlash } from "ufo";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export function withoutQuery(path) {
|
|
6
|
+
return path.split("?")[0];
|
|
7
|
+
}
|
|
8
|
+
export function createNitroRouteRuleMatcher() {
|
|
9
|
+
const { nitro, app } = useRuntimeConfig();
|
|
10
|
+
const _routeRulesMatcher = toRouteMatcher(
|
|
11
|
+
createRadixRouter({
|
|
12
|
+
routes: Object.fromEntries(
|
|
13
|
+
Object.entries(nitro?.routeRules || {}).map(([path, rules]) => [withoutTrailingSlash(path), rules])
|
|
14
|
+
)
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
return (path) => {
|
|
18
|
+
return defu({}, ..._routeRulesMatcher.matchAll(
|
|
19
|
+
// radix3 does not support trailing slashes
|
|
20
|
+
withoutBase(withoutTrailingSlash(withoutQuery(path)), app.baseURL)
|
|
21
|
+
).reverse());
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("nitropack").NitroAppPlugin;
|
|
1
|
+
declare const _default: import("nitropack/dist/runtime/plugin").NitroAppPlugin;
|
|
2
2
|
export default _default;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { parseURL
|
|
1
|
+
import { parseURL } from "ufo";
|
|
2
2
|
import { defineNitroPlugin } from "nitropack/dist/runtime/plugin";
|
|
3
|
-
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
4
|
-
import { defu } from "defu";
|
|
5
3
|
import { extractAndNormaliseOgImageOptions } from "../../core/options/extract.mjs";
|
|
6
4
|
import { prerenderOptionsCache } from "../../core/cache/prerender.mjs";
|
|
7
5
|
import { isInternalRoute } from "../../utils.pure.mjs";
|
|
8
6
|
import { resolvePathCacheKey } from "../utils.mjs";
|
|
9
|
-
import {
|
|
7
|
+
import { createNitroRouteRuleMatcher } from "../kit.mjs";
|
|
10
8
|
export default defineNitroPlugin(async (nitro) => {
|
|
11
9
|
if (!import.meta.prerender)
|
|
12
10
|
return;
|
|
@@ -15,14 +13,9 @@ export default defineNitroPlugin(async (nitro) => {
|
|
|
15
13
|
const path = parseURL(ctx.event.path).pathname;
|
|
16
14
|
if (isInternalRoute(path))
|
|
17
15
|
return;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
);
|
|
22
|
-
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(
|
|
23
|
-
withoutBase(path.split("?")[0], runtimeConfig.app.baseURL)
|
|
24
|
-
).reverse()).ogImage;
|
|
25
|
-
if (routeRules === false)
|
|
16
|
+
const routeRuleMatcher = createNitroRouteRuleMatcher();
|
|
17
|
+
const routeRules = routeRuleMatcher(path);
|
|
18
|
+
if (routeRules.ogImage === false)
|
|
26
19
|
return;
|
|
27
20
|
const options = extractAndNormaliseOgImageOptions([
|
|
28
21
|
head.join("\n"),
|
|
@@ -2,12 +2,11 @@ import { withoutLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
|
2
2
|
import { hash } from "ohash";
|
|
3
3
|
import { normalizeKey } from "unstorage";
|
|
4
4
|
import { getQuery } from "h3";
|
|
5
|
-
import { useSiteConfig } from "#imports";
|
|
6
5
|
export function resolvePathCacheKey(e, path) {
|
|
7
|
-
const siteConfig =
|
|
6
|
+
const siteConfig = e.context.siteConfig.get();
|
|
8
7
|
const basePath = withoutTrailingSlash(withoutLeadingSlash(normalizeKey(path || e.path)));
|
|
9
8
|
return [
|
|
10
|
-
!basePath ? "index" : basePath,
|
|
9
|
+
!basePath || basePath === "/" ? "index" : basePath,
|
|
11
10
|
hash([
|
|
12
11
|
basePath,
|
|
13
12
|
siteConfig.url,
|