nuxt-og-image 2.2.4 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/client/200.html +8 -8
- package/dist/client/404.html +8 -8
- package/dist/client/_nuxt/IconCSS.8f429b14.css +1 -0
- package/dist/client/_nuxt/IconCSS.ac398b56.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/d1d517c3-4927-4803-bbb0-d94e9d3e9581.json +1 -0
- package/dist/client/_nuxt/entry.434c2c45.css +1 -0
- package/dist/client/_nuxt/entry.bdb8a8d5.js +137 -0
- package/dist/client/_nuxt/{error-404.407d76a3.js → error-404.f37119e7.js} +1 -1
- package/dist/client/_nuxt/{error-500.531c4147.js → error-500.74b0a30f.js} +1 -1
- package/dist/client/grid.png +0 -0
- package/dist/client/index.html +8 -8
- package/dist/module.d.mts +43 -39
- package/dist/module.d.ts +43 -39
- package/dist/module.json +1 -1
- package/dist/module.mjs +341 -667
- package/dist/runtime/cache.d.ts +4 -4
- package/dist/runtime/cache.mjs +2 -1
- package/dist/runtime/components/OgImage/Cached.mjs +1 -1
- package/dist/runtime/components/OgImage/Dynamic.mjs +1 -1
- package/dist/runtime/components/OgImage/Screenshot.mjs +1 -1
- package/dist/runtime/components/OgImage/Static.mjs +1 -1
- package/dist/runtime/components/OgImage/WithoutCache.mjs +1 -1
- package/dist/runtime/components/OgImage/index.mjs +1 -1
- package/dist/runtime/components/Templates/Community/Nuxt.vue +183 -0
- package/dist/runtime/components/Templates/Official/BrandedLogo.vue +28 -0
- package/dist/runtime/components/Templates/Official/Fallback.vue +147 -0
- package/dist/runtime/components/Templates/Official/SimpleBlog.vue +33 -0
- package/dist/runtime/components/Templates/Official/Wave.vue +33 -0
- package/dist/runtime/components/Templates/Official/WithEmoji.vue +27 -0
- package/dist/runtime/composables/defineOgImage.d.ts +10 -6
- package/dist/runtime/composables/defineOgImage.mjs +21 -9
- package/dist/runtime/core/bindings/chromium/node.d.ts +2 -0
- package/dist/runtime/{nitro/providers/browser/universal.mjs → core/bindings/chromium/node.mjs} +3 -3
- package/dist/runtime/core/bindings/resvg/node.d.ts +6 -0
- package/dist/runtime/core/bindings/resvg/node.mjs +5 -0
- package/dist/runtime/core/bindings/resvg/wasm.d.ts +40 -0
- package/dist/runtime/core/bindings/resvg/wasm.mjs +7 -0
- package/dist/runtime/core/bindings/satori/node.d.ts +6 -0
- package/dist/runtime/core/bindings/satori/node.mjs +5 -0
- package/dist/runtime/core/bindings/satori/yoga-wasm.d.ts +6 -0
- package/dist/runtime/core/bindings/satori/yoga-wasm.mjs +7 -0
- package/dist/runtime/core/bindings/sharp/node.d.ts +2 -0
- package/dist/runtime/core/bindings/sharp/node.mjs +2 -0
- package/dist/runtime/core/bindings/sharp/wasm.d.ts +2 -0
- package/dist/runtime/core/bindings/sharp/wasm.mjs +2 -0
- package/dist/runtime/core/cache/prerender.d.ts +6 -0
- package/dist/runtime/core/cache/prerender.mjs +6 -0
- package/dist/runtime/core/env/assets.d.ts +2 -0
- package/dist/runtime/core/env/assets.mjs +15 -0
- package/dist/runtime/core/font/cache.d.ts +1 -0
- package/dist/runtime/core/font/cache.mjs +1 -0
- package/dist/runtime/core/font/fetch.d.ts +3 -0
- package/dist/runtime/core/font/fetch.mjs +29 -0
- package/dist/runtime/core/html/fetch.d.ts +3 -0
- package/dist/runtime/core/html/fetch.mjs +117 -0
- package/dist/runtime/core/options/extract.d.ts +3 -0
- package/dist/runtime/{nitro/utils-pure.mjs → core/options/extract.mjs} +23 -21
- package/dist/runtime/core/options/fetch.d.ts +3 -0
- package/dist/runtime/core/options/fetch.mjs +21 -0
- package/dist/runtime/core/options/normalise.d.ts +2 -0
- package/dist/runtime/{composables/util.mjs → core/options/normalise.mjs} +9 -6
- package/dist/runtime/core/renderers/chromium/index.d.ts +3 -0
- package/dist/runtime/core/renderers/chromium/index.mjs +26 -0
- package/dist/runtime/core/renderers/chromium/screenshot.d.ts +6 -0
- package/dist/runtime/core/renderers/chromium/screenshot.mjs +47 -0
- package/dist/runtime/core/renderers/satori/fonts.d.ts +3 -0
- package/dist/runtime/core/renderers/satori/fonts.mjs +8 -0
- package/dist/runtime/core/renderers/satori/index.d.ts +5 -0
- package/dist/runtime/core/renderers/satori/index.mjs +53 -0
- package/dist/runtime/core/renderers/satori/instances.d.ts +39 -0
- package/dist/runtime/core/renderers/satori/instances.mjs +17 -0
- package/dist/runtime/{nitro → core}/renderers/satori/plugins/encoding.mjs +1 -1
- package/dist/runtime/{nitro → core}/renderers/satori/plugins/imageSrc.mjs +9 -14
- package/dist/runtime/{nitro → core}/renderers/satori/plugins/twClasses.mjs +1 -0
- package/dist/runtime/core/renderers/satori/utils.d.ts +4 -0
- package/dist/runtime/core/renderers/satori/utils.mjs +20 -0
- package/dist/runtime/core/renderers/satori/vnodes.d.ts +3 -0
- package/dist/runtime/core/renderers/satori/vnodes.mjs +21 -0
- package/dist/runtime/core/utils/resolveRendererContext.d.ts +7 -0
- package/dist/runtime/core/utils/resolveRendererContext.mjs +76 -0
- package/dist/runtime/nitro/plugins/nuxt-content.d.ts +2 -0
- package/dist/runtime/nitro/plugins/nuxt-content.mjs +50 -0
- package/dist/runtime/nitro/plugins/prerender.d.ts +2 -3
- package/dist/runtime/nitro/plugins/prerender.mjs +25 -33
- package/dist/runtime/nuxt/plugins/nuxt-content-canonical-urls.mjs +29 -0
- package/dist/runtime/nuxt/plugins/route-rule-og-image.server.d.ts +2 -0
- package/dist/runtime/nuxt/plugins/route-rule-og-image.server.mjs +72 -0
- package/dist/runtime/{nitro/routes/debug.d.ts → server/routes/__og-image__/debug.json.d.ts} +1 -1
- package/dist/runtime/{nitro/routes/debug.mjs → server/routes/__og-image__/debug.json.mjs} +3 -2
- package/dist/runtime/server/routes/__og-image__/font-[name]-[weight].[extension].mjs +30 -0
- package/dist/runtime/server/routes/__og-image__/image-[path]-og.[extension].mjs +44 -0
- package/dist/runtime/types.d.ts +29 -24
- package/dist/runtime/utilts.d.ts +2 -0
- package/dist/runtime/utilts.mjs +8 -0
- package/dist/types.d.mts +3 -2
- package/dist/types.d.ts +3 -2
- package/package.json +37 -22
- package/dist/client/_nuxt/IconCSS.4a9d43d0.css +0 -1
- package/dist/client/_nuxt/IconCSS.9c30257a.js +0 -1
- package/dist/client/_nuxt/ImageLoader.752b0c7a.js +0 -1
- package/dist/client/_nuxt/ImageLoader.7571516f.css +0 -1
- package/dist/client/_nuxt/builds/meta/bb64bb30-cf6f-4625-97ba-06e6a0d3f8d1.json +0 -1
- package/dist/client/_nuxt/entry.39e39f51.css +0 -1
- package/dist/client/_nuxt/entry.ac864471.js +0 -135
- package/dist/client/_nuxt/index.dc1538d5.js +0 -1
- package/dist/client/_nuxt/index.ffbea0a9.css +0 -1
- package/dist/client/_nuxt/options.a77f5921.js +0 -1
- package/dist/client/_nuxt/png.41e0b446.js +0 -1
- package/dist/client/_nuxt/shiki.d4e62362.js +0 -7
- package/dist/client/_nuxt/svg.b8198280.js +0 -1
- package/dist/client/_nuxt/vnodes.67720126.js +0 -1
- package/dist/client/options/index.html +0 -15
- package/dist/client/png/index.html +0 -15
- package/dist/client/svg/index.html +0 -15
- package/dist/client/vnodes/index.html +0 -15
- package/dist/runtime/browserUtil.d.ts +0 -5
- package/dist/runtime/browserUtil.mjs +0 -41
- package/dist/runtime/components/OgImageTemplate/Fallback.vue +0 -161
- package/dist/runtime/composables/util.d.ts +0 -2
- package/dist/runtime/nitro/middleware/og.png.mjs +0 -69
- package/dist/runtime/nitro/middleware/playground.d.ts +0 -2
- package/dist/runtime/nitro/middleware/playground.mjs +0 -27
- package/dist/runtime/nitro/providers/browser/lambda.d.ts +0 -1
- package/dist/runtime/nitro/providers/browser/lambda.mjs +0 -9
- package/dist/runtime/nitro/providers/browser/playwright.d.ts +0 -1
- package/dist/runtime/nitro/providers/browser/playwright.mjs +0 -22
- package/dist/runtime/nitro/providers/browser/universal.d.ts +0 -2
- package/dist/runtime/nitro/providers/png/resvg-node.d.ts +0 -4
- package/dist/runtime/nitro/providers/png/resvg-node.mjs +0 -6
- package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +0 -3
- package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +0 -11
- package/dist/runtime/nitro/providers/png/svg2png.d.ts +0 -3
- package/dist/runtime/nitro/providers/png/svg2png.mjs +0 -11
- package/dist/runtime/nitro/providers/satori/default.d.ts +0 -2
- package/dist/runtime/nitro/providers/satori/default.mjs +0 -4
- package/dist/runtime/nitro/providers/satori/yoga-wasm.d.ts +0 -3
- package/dist/runtime/nitro/providers/satori/yoga-wasm.mjs +0 -10
- package/dist/runtime/nitro/renderers/browser.d.ts +0 -3
- package/dist/runtime/nitro/renderers/browser.mjs +0 -36
- package/dist/runtime/nitro/renderers/satori/index.d.ts +0 -3
- package/dist/runtime/nitro/renderers/satori/index.mjs +0 -58
- package/dist/runtime/nitro/renderers/satori/utils.d.ts +0 -4
- package/dist/runtime/nitro/renderers/satori/utils.mjs +0 -60
- package/dist/runtime/nitro/routes/font.mjs +0 -22
- package/dist/runtime/nitro/routes/html.d.ts +0 -2
- package/dist/runtime/nitro/routes/html.mjs +0 -178
- package/dist/runtime/nitro/routes/options.d.ts +0 -3
- package/dist/runtime/nitro/routes/options.mjs +0 -35
- package/dist/runtime/nitro/routes/svg.mjs +0 -19
- package/dist/runtime/nitro/routes/vnode.d.ts +0 -2
- package/dist/runtime/nitro/routes/vnode.mjs +0 -19
- package/dist/runtime/nitro/utils-pure.d.ts +0 -3
- package/dist/runtime/nitro/utils.d.ts +0 -18
- package/dist/runtime/nitro/utils.mjs +0 -108
- package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
- package/dist/runtime/public-assets-optional/svg2png/svg2png.wasm +0 -0
- package/dist/runtime/public-assets-optional/yoga/yoga.wasm +0 -0
- /package/dist/runtime/{nitro/providers → core/bindings}/css-inline/mock.d.ts +0 -0
- /package/dist/runtime/{nitro/providers → core/bindings}/css-inline/mock.mjs +0 -0
- /package/dist/runtime/{nitro/providers/css-inline/css-inline.d.ts → core/bindings/css-inline/node.d.ts} +0 -0
- /package/dist/runtime/{nitro/providers/css-inline/css-inline.mjs → core/bindings/css-inline/node.mjs} +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/emojis.d.ts +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/emojis.mjs +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/encoding.d.ts +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/flex.d.ts +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/flex.mjs +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/imageSrc.d.ts +0 -0
- /package/dist/runtime/{nitro → core}/renderers/satori/plugins/twClasses.d.ts +0 -0
- /package/dist/runtime/{nitro/routes/font.d.ts → nuxt/plugins/nuxt-content-canonical-urls.d.ts} +0 -0
- /package/dist/runtime/{nitro/middleware/og.png.d.ts → server/routes/__og-image__/font-[name]-[weight].[extension].d.ts} +0 -0
- /package/dist/runtime/{nitro/routes/svg.d.ts → server/routes/__og-image__/image-[path]-og.[extension].d.ts} +0 -0
package/dist/runtime/{nitro/providers/browser/universal.mjs → core/bindings/chromium/node.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import playwrightCore from "playwright-core";
|
|
2
|
-
export
|
|
3
|
-
if (
|
|
2
|
+
export async function createBrowser() {
|
|
3
|
+
if (import.meta.dev || import.meta.env.prerender) {
|
|
4
4
|
try {
|
|
5
5
|
const { Launcher } = await import(String("chrome-launcher"));
|
|
6
6
|
const chromePath = Launcher.getFirstInstallation();
|
|
@@ -23,7 +23,7 @@ export default async function createBrowser() {
|
|
|
23
23
|
headless: true
|
|
24
24
|
});
|
|
25
25
|
} catch (e) {
|
|
26
|
-
if (
|
|
26
|
+
if (import.meta.dev) {
|
|
27
27
|
console.warn("Failed to load chromium instance. Ensure you have chrome installed, otherwise add the dependency: `npm add -D playwright`.");
|
|
28
28
|
} else {
|
|
29
29
|
console.error("Failed to load browser instance. Please open an issue with the exception: https://github.com/harlan-zw/nuxt-og-image/issues.");
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
initWasmPromise: Promise<void>;
|
|
3
|
+
Resvg: new (svg: string | Uint8Array, options?: import("@resvg/resvg-wasm").ResvgRenderOptions | undefined) => {
|
|
4
|
+
free(): void;
|
|
5
|
+
render(): {
|
|
6
|
+
free(): void;
|
|
7
|
+
asPng(): Uint8Array;
|
|
8
|
+
readonly height: number;
|
|
9
|
+
readonly pixels: Uint8Array;
|
|
10
|
+
readonly width: number;
|
|
11
|
+
};
|
|
12
|
+
toString(): string;
|
|
13
|
+
innerBBox(): {
|
|
14
|
+
free(): void;
|
|
15
|
+
height: number;
|
|
16
|
+
width: number;
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
} | undefined;
|
|
20
|
+
getBBox(): {
|
|
21
|
+
free(): void;
|
|
22
|
+
height: number;
|
|
23
|
+
width: number;
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
} | undefined;
|
|
27
|
+
cropByBBox(bbox: {
|
|
28
|
+
free(): void;
|
|
29
|
+
height: number;
|
|
30
|
+
width: number;
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
}): void;
|
|
34
|
+
imagesToResolve(): any[];
|
|
35
|
+
resolveImage(href: string, buffer: Uint8Array): void;
|
|
36
|
+
readonly height: number;
|
|
37
|
+
readonly width: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export default _default;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Browser } from 'playwright-core';
|
|
2
|
+
import type { OgImageOptions } from '../../types';
|
|
3
|
+
export declare const prerenderCache: import("unstorage/dist/shared/unstorage.745f9650").a<OgImageOptions> | undefined;
|
|
4
|
+
export declare const prerenderChromiumContext: {
|
|
5
|
+
browser?: Browser;
|
|
6
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createStorage } from "unstorage";
|
|
2
|
+
import lruCacheDriver from "unstorage/drivers/lru-cache";
|
|
3
|
+
export const prerenderCache = import.meta.prerender ? createStorage({
|
|
4
|
+
driver: lruCacheDriver({ max: 1e3 })
|
|
5
|
+
}) : void 0;
|
|
6
|
+
export const prerenderChromiumContext = { browser: void 0 };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
export function base64ToArrayBuffer(base64) {
|
|
3
|
+
const buffer = Buffer.from(base64, "base64");
|
|
4
|
+
return new Uint8Array(buffer).buffer;
|
|
5
|
+
}
|
|
6
|
+
export function toBase64Image(fileName, data) {
|
|
7
|
+
const base64 = typeof data === "string" ? data : Buffer.from(data).toString("base64");
|
|
8
|
+
let type = "image/jpeg";
|
|
9
|
+
const ext = fileName.split(".").pop();
|
|
10
|
+
if (ext === "svg")
|
|
11
|
+
type = "image/svg+xml";
|
|
12
|
+
else if (ext === "png")
|
|
13
|
+
type = "image/png";
|
|
14
|
+
return `data:${type};base64,${base64}`;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const fontCache: Record<string, any>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const fontCache = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { base64ToArrayBuffer } from "../env/assets.mjs";
|
|
3
|
+
import { fontCache } from "./cache.mjs";
|
|
4
|
+
import { useNitroOrigin, useStorage } from "#imports";
|
|
5
|
+
export async function loadFont(e, font) {
|
|
6
|
+
const fontKey = `${font.name}:${font.weight}`;
|
|
7
|
+
const storageKey = `assets:nuxt-og-image:font:${fontKey}`;
|
|
8
|
+
if (fontCache[fontKey])
|
|
9
|
+
return fontCache[fontKey];
|
|
10
|
+
const [name, weight] = fontKey.split(":");
|
|
11
|
+
let data;
|
|
12
|
+
if (await useStorage().hasItem(storageKey))
|
|
13
|
+
data = base64ToArrayBuffer(await useStorage().getItem(storageKey));
|
|
14
|
+
if (!data) {
|
|
15
|
+
if (font.path) {
|
|
16
|
+
data = await e.$fetch(font.path, {
|
|
17
|
+
baseURL: useNitroOrigin(e),
|
|
18
|
+
responseType: "arrayBuffer"
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
data = await e.$fetch(`/__og-image__/font/${name}/${weight}.ttf`, {
|
|
22
|
+
responseType: "arrayBuffer"
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
fontCache[fontKey] = { name, weight: Number(weight), data, style: "normal" };
|
|
27
|
+
await useStorage().setItem(storageKey, Buffer.from(data).toString("base64"));
|
|
28
|
+
return fontCache[fontKey];
|
|
29
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createError } from "h3";
|
|
2
|
+
import { hash } from "ohash";
|
|
3
|
+
import { createHeadCore } from "@unhead/vue";
|
|
4
|
+
import twemoji from "twemoji";
|
|
5
|
+
import { renderSSRHead } from "@unhead/ssr";
|
|
6
|
+
import { useRuntimeConfig } from "#imports";
|
|
7
|
+
export async function fetchHTML(e, options) {
|
|
8
|
+
const { fonts, satoriOptions } = useRuntimeConfig()["nuxt-og-image"];
|
|
9
|
+
if (!options.component) {
|
|
10
|
+
throw createError({
|
|
11
|
+
statusCode: 500,
|
|
12
|
+
statusMessage: `Nuxt OG Image trying to render an invalid component. Received options ${JSON.stringify(options)}`
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const hashId = hash([options.component, options, Math.random() * 100]);
|
|
16
|
+
const island = await e.$fetch(`/__nuxt_island/${options.component}_${hashId}.json`, {
|
|
17
|
+
params: {
|
|
18
|
+
props: JSON.stringify(options)
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const head = createHeadCore();
|
|
22
|
+
head.push(island.head);
|
|
23
|
+
let defaultFontFamily = "sans-serif";
|
|
24
|
+
const firstFont = fonts[0];
|
|
25
|
+
if (firstFont)
|
|
26
|
+
defaultFontFamily = firstFont.name;
|
|
27
|
+
let html = island.html;
|
|
28
|
+
try {
|
|
29
|
+
html = twemoji.parse(html, {
|
|
30
|
+
folder: "svg",
|
|
31
|
+
ext: ".svg"
|
|
32
|
+
});
|
|
33
|
+
} catch (e2) {
|
|
34
|
+
}
|
|
35
|
+
const googleFonts = {};
|
|
36
|
+
fonts.filter((font) => !font.path).forEach((font) => {
|
|
37
|
+
if (!googleFonts[font.name])
|
|
38
|
+
googleFonts[font.name] = [];
|
|
39
|
+
googleFonts[font.name].push(font);
|
|
40
|
+
});
|
|
41
|
+
head.push({
|
|
42
|
+
style: [
|
|
43
|
+
{
|
|
44
|
+
// default font is the first font family
|
|
45
|
+
innerHTML: `body { font-family: '${defaultFontFamily.replace("+", " ")}', sans-serif; }`
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
innerHTML: `body {
|
|
49
|
+
transform: scale(${options.scale || 1});
|
|
50
|
+
transform-origin: top left;
|
|
51
|
+
max-height: 100vh;
|
|
52
|
+
position: relative;
|
|
53
|
+
width: ${options.width}px;
|
|
54
|
+
height: ${options.height}px;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
background-color: ${options.mode === "dark" ? "#1b1b1b" : "#fff"};
|
|
57
|
+
}
|
|
58
|
+
img.emoji {
|
|
59
|
+
height: 1em;
|
|
60
|
+
width: 1em;
|
|
61
|
+
margin: 0 .05em 0 .1em;
|
|
62
|
+
vertical-align: -0.1em;
|
|
63
|
+
}`
|
|
64
|
+
},
|
|
65
|
+
...fonts.filter((font) => font.path).map((font) => {
|
|
66
|
+
return `
|
|
67
|
+
@font-face {
|
|
68
|
+
font-family: '${font.name}';
|
|
69
|
+
font-style: normal;
|
|
70
|
+
font-weight: ${font.weight};
|
|
71
|
+
src: url('${font.path}') format('truetype');
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
})
|
|
75
|
+
],
|
|
76
|
+
meta: [
|
|
77
|
+
{
|
|
78
|
+
charset: "utf-8"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
script: [
|
|
82
|
+
{
|
|
83
|
+
src: "https://cdn.tailwindcss.com"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
innerHTML: `tailwind.config = {
|
|
87
|
+
corePlugins: {
|
|
88
|
+
preflight: false,
|
|
89
|
+
},
|
|
90
|
+
theme: ${JSON.stringify(satoriOptions?.tailwindConfig?.theme || {})}
|
|
91
|
+
}`
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
link: [
|
|
95
|
+
{
|
|
96
|
+
// reset css to match svg output
|
|
97
|
+
href: "https://cdn.jsdelivr.net/npm/gardevoir",
|
|
98
|
+
rel: "stylesheet"
|
|
99
|
+
},
|
|
100
|
+
// have to add each weight as their own stylesheet
|
|
101
|
+
...Object.entries(googleFonts).map(([name, fonts2]) => {
|
|
102
|
+
return {
|
|
103
|
+
href: `https://fonts.googleapis.com/css2?family=${name}:wght@${fonts2.map((f) => f.weight).join(";")}&display=swap`,
|
|
104
|
+
rel: "stylesheet"
|
|
105
|
+
};
|
|
106
|
+
})
|
|
107
|
+
]
|
|
108
|
+
});
|
|
109
|
+
html = html.replaceAll(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
|
|
110
|
+
const headChunk = await renderSSRHead(head);
|
|
111
|
+
const htmlTemplate = `<!DOCTYPE html>
|
|
112
|
+
<html ${headChunk.htmlAttrs}>
|
|
113
|
+
<head>${headChunk.headTags}</head>
|
|
114
|
+
<body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div data-v-inspector-ignore="true" style="position: relative; display: flex; margin: 0 auto; width: ${options.width}px; height: ${options.height}px; overflow: hidden;">${html}</div>${headChunk.bodyTags}</body>
|
|
115
|
+
</html>`;
|
|
116
|
+
return htmlTemplate;
|
|
117
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { defu } from "defu";
|
|
2
1
|
export function decodeHtml(html) {
|
|
3
2
|
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) => {
|
|
4
3
|
return String.fromCharCode(Number.parseInt(int));
|
|
@@ -11,37 +10,40 @@ function decodeObjectHtmlEntities(obj) {
|
|
|
11
10
|
});
|
|
12
11
|
return obj;
|
|
13
12
|
}
|
|
14
|
-
export function extractAndNormaliseOgImageOptions(
|
|
13
|
+
export function extractAndNormaliseOgImageOptions(html) {
|
|
15
14
|
const htmlPayload = html.match(/<script.+id="nuxt-og-image-options"[^>]*>(.+?)<\/script>/)?.[1];
|
|
16
15
|
if (!htmlPayload)
|
|
17
16
|
return false;
|
|
18
17
|
let options;
|
|
19
18
|
try {
|
|
20
|
-
const
|
|
21
|
-
Object.entries(
|
|
19
|
+
const payload2 = JSON.parse(htmlPayload);
|
|
20
|
+
Object.entries(payload2).forEach(([key, value]) => {
|
|
22
21
|
if (!value)
|
|
23
|
-
delete
|
|
22
|
+
delete payload2[key];
|
|
24
23
|
});
|
|
25
|
-
options =
|
|
24
|
+
options = payload2;
|
|
26
25
|
} catch (e) {
|
|
27
|
-
|
|
28
|
-
if (process.dev)
|
|
26
|
+
if (import.meta.dev)
|
|
29
27
|
console.warn("Failed to parse #nuxt-og-image-options", e, options);
|
|
30
28
|
}
|
|
31
29
|
if (!options)
|
|
32
30
|
return false;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
const payload = decodeObjectHtmlEntities(options);
|
|
32
|
+
if (import.meta.dev) {
|
|
33
|
+
const socialPreview = {};
|
|
34
|
+
const socialMetaTags = html.match(/<meta[^>]+(property|name)="(twitter|og):([^"]+)"[^>]*>/g);
|
|
35
|
+
if (socialMetaTags) {
|
|
36
|
+
socialMetaTags.forEach((tag) => {
|
|
37
|
+
const [, , type, key] = tag.match(/(property|name)="(twitter|og):([^"]+)"/);
|
|
38
|
+
const value = tag.match(/content="([^"]+)"/)?.[1];
|
|
39
|
+
if (!value)
|
|
40
|
+
return;
|
|
41
|
+
if (!socialPreview[type])
|
|
42
|
+
socialPreview[type] = {};
|
|
43
|
+
socialPreview[type][key] = value;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
payload.socialPreview = socialPreview;
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
return defu(
|
|
42
|
-
decoded,
|
|
43
|
-
// runtime options
|
|
44
|
-
{ path },
|
|
45
|
-
defaults
|
|
46
|
-
);
|
|
48
|
+
return payload;
|
|
47
49
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createError } from "h3";
|
|
2
|
+
import { extractAndNormaliseOgImageOptions } from "./extract.mjs";
|
|
3
|
+
export async function fetchPathHtmlAndExtractOptions(e, path) {
|
|
4
|
+
let html;
|
|
5
|
+
try {
|
|
6
|
+
html = await e.$fetch(path);
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return createError({
|
|
9
|
+
statusCode: 500,
|
|
10
|
+
statusMessage: `Failed to read the path ${path} for og-image extraction. ${err.message}.`
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
const payload = extractAndNormaliseOgImageOptions(html);
|
|
14
|
+
if (!payload) {
|
|
15
|
+
return createError({
|
|
16
|
+
statusCode: 400,
|
|
17
|
+
statusMessage: `The path is missing the Nuxt OG Image payload. Did you forget to use defineOgImage()?`
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return payload;
|
|
21
|
+
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { unref, useRuntimeConfig } from "#imports";
|
|
2
2
|
import { componentNames } from "#build/og-image-component-names.mjs";
|
|
3
|
-
export function
|
|
3
|
+
export function normaliseOptions(_options) {
|
|
4
|
+
const { runtimeSatori } = useRuntimeConfig()["nuxt-og-image"];
|
|
4
5
|
const options = { ...unref(_options) };
|
|
5
6
|
if (options.static)
|
|
6
7
|
options.cache = options.cache || options.static;
|
|
7
|
-
if (
|
|
8
|
-
options.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
if (options.provider === "satori")
|
|
9
|
+
options.renderer = options.renderer || "satori";
|
|
10
|
+
else if (options.provider === "browser")
|
|
11
|
+
options.renderer = options.renderer || "chromium";
|
|
12
|
+
options.renderer = options.renderer || "satori";
|
|
13
|
+
if (options.renderer === "satori" && !runtimeSatori)
|
|
14
|
+
options.renderer = "chromium";
|
|
12
15
|
if (options.component && componentNames) {
|
|
13
16
|
const originalName = options.component;
|
|
14
17
|
for (const component of componentNames) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getOgImagePath } from "../../../utilts.mjs";
|
|
2
|
+
import { prerenderChromiumContext } from "../../cache/prerender.mjs";
|
|
3
|
+
import { createScreenshot } from "./screenshot.mjs";
|
|
4
|
+
import { createBrowser } from "#nuxt-og-image/bindings/chromium";
|
|
5
|
+
const ChromiumRenderer = {
|
|
6
|
+
name: "chromium",
|
|
7
|
+
supportedFormats: ["png", "jpeg"],
|
|
8
|
+
async createImage(e, options) {
|
|
9
|
+
const browser = (import.meta.prerender ? prerenderChromiumContext.browser : null) || await createBrowser();
|
|
10
|
+
if (!browser) {
|
|
11
|
+
return createError({
|
|
12
|
+
statusCode: 400,
|
|
13
|
+
statusMessage: "Failed to create Local Chromium Browser."
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (import.meta.prerender)
|
|
17
|
+
prerenderChromiumContext.browser = browser;
|
|
18
|
+
return createScreenshot(e, browser, {
|
|
19
|
+
...options,
|
|
20
|
+
path: options.component === "PageScreenshot" ? options.path : getOgImagePath(options.path, "html")
|
|
21
|
+
}).finally(async () => {
|
|
22
|
+
await browser.close();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export default ChromiumRenderer;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { Buffer } from 'node:buffer';
|
|
3
|
+
import type { Browser } from 'playwright-core';
|
|
4
|
+
import type { H3Event } from 'h3';
|
|
5
|
+
import type { RendererOptions } from '../../../types';
|
|
6
|
+
export declare function createScreenshot(e: H3Event, browser: Browser, options: RendererOptions): Promise<Buffer>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { withQuery } from "ufo";
|
|
2
|
+
export async function createScreenshot(e, browser, options) {
|
|
3
|
+
const page = await browser.newPage({
|
|
4
|
+
colorScheme: options.colorScheme,
|
|
5
|
+
baseURL: useNitroOrigin(e)
|
|
6
|
+
});
|
|
7
|
+
if (import.meta.prerender && !options.html) {
|
|
8
|
+
options.html = await e.$fetch(options.path);
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
await page.setViewportSize({
|
|
12
|
+
width: options.width || 1200,
|
|
13
|
+
height: options.height || 630
|
|
14
|
+
});
|
|
15
|
+
const isHtml = options.html || options.path?.startsWith("html:");
|
|
16
|
+
if (isHtml) {
|
|
17
|
+
const html = options.html || options.path?.substring(5);
|
|
18
|
+
await page.evaluate((html2) => {
|
|
19
|
+
document.open("text/html");
|
|
20
|
+
document.write(html2);
|
|
21
|
+
document.close();
|
|
22
|
+
}, html);
|
|
23
|
+
await page.waitForLoadState("networkidle");
|
|
24
|
+
} else {
|
|
25
|
+
await page.goto(withQuery(options.path, options), {
|
|
26
|
+
timeout: 1e4,
|
|
27
|
+
waitUntil: "networkidle"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const screenshotOptions = {
|
|
31
|
+
timeout: 1e4,
|
|
32
|
+
animations: "disabled",
|
|
33
|
+
type: options.extension
|
|
34
|
+
};
|
|
35
|
+
if (options.mask) {
|
|
36
|
+
await page.evaluate((mask) => {
|
|
37
|
+
for (const el of document.querySelectorAll(mask))
|
|
38
|
+
el.style.display = "none";
|
|
39
|
+
}, options.mask);
|
|
40
|
+
}
|
|
41
|
+
if (options.selector)
|
|
42
|
+
return await page.locator(options.selector).screenshot(screenshotOptions);
|
|
43
|
+
return await page.screenshot(screenshotOptions);
|
|
44
|
+
} finally {
|
|
45
|
+
await page.close();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { loadFont } from "../../font/fetch.mjs";
|
|
2
|
+
export const satoriFonts = [];
|
|
3
|
+
let fontLoadPromise = null;
|
|
4
|
+
export function loadFonts(baseURL, fonts) {
|
|
5
|
+
if (fontLoadPromise)
|
|
6
|
+
return fontLoadPromise;
|
|
7
|
+
return fontLoadPromise = Promise.all(fonts.map((font) => loadFont(baseURL, font)));
|
|
8
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { createVNodes } from "./vnodes.mjs";
|
|
3
|
+
import { loadFonts, satoriFonts } from "./fonts.mjs";
|
|
4
|
+
import { useResvg, useSatori, useSharp } from "./instances.mjs";
|
|
5
|
+
import { useRuntimeConfig } from "#imports";
|
|
6
|
+
export async function createSvg(e, options) {
|
|
7
|
+
const { fonts, satoriOptions } = useRuntimeConfig()["nuxt-og-image"];
|
|
8
|
+
const vnodes = await createVNodes(e, options);
|
|
9
|
+
if (!satoriFonts.length)
|
|
10
|
+
satoriFonts.push(...await loadFonts(e, fonts));
|
|
11
|
+
const satori = await useSatori();
|
|
12
|
+
return satori(vnodes, defu(options.satori, satoriOptions, {
|
|
13
|
+
fonts: satoriFonts,
|
|
14
|
+
embedFont: true,
|
|
15
|
+
width: options.width,
|
|
16
|
+
height: options.height
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
async function createPng(e, options) {
|
|
20
|
+
const { resvgOptions } = useRuntimeConfig()["nuxt-og-image"];
|
|
21
|
+
const svg = await createSvg(e, options);
|
|
22
|
+
const Resvg = await useResvg();
|
|
23
|
+
const resvg = new Resvg(svg, defu(
|
|
24
|
+
options.resvg,
|
|
25
|
+
resvgOptions
|
|
26
|
+
));
|
|
27
|
+
const pngData = resvg.render();
|
|
28
|
+
return pngData.asPng();
|
|
29
|
+
}
|
|
30
|
+
async function createJpeg(e, options) {
|
|
31
|
+
const { sharpOptions } = useRuntimeConfig()["nuxt-og-image"];
|
|
32
|
+
const png = await createPng(e, options);
|
|
33
|
+
const sharp = await useSharp();
|
|
34
|
+
return sharp(png, defu(options.sharp, sharpOptions)).jpeg(defu(options.sharp, sharpOptions)).toBuffer();
|
|
35
|
+
}
|
|
36
|
+
const SatoriRenderer = {
|
|
37
|
+
name: "satori",
|
|
38
|
+
supportedFormats: ["svg", "png", "jpeg", "jpg", "json"],
|
|
39
|
+
async createImage(e, options) {
|
|
40
|
+
switch (options.extension) {
|
|
41
|
+
case "json":
|
|
42
|
+
return createVNodes(e, options);
|
|
43
|
+
case "svg":
|
|
44
|
+
return createSvg(e, options);
|
|
45
|
+
case "png":
|
|
46
|
+
return createPng(e, options);
|
|
47
|
+
case "jpeg":
|
|
48
|
+
case "jpg":
|
|
49
|
+
return createJpeg(e, options);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
export default SatoriRenderer;
|