nuxt-og-image 2.0.0-beta.8 → 2.0.0

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 (116) hide show
  1. package/README.md +18 -517
  2. package/dist/client/200.html +2 -2
  3. package/dist/client/404.html +2 -2
  4. package/dist/client/_nuxt/IconCSS.48ffa50d.js +1 -0
  5. package/dist/client/_nuxt/IconCSS.b41b9663.css +1 -0
  6. package/dist/client/_nuxt/ImageLoader.51157bac.js +1 -0
  7. package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
  8. package/dist/client/_nuxt/entry.1311cc29.css +1 -0
  9. package/dist/client/_nuxt/entry.74c20cae.js +143 -0
  10. package/dist/client/_nuxt/{error-404.f5dc80fe.js → error-404.102f7671.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.a1082086.js → error-500.f8617a9a.js} +1 -1
  14. package/dist/client/_nuxt/index.212ef337.js +1 -0
  15. package/dist/client/_nuxt/index.ffbea0a9.css +1 -0
  16. package/dist/client/_nuxt/options.fa4f11fe.js +1 -0
  17. package/dist/client/_nuxt/png.eb47fcca.js +1 -0
  18. package/dist/client/_nuxt/{shiki.665f08b3.js → shiki.b89869e1.js} +1 -1
  19. package/dist/client/_nuxt/svg.04901249.js +1 -0
  20. package/dist/client/_nuxt/vnodes.b05f3d68.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 +374 -118
  29. package/dist/runtime/browserUtil.d.ts +1 -0
  30. package/dist/runtime/browserUtil.mjs +7 -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/OgImage/Dynamic.d.ts +8 -0
  34. package/dist/runtime/components/{OgImageDynamic.mjs → OgImage/Dynamic.mjs} +3 -3
  35. package/dist/runtime/components/{OgImageScreenshot.d.ts → OgImage/Screenshot.d.ts} +2 -2
  36. package/dist/runtime/components/{OgImageScreenshot.mjs → OgImage/Screenshot.mjs} +2 -2
  37. package/dist/runtime/components/OgImage/Static.d.ts +8 -0
  38. package/dist/runtime/components/{OgImageStatic.mjs → OgImage/Static.mjs} +3 -3
  39. package/dist/runtime/components/{OgImageStatic.d.ts → OgImage/WithoutCache.d.ts} +2 -2
  40. package/dist/runtime/components/OgImage/WithoutCache.mjs +10 -0
  41. package/dist/runtime/components/OgImage/index.d.ts +5 -0
  42. package/dist/runtime/components/OgImage/index.mjs +10 -0
  43. package/dist/runtime/components/OgImageTemplate/Fallback.vue +156 -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/plugins/prerender.d.ts +3 -0
  50. package/dist/runtime/nitro/plugins/prerender.mjs +28 -0
  51. package/dist/runtime/nitro/providers/browser/lambda.d.ts +1 -1
  52. package/dist/runtime/nitro/providers/browser/lambda.mjs +3 -3
  53. package/dist/runtime/nitro/providers/browser/{node.mjs → playwright.mjs} +0 -9
  54. package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -0
  55. package/dist/runtime/nitro/providers/browser/universal.mjs +33 -0
  56. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +4 -0
  57. package/dist/runtime/nitro/providers/png/resvg-node.mjs +6 -0
  58. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +3 -0
  59. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +11 -0
  60. package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +2 -3
  61. package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
  62. package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +2 -3
  63. package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
  64. package/dist/runtime/nitro/renderers/browser.d.ts +2 -2
  65. package/dist/runtime/nitro/renderers/browser.mjs +14 -12
  66. package/dist/runtime/nitro/renderers/satori/index.d.ts +2 -2
  67. package/dist/runtime/nitro/renderers/satori/index.mjs +27 -32
  68. package/dist/runtime/nitro/renderers/satori/plugins/emojis.d.ts +1 -1
  69. package/dist/runtime/nitro/renderers/satori/plugins/emojis.mjs +19 -6
  70. package/dist/runtime/nitro/renderers/satori/plugins/encoding.d.ts +1 -1
  71. package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +5 -7
  72. package/dist/runtime/nitro/renderers/satori/plugins/flex.d.ts +1 -1
  73. package/dist/runtime/nitro/renderers/satori/plugins/flex.mjs +8 -10
  74. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.d.ts +1 -1
  75. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +45 -13
  76. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.d.ts +1 -1
  77. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.mjs +5 -7
  78. package/dist/runtime/nitro/renderers/satori/utils.d.ts +4 -5
  79. package/dist/runtime/nitro/renderers/satori/utils.mjs +28 -17
  80. package/dist/runtime/nitro/routes/debug.d.ts +8 -0
  81. package/dist/runtime/nitro/routes/debug.mjs +14 -0
  82. package/dist/runtime/nitro/routes/font.mjs +2 -2
  83. package/dist/runtime/nitro/routes/html.mjs +100 -26
  84. package/dist/runtime/nitro/routes/options.d.ts +2 -2
  85. package/dist/runtime/nitro/routes/options.mjs +21 -20
  86. package/dist/runtime/nitro/routes/svg.mjs +2 -2
  87. package/dist/runtime/nitro/routes/vnode.mjs +2 -2
  88. package/dist/runtime/nitro/utils-pure.d.ts +2 -2
  89. package/dist/runtime/nitro/utils-pure.mjs +1 -4
  90. package/dist/runtime/nitro/utils.d.ts +11 -11
  91. package/dist/runtime/nitro/utils.mjs +67 -53
  92. package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
  93. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  94. package/dist/types.d.ts +6 -0
  95. package/package.json +38 -27
  96. package/dist/client/_nuxt/IconCSS.e4ca33fe.js +0 -1
  97. package/dist/client/_nuxt/ImageLoader.b3a6a884.js +0 -1
  98. package/dist/client/_nuxt/entry.0bddba71.js +0 -5
  99. package/dist/client/_nuxt/entry.b37a20ad.css +0 -1
  100. package/dist/client/_nuxt/error-404.1469f10f.css +0 -1
  101. package/dist/client/_nuxt/error-500.92b94fae.css +0 -1
  102. package/dist/client/_nuxt/error-component.a28c293c.js +0 -3
  103. package/dist/client/_nuxt/index.7dc20983.js +0 -1
  104. package/dist/client/_nuxt/options.97e2328c.js +0 -1
  105. package/dist/client/_nuxt/png.50aa137a.js +0 -1
  106. package/dist/client/_nuxt/svg.d633e908.js +0 -1
  107. package/dist/client/_nuxt/vnodes.63ee1c3b.js +0 -1
  108. package/dist/runtime/components/OgImageBasic.island.vue +0 -92
  109. package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
  110. /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
  111. /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
  112. /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
  113. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
  114. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
  115. /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
  116. /package/dist/runtime/{public-assets → public-assets-optional/yoga}/yoga.wasm +0 -0
