nuxt-og-image 2.0.0-beta.7 → 2.0.0-beta.70

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 (117) hide show
  1. package/README.md +312 -97
  2. package/dist/client/200.html +2 -2
  3. package/dist/client/404.html +2 -2
  4. package/dist/client/_nuxt/IconCSS.78433ecd.js +1 -0
  5. package/dist/client/_nuxt/IconCSS.b41b9663.css +1 -0
  6. package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
  7. package/dist/client/_nuxt/ImageLoader.b930015f.js +1 -0
  8. package/dist/client/_nuxt/entry.1311cc29.css +1 -0
  9. package/dist/client/_nuxt/entry.212e85d9.js +143 -0
  10. package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.0875e3f6.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.f06aa28f.js} +1 -1
  14. package/dist/client/_nuxt/index.9dc826bf.js +1 -0
  15. package/dist/client/_nuxt/index.ffbea0a9.css +1 -0
  16. package/dist/client/_nuxt/options.f2b6402e.js +1 -0
  17. package/dist/client/_nuxt/png.04e14eef.js +1 -0
  18. package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.a8b6534b.js} +1 -1
  19. package/dist/client/_nuxt/svg.9ac05c0d.js +1 -0
  20. package/dist/client/_nuxt/vnodes.c25d5717.js +1 -0
  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 +394 -134
  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 +170 -0
  44. package/dist/runtime/composables/defineOgImage.d.ts +12 -4
  45. package/dist/runtime/composables/defineOgImage.mjs +31 -49
  46. package/dist/runtime/composables/util.d.ts +2 -0
  47. package/dist/runtime/composables/util.mjs +26 -0
  48. package/dist/runtime/nitro/middleware/og.png.mjs +54 -8
  49. package/dist/runtime/nitro/middleware/playground.mjs +4 -3
  50. package/dist/runtime/nitro/plugins/prerender.d.ts +3 -0
  51. package/dist/runtime/nitro/plugins/prerender.mjs +28 -0
  52. package/dist/runtime/nitro/providers/browser/lambda.d.ts +1 -1
  53. package/dist/runtime/nitro/providers/browser/lambda.mjs +3 -3
  54. package/dist/runtime/nitro/providers/browser/{node.mjs → playwright.mjs} +0 -9
  55. package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -0
  56. package/dist/runtime/nitro/providers/browser/universal.mjs +33 -0
  57. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +4 -0
  58. package/dist/runtime/nitro/providers/png/resvg-node.mjs +6 -0
  59. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +3 -0
  60. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +11 -0
  61. package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +2 -3
  62. package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
  63. package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +2 -3
  64. package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
  65. package/dist/runtime/nitro/renderers/browser.d.ts +2 -2
  66. package/dist/runtime/nitro/renderers/browser.mjs +14 -10
  67. package/dist/runtime/nitro/renderers/satori/index.d.ts +2 -2
  68. package/dist/runtime/nitro/renderers/satori/index.mjs +27 -32
  69. package/dist/runtime/nitro/renderers/satori/plugins/emojis.d.ts +1 -1
  70. package/dist/runtime/nitro/renderers/satori/plugins/emojis.mjs +19 -6
  71. package/dist/runtime/nitro/renderers/satori/plugins/encoding.d.ts +1 -1
  72. package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +5 -7
  73. package/dist/runtime/nitro/renderers/satori/plugins/flex.d.ts +1 -1
  74. package/dist/runtime/nitro/renderers/satori/plugins/flex.mjs +8 -10
  75. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.d.ts +1 -1
  76. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +45 -13
  77. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.d.ts +1 -1
  78. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.mjs +5 -7
  79. package/dist/runtime/nitro/renderers/satori/utils.d.ts +4 -5
  80. package/dist/runtime/nitro/renderers/satori/utils.mjs +28 -17
  81. package/dist/runtime/nitro/routes/debug.d.ts +8 -0
  82. package/dist/runtime/nitro/routes/debug.mjs +14 -0
  83. package/dist/runtime/nitro/routes/font.mjs +2 -2
  84. package/dist/runtime/nitro/routes/html.mjs +102 -27
  85. package/dist/runtime/nitro/routes/options.d.ts +2 -2
  86. package/dist/runtime/nitro/routes/options.mjs +25 -22
  87. package/dist/runtime/nitro/routes/svg.mjs +5 -3
  88. package/dist/runtime/nitro/routes/vnode.mjs +5 -3
  89. package/dist/runtime/nitro/utils-pure.d.ts +2 -2
  90. package/dist/runtime/nitro/utils-pure.mjs +1 -4
  91. package/dist/runtime/nitro/utils.d.ts +11 -11
  92. package/dist/runtime/nitro/utils.mjs +69 -54
  93. package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
  94. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  95. package/dist/types.d.ts +6 -0
  96. package/package.json +39 -27
  97. package/dist/client/_nuxt/IconCSS.a041aca0.js +0 -1
  98. package/dist/client/_nuxt/ImageLoader.9bf39d71.js +0 -1
  99. package/dist/client/_nuxt/entry.74018bda.js +0 -5
  100. package/dist/client/_nuxt/entry.7a8c1ab2.css +0 -1
  101. package/dist/client/_nuxt/error-404.1469f10f.css +0 -1
  102. package/dist/client/_nuxt/error-500.92b94fae.css +0 -1
  103. package/dist/client/_nuxt/error-component.cf7543e5.js +0 -3
  104. package/dist/client/_nuxt/index.3f356409.js +0 -1
  105. package/dist/client/_nuxt/options.56a3e5f9.js +0 -1
  106. package/dist/client/_nuxt/png.37f3e77b.js +0 -1
  107. package/dist/client/_nuxt/svg.186c6bd1.js +0 -1
  108. package/dist/client/_nuxt/vnodes.a799f183.js +0 -1
  109. package/dist/runtime/components/OgImageBasic.island.vue +0 -92
  110. package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
  111. /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
  112. /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
  113. /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
  114. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
  115. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
  116. /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
  117. /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,170 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useSiteConfig } from '#imports'
