nuxt-og-image 2.0.0-beta.6 → 2.0.0-beta.61

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 (114) hide show
  1. package/README.md +311 -97
  2. package/dist/client/200.html +2 -2
  3. package/dist/client/404.html +2 -2
  4. package/dist/client/_nuxt/IconCSS.b41b9663.css +1 -0
  5. package/dist/client/_nuxt/IconCSS.c2e73ab2.js +1 -0
  6. package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
  7. package/dist/client/_nuxt/ImageLoader.ed4bfccb.js +1 -0
  8. package/dist/client/_nuxt/entry.862302d7.css +1 -0
  9. package/dist/client/_nuxt/entry.f51ac6f7.js +7 -0
  10. package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.a94c6c21.js} +1 -1
  11. package/dist/client/_nuxt/error-404.f3dd5020.css +1 -0
  12. package/dist/client/_nuxt/error-500.06915589.css +1 -0
  13. package/dist/client/_nuxt/{error-500.f7d30da5.js → error-500.3955092a.js} +1 -1
  14. package/dist/client/_nuxt/index.17db8b1a.js +1 -0
  15. package/dist/client/_nuxt/index.403133d8.css +1 -0
  16. package/dist/client/_nuxt/options.3e6c211b.js +1 -0
  17. package/dist/client/_nuxt/png.23a5e57c.js +1 -0
  18. package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.2980d306.js} +1 -1
  19. package/dist/client/_nuxt/svg.a8bba1f1.js +1 -0
  20. package/dist/client/_nuxt/{vnodes.a799f183.js → vnodes.64d635d6.js} +1 -1
  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 +402 -141
  29. package/dist/runtime/browserUtil.d.ts +1 -0
  30. package/dist/runtime/browserUtil.mjs +10 -5
  31. package/dist/runtime/components/{OgImageDynamic.d.ts → OgImage/Cached.d.ts} +2 -2
  32. package/dist/runtime/components/OgImage/Cached.mjs +10 -0
  33. package/dist/runtime/components/{OgImageScreenshot.d.ts → OgImage/Screenshot.d.ts} +2 -2
  34. package/dist/runtime/components/{OgImageScreenshot.mjs → OgImage/Screenshot.mjs} +2 -2
  35. package/dist/runtime/components/OgImage/WithoutCache.d.ts +4 -0
  36. package/dist/runtime/components/OgImage/WithoutCache.mjs +10 -0
  37. package/dist/runtime/components/OgImage/_OgImageDynamic.d.ts +8 -0
  38. package/dist/runtime/components/{OgImageDynamic.mjs → OgImage/_OgImageDynamic.mjs} +3 -3
  39. package/dist/runtime/components/OgImage/_OgImageStatic.d.ts +8 -0
  40. package/dist/runtime/components/{OgImageStatic.mjs → OgImage/_OgImageStatic.mjs} +3 -3
  41. package/dist/runtime/components/{OgImageStatic.d.ts → OgImage/index.d.ts} +2 -2
  42. package/dist/runtime/components/OgImage/index.mjs +10 -0
  43. package/dist/runtime/components/OgImageTemplate/Fallback.vue +155 -0
  44. package/dist/runtime/composables/defineOgImage.d.ts +12 -4
  45. package/dist/runtime/composables/defineOgImage.mjs +46 -20
  46. package/dist/runtime/nitro/middleware/og.png.mjs +54 -8
  47. package/dist/runtime/nitro/middleware/playground.mjs +4 -3
  48. package/dist/runtime/nitro/plugins/prerender.d.ts +3 -0
  49. package/dist/runtime/nitro/plugins/prerender.mjs +28 -0
  50. package/dist/runtime/nitro/providers/browser/lambda.d.ts +1 -1
  51. package/dist/runtime/nitro/providers/browser/lambda.mjs +3 -3
  52. package/dist/runtime/nitro/providers/browser/{node.mjs → playwright.mjs} +0 -9
  53. package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -0
  54. package/dist/runtime/nitro/providers/browser/universal.mjs +33 -0
  55. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +4 -0
  56. package/dist/runtime/nitro/providers/png/resvg-node.mjs +6 -0
  57. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +3 -0
  58. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +11 -0
  59. package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +2 -3
  60. package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
  61. package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +2 -3
  62. package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
  63. package/dist/runtime/nitro/renderers/browser.d.ts +2 -2
  64. package/dist/runtime/nitro/renderers/browser.mjs +14 -10
  65. package/dist/runtime/nitro/renderers/satori/index.d.ts +2 -2
  66. package/dist/runtime/nitro/renderers/satori/index.mjs +27 -32
  67. package/dist/runtime/nitro/renderers/satori/plugins/emojis.d.ts +1 -1
  68. package/dist/runtime/nitro/renderers/satori/plugins/emojis.mjs +19 -6
  69. package/dist/runtime/nitro/renderers/satori/plugins/encoding.d.ts +1 -1
  70. package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +6 -7
  71. package/dist/runtime/nitro/renderers/satori/plugins/flex.d.ts +1 -1
  72. package/dist/runtime/nitro/renderers/satori/plugins/flex.mjs +8 -10
  73. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.d.ts +1 -1
  74. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +45 -13
  75. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.d.ts +1 -1
  76. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.mjs +5 -7
  77. package/dist/runtime/nitro/renderers/satori/utils.d.ts +4 -5
  78. package/dist/runtime/nitro/renderers/satori/utils.mjs +28 -17
  79. package/dist/runtime/nitro/routes/debug.d.ts +8 -0
  80. package/dist/runtime/nitro/routes/debug.mjs +14 -0
  81. package/dist/runtime/nitro/routes/font.mjs +2 -2
  82. package/dist/runtime/nitro/routes/html.mjs +100 -25
  83. package/dist/runtime/nitro/routes/options.d.ts +2 -2
  84. package/dist/runtime/nitro/routes/options.mjs +25 -22
  85. package/dist/runtime/nitro/routes/svg.mjs +5 -3
  86. package/dist/runtime/nitro/routes/vnode.mjs +5 -3
  87. package/dist/runtime/nitro/utils-pure.d.ts +3 -2
  88. package/dist/runtime/nitro/utils-pure.mjs +9 -10
  89. package/dist/runtime/nitro/utils.d.ts +11 -11
  90. package/dist/runtime/nitro/utils.mjs +68 -54
  91. package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
  92. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  93. package/dist/types.d.ts +6 -0
  94. package/package.json +38 -27
  95. package/dist/client/_nuxt/IconCSS.a041aca0.js +0 -1
  96. package/dist/client/_nuxt/ImageLoader.9bf39d71.js +0 -1
  97. package/dist/client/_nuxt/entry.74018bda.js +0 -5
  98. package/dist/client/_nuxt/entry.7a8c1ab2.css +0 -1
  99. package/dist/client/_nuxt/error-404.1469f10f.css +0 -1
  100. package/dist/client/_nuxt/error-500.92b94fae.css +0 -1
  101. package/dist/client/_nuxt/error-component.cf7543e5.js +0 -3
  102. package/dist/client/_nuxt/index.3f356409.js +0 -1
  103. package/dist/client/_nuxt/options.56a3e5f9.js +0 -1
  104. package/dist/client/_nuxt/png.37f3e77b.js +0 -1
  105. package/dist/client/_nuxt/svg.186c6bd1.js +0 -1
  106. package/dist/runtime/components/OgImageBasic.island.vue +0 -92
  107. package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
  108. /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
  109. /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
  110. /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
  111. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
  112. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
  113. /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
  114. /package/dist/runtime/{public-assets → public-assets-optional/yoga}/yoga.wasm +0 -0