@@ -1,41 +1,72 @@
1
1
  import { withBase } from "ufo";
2
2
  import { renderSSRHead } from "@unhead/ssr";
3
3
  import { createHeadCore } from "@unhead/vue";
4
- import { defineEventHandler, getQuery, sendRedirect } from "h3";
5
- import { fetchOptions, renderIsland, useHostname } from "../utils.mjs";
6
- import { useRuntimeConfig } from "#imports";
4
+ import { createError, defineEventHandler, getQuery, sendRedirect } from "h3";
5
+ import { hash } from "ohash";
6
+ import twemoji from "twemoji";
7
+ import { defu } from "defu";
8
+ import { fetchOptions } from "../utils.mjs";
9
+ import { useNitroOrigin, useRuntimeConfig } from "#imports";
7
10
  export default defineEventHandler(async (e) => {
8
- const { fonts, defaults } = useRuntimeConfig()["nuxt-og-image"];
11
+ const { fonts, satoriOptions } = useRuntimeConfig()["nuxt-og-image"];
9
12
  const query = getQuery(e);
10
13
  const path = withBase(query.path || "/", useRuntimeConfig().app.baseURL);
11
14
  const scale = query.scale;
12
15
  const mode = query.mode || "light";
13
- let options;
14
- if (query.options)
15
- options = JSON.parse(query.options);
16
- if (!options)
17
- options = await fetchOptions(e, path);
16
+ let queryOptions;
17
+ if (query.options) {
18
+ try {
19
+ queryOptions = JSON.parse(query.options);
20
+ } catch {
21
+ }
22
+ }
23
+ let options = await fetchOptions(e, path);
24
+ if (queryOptions)
25
+ options = defu(queryOptions, options);
18
26
  if (options.provider === "browser" && !options.component) {
19
27
  const pathWithoutBase = path.replace(new RegExp(`^${useRuntimeConfig().app.baseURL}`), "");
20
- return sendRedirect(e, withBase(pathWithoutBase, useHostname(e)));
28
+ return sendRedirect(e, withBase(pathWithoutBase, useNitroOrigin(e)));
29
+ }
30
+ if (!options.component) {
31
+ throw createError({
32
+ statusCode: 500,
33
+ statusMessage: `Nuxt OG Image trying to render an invalid component. Received options ${JSON.stringify(options)}`
34
+ });
21
35
  }
22
- const island = await renderIsland(options);
36
+ const hashId = hash([options.component, options]);
37
+ const island = await $fetch(`/__nuxt_island/${options.component}_${hashId}`, {
38
+ params: {
39
+ props: JSON.stringify(options)
40
+ }
41
+ });
23
42
  const head = createHeadCore();
24
43
  head.push(island.head);
44
+ let defaultFontFamily = "sans-serif";
45
+ const firstFont = fonts[0];
46
+ if (firstFont)
47
+ defaultFontFamily = firstFont.name;
48
+ let html = island.html;
49
+ try {
50
+ html = twemoji.parse(html, {
51
+ folder: "svg",
52
+ ext: ".svg"
53
+ });
54
+ } catch (e2) {
55
+ }
25
56
  head.push({
26
57
  style: [
27
58
  {
28
59
  // default font is the first font family
29
- innerHTML: `body { font-family: '${fonts[0].split(":")[0].replace("+", " ")}', sans-serif; }`
60
+ innerHTML: `body { font-family: '${defaultFontFamily.replace("+", " ")}', sans-serif; }`
30
61
  },
31
- scale ? {
62
+ {
32
63
  innerHTML: `body {
33
- transform: scale(${scale});
64
+ transform: scale(${scale || 1});
34
65
  transform-origin: top left;
35
66
  max-height: 100vh;
36
67
  position: relative;
37
- width: ${defaults.width}px;
38
- height: ${defaults.height}px;
68
+ width: ${options.width}px;
69
+ height: ${options.height}px;
39
70
  overflow: hidden;
40
71
  background-color: ${mode === "dark" ? "#1b1b1b" : "#fff"};
41
72
  }
@@ -44,9 +75,18 @@ img.emoji {
44
75
  width: 1em;
45
76
  margin: 0 .05em 0 .1em;
46
77
  vertical-align: -0.1em;
47
- }
48
- `
49
- } : {}
78
+ }`
79
+ },
80
+ ...fonts.filter((font) => font.path).map((font) => {
81
+ return `
82
+ @font-face {
83
+ font-family: '${font.name}';
84
+ font-style: normal;
85
+ font-weight: ${font.weight};
86
+ src: url('${font.path}') format('truetype');
87
+ }
88
+ `;
89
+ })
50
90
  ],
51
91
  meta: [
52
92
  {
@@ -57,12 +97,12 @@ img.emoji {
57
97
  {
58
98
  src: "https://cdn.tailwindcss.com"
59
99
  },
60
- // @todo merge with users tailwind
61
100
  {
62
101
  innerHTML: `tailwind.config = {
63
102
  corePlugins: {
64
103
  preflight: false,
65
- }
104
+ },
105
+ theme: ${JSON.stringify(satoriOptions?.tailwindConfig?.theme || {})}
66
106
  }`
67
107
  }
68
108
  ],
@@ -73,19 +113,53 @@ img.emoji {
73
113
  rel: "stylesheet"
74
114
  },
75
115
  // have to add each weight as their own stylesheet
76
- ...fonts.map((font) => {
77
- const [name, weight] = font.split(":");
116
+ ...fonts.filter((font) => !font.path).map((font) => {
78
117
  return {
79
- href: `https://fonts.googleapis.com/css2?family=${name}:wght@${weight}&display=swap`,
118
+ href: `https://fonts.googleapis.com/css2?family=${font.name}:wght@${font.weight}&display=swap`,
80
119
  rel: "stylesheet"
81
120
  };
82
121
  })
83
122
  ]
