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
package/dist/module.mjs CHANGED
@@ -1,30 +1,34 @@
1
1
  import { readFile, writeFile } from 'node:fs/promises';
2
- import { defineNuxtModule, createResolver, addTemplate, addServerHandler, addImports, addComponent } from '@nuxt/kit';
2
+ import { useNuxt, addTemplate, defineNuxtModule, useLogger, createResolver, addServerHandler, addImports, addComponent, hasNuxtModule, addServerPlugin } from '@nuxt/kit';
3
3
  import { execa } from 'execa';
4
4
  import chalk from 'chalk';
5
5
  import defu from 'defu';
6
6
  import { toRouteMatcher, createRouter } from 'radix3';
7
7
  import { withBase, joinURL } from 'ufo';
8
- import { resolve, relative } from 'pathe';
8
+ import { resolve, relative, dirname } from 'pathe';
9
9
  import { tinyws } from 'tinyws';
10
10
  import sirv from 'sirv';
11
11
  import { pathExists, copy, mkdirp } from 'fs-extra';
12
- import { provider } from 'std-env';
13
- import { $fetch } from 'ofetch';
12
+ import { globby } from 'globby';
13
+ import { installNuxtSiteConfig, updateSiteConfig, requireSiteConfig } from 'nuxt-site-config-kit';
14
14
  import playwrightCore from 'playwright-core';
15
15
  import { existsSync } from 'node:fs';
16
16
  import { createBirpcGroup } from 'birpc';
17
17
  import { stringify, parse } from 'flatted';
18
+ import { addDependency } from 'nypm';
19
+ import { provider } from 'std-env';
18
20
 