package/dist/module.mjs CHANGED
@@ -1,29 +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, installModule, addServerHandler, addImports, addComponent, 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
- import { joinURL } from 'ufo';
8
- import { resolve, relative } from 'pathe';
7
+ import { withBase, joinURL } from 'ufo';
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';
12
+ import { globby } from 'globby';
13
+ import { updateSiteConfig, requireSiteConfig } from 'nuxt-site-config-kit';
13
14
  import playwrightCore from 'playwright-core';
14
15
  import { existsSync } from 'node:fs';
15
16
  import { createBirpcGroup } from 'birpc';
16
17
  import { stringify, parse } from 'flatted';
18
+ import { addDependency } from 'nypm';
19
+ import { provider } from 'std-env';
17
20
 
18
21
  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) {
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
+ }
27
32
  }
28
33
  try {
29
34
  return await playwrightCore.chromium.launch({
@@ -54,9 +59,9 @@ async function screenshot(browser, options) {
54
59
  width: options.width || 1200,
55
60
  height: options.height || 630
56
61
  });
57
- const isHtml = options.path.startsWith("html:") || options.html;
62
+ const isHtml = options.html || options.path?.startsWith("html:");
58
63
  if (isHtml) {
59
- const html = options.html || options.path.substring(5);
64
+ const html = options.html || options.path?.substring(5);
60
65
  await page.evaluate((html2) => {
61
66
  document.open("text/html");
62
67
  document.write(html2);
@@ -65,10 +70,13 @@ async function screenshot(browser, options) {
65
70
  await page.waitForLoadState("networkidle");
66
71
  } else {
67
72
  await page.goto(`${options.host}${options.path}`, {
68
- timeout: 1e4,
73
+ timeout: process.env.prerender || process.dev ? 1e4 : 3500,
69
74
  waitUntil: "networkidle"
70
75
  });
71
76
  }
77
+ const screenshotOptions = {
78
+ timeout: process.env.prerender || process.dev ? 1e4 : 3500
79
+ };
72
80
  if (options.delay)
73
81
  await page.waitForTimeout(options.delay);
74
82
  if (options.mask) {
@@ -78,8 +86,10 @@ async function screenshot(browser, options) {
78
86
  }, options.mask);
79
87
  }
80
88
  if (options.selector)
81
- return await page.locator(options.selector).screenshot();
82
- return await page.screenshot();
89
+ return await page.locator(options.selector).screenshot(screenshotOptions);
90
+ const screenshot2 = await page.screenshot(screenshotOptions);
91
+ await page.close();
92
+ return screenshot2;
83
93
  }
84
94
 
85
95
  function setupPlaygroundRPC(nuxt, config) {
@@ -113,7 +123,7 @@ function setupPlaygroundRPC(nuxt, config) {
113
123
  const birpc = createBirpcGroup(serverFunctions, []);
114
124
  nuxt.hook("builder:watch", (e, path) => {
115
125
  if (e === "change")
116
- birpc.boardcast.refresh.asEvent(path);
126
+ birpc.broadcast.refresh.asEvent(path);
117
127
  });
118
128
  const middleware = async (req, res) => {
119
129
  if (req.ws) {
@@ -164,13 +174,15 @@ function getBodyJson(req) {
164
174
  });
165
175
  }
166
176
 
167
- function decodeHtmlEntities(obj) {
177
+ function decodeHtml(html) {
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) => {
179
+ return String.fromCharCode(Number.parseInt(int));
180
+ });
181
+ }
182
+ function decodeObjectHtmlEntities(obj) {
168
183
  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(/&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) => {
171
- return String.fromCharCode(parseInt(int));
172
- });
173
- }
184
+ if (typeof value === "string")
185
+ obj[key] = decodeHtml(value);
174
186
  });
175
187
  return obj;
176
188
  }
