nuxt-og-image 2.0.0-beta.6 → 2.0.0-beta.61

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 (114) hide show
  1. package/README.md +311 -97
  2. package/dist/client/200.html +2 -2
  3. package/dist/client/404.html +2 -2
  4. package/dist/client/_nuxt/IconCSS.b41b9663.css +1 -0
  5. package/dist/client/_nuxt/IconCSS.c2e73ab2.js +1 -0
  6. package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
  7. package/dist/client/_nuxt/ImageLoader.ed4bfccb.js +1 -0
  8. package/dist/client/_nuxt/entry.862302d7.css +1 -0
  9. package/dist/client/_nuxt/entry.f51ac6f7.js +7 -0
  10. package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.a94c6c21.js} +1 -1
  11. package/dist/client/_nuxt/error-404.f3dd5020.css +1 -0
  12. package/dist/client/_nuxt/error-500.06915589.css +1 -0
  13. package/dist/client/_nuxt/{error-500.f7d30da5.js → error-500.3955092a.js} +1 -1
  14. package/dist/client/_nuxt/index.17db8b1a.js +1 -0
  15. package/dist/client/_nuxt/index.403133d8.css +1 -0
  16. package/dist/client/_nuxt/options.3e6c211b.js +1 -0
  17. package/dist/client/_nuxt/png.23a5e57c.js +1 -0
  18. package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.2980d306.js} +1 -1
  19. package/dist/client/_nuxt/svg.a8bba1f1.js +1 -0
  20. package/dist/client/_nuxt/{vnodes.a799f183.js → vnodes.64d635d6.js} +1 -1
  21. package/dist/client/index.html +2 -2
  22. package/dist/client/options/index.html +2 -2
  23. package/dist/client/png/index.html +2 -2
  24. package/dist/client/svg/index.html +2 -2
  25. package/dist/client/vnodes/index.html +2 -2
  26. package/dist/module.d.ts +101 -11
  27. package/dist/module.json +2 -2
  28. package/dist/module.mjs +402 -141
  29. package/dist/runtime/browserUtil.d.ts +1 -0
  30. package/dist/runtime/browserUtil.mjs +10 -5
  31. package/dist/runtime/components/{OgImageDynamic.d.ts → OgImage/Cached.d.ts} +2 -2
  32. package/dist/runtime/components/OgImage/Cached.mjs +10 -0
  33. package/dist/runtime/components/{OgImageScreenshot.d.ts → OgImage/Screenshot.d.ts} +2 -2
  34. package/dist/runtime/components/{OgImageScreenshot.mjs → OgImage/Screenshot.mjs} +2 -2
  35. package/dist/runtime/components/OgImage/WithoutCache.d.ts +4 -0
  36. package/dist/runtime/components/OgImage/WithoutCache.mjs +10 -0
  37. package/dist/runtime/components/OgImage/_OgImageDynamic.d.ts +8 -0
  38. package/dist/runtime/components/{OgImageDynamic.mjs → OgImage/_OgImageDynamic.mjs} +3 -3
  39. package/dist/runtime/components/OgImage/_OgImageStatic.d.ts +8 -0
  40. package/dist/runtime/components/{OgImageStatic.mjs → OgImage/_OgImageStatic.mjs} +3 -3
  41. package/dist/runtime/components/{OgImageStatic.d.ts → OgImage/index.d.ts} +2 -2
  42. package/dist/runtime/components/OgImage/index.mjs +10 -0
  43. package/dist/runtime/components/OgImageTemplate/Fallback.vue +155 -0
  44. package/dist/runtime/composables/defineOgImage.d.ts +12 -4
  45. package/dist/runtime/composables/defineOgImage.mjs +46 -20
  46. package/dist/runtime/nitro/middleware/og.png.mjs +54 -8
  47. package/dist/runtime/nitro/middleware/playground.mjs +4 -3
  48. package/dist/runtime/nitro/plugins/prerender.d.ts +3 -0
  49. package/dist/runtime/nitro/plugins/prerender.mjs +28 -0
  50. package/dist/runtime/nitro/providers/browser/lambda.d.ts +1 -1
  51. package/dist/runtime/nitro/providers/browser/lambda.mjs +3 -3
  52. package/dist/runtime/nitro/providers/browser/{node.mjs → playwright.mjs} +0 -9
  53. package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -0
  54. package/dist/runtime/nitro/providers/browser/universal.mjs +33 -0
  55. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +4 -0
  56. package/dist/runtime/nitro/providers/png/resvg-node.mjs +6 -0
  57. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +3 -0
  58. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +11 -0
  59. package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +2 -3
  60. package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
  61. package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +2 -3
  62. package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
  63. package/dist/runtime/nitro/renderers/browser.d.ts +2 -2
  64. package/dist/runtime/nitro/renderers/browser.mjs +14 -10
  65. package/dist/runtime/nitro/renderers/satori/index.d.ts +2 -2
  66. package/dist/runtime/nitro/renderers/satori/index.mjs +27 -32
  67. package/dist/runtime/nitro/renderers/satori/plugins/emojis.d.ts +1 -1
  68. package/dist/runtime/nitro/renderers/satori/plugins/emojis.mjs +19 -6
  69. package/dist/runtime/nitro/renderers/satori/plugins/encoding.d.ts +1 -1
  70. package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +6 -7
  71. package/dist/runtime/nitro/renderers/satori/plugins/flex.d.ts +1 -1
  72. package/dist/runtime/nitro/renderers/satori/plugins/flex.mjs +8 -10
  73. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.d.ts +1 -1
  74. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +45 -13
  75. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.d.ts +1 -1
  76. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.mjs +5 -7
  77. package/dist/runtime/nitro/renderers/satori/utils.d.ts +4 -5
  78. package/dist/runtime/nitro/renderers/satori/utils.mjs +28 -17
  79. package/dist/runtime/nitro/routes/debug.d.ts +8 -0
  80. package/dist/runtime/nitro/routes/debug.mjs +14 -0
  81. package/dist/runtime/nitro/routes/font.mjs +2 -2
  82. package/dist/runtime/nitro/routes/html.mjs +100 -25
  83. package/dist/runtime/nitro/routes/options.d.ts +2 -2
  84. package/dist/runtime/nitro/routes/options.mjs +25 -22
  85. package/dist/runtime/nitro/routes/svg.mjs +5 -3
  86. package/dist/runtime/nitro/routes/vnode.mjs +5 -3
  87. package/dist/runtime/nitro/utils-pure.d.ts +3 -2
  88. package/dist/runtime/nitro/utils-pure.mjs +9 -10
  89. package/dist/runtime/nitro/utils.d.ts +11 -11
  90. package/dist/runtime/nitro/utils.mjs +68 -54
  91. package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
  92. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  93. package/dist/types.d.ts +6 -0
  94. package/package.json +38 -27
  95. package/dist/client/_nuxt/IconCSS.a041aca0.js +0 -1
  96. package/dist/client/_nuxt/ImageLoader.9bf39d71.js +0 -1
  97. package/dist/client/_nuxt/entry.74018bda.js +0 -5
  98. package/dist/client/_nuxt/entry.7a8c1ab2.css +0 -1
  99. package/dist/client/_nuxt/error-404.1469f10f.css +0 -1
  100. package/dist/client/_nuxt/error-500.92b94fae.css +0 -1
  101. package/dist/client/_nuxt/error-component.cf7543e5.js +0 -3
  102. package/dist/client/_nuxt/index.3f356409.js +0 -1
  103. package/dist/client/_nuxt/options.56a3e5f9.js +0 -1
  104. package/dist/client/_nuxt/png.37f3e77b.js +0 -1
  105. package/dist/client/_nuxt/svg.186c6bd1.js +0 -1
  106. package/dist/runtime/components/OgImageBasic.island.vue +0 -92
  107. package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
  108. /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
  109. /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
  110. /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
  111. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
  112. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
  113. /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
  114. /package/dist/runtime/{public-assets → public-assets-optional/yoga}/yoga.wasm +0 -0
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ import type { Buffer } from 'node:buffer';
2
3
  import type { Browser } from 'playwright-core';
