nuxt-og-image 0.6.0 → 1.0.0-beta.0
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 +33 -26
- package/dist/client/200.html +7 -0
- package/dist/client/404.html +7 -0
- package/dist/client/_nuxt/Icon.4a9650c6.js +1 -0
- package/dist/client/_nuxt/Icon.e17ad835.css +1 -0
- package/dist/client/_nuxt/entry.0827acf4.css +1 -0
- package/dist/client/_nuxt/entry.ce848650.js +4 -0
- package/dist/client/_nuxt/error-404.556d8899.js +1 -0
- package/dist/client/_nuxt/error-404.68aa58b4.css +1 -0
- package/dist/client/_nuxt/error-500.70731718.js +1 -0
- package/dist/client/_nuxt/error-500.dc5710d1.css +1 -0
- package/dist/client/_nuxt/error-component.418ebd67.js +3 -0
- package/dist/client/index.html +7 -0
- package/dist/client/nuxt.svg +3 -0
- package/dist/module.d.ts +13 -12
- package/dist/module.json +1 -1
- package/dist/module.mjs +199 -102
- package/dist/runtime/components/{OgImage.d.ts → OgImageDynamic.d.ts} +0 -0
- package/dist/runtime/components/OgImageDynamic.mjs +9 -0
- package/dist/runtime/components/OgImageStatic.d.ts +5 -0
- package/dist/runtime/components/{OgImage.mjs → OgImageStatic.mjs} +3 -3
- package/dist/runtime/components/OgImageTemplate.island.vue +8 -83
- package/dist/runtime/composables/defineOgImage.d.ts +2 -0
- package/dist/runtime/composables/defineOgImage.mjs +33 -10
- package/dist/runtime/{browsers → nitro/browsers}/default.d.ts +0 -0
- package/dist/runtime/{browsers → nitro/browsers}/default.mjs +0 -0
- package/dist/runtime/{browsers → nitro/browsers}/lambda.d.ts +0 -0
- package/dist/runtime/{browsers → nitro/browsers}/lambda.mjs +0 -0
- package/dist/runtime/nitro/providers/browser.d.ts +3 -0
- package/dist/runtime/nitro/providers/browser.mjs +16 -0
- package/dist/runtime/nitro/providers/satori.d.ts +3 -0
- package/dist/runtime/nitro/providers/satori.mjs +48 -0
- package/dist/runtime/nitro/routes/__og_image__/html.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/html.mjs +43 -0
- package/dist/runtime/nitro/routes/__og_image__/index.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/index.mjs +26 -0
- package/dist/runtime/nitro/routes/__og_image__/og.png.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/og.png.mjs +23 -0
- package/dist/runtime/nitro/{html.d.ts → routes/__og_image__/payload.d.ts} +2 -1
- package/dist/runtime/nitro/routes/__og_image__/payload.mjs +53 -0
- package/dist/runtime/nitro/routes/__og_image__/svg.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/svg.mjs +16 -0
- package/dist/runtime/nitro/utils.d.ts +8 -0
- package/dist/runtime/nitro/utils.mjs +18 -0
- package/dist/runtime/public/inter-latin-ext-400-normal.woff +0 -0
- package/dist/runtime/public/inter-latin-ext-700-normal.woff +0 -0
- package/package.json +18 -7
- package/dist/runtime/nitro/html.mjs +0 -55
- package/dist/runtime/nitro/image.d.ts +0 -3
- package/dist/runtime/nitro/image.mjs +0 -18
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { OgImagePayload, OgImageScreenshotPayload } from '../../types';
|
|
2
2
|
export declare function defineOgImageScreenshot(options?: OgImageScreenshotPayload): void;
|
|
3
|
+
export declare function defineOgImageDynamic(options?: OgImageScreenshotPayload): void;
|
|
4
|
+
export declare function defineOgImageStatic(options?: OgImageScreenshotPayload): void;
|
|
3
5
|
export declare function defineOgImage(options?: OgImagePayload): void;
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
import { useServerHead } from "@vueuse/head";
|
|
2
|
+
import { withBase } from "ufo";
|
|
2
3
|
import { useRouter } from "#imports";
|
|
3
|
-
import {
|
|
4
|
+
import { PayloadScriptId } from "#nuxt-og-image/constants";
|
|
5
|
+
import { forcePrerender, height, host, width } from "#nuxt-og-image/config";
|
|
4
6
|
export function defineOgImageScreenshot(options = {}) {
|
|
7
|
+
const router = useRouter();
|
|
8
|
+
const route = router?.currentRoute?.value?.path || "";
|
|
5
9
|
defineOgImage({
|
|
6
|
-
alt: "
|
|
10
|
+
alt: `Web page screenshot${route ? ` of ${route}` : ""}.`,
|
|
11
|
+
provider: "browser",
|
|
12
|
+
prerender: true,
|
|
13
|
+
...options
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function defineOgImageDynamic(options = {}) {
|
|
17
|
+
defineOgImage({
|
|
18
|
+
provider: "satori",
|
|
19
|
+
...options
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function defineOgImageStatic(options = {}) {
|
|
23
|
+
defineOgImage({
|
|
24
|
+
provider: "satori",
|
|
25
|
+
prerender: true,
|
|
7
26
|
...options
|
|
8
27
|
});
|
|
9
28
|
}
|
|
@@ -11,6 +30,9 @@ export function defineOgImage(options = {}) {
|
|
|
11
30
|
if (process.server) {
|
|
12
31
|
const router = useRouter();
|
|
13
32
|
const route = router?.currentRoute?.value?.path || "";
|
|
33
|
+
const e = useRequestEvent();
|
|
34
|
+
if ((forcePrerender || options.prerender) && options.provider === "satori")
|
|
35
|
+
e.res.setHeader("x-nitro-prerender", `${route === "/" ? "" : route}/__og_image__/og.png`);
|
|
14
36
|
const meta = [
|
|
15
37
|
{
|
|
16
38
|
property: "twitter:card",
|
|
@@ -18,7 +40,15 @@ export function defineOgImage(options = {}) {
|
|
|
18
40
|
},
|
|
19
41
|
{
|
|
20
42
|
property: "og:image",
|
|
21
|
-
content: () =>
|
|
43
|
+
content: () => withBase(`${route}/__og_image__/og.png`, host)
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
property: "og:image:width",
|
|
47
|
+
content: width
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
property: "og:image:height",
|
|
51
|
+
content: height
|
|
22
52
|
}
|
|
23
53
|
];
|
|
24
54
|
if (options.alt) {
|
|
@@ -29,13 +59,6 @@ export function defineOgImage(options = {}) {
|
|
|
29
59
|
}
|
|
30
60
|
useServerHead({
|
|
31
61
|
meta,
|
|
32
|
-
link: !options.runtime && options.component ? [
|
|
33
|
-
{
|
|
34
|
-
id: LinkPrerenderId,
|
|
35
|
-
rel: "prerender",
|
|
36
|
-
href: `${route}/${HtmlRendererRoute}`
|
|
37
|
-
}
|
|
38
|
-
] : [],
|
|
39
62
|
script: [
|
|
40
63
|
{
|
|
41
64
|
id: PayloadScriptId,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { screenshot } from "../../browserUtil.mjs";
|
|
2
|
+
import { height, width } from "#nuxt-og-image/config";
|
|
3
|
+
import { createBrowser } from "#nuxt-og-image/browser";
|
|
4
|
+
export default {
|
|
5
|
+
name: "browser",
|
|
6
|
+
createSvg: function createSvg() {
|
|
7
|
+
throw new Error("Browser provider isn't able to create SVGs.");
|
|
8
|
+
},
|
|
9
|
+
createPng: async function createPng(basePath) {
|
|
10
|
+
const browser = await createBrowser();
|
|
11
|
+
return screenshot(browser, basePath, {
|
|
12
|
+
width: Number(width),
|
|
13
|
+
height: Number(height)
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { promises as fsp } from "node:fs";
|
|
3
|
+
import { html as convertHtmlToSatori } from "satori-html";
|
|
4
|
+
import satori from "satori";
|
|
5
|
+
import { parseURL } from "ufo";
|
|
6
|
+
import { Resvg } from "@resvg/resvg-js";
|
|
7
|
+
import { dirname, resolve } from "pathe";
|
|
8
|
+
import { height, width } from "#nuxt-og-image/config";
|
|
9
|
+
import { getAsset } from "#internal/nitro/virtual/public-assets";
|
|
10
|
+
export default {
|
|
11
|
+
name: "satori",
|
|
12
|
+
createPng: async function createPng(baseUrl) {
|
|
13
|
+
const svg = await this.createSvg(baseUrl);
|
|
14
|
+
const resvg = new Resvg(svg, {});
|
|
15
|
+
const pngData = resvg.render();
|
|
16
|
+
return pngData.asPng();
|
|
17
|
+
},
|
|
18
|
+
createSvg: async function createSvg(baseUrl) {
|
|
19
|
+
const url = parseURL(baseUrl);
|
|
20
|
+
const html = await $fetch(url.pathname);
|
|
21
|
+
const body = html.match(/<body[^>]*>([\s\S]*)<\/body>/)?.[1];
|
|
22
|
+
const font = async (weight) => {
|
|
23
|
+
let fontData;
|
|
24
|
+
const file = getAsset(`/inter-latin-ext-${weight}-normal.woff`);
|
|
25
|
+
if (file) {
|
|
26
|
+
const serverDir = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
fontData = await fsp.readFile(resolve(serverDir, file.path));
|
|
28
|
+
}
|
|
29
|
+
if (!fontData)
|
|
30
|
+
fontData = await (await $fetch(`${url.protocol}//${url.host}/inter-latin-ext-${weight}-normal.woff`)).arrayBuffer();
|
|
31
|
+
return {
|
|
32
|
+
name: "Inter",
|
|
33
|
+
weight,
|
|
34
|
+
style: "normal",
|
|
35
|
+
data: fontData
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const satoriTree = convertHtmlToSatori(body);
|
|
39
|
+
return await satori(satoriTree, {
|
|
40
|
+
width,
|
|
41
|
+
height,
|
|
42
|
+
fonts: [
|
|
43
|
+
await font(400),
|
|
44
|
+
await font(700)
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseURL, withoutTrailingSlash } from "ufo";
|
|
2
|
+
import { renderSSRHead } from "@unhead/ssr";
|
|
3
|
+
import { createHeadCore } from "@unhead/vue";
|
|
4
|
+
import { defineEventHandler, sendRedirect } from "h3";
|
|
5
|
+
import { fetchPayload, renderIsland, useHostname } from "../../utils.mjs";
|
|
6
|
+
import { height, width } from "#nuxt-og-image/config";
|
|
7
|
+
export default defineEventHandler(async (e) => {
|
|
8
|
+
const path = parseURL(e.path).pathname;
|
|
9
|
+
if (!path.endsWith("__og_image__/html"))
|
|
10
|
+
return;
|
|
11
|
+
const basePath = withoutTrailingSlash(path.replace("__og_image__/html", ""));
|
|
12
|
+
const payload = await fetchPayload(basePath);
|
|
13
|
+
if (payload.provider === "browser")
|
|
14
|
+
return sendRedirect(e, useHostname(e) + basePath);
|
|
15
|
+
const component = payload.component || "OgImageTemplate";
|
|
16
|
+
delete payload.component;
|
|
17
|
+
const island = await renderIsland(component, payload);
|
|
18
|
+
const head = createHeadCore();
|
|
19
|
+
head.push(island.head);
|
|
20
|
+
head.push({
|
|
21
|
+
style: [
|
|
22
|
+
{
|
|
23
|
+
innerHTML: "body { font-family: 'Inter', sans-serif; }"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
link: [
|
|
27
|
+
{
|
|
28
|
+
href: "https://cdn.jsdelivr.net/npm/gardevoir",
|
|
29
|
+
rel: "stylesheet"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap",
|
|
33
|
+
rel: "stylesheet"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
const headChunk = await renderSSRHead(head);
|
|
38
|
+
return `<!DOCTYPE html>
|
|
39
|
+
<html ${headChunk.htmlAttrs}>
|
|
40
|
+
<head>${headChunk.headTags}</head>
|
|
41
|
+
<body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div style="width: ${width}px; height: ${height}px; display: flex; margin: 0 auto;">${island.html}</div>${headChunk.bodyTags}</body>
|
|
42
|
+
</html>`;
|
|
43
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineEventHandler } from "h3";
|
|
2
|
+
import { parseURL, withoutTrailingSlash } from "ufo";
|
|
3
|
+
import { fetchPayload, useHostname } from "../../utils.mjs";
|
|
4
|
+
export default defineEventHandler(async (e) => {
|
|
5
|
+
const path = parseURL(e.path).pathname;
|
|
6
|
+
if (!path.endsWith("/__og_image__"))
|
|
7
|
+
return;
|
|
8
|
+
const basePath = withoutTrailingSlash(path.replace("__og_image__", ""));
|
|
9
|
+
const payload = await fetchPayload(basePath);
|
|
10
|
+
if (!payload)
|
|
11
|
+
return `The route ${basePath} has not been set up for og:image generation.`;
|
|
12
|
+
return `
|
|
13
|
+
<style>
|
|
14
|
+
body {
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
}
|
|
18
|
+
iframe {
|
|
19
|
+
border: none;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
24
|
+
<title>Og Image Playground</title>
|
|
25
|
+
<iframe src="${useHostname(e)}/__nuxt_og_image__/client/?&path=${withoutTrailingSlash(path.replace("__og_image__", ""))}"></iframe>`;
|
|
26
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineEventHandler, setHeader } from "h3";
|
|
2
|
+
import { parseURL, withBase, withoutTrailingSlash } from "ufo";
|
|
3
|
+
import { fetchPayload, useHostname } from "../../utils.mjs";
|
|
4
|
+
import { useProvider } from "#nuxt-og-image/provider";
|
|
5
|
+
export default defineEventHandler(async (e) => {
|
|
6
|
+
const path = parseURL(e.path).pathname;
|
|
7
|
+
if (!path.endsWith("__og_image__/og.png"))
|
|
8
|
+
return;
|
|
9
|
+
const basePath = withoutTrailingSlash(
|
|
10
|
+
path.replace("__og_image__/og.png", "")
|
|
11
|
+
);
|
|
12
|
+
const { provider: providerName, prerender } = await fetchPayload(basePath);
|
|
13
|
+
setHeader(e, "Content-Type", "image/png");
|
|
14
|
+
if (prerender && !process.dev) {
|
|
15
|
+
setHeader(e, "Cache-Control", "public, max-age=86400");
|
|
16
|
+
} else {
|
|
17
|
+
setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
|
|
18
|
+
setHeader(e, "Pragma", "no-cache");
|
|
19
|
+
setHeader(e, "Expires", "0");
|
|
20
|
+
}
|
|
21
|
+
const provider = await useProvider(providerName);
|
|
22
|
+
return provider.createPng(withBase(`${basePath}/__og_image__/html`, useHostname(e)));
|
|
23
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import type { OgImagePayload } from '../../../../types';
|
|
1
2
|
export declare const extractOgPayload: (html: string) => any;
|
|
2
3
|
export declare const inferOgPayload: (html: string) => Record<string, any>;
|
|
3
|
-
declare const _default: import("h3").EventHandler<
|
|
4
|
+
declare const _default: import("h3").EventHandler<false | OgImagePayload | undefined>;
|
|
4
5
|
export default _default;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { parseURL, withoutTrailingSlash } from "ufo";
|
|
2
|
+
import { defineEventHandler, getQuery } from "h3";
|
|
3
|
+
import { PayloadScriptId } from "#nuxt-og-image/constants";
|
|
4
|
+
import { getRouteRules } from "#internal/nitro";
|
|
5
|
+
export const extractOgPayload = (html) => {
|
|
6
|
+
const payload = html.match(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.+?)<\/script>`))?.[1];
|
|
7
|
+
if (payload) {
|
|
8
|
+
return JSON.parse(payload);
|
|
9
|
+
}
|
|
10
|
+
return false;
|
|
11
|
+
};
|
|
12
|
+
export const inferOgPayload = (html) => {
|
|
13
|
+
const payload = {};
|
|
14
|
+
const title = html.match(/<meta property="og:title" content="(.*?)">/)?.[1];
|
|
15
|
+
if (title)
|
|
16
|
+
payload.title = title;
|
|
17
|
+
const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
|
|
18
|
+
if (description)
|
|
19
|
+
payload.description = description;
|
|
20
|
+
return payload;
|
|
21
|
+
};
|
|
22
|
+
export default defineEventHandler(async (e) => {
|
|
23
|
+
const path = parseURL(e.path).pathname;
|
|
24
|
+
if (!path.endsWith("__og_image__/payload"))
|
|
25
|
+
return;
|
|
26
|
+
const basePath = withoutTrailingSlash(path.replace("__og_image__/payload", ""));
|
|
27
|
+
const html = await $fetch(basePath);
|
|
28
|
+
const extractedPayload = extractOgPayload(html);
|
|
29
|
+
if (!extractedPayload)
|
|
30
|
+
return false;
|
|
31
|
+
e.node.req.url = basePath;
|
|
32
|
+
e.context._nitro.routeRules = void 0;
|
|
33
|
+
const routeRules = getRouteRules(e)?.ogImage;
|
|
34
|
+
e.node.req.url = e.path;
|
|
35
|
+
if (routeRules === false)
|
|
36
|
+
return false;
|
|
37
|
+
let payload = {
|
|
38
|
+
path: basePath,
|
|
39
|
+
...extractOgPayload(html),
|
|
40
|
+
...inferOgPayload(html),
|
|
41
|
+
...routeRules || {},
|
|
42
|
+
...getQuery(e)
|
|
43
|
+
};
|
|
44
|
+
if (payload.provider === "satori") {
|
|
45
|
+
payload = {
|
|
46
|
+
title: "Hello World",
|
|
47
|
+
description: "Example description",
|
|
48
|
+
image: "https://example.com/image.png",
|
|
49
|
+
...payload
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return payload;
|
|
53
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineEventHandler, setHeader } from "h3";
|
|
2
|
+
import { parseURL, withBase, withoutTrailingSlash } from "ufo";
|
|
3
|
+
import { fetchPayload, useHostname } from "../../utils.mjs";
|
|
4
|
+
import { useProvider } from "#nuxt-og-image/provider";
|
|
5
|
+
export default defineEventHandler(async (e) => {
|
|
6
|
+
const path = parseURL(e.path).pathname;
|
|
7
|
+
if (!path.endsWith("__og_image__/svg"))
|
|
8
|
+
return;
|
|
9
|
+
const basePath = withoutTrailingSlash(
|
|
10
|
+
path.replace("__og_image__/svg", "")
|
|
11
|
+
);
|
|
12
|
+
const { provider: providerName } = await fetchPayload(basePath);
|
|
13
|
+
setHeader(e, "Content-Type", "image/svg+xml");
|
|
14
|
+
const provider = await useProvider(providerName);
|
|
15
|
+
return provider.createSvg(withBase(`${basePath}/__og_image__/html`, useHostname(e)));
|
|
16
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { OgImagePayload } from '../../types';
|
|
3
|
+
export declare function fetchPayload(path: string): Promise<OgImagePayload>;
|
|
4
|
+
export declare function renderIsland(island: string, payload: Record<string, any>): Promise<{
|
|
5
|
+
html: string;
|
|
6
|
+
head: any;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function useHostname(e: H3Event): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { joinURL, withQuery } from "ufo";
|
|
2
|
+
import { getRequestHeader, getRequestHeaders } from "h3";
|
|
3
|
+
export function fetchPayload(path) {
|
|
4
|
+
return $fetch(joinURL(path, "__og_image__/payload"));
|
|
5
|
+
}
|
|
6
|
+
export function renderIsland(island, payload) {
|
|
7
|
+
return $fetch(withQuery(`/__nuxt_island/${island}`, {
|
|
8
|
+
props: JSON.stringify(payload)
|
|
9
|
+
}));
|
|
10
|
+
}
|
|
11
|
+
export function useHostname(e) {
|
|
12
|
+
console.log(getRequestHeader(e, "host"), getRequestHeaders(e));
|
|
13
|
+
const host = getRequestHeader(e, "host") || "localhost:3000";
|
|
14
|
+
const protocol = getRequestHeader(e, "x-forwarded-proto") || "http";
|
|
15
|
+
if (protocol.startsWith("http"))
|
|
16
|
+
return `${protocol}://${host}`;
|
|
17
|
+
return `http${process.env.NODE_ENV === "development" ? "" : "s"}://${host}`;
|
|
18
|
+
}
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-og-image",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0-beta.0",
|
|
5
5
|
"packageManager": "pnpm@7.8.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/harlan-zw",
|
|
@@ -27,32 +27,43 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@nuxt/kit": "3.0.0",
|
|
30
|
+
"@resvg/resvg-js": "^2.2.0",
|
|
31
|
+
"birpc": "^0.2.3",
|
|
30
32
|
"chalk": "^5.2.0",
|
|
31
33
|
"chrome-launcher": "^0.15.1",
|
|
32
34
|
"defu": "^6.1.1",
|
|
33
35
|
"execa": "^6.1.0",
|
|
34
36
|
"fast-glob": "^3.2.12",
|
|
37
|
+
"flatted": "^3.2.7",
|
|
38
|
+
"launch-editor": "^2.6.0",
|
|
35
39
|
"ohash": "^1.0.0",
|
|
36
40
|
"pathe": "^1.0.0",
|
|
37
|
-
"playwright-core": "^1.29.
|
|
41
|
+
"playwright-core": "^1.29.2",
|
|
38
42
|
"radix3": "^1.0.0",
|
|
43
|
+
"satori": "^0.1.1",
|
|
44
|
+
"satori-html": "^0.3.2",
|
|
45
|
+
"sirv": "^2.0.2",
|
|
46
|
+
"tinyws": "^0.1.0",
|
|
39
47
|
"ufo": "^1.0.1"
|
|
40
48
|
},
|
|
41
49
|
"devDependencies": {
|
|
42
|
-
"@antfu/eslint-config": "^0.34.
|
|
50
|
+
"@antfu/eslint-config": "^0.34.1",
|
|
43
51
|
"@nuxt/kit": "3.0.0",
|
|
44
52
|
"@nuxt/module-builder": "^0.2.1",
|
|
45
53
|
"@nuxt/test-utils": "3.0.0",
|
|
46
54
|
"@nuxtjs/eslint-config-typescript": "^12.0.0",
|
|
55
|
+
"@types/ws": "^8.5.4",
|
|
47
56
|
"bumpp": "^8.2.1",
|
|
48
|
-
"eslint": "8.
|
|
57
|
+
"eslint": "8.32.0",
|
|
49
58
|
"nuxt": "npm:nuxt3@latest",
|
|
50
|
-
"puppeteer": "^19.
|
|
51
|
-
"vitest": "^0.
|
|
59
|
+
"puppeteer": "^19.5.2",
|
|
60
|
+
"vitest": "^0.27.2"
|
|
52
61
|
},
|
|
53
62
|
"scripts": {
|
|
63
|
+
"build": "pnpm dev:prepare && pnpm build:module && pnpm build:client",
|
|
64
|
+
"build:client": "nuxi generate client",
|
|
65
|
+
"build:module": "nuxt-build-module",
|
|
54
66
|
"lint": "eslint \"**/*.{ts,vue,json,yml}\"",
|
|
55
|
-
"build": "nuxi prepare .playground && nuxt-module-build",
|
|
56
67
|
"dev": "nuxi dev .playground",
|
|
57
68
|
"dev:build": "nuxi build .playground",
|
|
58
69
|
"dev:prepare": "nuxt-module-build --stub && nuxi prepare .playground",
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { withQuery, withoutTrailingSlash } from "ufo";
|
|
2
|
-
import { renderSSRHead } from "@unhead/ssr";
|
|
3
|
-
import { createHeadCore } from "@unhead/vue";
|
|
4
|
-
import { defineEventHandler, getQuery } from "h3";
|
|
5
|
-
import { HtmlRendererRoute, PayloadScriptId } from "#nuxt-og-image/constants";
|
|
6
|
-
export const extractOgPayload = (html) => {
|
|
7
|
-
const payload = html.match(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.+?)<\/script>`))?.[1];
|
|
8
|
-
if (payload) {
|
|
9
|
-
return JSON.parse(payload);
|
|
10
|
-
}
|
|
11
|
-
return false;
|
|
12
|
-
};
|
|
13
|
-
export const inferOgPayload = (html) => {
|
|
14
|
-
const payload = {};
|
|
15
|
-
const title = html.match(/<meta property="og:title" content="(.*?)">/)?.[1];
|
|
16
|
-
if (title)
|
|
17
|
-
payload.title = title;
|
|
18
|
-
const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
|
|
19
|
-
if (description)
|
|
20
|
-
payload.description = description;
|
|
21
|
-
return payload;
|
|
22
|
-
};
|
|
23
|
-
export default defineEventHandler(async (req) => {
|
|
24
|
-
if (!req.path?.endsWith(HtmlRendererRoute))
|
|
25
|
-
return;
|
|
26
|
-
const path = req.path.replace(HtmlRendererRoute, "");
|
|
27
|
-
const html = await $fetch(withoutTrailingSlash(path));
|
|
28
|
-
const payload = {
|
|
29
|
-
path,
|
|
30
|
-
title: "Hello World",
|
|
31
|
-
description: "Example description",
|
|
32
|
-
image: "https://example.com/image.png",
|
|
33
|
-
...extractOgPayload(html),
|
|
34
|
-
...inferOgPayload(html),
|
|
35
|
-
...getQuery(req)
|
|
36
|
-
};
|
|
37
|
-
const result = await $fetch(withQuery(`/__nuxt_island/${payload.component || "OgImageTemplate"}`, {
|
|
38
|
-
props: JSON.stringify(payload)
|
|
39
|
-
}));
|
|
40
|
-
const head = createHeadCore();
|
|
41
|
-
head.push(result.head);
|
|
42
|
-
head.push({
|
|
43
|
-
style: [
|
|
44
|
-
{
|
|
45
|
-
innerHTML: "body { margin: 0; padding: 0; } .og-image-container { width: 1200px; height: 630px; display: flex; margin: 0 auto; }"
|
|
46
|
-
}
|
|
47
|
-
]
|
|
48
|
-
});
|
|
49
|
-
const headChunk = await renderSSRHead(head);
|
|
50
|
-
return `<!DOCTYPE html>
|
|
51
|
-
<html ${headChunk.htmlAttrs}>
|
|
52
|
-
<head>${headChunk.headTags}</head>
|
|
53
|
-
<body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div class="og-image-container">${result.html}</div>${headChunk.bodyTags}</body>
|
|
54
|
-
</html>`;
|
|
55
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createError, defineEventHandler, getRequestHeader, setHeader } from "h3";
|
|
2
|
-
import { screenshot } from "../browserUtil.mjs";
|
|
3
|
-
import { DefaultRuntimeImageSuffix, HtmlRendererRoute } from "#nuxt-og-image/constants";
|
|
4
|
-
import { createBrowser } from "#nuxt-og-image/browser";
|
|
5
|
-
export default defineEventHandler(async (e) => {
|
|
6
|
-
if (!e.path?.endsWith(DefaultRuntimeImageSuffix))
|
|
7
|
-
return;
|
|
8
|
-
const path = e.path.replace(DefaultRuntimeImageSuffix, HtmlRendererRoute);
|
|
9
|
-
const host = getRequestHeader(e, "host") || "localhost:3000";
|
|
10
|
-
const browser = await createBrowser();
|
|
11
|
-
if (!browser)
|
|
12
|
-
return createError("Could not create browser");
|
|
13
|
-
setHeader(e, "Content-Type", "image/png");
|
|
14
|
-
return await screenshot(browser, `http${host.startsWith("localhost") ? "" : "s"}://${host}/${path}`, {
|
|
15
|
-
width: 1200,
|
|
16
|
-
height: 630
|
|
17
|
-
});
|
|
18
|
-
});
|