84
123
  });
85
124
  const headChunk = await renderSSRHead(head);
86
- return `<!DOCTYPE html>
125
+ let htmlTemplate = `<!DOCTYPE html>
87
126
  <html ${headChunk.htmlAttrs}>
88
127
  <head>${headChunk.headTags}</head>
89
- <body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div style="position: relative; display: flex; margin: 0 auto; width: 1200px; height: 630px;">${island.html}</div>${headChunk.bodyTags}</body>
128
+ <body ${headChunk.bodyAttrs}>${headChunk.bodyTagsOpen}<div style="position: relative; display: flex; margin: 0 auto; width: ${options.width}px; height: ${options.height}px; overflow: hidden;">${html}</div>${headChunk.bodyTags}</body>
90
129
  </html>`;
130
+ let hasInlineStyles = false;
131
+ const stylesheets = htmlTemplate.match(/<link rel="stylesheet" href=".*?">/g);
132
+ if (stylesheets) {
133
+ for (const stylesheet of stylesheets) {
134
+ if (!stylesheet.includes(`${options.component}.vue`)) {
135
+ htmlTemplate = htmlTemplate.replace(stylesheet, "");
136
+ } else {
137
+ const href = stylesheet.match(/href="(.*?)"/)[1];
138
+ try {
139
+ let css = await (await $fetch(href, {
140
+ baseURL: useNitroOrigin(e)
141
+ })).text();
142
+ if (css.includes("const __vite__css =")) {
143
+ css = css.match(/const __vite__css = "(.*)"/)[1].replace(/\\n/g, "\n");
144
+ }
145
+ htmlTemplate = htmlTemplate.replace(stylesheet, `<style>${css.replace(/\/\/# sourceMappingURL=.*/, "")}</style>`);
146
+ hasInlineStyles = true;
147
+ } catch {
148
+ }
149
+ }
150
+ }
151
+ }
152
+ try {
153
+ if (hasInlineStyles) {
154
+ const inlineCss = await import("inline-css").then((m) => m?.default || m);
155
+ htmlTemplate = inlineCss(htmlTemplate, {
156
+ url: useNitroOrigin(e),
157
+ applyLinkTags: false,
158
+ removeLinkTags: false,
159
+ removeStyleTags: false
160
+ });
161
+ }
162
+ } catch {
163
+ }
164
+ return htmlTemplate;
91
165
  });
