nuxt-og-image 2.0.0-beta.6 → 2.0.0-beta.60
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 +311 -97
- package/dist/client/200.html +2 -2
- package/dist/client/404.html +2 -2
- package/dist/client/_nuxt/IconCSS.b41b9663.css +1 -0
- package/dist/client/_nuxt/IconCSS.c2e73ab2.js +1 -0
- package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
- package/dist/client/_nuxt/ImageLoader.ed4bfccb.js +1 -0
- package/dist/client/_nuxt/entry.862302d7.css +1 -0
- package/dist/client/_nuxt/entry.f51ac6f7.js +7 -0
- package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.a94c6c21.js} +1 -1
- package/dist/client/_nuxt/error-404.f3dd5020.css +1 -0
- package/dist/client/_nuxt/error-500.06915589.css +1 -0
- package/dist/client/_nuxt/{error-500.f7d30da5.js → error-500.3955092a.js} +1 -1
- package/dist/client/_nuxt/index.17db8b1a.js +1 -0
- package/dist/client/_nuxt/index.403133d8.css +1 -0
- package/dist/client/_nuxt/options.3e6c211b.js +1 -0
- package/dist/client/_nuxt/png.23a5e57c.js +1 -0
- package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.2980d306.js} +1 -1
- package/dist/client/_nuxt/svg.a8bba1f1.js +1 -0
- package/dist/client/_nuxt/{vnodes.a799f183.js → vnodes.64d635d6.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/options/index.html +2 -2
- package/dist/client/png/index.html +2 -2
- package/dist/client/svg/index.html +2 -2
- package/dist/client/vnodes/index.html +2 -2
- package/dist/module.d.ts +101 -11
- package/dist/module.json +2 -2
- package/dist/module.mjs +401 -141
- package/dist/runtime/browserUtil.d.ts +1 -0
- package/dist/runtime/browserUtil.mjs +10 -5
- package/dist/runtime/components/{OgImageDynamic.d.ts → OgImage/Cached.d.ts} +2 -3
- package/dist/runtime/components/OgImage/Cached.mjs +10 -0
- package/dist/runtime/components/{OgImageScreenshot.d.ts → OgImage/Screenshot.d.ts} +2 -4
- package/dist/runtime/components/{OgImageScreenshot.mjs → OgImage/Screenshot.mjs} +2 -2
- package/dist/runtime/components/{OgImageStatic.d.ts → OgImage/WithoutCache.d.ts} +2 -3
- package/dist/runtime/components/OgImage/WithoutCache.mjs +10 -0
- package/dist/runtime/components/OgImage/_OgImageDynamic.d.ts +7 -0
- package/dist/runtime/components/{OgImageDynamic.mjs → OgImage/_OgImageDynamic.mjs} +3 -3
- package/dist/runtime/components/OgImage/_OgImageStatic.d.ts +7 -0
- package/dist/runtime/components/{OgImageStatic.mjs → OgImage/_OgImageStatic.mjs} +3 -3
- package/dist/runtime/components/OgImage/index.d.ts +4 -0
- package/dist/runtime/components/OgImage/index.mjs +9 -0
- package/dist/runtime/components/OgImageTemplate/Fallback.vue +155 -0
- package/dist/runtime/composables/defineOgImage.d.ts +12 -4
- package/dist/runtime/composables/defineOgImage.mjs +46 -20
- package/dist/runtime/nitro/middleware/og.png.mjs +54 -8
- package/dist/runtime/nitro/middleware/playground.mjs +4 -3
- package/dist/runtime/nitro/plugins/prerender.d.ts +3 -0
- package/dist/runtime/nitro/plugins/prerender.mjs +28 -0
- package/dist/runtime/nitro/providers/browser/lambda.d.ts +1 -1
- package/dist/runtime/nitro/providers/browser/lambda.mjs +3 -3
- package/dist/runtime/nitro/providers/browser/{node.mjs → playwright.mjs} +0 -9
- package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -0
- package/dist/runtime/nitro/providers/browser/universal.mjs +33 -0
- package/dist/runtime/nitro/providers/png/resvg-node.d.ts +4 -0
- package/dist/runtime/nitro/providers/png/resvg-node.mjs +6 -0
- package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +3 -0
- package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +11 -0
- package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +2 -3
- package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
- package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +2 -3
- package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
- package/dist/runtime/nitro/renderers/browser.d.ts +2 -2
- package/dist/runtime/nitro/renderers/browser.mjs +14 -10
- package/dist/runtime/nitro/renderers/satori/index.d.ts +2 -2
- package/dist/runtime/nitro/renderers/satori/index.mjs +27 -32
- package/dist/runtime/nitro/renderers/satori/plugins/emojis.d.ts +1 -1
- package/dist/runtime/nitro/renderers/satori/plugins/emojis.mjs +19 -6
- package/dist/runtime/nitro/renderers/satori/plugins/encoding.d.ts +1 -1
- package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +6 -7
- package/dist/runtime/nitro/renderers/satori/plugins/flex.d.ts +1 -1
- package/dist/runtime/nitro/renderers/satori/plugins/flex.mjs +8 -10
- package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.d.ts +1 -1
- package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +45 -13
- package/dist/runtime/nitro/renderers/satori/plugins/twClasses.d.ts +1 -1
- package/dist/runtime/nitro/renderers/satori/plugins/twClasses.mjs +5 -7
- package/dist/runtime/nitro/renderers/satori/utils.d.ts +4 -5
- package/dist/runtime/nitro/renderers/satori/utils.mjs +28 -17
- package/dist/runtime/nitro/routes/debug.d.ts +8 -0
- package/dist/runtime/nitro/routes/debug.mjs +14 -0
- package/dist/runtime/nitro/routes/font.mjs +2 -2
- package/dist/runtime/nitro/routes/html.mjs +100 -25
- package/dist/runtime/nitro/routes/options.d.ts +2 -2
- package/dist/runtime/nitro/routes/options.mjs +25 -22
- package/dist/runtime/nitro/routes/svg.mjs +5 -3
- package/dist/runtime/nitro/routes/vnode.mjs +5 -3
- package/dist/runtime/nitro/utils-pure.d.ts +3 -2
- package/dist/runtime/nitro/utils-pure.mjs +9 -10
- package/dist/runtime/nitro/utils.d.ts +11 -11
- package/dist/runtime/nitro/utils.mjs +68 -54
- package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
- package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
- package/dist/types.d.ts +6 -0
- package/package.json +38 -27
- package/dist/client/_nuxt/IconCSS.a041aca0.js +0 -1
- package/dist/client/_nuxt/ImageLoader.9bf39d71.js +0 -1
- package/dist/client/_nuxt/entry.74018bda.js +0 -5
- package/dist/client/_nuxt/entry.7a8c1ab2.css +0 -1
- package/dist/client/_nuxt/error-404.1469f10f.css +0 -1
- package/dist/client/_nuxt/error-500.92b94fae.css +0 -1
- package/dist/client/_nuxt/error-component.cf7543e5.js +0 -3
- package/dist/client/_nuxt/index.3f356409.js +0 -1
- package/dist/client/_nuxt/options.56a3e5f9.js +0 -1
- package/dist/client/_nuxt/png.37f3e77b.js +0 -1
- package/dist/client/_nuxt/svg.186c6bd1.js +0 -1
- package/dist/runtime/components/OgImageBasic.island.vue +0 -92
- package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
- /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
- /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
- /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/yoga}/yoga.wasm +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import playwrightCore from "playwright-core";
|
|
2
|
+
export default async function createBrowser() {
|
|
3
|
+
if (process.dev || process.env.prerender) {
|
|
4
|
+
try {
|
|
5
|
+
const { Launcher } = await import(String("chrome-launcher"));
|
|
6
|
+
const chromePath = Launcher.getFirstInstallation();
|
|
7
|
+
return await playwrightCore.chromium.launch({
|
|
8
|
+
headless: true,
|
|
9
|
+
executablePath: chromePath
|
|
10
|
+
});
|
|
11
|
+
} catch (e) {
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return await playwrightCore.chromium.launch({
|
|
16
|
+
headless: true
|
|
17
|
+
});
|
|
18
|
+
} catch (e) {
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const playwright = await import(String("playwright"));
|
|
22
|
+
return await playwright.chromium.launch({
|
|
23
|
+
headless: true
|
|
24
|
+
});
|
|
25
|
+
} catch (e) {
|
|
26
|
+
if (process.dev) {
|
|
27
|
+
console.warn("Failed to load chromium instance. Ensure you have chrome installed, otherwise add the dependency: `npm add -D playwright`.");
|
|
28
|
+
} else {
|
|
29
|
+
console.error("Failed to load browser instance. Please open an issue with the exception: https://github.com/harlan-zw/nuxt-og-image/issues.");
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Resvg, initWasm } from "@resvg/resvg-wasm";
|
|
2
|
+
import { wasmLoader } from "../../utils.mjs";
|
|
3
|
+
const ReSvgLoader = wasmLoader("/* NUXT_OG_IMAGE_RESVG_WASM */", "/resvg.wasm");
|
|
4
|
+
export default async function(svg, options) {
|
|
5
|
+
const ReSvgWasm = await ReSvgLoader.load(options);
|
|
6
|
+
await initWasm(ReSvgWasm).catch(() => {
|
|
7
|
+
});
|
|
8
|
+
const resvgJS = new Resvg(svg, options);
|
|
9
|
+
const pngData = resvgJS.render();
|
|
10
|
+
return pngData.asPng();
|
|
11
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { ConvertOptions } from 'svg2png-wasm';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}): Promise<Uint8Array>;
|
|
2
|
+
import type { RuntimeOgImageOptions } from '../../../../types';
|
|
3
|
+
export default function (svg: string, options: ConvertOptions & RuntimeOgImageOptions): Promise<Uint8Array>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { initialize, svg2png } from "svg2png-wasm";
|
|
2
|
+
import { wasmLoader } from "../../utils.mjs";
|
|
3
|
+
const Svg2PngLoader = wasmLoader("/* NUXT_OG_IMAGE_SVG2PNG_WASM */", "/svg2png.wasm");
|
|
4
|
+
export default async function(svg, options) {
|
|
5
|
+
const Svg2PngWasm = await Svg2PngLoader.load(options);
|
|
6
|
+
await initialize(Svg2PngWasm).catch((e) => {
|
|
7
|
+
if (!e.message.trim().endsWith("function can be used only once."))
|
|
8
|
+
throw e;
|
|
9
|
+
});
|
|
10
|
+
return await svg2png(svg, options);
|
|
11
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { SatoriOptions } from 'satori';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}): Promise<string>;
|
|
2
|
+
import type { RuntimeOgImageOptions } from '../../../../types';
|
|
3
|
+
export default function (nodes: string, options: SatoriOptions & RuntimeOgImageOptions): Promise<string>;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import satori, { init } from "satori/wasm";
|
|
2
2
|
import initYoga from "yoga-wasm-web";
|
|
3
3
|
import { wasmLoader } from "../../utils.mjs";
|
|
4
|
+
const YogaLoader = wasmLoader("/* NUXT_OG_IMAGE_YOGA_WASM */", "/yoga.wasm");
|
|
4
5
|
export default async function(nodes, options) {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
init(yoga);
|
|
9
|
-
}
|
|
6
|
+
const yogaWasm = await YogaLoader.load(options);
|
|
7
|
+
const yoga = await initYoga(yogaWasm);
|
|
8
|
+
init(yoga);
|
|
10
9
|
return await satori(nodes, options);
|
|
11
10
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Renderer } from '../../../types';
|
|
2
|
-
declare const
|
|
3
|
-
export default
|
|
2
|
+
declare const BrowserRenderer: Renderer;
|
|
3
|
+
export default BrowserRenderer;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { screenshot } from "../../browserUtil.mjs";
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import loadBrowserLauncherChunk from "#nuxt-og-image/browser";
|
|
3
|
+
const BrowserRenderer = {
|
|
4
4
|
name: "browser",
|
|
5
5
|
createSvg: async function createSvg() {
|
|
6
6
|
throw new Error("Browser provider can't create SVGs.");
|
|
@@ -8,21 +8,25 @@ export default {
|
|
|
8
8
|
createVNode: async function createVNode() {
|
|
9
9
|
throw new Error("Browser provider can't create VNodes.");
|
|
10
10
|
},
|
|
11
|
-
createPng: async function createPng(
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
createPng: async function createPng(options) {
|
|
12
|
+
const launchBrowser = await loadBrowserLauncherChunk();
|
|
13
|
+
if (!launchBrowser) {
|
|
14
|
+
throw new Error("Failed to load browser. Is the `browserProvider` enabled?");
|
|
15
|
+
}
|
|
16
|
+
const browser = await launchBrowser();
|
|
17
|
+
let res = null;
|
|
15
18
|
if (browser) {
|
|
16
19
|
try {
|
|
17
|
-
|
|
20
|
+
res = await screenshot(browser, {
|
|
18
21
|
...options,
|
|
19
|
-
host:
|
|
20
|
-
path: `/api/og-image-html?path=${
|
|
22
|
+
host: options.requestOrigin,
|
|
23
|
+
path: `/api/og-image-html?path=${options.path}`
|
|
21
24
|
});
|
|
22
25
|
} finally {
|
|
23
26
|
browser.close();
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
|
-
return
|
|
29
|
+
return res;
|
|
27
30
|
}
|
|
28
31
|
};
|
|
32
|
+
export default BrowserRenderer;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Renderer } from '../../../../types';
|
|
2
|
-
declare const
|
|
3
|
-
export default
|
|
2
|
+
declare const SatoriRenderer: Renderer;
|
|
3
|
+
export default SatoriRenderer;
|
|
@@ -1,59 +1,53 @@
|
|
|
1
1
|
import { html as convertHtmlToSatori } from "satori-html";
|
|
2
|
-
import { parseURL } from "ufo";
|
|
3
|
-
import twemoji from "twemoji";
|
|
4
2
|
import { loadFont, walkSatoriTree } from "./utils.mjs";
|
|
5
3
|
import imageSrc from "./plugins/imageSrc.mjs";
|
|
6
4
|
import twClasses from "./plugins/twClasses.mjs";
|
|
7
5
|
import flex from "./plugins/flex.mjs";
|
|
8
6
|
import emojis from "./plugins/emojis.mjs";
|
|
9
7
|
import encoding from "./plugins/encoding.mjs";
|
|
10
|
-
import
|
|
8
|
+
import loadPngCreator from "#nuxt-og-image/png";
|
|
11
9
|
import loadSatori from "#nuxt-og-image/satori";
|
|
12
|
-
import { useRuntimeConfig } from "#
|
|
10
|
+
import { useRuntimeConfig } from "#imports";
|
|
13
11
|
const satoriFonts = [];
|
|
14
12
|
let fontLoadPromise = null;
|
|
15
|
-
function loadFonts(fonts) {
|
|
13
|
+
function loadFonts(baseURL, fonts) {
|
|
16
14
|
if (fontLoadPromise)
|
|
17
15
|
return fontLoadPromise;
|
|
18
|
-
return fontLoadPromise = Promise.all(fonts.map((font) => loadFont(font)));
|
|
16
|
+
return fontLoadPromise = Promise.all(fonts.map((font) => loadFont(baseURL, font)));
|
|
19
17
|
}
|
|
20
|
-
|
|
18
|
+
const SatoriRenderer = {
|
|
21
19
|
name: "satori",
|
|
22
|
-
createPng: async function createPng(
|
|
23
|
-
const svg = await this.createSvg(
|
|
24
|
-
const
|
|
25
|
-
return
|
|
20
|
+
createPng: async function createPng(options) {
|
|
21
|
+
const svg = await this.createSvg(options);
|
|
22
|
+
const pngCreator = await loadPngCreator();
|
|
23
|
+
return pngCreator(svg, options);
|
|
26
24
|
},
|
|
27
|
-
createVNode: async function createVNode(
|
|
28
|
-
const url = parseURL(baseUrl);
|
|
25
|
+
createVNode: async function createVNode(options) {
|
|
29
26
|
const html = await globalThis.$fetch("/api/og-image-html", {
|
|
30
|
-
|
|
27
|
+
params: {
|
|
28
|
+
path: options.path,
|
|
29
|
+
options: JSON.stringify(options)
|
|
30
|
+
}
|
|
31
31
|
});
|
|
32
|
-
const body = html.match(/<body[^>]*>([\s\S]*)<\/body>/)?.[1];
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
twClasses(url),
|
|
42
|
-
imageSrc(url),
|
|
43
|
-
flex(url),
|
|
44
|
-
encoding(url)
|
|
45
|
-
]);
|
|
32
|
+
const body = html.match(/<body[^>]*>([\s\S]*)<\/body>/)?.[1] || "";
|
|
33
|
+
const satoriTree = convertHtmlToSatori(body);
|
|
34
|
+
await walkSatoriTree(satoriTree, [
|
|
35
|
+
emojis,
|
|
36
|
+
twClasses,
|
|
37
|
+
imageSrc,
|
|
38
|
+
flex,
|
|
39
|
+
encoding
|
|
40
|
+
], options);
|
|
46
41
|
return satoriTree;
|
|
47
42
|
},
|
|
48
|
-
createSvg: async function createSvg(
|
|
43
|
+
createSvg: async function createSvg(options) {
|
|
49
44
|
const { fonts, satoriOptions } = useRuntimeConfig()["nuxt-og-image"];
|
|
50
|
-
const vnodes = await this.createVNode(
|
|
45
|
+
const vnodes = await this.createVNode(options);
|
|
51
46
|
if (!satoriFonts.length)
|
|
52
|
-
satoriFonts.push(...await loadFonts(fonts));
|
|
47
|
+
satoriFonts.push(...await loadFonts(options.requestOrigin, fonts));
|
|
53
48
|
const satori = await loadSatori();
|
|
54
49
|
return await satori(vnodes, {
|
|
55
50
|
...satoriOptions,
|
|
56
|
-
baseUrl,
|
|
57
51
|
fonts: satoriFonts,
|
|
58
52
|
embedFont: true,
|
|
59
53
|
width: options.width,
|
|
@@ -61,3 +55,4 @@ export default {
|
|
|
61
55
|
});
|
|
62
56
|
}
|
|
63
57
|
};
|
|
58
|
+
export default SatoriRenderer;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: import("../../../../../types").SatoriTransformer | import("../../../../../types").SatoriTransformer[];
|
|
2
2
|
export default _default;
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
2
|
-
|
|
3
|
-
return
|
|
4
|
-
|
|
2
|
+
function isEmojiFilter(node) {
|
|
3
|
+
return node.type === "img" && node.props?.class?.includes("emoji");
|
|
4
|
+
}
|
|
5
|
+
export default defineSatoriTransformer([
|
|
6
|
+
// need to make sure parent div has flex for the emoji to render inline
|
|
7
|
+
{
|
|
8
|
+
filter: (node) => node.type === "div" && Array.isArray(node.props?.children) && node.props.children.some(isEmojiFilter),
|
|
9
|
+
transform: async (node) => {
|
|
10
|
+
node.props.style = node.props.style || {};
|
|
11
|
+
node.props.style.display = "flex";
|
|
12
|
+
node.props.style.alignItems = "center";
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
filter: isEmojiFilter,
|
|
5
17
|
transform: async (node) => {
|
|
6
18
|
node.props.style = node.props.style || {};
|
|
7
19
|
node.props.style.height = "1em";
|
|
8
20
|
node.props.style.width = "1em";
|
|
9
|
-
node.props.style.margin = "0 .
|
|
21
|
+
node.props.style.margin = "0 .3em 0 .3em";
|
|
10
22
|
node.props.style.verticalAlign = "0.1em";
|
|
23
|
+
node.props.class = "";
|
|
11
24
|
}
|
|
12
|
-
}
|
|
13
|
-
|
|
25
|
+
}
|
|
26
|
+
]);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: import("../../../../../types").SatoriTransformer | import("../../../../../types").SatoriTransformer[];
|
|
2
2
|
export default _default;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
2
|
+
import { decodeHtml } from "../../../utils-pure.mjs";
|
|
3
|
+
export default defineSatoriTransformer({
|
|
4
|
+
filter: (node) => typeof node.props?.children === "string",
|
|
5
|
+
transform: async (node) => {
|
|
6
|
+
node.props.children = decodeHtml(node.props.children);
|
|
7
|
+
}
|
|
9
8
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: import("../../../../../types").SatoriTransformer | import("../../../../../types").SatoriTransformer[];
|
|
2
2
|
export default _default;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
2
|
-
export default defineSatoriTransformer(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
};
|
|
2
|
+
export default defineSatoriTransformer({
|
|
3
|
+
filter: (node) => node.type === "div" && (Array.isArray(node.props?.children) && node.props?.children.length >= 1) && (!node.props.style?.display && !node.props?.class?.includes("hidden")),
|
|
4
|
+
transform: async (node) => {
|
|
5
|
+
node.props.style = node.props.style || {};
|
|
6
|
+
node.props.style.display = "flex";
|
|
7
|
+
if (!node.props?.class?.includes("flex-"))
|
|
8
|
+
node.props.style.flexDirection = "column";
|
|
9
|
+
}
|
|
12
10
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: import("../../../../../types").SatoriTransformer | import("../../../../../types").SatoriTransformer[];
|
|
2
2
|
export default _default;
|
|
@@ -1,18 +1,50 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
1
2
|
import { withBase } from "ufo";
|
|
3
|
+
import sizeOf from "image-size";
|
|
2
4
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
3
|
-
import { readPublicAssetBase64 } from "../../../utils.mjs";
|
|
4
|
-
export default defineSatoriTransformer(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
import { readPublicAssetBase64, toBase64Image } from "../../../utils.mjs";
|
|
6
|
+
export default defineSatoriTransformer({
|
|
7
|
+
filter: (node) => node.type === "img",
|
|
8
|
+
transform: async (node, options) => {
|
|
9
|
+
const src = node.props?.src;
|
|
10
|
+
if (src && src.startsWith("/")) {
|
|
11
|
+
let updated = false;
|
|
12
|
+
const file = await readPublicAssetBase64(src);
|
|
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
|
+
if (!updated) {
|
|
20
|
+
let valid = true;
|
|
21
|
+
const response = await globalThis.$fetch(src, {
|
|
22
|
+
responseType: "arrayBuffer",
|
|
23
|
+
baseURL: options.requestOrigin
|
|
24
|
+
}).catch(() => {
|
|
25
|
+
valid = false;
|
|
26
|
+
});
|
|
27
|
+
if (valid) {
|
|
28
|
+
node.props.src = toBase64Image(src, response);
|
|
29
|
+
const imageSize = await sizeOf(Buffer.from(response));
|
|
30
|
+
dimensions = { width: imageSize.width, height: imageSize.height };
|
|
31
|
+
updated = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (dimensions?.width && dimensions?.height) {
|
|
35
|
+
const naturalAspectRatio = dimensions.width / dimensions.height;
|
|
36
|
+
if (node.props.width && !node.props.height) {
|
|
37
|
+
node.props.height = Math.round(node.props.width / naturalAspectRatio);
|
|
38
|
+
} else if (node.props.height && !node.props.width) {
|
|
39
|
+
node.props.width = Math.round(node.props.height * naturalAspectRatio);
|
|
40
|
+
} else if (!node.props.width && !node.props.height) {
|
|
41
|
+
node.props.width = dimensions.width;
|
|
42
|
+
node.props.height = dimensions.height;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!updated) {
|
|
46
|
+
node.props.src = `${withBase(src, `${options.requestOrigin}`)}?${Date.now()}`;
|
|
15
47
|
}
|
|
16
48
|
}
|
|
17
|
-
}
|
|
49
|
+
}
|
|
18
50
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: import("../../../../../types").SatoriTransformer | import("../../../../../types").SatoriTransformer[];
|
|
2
2
|
export default _default;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { defineSatoriTransformer } from "../utils.mjs";
|
|
2
|
-
export default defineSatoriTransformer(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
};
|
|
2
|
+
export default defineSatoriTransformer({
|
|
3
|
+
filter: (node) => !!node.props?.class && !node.props?.tw,
|
|
4
|
+
transform: async (node) => {
|
|
5
|
+
node.props.tw = node.props.class;
|
|
6
|
+
}
|
|
9
7
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function defineSatoriTransformer(transformer: (url: ParsedURL) => SatoriTransformer): (url: ParsedURL) => SatoriTransformer;
|
|
1
|
+
import type { FontConfig, RuntimeOgImageOptions, SatoriTransformer, VNode } from '../../../../types';
|
|
2
|
+
export declare function loadFont(requestOrigin: string, font: FontConfig): Promise<any>;
|
|
3
|
+
export declare function walkSatoriTree(node: VNode, plugins: (SatoriTransformer | SatoriTransformer[])[], props: RuntimeOgImageOptions): Promise<void>;
|
|
4
|
+
export declare function defineSatoriTransformer(transformer: SatoriTransformer | SatoriTransformer[]): SatoriTransformer | SatoriTransformer[];
|
|
@@ -1,20 +1,30 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
1
2
|
import { base64ToArrayBuffer, readPublicAsset } from "../../utils.mjs";
|
|
2
|
-
import { useStorage } from "#
|
|
3
|
+
import { useStorage } from "#imports";
|
|
3
4
|
const cachedFonts = {};
|
|
4
|
-
export async function loadFont(font) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export async function loadFont(requestOrigin, font) {
|
|
6
|
+
const fontKey = `${font.name}:${font.weight}`;
|
|
7
|
+
const storageKey = `assets:nuxt-og-image:font:${fontKey}`;
|
|
8
|
+
if (cachedFonts[fontKey])
|
|
9
|
+
return cachedFonts[fontKey];
|
|
10
|
+
const [name, weight] = fontKey.split(":");
|
|
7
11
|
let data;
|
|
8
|
-
|
|
9
|
-
if (await useStorage().hasItem(storageKey)) {
|
|
12
|
+
if (await useStorage().hasItem(storageKey))
|
|
10
13
|
data = base64ToArrayBuffer(await useStorage().getItem(storageKey));
|
|
11
|
-
|
|
14
|
+
if (!data && name === "Inter" && ["400", "700"].includes(weight)) {
|
|
15
|
+
data = await readPublicAsset(`/inter-latin-ext-${weight}-normal.woff`);
|
|
12
16
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
if (font.path) {
|
|
18
|
+
data = await readPublicAsset(font.path);
|
|
19
|
+
if (!data) {
|
|
20
|
+
try {
|
|
21
|
+
data = await globalThis.$fetch(font.path, {
|
|
22
|
+
responseType: "arrayBuffer",
|
|
23
|
+
baseURL: requestOrigin
|
|
24
|
+
});
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
18
28
|
}
|
|
19
29
|
if (!data) {
|
|
20
30
|
const fontUrl = await globalThis.$fetch("/api/og-image-font", {
|
|
@@ -24,10 +34,11 @@ export async function loadFont(font) {
|
|
|
24
34
|
responseType: "arrayBuffer"
|
|
25
35
|
});
|
|
26
36
|
}
|
|
37
|
+
cachedFonts[fontKey] = { name, weight: Number(weight), data, style: "normal" };
|
|
27
38
|
await useStorage().setItem(storageKey, Buffer.from(data).toString("base64"));
|
|
28
|
-
return cachedFonts[
|
|
39
|
+
return cachedFonts[fontKey];
|
|
29
40
|
}
|
|
30
|
-
export async function walkSatoriTree(
|
|
41
|
+
export async function walkSatoriTree(node, plugins, props) {
|
|
31
42
|
if (!node.props?.children)
|
|
32
43
|
return;
|
|
33
44
|
if (Array.isArray(node.props.children) && node.props.children.length === 0) {
|
|
@@ -36,11 +47,11 @@ export async function walkSatoriTree(url, node, plugins) {
|
|
|
36
47
|
}
|
|
37
48
|
for (const child of node.props.children || []) {
|
|
38
49
|
if (child) {
|
|
39
|
-
for (const plugin of plugins) {
|
|
50
|
+
for (const plugin of plugins.flat()) {
|
|
40
51
|
if (plugin.filter(child))
|
|
41
|
-
await plugin.transform(child);
|
|
52
|
+
await plugin.transform(child, props);
|
|
42
53
|
}
|
|
43
|
-
await walkSatoriTree(
|
|
54
|
+
await walkSatoriTree(child, plugins, props);
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
57
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineEventHandler, setHeader } from "h3";
|
|
2
|
+
import { useRuntimeConfig, useSiteConfig } from "#imports";
|
|
3
|
+
export default defineEventHandler(async (e) => {
|
|
4
|
+
setHeader(e, "Content-Type", "application/json");
|
|
5
|
+
const runtimeConfig = useRuntimeConfig()["nuxt-og-image"];
|
|
6
|
+
const siteConfig = await useSiteConfig(e);
|
|
7
|
+
return {
|
|
8
|
+
siteConfigUrl: {
|
|
9
|
+
value: siteConfig.url,
|
|
10
|
+
source: siteConfig._context.url || "unknown"
|
|
11
|
+
},
|
|
12
|
+
runtimeConfig
|
|
13
|
+
};
|
|
14
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getQuery } from "h3";
|
|
2
|
-
import {
|
|
3
|
-
export default
|
|
2
|
+
import { cachedEventHandler } from "#imports";
|
|
3
|
+
export default cachedEventHandler(async (e) => {
|
|
4
4
|
const { name, weight } = getQuery(e);
|
|
5
5
|
if (!name || !weight)
|
|
6
6
|
return "Provide a font name and weight";
|