19
21
  async function createBrowser() {
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) {
22
+ if (process.dev || process.env.prerender) {
23
+ try {
24
+ const { Launcher } = await import(String("chrome-launcher"));
25
+ const chromePath = Launcher.getFirstInstallation();
26
+ return await playwrightCore.chromium.launch({
27
+ headless: true,
28
+ executablePath: chromePath
29
+ });
30
+ } catch (e) {
31
+ }
28
32
  }
29
33
  try {
30
34
  return await playwrightCore.chromium.launch({
@@ -55,9 +59,9 @@ async function screenshot(browser, options) {
55
59
  width: options.width || 1200,
56
60
  height: options.height || 630
57
61
  });
58
- const isHtml = options.path.startsWith("html:") || options.html;
62
+ const isHtml = options.html || options.path?.startsWith("html:");
59
63
  if (isHtml) {
60
- const html = options.html || options.path.substring(5);
64
+ const html = options.html || options.path?.substring(5);
61
65
  await page.evaluate((html2) => {
62
66
  document.open("text/html");
63
67
  document.write(html2);
@@ -66,12 +70,12 @@ async function screenshot(browser, options) {
66
70
  await page.waitForLoadState("networkidle");
67
71
  } else {
68
72
  await page.goto(`${options.host}${options.path}`, {
69
- timeout: 1e4,
73
+ timeout: process.env.prerender || process.dev ? 1e4 : 3500,
70
74
  waitUntil: "networkidle"
71
75
  });
72
76
  }
73
77
  const screenshotOptions = {
74
- timeout: 1e4
78
+ timeout: process.env.prerender || process.dev ? 1e4 : 3500
75
79
  };
76
80
  if (options.delay)
77
81
  await page.waitForTimeout(options.delay);
@@ -83,7 +87,9 @@ async function screenshot(browser, options) {
83
87
  }
84
88
  if (options.selector)
85
89
  return await page.locator(options.selector).screenshot(screenshotOptions);
86
- return await page.screenshot(screenshotOptions);
90
+ const screenshot2 = await page.screenshot(screenshotOptions);
91
+ await page.close();
92
+ return screenshot2;
87
93
  }
88
94
 
89
95
  function setupPlaygroundRPC(nuxt, config) {
@@ -170,7 +176,7 @@ function getBodyJson(req) {
170
176
 
171
177
  function decodeHtml(html) {
172
178
  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) => {
173
- return String.fromCharCode(parseInt(int));
179
+ return String.fromCharCode(Number.parseInt(int));
174
180
  });
175
181
  }
176
182
  function decodeObjectHtmlEntities(obj) {
@@ -204,50 +210,204 @@ function extractOgImageOptions(html) {
204
210
  }
205
211
  return false;
206
212
  }
207
- function stripOgImageOptions(html) {
208
- return html.replace(/<script id="nuxt-og-image-options" type="application\/json">(.*?)<\/script>/, "");
213
+
214
+ const SVG2PNGWasmPlaceholder = '"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"';
215
+ const YogaWasmPlaceholder = '"/* NUXT_OG_IMAGE_YOGA_WASM */"';
216
+ const ReSVGWasmPlaceholder = '"/* NUXT_OG_IMAGE_RESVG_WASM */"';
217
+ const Wasms = [
218
+ {
219
+ placeholder: SVG2PNGWasmPlaceholder,
220
+ path: "svg2png/svg2png.wasm",
221
+ file: "svg2png.wasm"
222
+ },
223
+ {
224
+ placeholder: ReSVGWasmPlaceholder,
225
+ path: "resvg/resvg.wasm",
226
+ file: "resvg.wasm"
227
+ },
228
+ {
229
+ placeholder: YogaWasmPlaceholder,
230
+ path: "yoga/yoga.wasm",
231
+ file: "yoga.wasm"
232
+ }
233
+ ];
234
+ const DefaultRuntimeCompatibility = {
235
+ // node-server runtime
236
+ browser: "playwright",
237
+ satori: "default",
238
+ wasm: "fetch",
239
+ png: "resvg-node"
240
+ };
241
+ const RuntimeCompatibility = {
242
+ "nitro-dev": {
243
+ wasm: "fetch",
244
+ browser: "universal"
245
+ },
246
+ "stackblitz": {
247
+ browser: false,
248
+ satori: "yoga-wasm",
249
+ wasm: "inline",
250
+ png: "resvg-wasm"
251
+ },
252
+ "netlify": {
253
+ browser: "lambda",
254
+ wasm: "inline"
255
+ },
256
+ "netlify-edge": {
257
+ wasm: "inline",
258
+ png: "resvg-wasm"
259
+ },
260
+ "vercel": {
261
+ // exceeds 50mb limit
262
+ browser: false
263
+ },
264
+ "vercel-edge": {
265
+ browser: false,
266
+ wasm: "import",
267
+ wasmImportQuery: "?module",
268
+ png: "resvg-wasm"
269
+ },
270
+ "cloudflare-pages": {
271
+ browser: false,
272
+ wasm: "import",
273
+ png: "resvg-wasm"
274
+ },
275
+ "cloudflare": {
276
+ browser: false,
277
+ wasm: "import"
278
+ }
279
+ };
280
+
281
+ const autodetectableProviders = {
282
+ azure_static: "azure",
283
+ cloudflare_pages: "cloudflare-pages",
284
+ netlify: "netlify",
285
+ stormkit: "stormkit",
286
+ vercel: "vercel",
287
+ cleavr: "cleavr",
288
+ stackblitz: "stackblitz"
289
+ };
290
+ const autodetectableStaticProviders = {
291
+ netlify: "netlify-static",
292
+ vercel: "vercel-static"
293
+ };
294
+ function detectTarget(options = {}) {
295
+ return options?.static ? autodetectableStaticProviders[provider] : autodetectableProviders[provider];
296
+ }
297
+ function getNitroPreset(nuxt) {
298
+ return process.env.NITRO_PRESET || nuxt.options.nitro.preset || detectTarget() || "node-server";
299
+ }
300
+ function getNitroProviderCompatibility(nuxt) {
301
+ if (provider === "stackblitz")
302
+ return defu(RuntimeCompatibility.stackblitz, DefaultRuntimeCompatibility);
303
+ if (nuxt.options.dev || nuxt.options._prepare || nuxt.options._generate) {
304
+ return defu({
305
+ wasm: "fetch",
306
+ browser: "universal"
307
+ }, DefaultRuntimeCompatibility);
308
+ }
309
+ const target = getNitroPreset(nuxt);
310
+ const compatibility = RuntimeCompatibility[target];
311
+ if (compatibility === false)
312
+ return false;
313
+ return defu(compatibility || {}, DefaultRuntimeCompatibility);
314
+ }
315
+ function ensureDependencies(nuxt, dep) {
316
+ return Promise.all(dep.map((d) => {
317
+ return addDependency(d, { cwd: nuxt.options.rootDir });
318
+ }));
319
+ }
320
+
321
+ function extendTypes(module, template) {
322
+ const nuxt = useNuxt();
323
+ addTemplate({
324
+ filename: `${module}.d.ts`,
325
+ getContents: () => {
326
+ const s = template();
327
+ return `// Generated by ${module}
328
+ ${s}
329
+ export {}
330
+ `;
331
+ }
332
+ });
333
+ nuxt.hooks.hook("prepare:types", ({ references }) => {
334
+ references.push({ path: resolve(nuxt.options.buildDir, `${module}.d.ts`) });
335
+ });
209
336
  }
210
337
 
211
338
  const PATH = "/__nuxt_og_image__";
212
339
  const PATH_ENTRY = `${PATH}/entry`;
213
340
  const PATH_PLAYGROUND = `${PATH}/client`;
214
- const edgeProvidersSupported = [
215
- "cloudflare",
216
- "vercel-edge",
217
- "netlify-edge"
218
- ];
219
341
  const module = defineNuxtModule({
220
342
  meta: {
221
343
  name: "nuxt-og-image",
222
344
  compatibility: {
223
- nuxt: "^3.3.1",
345
+ nuxt: "^3.6.1",
224
346
  bridge: false
225
347
  },
226
348
  configKey: "ogImage"
227
349
  },
228
350
  defaults(nuxt) {
229
- const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.NUXT_SITE_URL || nuxt.options.runtimeConfig.public?.siteUrl || nuxt.options.runtimeConfig.siteUrl;
230
351
  return {
231
- // when we run `nuxi generate` we need to force prerendering
232
- forcePrerender: !nuxt.options.dev && nuxt.options._generate,
233
- siteUrl,
352
+ enabled: true,
234
353
  defaults: {
235
- component: "OgImageBasic",
354
+ component: "OgImageTemplateFallback",
236
355
  width: 1200,
237
- height: 630
356
+ height: 630,
357
+ // default is to cache the image for 24 hours
358
+ cache: true,
359
+ cacheTtl: 24 * 60 * 60 * 1e3
238
360
  },
239
- satoriProvider: true,
240
- browserProvider: true,
361
+ componentDirs: ["OgImage", "OgImageTemplate"],
362
+ runtimeSatori: true,
363
+ runtimeBrowser: nuxt.options.dev,
241
364
  fonts: [],
365
+ runtimeCacheStorage: true,
242
366
  satoriOptions: {},
243
- experimentalInlineWasm: process.env.NITRO_PRESET === "netlify-edge" || nuxt.options.nitro.preset === "netlify-edge" || false,
244
- experimentalRuntimeBrowser: false,
245
- playground: process.env.NODE_ENV === "development" || nuxt.options.dev
367
+ playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
368
+ debug: false
246
369
  };
247
370
  },
248
371
  async setup(config, nuxt) {
372
+ const logger = useLogger("nuxt-og-image");
373
+ logger.level = config.debug || nuxt.options.debug ? 4 : 3;
374
+ if (config.enabled === false) {
375
+ logger.debug("The module is disabled, skipping setup.");
376
+ return;
377
+ }
249
378
  const { resolve } = createResolver(import.meta.url);
250
- config.siteUrl = config.siteUrl || config.host;
379
+ logger.debug("Using Nitro preset", getNitroPreset(nuxt));
380
+ const nitroCompatibility = getNitroProviderCompatibility(nuxt);
381
+ logger.debug("Nitro compatibility", nitroCompatibility);
382
+ const nitroTarget = process.env.NITRO_PRESET || nuxt.options.nitro.preset;
383
+ if (!nitroCompatibility) {
384
+ logger.warn(`\`nuxt-og-image\` does not support the nitro target \`${nitroTarget}\`. Please make an issue. `);
385
+ return;
386
+ }
387
+ if (!nitroCompatibility.browser && config.runtimeBrowser) {
388
+ config.runtimeBrowser = false;
389
+ logger.warn(`\`nuxt-og-image\` does not support the nitro target \`${nitroTarget}\` with the runtime browser. Set runtimeBrowser: false to stop seeing this.`);
390
+ }
391
+ if (config.runtimeBrowser && nitroCompatibility.browser === "lambda") {
392
+ logger.info(`\`nuxt-og-image\` is deploying to nitro target \`${nitroTarget}\` that installs extra dependencies.`);
393
+ await ensureDependencies(nuxt, ["puppeteer-core@14.1.1", "@sparticuz/chrome-aws-lambda@14.1.1"]);
394
+ }
395
+ await installNuxtSiteConfig();
396
+ await updateSiteConfig({
397
+ _context: "nuxt-og-image:config",
398
+ url: config.siteUrl || config.host
399
+ });
400
+ requireSiteConfig("nuxt-og-image", {
401
+ url: "Required to generate absolute URLs for the og:image."
402
+ }, { prerender: true });
403
+ nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
404
+ if (nuxt.options._generate) {
405
+ nuxt.options.nitro.storage["og-image"] = {
406
+ driver: "memory"
407
+ };
408
+ } else if (config.runtimeCacheStorage && !nuxt.options.dev && typeof config.runtimeCacheStorage === "object") {
409
+ nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
410
+ }
251
411
  if (!config.fonts.length)
252
412
  config.fonts = ["Inter:400", "Inter:700"];
253
413
  const distResolve = (p) => {
@@ -257,34 +417,27 @@ const module = defineNuxtModule({
257
417
  return resolve(`../dist/${p}`);
258
418
  };
259
419
  nuxt.options.experimental.componentIslands = true;
260
- addTemplate({
261
- filename: "nuxt-og-image.d.ts",
262
- getContents: () => {
263
- return `// Generated by nuxt-og-image
264
- interface NuxtOgImageNitroRules {
420
+ extendTypes("nuxt-og-image", () => {
421
+ return `interface NuxtOgImageNitroRules {
265
422
  ogImage?: false | Record<string, any>
266
423
  }
267
424
  declare module 'nitropack' {
268
425
  interface NitroRouteRules extends NuxtOgImageNitroRules {}
269
426
  interface NitroRouteConfig extends NuxtOgImageNitroRules {}
270
- }
271
- export {}
272
- `;
273
- }
274
- });
275
- nuxt.hooks.hook("prepare:types", ({ references }) => {
276
- references.push({ path: resolve(nuxt.options.buildDir, "nuxt-og-image.d.ts") });
427
+ }`;
277
428
  });
278
429
  addServerHandler({
279
430
  lazy: true,
280
431
  handler: resolve("./runtime/nitro/middleware/og.png")
281
432
  });
282
- ["html", "options", "svg", "vnode", "font"].forEach((type) => {
283
- addServerHandler({
284
- lazy: true,
285
- route: `/api/og-image-${type}`,
286
- handler: resolve(`./runtime/nitro/routes/${type}`)
287
- });
433
+ ["html", "options", "svg", "vnode", "font", "debug"].forEach((type) => {
434
+ if (type !== "debug" || config.debug) {
435
+ addServerHandler({
436
+ lazy: true,
437
+ route: `/api/og-image-${type}`,
438
+ handler: resolve(`./runtime/nitro/routes/${type}`)
439
+ });
440
+ }
288
441
  });
289
442
  nuxt.hook("devtools:customTabs", (iframeTabs) => {
290
443
  iframeTabs.push({
@@ -313,7 +466,17 @@ export {}
313
466
  });
314
467
  }
315
468
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
316
- ["defineOgImageDynamic", "defineOgImageStatic", "defineOgImageScreenshot"].forEach((name) => {
469
+ [
470
+ // deprecated
471
+ "Dynamic",
472
+ "Static",
473
+ // new
474
+ "index",
475
+ "Cached",
476
+ "WithoutCache",
477
+ "Screenshot"
478
+ ].forEach((name) => {
479
+ name = name === "index" ? "defineOgImage" : `defineOgImage${name}`;
317
480
  addImports({
318
481
  name,
319
482
  from: resolve("./runtime/composables/defineOgImage")
@@ -321,104 +484,198 @@ export {}
321
484
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
322
485
  });
323
486
  await addComponent({
324
- name: "OgImageBasic",
325
- filePath: resolve("./runtime/components/OgImageBasic.island.vue"),
487
+ name: "OgImageTemplateFallback",
488
+ filePath: resolve("./runtime/components/OgImageTemplate/Fallback.vue"),
326
489
  island: true
327
490
  });
328
- ["OgImageStatic", "OgImageDynamic", "OgImageScreenshot"].forEach((name) => {
491
+ [
492
+ // deprecated
493
+ "Static",
494
+ "Dynamic",
495
+ // new
496
+ "index",
497
+ "Cached",
498
+ "WithoutCache",
499
+ "Screenshot"
500
+ ].forEach((name) => {
329
501
  addComponent({
330
- name,
331
- filePath: resolve(`./runtime/components/${name}`)
502
+ global: hasNuxtModule("@nuxt/content"),
503
+ name: name === "index" ? "OgImage" : `OgImage${name}`,
504
+ filePath: resolve(`./runtime/components/OgImage/${name}`)
332
505
  });
333
506
  });
507
+ const ogImageComponents = [];
508
+ nuxt.hook("components:extend", (components) => {
509
+ components.forEach((component) => {
510
+ let valid = false;
511
+ config.componentDirs.forEach((dir) => {
512
+ if (component.pascalName.startsWith(dir) || component.kebabName.startsWith(dir))
513
+ valid = true;
514
+ });
515
+ if (valid) {
516
+ component.island = true;
517
+ component.mode = "server";
518
+ ogImageComponents.push({ pascalName: component.pascalName, kebabName: component.kebabName });
519
+ }
520
+ });
521
+ });
522
+ addTemplate({
523
+ filename: "og-image-component-names.mjs",
524
+ getContents() {
525
+ return `export const componentNames = ${JSON.stringify(ogImageComponents)}`;
526
+ },
527
+ options: { mode: "server" }
528
+ });
334
529
  const runtimeDir = resolve("./runtime");
335
530
  nuxt.options.build.transpile.push(runtimeDir);
336
- const moduleAssetDir = resolve("./runtime/public-assets");
337
- const assetDirs = [
338
- resolve(nuxt.options.rootDir, nuxt.options.dir.public),
339
- moduleAssetDir
531
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
532
+ const customAssetDirs = [
533
+ // allows us to show custom error images
534
+ resolve("./runtime/public-assets")
340
535
  ];
536
+ if (config.runtimeSatori) {
537
+ if (config.fonts.includes("Inter:400"))
538
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/inter-font"));
539
+ if (nitroCompatibility.png === "resvg-wasm" && nitroCompatibility.wasm === "fetch")
540
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/resvg"));
541
+ else if (nitroCompatibility.png === "svg2png" && nitroCompatibility.wasm === "fetch")
542
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/svg2png"));
543
+ if (nitroCompatibility.satori === "yoga-wasm")
544
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/yoga"));
545
+ }
341
546
  nuxt.hooks.hook("modules:done", async () => {
342
547
  nuxt.hooks.callHook("og-image:config", config);
343
- nuxt.options.runtimeConfig["nuxt-og-image"] = { ...config, assetDirs };
548
+ nuxt.options.runtimeConfig["nuxt-og-image"] = {
549
+ satoriOptions: config.satoriOptions,
550
+ runtimeSatori: config.runtimeSatori,
551
+ runtimeBrowser: config.runtimeBrowser,
552
+ // @ts-expect-error runtime type
553
+ defaults: config.defaults,
554
+ // avoid adding credentials
555
+ runtimeCacheStorage: typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver,
556
+ // convert the fonts to uniform type to fix ts issue
557
+ fonts: config.fonts.map((f) => {
558
+ if (typeof f === "string") {
559
+ const [name, weight] = f.split(":");
560
+ return {
561
+ name,
562
+ weight
563
+ };
564
+ }
565
+ return f;
566
+ }),
567
+ assetDirs: [
568
+ resolve(nuxt.options.srcDir, nuxt.options.dir.public),
569
+ ...customAssetDirs,
570
+ // always add runtime dirs for prerendering to work, these are just used as scan roots
571
+ resolve("./runtime/public-assets-optional/inter-font"),
572
+ resolve("./runtime/public-assets-optional/resvg"),
573
+ resolve("./runtime/public-assets-optional/yoga"),
574
+ resolve("./runtime/public-assets-optional/svg2png")
575
+ ]
576
+ };
344
577
  });
345
- const useSatoriWasm = provider === "stackblitz";
346
578
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
347
579
  nitroConfig.externals = defu(nitroConfig.externals || {}, {
348
580
  inline: [runtimeDir]
349
581
  });
350
- if (config.experimentalRuntimeBrowser) {
582
+ if (config.runtimeBrowser) {
351
583
  nitroConfig.alias = nitroConfig.alias || {};
352
584
  nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
353
585
  nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
354
586
  nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
355
587
  }
356
588
  nitroConfig.publicAssets = nitroConfig.publicAssets || [];
357
- nitroConfig.publicAssets.push({ dir: moduleAssetDir, maxAge: 31536e3 });
589
+ customAssetDirs.forEach((dir) => {
590
+ nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
591
+ });
358
592
  const providerPath = `${runtimeDir}/nitro/providers`;
359
- if (config.browserProvider) {
360
- nitroConfig.virtual["#nuxt-og-image/browser"] = nuxt.options.dev || config.experimentalRuntimeBrowser ? `
361
- import node from '${providerPath}/browser/node'
362
-
593
+ if (config.runtimeBrowser) {
594
+ nitroConfig.virtual["#nuxt-og-image/browser"] = `
595
+ let browser
363
596
  export default async function() {
364
- return node
365
- }
366
- ` : `export default async function() {
367
- return () => {}
597
+ browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
598
+ return browser
368
599
  }
369
600
  `;
370
601
  }
371
- if (config.satoriProvider) {
372
- nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${useSatoriWasm ? "webworker" : "node"}'
373
- export default async function() {
602
+ if (config.runtimeSatori) {
603
+ nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
604
+ export default function() {
374
605
  return satori
375
606
  }`;
376
- nitroConfig.virtual["#nuxt-og-image/svg2png"] = `
377
- import svg2png from '${providerPath}/svg2png/universal'
378
- export default async function() {
379
- return svg2png
380
- }`;
607
+ nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
608
+ export default function() {
609
+ return png
610
+ }
611
+ `;
381
612
  }
382
613
  nitroConfig.virtual["#nuxt-og-image/provider"] = `
383
- import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'
384
- import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'
614
+ ${config.runtimeSatori ? `import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'` : ""}
615
+ ${config.runtimeBrowser ? `import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'` : ""}
385
616
 
386
617
  export async function useProvider(provider) {
387
618
  if (provider === 'satori')
388
- return satori
619
+ return ${config.runtimeSatori ? "satori" : "null"}
389
620
  if (provider === 'browser')
390
- return browser
621
+ return ${config.runtimeBrowser ? "browser" : "null"}
622
+ return null
391
623
  }
392
624
  `;
393
625
  });
394
626
  nuxt.hooks.hook("nitro:init", async (nitro) => {
395
627
  let screenshotQueue = [];
396
628
  nitro.hooks.hook("compiled", async (_nitro) => {
397
- if (edgeProvidersSupported.includes(_nitro.options.preset)) {
398
- await copy(resolve("./runtime/public-assets/inter-latin-ext-400-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-400-normal.woff"));
399
- await copy(resolve("./runtime/public-assets/inter-latin-ext-700-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-700-normal.woff"));
400
- if (!config.experimentalInlineWasm) {
401
- await copy(resolve("./runtime/public-assets/svg2png.wasm"), resolve(_nitro.options.output.serverDir, "svg2png.wasm"));
402
- if (useSatoriWasm)
403
- await copy(resolve("./runtime/public-assets/yoga.wasm"), resolve(_nitro.options.output.serverDir, "yoga.wasm"));
629
+ if (!config.runtimeSatori || nuxt.options.dev)
630
+ return;
631
+ if (config.fonts.includes("Inter:400"))
632
+ await copy(resolve("./runtime/public-assets-optional/inter-font/inter-latin-ext-400-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-400-normal.woff"));
633
+ if (config.fonts.includes("Inter:700"))
634
+ await copy(resolve("./runtime/public-assets-optional/inter-font/inter-latin-ext-700-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-700-normal.woff"));
635
+ const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
636
+ const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
637
+ const paths = [wasmProviderPath];
638
+ const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
639
+ paths.push(...chunks);
640
+ for (const path of paths) {
641
+ if (!await pathExists(path))
642
+ continue;
643
+ let contents = await readFile(path, "utf-8");
644
+ let updated = false;
645
+ if (_nitro.options.preset.includes("vercel") && path === wasmProviderPath) {
646
+ contents = contents.replace(".cwd(),", '?.cwd || "/",');
647
+ updated = true;
404
648
  }
405
- const indexFile = resolve(_nitro.options.output.serverDir, _nitro.options.preset === "netlify-edge" ? "server.mjs" : "index.mjs");
406
- if (await pathExists(indexFile)) {
407
- const indexContents = (await readFile(indexFile, "utf-8")).replace(".cwd(),", '?.cwd || "/",');
408
- if (!config.experimentalInlineWasm) {
409
- await writeFile(
410
- indexFile,
411
- indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', 'import("./svg2png.wasm").then(m => m.default || m)').replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', 'import("./yoga.wasm").then(m => m.default || m)')
412
- );
413
- } else {
414
- const svg2pngWasm = await readFile(resolve("./runtime/public-assets/svg2png.wasm"), "base64");
415
- const yogaWasm = await readFile(resolve("./runtime/public-assets/yoga.wasm"), "base64");
416
- await writeFile(
417
- indexFile,
418
- indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', `Buffer.from("${svg2pngWasm}", "base64")`).replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', `Buffer.from("${yogaWasm}", "base64")`)
419
- );
649
+ if (_nitro.options.preset.includes("netlify") && path.endsWith("netlify.mjs")) {
650
+ const match = "// TODO: handle event.isBase64Encoded\n });";
651
+ contents = contents.replace(match, `${match}
652
+
653
+ const headers = normalizeOutgoingHeaders(r.headers);
654
+ // image buffers must be base64 encoded
655
+ if (Buffer.isBuffer(r.body) && headers["content-type"].startsWith("image/")) {
656
+ return {
657
+ statusCode: r.status,
658
+ headers,
659
+ body: r.body.toString("base64"),
660
+ isBase64Encoded: true
661
+ };
662
+ }`);
663
+ updated = true;
664
+ }
665
+ for (const wasm of Wasms) {
666
+ if (contents.includes(wasm.placeholder)) {
667
+ if (nitroCompatibility.wasm === "import") {
668
+ contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
669
+ await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
670
+ } else if (nitroCompatibility.wasm === "inline") {
671
+ const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`));
672
+ contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
673
+ }
674
+ updated = true;
420
675
  }
421
676
  }
677
+ if (updated)
678
+ await writeFile(path, contents, { encoding: "utf-8" });
422
679
  }
423
680
  });
424
681
  const _routeRulesMatcher = toRouteMatcher(
@@ -431,7 +688,6 @@ export async function useProvider(provider) {
431
688
  if (!html)
432
689
  return;
433
690
  const extractedOptions = extractOgImageOptions(html);
434
- ctx.contents = stripOgImageOptions(html);
435
691
  const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
436
692
  if (!extractedOptions || routeRules.ogImage === false)
437
693
  return;
@@ -441,7 +697,7 @@ export async function useProvider(provider) {
441
697
  ...extractedOptions,
442
698
  ...routeRules.ogImage || {}
443
699
  };
444
- if ((nuxt.options._generate || entry.static) && entry.provider === "browser")
700
+ if ((nuxt.options._generate || entry.cache) && entry.provider === "browser")
445
701
  screenshotQueue.push(entry);
446
702
  });
447
703
  if (nuxt.options.dev)
@@ -489,14 +745,14 @@ export async function useProvider(provider) {
489
745
  });
490
746
  }
491
747
  if (entry.component)
492
- entry.html = await $fetch(entry.path, { baseURL: withBase(nuxt.options.app.baseURL, host) });
748
+ entry.html = await globalThis.$fetch(entry.path);
493
749
  }
494
750
  for (const k in screenshotQueue) {
495
751
  const entry = screenshotQueue[k];
496
752
  const start = Date.now();
497
753
  let hasError = false;
498
- const dirname = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
499
- const filename = joinURL(dirname, "/og.png");
754
+ const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
755
+ const filename = joinURL(dirname2, "/og.png");
500
756
  try {
501
757
  const imgBuffer = await screenshot(browser, {
502
758
  ...config.defaults || {},
@@ -504,7 +760,7 @@ export async function useProvider(provider) {
504
760
  host
505
761
  });
506
762
  try {
507
- await mkdirp(dirname);
763
+ await mkdirp(dirname2);
508
764
  } catch (e) {
509
765
  }
510
766
  await writeFile(filename, imgBuffer);