@@ -194,54 +206,208 @@ function extractOgImageOptions(html) {
194
206
  else
195
207
  options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
196
208
  }
197
- return decodeHtmlEntities(options);
209
+ return decodeObjectHtmlEntities(options);
198
210
  }
199
211
  return false;
200
212
  }
201
- function stripOgImageOptions(html) {
202
- 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
+ });
203
336
  }
204
337
 
205
338
  const PATH = "/__nuxt_og_image__";
206
339
  const PATH_ENTRY = `${PATH}/entry`;
207
340
  const PATH_PLAYGROUND = `${PATH}/client`;
208
- const edgeProvidersSupported = [
209
- "cloudflare",
210
- "vercel-edge",
211
- "netlify-edge"
212
- ];
213
341
  const module = defineNuxtModule({
214
342
  meta: {
215
343
  name: "nuxt-og-image",
216
344
  compatibility: {
217
- nuxt: "^3.3.1",
345
+ nuxt: "^3.6.1",
218
346
  bridge: false
219
347
  },
220
348
  configKey: "ogImage"
221
349
  },
222
350
  defaults(nuxt) {
223
- const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.NUXT_SITE_URL || nuxt.options.runtimeConfig.public?.siteUrl || nuxt.options.runtimeConfig.siteUrl;
224
351
  return {
225
- // when we run `nuxi generate` we need to force prerendering
226
- forcePrerender: !nuxt.options.dev && nuxt.options._generate,
227
- siteUrl,
352
+ enabled: true,
228
353
  defaults: {
229
- component: "OgImageBasic",
354
+ component: "OgImageTemplateFallback",
230
355
  width: 1200,
231
- height: 630
356
+ height: 630,
357
+ // default is to cache the image for 24 hours
358
+ cache: true,
359
+ cacheTtl: 24 * 60 * 60 * 1e3
232
360
  },
233
- satoriProvider: true,
234
- browserProvider: true,
361
+ componentDirs: ["OgImage", "OgImageTemplate"],
362
+ runtimeSatori: true,
363
+ runtimeBrowser: nuxt.options.dev,
235
364
  fonts: [],
365
+ runtimeCacheStorage: true,
236
366
  satoriOptions: {},
237
- experimentalInlineWasm: process.env.NITRO_PRESET === "netlify-edge" || nuxt.options.nitro.preset === "netlify-edge" || false,
238
- experimentalRuntimeBrowser: false,
239
- playground: process.env.NODE_ENV === "development" || nuxt.options.dev
367
+ playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
368
+ debug: false
240
369
  };
241
370
  },
242
371
  async setup(config, nuxt) {
243
- const { resolve } = createResolver(import.meta.url);
244
- config.siteUrl = config.siteUrl || config.host;
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
+ }
378
+ const { resolve, resolvePath } = createResolver(import.meta.url);
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 installModule(await resolvePath("nuxt-site-config"));
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
+ }
245
411
  if (!config.fonts.length)
246
412
  config.fonts = ["Inter:400", "Inter:700"];
247
413
  const distResolve = (p) => {
@@ -251,34 +417,27 @@ const module = defineNuxtModule({
251
417
  return resolve(`../dist/${p}`);
252
418
  };
253
419
  nuxt.options.experimental.componentIslands = true;
254
- addTemplate({
255
- filename: "nuxt-og-image.d.ts",
256
- getContents: () => {
257
- return `// Generated by nuxt-og-image
258
- interface NuxtOgImageNitroRules {
420
+ extendTypes("nuxt-og-image", () => {
421
+ return `interface NuxtOgImageNitroRules {
259
422
  ogImage?: false | Record<string, any>
260
423
  }
261
424
  declare module 'nitropack' {
262
425
  interface NitroRouteRules extends NuxtOgImageNitroRules {}
263
426
  interface NitroRouteConfig extends NuxtOgImageNitroRules {}
264
- }
265
- export {}
266
- `;
267
- }
268
- });
269
- nuxt.hooks.hook("prepare:types", ({ references }) => {
270
- references.push({ path: resolve(nuxt.options.buildDir, "nuxt-og-image.d.ts") });
427
+ }`;
271
428
  });
272
429
  addServerHandler({
273
430
  lazy: true,
274
431
  handler: resolve("./runtime/nitro/middleware/og.png")
275
432
  });
276
- ["html", "options", "svg", "vnode", "font"].forEach((type) => {
277
- addServerHandler({
278
- lazy: true,
279
- route: `/api/og-image-${type}`,
280
- handler: resolve(`./runtime/nitro/routes/${type}`)
281
- });
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
+ }
282
441
  });
283
442
  nuxt.hook("devtools:customTabs", (iframeTabs) => {
284
443
  iframeTabs.push({
@@ -307,7 +466,17 @@ export {}
307
466
  });
308
467
  }
309
468
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
310
- ["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}`;
311
480
  addImports({
312
481
  name,
313
482
  from: resolve("./runtime/composables/defineOgImage")
@@ -315,104 +484,197 @@ export {}
315
484
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
316
485
  });
317
486
  await addComponent({
318
- name: "OgImageBasic",
319
- filePath: resolve("./runtime/components/OgImageBasic.island.vue"),
487
+ name: "OgImageTemplateFallback",
488
+ filePath: resolve("./runtime/components/OgImageTemplate/Fallback.vue"),
320
489
  island: true
321
490
  });
322
- ["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) => {
323
501
  addComponent({
324
- name,
325
- filePath: resolve(`./runtime/components/${name}`)
502
+ name: name === "index" ? "OgImage" : `OgImage${name}`,
503
+ filePath: resolve(`./runtime/components/OgImage/${name}`)
326
504
  });
327
505
  });
506
+ const ogImageComponents = [];
507
+ nuxt.hook("components:extend", (components) => {
508
+ components.forEach((component) => {
509
+ let valid = false;
510
+ config.componentDirs.forEach((dir) => {
511
+ if (component.pascalName.startsWith(dir) || component.kebabName.startsWith(dir))
512
+ valid = true;
513
+ });
514
+ if (valid) {
515
+ component.island = true;
516
+ component.mode = "server";
517
+ ogImageComponents.push({ pascalName: component.pascalName, kebabName: component.kebabName });
518
+ }
519
+ });
520
+ });
521
+ addTemplate({
522
+ filename: "og-image-component-names.mjs",
523
+ getContents() {
524
+ return `export const componentNames = ${JSON.stringify(ogImageComponents)}`;
525
+ },
526
+ options: { mode: "server" }
527
+ });
328
528
  const runtimeDir = resolve("./runtime");
329
529
  nuxt.options.build.transpile.push(runtimeDir);
330
- const moduleAssetDir = resolve("./runtime/public-assets");
331
- const assetDirs = [
332
- resolve(nuxt.options.rootDir, nuxt.options.dir.public),
333
- moduleAssetDir
530
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
531
+ const customAssetDirs = [
532
+ // allows us to show custom error images
533
+ resolve("./runtime/public-assets")
334
534
  ];
535
+ if (config.runtimeSatori) {
536
+ if (config.fonts.includes("Inter:400"))
537
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/inter-font"));
538
+ if (nitroCompatibility.png === "resvg-wasm" && nitroCompatibility.wasm === "fetch")
539
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/resvg"));
540
+ else if (nitroCompatibility.png === "svg2png" && nitroCompatibility.wasm === "fetch")
541
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/svg2png"));
542
+ if (nitroCompatibility.satori === "yoga-wasm")
543
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/yoga"));
544
+ }
335
545
  nuxt.hooks.hook("modules:done", async () => {
336
546
  nuxt.hooks.callHook("og-image:config", config);
337
- nuxt.options.runtimeConfig["nuxt-og-image"] = { ...config, assetDirs };
547
+ nuxt.options.runtimeConfig["nuxt-og-image"] = {
548
+ satoriOptions: config.satoriOptions,
549
+ runtimeSatori: config.runtimeSatori,
550
+ runtimeBrowser: config.runtimeBrowser,
551
+ // @ts-expect-error runtime type
552
+ defaults: config.defaults,
553
+ // avoid adding credentials
554
+ runtimeCacheStorage: typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver,
555
+ // convert the fonts to uniform type to fix ts issue
556
+ fonts: config.fonts.map((f) => {
557
+ if (typeof f === "string") {
558
+ const [name, weight] = f.split(":");
559
+ return {
560
+ name,
561
+ weight
562
+ };
563
+ }
564
+ return f;
565
+ }),
566
+ assetDirs: [
567
+ resolve(nuxt.options.srcDir, nuxt.options.dir.public),
568
+ ...customAssetDirs,
569
+ // always add runtime dirs for prerendering to work, these are just used as scan roots
570
+ resolve("./runtime/public-assets-optional/inter-font"),
571
+ resolve("./runtime/public-assets-optional/resvg"),
572
+ resolve("./runtime/public-assets-optional/yoga"),
573
+ resolve("./runtime/public-assets-optional/svg2png")
574
+ ]
575
+ };
338
576
  });
339
- const useSatoriWasm = provider === "stackblitz";
340
577
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
341
578
  nitroConfig.externals = defu(nitroConfig.externals || {}, {
342
579
  inline: [runtimeDir]
343
580
  });
344
- if (config.experimentalRuntimeBrowser) {
581
+ if (config.runtimeBrowser) {
345
582
  nitroConfig.alias = nitroConfig.alias || {};
346
583
  nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
347
584
  nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
348
585
  nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
349
586
  }
350
587
  nitroConfig.publicAssets = nitroConfig.publicAssets || [];
351
- nitroConfig.publicAssets.push({ dir: moduleAssetDir, maxAge: 31536e3 });
588
+ customAssetDirs.forEach((dir) => {
589
+ nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
590
+ });
352
591
  const providerPath = `${runtimeDir}/nitro/providers`;
353
- if (config.browserProvider) {
354
- nitroConfig.virtual["#nuxt-og-image/browser"] = nuxt.options.dev || config.experimentalRuntimeBrowser ? `
355
- import node from '${providerPath}/browser/node'
356
-
592
+ if (config.runtimeBrowser) {
593
+ nitroConfig.virtual["#nuxt-og-image/browser"] = `
594
+ let browser
357
595
  export default async function() {
358
- return node
359
- }
360
- ` : `export default async function() {
361
- return () => {}
596
+ browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
597
+ return browser
362
598
  }
363
599
  `;
364
600
  }
365
- if (config.satoriProvider) {
366
- nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${useSatoriWasm ? "webworker" : "node"}'
367
- export default async function() {
601
+ if (config.runtimeSatori) {
602
+ nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
603
+ export default function() {
368
604
  return satori
369
605
  }`;
370
- nitroConfig.virtual["#nuxt-og-image/svg2png"] = `
371
- import svg2png from '${providerPath}/svg2png/universal'
372
- export default async function() {
373
- return svg2png
374
- }`;
606
+ nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
607
+ export default function() {
608
+ return png
609
+ }
610
+ `;
375
611
  }
376
612
  nitroConfig.virtual["#nuxt-og-image/provider"] = `
377
- import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'
378
- import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'
613
+ ${config.runtimeSatori ? `import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'` : ""}
614
+ ${config.runtimeBrowser ? `import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'` : ""}
379
615
 
380
616
  export async function useProvider(provider) {
381
617
  if (provider === 'satori')
382
- return satori
618
+ return ${config.runtimeSatori ? "satori" : "null"}
383
619
  if (provider === 'browser')
384
- return browser
620
+ return ${config.runtimeBrowser ? "browser" : "null"}
621
+ return null
385
622
  }
386
623
  `;
387
624
  });
388
625
  nuxt.hooks.hook("nitro:init", async (nitro) => {
389
626
  let screenshotQueue = [];
390
627
  nitro.hooks.hook("compiled", async (_nitro) => {
391
- if (edgeProvidersSupported.includes(_nitro.options.preset)) {
392
- await copy(resolve("./runtime/public-assets/inter-latin-ext-400-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-400-normal.woff"));
393
- await copy(resolve("./runtime/public-assets/inter-latin-ext-700-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-700-normal.woff"));
394
- if (!config.experimentalInlineWasm) {
395
- await copy(resolve("./runtime/public-assets/svg2png.wasm"), resolve(_nitro.options.output.serverDir, "svg2png.wasm"));
396
- if (useSatoriWasm)
397
- await copy(resolve("./runtime/public-assets/yoga.wasm"), resolve(_nitro.options.output.serverDir, "yoga.wasm"));
628
+ if (!config.runtimeSatori || nuxt.options.dev)
629
+ return;
630
+ if (config.fonts.includes("Inter:400"))
631
+ 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"));
632
+ if (config.fonts.includes("Inter:700"))
633
+ 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"));
634
+ const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
635
+ const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
636
+ const paths = [wasmProviderPath];
637
+ const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
638
+ paths.push(...chunks);
639
+ for (const path of paths) {
640
+ if (!await pathExists(path))
641
+ continue;
642
+ let contents = await readFile(path, "utf-8");
643
+ let updated = false;
644
+ if (_nitro.options.preset.includes("vercel") && path === wasmProviderPath) {
645
+ contents = contents.replace(".cwd(),", '?.cwd || "/",');
646
+ updated = true;
398
647
  }
399
- const indexFile = resolve(_nitro.options.output.serverDir, _nitro.options.preset === "netlify-edge" ? "server.mjs" : "index.mjs");
400
- if (await pathExists(indexFile)) {
401
- const indexContents = (await readFile(indexFile, "utf-8")).replace(".cwd(),", '?.cwd || "/",');
402
- if (!config.experimentalInlineWasm) {
403
- await writeFile(
404
- indexFile,
405
- 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)')
406
- );
407
- } else {
408
- const svg2pngWasm = await readFile(resolve("./runtime/public-assets/svg2png.wasm"), "base64");
409
- const yogaWasm = await readFile(resolve("./runtime/public-assets/yoga.wasm"), "base64");
410
- await writeFile(
411
- indexFile,
412
- indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', `Buffer.from("${svg2pngWasm}", "base64")`).replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', `Buffer.from("${yogaWasm}", "base64")`)
413
- );
648
+ if (_nitro.options.preset.includes("netlify") && path.endsWith("netlify.mjs")) {
649
+ const match = "// TODO: handle event.isBase64Encoded\n });";
650
+ contents = contents.replace(match, `${match}
651
+
652
+ const headers = normalizeOutgoingHeaders(r.headers);
653
+ // image buffers must be base64 encoded
654
+ if (Buffer.isBuffer(r.body) && headers["content-type"].startsWith("image/")) {
655
+ return {
656
+ statusCode: r.status,
657
+ headers,
658
+ body: r.body.toString("base64"),
659
+ isBase64Encoded: true
660
+ };
661
+ }`);
662
+ updated = true;
663
+ }
664
+ for (const wasm of Wasms) {
665
+ if (contents.includes(wasm.placeholder)) {
666
+ if (nitroCompatibility.wasm === "import") {
667
+ contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
668
+ await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
669
+ } else if (nitroCompatibility.wasm === "inline") {
670
+ const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`));
671
+ contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
672
+ }
673
+ updated = true;
414
674
  }
415
675
  }
676
+ if (updated)
677
+ await writeFile(path, contents, { encoding: "utf-8" });
416
678
  }
417
679
  });
