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
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type _satori from 'satori';
|
|
2
|
+
export declare function useResvg(): Promise<new (svg: string | Uint8Array, options?: import("@resvg/resvg-wasm").ResvgRenderOptions | undefined) => {
|
|
3
|
+
free(): void;
|
|
4
|
+
render(): {
|
|
5
|
+
free(): void;
|
|
6
|
+
asPng(): Uint8Array;
|
|
7
|
+
readonly height: number;
|
|
8
|
+
readonly pixels: Uint8Array;
|
|
9
|
+
readonly width: number;
|
|
10
|
+
};
|
|
11
|
+
toString(): string;
|
|
12
|
+
innerBBox(): {
|
|
13
|
+
free(): void;
|
|
14
|
+
height: number;
|
|
15
|
+
width: number;
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
} | undefined;
|
|
19
|
+
getBBox(): {
|
|
20
|
+
free(): void;
|
|
21
|
+
height: number;
|
|
22
|
+
width: number;
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
} | undefined;
|
|
26
|
+
cropByBBox(bbox: {
|
|
27
|
+
free(): void;
|
|
28
|
+
height: number;
|
|
29
|
+
width: number;
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
}): void;
|
|
33
|
+
imagesToResolve(): any[];
|
|
34
|
+
resolveImage(href: string, buffer: Uint8Array): void;
|
|
35
|
+
readonly height: number;
|
|
36
|
+
readonly width: number;
|
|
37
|
+
}>;
|
|
38
|
+
export declare function useSatori(): Promise<typeof _satori>;
|
|
39
|
+
export declare function useSharp(): Promise<any>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const sharpInstance = { instance: void 0 };
|
|
2
|
+
const resvgInstance = { instance: void 0 };
|
|
3
|
+
const satoriInstance = { instance: void 0 };
|
|
4
|
+
export async function useResvg() {
|
|
5
|
+
resvgInstance.instance = resvgInstance.instance || await import("#nuxt-og-image/bindings/resvg").then((m) => m.default);
|
|
6
|
+
await resvgInstance.instance.initWasmPromise;
|
|
7
|
+
return resvgInstance.instance.Resvg;
|
|
8
|
+
}
|
|
9
|
+
export async function useSatori() {
|
|
10
|
+
satoriInstance.instance = satoriInstance.instance || await import("#nuxt-og-image/bindings/satori").then((m) => m.default);
|
|
11
|
+
await satoriInstance.instance.initWasmPromise;
|
|
12
|
+
return satoriInstance.instance.satori;
|
|
13
|
+
}
|
|
14
|
+
export async function useSharp() {
|
|
15
|
+
sharpInstance.instance = sharpInstance.instance || await import("#nuxt-og-image/bindings/sharp").then((m) => m.default);
|
|
16
|
+
return sharpInstance.instance;
|
|
17
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
2
|
-
import { decodeHtml } from "../../../
|
|
2
|
+
import { decodeHtml } from "../../../options/extract.mjs";
|
|
3
3
|
export default defineSatoriTransformer({
|
|
4
4
|
filter: (node) => typeof node.props?.children === "string",
|
|
5
5
|
transform: async (node) => {
|
|
@@ -2,31 +2,26 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import { withBase } from "ufo";
|
|
3
3
|
import sizeOf from "image-size";
|
|
4
4
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { toBase64Image } from "../../../env/assets.mjs";
|
|
6
|
+
import { useNitroOrigin } from "#imports";
|
|
6
7
|
export default defineSatoriTransformer({
|
|
7
8
|
filter: (node) => node.type === "img",
|
|
8
|
-
transform: async (node,
|
|
9
|
+
transform: async (node, e) => {
|
|
9
10
|
const src = node.props?.src;
|
|
10
|
-
if (src
|
|
11
|
+
if (src) {
|
|
11
12
|
let updated = false;
|
|
12
|
-
const file = await readPublicAssetBase64(src);
|
|
13
13
|
let dimensions;
|
|
14
|
-
if (file) {
|
|
15
|
-
node.props.src = file.src;
|
|
16
|
-
dimensions = { width: file.width, height: file.height };
|
|
17
|
-
updated = true;
|
|
18
|
-
}
|
|
19
14
|
if (!updated) {
|
|
20
15
|
let valid = true;
|
|
21
|
-
const response = await
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const response = await e.$fetch(src, {
|
|
17
|
+
baseURL: useNitroOrigin(e),
|
|
18
|
+
responseType: "arrayBuffer"
|
|
24
19
|
}).catch(() => {
|
|
25
20
|
valid = false;
|
|
26
21
|
});
|
|
27
22
|
if (valid) {
|
|
28
23
|
node.props.src = toBase64Image(src, response);
|
|
29
|
-
const imageSize =
|
|
24
|
+
const imageSize = sizeOf(Buffer.from(response));
|
|
30
25
|
dimensions = { width: imageSize.width, height: imageSize.height };
|
|
31
26
|
updated = true;
|
|
32
27
|
}
|
|
@@ -43,7 +38,7 @@ export default defineSatoriTransformer({
|
|
|
43
38
|
}
|
|
44
39
|
}
|
|
45
40
|
if (!updated) {
|
|
46
|
-
node.props.src = `${withBase(src, `${
|
|
41
|
+
node.props.src = `${withBase(src, `${useNitroOrigin(e)}`)}?${Date.now()}`;
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { SatoriTransformer, VNode } from '../../../types';
|
|
3
|
+
export declare function walkSatoriTree(e: H3Event, node: VNode, plugins: (SatoriTransformer | SatoriTransformer[])[]): Promise<void>;
|
|
4
|
+
export declare function defineSatoriTransformer(transformer: SatoriTransformer | SatoriTransformer[]): SatoriTransformer | SatoriTransformer[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export async function walkSatoriTree(e, node, plugins) {
|
|
2
|
+
if (!node.props?.children || !Array.isArray(node.props.children))
|
|
3
|
+
return;
|
|
4
|
+
if (node.props.children.length === 0) {
|
|
5
|
+
delete node.props.children;
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
for (const child of node.props.children || []) {
|
|
9
|
+
if (child) {
|
|
10
|
+
for (const plugin of plugins.flat()) {
|
|
11
|
+
if (plugin.filter(child))
|
|
12
|
+
await plugin.transform(child, e);
|
|
13
|
+
}
|
|
14
|
+
await walkSatoriTree(e, child, plugins);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function defineSatoriTransformer(transformer) {
|
|
19
|
+
return transformer;
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { html as convertHtmlToSatori } from "satori-html";
|
|
2
|
+
import { fetchHTML } from "../../html/fetch.mjs";
|
|
3
|
+
import { walkSatoriTree } from "./utils.mjs";
|
|
4
|
+
import emojis from "./plugins/emojis.mjs";
|
|
5
|
+
import twClasses from "./plugins/twClasses.mjs";
|
|
6
|
+
import imageSrc from "./plugins/imageSrc.mjs";
|
|
7
|
+
import flex from "./plugins/flex.mjs";
|
|
8
|
+
import encoding from "./plugins/encoding.mjs";
|
|
9
|
+
export async function createVNodes(e, options) {
|
|
10
|
+
const html = options.html || await fetchHTML(e, options);
|
|
11
|
+
const body = html.match(/<body[^>]*>([\s\S]*)<\/body>/)?.[1] || html;
|
|
12
|
+
const satoriTree = convertHtmlToSatori(body);
|
|
13
|
+
await walkSatoriTree(e, satoriTree, [
|
|
14
|
+
emojis,
|
|
15
|
+
twClasses,
|
|
16
|
+
imageSrc,
|
|
17
|
+
flex,
|
|
18
|
+
encoding
|
|
19
|
+
]);
|
|
20
|
+
return satoriTree;
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { H3Error, H3Event } from 'h3';
|
|
2
|
+
import type { Renderer, RuntimeOgImageOptions } from '../../types';
|
|
3
|
+
export declare function resolveRendererContext(e: H3Event): Promise<H3Error | {
|
|
4
|
+
extension: RuntimeOgImageOptions['extension'];
|
|
5
|
+
renderer: Renderer;
|
|
6
|
+
options: RuntimeOgImageOptions;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { parseURL, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
2
|
+
import { createError, getQuery } from "h3";
|
|
3
|
+
import { defu } from "defu";
|
|
4
|
+
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
5
|
+
import { fetchPathHtmlAndExtractOptions } from "../options/fetch.mjs";
|
|
6
|
+
import { prerenderCache } from "../cache/prerender.mjs";
|
|
7
|
+
import { useRuntimeConfig } from "#imports";
|
|
8
|
+
const satoriRendererInstance = { instance: void 0 };
|
|
9
|
+
const chromiumRendererInstance = { instance: void 0 };
|
|
10
|
+
export async function resolveRendererContext(e) {
|
|
11
|
+
const runtimeConfig = useRuntimeConfig()["nuxt-og-image"];
|
|
12
|
+
const path = parseURL(e.path).pathname;
|
|
13
|
+
const extension = path.split(".").pop();
|
|
14
|
+
if (!extension) {
|
|
15
|
+
return createError({
|
|
16
|
+
statusCode: 400,
|
|
17
|
+
statusMessage: `Missing OG Image type.`
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const basePath = withoutTrailingSlash(
|
|
21
|
+
path.replace("/__og-image__/image", "").replace(`/og.${extension}`, "")
|
|
22
|
+
);
|
|
23
|
+
const queryParams = { ...getQuery(e) };
|
|
24
|
+
let options = queryParams.options;
|
|
25
|
+
if (!options) {
|
|
26
|
+
if (import.meta.prerender) {
|
|
27
|
+
const key = [
|
|
28
|
+
withoutLeadingSlash(basePath === "/" || !basePath ? "index" : basePath).replaceAll("/", "-")
|
|
29
|
+
].join(":");
|
|
30
|
+
options = await prerenderCache?.getItem(key);
|
|
31
|
+
} else {
|
|
32
|
+
const payloadOptions = await fetchPathHtmlAndExtractOptions(e, basePath);
|
|
33
|
+
if (payloadOptions instanceof Error)
|
|
34
|
+
return payloadOptions;
|
|
35
|
+
options = payloadOptions;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
delete queryParams.options;
|
|
39
|
+
const _routeRulesMatcher = toRouteMatcher(
|
|
40
|
+
createRadixRouter({ routes: useRuntimeConfig().nitro?.routeRules })
|
|
41
|
+
);
|
|
42
|
+
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(
|
|
43
|
+
withoutBase(basePath.split("?")[0], useRuntimeConfig().app.baseURL)
|
|
44
|
+
).reverse()).ogImage;
|
|
45
|
+
options = defu(queryParams, routeRules, options, runtimeConfig.defaults);
|
|
46
|
+
if (!options) {
|
|
47
|
+
return createError({
|
|
48
|
+
statusCode: 404,
|
|
49
|
+
statusMessage: "OG Image not found."
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
let renderer;
|
|
53
|
+
switch (options.renderer) {
|
|
54
|
+
case "satori":
|
|
55
|
+
renderer = satoriRendererInstance.instance = satoriRendererInstance.instance || await import("#nuxt-og-image/renderers/satori").then((m) => Object.keys(m.default).length ? m.default : false);
|
|
56
|
+
break;
|
|
57
|
+
case "chromium":
|
|
58
|
+
renderer = chromiumRendererInstance.instance = chromiumRendererInstance.instance || await import("#nuxt-og-image/renderers/chromium").then((m) => Object.keys(m.default).length ? m.default : false);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (!renderer) {
|
|
62
|
+
throw createError({
|
|
63
|
+
statusCode: 400,
|
|
64
|
+
statusMessage: `Renderer ${options.renderer} is missing.`
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
renderer,
|
|
69
|
+
extension,
|
|
70
|
+
options: {
|
|
71
|
+
...options,
|
|
72
|
+
extension: extension === "json" ? options.extension : extension,
|
|
73
|
+
path: basePath
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { defineNitroPlugin } from "nitropack/dist/runtime/plugin";
|
|
2
|
+
import { defu } from "defu";
|
|
3
|
+
import { getOgImagePath } from "../../utilts.mjs";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
6
|
+
nitroApp.hooks.hook("content:file:afterParse", async (content) => {
|
|
7
|
+
if (content._draft || content._extension !== "md" || content._partial || content.indexable === false || content.index === false)
|
|
8
|
+
return;
|
|
9
|
+
if (content.path && content.ogImage) {
|
|
10
|
+
const ogImageConfig = typeof content.ogImage === "object" ? content.ogImage : {};
|
|
11
|
+
const { defaults } = useRuntimeConfig()["nuxt-og-image"];
|
|
12
|
+
const optionsWithDefault = defu(ogImageConfig, defaults);
|
|
13
|
+
const src = getOgImagePath(content.path, optionsWithDefault.extension);
|
|
14
|
+
const payload = {
|
|
15
|
+
title: content.title,
|
|
16
|
+
excerpt: content.description || content.excerpt,
|
|
17
|
+
...content.ogImage
|
|
18
|
+
};
|
|
19
|
+
Object.entries(ogImageConfig).forEach(([key, val]) => {
|
|
20
|
+
payload[key.replace(/-([a-z])/g, (g) => g[1].toUpperCase())] = val;
|
|
21
|
+
});
|
|
22
|
+
content.head = defu({
|
|
23
|
+
script: [
|
|
24
|
+
{
|
|
25
|
+
id: "nuxt-og-image-options",
|
|
26
|
+
type: "application/json",
|
|
27
|
+
processTemplateParams: true,
|
|
28
|
+
innerHTML: payload,
|
|
29
|
+
// we want this to be last in our head
|
|
30
|
+
tagPosition: "bodyClose"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
meta: [
|
|
34
|
+
{ property: "og:image", content: src },
|
|
35
|
+
{ property: "og:image:width", content: optionsWithDefault.width },
|
|
36
|
+
{ property: "og:image:height", content: optionsWithDefault.height },
|
|
37
|
+
{ property: "og:image:type", content: `image/${optionsWithDefault.extension}` },
|
|
38
|
+
{ property: "og:image:alt", content: optionsWithDefault.alt },
|
|
39
|
+
// twitter
|
|
40
|
+
{ name: "twitter:card", content: "summary_large_image" },
|
|
41
|
+
{ name: "twitter:image:src", content: src },
|
|
42
|
+
{ name: "twitter:image:width", content: optionsWithDefault.width },
|
|
43
|
+
{ name: "twitter:image:height", content: optionsWithDefault.height },
|
|
44
|
+
{ name: "twitter:image:alt", content: optionsWithDefault.alt }
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return content;
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export default OgImagePrenderNitroPlugin;
|
|
1
|
+
declare const _default: import("nitropack/dist/runtime/plugin").NitroAppPlugin;
|
|
2
|
+
export default _default;
|
|
@@ -1,42 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { extractAndNormaliseOgImageOptions } from "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
if (!
|
|
1
|
+
import { parseURL, withoutLeadingSlash } from "ufo";
|
|
2
|
+
import { getRouteRules } from "nitropack/dist/runtime/route-rules";
|
|
3
|
+
import { extractAndNormaliseOgImageOptions } from "../../core/options/extract.mjs";
|
|
4
|
+
import { prerenderCache, prerenderChromiumContext } from "../../core/cache/prerender.mjs";
|
|
5
|
+
import { isInternalRoute } from "../../utilts.mjs";
|
|
6
|
+
import { defineNitroPlugin } from "nitropack/dist/runtime/plugin";
|
|
7
|
+
export default defineNitroPlugin(async (nitro) => {
|
|
8
|
+
if (!import.meta.prerender)
|
|
9
9
|
return;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (path.includes(".") || path.startsWith("/__nuxt_island/"))
|
|
10
|
+
nitro.hooks.hook("render:html", async ({ head, bodyAppend }, e) => {
|
|
11
|
+
const path = parseURL(e.event.path).pathname;
|
|
12
|
+
if (isInternalRoute(path))
|
|
14
13
|
return;
|
|
15
|
-
const routeRules = getRouteRules(
|
|
14
|
+
const routeRules = getRouteRules(e)?.ogImage || {};
|
|
16
15
|
if (routeRules === false)
|
|
17
16
|
return;
|
|
18
|
-
const options = extractAndNormaliseOgImageOptions(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
].join("\n"), routeRules, defaults);
|
|
17
|
+
const options = extractAndNormaliseOgImageOptions([
|
|
18
|
+
head.join("\n"),
|
|
19
|
+
bodyAppend.join("\n")
|
|
20
|
+
].join("\n"));
|
|
23
21
|
if (!options)
|
|
24
22
|
return;
|
|
25
23
|
const key = [
|
|
26
|
-
withoutLeadingSlash(path === "/" || !path ? "index" : path).replaceAll("/", "-")
|
|
27
|
-
"options"
|
|
24
|
+
withoutLeadingSlash(path === "/" || !path ? "index" : path).replaceAll("/", "-")
|
|
28
25
|
].join(":");
|
|
29
|
-
|
|
30
|
-
key,
|
|
31
|
-
// shouldn't change for the prerender, 5 min cache
|
|
32
|
-
cacheTtl: 5 * 60 * 1e3,
|
|
33
|
-
cache: true,
|
|
34
|
-
headers: false,
|
|
35
|
-
skipRestore: true
|
|
36
|
-
});
|
|
37
|
-
await update(options);
|
|
38
|
-
if (options.provider === "satori")
|
|
39
|
-
appendHeader(event, "x-nitro-prerender", joinURL(path, "/__og_image__/og.png"));
|
|
26
|
+
await prerenderCache.setItem(key, options);
|
|
40
27
|
});
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
nitro.hooks.hook("close", () => {
|
|
29
|
+
if (prerenderChromiumContext.browser) {
|
|
30
|
+
prerenderChromiumContext.browser.close();
|
|
31
|
+
prerenderChromiumContext.browser = void 0;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parseURL } from "ufo";
|
|
2
|
+
import { toValue } from "vue";
|
|
3
|
+
import { isInternalRoute } from "../../utilts.mjs";
|
|
4
|
+
import { useRequestEvent, withSiteUrl } from "#imports";
|
|
5
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
6
|
+
nuxtApp.hooks.hook("app:rendered", async (ctx) => {
|
|
7
|
+
const { ssrContext } = ctx;
|
|
8
|
+
const e = useRequestEvent();
|
|
9
|
+
const path = parseURL(e.path).pathname;
|
|
10
|
+
if (isInternalRoute(path))
|
|
11
|
+
return;
|
|
12
|
+
ssrContext?.head.use({
|
|
13
|
+
key: "nuxt-og-image:canonical-urls",
|
|
14
|
+
hooks: {
|
|
15
|
+
"tags:resolve": async ({ tags }) => {
|
|
16
|
+
for (const tag of tags) {
|
|
17
|
+
if (tag.tag === "meta" && (tag.props.property === "og:image" || tag.props.name === "twitter:image:src")) {
|
|
18
|
+
if (!tag.props.content.startsWith("https")) {
|
|
19
|
+
await nuxtApp.runWithContext(() => {
|
|
20
|
+
tag.props.content = toValue(withSiteUrl(tag.props.content));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { parseURL, withoutBase } from "ufo";
|
|
3
|
+
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
4
|
+
import { normaliseOptions } from "../../core/options/normalise.mjs";
|
|
5
|
+
import { getOgImagePath, isInternalRoute } from "../../utilts.mjs";
|
|
6
|
+
import { defineNuxtPlugin, useRequestEvent, useRuntimeConfig } from "#imports";
|
|
7
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
8
|
+
nuxtApp.hooks.hook("app:rendered", async (ctx) => {
|
|
9
|
+
const { ssrContext } = ctx;
|
|
10
|
+
const e = useRequestEvent();
|
|
11
|
+
const path = parseURL(e.path).pathname;
|
|
12
|
+
if (isInternalRoute(path))
|
|
13
|
+
return;
|
|
14
|
+
const _routeRulesMatcher = toRouteMatcher(
|
|
15
|
+
createRadixRouter({ routes: ssrContext?.runtimeConfig?.nitro?.routeRules })
|
|
16
|
+
);
|
|
17
|
+
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(
|
|
18
|
+
withoutBase(path.split("?")[0], ssrContext?.runtimeConfig?.app.baseURL)
|
|
19
|
+
).reverse()).ogImage;
|
|
20
|
+
if (typeof routeRules === "undefined")
|
|
21
|
+
return;
|
|
22
|
+
const payloadIndex = ssrContext.head.headEntries().findIndex((entry) => {
|
|
23
|
+
return entry.input?.script?.[0]?.id === "nuxt-og-image-options";
|
|
24
|
+
});
|
|
25
|
+
if (payloadIndex >= 0 && routeRules === false) {
|
|
26
|
+
ssrContext.head.headEntries().splice(payloadIndex, 1);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (payloadIndex >= 0)
|
|
30
|
+
return;
|
|
31
|
+
const options = normaliseOptions(routeRules);
|
|
32
|
+
const { defaults } = useRuntimeConfig()["nuxt-og-image"];
|
|
33
|
+
const optionsWithDefault = normaliseOptions(defu(options, defaults));
|
|
34
|
+
const src = getOgImagePath(ssrContext.url, optionsWithDefault.extension);
|
|
35
|
+
ssrContext?.head.push({
|
|
36
|
+
script: [
|
|
37
|
+
{
|
|
38
|
+
id: "nuxt-og-image-options",
|
|
39
|
+
type: "application/json",
|
|
40
|
+
processTemplateParams: true,
|
|
41
|
+
innerHTML: () => {
|
|
42
|
+
const payload = {
|
|
43
|
+
title: "%s"
|
|
44
|
+
};
|
|
45
|
+
Object.entries(options).forEach(([key, val]) => {
|
|
46
|
+
payload[key.replace(/-([a-z])/g, (g) => g[1].toUpperCase())] = val;
|
|
47
|
+
});
|
|
48
|
+
return payload;
|
|
49
|
+
},
|
|
50
|
+
// we want this to be last in our head
|
|
51
|
+
tagPosition: "bodyClose"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
meta: [
|
|
55
|
+
{ property: "og:image", content: src },
|
|
56
|
+
{ property: "og:image:width", content: optionsWithDefault.width },
|
|
57
|
+
{ property: "og:image:height", content: optionsWithDefault.height },
|
|
58
|
+
{ property: "og:image:type", content: `image/${optionsWithDefault.extension}` },
|
|
59
|
+
{ property: "og:image:alt", content: optionsWithDefault.alt },
|
|
60
|
+
// twitter
|
|
61
|
+
{ name: "twitter:card", content: "summary_large_image" },
|
|
62
|
+
{ name: "twitter:image:src", content: src },
|
|
63
|
+
{ name: "twitter:image:width", content: optionsWithDefault.width },
|
|
64
|
+
{ name: "twitter:image:height", content: optionsWithDefault.height },
|
|
65
|
+
{ name: "twitter:image:alt", content: optionsWithDefault.alt }
|
|
66
|
+
]
|
|
67
|
+
}, {
|
|
68
|
+
mode: "server",
|
|
69
|
+
tagPriority: 35
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { defineEventHandler, setHeader } from "h3";
|
|
2
2
|
import { prefixStorage } from "unstorage";
|
|
3
3
|
import { useRuntimeConfig, useSiteConfig, useStorage } from "#imports";
|
|
4
|
+
import { componentNames } from "#nuxt-og-image/component-names.mjs";
|
|
4
5
|
export default defineEventHandler(async (e) => {
|
|
5
6
|
setHeader(e, "Content-Type", "application/json");
|
|
6
7
|
const runtimeConfig = useRuntimeConfig()["nuxt-og-image"];
|
|
7
|
-
const siteConfig = await useSiteConfig(e);
|
|
8
|
+
const siteConfig = await useSiteConfig(e, { debug: true });
|
|
8
9
|
const baseCacheKey = runtimeConfig.runtimeCacheStorage === "default" ? `/cache/nuxt-og-image@${runtimeConfig.version}` : `/nuxt-og-image@${runtimeConfig.version}`;
|
|
9
10
|
const cache = prefixStorage(useStorage(), `${baseCacheKey}/`);
|
|
10
11
|
return {
|
|
@@ -12,7 +13,7 @@ export default defineEventHandler(async (e) => {
|
|
|
12
13
|
value: siteConfig.url,
|
|
13
14
|
source: siteConfig._context.url || "unknown"
|
|
14
15
|
},
|
|
15
|
-
|
|
16
|
+
componentNames,
|
|
16
17
|
runtimeConfig,
|
|
17
18
|
baseCacheKey,
|
|
18
19
|
cachedKeys: await cache.getKeys()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createError, defineEventHandler, proxyRequest, sendRedirect } from "h3";
|
|
2
|
+
import { parseURL } from "ufo";
|
|
3
|
+
export default defineEventHandler(async (e) => {
|
|
4
|
+
const path = parseURL(e.path).pathname;
|
|
5
|
+
const [name, weight] = path.split("/font/")[1].split(".")[0].split("/");
|
|
6
|
+
if (!name || !weight)
|
|
7
|
+
return "Provide a font name and weight";
|
|
8
|
+
const css = await globalThis.$fetch(`https://fonts.googleapis.com/css2?family=${name}:wght@${weight}`, {
|
|
9
|
+
headers: {
|
|
10
|
+
// Make sure it returns TTF.
|
|
11
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
if (!css) {
|
|
15
|
+
return createError({
|
|
16
|
+
statusCode: 500,
|
|
17
|
+
statusMessage: `Invalid Google Font ${name}:${weight}`
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const ttfResource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/);
|
|
21
|
+
if (ttfResource?.[1])
|
|
22
|
+
return proxyRequest(e, ttfResource[1], {});
|
|
23
|
+
const woff2Resource = css.match(/src: url\((.+)\) format\('woff2'\)/);
|
|
24
|
+
if (woff2Resource?.[1])
|
|
25
|
+
return sendRedirect(e, woff2Resource[1]);
|
|
26
|
+
return createError({
|
|
27
|
+
statusCode: 500,
|
|
28
|
+
statusMessage: `Malformed Google Font CSS ${css}`
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { H3Error, createError, defineEventHandler, setHeader } from "h3";
|
|
2
|
+
import { resolveRendererContext } from "../../../core/utils/resolveRendererContext.mjs";
|
|
3
|
+
import { fetchHTML } from "../../../core/html/fetch.mjs";
|
|
4
|
+
export default defineEventHandler(async (e) => {
|
|
5
|
+
const ctx = await resolveRendererContext(e);
|
|
6
|
+
if (ctx instanceof H3Error)
|
|
7
|
+
return ctx;
|
|
8
|
+
const { extension, options, renderer } = ctx;
|
|
9
|
+
if (!options.cacheTtl || import.meta.dev) {
|
|
10
|
+
setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
|
|
11
|
+
setHeader(e, "Pragma", "no-cache");
|
|
12
|
+
setHeader(e, "Expires", "0");
|
|
13
|
+
}
|
|
14
|
+
switch (extension) {
|
|
15
|
+
case "html":
|
|
16
|
+
setHeader(e, "Content-Type", `text/html`);
|
|
17
|
+
return fetchHTML(e, options);
|
|
18
|
+
case "json":
|
|
19
|
+
setHeader(e, "Content-Type", "application/json");
|
|
20
|
+
return {
|
|
21
|
+
...ctx,
|
|
22
|
+
siteConfig: useSiteConfig(e),
|
|
23
|
+
vnodes: options.renderer === "satori" ? await renderer.createImage(e, { ...options, extension: "json" }) : void 0
|
|
24
|
+
};
|
|
25
|
+
case "svg":
|
|
26
|
+
case "png":
|
|
27
|
+
case "jpeg":
|
|
28
|
+
case "jpg":
|
|
29
|
+
if (!renderer.supportedFormats.includes(options.extension)) {
|
|
30
|
+
return createError({
|
|
31
|
+
statusCode: 400,
|
|
32
|
+
statusMessage: `Generating ${options.extension}'s with ${renderer.name} is not supported.`
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
setHeader(e, "Content-Type", `image/${options.extension === "svg" ? "svg+xml" : options.extension}`);
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
return createError({
|
|
39
|
+
statusCode: 400,
|
|
40
|
+
statusMessage: `Invalid request for og.${options.extension}.`
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return renderer.createImage(e, options);
|
|
44
|
+
});
|