nuxt-og-image 2.0.0-beta.9 → 2.0.1

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 (120) 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.685477c5.js +1 -0
  5. package/dist/client/_nuxt/IconCSS.b41b9663.css +1 -0
  6. package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
  7. package/dist/client/_nuxt/ImageLoader.fb4e5eb3.js +1 -0
  8. package/dist/client/_nuxt/entry.0fc037e0.js +143 -0
  9. package/dist/client/_nuxt/entry.1311cc29.css +1 -0
  10. package/dist/client/_nuxt/{error-404.02537f9e.js → error-404.068f19dd.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.4d72610e.js} +1 -1
  14. package/dist/client/_nuxt/index.33389497.js +1 -0
  15. package/dist/client/_nuxt/index.ffbea0a9.css +1 -0
  16. package/dist/client/_nuxt/options.e1a971ea.js +1 -0
  17. package/dist/client/_nuxt/png.110ab8f5.js +1 -0
  18. package/dist/client/_nuxt/{shiki.aace8ca2.js → shiki.3fe159de.js} +1 -1
  19. package/dist/client/_nuxt/svg.1a58cdf3.js +1 -0
  20. package/dist/client/_nuxt/vnodes.42fbc6e6.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 +117 -11
  27. package/dist/module.json +2 -2
  28. package/dist/module.mjs +391 -114
  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/inline-css/mock.d.ts +5 -0
  57. package/dist/runtime/nitro/providers/inline-css/mock.mjs +3 -0
  58. package/dist/runtime/nitro/providers/inline-css/node.d.ts +5 -0
  59. package/dist/runtime/nitro/providers/inline-css/node.mjs +11 -0
  60. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +4 -0
  61. package/dist/runtime/nitro/providers/png/resvg-node.mjs +6 -0
  62. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +3 -0
  63. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +11 -0
  64. package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +2 -3
  65. package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
  66. package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +2 -3
  67. package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
  68. package/dist/runtime/nitro/renderers/browser.d.ts +2 -2
  69. package/dist/runtime/nitro/renderers/browser.mjs +14 -12
  70. package/dist/runtime/nitro/renderers/satori/index.d.ts +2 -2
  71. package/dist/runtime/nitro/renderers/satori/index.mjs +27 -32
  72. package/dist/runtime/nitro/renderers/satori/plugins/emojis.d.ts +1 -1
  73. package/dist/runtime/nitro/renderers/satori/plugins/emojis.mjs +19 -6
  74. package/dist/runtime/nitro/renderers/satori/plugins/encoding.d.ts +1 -1
  75. package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +5 -7
  76. package/dist/runtime/nitro/renderers/satori/plugins/flex.d.ts +1 -1
  77. package/dist/runtime/nitro/renderers/satori/plugins/flex.mjs +8 -10
  78. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.d.ts +1 -1
  79. package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +45 -13
  80. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.d.ts +1 -1
  81. package/dist/runtime/nitro/renderers/satori/plugins/twClasses.mjs +5 -7
  82. package/dist/runtime/nitro/renderers/satori/utils.d.ts +4 -5
  83. package/dist/runtime/nitro/renderers/satori/utils.mjs +28 -17
  84. package/dist/runtime/nitro/routes/debug.d.ts +8 -0
  85. package/dist/runtime/nitro/routes/debug.mjs +14 -0
  86. package/dist/runtime/nitro/routes/font.mjs +2 -2
  87. package/dist/runtime/nitro/routes/html.mjs +102 -26
  88. package/dist/runtime/nitro/routes/options.d.ts +2 -2
  89. package/dist/runtime/nitro/routes/options.mjs +21 -20
  90. package/dist/runtime/nitro/routes/svg.mjs +2 -2
  91. package/dist/runtime/nitro/routes/vnode.mjs +2 -2
  92. package/dist/runtime/nitro/utils-pure.d.ts +0 -1
  93. package/dist/runtime/nitro/utils-pure.mjs +1 -4
  94. package/dist/runtime/nitro/utils.d.ts +11 -11
  95. package/dist/runtime/nitro/utils.mjs +67 -53
  96. package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
  97. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  98. package/dist/types.d.ts +6 -0
  99. package/package.json +38 -27
  100. package/dist/client/_nuxt/IconCSS.ef0613e7.js +0 -1
  101. package/dist/client/_nuxt/ImageLoader.a4418cab.js +0 -1
  102. package/dist/client/_nuxt/entry.09f25aaf.css +0 -1
  103. package/dist/client/_nuxt/entry.fc9150b0.js +0 -5
  104. package/dist/client/_nuxt/error-404.1469f10f.css +0 -1
  105. package/dist/client/_nuxt/error-500.92b94fae.css +0 -1
  106. package/dist/client/_nuxt/error-component.8148b615.js +0 -3
  107. package/dist/client/_nuxt/index.80f38ec7.js +0 -1
  108. package/dist/client/_nuxt/options.cc3fd02b.js +0 -1
  109. package/dist/client/_nuxt/png.62758167.js +0 -1
  110. package/dist/client/_nuxt/svg.853cdaad.js +0 -1
  111. package/dist/client/_nuxt/vnodes.69b24963.js +0 -1
  112. package/dist/runtime/components/OgImageBasic.island.vue +0 -92
  113. package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
  114. /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
  115. /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
  116. /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
  117. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
  118. /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
  119. /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
  120. /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 { globby } from 'globby';
