nuxt-og-image 2.0.0-beta.9 → 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.02537f9e.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.a60bf0b5.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.aace8ca2.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 +375 -115
  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 +0 -1
  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.ef0613e7.js +0 -1
  97. package/dist/client/_nuxt/ImageLoader.a4418cab.js +0 -1
  98. package/dist/client/_nuxt/entry.09f25aaf.css +0 -1
  99. package/dist/client/_nuxt/entry.fc9150b0.js +0 -5
  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.8148b615.js +0 -3
  103. package/dist/client/_nuxt/index.80f38ec7.js +0 -1
  104. package/dist/client/_nuxt/options.cc3fd02b.js +0 -1
  105. package/dist/client/_nuxt/png.62758167.js +0 -1
  106. package/dist/client/_nuxt/svg.853cdaad.js +0 -1
  107. package/dist/client/_nuxt/vnodes.69b24963.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) {
@@ -205,46 +211,203 @@ function extractOgImageOptions(html) {
205
211
  return false;
206
212
  }
207
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
+ });
336
+ }
337
+
208
338
  const PATH = "/__nuxt_og_image__";
209
339
  const PATH_ENTRY = `${PATH}/entry`;
210
340
  const PATH_PLAYGROUND = `${PATH}/client`;
211
- const edgeProvidersSupported = [
212
- "cloudflare",
213
- "vercel-edge",
214
- "netlify-edge"
215
- ];
216
341
  const module = defineNuxtModule({
217
342
  meta: {
218
343
  name: "nuxt-og-image",
219
344
  compatibility: {
220
- nuxt: "^3.3.1",
345
+ nuxt: "^3.6.1",
221
346
  bridge: false
222
347
  },
223
348
  configKey: "ogImage"
224
349
  },
225
350
  defaults(nuxt) {
226
- const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.NUXT_SITE_URL || nuxt.options.runtimeConfig.public?.siteUrl || nuxt.options.runtimeConfig.siteUrl;
227
351
  return {
228
- // when we run `nuxi generate` we need to force prerendering
229
- forcePrerender: !nuxt.options.dev && nuxt.options._generate,
230
- siteUrl,
352
+ enabled: true,
231
353
  defaults: {
232
- component: "OgImageBasic",
354
+ component: "OgImageTemplateFallback",
233
355
  width: 1200,
234
- height: 630
356
+ height: 630,
357
+ // default is to cache the image for 24 hours
358
+ cache: true,
359
+ cacheTtl: 24 * 60 * 60 * 1e3
235
360
  },
236
- satoriProvider: true,
237
- browserProvider: true,
361
+ componentDirs: ["OgImage", "OgImageTemplate"],
362
+ runtimeSatori: true,
363
+ runtimeBrowser: nuxt.options.dev,
238
364
  fonts: [],
365
+ runtimeCacheStorage: true,
239
366
  satoriOptions: {},
240
- experimentalInlineWasm: process.env.NITRO_PRESET === "netlify-edge" || nuxt.options.nitro.preset === "netlify-edge" || false,
241
- experimentalRuntimeBrowser: false,
242
- playground: process.env.NODE_ENV === "development" || nuxt.options.dev
367
+ playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
368
+ debug: false
243
369
  };
244
370
  },
245
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
+ }
246
378
  const { resolve } = createResolver(import.meta.url);
247
- 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
+ }
248
411
  if (!config.fonts.length)
249
412
  config.fonts = ["Inter:400", "Inter:700"];
