nuxt-og-image 2.0.0-beta.1 → 2.0.0-beta.10

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 (49) hide show
  1. package/README.md +90 -35
  2. package/dist/client/200.html +2 -2
  3. package/dist/client/404.html +2 -2
  4. package/dist/client/_nuxt/{IconCSS.60a77d69.js → IconCSS.ef0613e7.js} +1 -1
  5. package/dist/client/_nuxt/ImageLoader.a4418cab.js +1 -0
  6. package/dist/client/_nuxt/entry.09f25aaf.css +1 -0
  7. package/dist/client/_nuxt/entry.fc9150b0.js +5 -0
  8. package/dist/client/_nuxt/{error-404.8c1e055e.js → error-404.02537f9e.js} +1 -1
  9. package/dist/client/_nuxt/{error-500.bb6daf96.js → error-500.a60bf0b5.js} +1 -1
  10. package/dist/client/_nuxt/{error-component.62480d86.js → error-component.8148b615.js} +2 -2
  11. package/dist/client/_nuxt/index.80f38ec7.js +1 -0
  12. package/dist/client/_nuxt/options.cc3fd02b.js +1 -0
  13. package/dist/client/_nuxt/png.62758167.js +1 -0
  14. package/dist/client/_nuxt/shiki.aace8ca2.js +7 -0
  15. package/dist/client/_nuxt/{svg.b9393fe1.js → svg.853cdaad.js} +1 -1
  16. package/dist/client/_nuxt/vnodes.69b24963.js +1 -0
  17. package/dist/client/index.html +2 -2
  18. package/dist/client/options/index.html +2 -2
  19. package/dist/client/png/index.html +2 -2
  20. package/dist/client/svg/index.html +2 -2
  21. package/dist/client/vnodes/index.html +2 -2
  22. package/dist/module.d.ts +0 -1
  23. package/dist/module.json +1 -1
  24. package/dist/module.mjs +54 -52
  25. package/dist/runtime/browserUtil.mjs +7 -4
  26. package/dist/runtime/composables/defineOgImage.mjs +6 -4
  27. package/dist/runtime/nitro/middleware/og.png.mjs +16 -2
  28. package/dist/runtime/nitro/middleware/playground.mjs +4 -3
  29. package/dist/runtime/nitro/providers/browser/node.mjs +10 -8
  30. package/dist/runtime/nitro/renderers/browser.mjs +6 -1
  31. package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +2 -1
  32. package/dist/runtime/nitro/routes/html.mjs +11 -8
  33. package/dist/runtime/nitro/routes/options.mjs +6 -3
  34. package/dist/runtime/nitro/routes/svg.mjs +3 -1
  35. package/dist/runtime/nitro/routes/vnode.mjs +3 -1
  36. package/dist/runtime/nitro/util-hostname.d.ts +2 -0
  37. package/dist/runtime/nitro/util-hostname.mjs +13 -0
  38. package/dist/runtime/nitro/utils-pure.d.ts +3 -2
  39. package/dist/runtime/nitro/utils-pure.mjs +16 -13
  40. package/dist/runtime/nitro/utils.d.ts +1 -1
  41. package/dist/runtime/nitro/utils.mjs +6 -15
  42. package/package.json +14 -13
  43. package/dist/client/_nuxt/ImageLoader.dba956e5.js +0 -1
  44. package/dist/client/_nuxt/entry.3ad25beb.js +0 -5
  45. package/dist/client/_nuxt/entry.99fbf2af.css +0 -1
  46. package/dist/client/_nuxt/index.5554552f.js +0 -1
  47. package/dist/client/_nuxt/options.a3f35ef0.js +0 -1
  48. package/dist/client/_nuxt/png.e6fefdc5.js +0 -1
  49. package/dist/client/_nuxt/vnodes.fe6b127e.js +0 -1
package/dist/module.mjs CHANGED
@@ -4,7 +4,7 @@ import { execa } from 'execa';
4
4
  import chalk from 'chalk';
5
5
  import defu from 'defu';
6
6
  import { toRouteMatcher, createRouter } from 'radix3';