4
+
5
+ // inherited attrs can mess up the satori parser
6
+ defineOptions({
7
+ inheritAttrs: false,
8
+ })
9
+
10
+ const props = defineProps({
11
+ path: String,
12
+ title: {
13
+ type: String,
14
+ default: 'Og Image Template',
15
+ },
16
+ description: {
17
+ type: String,
18
+ default: 'Set a description to change me.',
19
+ },
20
+ color: {
21
+ type: String,
22
+ },
23
+ padding: {
24
+ type: String,
25
+ default: '0 100px',
26
+ },
27
+ titleFontSize: {
28
+ type: String,
29
+ default: '75px',
30
+ },
31
+ descriptionFontSize: {
32
+ type: String,
33
+ default: '35px',
34
+ },
35
+ icon: {
36
+ type: [String, Boolean],
37
+ default: false,
38
+ },
39
+ siteName: {
40
+ type: String,
41
+ required: false,
42
+ },
43
+ siteLogo: {
44
+ type: String,
45
+ required: false,
46
+ },
47
+ })
48
+
49
+ const backgroundAttrs = computed(() => {
50
+ // we want to make a
51
+ // const isBackgroundTw = props.background?.startsWith('bg-')
52
+ return {
53
+ style: {
54
+ display: 'flex',
55
+ position: 'absolute',
56
+ width: '100%',
57
+ height: '100%',
58
+ background: 'rgba(5, 5, 5,1)',
59
+ },
60
+ }
61
+ })
62
+
63
+ const backgroundFlareAttrs = computed(() => {
64
+ // we want to make a
65
+ // const isBackgroundTw = props.background?.startsWith('bg-')
66
+ return {
67
+ style: {
68
+ display: 'flex',
69
+ position: 'absolute',
70
+ right: '-100%',
71
+ top: '10%',
72
+ width: '200%',
73
+ height: '200%',
74
+ backgroundImage: 'radial-gradient(circle, rgba(0,220,130, 0.5) 0%, rgba(5, 5, 5,0.3) 50%, rgba(5, 5, 5,0) 70%)',
75
+ },
76
+ }
77
+ })
78
+
79
+ const containerAttrs = computed(() => {
80
+ const isColorTw = props.color?.startsWith('text-')
81
+
82
+ const classes = [
83
+ 'w-full',
84
+ 'h-full',
85
+ 'flex',
86
+ 'text-gray-100',
87
+ 'relative',
88
+ 'items-center',
89
+ 'justify-center',
90
+ ]
91
+ const styles: Record<string, any> = {
92
+ padding: props.padding,
93
+ }
94
+ if (isColorTw)
95
+ classes.push(props.color)
96
+ else
97
+ styles.color = props.color
98
+ return { class: classes, style: styles }
99
+ })
100
+
101
+ const titleAttrs = computed(() => {
102
+ const classes = []
103
+ const styles = {
104
+ fontWeight: 'bold',
105
+ marginBottom: '50px',
106
+ fontSize: props.titleFontSize,
107
+ }
108
+ return { class: classes, style: styles }
109
+ })
110
+
111
+ const descriptionAttrs = computed(() => {
112
+ const classes = []
113
+ const styles = {
114
+ fontSize: props.descriptionFontSize,
115
+ lineHeight: `${props.descriptionFontSize.replace('px', '') * 1.5}px`,
116
+ opacity: '0.8',
117
+ }
118
+ return { class: classes, style: styles }
119
+ })
120
+
121
+ const siteConfig = useSiteConfig()
122
+ const siteName = computed(() => {
123
+ return props.siteName || siteConfig.name
124
+ })
125
+ const siteLogo = computed(() => {
126
+ return props.siteLogo || siteConfig.logo
127
+ })
128
+
129
+ const MaybeIconComponent = resolveComponent('Icon')
130
+ </script>
131
+
132
+ <template>
133
+ <div v-bind="backgroundAttrs" />
134
+ <div v-bind="backgroundFlareAttrs" />
135
+ <div v-bind="containerAttrs">
136
+ <div class="flex flex-row justify-between items-center" style="margin-bottom: 100px;">
137
+ <div class="flex flex-col w-full" :style="icon ? { width: '65%' } : {}">
138
+ <div v-bind="titleAttrs">
139
+ {{ title || 'Null Title' }}
140
+ </div>
141
+ <div v-if="description" v-bind="descriptionAttrs">
142
+ {{ description }}
143
+ </div>
144
+ </div>
145
+ <div v-if="typeof icon === 'string' && typeof MaybeIconComponent !== 'string'" style="width: 30%;" class="flex justify-end">
146
+ <MaybeIconComponent :name="icon" size="250px" style="margin: 0 auto; margin-left: 100px; opacity: 0.9;" />
147
+ </div>
148
+ </div>
149
+ <div class="flex flex-row absolute bottom-10 text-left items-start">
150
+ <img v-if="siteLogo" :src="siteLogo" height="30">
151
+ <div v-else-if="siteName" style="font-size: 25px;" class="font-bold">
152
+ {{ siteName }}
153
+ </div>
154
+ <div v-else class="flex flex-row items-center space-x-5">
155
+ <div class="flex flex-row items-end gap-1.5 font-bold text-2xl text-white font-title">
156
+ <svg height="25" width="25" class="d-inline-block mb-2px mr-2" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
157
+ <path fill="#00DC82" d="M62.3,-53.9C74.4,-34.5,73.5,-9,67.1,13.8C60.6,36.5,48.7,56.5,30.7,66.1C12.7,75.7,-11.4,74.8,-31.6,65.2C-51.8,55.7,-67.9,37.4,-73.8,15.7C-79.6,-6,-75.1,-31.2,-61.1,-51C-47.1,-70.9,-23.6,-85.4,0.8,-86C25.1,-86.7,50.2,-73.4,62.3,-53.9Z" transform="translate(100 100)" />
158
+ </svg>
159
+ <span>Nuxt</span><span class="sm:text-primary-500 dark:sm:text-primary-400 ml-2">SEO</span>
160
+ </div>
161
+ <div class="opacity-75 flex flex-row items-end gap-1.5 font-bold text-2xl text-white font-title px-5">
162
+
163
+ </div>
164
+ <div class="flex flex-row items-end gap-1.5 font-bold text-2xl text-white font-title">
165
+ OG Image
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </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,70 +1,52 @@
1
- import { useServerHead } from "@vueuse/head";
2
- import { withBase } from "ufo";
3
- import { useRequestEvent } from "#app";
4
- import { useRouter, useRuntimeConfig } from "#imports";
1
+ import { joinURL } from "ufo";
2
+ import { normaliseOgImageOptions } from "./util.mjs";
3
+ import { useRouter, useRuntimeConfig, useServerHead, withSiteUrl } from "#imports";
5
4
  export function defineOgImageScreenshot(options = {}) {
6
5
  const router = useRouter();
7
6
  const route = router?.currentRoute?.value?.path || "";
8
- defineOgImage({
7
+ return defineOgImage({
9
8
  alt: `Web page screenshot${route ? ` of ${route}` : ""}.`,
10
9
  provider: "browser",
11
10
  component: null,
12
- static: true,
11
+ cache: true,
13
12
  ...options
14
13
  });
15
14
  }
16
- export function defineOgImageDynamic(options = {}) {
17
- const { satoriProvider, forcePrerender } = useRuntimeConfig()["nuxt-og-image"];
18
- defineOgImage({
19
- provider: satoriProvider ? "satori" : "browser",
20
- static: !!forcePrerender,
15
+ export function defineOgImageStatic(options = {}) {
16
+ return defineOgImageCached(options);
17
+ }
18
+ export function defineOgImageCached(options = {}) {
19
+ const { defaults } = useRuntimeConfig()["nuxt-og-image"];
20
+ if (!defaults.cacheTtl && !options.cacheTtl)
21
+ options.cacheTtl = 60 * 60 * 24 * 7;
22
+ return defineOgImage({
23
+ cache: true,
21
24
  ...options
22
25
  });
23
26
  }
24
- export function defineOgImageStatic(options = {}) {
25
- const { satoriProvider } = useRuntimeConfig()["nuxt-og-image"];
26
- defineOgImage({
27
- provider: satoriProvider ? "satori" : "browser",
28
- static: true,
27
+ export function defineOgImageWithoutCache(options = {}) {
28
+ return defineOgImage({
29
+ cache: false,
30
+ cacheTtl: 0,
29
31
  ...options
30
32
  });
31
33
  }
32
- export function defineOgImage(options = {}) {
34
+ export function defineOgImageDynamic(options = {}) {
35
+ return defineOgImageWithoutCache(options);
36
+ }
37
+ export async function defineOgImage(_options = {}) {
33
38
  if (process.server) {
34
- const { forcePrerender, defaults, siteUrl } = useRuntimeConfig()["nuxt-og-image"];
35
- const router = useRouter();
36
- const route = router?.currentRoute?.value?.path || "";
37
- const e = useRequestEvent();
38
- if ((forcePrerender || options.static) && options.provider === "satori")
39
- e.res.setHeader("x-nitro-prerender", `${route === "/" ? "" : route}/__og_image__/og.png`);
39
+ const options = normaliseOgImageOptions(_options);
40
+ const src = withSiteUrl(joinURL(useRouter().currentRoute.value.path || "", "/__og_image__/og.png"));
40
41
  const meta = [
41
- {
42
- name: "twitter:card",
43
- content: "summary_large_image"
44
- },
45
- {
46
- name: "twitter:image:src",
47
- content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, siteUrl)
48
- },
49
- {
50
- property: "og:image",
51
- content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, siteUrl)
52
- },
53
- {
54
- property: "og:image:width",
55
- content: options.width || defaults.width
56
- },
57
- {
58
- property: "og:image:height",
59
- content: options.height || defaults.height
60
- }
42
+ { name: "twitter:card", content: "summary_large_image" },
43
+ { name: "twitter:image:src", content: src },
44
+ { property: "og:image", content: src },
45
+ { property: "og:image:width", content: options.width },
46
+ { property: "og:image:height", content: options.height }
61
47
  ];
62
- if (options.alt) {
63
- meta.push({
64
- property: "og:image:alt",
65
- content: options.alt
66
- });
67
- }
48
+ if (options.alt)
49
+ meta.push({ property: "og:image:alt", content: options.alt });
68
50
  useServerHead({
69
51
  meta,
70
52
  script: [
@@ -0,0 +1,2 @@
1
+ import type { OgImageOptions } from '../../types';
2
+ export declare function normaliseOgImageOptions(_options: OgImageOptions): any;
@@ -0,0 +1,26 @@
1
+ import { unref, useRuntimeConfig } from "#imports";
2
+ import { componentNames } from "#build/og-image-component-names.mjs";
3
+ export function normaliseOgImageOptions(_options) {
4
+ const options = { ...unref(_options) };
5
+ if (options.static)
6
+ 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";
12
+ if (options.component && componentNames) {
13
+ const originalName = options.component;
14
+ let isValid = componentNames.some((component) => component.pascalName === originalName || component.kebabName === originalName);
15
+ if (!isValid) {
16
+ for (const component of componentNames) {
17
+ if (component.pascalName.endsWith(originalName) || component.kebabName.endsWith(originalName)) {
18
+ options.component = component.pascalName;
19
+ isValid = true;
20
+ break;
21
+ }
22
+ }
23
+ }
24
+ }
25
+ return options;
26
+ }
@@ -1,19 +1,22 @@
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 { hash } from "ohash";
6
+ import { fetchOptions } from "../utils.mjs";
4
7
  import { useProvider } from "#nuxt-og-image/provider";
8
+ import { useNitroOrigin, useRuntimeConfig, useStorage } from "#imports";
5
9
  export default defineEventHandler(async (e) => {
10
+ const { runtimeBrowser, runtimeCacheStorage } = useRuntimeConfig()["nuxt-og-image"];
6
11
  const path = parseURL(e.path).pathname;
7
12
  if (!path.endsWith("__og_image__/og.png"))
8
13
  return;
9
14
  const basePath = withoutTrailingSlash(
10
15
  path.replace("__og_image__/og.png", "")
11
16
  );
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
17
  const options = await fetchOptions(e, basePath);
18
+ if (process.env.NODE_ENV === "production" && !process.env.prerender && !runtimeBrowser && options.provider === "browser")
19
+ return sendRedirect(e, joinURL(useNitroOrigin(e), "__nuxt_og_image__/browser-provider-not-supported.png"));
17
20
  const provider = await useProvider(options.provider);
18
21
  if (!provider) {
19
22
  throw createError({
@@ -21,5 +24,48 @@ export default defineEventHandler(async (e) => {
21
24
  statusMessage: `Provider ${options.provider} is missing.`
22
25
  });
23
26
  }
24
- return provider.createPng(withBase(basePath, useHostname(e)), options);
27
+ const useCache = runtimeCacheStorage && !process.dev && options.cacheTtl && options.cacheTtl > 0 && options.cache;
28
+ const baseCacheKey = runtimeCacheStorage === "default" ? "/cache/og-image" : "/og-image";
29
+ const cache = prefixStorage(useStorage(), `${baseCacheKey}/images`);
30
+ const key = [options.path === "/" || !options.path ? "index" : options.path, hash(options)].join(":");
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 { useRuntimeConfig, useStorage } 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
  }