250
413
  const distResolve = (p) => {
@@ -254,34 +417,27 @@ const module = defineNuxtModule({
254
417
  return resolve(`../dist/${p}`);
255
418
  };
256
419
  nuxt.options.experimental.componentIslands = true;
257
- addTemplate({
258
- filename: "nuxt-og-image.d.ts",
259
- getContents: () => {
260
- return `// Generated by nuxt-og-image
261
- interface NuxtOgImageNitroRules {
420
+ extendTypes("nuxt-og-image", () => {
421
+ return `interface NuxtOgImageNitroRules {
262
422
  ogImage?: false | Record<string, any>
263
423
  }
264
424
  declare module 'nitropack' {
265
425
  interface NitroRouteRules extends NuxtOgImageNitroRules {}
266
426
  interface NitroRouteConfig extends NuxtOgImageNitroRules {}
267
- }
268
- export {}
269
- `;
270
- }
271
- });
272
- nuxt.hooks.hook("prepare:types", ({ references }) => {
273
- references.push({ path: resolve(nuxt.options.buildDir, "nuxt-og-image.d.ts") });
427
+ }`;
274
428
  });
275
429
  addServerHandler({
276
430
  lazy: true,
277
431
  handler: resolve("./runtime/nitro/middleware/og.png")
278
432
  });
279
- ["html", "options", "svg", "vnode", "font"].forEach((type) => {
280
- addServerHandler({
281
- lazy: true,
282
- route: `/api/og-image-${type}`,
283
- handler: resolve(`./runtime/nitro/routes/${type}`)
284
- });
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
+ }
285
441
  });
286
442
  nuxt.hook("devtools:customTabs", (iframeTabs) => {
287
443
  iframeTabs.push({
@@ -310,7 +466,17 @@ export {}
310
466
  });
311
467
  }
312
468
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
313
- ["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}`;
314
480
  addImports({
315
481
  name,
316
482
  from: resolve("./runtime/composables/defineOgImage")
@@ -318,104 +484,198 @@ export {}
318
484
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
319
485
  });
320
486
  await addComponent({
321
- name: "OgImageBasic",
322
- filePath: resolve("./runtime/components/OgImageBasic.island.vue"),
487
+ name: "OgImageTemplateFallback",
488
+ filePath: resolve("./runtime/components/OgImageTemplate/Fallback.vue"),
323
489
  island: true
324
490
  });
325
- ["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) => {
326
501
  addComponent({
327
- name,
328
- filePath: resolve(`./runtime/components/${name}`)
502
+ global: hasNuxtModule("@nuxt/content"),
503
+ name: name === "index" ? "OgImage" : `OgImage${name}`,
504
+ filePath: resolve(`./runtime/components/OgImage/${name}`)
329
505
  });
330
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
+ });
331
529
  const runtimeDir = resolve("./runtime");
332
530
  nuxt.options.build.transpile.push(runtimeDir);
333
- const moduleAssetDir = resolve("./runtime/public-assets");
334
- const assetDirs = [
335
- resolve(nuxt.options.rootDir, nuxt.options.dir.public),
336
- moduleAssetDir
531
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
532
+ const customAssetDirs = [
533
+ // allows us to show custom error images
534
+ resolve("./runtime/public-assets")
337
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
+ }
338
546
  nuxt.hooks.hook("modules:done", async () => {
339
547
  nuxt.hooks.callHook("og-image:config", config);
340
- 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
+ };
341
577
  });
342
- const useSatoriWasm = provider === "stackblitz";
343
578
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
344
579
  nitroConfig.externals = defu(nitroConfig.externals || {}, {
345
580
  inline: [runtimeDir]
346
581
  });
347
- if (config.experimentalRuntimeBrowser) {
582
+ if (config.runtimeBrowser) {
348
583
  nitroConfig.alias = nitroConfig.alias || {};
349
584
  nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
350
585
  nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
351
586
  nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
352
587
  }
353
588
  nitroConfig.publicAssets = nitroConfig.publicAssets || [];
354
- nitroConfig.publicAssets.push({ dir: moduleAssetDir, maxAge: 31536e3 });
589
+ customAssetDirs.forEach((dir) => {
590
+ nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
591
+ });
355
592
  const providerPath = `${runtimeDir}/nitro/providers`;
356
- if (config.browserProvider) {
357
- nitroConfig.virtual["#nuxt-og-image/browser"] = nuxt.options.dev || config.experimentalRuntimeBrowser ? `
358
- import node from '${providerPath}/browser/node'
359
-
593
+ if (config.runtimeBrowser) {
594
+ nitroConfig.virtual["#nuxt-og-image/browser"] = `
595
+ let browser
360
596
  export default async function() {
361
- return node
362
- }
363
- ` : `export default async function() {
364
- return () => {}
597
+ browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
598
+ return browser
365
599
  }
366
600
  `;
367
601
  }
368
- if (config.satoriProvider) {
369
- nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${useSatoriWasm ? "webworker" : "node"}'
370
- 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() {
371
605
  return satori
372
606
  }`;
373
- nitroConfig.virtual["#nuxt-og-image/svg2png"] = `
374
- import svg2png from '${providerPath}/svg2png/universal'
375
- export default async function() {
376
- return svg2png
377
- }`;
607
+ nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
608
+ export default function() {
609
+ return png
610
+ }
611
+ `;
378
612
  }
379
613
  nitroConfig.virtual["#nuxt-og-image/provider"] = `
380
- import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'
381
- 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"))}'` : ""}
382
616
 
383
617
  export async function useProvider(provider) {
384
618
  if (provider === 'satori')
385
- return satori
619
+ return ${config.runtimeSatori ? "satori" : "null"}
386
620
  if (provider === 'browser')
387
- return browser
621
+ return ${config.runtimeBrowser ? "browser" : "null"}
622
+ return null
388
623
  }
389
624
  `;
390
625
  });
391
626
  nuxt.hooks.hook("nitro:init", async (nitro) => {
392
627
  let screenshotQueue = [];
393
628
  nitro.hooks.hook("compiled", async (_nitro) => {
394
- if (edgeProvidersSupported.includes(_nitro.options.preset)) {
395
- await copy(resolve("./runtime/public-assets/inter-latin-ext-400-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-400-normal.woff"));
396
- await copy(resolve("./runtime/public-assets/inter-latin-ext-700-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-700-normal.woff"));
397
- if (!config.experimentalInlineWasm) {
398
- await copy(resolve("./runtime/public-assets/svg2png.wasm"), resolve(_nitro.options.output.serverDir, "svg2png.wasm"));
399
- if (useSatoriWasm)
400
- 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;
401
648
  }
402
- const indexFile = resolve(_nitro.options.output.serverDir, _nitro.options.preset === "netlify-edge" ? "server.mjs" : "index.mjs");
403
- if (await pathExists(indexFile)) {
404
- const indexContents = (await readFile(indexFile, "utf-8")).replace(".cwd(),", '?.cwd || "/",');
405
- if (!config.experimentalInlineWasm) {
406
- await writeFile(
407
- indexFile,
408
- 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)')
409
- );
410
- } else {
411
- const svg2pngWasm = await readFile(resolve("./runtime/public-assets/svg2png.wasm"), "base64");
412
- const yogaWasm = await readFile(resolve("./runtime/public-assets/yoga.wasm"), "base64");
413
- await writeFile(
414
- indexFile,
415
- indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', `Buffer.from("${svg2pngWasm}", "base64")`).replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', `Buffer.from("${yogaWasm}", "base64")`)
416
- );
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;
417
675
  }
418
676
  }
677
+ if (updated)
678
+ await writeFile(path, contents, { encoding: "utf-8" });
419
679
  }
420
680
  });
421
681
  const _routeRulesMatcher = toRouteMatcher(
@@ -437,7 +697,7 @@ export async function useProvider(provider) {
437
697
  ...extractedOptions,
438
698
  ...routeRules.ogImage || {}
439
699
  };
440
- if ((nuxt.options._generate || entry.static) && entry.provider === "browser")
700
+ if ((nuxt.options._generate || entry.cache) && entry.provider === "browser")
441
701
  screenshotQueue.push(entry);
442
702
  });
443
703
  if (nuxt.options.dev)
@@ -485,14 +745,14 @@ export async function useProvider(provider) {
485
745
  });
486
746
  }
487
747
  if (entry.component)
488
- entry.html = await $fetch(entry.path, { baseURL: withBase(nuxt.options.app.baseURL, host) });
748
+ entry.html = await globalThis.$fetch(entry.path);
489
749
  }
490
750
  for (const k in screenshotQueue) {
491
751
  const entry = screenshotQueue[k];
492
752
  const start = Date.now();
493
753
  let hasError = false;
494
- const dirname = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
495
- 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");
496
756
  try {
497
757
  const imgBuffer = await screenshot(browser, {
498
758
  ...config.defaults || {},
@@ -500,7 +760,7 @@ export async function useProvider(provider) {
500
760
  host
501
761
  });
502
762
  try {
503
- await mkdirp(dirname);
763
+ await mkdirp(dirname2);
504
764
  } catch (e) {
505
765
  }
506
766
  await writeFile(filename, imgBuffer);