7
- import { joinURL } from 'ufo';
7
+ import { withBase, joinURL } from 'ufo';
8
8
  import { resolve, relative } from 'pathe';
9
9
  import { tinyws } from 'tinyws';
10
10
  import sirv from 'sirv';
@@ -16,14 +16,16 @@ import { createBirpcGroup } from 'birpc';
16
16
  import { stringify, parse } from 'flatted';
17
17
 
18
18
  async function createBrowser() {
19
- try {
20
- const { Launcher } = await import(String("chrome-launcher"));
21
- const chromePath = Launcher.getFirstInstallation();
22
- return await playwrightCore.chromium.launch({
23
- headless: true,
24
- executablePath: chromePath
25
- });
26
- } catch (e) {
19
+ if (process.dev || process.env.prerender) {
20
+ try {
21
+ const { Launcher } = await import(String("chrome-launcher"));
22
+ const chromePath = Launcher.getFirstInstallation();
23
+ return await playwrightCore.chromium.launch({
24
+ headless: true,
25
+ executablePath: chromePath
26
+ });
27
+ } catch (e) {
28
+ }
27
29
  }
28
30
  try {
29
31
  return await playwrightCore.chromium.launch({
@@ -54,9 +56,9 @@ async function screenshot(browser, options) {
54
56
  width: options.width || 1200,
55
57
  height: options.height || 630
56
58
  });
57
- const isHtml = options.path.startsWith("html:") || options.html;
59
+ const isHtml = options.html || options.path?.startsWith("html:");
58
60
  if (isHtml) {
59
- const html = options.html || options.path.substring(5);
61
+ const html = options.html || options.path?.substring(5);
60
62
  await page.evaluate((html2) => {
61
63
  document.open("text/html");
62
64
  document.write(html2);
@@ -69,6 +71,9 @@ async function screenshot(browser, options) {
69
71
  waitUntil: "networkidle"
70
72
  });
71
73
  }
74
+ const screenshotOptions = {
75
+ timeout: 1e4
76
+ };
72
77
  if (options.delay)
73
78
  await page.waitForTimeout(options.delay);
74
79
  if (options.mask) {
@@ -78,8 +83,8 @@ async function screenshot(browser, options) {
78
83
  }, options.mask);
79
84
  }
80
85
  if (options.selector)
81
- return await page.locator(options.selector).screenshot();
82
- return await page.screenshot();
86
+ return await page.locator(options.selector).screenshot(screenshotOptions);
87
+ return await page.screenshot(screenshotOptions);
83
88
  }
84
89
 
85
90
  function setupPlaygroundRPC(nuxt, config) {
@@ -113,7 +118,7 @@ function setupPlaygroundRPC(nuxt, config) {
113
118
  const birpc = createBirpcGroup(serverFunctions, []);
114
119
  nuxt.hook("builder:watch", (e, path) => {
115
120
  if (e === "change")
116
- birpc.boardcast.refresh.asEvent(path);
121
+ birpc.broadcast.refresh.asEvent(path);
117
122
  });
118
123
  const middleware = async (req, res) => {
119
124
  if (req.ws) {
@@ -164,11 +169,15 @@ function getBodyJson(req) {
164
169
  });
165
170
  }
166
171
 
167
- function decodeHtmlEntities(obj) {
172
+ function decodeHtml(html) {
173
+ 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) => {
174
+ return String.fromCharCode(parseInt(int));
175
+ });
176
+ }
177
+ function decodeObjectHtmlEntities(obj) {
168
178
  Object.entries(obj).forEach(([key, value]) => {
169
- if (typeof value === "string") {
170
- obj[key] = value.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#x2F;/g, "/");
171
- }
179
+ if (typeof value === "string")
180
+ obj[key] = decodeHtml(value);
172
181
  });
173
182
  return obj;
174
183
  }
@@ -185,18 +194,17 @@ function extractOgImageOptions(html) {
185
194
  console.warn("Failed to parse #nuxt-og-image-options", e, options);
186
195
  }
187
196
  if (options) {
188
- const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
189
- if (description)
190
- options.description = description;
191
- else
192
- options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
193
- return decodeHtmlEntities(options);
197
+ if (!options.description) {
198
+ const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
199
+ if (description)
200
+ options.description = description;
201
+ else
202
+ options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
203
+ }
204
+ return decodeObjectHtmlEntities(options);
194
205
  }
195
206
  return false;
196
207
  }
197
- function stripOgImageOptions(html) {
198
- return html.replace(/<script id="nuxt-og-image-options" type="application\/json">(.*?)<\/script>/, "");
199
- }
200
208
 
201
209
  const PATH = "/__nuxt_og_image__";
202
210
  const PATH_ENTRY = `${PATH}/entry`;
@@ -231,7 +239,6 @@ const module = defineNuxtModule({
231
239
  fonts: [],
232
240
  satoriOptions: {},
233
241
  experimentalInlineWasm: process.env.NITRO_PRESET === "netlify-edge" || nuxt.options.nitro.preset === "netlify-edge" || false,
234
- experimentalRuntimeBrowser: false,
235
242
  playground: process.env.NODE_ENV === "development" || nuxt.options.dev
236
243
  };
237
244
  },
@@ -337,17 +344,13 @@ export {}
337
344
  nitroConfig.externals = defu(nitroConfig.externals || {}, {
338
345
  inline: [runtimeDir]
339
346
  });
340
- if (config.experimentalRuntimeBrowser) {
341
- nitroConfig.alias = nitroConfig.alias || {};
342
- nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
343
- nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
344
- nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
345
- }
346
347
  nitroConfig.publicAssets = nitroConfig.publicAssets || [];
347
348
  nitroConfig.publicAssets.push({ dir: moduleAssetDir, maxAge: 31536e3 });
348
349
  const providerPath = `${runtimeDir}/nitro/providers`;
350
+ const nitroPreset = nuxt.options.nitro.preset || process.env.NITRO_PRESET;
351
+ const isNodeNitroServer = !nitroPreset || nitroPreset === "node";
349
352
  if (config.browserProvider) {
350
- nitroConfig.virtual["#nuxt-og-image/browser"] = nuxt.options.dev || config.experimentalRuntimeBrowser ? `
353
+ nitroConfig.virtual["#nuxt-og-image/browser"] = nuxt.options.dev || process.env.prerender || isNodeNitroServer ? `
351
354
  import node from '${providerPath}/browser/node'
352
355
 
353
356
  export default async function() {
@@ -421,7 +424,6 @@ export async function useProvider(provider) {
421
424
  if (!html)
422
425
  return;
423
426
  const extractedOptions = extractOgImageOptions(html);
424
- ctx.contents = stripOgImageOptions(html);
425
427
  const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
426
428
  if (!extractedOptions || routeRules.ogImage === false)
427
429
  return;
@@ -440,21 +442,6 @@ export async function useProvider(provider) {
440
442
  await nuxt.callHook("og-image:prerenderScreenshots", screenshotQueue);
441
443
  if (screenshotQueue.length === 0)
442
444
  return;
443
- for (const entry of screenshotQueue) {
444
- if (entry.route && Object.keys(entry).length === 1) {
445
- const html = await $fetch(entry.route);
446
- const extractedOptions = extractOgImageOptions(html);
447
- const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
448
- Object.assign(entry, {
449
- // @ts-expect-error runtime
450
- path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
451
- ...extractedOptions,
452
- ...routeRules.ogImage || {}
453
- });
454
- }
455
- if (entry.component)
456
- entry.html = await $fetch(entry.path);
457
- }
458
445
  nitro.logger.info("Ensuring chromium install for og:image generation...");
459
446
  const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
460
447
  stdio: "inherit"
@@ -481,6 +468,21 @@ export async function useProvider(provider) {
481
468
  browser = await createBrowser();
482
469
  if (browser) {
483
470
  nitro.logger.info(`Prerendering ${screenshotQueue.length} og:image screenshots...`);
471
+ for (const entry of screenshotQueue) {
472
+ if (entry.route && Object.keys(entry).length === 1) {
473
+ const html = await $fetch(entry.route, { baseURL: withBase(nuxt.options.app.baseURL, host) });
474
+ const extractedOptions = extractOgImageOptions(html);
475
+ const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
476
+ Object.assign(entry, {
477
+ // @ts-expect-error runtime
478
+ path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
479
+ ...extractedOptions,
480
+ ...routeRules.ogImage || {}
481
+ });
482
+ }
483
+ if (entry.component)
484
+ entry.html = await globalThis.$fetch(entry.path);
485
+ }
484
486
  for (const k in screenshotQueue) {
485
487
  const entry = screenshotQueue[k];
486
488
  const start = Date.now();
@@ -518,7 +520,7 @@ export async function useProvider(provider) {
518
520
  }
519
521
  screenshotQueue = [];
520
522
  };
521
- if (!nuxt.options._prepare) {
523
+ if (nuxt.options._generate) {
522
524
  nitro.hooks.hook("rollup:before", async () => {
523
525
  await captureScreenshots();
524
526
  });
@@ -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);
@@ -21,6 +21,9 @@ export async function screenshot(browser, options) {
21
21
  waitUntil: "networkidle"
22
22
  });
23
23
  }
24
+ const screenshotOptions = {
25
+ timeout: 1e4
26
+ };
24
27
  if (options.delay)
25
28
  await page.waitForTimeout(options.delay);
26
29
  if (options.mask) {
@@ -30,6 +33,6 @@ 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
+ return await page.screenshot(screenshotOptions);
35
38
  }
@@ -1,7 +1,8 @@
1
1
  import { useServerHead } from "@vueuse/head";
2
2
  import { withBase } from "ufo";
3
3
  import { useRequestEvent } from "#app";
4
- import { useRouter } from "#imports";
4
+ import { useHostname } from "../nitro/util-hostname.mjs";
5
+ import { useRouter, useRuntimeConfig } from "#imports";
5
6
  export function defineOgImageScreenshot(options = {}) {
6
7
  const router = useRouter();
7
8
  const route = router?.currentRoute?.value?.path || "";
@@ -31,12 +32,13 @@ export function defineOgImageStatic(options = {}) {
31
32
  }
32
33
  export function defineOgImage(options = {}) {
33
34
  if (process.server) {
34
- const { forcePrerender, defaults, host } = useRuntimeConfig()["nuxt-og-image"];
35
+ const { forcePrerender, defaults, siteUrl } = useRuntimeConfig()["nuxt-og-image"];
35
36
  const router = useRouter();
36
37
  const route = router?.currentRoute?.value?.path || "";
37
38
  const e = useRequestEvent();
38
39
  if ((forcePrerender || options.static) && options.provider === "satori")
39
40
  e.res.setHeader("x-nitro-prerender", `${route === "/" ? "" : route}/__og_image__/og.png`);
41
+ const baseUrl = process.env.prerender ? siteUrl : useHostname(e);
40
42
  const meta = [
41
43
  {
42
44
  name: "twitter:card",
@@ -44,11 +46,11 @@ export function defineOgImage(options = {}) {
44
46
  },
45
47
  {
46
48
  name: "twitter:image:src",
47
- content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, host)
49
+ content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, baseUrl)
48
50
  },
49
51
  {
50
52
  property: "og:image",
51
- content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, host)
53
+ content: () => withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, baseUrl)
52
54
  },
53
55
  {
54
56
  property: "og:image:width",
@@ -9,7 +9,6 @@ export default defineEventHandler(async (e) => {
9
9
  const basePath = withoutTrailingSlash(
10
10
  path.replace("__og_image__/og.png", "")
11
11
  );
12
- setHeader(e, "Content-Type", "image/png");
13
12
  setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
14
13
  setHeader(e, "Pragma", "no-cache");
15
14
  setHeader(e, "Expires", "0");
@@ -21,5 +20,20 @@ export default defineEventHandler(async (e) => {
21
20
  statusMessage: `Provider ${options.provider} is missing.`
22
21
  });
23
22
  }
24
- return provider.createPng(withBase(basePath, useHostname(e)), options);
23
+ try {
24
+ const png = provider.createPng(withBase(basePath, useHostname(e)), options);
25
+ if (png) {
26
+ setHeader(e, "Content-Type", "image/png");
27
+ return png;
28
+ }
29
+ } catch (err) {
30
+ throw createError({
31
+ statusCode: 500,
32
+ statusMessage: `Failed to create og image: ${err.message}`
33
+ });
34
+ }
35
+ throw createError({
36
+ statusCode: 500,
37
+ statusMessage: "Failed to create og image, unknown error."
38
+ });
25
39
  });
@@ -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
  });
@@ -1,13 +1,15 @@
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) {
3
+ if (process.dev || process.env.prerender) {
4
+ try {
5
+ const { Launcher } = await import(String("chrome-launcher"));
6
+ const chromePath = Launcher.getFirstInstallation();
7
+ return await playwrightCore.chromium.launch({
8
+ headless: true,
9
+ executablePath: chromePath
10
+ });
11
+ } catch (e) {
12
+ }
11
13
  }
12
14
  try {
13
15
  return await playwrightCore.chromium.launch({
@@ -1,5 +1,7 @@
1
+ import { withBase } from "ufo";
1
2
  import { screenshot } from "../../browserUtil.mjs";
2
3
  import loadBrowser from "#nuxt-og-image/browser";
4
+ import { useRuntimeConfig } from "#imports";
3
5
  export default {
4
6
  name: "browser",
5
7
  createSvg: async function createSvg() {
@@ -11,12 +13,15 @@ export default {
11
13
  createPng: async function createPng(basePath, options) {
12
14
  const url = new URL(basePath);
13
15
  const createBrowser = await loadBrowser();
16
+ if (!createBrowser) {
17
+ throw new Error("Failed to load browser. Is the `browserProvider` enabled?");
18
+ }
14
19
  const browser = await createBrowser();
15
20
  if (browser) {
16
21
  try {
17
22
  return await screenshot(browser, {
18
23
  ...options,
19
- host: url.origin,
24
+ host: withBase(useRuntimeConfig().app.baseURL, url.origin),
20
25
  path: `/api/og-image-html?path=${url.pathname}`
21
26
  });
22
27
  } finally {
@@ -1,9 +1,10 @@
1
1
  import { defineSatoriTransformer } from "../utils.mjs";
2
+ import { decodeHtml } from "../../../utils-pure.mjs";
2
3
  export default defineSatoriTransformer(() => {
3
4
  return {
4
5
  filter: (node) => typeof node.props?.children === "string",
5
6
  transform: async (node) => {
6
- node.props.children = node.props.children.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#x2F;/g, "/");
7
+ node.props.children = decodeHtml(node.props.children);
7
8
  }
8
9
  };
9
10
  });
@@ -3,19 +3,22 @@ import { renderSSRHead } from "@unhead/ssr";
3
3
  import { createHeadCore } from "@unhead/vue";
4
4
  import { defineEventHandler, getQuery, sendRedirect } from "h3";
5
5
  import { fetchOptions, renderIsland, useHostname } from "../utils.mjs";
6
- import { useRuntimeConfig } from "#internal/nitro";
6
+ import { useRuntimeConfig } from "#imports";
7
7
  export default defineEventHandler(async (e) => {
8
8
  const { fonts, defaults } = useRuntimeConfig()["nuxt-og-image"];
9
- const path = getQuery(e).path || "/";
10
- const scale = getQuery(e).scale;
11
- const mode = getQuery(e).mode || "light";
9
+ const query = getQuery(e);
10
+ const path = withBase(query.path || "/", useRuntimeConfig().app.baseURL);
11
+ const scale = query.scale;
12
+ const mode = query.mode || "light";
12
13
  let options;
13
- if (getQuery(e).options)
14
- options = JSON.parse(getQuery(e).options);
14
+ if (query.options)
15
+ options = JSON.parse(query.options);
15
16
  if (!options)
16
17
  options = await fetchOptions(e, path);
17
- if (options.provider === "browser" && !options.component)
18
- return sendRedirect(e, withBase(path, useHostname(e)));
18
+ if (options.provider === "browser" && !options.component) {
19
+ const pathWithoutBase = path.replace(new RegExp(`^${useRuntimeConfig().app.baseURL}`), "");
20
+ return sendRedirect(e, withBase(pathWithoutBase, useHostname(e)));
21
+ }
19
22
  const island = await renderIsland(options);
20
23
  const head = createHeadCore();
21
24
  head.push(island.head);
@@ -1,9 +1,12 @@
1
1
  import { createError, defineEventHandler, getHeaders, getQuery } from "h3";
2
- import { extractOgImageOptions, useHostname } from "../utils.mjs";
3
- import { getRouteRules, useRuntimeConfig } from "#internal/nitro";
2
+ import { withoutBase } from "ufo";
3
+ import { extractOgImageOptions } from "../utils.mjs";
4
+ import { useHostname } from "../util-hostname.mjs";
5
+ import { getRouteRules } from "#internal/nitro";
6
+ import { useRuntimeConfig } from "#imports";
4
7
  export default defineEventHandler(async (e) => {
5
8
  const query = getQuery(e);
6
- const path = query.path || "/";
9
+ const path = withoutBase(query.path || "/", useRuntimeConfig().app.baseURL);
7
10
  const fetchOptions = process.dev || process.env.prerender ? {
8
11
  headers: getHeaders(e)
9
12
  } : {
@@ -2,8 +2,10 @@ import { createError, defineEventHandler, getQuery, setHeader } from "h3";
2
2
  import { withBase } from "ufo";
3
3
  import { fetchOptions, useHostname } from "../utils.mjs";
4
4
  import { useProvider } from "#nuxt-og-image/provider";
5
+ import { useRuntimeConfig } from "#imports";
5
6
  export default defineEventHandler(async (e) => {
6
- const path = getQuery(e).path || "/";
7
+ const query = getQuery(e);
8
+ const path = withBase(query.path || "/", useRuntimeConfig().app.baseURL);
7
9
  const options = await fetchOptions(e, path);
8
10
  setHeader(e, "Content-Type", "image/svg+xml");
9
11
  const provider = await useProvider(options.provider);
@@ -2,8 +2,10 @@ import { createError, defineEventHandler, getQuery, setHeader } from "h3";
2
2
  import { withBase } from "ufo";
3
3
  import { fetchOptions, useHostname } from "../utils.mjs";
4
4
  import { useProvider } from "#nuxt-og-image/provider";
5
+ import { useRuntimeConfig } from "#imports";
5
6
  export default defineEventHandler(async (e) => {
6
- const path = getQuery(e).path || "/";
7
+ const query = getQuery(e);
8
+ const path = withBase(query.path || "/", useRuntimeConfig().app.baseURL);
7
9
  const options = await fetchOptions(e, path);
8
10
  setHeader(e, "Content-Type", "application/json");
9
11
  const provider = await useProvider(options.provider);
@@ -0,0 +1,2 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function useHostname(e: H3Event): string;
@@ -0,0 +1,13 @@
1
+ import { getRequestHeader } from "h3";
2
+ import { withBase } from "ufo";
3
+ import { useRuntimeConfig } from "#imports";
4
+ export function useHostname(e) {
5
+ const base = useRuntimeConfig().app.baseURL;
6
+ const host = getRequestHeader(e, "host") || process.env.NITRO_HOST || process.env.HOST || "localhost";
7
+ const protocol = getRequestHeader(e, "x-forwarded-proto") || "http";
8
+ const useHttp = process.dev || host.includes("127.0.0.1") || host.includes("localhost") || protocol === "http";
9
+ let port = host.includes(":") ? host.split(":").pop() : false;
10
+ if ((process.dev || process.env.prerender || host.includes("localhost")) && !port)
11
+ port = process.env.NITRO_PORT || process.env.PORT;
12
+ return withBase(base, `http${useHttp ? "" : "s"}://${host.includes(":") ? host.split(":")[0] : host}${port ? `:${port}` : ""}`);
13
+ }
@@ -1,2 +1,3 @@
1
- export declare function extractOgImageOptions(html: string): false | Record<string, any>;
2
- export declare function stripOgImageOptions(html: string): string;
1
+ import type { OgImageOptions } from '../../types';
2
+ export declare function decodeHtml(html: string): string;
3
+ export declare function extractOgImageOptions(html: string): OgImageOptions | false;
@@ -1,8 +1,12 @@
1
- function decodeHtmlEntities(obj) {
1
+ export function decodeHtml(html) {
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));
4
+ });
5
+ }
6
+ function decodeObjectHtmlEntities(obj) {
2
7
  Object.entries(obj).forEach(([key, value]) => {
3
- if (typeof value === "string") {
4
- obj[key] = value.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#x2F;/g, "/");
5
- }
8
+ if (typeof value === "string")
9
+ obj[key] = decodeHtml(value);
6
10
  });
7
11
  return obj;
8
12
  }
@@ -19,15 +23,14 @@ export function extractOgImageOptions(html) {
19
23
  console.warn("Failed to parse #nuxt-og-image-options", e, options);
20
24
  }
21
25
  if (options) {
22
- const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
23
- if (description)
24
- options.description = description;
25
- else
26
- options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
27
- return decodeHtmlEntities(options);
26
+ if (!options.description) {
27
+ const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
28
+ if (description)
29
+ options.description = description;
30
+ else
31
+ options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
32
+ }
33
+ return decodeObjectHtmlEntities(options);
28
34
  }
29
35
  return false;
30
36
  }
31
- export function stripOgImageOptions(html) {
32
- return html.replace(/<script id="nuxt-og-image-options" type="application\/json">(.*?)<\/script>/, "");
33
- }
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import type { H3Event } from 'h3';
3
3
  import type { OgImageOptions } from '../../types';
4
+ export * from './util-hostname';
4
5
  export declare function wasmLoader(key: any, fallback: string, baseUrl: string): {
5
6
  loaded(): boolean | Promise<any>;
6
7
  load(): Promise<any>;
@@ -11,7 +12,6 @@ export declare function renderIsland(payload: OgImageOptions): Promise<{
11
12
  html: string;
12
13
  head: any;
13
14
  }>;
14
- export declare function useHostname(e: H3Event): any;
15
15
  export declare function readPublicAsset(file: string, encoding?: BufferEncoding): Promise<string | Buffer | undefined>;
16
16
  export declare function readPublicAssetBase64(file: string): Promise<string | undefined>;
17
17
  export * from './utils-pure';
@@ -1,7 +1,9 @@
1
1
  import { existsSync, promises as fsp } from "node:fs";
2
- import { getHeaders, getQuery, getRequestHeader } from "h3";
2
+ import { getHeaders, getQuery } from "h3";
3
3
  import { join } from "pathe";
4
- import { useRuntimeConfig } from "#internal/nitro";
4
+ import { useHostname } from "./util-hostname.mjs";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export * from "./util-hostname.mjs";
5
7
  export function wasmLoader(key, fallback, baseUrl) {
6
8
  let promise;
7
9
  let loaded = false;
@@ -58,20 +60,9 @@ export function renderIsland(payload) {
58
60
  query: { props: JSON.stringify(payload) }
59
61
  });
60
62
  }
61
- export function useHostname(e) {
62
- const config = useRuntimeConfig()["nuxt-og-image"];
63
- if (!process.dev && config.siteUrl)
64
- return config.siteUrl;
65
- const host = getRequestHeader(e, "host") || process.env.NITRO_HOST || process.env.HOST || "localhost";
66
- const protocol = getRequestHeader(e, "x-forwarded-proto") || "http";
67
- const useHttp = process.env.NODE_ENV === "development" || host.includes("127.0.0.1") || host.includes("localhost") || protocol === "http";
68
- const port = host.includes(":") ? host.split(":").pop() : process.env.NITRO_PORT || process.env.PORT;
69
- const base = useRuntimeConfig().app.baseURL;
70
- return `http${useHttp ? "" : "s"}://${host.includes(":") ? host.split(":")[0] : host}${port ? `:${port}` : ""}${base}`;
71
- }
72
- const r = (base, key) => {
63
+ function r(base, key) {
73
64
  return join(base, key.replace(/:/g, "/"));
74
- };
65
+ }
75
66
  export async function readPublicAsset(file, encoding) {
76
67
  const { assetDirs } = useRuntimeConfig()["nuxt-og-image"];
77
68
  for (const assetDir of assetDirs) {