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.
Files changed (173) hide show
  1. package/README.md +2 -2
  2. package/dist/client/200.html +8 -8
  3. package/dist/client/404.html +8 -8
  4. package/dist/client/_nuxt/IconCSS.8f429b14.css +1 -0
  5. package/dist/client/_nuxt/IconCSS.ac398b56.js +1 -0
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/d1d517c3-4927-4803-bbb0-d94e9d3e9581.json +1 -0
  8. package/dist/client/_nuxt/entry.434c2c45.css +1 -0
  9. package/dist/client/_nuxt/entry.bdb8a8d5.js +137 -0
  10. package/dist/client/_nuxt/{error-404.407d76a3.js → error-404.f37119e7.js} +1 -1
  11. package/dist/client/_nuxt/{error-500.531c4147.js → error-500.74b0a30f.js} +1 -1
  12. package/dist/client/grid.png +0 -0
  13. package/dist/client/index.html +8 -8
  14. package/dist/module.d.mts +43 -39
  15. package/dist/module.d.ts +43 -39
  16. package/dist/module.json +1 -1
  17. package/dist/module.mjs +341 -667
  18. package/dist/runtime/cache.d.ts +4 -4
  19. package/dist/runtime/cache.mjs +2 -1
  20. package/dist/runtime/components/OgImage/Cached.mjs +1 -1
  21. package/dist/runtime/components/OgImage/Dynamic.mjs +1 -1
  22. package/dist/runtime/components/OgImage/Screenshot.mjs +1 -1
  23. package/dist/runtime/components/OgImage/Static.mjs +1 -1
  24. package/dist/runtime/components/OgImage/WithoutCache.mjs +1 -1
  25. package/dist/runtime/components/OgImage/index.mjs +1 -1
  26. package/dist/runtime/components/Templates/Community/Nuxt.vue +183 -0
  27. package/dist/runtime/components/Templates/Official/BrandedLogo.vue +28 -0
  28. package/dist/runtime/components/Templates/Official/Fallback.vue +147 -0
  29. package/dist/runtime/components/Templates/Official/SimpleBlog.vue +33 -0
  30. package/dist/runtime/components/Templates/Official/Wave.vue +33 -0
  31. package/dist/runtime/components/Templates/Official/WithEmoji.vue +27 -0
  32. package/dist/runtime/composables/defineOgImage.d.ts +10 -6
  33. package/dist/runtime/composables/defineOgImage.mjs +21 -9
  34. package/dist/runtime/core/bindings/chromium/node.d.ts +2 -0
  35. package/dist/runtime/{nitro/providers/browser/universal.mjs → core/bindings/chromium/node.mjs} +3 -3
  36. package/dist/runtime/core/bindings/resvg/node.d.ts +6 -0
  37. package/dist/runtime/core/bindings/resvg/node.mjs +5 -0
  38. package/dist/runtime/core/bindings/resvg/wasm.d.ts +40 -0
  39. package/dist/runtime/core/bindings/resvg/wasm.mjs +7 -0
  40. package/dist/runtime/core/bindings/satori/node.d.ts +6 -0
  41. package/dist/runtime/core/bindings/satori/node.mjs +5 -0
  42. package/dist/runtime/core/bindings/satori/yoga-wasm.d.ts +6 -0
  43. package/dist/runtime/core/bindings/satori/yoga-wasm.mjs +7 -0
  44. package/dist/runtime/core/bindings/sharp/node.d.ts +2 -0
  45. package/dist/runtime/core/bindings/sharp/node.mjs +2 -0
  46. package/dist/runtime/core/bindings/sharp/wasm.d.ts +2 -0
  47. package/dist/runtime/core/bindings/sharp/wasm.mjs +2 -0
  48. package/dist/runtime/core/cache/prerender.d.ts +6 -0
  49. package/dist/runtime/core/cache/prerender.mjs +6 -0
  50. package/dist/runtime/core/env/assets.d.ts +2 -0
  51. package/dist/runtime/core/env/assets.mjs +15 -0
  52. package/dist/runtime/core/font/cache.d.ts +1 -0
  53. package/dist/runtime/core/font/cache.mjs +1 -0
  54. package/dist/runtime/core/font/fetch.d.ts +3 -0
  55. package/dist/runtime/core/font/fetch.mjs +29 -0
  56. package/dist/runtime/core/html/fetch.d.ts +3 -0
  57. package/dist/runtime/core/html/fetch.mjs +117 -0
  58. package/dist/runtime/core/options/extract.d.ts +3 -0
  59. package/dist/runtime/{nitro/utils-pure.mjs → core/options/extract.mjs} +23 -21
  60. package/dist/runtime/core/options/fetch.d.ts +3 -0
  61. package/dist/runtime/core/options/fetch.mjs +21 -0
  62. package/dist/runtime/core/options/normalise.d.ts +2 -0
  63. package/dist/runtime/{composables/util.mjs → core/options/normalise.mjs} +9 -6
  64. package/dist/runtime/core/renderers/chromium/index.d.ts +3 -0
  65. package/dist/runtime/core/renderers/chromium/index.mjs +26 -0
  66. package/dist/runtime/core/renderers/chromium/screenshot.d.ts +6 -0
  67. package/dist/runtime/core/renderers/chromium/screenshot.mjs +47 -0
  68. package/dist/runtime/core/renderers/satori/fonts.d.ts +3 -0
  69. package/dist/runtime/core/renderers/satori/fonts.mjs +8 -0
  70. package/dist/runtime/core/renderers/satori/index.d.ts +5 -0
  71. package/dist/runtime/core/renderers/satori/index.mjs +53 -0
  72. package/dist/runtime/core/renderers/satori/instances.d.ts +39 -0
  73. package/dist/runtime/core/renderers/satori/instances.mjs +17 -0
  74. package/dist/runtime/{nitro → core}/renderers/satori/plugins/encoding.mjs +1 -1
  75. package/dist/runtime/{nitro → core}/renderers/satori/plugins/imageSrc.mjs +9 -14
  76. package/dist/runtime/{nitro → core}/renderers/satori/plugins/twClasses.mjs +1 -0
  77. package/dist/runtime/core/renderers/satori/utils.d.ts +4 -0
  78. package/dist/runtime/core/renderers/satori/utils.mjs +20 -0
  79. package/dist/runtime/core/renderers/satori/vnodes.d.ts +3 -0
  80. package/dist/runtime/core/renderers/satori/vnodes.mjs +21 -0
  81. package/dist/runtime/core/utils/resolveRendererContext.d.ts +7 -0
  82. package/dist/runtime/core/utils/resolveRendererContext.mjs +76 -0
  83. package/dist/runtime/nitro/plugins/nuxt-content.d.ts +2 -0
  84. package/dist/runtime/nitro/plugins/nuxt-content.mjs +50 -0
  85. package/dist/runtime/nitro/plugins/prerender.d.ts +2 -3
  86. package/dist/runtime/nitro/plugins/prerender.mjs +25 -33
  87. package/dist/runtime/nuxt/plugins/nuxt-content-canonical-urls.mjs +29 -0
  88. package/dist/runtime/nuxt/plugins/route-rule-og-image.server.d.ts +2 -0
  89. package/dist/runtime/nuxt/plugins/route-rule-og-image.server.mjs +72 -0
  90. package/dist/runtime/{nitro/routes/debug.d.ts → server/routes/__og-image__/debug.json.d.ts} +1 -1
  91. package/dist/runtime/{nitro/routes/debug.mjs → server/routes/__og-image__/debug.json.mjs} +3 -2
  92. package/dist/runtime/server/routes/__og-image__/font-[name]-[weight].[extension].mjs +30 -0
  93. package/dist/runtime/server/routes/__og-image__/image-[path]-og.[extension].mjs +44 -0
  94. package/dist/runtime/types.d.ts +29 -24
  95. package/dist/runtime/utilts.d.ts +2 -0
  96. package/dist/runtime/utilts.mjs +8 -0
  97. package/dist/types.d.mts +3 -2
  98. package/dist/types.d.ts +3 -2
  99. package/package.json +37 -22
  100. package/dist/client/_nuxt/IconCSS.4a9d43d0.css +0 -1
  101. package/dist/client/_nuxt/IconCSS.9c30257a.js +0 -1
  102. package/dist/client/_nuxt/ImageLoader.752b0c7a.js +0 -1
  103. package/dist/client/_nuxt/ImageLoader.7571516f.css +0 -1
  104. package/dist/client/_nuxt/builds/meta/bb64bb30-cf6f-4625-97ba-06e6a0d3f8d1.json +0 -1
  105. package/dist/client/_nuxt/entry.39e39f51.css +0 -1
  106. package/dist/client/_nuxt/entry.ac864471.js +0 -135
  107. package/dist/client/_nuxt/index.dc1538d5.js +0 -1
  108. package/dist/client/_nuxt/index.ffbea0a9.css +0 -1
  109. package/dist/client/_nuxt/options.a77f5921.js +0 -1
  110. package/dist/client/_nuxt/png.41e0b446.js +0 -1
  111. package/dist/client/_nuxt/shiki.d4e62362.js +0 -7
  112. package/dist/client/_nuxt/svg.b8198280.js +0 -1
  113. package/dist/client/_nuxt/vnodes.67720126.js +0 -1
  114. package/dist/client/options/index.html +0 -15
  115. package/dist/client/png/index.html +0 -15
  116. package/dist/client/svg/index.html +0 -15
  117. package/dist/client/vnodes/index.html +0 -15
  118. package/dist/runtime/browserUtil.d.ts +0 -5
  119. package/dist/runtime/browserUtil.mjs +0 -41
  120. package/dist/runtime/components/OgImageTemplate/Fallback.vue +0 -161
  121. package/dist/runtime/composables/util.d.ts +0 -2
  122. package/dist/runtime/nitro/middleware/og.png.mjs +0 -69
  123. package/dist/runtime/nitro/middleware/playground.d.ts +0 -2
  124. package/dist/runtime/nitro/middleware/playground.mjs +0 -27
  125. package/dist/runtime/nitro/providers/browser/lambda.d.ts +0 -1
  126. package/dist/runtime/nitro/providers/browser/lambda.mjs +0 -9
  127. package/dist/runtime/nitro/providers/browser/playwright.d.ts +0 -1
  128. package/dist/runtime/nitro/providers/browser/playwright.mjs +0 -22
  129. package/dist/runtime/nitro/providers/browser/universal.d.ts +0 -2
  130. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +0 -4
  131. package/dist/runtime/nitro/providers/png/resvg-node.mjs +0 -6
  132. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +0 -3
  133. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +0 -11
  134. package/dist/runtime/nitro/providers/png/svg2png.d.ts +0 -3
  135. package/dist/runtime/nitro/providers/png/svg2png.mjs +0 -11
  136. package/dist/runtime/nitro/providers/satori/default.d.ts +0 -2
  137. package/dist/runtime/nitro/providers/satori/default.mjs +0 -4
  138. package/dist/runtime/nitro/providers/satori/yoga-wasm.d.ts +0 -3
  139. package/dist/runtime/nitro/providers/satori/yoga-wasm.mjs +0 -10
  140. package/dist/runtime/nitro/renderers/browser.d.ts +0 -3
  141. package/dist/runtime/nitro/renderers/browser.mjs +0 -36
  142. package/dist/runtime/nitro/renderers/satori/index.d.ts +0 -3
  143. package/dist/runtime/nitro/renderers/satori/index.mjs +0 -58
  144. package/dist/runtime/nitro/renderers/satori/utils.d.ts +0 -4
  145. package/dist/runtime/nitro/renderers/satori/utils.mjs +0 -60
  146. package/dist/runtime/nitro/routes/font.mjs +0 -22
  147. package/dist/runtime/nitro/routes/html.d.ts +0 -2
  148. package/dist/runtime/nitro/routes/html.mjs +0 -178
  149. package/dist/runtime/nitro/routes/options.d.ts +0 -3
  150. package/dist/runtime/nitro/routes/options.mjs +0 -35
  151. package/dist/runtime/nitro/routes/svg.mjs +0 -19
  152. package/dist/runtime/nitro/routes/vnode.d.ts +0 -2
  153. package/dist/runtime/nitro/routes/vnode.mjs +0 -19
  154. package/dist/runtime/nitro/utils-pure.d.ts +0 -3
  155. package/dist/runtime/nitro/utils.d.ts +0 -18
  156. package/dist/runtime/nitro/utils.mjs +0 -108
  157. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  158. package/dist/runtime/public-assets-optional/svg2png/svg2png.wasm +0 -0
  159. package/dist/runtime/public-assets-optional/yoga/yoga.wasm +0 -0
  160. /package/dist/runtime/{nitro/providers → core/bindings}/css-inline/mock.d.ts +0 -0
  161. /package/dist/runtime/{nitro/providers → core/bindings}/css-inline/mock.mjs +0 -0
  162. /package/dist/runtime/{nitro/providers/css-inline/css-inline.d.ts → core/bindings/css-inline/node.d.ts} +0 -0
  163. /package/dist/runtime/{nitro/providers/css-inline/css-inline.mjs → core/bindings/css-inline/node.mjs} +0 -0
  164. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/emojis.d.ts +0 -0
  165. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/emojis.mjs +0 -0
  166. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/encoding.d.ts +0 -0
  167. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/flex.d.ts +0 -0
  168. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/flex.mjs +0 -0
  169. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/imageSrc.d.ts +0 -0
  170. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/twClasses.d.ts +0 -0
  171. /package/dist/runtime/{nitro/routes/font.d.ts → nuxt/plugins/nuxt-content-canonical-urls.d.ts} +0 -0
  172. /package/dist/runtime/{nitro/middleware/og.png.d.ts → server/routes/__og-image__/font-[name]-[weight].[extension].d.ts} +0 -0
  173. /package/dist/runtime/{nitro/routes/svg.d.ts → server/routes/__og-image__/image-[path]-og.[extension].d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import playwrightCore from "playwright-core";
2
- export default async function createBrowser() {
3
- if (process.dev || process.env.prerender) {
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 (process.dev) {
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,6 @@
1
+ import { Resvg as _Resvg } from '@resvg/resvg-js';
2
+ declare const _default: {
3
+ initWasmPromise: Promise<void>;
4
+ Resvg: typeof _Resvg;
5
+ };
6
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { Resvg as _Resvg } from "@resvg/resvg-js";
2
+ export default {
3
+ initWasmPromise: Promise.resolve(),
4
+ Resvg: _Resvg
5
+ };
@@ -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,7 @@
1
+ import { Resvg as _Resvg, initWasm } from "@resvg/resvg-wasm";
2
+ import ReSVGWasm from "@resvg/resvg-wasm/index_bg.wasm";
3
+ export default {
4
+ initWasmPromise: initWasm(ReSVGWasm).catch(() => {
5
+ }),
6
+ Resvg: _Resvg
7
+ };
@@ -0,0 +1,6 @@
1
+ import _satori from 'satori';
2
+ declare const _default: {
3
+ initWasmPromise: Promise<void>;
4
+ satori: typeof _satori;
5
+ };
6
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import _satori from "satori";
2
+ export default {
3
+ initWasmPromise: Promise.resolve(),
4
+ satori: _satori
5
+ };
@@ -0,0 +1,6 @@
1
+ import _satori from 'satori/wasm';
2
+ declare const _default: {
3
+ initWasmPromise: Promise<void>;
4
+ satori: typeof _satori;
5
+ };
6
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import _satori, { init } from "satori/wasm";
2
+ import initYoga from "yoga-wasm-web";
3
+ import YogaWasm from "yoga-wasm-web/dist/yoga.wasm";
4
+ export default {
5
+ initWasmPromise: initYoga(YogaWasm).then((yoga) => init(yoga)),
6
+ satori: _satori
7
+ };
@@ -0,0 +1,2 @@
1
+ import sharp from 'sharp';
2
+ export default sharp;
@@ -0,0 +1,2 @@
1
+ import sharp from "sharp";
2
+ export default sharp;
@@ -0,0 +1,2 @@
1
+ import sharp from '@img/sharp-wasm32/package.json';
2
+ export default sharp;
@@ -0,0 +1,2 @@
1
+ import sharp from "@img/sharp-wasm32/package.json";
2
+ export default sharp;
@@ -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,2 @@
1
+ export declare function base64ToArrayBuffer(base64: string): ArrayBuffer;
2
+ export declare function toBase64Image(fileName: string, data: string | ArrayBuffer): string;
@@ -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,3 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { FontConfig } from '../../types';
3
+ export declare function loadFont(e: H3Event, font: FontConfig): Promise<any>;
@@ -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,3 @@
1
+ import { type H3Event } from 'h3';
2
+ import type { RendererOptions, RuntimeOgImageOptions } from '../../types';
3
+ export declare function fetchHTML(e: H3Event, options: RuntimeOgImageOptions | RendererOptions): Promise<string>;
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ import type { OgImageOptions } from '../../types';
2
+ export declare function decodeHtml(html: string): string;
3
+ export declare function extractAndNormaliseOgImageOptions(html: string): OgImageOptions | false;
@@ -1,4 +1,3 @@
1
- import { defu } from "defu";
2
1
  export function decodeHtml(html) {
3
2
  return html.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&cent;/g, "\xA2").replace(/&pound;/g, "\xA3").replace(/&yen;/g, "\xA5").replace(/&euro;/g, "\u20AC").replace(/&copy;/g, "\xA9").replace(/&reg;/g, "\xAE").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'").replace(/&#x2F;/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(path, html, routeRules, defaults) {
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 payload = JSON.parse(htmlPayload);
21
- Object.entries(payload).forEach(([key, value]) => {
19
+ const payload2 = JSON.parse(htmlPayload);
20
+ Object.entries(payload2).forEach(([key, value]) => {
22
21
  if (!value)
23
- delete payload[key];
22
+ delete payload2[key];
24
23
  });
25
- options = defu(payload, routeRules);
24
+ options = payload2;
26
25
  } catch (e) {
27
- options = routeRules;
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
- if (!options.description) {
34
- const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
35
- if (description)
36
- options.description = description;
37
- else
38
- options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
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
- const decoded = decodeObjectHtmlEntities(options);
41
- return defu(
42
- decoded,
43
- // runtime options
44
- { path },
45
- defaults
46
- );
48
+ return payload;
47
49
  }
@@ -0,0 +1,3 @@
1
+ import { type H3Error, type H3Event } from 'h3';
2
+ import type { OgImageOptions } from '../../types';
3
+ export declare function fetchPathHtmlAndExtractOptions(e: H3Event, path: string): Promise<H3Error | OgImageOptions>;
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ import type { OgImageOptions } from '../../types';
2
+ export declare function normaliseOptions(_options: OgImageOptions): any;
@@ -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 normaliseOgImageOptions(_options) {
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 (!options.provider)
8
- options.provider = "satori";
9
- const { runtimeSatori } = useRuntimeConfig()["nuxt-og-image"];
10
- if (options.provider === "satori" && !runtimeSatori)
11
- options.provider = "browser";
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,3 @@
1
+ import type { Renderer } from '../../../types';
2
+ declare const ChromiumRenderer: Renderer;
3
+ export default ChromiumRenderer;
@@ -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,3 @@
1
+ import type { FontConfig } from '../../../types';
2
+ export declare const satoriFonts: any[];
3
+ export declare function loadFonts(baseURL: string, fonts: FontConfig[]): Promise<any>;
@@ -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,5 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { Renderer, RendererOptions } from '../../../types';
3
+ export declare function createSvg(e: H3Event, options: RendererOptions): Promise<string>;
4
+ declare const SatoriRenderer: Renderer;
5
+ export default SatoriRenderer;
@@ -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;