@@ -1,3 +1,3 @@
1
- import type { OgImageOptions } from '../../../types';
2
- declare const _default: import("h3").EventHandler<false | OgImageOptions>;
1
+ import type { RuntimeOgImageOptions } from '../../../types';
2
+ declare const _default: import("h3").EventHandler<false | RuntimeOgImageOptions>;
3
3
  export default _default;
@@ -1,19 +1,21 @@
1
- import { createError, defineEventHandler, getHeaders, getQuery } from "h3";
1
+ import { createError, defineEventHandler, getQuery } from "h3";
2
2
  import { withoutBase } from "ufo";
3
- import { extractOgImageOptions, useHostname } from "../utils.mjs";
3
+ import { defu } from "defu";
4
+ import { extractOgImageOptions } from "../utils.mjs";
4
5
  import { getRouteRules } from "#internal/nitro";
5
6
  import { useRuntimeConfig } from "#imports";
6
7
  export default defineEventHandler(async (e) => {
7
8
  const query = getQuery(e);
8
9
  const path = withoutBase(query.path || "/", useRuntimeConfig().app.baseURL);
9
- const fetchOptions = process.dev || process.env.prerender ? {
10
- headers: getHeaders(e)
11
- } : {
12
- baseURL: useHostname(e)
13
- };
14
- const html = await globalThis.$fetch(path, {
15
- ...fetchOptions
16
- });
10
+ let html;
11
+ try {
12
+ html = await globalThis.$fetch(path);
13
+ } catch (err) {
14
+ throw createError({
15
+ statusCode: 500,
16
+ statusMessage: `Failed to read the path ${path} for og-image extraction. ${err.message}.`
17
+ });
18
+ }
17
19
  const extractedPayload = extractOgImageOptions(html);
18
20
  if (!extractedPayload) {
19
21
  throw createError({
@@ -22,20 +24,19 @@ export default defineEventHandler(async (e) => {
22
24
  });
23
25
  }
24
26
  e.node.req.url = path;
27
+ const oldRouteRules = e.context._nitro.routeRules;
25
28
  e.context._nitro.routeRules = void 0;
26
29
  const routeRules = getRouteRules(e)?.ogImage;
30
+ e.context._nitro.routeRules = oldRouteRules;
27
31
  e.node.req.url = e.path;
28
32
  if (routeRules === false)
29
33
  return false;
30
34
  const { defaults } = useRuntimeConfig()["nuxt-og-image"];
31
- return {
32
- path,
33
- ...defaults,
34
- // use route rules
35
- ...routeRules || {},
36
- // use provided data
37
- ...extractedPayload,
38
- // use query data
39
- ...query
40
- };
35
+ return defu(
36
+ extractedPayload,
37
+ routeRules,
38
+ // runtime options
39
+ { path },
40
+ defaults
41
+ );
41
42
  });
@@ -1,6 +1,6 @@
1
1
  import { createError, defineEventHandler, getQuery, setHeader } from "h3";
2
2
  import { withBase } from "ufo";
3
- import { fetchOptions, useHostname } from "../utils.mjs";
3
+ import { fetchOptions } from "../utils.mjs";
4
4
  import { useProvider } from "#nuxt-og-image/provider";
5
5
  import { useRuntimeConfig } from "#imports";
6
6
  export default defineEventHandler(async (e) => {
@@ -15,5 +15,5 @@ export default defineEventHandler(async (e) => {
15
15
  statusMessage: `Provider ${options.provider} is missing.`
16
16
  });
17
17
  }
18
- return provider.createSvg(withBase(path, useHostname(e)), options);
18
+ return provider.createSvg(options);
19
19
  });
@@ -1,6 +1,6 @@
1
1
  import { createError, defineEventHandler, getQuery, setHeader } from "h3";
2
2
  import { withBase } from "ufo";
3
- import { fetchOptions, useHostname } from "../utils.mjs";
3
+ import { fetchOptions } from "../utils.mjs";
4
4
  import { useProvider } from "#nuxt-og-image/provider";
5
5
  import { useRuntimeConfig } from "#imports";
6
6
  export default defineEventHandler(async (e) => {
@@ -15,5 +15,5 @@ export default defineEventHandler(async (e) => {
15
15
  statusMessage: `Provider ${options.provider} is missing.`
16
16
  });
17
17
  }
18
- return provider.createVNode(withBase(path, useHostname(e)), options);
18
+ return provider.createVNode(options);
19
19
  });