13
+ import { installNuxtSiteConfig, updateSiteConfig, requireSiteConfig } from 'nuxt-site-config-kit';
12
14
  import { provider } from 'std-env';
13
- import { $fetch } from 'ofetch';
14
15
  import playwrightCore from 'playwright-core';
15
16
  import { existsSync } from 'node:fs';
16
17
  import { createBirpcGroup } from 'birpc';
17
18
  import { stringify, parse } from 'flatted';
19
+ import { addDependency } from 'nypm';
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,213 @@ 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
+ node: true
241
+ };
242
+ const RuntimeCompatibility = {
243
+ "nitro-dev": {
244
+ wasm: "fetch",
245
+ browser: "universal"
246
+ },
247
+ "stackblitz": {
248
+ browser: false,
249
+ satori: "yoga-wasm",
250
+ wasm: "inline",
251
+ png: "resvg-wasm"
252
+ },
253
+ "netlify": {
254
+ browser: "lambda",
255
+ wasm: "inline"
256
+ },
257
+ "netlify-edge": {
258
+ wasm: "inline",
259
+ png: "resvg-wasm",
260
+ node: false
261
+ },
262
+ "vercel": {
263
+ // exceeds 50mb limit
264
+ browser: false
265
+ },
266
+ "vercel-edge": {
267
+ browser: false,
268
+ wasm: "import",
269
+ wasmImportQuery: "?module",
270
+ png: "resvg-wasm",
271
+ node: false
272
+ },
273
+ "cloudflare-pages": {
274
+ browser: false,
275
+ wasm: "import",
276
+ png: "resvg-wasm",
277
+ node: false
278
+ },
279
+ "cloudflare": {
280
+ browser: false,
281
+ wasm: "import"
282
+ }
283
+ };
284
+
285
+ const autodetectableProviders = {
286
+ azure_static: "azure",
287
+ cloudflare_pages: "cloudflare-pages",
288
+ netlify: "netlify",
289
+ stormkit: "stormkit",
290
+ vercel: "vercel",
291
+ cleavr: "cleavr",
292
+ stackblitz: "stackblitz"
293
+ };
294
+ const autodetectableStaticProviders = {
295
+ netlify: "netlify-static",
296
+ vercel: "vercel-static"
297
+ };
298
+ function detectTarget(options = {}) {
299
+ return options?.static ? autodetectableStaticProviders[provider] : autodetectableProviders[provider];
300
+ }
301
+ function getNitroPreset(nuxt = useNuxt()) {
302
+ return process.env.NITRO_PRESET || nuxt.options.nitro.preset || detectTarget() || "node-server";
303
+ }
304
+ function getNitroProviderCompatibility(defaults, nuxt = useNuxt()) {
305
+ let compatibility;
306
+ if (provider === "stackblitz") {
307
+ compatibility = RuntimeCompatibility.stackblitz;
308
+ } else if (nuxt.options.dev || nuxt.options._prepare || nuxt.options._generate) {
309
+ compatibility = {
310
+ wasm: "fetch",
311
+ browser: "universal"
312
+ };
313
+ } else {
314
+ const target = getNitroPreset(nuxt);
315
+ const lookup = RuntimeCompatibility[target];
316
+ if (lookup === false)
317
+ return false;
318
+ compatibility = lookup || {};
319
+ }
320
+ compatibility = defu(compatibility, defaults);
321
+ compatibility.inlineCss = compatibility.inlineCss || (compatibility.node ? "node" : "mock");
322
+ return compatibility;
323
+ }
324
+ function ensureDependencies(nuxt, dep) {
325
+ return Promise.all(dep.map((d) => {
326
+ return addDependency(d, { cwd: nuxt.options.rootDir });
327
+ }));
328
+ }
329
+
330
+ function extendTypes(module, template) {
331
+ const nuxt = useNuxt();
332
+ addTemplate({
333
+ filename: `${module}.d.ts`,
334
+ getContents: () => {
335
+ const s = template();
336
+ return `// Generated by ${module}
337
+ ${s}
338
+ export {}
339
+ `;
340
+ }
341
+ });
342
+ nuxt.hooks.hook("prepare:types", ({ references }) => {
343
+ references.push({ path: resolve(nuxt.options.buildDir, `${module}.d.ts`) });
344
+ });
345
+ }
346
+
208
347
  const PATH = "/__nuxt_og_image__";