418
680
  const _routeRulesMatcher = toRouteMatcher(
@@ -425,7 +687,6 @@ export async function useProvider(provider) {
425
687
  if (!html)
426
688
  return;
427
689
  const extractedOptions = extractOgImageOptions(html);
428
- ctx.contents = stripOgImageOptions(html);
429
690
  const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
430
691
  if (!extractedOptions || routeRules.ogImage === false)
431
692
  return;
@@ -435,7 +696,7 @@ export async function useProvider(provider) {
435
696
  ...extractedOptions,
436
697
  ...routeRules.ogImage || {}
437
698
  };
438
- if ((nuxt.options._generate || entry.static) && entry.provider === "browser")
699
+ if ((nuxt.options._generate || entry.cache) && entry.provider === "browser")
439
700
  screenshotQueue.push(entry);
440
701
  });
441
702
  if (nuxt.options.dev)
@@ -444,21 +705,6 @@ export async function useProvider(provider) {
444
705
  await nuxt.callHook("og-image:prerenderScreenshots", screenshotQueue);
445
706
  if (screenshotQueue.length === 0)
446
707
  return;
447
- for (const entry of screenshotQueue) {
448
- if (entry.route && Object.keys(entry).length === 1) {
449
- const html = await $fetch(entry.route);
450
- const extractedOptions = extractOgImageOptions(html);
451
- const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
452
- Object.assign(entry, {
453
- // @ts-expect-error runtime
454
- path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
455
- ...extractedOptions,
456
- ...routeRules.ogImage || {}
457
- });
458
- }
459
- if (entry.component)
460
- entry.html = await $fetch(entry.path);
461
- }
462
708
  nitro.logger.info("Ensuring chromium install for og:image generation...");
463
709
  const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
464
710
  stdio: "inherit"
@@ -485,12 +731,27 @@ export async function useProvider(provider) {
485
731
  browser = await createBrowser();
486
732
  if (browser) {
487
733
  nitro.logger.info(`Prerendering ${screenshotQueue.length} og:image screenshots...`);
734
+ for (const entry of screenshotQueue) {
735
+ if (entry.route && Object.keys(entry).length === 1) {
736
+ const html = await $fetch(entry.route, { baseURL: withBase(nuxt.options.app.baseURL, host) });
737
+ const extractedOptions = extractOgImageOptions(html);
738
+ const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
739
+ Object.assign(entry, {
740
+ // @ts-expect-error runtime
741
+ path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
742
+ ...extractedOptions,
743
+ ...routeRules.ogImage || {}
744
+ });
745
+ }
746
+ if (entry.component)
747
+ entry.html = await globalThis.$fetch(entry.path);
748
+ }
488
749
  for (const k in screenshotQueue) {
489
750
  const entry = screenshotQueue[k];
490
751
  const start = Date.now();
491
752
  let hasError = false;
492
- const dirname = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
493
- const filename = joinURL(dirname, "/og.png");
753
+ const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
754
+ const filename = joinURL(dirname2, "/og.png");
494
755
  try {
495
756
  const imgBuffer = await screenshot(browser, {
496
757
  ...config.defaults || {},
@@ -498,7 +759,7 @@ export async function useProvider(provider) {
498
759
  host
499
760
  });
500
761
  try {
501
- await mkdirp(dirname);
762
+ await mkdirp(dirname2);
502
763
  } catch (e) {
503
764
  }
504
765
  await writeFile(filename, imgBuffer);
@@ -522,7 +783,7 @@ export async function useProvider(provider) {
522
783
  }
523
784
  screenshotQueue = [];
524
785
  };
525
- if (!nuxt.options._prepare) {
786
+ if (nuxt.options._generate) {
526
787
  nitro.hooks.hook("rollup:before", async () => {
527
788
  await captureScreenshots();
528
789
  });