@@ -1,3 +1,3 @@
1
+ import type { OgImageOptions } from '../../types';
1
2
  export declare function decodeHtml(html: string): string;
2
- export declare function extractOgImageOptions(html: string): false | Record<string, any>;
3
- export declare function stripOgImageOptions(html: string): string;
3
+ export declare function extractOgImageOptions(html: string): OgImageOptions | false;
@@ -1,6 +1,6 @@
1
1
  export function decodeHtml(html) {
2
2
  return html.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&cent;/g, "\xA2").replace(/&pound;/g, "\xA3").replace(/&yen;/g, "\xA5").replace(/&euro;/g, "\u20AC").replace(/&copy;/g, "\xA9").replace(/&reg;/g, "\xAE").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'").replace(/&#x2F;/g, "/").replace(/&#([0-9]+);/g, (full, int) => {
3
- return String.fromCharCode(parseInt(int));
3
+ return String.fromCharCode(Number.parseInt(int));
4
4
  });
5
5
  }
6
6
  function decodeObjectHtmlEntities(obj) {
@@ -34,6 +34,3 @@ export function extractOgImageOptions(html) {
34
34
  }
35
35
  return false;
36
36
  }
37
- export function stripOgImageOptions(html) {
38
- return html.replace(/<script id="nuxt-og-image-options" type="application\/json">(.*?)<\/script>/, "");
39
- }
@@ -1,17 +1,17 @@
1
1
  /// <reference types="node" />
2
+ import { Buffer } from 'node:buffer';
2
3
  import type { H3Event } from 'h3';