209
348
  const PATH_ENTRY = `${PATH}/entry`;
210
349
  const PATH_PLAYGROUND = `${PATH}/client`;
211
- const edgeProvidersSupported = [
212
- "cloudflare",
213
- "vercel-edge",
214
- "netlify-edge"
215
- ];
216
350
  const module = defineNuxtModule({
217
351
  meta: {
218
352
  name: "nuxt-og-image",
219
353
  compatibility: {
220
- nuxt: "^3.3.1",
354
+ nuxt: "^3.6.1",
221
355
  bridge: false
222
356
  },
223
357
  configKey: "ogImage"
224
358
  },
225
359
  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
360
  return {
228
- // when we run `nuxi generate` we need to force prerendering
229
- forcePrerender: !nuxt.options.dev && nuxt.options._generate,
230
- siteUrl,
361
+ enabled: true,
231
362
  defaults: {
232
- component: "OgImageBasic",
363
+ component: "OgImageTemplateFallback",
233
364
  width: 1200,
234
- height: 630
365
+ height: 630,
366
+ // default is to cache the image for 24 hours
367
+ cache: true,
368
+ cacheTtl: 24 * 60 * 60 * 1e3
235
369
  },
236
- satoriProvider: true,
237
- browserProvider: true,
370
+ runtimeCompatibility: DefaultRuntimeCompatibility,
371
+ componentDirs: ["OgImage", "OgImageTemplate"],
372
+ runtimeSatori: true,
373
+ runtimeBrowser: nuxt.options.dev,
238
374
  fonts: [],
375
+ runtimeCacheStorage: true,
239
376
  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
377
+ playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
378
+ debug: false
243
379
  };
244
380
  },
245
381
  async setup(config, nuxt) {
382
+ const logger = useLogger("nuxt-og-image");
383
+ logger.level = config.debug || nuxt.options.debug ? 4 : 3;
384
+ if (config.enabled === false) {
385
+ logger.debug("The module is disabled, skipping setup.");
386
+ return;
387
+ }
246
388
  const { resolve } = createResolver(import.meta.url);
247
- config.siteUrl = config.siteUrl || config.host;
389
+ logger.debug("Using Nitro preset", getNitroPreset());
390
+ const nitroCompatibility = getNitroProviderCompatibility(config.runtimeCompatibility);
391
+ logger.debug("Nitro compatibility", nitroCompatibility);
392
+ const nitroTarget = process.env.NITRO_PRESET || nuxt.options.nitro.preset || provider;
393
+ if (!nitroCompatibility) {
394
+ logger.warn(`\`nuxt-og-image\` does not support the nitro preset \`${nitroTarget}\`. Please make an issue. `);
395
+ return;
396
+ }
397
+ if (!nitroCompatibility.browser && config.runtimeBrowser) {
398
+ config.runtimeBrowser = false;
399
+ logger.warn(`\`nuxt-og-image\` does not support the nitro target \`${nitroTarget}\` with the runtime browser. Set runtimeBrowser: false to stop seeing this.`);
400
+ }
401
+ if (config.runtimeBrowser && nitroCompatibility.browser === "lambda") {
402
+ logger.info(`\`nuxt-og-image\` is deploying to nitro target \`${nitroTarget}\` that installs extra dependencies.`);
403
+ await ensureDependencies(nuxt, ["puppeteer-core@14.1.1", "@sparticuz/chrome-aws-lambda@14.1.1"]);
404
+ }
405
+ await installNuxtSiteConfig();
406
+ await updateSiteConfig({
407
+ _context: "nuxt-og-image:config",
408
+ url: config.siteUrl || config.host
409
+ });
410
+ requireSiteConfig("nuxt-og-image", {
411
+ url: "Required to generate absolute URLs for the og:image."
412
+ }, { prerender: true });
413
+ nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
414
+ if (nuxt.options._generate) {
415
+ nuxt.options.nitro.storage["og-image"] = {
416
+ driver: "memory"
417
+ };
418
+ } else if (config.runtimeCacheStorage && !nuxt.options.dev && typeof config.runtimeCacheStorage === "object") {
419
+ nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
420
+ }
248
421
  if (!config.fonts.length)
249
422
  config.fonts = ["Inter:400", "Inter:700"];
250
423
  const distResolve = (p) => {
@@ -254,34 +427,27 @@ const module = defineNuxtModule({
254
427
  return resolve(`../dist/${p}`);
255
428
  };
256
429
  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 {
430
+ extendTypes("nuxt-og-image", () => {
431
+ return `interface NuxtOgImageNitroRules {
262
432
  ogImage?: false | Record<string, any>
263
433
  }
264
434
  declare module 'nitropack' {
265
435
  interface NitroRouteRules extends NuxtOgImageNitroRules {}
266
436
  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") });
437
+ }`;
274
438
  });
275
439
  addServerHandler({
276
440
  lazy: true,
277
441
  handler: resolve("./runtime/nitro/middleware/og.png")
278
442
  });
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
- });
443
+ ["html", "options", "svg", "vnode", "font", "debug"].forEach((type) => {
444
+ if (type !== "debug" || config.debug) {
445
+ addServerHandler({
446
+ lazy: true,
447
+ route: `/api/og-image-${type}`,
448
+ handler: resolve(`./runtime/nitro/routes/${type}`)
449
+ });
450
+ }
285
451
  });
286
452
  nuxt.hook("devtools:customTabs", (iframeTabs) => {
287
453
  iframeTabs.push({
@@ -310,7 +476,17 @@ export {}
310
476
  });
311
477
  }
312
478
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
313
- ["defineOgImageDynamic", "defineOgImageStatic", "defineOgImageScreenshot"].forEach((name) => {
479
+ [
480
+ // deprecated
481
+ "Dynamic",
482
+ "Static",
483
+ // new
484
+ "index",
485
+ "Cached",
486
+ "WithoutCache",
487
+ "Screenshot"
488
+ ].forEach((name) => {
489
+ name = name === "index" ? "defineOgImage" : `defineOgImage${name}`;
314
490
  addImports({
315
491
  name,
316
492
  from: resolve("./runtime/composables/defineOgImage")
@@ -318,104 +494,205 @@ export {}
318
494
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
319
495
  });
320
496
  await addComponent({
321
- name: "OgImageBasic",
322
- filePath: resolve("./runtime/components/OgImageBasic.island.vue"),
497
+ name: "OgImageTemplateFallback",
498
+ filePath: resolve("./runtime/components/OgImageTemplate/Fallback.vue"),
323
499
  island: true
324
500
  });
325
- ["OgImageStatic", "OgImageDynamic", "OgImageScreenshot"].forEach((name) => {
501
+ [
502
+ // deprecated
503
+ "Static",
504
+ "Dynamic",
505
+ // new
506
+ "index",
507
+ "Cached",
508
+ "WithoutCache",
509
+ "Screenshot"
510
+ ].forEach((name) => {
326
511
  addComponent({
327
- name,
328
- filePath: resolve(`./runtime/components/${name}`)
512
+ global: hasNuxtModule("@nuxt/content"),
513
+ name: name === "index" ? "OgImage" : `OgImage${name}`,
514
+ filePath: resolve(`./runtime/components/OgImage/${name}`)
515
+ });
516
+ });
517
+ const ogImageComponents = [];
518
+ nuxt.hook("components:extend", (components) => {
519
+ components.forEach((component) => {
520
+ let valid = false;
521
+ config.componentDirs.forEach((dir) => {
522
+ if (component.pascalName.startsWith(dir) || component.kebabName.startsWith(dir))
523
+ valid = true;
524
+ });
525
+ if (valid) {
526
+ component.island = true;
527
+ component.mode = "server";
528
+ ogImageComponents.push({ pascalName: component.pascalName, kebabName: component.kebabName });
529
+ }
329
530
  });
330
531
  });
532
+ addTemplate({
533
+ filename: "og-image-component-names.mjs",
534
+ getContents() {
535
+ return `export const componentNames = ${JSON.stringify(ogImageComponents)}`;
536
+ },
537
+ options: { mode: "server" }
538
+ });
331
539
  const runtimeDir = resolve("./runtime");
332
540
  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
541
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
542
+ const customAssetDirs = [
543
+ // allows us to show custom error images
544
+ resolve("./runtime/public-assets")
337
545
  ];
546
+ if (config.runtimeSatori) {
547
+ if (config.fonts.includes("Inter:400"))
548
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/inter-font"));
549
+ if (nitroCompatibility.png === "resvg-wasm" && nitroCompatibility.wasm === "fetch")
550
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/resvg"));
551
+ else if (nitroCompatibility.png === "svg2png" && nitroCompatibility.wasm === "fetch")
552
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/svg2png"));
553
+ if (nitroCompatibility.satori === "yoga-wasm")
554
+ customAssetDirs.push(resolve("./runtime/public-assets-optional/yoga"));
555
+ }
338
556
  nuxt.hooks.hook("modules:done", async () => {
339
557
  nuxt.hooks.callHook("og-image:config", config);
340
- nuxt.options.runtimeConfig["nuxt-og-image"] = { ...config, assetDirs };
558
+ nuxt.options.runtimeConfig["nuxt-og-image"] = {
559
+ satoriOptions: config.satoriOptions,
560
+ runtimeSatori: config.runtimeSatori,
561
+ runtimeBrowser: config.runtimeBrowser,
562
+ // @ts-expect-error runtime type
563
+ defaults: config.defaults,
564
+ // avoid adding credentials
565
+ runtimeCacheStorage: typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver,
566
+ // convert the fonts to uniform type to fix ts issue
567
+ fonts: config.fonts.map((f) => {
568
+ if (typeof f === "string") {
569
+ const [name, weight] = f.split(":");
570
+ return {
571
+ name,
572
+ weight
573
+ };
574
+ }
575
+ return f;
576
+ }),
577
+ assetDirs: [
578
+ resolve(nuxt.options.srcDir, nuxt.options.dir.public),
579
+ ...customAssetDirs,
580
+ // always add runtime dirs for prerendering to work, these are just used as scan roots
581
+ resolve("./runtime/public-assets-optional/inter-font"),
582
+ resolve("./runtime/public-assets-optional/resvg"),
583
+ resolve("./runtime/public-assets-optional/yoga"),
584
+ resolve("./runtime/public-assets-optional/svg2png")
585
+ ]
586
+ };
341
587
  });
342
- const useSatoriWasm = provider === "stackblitz";
343
588
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
344
589
  nitroConfig.externals = defu(nitroConfig.externals || {}, {
345
590
  inline: [runtimeDir]
346
591
  });
347
- if (config.experimentalRuntimeBrowser) {
592
+ if (config.runtimeBrowser) {
348
593
  nitroConfig.alias = nitroConfig.alias || {};
349
594
  nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
350
595
  nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
351
596
  nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
352
597
  }
598
+ if (nitroCompatibility.png === "resvg-wasm")
599
+ nitroConfig.alias["@resvg/resvg-js"] = "unenv/runtime/mock/proxy-cjs";
353
600
  nitroConfig.publicAssets = nitroConfig.publicAssets || [];
354
- nitroConfig.publicAssets.push({ dir: moduleAssetDir, maxAge: 31536e3 });
601
+ customAssetDirs.forEach((dir) => {
602
+ nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
603
+ });
355
604
  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
-
605
+ if (config.runtimeBrowser) {
606
+ nitroConfig.virtual["#nuxt-og-image/browser"] = `
607
+ let browser
360
608
  export default async function() {
361
- return node
362
- }
363
- ` : `export default async function() {
364
- return () => {}
609
+ browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
610
+ return browser
365
611
  }
366
612
  `;
367
613
  }
368
- if (config.satoriProvider) {
369
- nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${useSatoriWasm ? "webworker" : "node"}'
370
- export default async function() {
614
+ if (config.runtimeSatori) {
615
+ nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
616
+ export default function() {
371
617
  return satori
372
618
  }`;
373
- nitroConfig.virtual["#nuxt-og-image/svg2png"] = `
374
- import svg2png from '${providerPath}/svg2png/universal'
375
- export default async function() {
376
- return svg2png
377
- }`;
619
+ nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
620
+ export default function() {
621
+ return png
622
+ }
623
+ `;
624
+ nitroConfig.virtual["#nuxt-og-image/inline-css"] = `import inlineCss from '${providerPath}/inline-css/${nitroCompatibility.inlineCss || "mock"}'
625
+ export default function() {
626
+ return inlineCss
627
+ }
628
+ `;
378
629
  }
379
630
  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"))}'
631
+ ${config.runtimeSatori ? `import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'` : ""}
632
+ ${config.runtimeBrowser ? `import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'` : ""}
382
633
 
383
634
  export async function useProvider(provider) {
384
635
  if (provider === 'satori')
385
- return satori
636
+ return ${config.runtimeSatori ? "satori" : "null"}
386
637
  if (provider === 'browser')
387
- return browser
638
+ return ${config.runtimeBrowser ? "browser" : "null"}
639
+ return null
388
640
  }
389
641
  `;
390
642
  });
391
643
  nuxt.hooks.hook("nitro:init", async (nitro) => {
392
644
  let screenshotQueue = [];
393
645
  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"));
646
+ if (!config.runtimeSatori || nuxt.options.dev)
647
+ return;
648
+ if (config.fonts.includes("Inter:400"))
649
+ 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"));
650
+ if (config.fonts.includes("Inter:700"))
651
+ 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"));
652
+ const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
653
+ const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
654
+ const paths = [wasmProviderPath];
655
+ const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
656
+ paths.push(...chunks);
657
+ for (const path of paths) {
658
+ if (!await pathExists(path))
659
+ continue;
660
+ let contents = await readFile(path, "utf-8");
661
+ let updated = false;
662
+ if (_nitro.options.preset.includes("vercel") && path === wasmProviderPath) {
663
+ contents = contents.replace(".cwd(),", '?.cwd || "/",');
664
+ updated = true;
401
665
  }
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
- );
666
+ if (_nitro.options.preset.includes("netlify") && path.endsWith("netlify.mjs")) {
667
+ const match = "// TODO: handle event.isBase64Encoded\n });";
668
+ contents = contents.replace(match, `${match}
669
+
670
+ const headers = normalizeOutgoingHeaders(r.headers);
671
+ // image buffers must be base64 encoded
672
+ if (Buffer.isBuffer(r.body) && headers["content-type"].startsWith("image/")) {
673
+ return {
674
+ statusCode: r.status,
675
+ headers,
676
+ body: r.body.toString("base64"),
677
+ isBase64Encoded: true
678
+ };
679
+ }`);
680
+ updated = true;
681
+ }
682
+ for (const wasm of Wasms) {
683
+ if (contents.includes(wasm.placeholder)) {
684
+ if (nitroCompatibility.wasm === "import") {
685
+ contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
686
+ await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
687
+ } else if (nitroCompatibility.wasm === "inline") {
688
+ const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`));
689
+ contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
690
+ }
691
+ updated = true;
417
692
  }
418
693
  }
694
+ if (updated)
695
+ await writeFile(path, contents, { encoding: "utf-8" });
419
696
  }
420
697
  });
421
698
  const _routeRulesMatcher = toRouteMatcher(
@@ -437,7 +714,7 @@ export async function useProvider(provider) {
437
714
  ...extractedOptions,
438
715
  ...routeRules.ogImage || {}
439
716
  };
440
- if ((nuxt.options._generate || entry.static) && entry.provider === "browser")
717
+ if ((nuxt.options._generate || entry.cache) && entry.provider === "browser")
441
718
  screenshotQueue.push(entry);
442
719
  });
443
720
  if (nuxt.options.dev)
@@ -485,14 +762,14 @@ export async function useProvider(provider) {
485
762
  });
486
763
  }
487
764
  if (entry.component)
488
- entry.html = await $fetch(entry.path, { baseURL: withBase(nuxt.options.app.baseURL, host) });
765
+ entry.html = await globalThis.$fetch(entry.path);
489
766
  }
490
767
  for (const k in screenshotQueue) {
491
768
  const entry = screenshotQueue[k];
492
769
  const start = Date.now();
493
770
  let hasError = false;
494
- const dirname = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
495
- const filename = joinURL(dirname, "/og.png");
771
+ const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
772
+ const filename = joinURL(dirname2, "/og.png");
496
773
  try {
497
774
  const imgBuffer = await screenshot(browser, {
498
775
  ...config.defaults || {},
@@ -500,7 +777,7 @@ export async function useProvider(provider) {
500
777
  host
501
778
  });
502
779
  try {
503
- await mkdirp(dirname);
780
+ await mkdirp(dirname2);
504
781
  } catch (e) {
505
782
  }
506
783
  await writeFile(filename, imgBuffer);