nuxt-og-image 3.0.0-rc.1 → 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.bca1abaf.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.f2e056ce.js → entry.ecf0d33f.js} +47 -47
- package/dist/client/_nuxt/{error-404.1b7ec865.js → error-404.b9a11c8e.js} +1 -1
- package/dist/client/_nuxt/{error-500.1f097e6f.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 +30 -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/c430c582-423d-48d2-8592-39b7bfd61658.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.`);
|
|
@@ -474,6 +479,11 @@ const module = defineNuxtModule({
|
|
|
474
479
|
dev: { resvg: "wasm-fs" },
|
|
475
480
|
prerender: { resvg: "wasm-fs" }
|
|
476
481
|
});
|
|
482
|
+
if (targetCompatibility.resvg === "node") {
|
|
483
|
+
config.compatibility = defu(config.compatibility, {
|
|
484
|
+
runtime: { resvg: "wasm" }
|
|
485
|
+
});
|
|
486
|
+
}
|
|
477
487
|
});
|
|
478
488
|
await installNuxtSiteConfig();
|
|
479
489
|
if (hasNuxtModule("@nuxt/content"))
|
|
@@ -623,7 +633,7 @@ declare module 'nitropack' {
|
|
|
623
633
|
}
|
|
624
634
|
interface NitroRuntimeHooks {
|
|
625
635
|
'nuxt-og-image:context': (ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
|
|
626
|
-
'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>
|
|
627
637
|
}
|
|
628
638
|
}
|
|
629
639
|
|
|
@@ -636,16 +646,26 @@ ${componentImports}
|
|
|
636
646
|
});
|
|
637
647
|
const cacheEnabled = typeof config.runtimeCacheStorage !== "undefined" && config.runtimeCacheStorage !== false;
|
|
638
648
|
const runtimeCacheStorage = typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver;
|
|
639
|
-
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}`;
|
|
640
650
|
if (!cacheEnabled)
|
|
641
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
|
+
}
|
|
642
656
|
nuxt.hooks.hook("modules:done", async () => {
|
|
643
657
|
const normalisedFonts = normaliseFontInput(config.fonts);
|
|
644
|
-
if (!
|
|
658
|
+
if (!isNuxtGenerate() && nuxt.options.build) {
|
|
659
|
+
nuxt.options.nitro = nuxt.options.nitro || {};
|
|
645
660
|
nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
|
|
646
661
|
nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
|
|
647
|
-
normalisedFonts.filter((f) => !f.path && !f.key).forEach((
|
|
648
|
-
|
|
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;
|
|
649
669
|
});
|
|
650
670
|
}
|
|
651
671
|
const hasColorModeModule = hasNuxtModule("@nuxtjs/color-mode");
|
|
@@ -676,7 +696,7 @@ ${componentImports}
|
|
|
676
696
|
if (nuxt.options.dev) {
|
|
677
697
|
setupDevHandler(config, resolve);
|
|
678
698
|
setupDevToolsUI(config, resolve);
|
|
679
|
-
} else if (
|
|
699
|
+
} else if (isNuxtGenerate()) {
|
|
680
700
|
setupGenerateHandler(config, resolve);
|
|
681
701
|
} else if (nuxt.options.build) {
|
|
682
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,
|