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
@@ -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 "../../../utils-pure.mjs";
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 { readPublicAssetBase64, toBase64Image } from "../../../utils.mjs";
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, options) => {
9
+ transform: async (node, e) => {
9
10
  const src = node.props?.src;
10
- if (src && src.startsWith("/")) {
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 globalThis.$fetch(src, {
22
- responseType: "arrayBuffer",
23
- baseURL: options.requestOrigin
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 = await sizeOf(Buffer.from(response));
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, `${options.requestOrigin}`)}?${Date.now()}`;
41
+ node.props.src = `${withBase(src, `${useNitroOrigin(e)}`)}?${Date.now()}`;
47
42
  }
48
43
  }
49
44
  }
@@ -3,5 +3,6 @@ export default defineSatoriTransformer({
3
3
  filter: (node) => !!node.props?.class && !node.props?.tw,
4
4
  transform: async (node) => {
5
5
  node.props.tw = node.props.class;
6
+ node.props.tw = node.props.tw.replace(/icon|inline-style/g, "");
6
7
  }
7
8
  });
@@ -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,3 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { RendererOptions, VNode } from '../../../types';
3
+ export declare function createVNodes(e: H3Event, options: RendererOptions): Promise<VNode>;
@@ -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,2 @@
1
+ declare const _default: import("nitropack/dist/runtime/plugin").NitroAppPlugin;
2
+ export default _default;
@@ -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
- import type { NitroAppPlugin } from 'nitropack';
2
- declare const OgImagePrenderNitroPlugin: NitroAppPlugin;
3
- export default OgImagePrenderNitroPlugin;
1
+ declare const _default: import("nitropack/dist/runtime/plugin").NitroAppPlugin;
2
+ export default _default;
@@ -1,42 +1,34 @@
1
- import { appendHeader } from "h3";
2
- import { joinURL, parseURL, withoutLeadingSlash } from "ufo";
3
- import { extractAndNormaliseOgImageOptions } from "../utils-pure.mjs";
4
- import { useNitroCache } from "../../cache.mjs";
5
- import { getRouteRules } from "#internal/nitro";
6
- import { useRuntimeConfig } from "#imports";
7
- const OgImagePrenderNitroPlugin = async (nitroApp) => {
8
- if (!process.env.prerender)
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
- const { defaults } = useRuntimeConfig()["nuxt-og-image"];
11
- nitroApp.hooks.hook("render:html", async (ctx, { event }) => {
12
- const path = parseURL(event.path).pathname;
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(event)?.ogImage || {};
14
+ const routeRules = getRouteRules(e)?.ogImage || {};
16
15
  if (routeRules === false)
17
16
  return;
18
- const options = extractAndNormaliseOgImageOptions(path, [
19
- // payload may move
20
- ctx.head.join("\n"),
21
- ctx.bodyAppend.join("\n")
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
- const { update } = await useNitroCache(event, "nuxt-og-image", {
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
- export default OgImagePrenderNitroPlugin;
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,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -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
+ });
@@ -3,7 +3,7 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
3
3
  value: any;
4
4
  source: any;
5
5
  };
6
- dev: boolean | undefined;
6
+ componentNames: any;
7
7
  runtimeConfig: any;
8
8
  baseCacheKey: string;
9
9
  cachedKeys: string[];
@@ -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
- dev: process.dev,
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
+ });