3
4
  import type { ScreenshotOptions } from '../types';
4
5
  export declare function screenshot(browser: Browser, options: Partial<ScreenshotOptions> & Record<string, any>): Promise<Buffer>;
@@ -6,9 +6,9 @@ export async function screenshot(browser, options) {
6
6
  width: options.width || 1200,
7
7
  height: options.height || 630
8
8
  });
9
- const isHtml = options.path.startsWith("html:") || options.html;
9
+ const isHtml = options.html || options.path?.startsWith("html:");
10
10
  if (isHtml) {
11
- const html = options.html || options.path.substring(5);
11
+ const html = options.html || options.path?.substring(5);
12
12
  await page.evaluate((html2) => {
13
13
  document.open("text/html");
14
14
  document.write(html2);
@@ -17,10 +17,13 @@ export async function screenshot(browser, options) {
17
17
  await page.waitForLoadState("networkidle");
18
18
  } else {
19
19
  await page.goto(`${options.host}${options.path}`, {
20
- timeout: 1e4,
20
+ timeout: process.env.prerender || process.dev ? 1e4 : 3500,
21
21
  waitUntil: "networkidle"
22
22
  });
23
23
  }
24
+ const screenshotOptions = {
25
+ timeout: process.env.prerender || process.dev ? 1e4 : 3500
26
+ };
24
27
  if (options.delay)
25
28
  await page.waitForTimeout(options.delay);
26
29
  if (options.mask) {
@@ -30,6 +33,8 @@ export async function screenshot(browser, options) {
30
33
  }, options.mask);
31
34
  }
32
35
  if (options.selector)
33
- return await page.locator(options.selector).screenshot();
34
- return await page.screenshot();
36
+ return await page.locator(options.selector).screenshot(screenshotOptions);
37
+ const screenshot2 = await page.screenshot(screenshotOptions);
38
+ await page.close();
39
+ return screenshot2;
35
40
  }
@@ -1,5 +1,5 @@
1
- import type { OgImageOptions } from '../../types';
1
+ import type { OgImageOptions } from '../../../types';
2
2
  declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageOptions>>, {
3
3
  [x: string]: any;
4
- }>;
4
+ }, {}>;
5
5
  export default _default;