3
- import type { OgImageOptions } from '../../types';
4
- export declare function wasmLoader(key: any, fallback: string, baseUrl: string): {
5
- loaded(): boolean | Promise<any>;
6
- load(): Promise<any>;
4
+ import type { RuntimeOgImageOptions } from '../../types';
5
+ export declare function wasmLoader(asyncModuleLoad: Promise<any> | Buffer | string, fallback: string): {
6
+ load(options: RuntimeOgImageOptions): Promise<any>;
7
7
  };
8
- export declare function fetchOptions(e: H3Event, path: string): Promise<OgImageOptions>;
8
+ export declare function fetchOptions(e: H3Event, path: string): Promise<RuntimeOgImageOptions>;
9
9
  export declare function base64ToArrayBuffer(base64: string): ArrayBuffer;
10
- export declare function renderIsland(payload: OgImageOptions): Promise<{
11
- html: string;
12
- head: any;
13
- }>;
14
- export declare function useHostname(e: H3Event): string;
15
10
  export declare function readPublicAsset(file: string, encoding?: BufferEncoding): Promise<string | Buffer | undefined>;
16
- export declare function readPublicAssetBase64(file: string): Promise<string | undefined>;
11
+ export declare function readPublicAssetBase64(file: string): Promise<{
12
+ src: string;
13
+ width?: number;
14
+ height?: number;
15
+ } | undefined>;
16
+ export declare function toBase64Image(fileName: string, data: string | ArrayBuffer): string;
17
17
  export * from './utils-pure';
@@ -1,75 +1,81 @@
1
1
  import { existsSync, promises as fsp } from "node:fs";
2
- import { getHeaders, getQuery, getRequestHeader } from "h3";
2
+ import { Buffer } from "node:buffer";
3
+ import { getQuery } from "h3";
3
4
  import { join } from "pathe";
4
- import { withBase } from "ufo";
5
- import { useRuntimeConfig } from "#internal/nitro";
6
- export function wasmLoader(key, fallback, baseUrl) {
5
+ import { prefixStorage } from "unstorage";
6
+ import sizeOf from "image-size";
7
+ import { useNitroOrigin, useRuntimeConfig, useStorage } from "#imports";
8
+ export function wasmLoader(asyncModuleLoad, fallback) {
7
9
  let promise;
8
- let loaded = false;
10
+ let wasm;
9
11
  return {
10
- loaded() {
11
- if (loaded)
12
- return true;
12
+ async load(options) {
13
13
  if (typeof promise !== "undefined")
14
14
  return promise;
15
- return false;
16
- },
17
- async load() {
15
+ if (wasm)
16
+ return wasm;
18
17
  promise = promise || new Promise(async (resolve) => {
19
- let wasm;
20
18
  try {
21
- wasm = await key;
19
+ wasm = await asyncModuleLoad;
22
20
  if (typeof wasm === "string")
23
21
  wasm = void 0;
24
22
  } catch (e) {
25
23
  }
26
- if (!wasm)
27
- wasm = await readPublicAsset(fallback);
28
24
  if (!wasm) {
29
- const url = new URL(baseUrl);
30
- wasm = await (await fetch(`${url.origin}${fallback}`)).arrayBuffer();
25
+ wasm = await readPublicAsset(fallback, "base64");
26
+ if (wasm)
27
+ wasm = Buffer.from(wasm, "base64");
28
+ }
29
+ if (!wasm) {
30
+ wasm = await (await globalThis.$fetch(fallback, { baseURL: options.requestOrigin })).arrayBuffer();
31
+ wasm = Buffer.from(wasm);
31
32
  }
32
- loaded = true;
33
33
  resolve(wasm);
34
34
  });
35
35
  return promise;
36
36
  }
37
37
  };
38
38
  }
39
- export function fetchOptions(e, path) {
40
- const fetchOptions2 = process.dev || process.env.prerender ? {
41
- headers: getHeaders(e)
42
- } : {
43
- baseURL: useHostname(e)
39
+ export async function fetchOptions(e, path) {
40
+ const { runtimeCacheStorage } = useRuntimeConfig()["nuxt-og-image"];
41
+ const baseCacheKey = runtimeCacheStorage === "default" ? "/cache/og-image" : "/og-image";
42
+ const cache = runtimeCacheStorage || process.env.prerender ? prefixStorage(useStorage(), `${baseCacheKey}/options`) : false;
43
+ let key = path.replace("/__og_image__/og.png", "");
44
+ key = key === "/" || !key ? "index" : key;
45
+ let options;
46
+ if (!process.dev && cache && await cache.hasItem(key)) {
47
+ const cachedValue = await cache.getItem(key);
48
+ if (cachedValue && cachedValue.value && cachedValue.expiresAt < Date.now())
49
+ options = cachedValue.value;
50
+ else
51
+ await cache.removeItem(key);
52
+ }
53
+ if (!options) {
54
+ options = await globalThis.$fetch("/api/og-image-options", {
55
+ query: {
56
+ path
57
+ },
58
+ responseType: "json"
59
+ });
60
+ if (cache) {
61
+ await cache.setItem(key, {
62
+ value: options,
63
+ // cache for 1 minute or 5 seconds, avoids subsequent internal fetches
64
+ expiresAt: Date.now() + (options.cache ? 60 * 60 * 1e3 : 5 * 1e3)
65
+ });
66
+ }
67
+ }
68
+ return {
69
+ ...options,
70
+ // use query data
71
+ ...getQuery(e),
72
+ requestOrigin: useNitroOrigin(e)
44
73
  };
45
- return globalThis.$fetch("/api/og-image-options", {
46
- query: {
47
- ...getQuery(e),
48
- path
49
- },
50
- ...fetchOptions2
51
- });
52
74
  }
53
75
  export function base64ToArrayBuffer(base64) {
54
76
  const buffer = Buffer.from(base64, "base64");
55
77
  return new Uint8Array(buffer).buffer;
56
78
  }
57
- export function renderIsland(payload) {
58
- return globalThis.$fetch(`/__nuxt_island/${payload.component}`, {
59
- query: { props: JSON.stringify(payload) }
60
- });
61
- }
62
- export function useHostname(e) {
63
- const config = useRuntimeConfig()["nuxt-og-image"];
64
- const base = useRuntimeConfig().app.baseURL;
65
- if (!process.dev && config.siteUrl)
66
- return withBase(base, config.siteUrl);
67
- const host = getRequestHeader(e, "host") || process.env.NITRO_HOST || process.env.HOST || "localhost";
68
- const protocol = getRequestHeader(e, "x-forwarded-proto") || "http";
69
- const useHttp = process.env.NODE_ENV === "development" || host.includes("127.0.0.1") || host.includes("localhost") || protocol === "http";
70
- const port = host.includes(":") ? host.split(":").pop() : process.env.NITRO_PORT || process.env.PORT;
71
- return withBase(base, `http${useHttp ? "" : "s"}://${host.includes(":") ? host.split(":")[0] : host}${port ? `:${port}` : ""}`);
72
- }
73
79
  function r(base, key) {
74
80
  return join(base, key.replace(/:/g, "/"));
75
81
  }
@@ -84,13 +90,21 @@ export async function readPublicAsset(file, encoding) {
84
90
  export async function readPublicAssetBase64(file) {
85
91
  const base64 = await readPublicAsset(file, "base64");
86
92
  if (base64) {
87
- let type = "image/jpeg";
88
- const ext = file.split(".").pop();
89
- if (ext === "svg")
90
- type = "image/svg+xml";
91
- else if (ext === "png")
92
- type = "image/png";
93
- return `data:${type};base64,${base64}`;
93
+ const dimensions = await sizeOf(Buffer.from(base64, "base64"));
94
+ return {
95
+ src: toBase64Image(file, base64),
96
+ ...dimensions
97
+ };
94
98
  }
95
99
  }
100
+ export function toBase64Image(fileName, data) {
101
+ const base64 = typeof data === "string" ? data : Buffer.from(data).toString("base64");
102
+ let type = "image/jpeg";
103
+ const ext = fileName.split(".").pop();
104
+ if (ext === "svg")
105
+ type = "image/svg+xml";
106
+ else if (ext === "png")
107
+ type = "image/png";
108
+ return `data:${type};base64,${base64}`;
109
+ }
96
110
  export * from "./utils-pure.mjs";
package/dist/types.d.ts CHANGED
@@ -7,5 +7,11 @@ declare module '@nuxt/schema' {
7
7
  interface NuxtHooks extends ModuleHooks {}
8
8
  }
9
9
 
10
+ declare module 'nuxt/schema' {
11
+ interface NuxtConfig { ['ogImage']?: Partial<ModuleOptions> }
12
+ interface NuxtOptions { ['ogImage']?: ModuleOptions }
13
+ interface NuxtHooks extends ModuleHooks {}
14
+ }
15
+
10
16
 
11
17
  export { ModuleHooks, ModuleOptions, default } from './module'
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "nuxt-og-image",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.8",
5
- "packageManager": "pnpm@8.1.0",
4
+ "version": "2.0.0",
5
+ "packageManager": "pnpm@8.6.5",
6
+ "description": "Enlightened OG Image generation for Nuxt.",
6
7
  "license": "MIT",
7
8
  "funding": "https://github.com/sponsors/harlan-zw",
8
9
  "homepage": "https://github.com/harlan-zw/nuxt-og-image#readme",
@@ -26,46 +27,56 @@
26
27
  "dist"
27
28
  ],
28
29
  "dependencies": {
29
- "@nuxt/kit": "3.3.2",
30
+ "@nuxt/kit": "^3.6.1",
31
+ "@resvg/resvg-js": "^2.4.1",
32
+ "@resvg/resvg-wasm": "^2.4.1",
30
33
  "@types/fs-extra": "^11.0.1",
31
- "birpc": "^0.2.10",
32
- "chalk": "^5.2.0",
33
- "chrome-launcher": "^0.15.1",
34
+ "birpc": "^0.2.12",
35
+ "chalk": "^5.3.0",
36
+ "chrome-launcher": "^0.15.2",
34
37
  "defu": "^6.1.2",
35
38
  "execa": "^7.1.1",
36
- "fast-glob": "^3.2.12",
39
+ "fast-glob": "^3.3.0",
37
40
  "flatted": "^3.2.7",
38
41
  "fs-extra": "^11.1.1",
42
+ "globby": "^13.2.1",
43
+ "image-size": "^1.0.2",
44
+ "inline-css": "^4.0.2",
39
45
  "launch-editor": "^2.6.0",
40
- "ofetch": "^1.0.1",
41
- "ohash": "^1.0.0",
42
- "pathe": "^1.1.0",
43
- "playwright-core": "^1.32.1",
46
+ "nuxt-site-config": "^0.8.1",
47
+ "nuxt-site-config-kit": "^0.8.1",
48
+ "nypm": "^0.2.2",
49
+ "ofetch": "^1.1.1",
50
+ "ohash": "^1.1.2",
51
+ "pathe": "^1.1.1",
52
+ "playwright-core": "^1.35.1",
44
53
  "radix3": "^1.0.1",
45
- "satori": "0.4.4",
54
+ "satori": "0.10.1",
46
55
  "satori-html": "^0.3.2",
47
- "sirv": "^2.0.2",
48
- "std-env": "^3.3.2",
49
- "svg2png-wasm": "^1.3.4",
56
+ "sirv": "^2.0.3",
57
+ "std-env": "^3.3.3",
58
+ "svg2png-wasm": "^1.4.0",
50
59
  "tinyws": "^0.1.0",
51
60
  "twemoji": "^14.0.2",
52
- "ufo": "^1.1.1",
61
+ "ufo": "^1.1.2",
53
62
  "ws": "^8.13.0",
54
63
  "yoga-wasm-web": "^0.3.3"
55
64
  },
56
65
  "devDependencies": {
57
- "@antfu/eslint-config": "^0.38.2",
58
- "@nuxt/devtools-edge": "0.3.1-28002813.ab4e03d",
59
- "@nuxt/module-builder": "^0.2.1",
60
- "@nuxt/test-utils": "3.3.2",
66
+ "@antfu/eslint-config": "^0.39.6",
67
+ "@nuxt/devtools-edge": "0.6.6-28139075.95f3dd8",
68
+ "@nuxt/module-builder": "^0.4.0",
69
+ "@nuxt/test-utils": "3.6.1",
61
70
  "@nuxtjs/eslint-config-typescript": "^12.0.0",
62
- "@types/ws": "^8.5.4",
63
- "bumpp": "^9.1.0",
64
- "eslint": "8.37.0",
71
+ "@types/ws": "^8.5.5",
72
+ "bumpp": "^9.1.1",
73
+ "eslint": "8.44.0",
65
74
  "jest-image-snapshot": "^6.1.0",
66
- "nuxt": "^3.3.2",
67
- "puppeteer": "^19.8.2",
68
- "vitest": "^0.29.8"
75
+ "nuxt": "^3.6.1",
76
+ "nuxt-icon": "^0.4.2",
77
+ "playwright": "^1.35.1",
78
+ "sass": "^1.63.6",
79
+ "vitest": "^0.32.4"
69
80
  },
70
81
  "scripts": {
71
82
  "build": "pnpm dev:prepare && pnpm build:module && pnpm build:client",
@@ -76,6 +87,6 @@
76
87
  "dev:build": "nuxi build .playground",
77
88
  "dev:prepare": "nuxt-module-build --stub && nuxi prepare .playground",
78
89
  "release": "bumpp package.json --commit --push --tag",
79
- "test": "pnpm lint"
90
+ "test": "vitest"
80
91
  }
81
92
  }
@@ -1 +0,0 @@
1
- import{a as p,b as u,e as l,f as t,o as f,h as m,i as _,u as o,j as d}from"./entry.0bddba71.js";const x=p({__name:"IconCSS",props:{name:{type:String,required:!0},size:{type:String,default:""}},setup(c){const s=c;u(e=>({"09a3f8ca":o(r)}));const n=l();n?.nuxtIcon?.aliases;const i=t(()=>(n?.nuxtIcon?.aliases||{})[s.name]||s.name),r=t(()=>`url('https://api.iconify.design/${i.value.replace(":","/")}.svg')`),a=t(()=>{if(!s.size&&typeof n.nuxtIcon?.size=="boolean"&&!n.nuxtIcon?.size)return;const e=s.size||n.nuxtIcon?.size||"1em";return String(Number(e))===e?`${e}px`:e});return(e,z)=>(f(),m("span",{style:_({width:o(a),height:o(a)})},null,4))}}),g=d(x,[["__scopeId","data-v-c836ce7b"]]);export{g as default};
@@ -1 +0,0 @@
1
- import{a as m,r as c,m as u,w as r,f as d,s as _,o as f,h as g,i as y,j as v}from"./entry.0bddba71.js";const h=m({__name:"ImageLoader",props:{src:String,aspectRatio:Number,description:String},setup(a){const s=a,n=c(),o=c(0);function i(e){const t=n.value,p=Date.now();t.src="",o.value=0,t.style.opacity="0",t.onload=()=>{t.style.opacity="1",o.value=Date.now()-p},t.src=e}u(()=>{r(()=>s.src,e=>{i(e)},{immediate:!0})});const l=d(()=>s.description.replace("%s",o.value.toString()));return r(l,e=>{_.value=e}),(e,t)=>(f(),g("img",{ref_key:"image",ref:n,class:"max-h-full border-1 border-light-500 rounded",style:y({aspectRatio:a.aspectRatio})},null,4))}}),x=v(h,[["__scopeId","data-v-23ec6856"]]);export{x as _};