@@ -0,0 +1,10 @@
1
+ import { defineComponent } from "vue";
2
+ import { defineOgImageCached } from "#imports";
3
+ export default defineComponent({
4
+ name: "OgImageCached",
5
+ async setup(_, { attrs }) {
6
+ if (process.server)
7
+ await defineOgImageCached(attrs);
8
+ return () => null;
9
+ }
10
+ });
@@ -1,6 +1,6 @@
1
- import type { OgImageScreenshotOptions } from '../../types';
1
+ import type { OgImageScreenshotOptions } from '../../../types';
2
2
  declare const _default: import("vue").DefineComponent<OgImageScreenshotOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageScreenshotOptions>>, {
3
3
  [x: string]: any;
4
4
  [x: number]: any;
5
- }>;
5
+ }, {}>;
6
6
  export default _default;
@@ -2,9 +2,9 @@ import { defineComponent } from "vue";
2
2
  import { defineOgImageScreenshot } from "#imports";
3
3
  export default defineComponent({
4
4
  name: "OgImageScreenshot",
5
- setup(_, { attrs }) {
5
+ async setup(_, { attrs }) {
6
6
  if (process.server)
7
- defineOgImageScreenshot(attrs);
7
+ await defineOgImageScreenshot(attrs);
8
8
  return () => null;
9
9
  }
10
10
  });
@@ -0,0 +1,4 @@
1
+ declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<any>, {} | {
2
+ [x: string]: any;
3
+ }, {}>;
4
+ export default _default;
@@ -0,0 +1,10 @@
1
+ import { defineComponent } from "vue";
2
+ import { defineOgImageWithoutCache } from "#imports";
3
+ export default defineComponent({
4
+ name: "OgImageWithoutCache",
5
+ async setup(_, { attrs }) {
6
+ if (process.server)
7
+ defineOgImageWithoutCache(attrs);
8
+ return () => null;
9
+ }
10
+ });
@@ -0,0 +1,8 @@
1
+ import type { OgImageOptions } from '../../../types';
2
+ /**
3
+ * @deprecated Use OgImageWithoutCache
4
+ */
5
+ declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageOptions>>, {
6
+ [x: string]: any;
7
+ }, {}>;
8
+ export default _default;
@@ -1,10 +1,10 @@
1
1
  import { defineComponent } from "vue";
2
- import { defineOgImageDynamic } from "#imports";
2
+ import { defineOgImageWithoutCache } from "#imports";
3
3
  export default defineComponent({
4
4
  name: "OgImageDynamic",
5
- setup(_, { attrs }) {
5
+ async setup(_, { attrs }) {
6
6
  if (process.server)
7
- defineOgImageDynamic(attrs);
7
+ await defineOgImageWithoutCache(attrs);
8
8
  return () => null;
9
9
  }
10
10
  });
@@ -0,0 +1,8 @@
1
+ import type { OgImageOptions } from '../../../types';
2
+ /**
3
+ * @deprecated Use OgImageCached instead
4
+ */
5
+ declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageOptions>>, {
6
+ [x: string]: any;
7
+ }, {}>;
8
+ export default _default;
@@ -1,10 +1,10 @@
1
1
  import { defineComponent } from "vue";
2
- import { defineOgImageStatic } from "#imports";
2
+ import { defineOgImageCached } from "#imports";
3
3
  export default defineComponent({
4
4
  name: "OgImageStatic",
5
- setup(_, { attrs }) {
5
+ async setup(_, { attrs }) {
6
6
  if (process.server)
7
- defineOgImageStatic(attrs);
7
+ await defineOgImageCached(attrs);
8
8
  return () => null;
9
9
  }
10
10
  });
@@ -1,5 +1,5 @@
1
- import type { OgImageOptions } from '../../types';
1
+ import type { OgImageOptions } from '../../../types';
2
2
  declare const _default: import("vue").DefineComponent<OgImageOptions, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImageOptions>>, {
3
3
  [x: string]: any;
4
- }>;
4
+ }, {}>;
5
5
  export default _default;
@@ -0,0 +1,10 @@
1
+ import { defineComponent } from "vue";
2
+ import { defineOgImage } from "#imports";
3
+ export default defineComponent({
4
+ name: "OgImage",
5
+ async setup(_, { attrs }) {
6
+ if (process.server)
7
+ await defineOgImage(attrs);
8
+ return () => null;
9
+ }
10
+ });
@@ -0,0 +1,155 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ // inherited attrs can mess up the satori parser
5
+ defineOptions({
6
+ inheritAttrs: false,
7
+ })
8
+
9
+ const props = defineProps({
10
+ path: String,
11
+ title: {
12
+ type: String,
13
+ default: 'Og Image Template',
14
+ },
15
+ description: {
16
+ type: String,
17
+ default: 'Set a description to change me.',
18
+ },
19
+ background: {
20
+ type: String,
21
+ default: 'linear-gradient(to bottom, #dbf4ff, #fff1f1)',
22
+ },
23
+ color: {
24
+ type: String,
25
+ },
26
+ padding: {
27
+ type: String,
28
+ default: '0 100px',
29
+ },
30
+ titleFontSize: {
31
+ type: String,
32
+ default: '75px',
33
+ },
34
+ descriptionFontSize: {
35
+ type: String,
36
+ default: '35px',
37
+ },
38
+ icon: {
39
+ type: [String, Boolean],
40
+ default: false,
41
+ },
42
+ siteName: {
43
+ type: String,
44
+ required: false,
45
+ },
46
+ siteLogo: {
47
+ type: String,
48
+ required: false,
49
+ },
50
+ })
51
+
52
+ const backgroundAttrs = computed(() => {
53
+ // we want to make a
54
+ // const isBackgroundTw = props.background?.startsWith('bg-')
55
+ return {
56
+ style: {
57
+ display: 'flex',
58
+ position: 'absolute',
59
+ width: '100%',
60
+ height: '100%',
61
+ background: 'rgba(5, 5, 5,1)',
62
+ },
63
+ }
64
+ })
65
+
66
+ const backgroundFlareAttrs = computed(() => {
67
+ // we want to make a
68
+ // const isBackgroundTw = props.background?.startsWith('bg-')
69
+ return {
70
+ style: {
71
+ display: 'flex',
72
+ position: 'absolute',
73
+ right: '-100%',
74
+ top: '10%',
75
+ width: '200%',
76
+ height: '200%',
77
+ backgroundImage: 'radial-gradient(circle, rgba(0,220,130, 0.5) 0%, rgba(5, 5, 5,0.3) 50%, rgba(5, 5, 5,0) 70%)',
78
+ },
79
+ }
80
+ })
81
+
82
+ const containerAttrs = computed(() => {
83
+ const isColorTw = props.color?.startsWith('text-')
84
+
85
+ const classes = [
86
+ 'w-full',
87
+ 'h-full',
88
+ 'flex',
89
+ 'text-gray-100',
90
+ 'relative',
91
+ 'items-center',
92
+ 'justify-center',
93
+ ]
94
+ const styles: Record<string, any> = {
95
+ padding: props.padding,
96
+ }
97
+ if (isColorTw)
98
+ classes.push(props.color)
99
+ else
100
+ styles.color = props.color
101
+ return { class: classes, style: styles }
102
+ })
103
+
104
+ const titleAttrs = computed(() => {
105
+ const classes = []
106
+ const styles = {
107
+ fontWeight: 'bold',
108
+ marginBottom: '50px',
109
+ fontSize: props.titleFontSize,
110
+ }
111
+ return { class: classes, style: styles }
112
+ })
113
+
114
+ const descriptionAttrs = computed(() => {
115
+ const classes = []
116
+ const styles = {
117
+ fontSize: props.descriptionFontSize,
118
+ }
119
+ return { class: classes, style: styles }
120
+ })
121
+
122
+ const siteConfig = useSiteConfig()
123
+ const siteName = computed(() => {
124
+ return props.siteName || siteConfig.name
125
+ })
126
+ const siteLogo = computed(() => {
127
+ return props.siteLogo || siteConfig.logo || 'https://nuxt.com/assets/design-kit/logo/full-logo-green-light.png'
128
+ })
129
+ </script>
130
+
131
+ <template>
132
+ <div v-bind="backgroundAttrs" />
133
+ <div v-bind="backgroundFlareAttrs" />
134
+ <div v-bind="containerAttrs">
135
+ <div class="flex flex-row justify-between items-center" style="margin-bottom: 100px;">
136
+ <div class="flex flex-col w-full" :style="icon ? { width: '65%' } : {}">
137
+ <div v-bind="titleAttrs">
138
+ {{ title || 'Null Title' }}
139
+ </div>
140
+ <div v-if="description" v-bind="descriptionAttrs">
141
+ {{ description }}
142
+ </div>
143
+ </div>
144
+ <div v-if="typeof icon === 'string'" style="width: 30%;" class="flex justify-end">
145
+ <Icon :name="icon" size="250px" style="margin: 0 auto;" />
146
+ </div>
147
+ </div>
148
+ <div class="flex flex-row absolute bottom-10 text-left items-start">
149
+ <img v-if="siteLogo" :src="siteLogo" height="30">
150
+ <div v-else-if="siteName" style="font-size: 25px;">
151
+ {{ siteName }}
152
+ </div>
153
+ </div>
154
+ </div>
155
+ </template>
@@ -1,5 +1,13 @@
1
1
  import type { OgImageOptions, OgImageScreenshotOptions } from '../../types';
2
- export declare function defineOgImageScreenshot(options?: OgImageScreenshotOptions): void;
3
- export declare function defineOgImageDynamic(options?: OgImageOptions): void;
4
- export declare function defineOgImageStatic(options?: OgImageOptions): void;
5
- export declare function defineOgImage(options?: OgImageOptions): void;
2
+ export declare function defineOgImageScreenshot(options?: OgImageScreenshotOptions): Promise<void>;
3
+ /**
4
+ * @deprecated Use `defineOgImage` or `defineOgImageCached` instead
5
+ */
6
+ export declare function defineOgImageStatic(options?: OgImageOptions): Promise<void>;
7
+ export declare function defineOgImageCached(options?: OgImageOptions): Promise<void>;
8
+ export declare function defineOgImageWithoutCache(options?: OgImageOptions): Promise<void>;
9
+ /**
10
+ * @deprecated Use `defineOgImageWithoutCache` instead
11
+ */
12
+ export declare function defineOgImageDynamic(options?: OgImageOptions): Promise<void>;
13
+ export declare function defineOgImage(_options?: OgImageOptions): Promise<void>;
@@ -1,42 +1,68 @@
1
- import { useServerHead } from "@vueuse/head";
2
1
  import { withBase } from "ufo";
3
2
  import { useRequestEvent } from "#app";
4
- import { useRouter, useRuntimeConfig } from "#imports";
3
+ import { useNitroOrigin, useRouter, useRuntimeConfig, useServerHead, useSiteConfig } from "#imports";
4
+ import { componentNames } from "#build/og-image-component-names.mjs";
5
5
  export function defineOgImageScreenshot(options = {}) {
6
6
  const router = useRouter();
7
7
  const route = router?.currentRoute?.value?.path || "";
8
- defineOgImage({
8
+ return defineOgImage({
9
9
  alt: `Web page screenshot${route ? ` of ${route}` : ""}.`,
10
10
  provider: "browser",
11
11
  component: null,
12
- static: true,
12
+ cache: true,
13
13
  ...options
14
14
  });
15
15
  }
16
- export function defineOgImageDynamic(options = {}) {
17
- const { satoriProvider, forcePrerender } = useRuntimeConfig()["nuxt-og-image"];
18
- defineOgImage({
19
- provider: satoriProvider ? "satori" : "browser",
20
- static: !!forcePrerender,
16
+ export function defineOgImageStatic(options = {}) {
17
+ return defineOgImageCached(options);
18
+ }
19
+ export function defineOgImageCached(options = {}) {
20
+ const { defaults } = useRuntimeConfig()["nuxt-og-image"];
21
+ if (!defaults.cacheTtl && !options.cacheTtl)
22
+ options.cacheTtl = 60 * 60 * 24 * 7;
23
+ return defineOgImage({
24
+ cache: true,
21
25
  ...options
22
26
  });
23
27
  }
24
- export function defineOgImageStatic(options = {}) {
25
- const { satoriProvider } = useRuntimeConfig()["nuxt-og-image"];
26
- defineOgImage({
27
- provider: satoriProvider ? "satori" : "browser",
28
- static: true,
28
+ export function defineOgImageWithoutCache(options = {}) {
29
+ return defineOgImage({
30
+ cache: false,
31
+ cacheTtl: 0,
29
32
  ...options
30
33
  });
31
34
  }
32
- export function defineOgImage(options = {}) {
35
+ export function defineOgImageDynamic(options = {}) {
36
+ return defineOgImageWithoutCache(options);
37
+ }
38
+ export async function defineOgImage(_options = {}) {
39
+ const options = { ...unref(_options) };
33
40
  if (process.server) {
34
- const { forcePrerender, defaults, siteUrl } = useRuntimeConfig()["nuxt-og-image"];
41
+ if (options.static)
42
+ options.cache = options.cache || options.static;
43
+ if (!options.provider)
44
+ options.provider = "satori";
45
+ const { defaults, runtimeSatori } = useRuntimeConfig()["nuxt-og-image"];
46
+ if (options.provider === "satori" && !runtimeSatori)
47
+ options.provider = "browser";
48
+ if (options.component && componentNames) {
49
+ const originalName = options.component;
50
+ let isValid = componentNames.some((component) => component.pascalName === originalName || component.kebabName === originalName);
51
+ if (!isValid) {
52
+ for (const component of componentNames) {
53
+ if (component.pascalName.endsWith(originalName) || component.kebabName.endsWith(originalName)) {
54
+ options.component = component.pascalName;
55
+ isValid = true;
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ }
35
61
  const router = useRouter();
36
62
  const route = router?.currentRoute?.value?.path || "";
37
63
  const e = useRequestEvent();
38
- if ((forcePrerender || options.static) && options.provider === "satori")
39
- e.res.setHeader("x-nitro-prerender", `${route === "/" ? "" : route}/__og_image__/og.png`);
64
+ const baseUrl = process.env.prerender ? (await useSiteConfig(e)).url : useNitroOrigin(e);
65
+ const src = withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, baseUrl || "");
40
66
  const meta = [
41
67
  {
42
68
  name: "twitter:card",
@@ -44,11 +70,11 @@ export function defineOgImage(options = {}) {
44
70
  },
45
71
  {
46
72
  name: "twitter:image:src",
47
- content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, siteUrl)
73
+ content: src
48
74
  },
49
75
  {
50
76
  property: "og:image",
51
- content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, siteUrl)
77
+ content: src
52
78
  },
53
79
  {
54
80
  property: "og:image:width",
@@ -1,19 +1,21 @@
1
- import { createError, defineEventHandler, setHeader } from "h3";
2
- import { parseURL, withBase, withoutTrailingSlash } from "ufo";
3
- import { fetchOptions, useHostname } from "../utils.mjs";
1
+ import { Buffer } from "node:buffer";
2
+ import { createError, defineEventHandler, sendRedirect, setHeader } from "h3";
3
+ import { joinURL, parseURL, withoutTrailingSlash } from "ufo";
4
+ import { prefixStorage } from "unstorage";
5
+ import { fetchOptions } from "../utils.mjs";
4
6
  import { useProvider } from "#nuxt-og-image/provider";
7
+ import { useNitroOrigin, useRuntimeConfig, useStorage } from "#imports";
5
8
  export default defineEventHandler(async (e) => {
9
+ const { runtimeBrowser, runtimeCacheStorage } = useRuntimeConfig()["nuxt-og-image"];
6
10
  const path = parseURL(e.path).pathname;
7
11
  if (!path.endsWith("__og_image__/og.png"))
8
12
  return;
9
13
  const basePath = withoutTrailingSlash(
10
14
  path.replace("__og_image__/og.png", "")
11
15
  );
12
- setHeader(e, "Content-Type", "image/png");
13
- setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
14
- setHeader(e, "Pragma", "no-cache");
15
- setHeader(e, "Expires", "0");
16
16
  const options = await fetchOptions(e, basePath);
17
+ if (process.env.NODE_ENV === "production" && !process.env.prerender && !runtimeBrowser && options.provider === "browser")
18
+ return sendRedirect(e, joinURL(useNitroOrigin(e), "__nuxt_og_image__/browser-provider-not-supported.png"));
17
19
  const provider = await useProvider(options.provider);
18
20
  if (!provider) {
19
21
  throw createError({
@@ -21,5 +23,49 @@ export default defineEventHandler(async (e) => {
21
23
  statusMessage: `Provider ${options.provider} is missing.`
22
24
  });
23
25
  }
24
- return provider.createPng(withBase(basePath, useHostname(e)), options);
26
+ const useCache = runtimeCacheStorage && !process.dev && options.cacheTtl && options.cacheTtl > 0 && options.cache;
27
+ const baseCacheKey = runtimeCacheStorage === "default" ? "/cache/og-image" : "/og-image";
28
+ const cache = prefixStorage(useStorage(), `${baseCacheKey}/images`);
29
+ let key = options.cacheKey || e.node.req.url.replace("/__og_image__/og.png", "");
30
+ key = key === "/" || !key ? "index" : key;
31
+ let png;
32
+ if (useCache && await cache.hasItem(key)) {
33
+ const { value, expiresAt } = await cache.getItem(key);
34
+ if (expiresAt > Date.now()) {
35
+ setHeader(e, "Cache-Control", "public, max-age=31536000");
36
+ setHeader(e, "Content-Type", "image/png");
37
+ png = Buffer.from(value, "base64");
38
+ } else {
39
+ await cache.removeItem(key);
40
+ }
41
+ }
42
+ if (!png) {
43
+ try {
44
+ png = await provider.createPng(options);
45
+ if (useCache && png) {
46
+ const base64png = Buffer.from(png).toString("base64");
47
+ await cache.setItem(key, { value: base64png, expiresAt: Date.now() + (options.cacheTtl || 0) });
48
+ }
49
+ } catch (err) {
50
+ throw createError({
51
+ statusCode: 500,
52
+ statusMessage: `Failed to create og image: ${err.message}`
53
+ });
54
+ }
55
+ }
56
+ if (png) {
57
+ if (!process.dev && options.cache) {
58
+ setHeader(e, "Cache-Control", "public, max-age=31536000");
59
+ } else {
60
+ setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
61
+ setHeader(e, "Pragma", "no-cache");
62
+ setHeader(e, "Expires", "0");
63
+ }
64
+ setHeader(e, "Content-Type", "image/png");
65
+ return png;
66
+ }
67
+ throw createError({
68
+ statusCode: 500,
69
+ statusMessage: "Failed to create og image, unknown error."
70
+ });
25
71
  });
@@ -1,11 +1,12 @@
1
1
  import { defineEventHandler } from "h3";
2
- import { parseURL, withoutTrailingSlash } from "ufo";
2
+ import { parseURL, withBase, withoutTrailingSlash } from "ufo";
3
3
  import { fetchOptions } from "../utils.mjs";
4
+ import { useRuntimeConfig } from "#imports";
4
5
  export default defineEventHandler(async (e) => {
5
6
  const path = withoutTrailingSlash(parseURL(e.path).pathname);
6
7
  if (!path.endsWith("/__og_image__"))
7
8
  return;
8
- const basePath = path.replace("/__og_image__", "");
9
+ const basePath = withBase(path.replace("/__og_image__", ""), useRuntimeConfig().app.baseURL);
9
10
  const options = await fetchOptions(e, basePath === "" ? "/" : basePath);
10
11
  if (!options)
11
12
  return `The route ${basePath} has not been set up for og:image generation.`;
@@ -22,5 +23,5 @@ export default defineEventHandler(async (e) => {
22
23
  }
23
24
  </style>
24
25
  <title>OG Image Playground</title>
25
- <iframe src="/__nuxt_og_image__/client/?&path=${basePath}"></iframe>`;
26
+ <iframe src="/__nuxt_og_image__/client?&path=${basePath}&base=${useRuntimeConfig().app.baseURL}"></iframe>`;
26
27
  });
@@ -0,0 +1,3 @@
1
+ import type { NitroAppPlugin } from 'nitropack';
2
+ declare const OgImagePrenderNitroPlugin: NitroAppPlugin;
3
+ export default OgImagePrenderNitroPlugin;
@@ -0,0 +1,28 @@
1
+ import { appendHeader } from "h3";
2
+ import { joinURL } from "ufo";
3
+ import { prefixStorage } from "unstorage";
4
+ import { extractOgImageOptions } from "../utils-pure.mjs";
5
+ import { useStorage, useRuntimeConfig } from "#imports";
6
+ const OgImagePrenderNitroPlugin = (nitroApp) => {
7
+ if (!process.env.prerender)
8
+ return;
9
+ const { runtimeCacheStorage } = useRuntimeConfig()["nuxt-og-image"];
10
+ const baseCacheKey = runtimeCacheStorage === "default" ? "/cache/og-image" : "/og-image";
11
+ const cache = prefixStorage(useStorage(), `${baseCacheKey}/options`);
12
+ nitroApp.hooks.hook("render:html", async (ctx, { event }) => {
13
+ const url = event.node.req.url;
14
+ if (url.includes(".") || url.startsWith("/__nuxt_island/"))
15
+ return;
16
+ const options = extractOgImageOptions(ctx.head.join("\n"));
17
+ if (!options)
18
+ return;
19
+ await cache.setItem(url === "/" || !url ? "index" : url, {
20
+ value: JSON.stringify(options),
21
+ expiresAt: Date.now() + 60 * 60 * 1e3
22
+ // 60 minutes to prerender
23
+ });
24
+ if (options.provider === "satori")
25
+ appendHeader(event, "x-nitro-prerender", joinURL(url, "/__og_image__/og.png"));
26
+ });
27
+ };
28
+ export default OgImagePrenderNitroPlugin;
@@ -1 +1 @@
1
- export default function createBrowser(): Promise<import("puppeteer-core").Browser>;
1
+ export default function createBrowser(): Promise<any>;
@@ -1,9 +1,9 @@
1
- import edgeChromium from "chrome-aws-lambda";
1
+ import edgeChromium from "@sparticuz/chrome-aws-lambda";
2
2
  import puppeteer from "puppeteer-core";
3
3
  export default async function createBrowser() {
4
- return puppeteer.launch({
5
- args: edgeChromium.args,
4
+ return await puppeteer.launch({
6
5
  executablePath: await edgeChromium.executablePath,
6
+ args: edgeChromium.args,
7
7
  headless: true
8
8
  });
9
9
  }
@@ -1,14 +1,5 @@
1
1
  import playwrightCore from "playwright-core";
2
2
  export default async function createBrowser() {
3
- try {
4
- const { Launcher } = await import(String("chrome-launcher"));
5
- const chromePath = Launcher.getFirstInstallation();
6
- return await playwrightCore.chromium.launch({
7
- headless: true,
8
- executablePath: chromePath
9
- });
10
- } catch (e) {
11
- }
12
3
  try {
13
4
  return await playwrightCore.chromium.launch({
14
5
  headless: true
@@ -0,0 +1 @@
1
+ export default function createBrowser(): Promise<any>;