nuxt-og-image 3.0.0-beta.9 → 3.0.0-rc.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 (143) hide show
  1. package/README.md +4 -4
  2. package/dist/client/200.html +6 -5
  3. package/dist/client/404.html +6 -5
  4. package/dist/client/_nuxt/IconCSS.9f95294e.js +1 -0
  5. package/dist/client/_nuxt/IconCSS.f0b56d3e.css +1 -0
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/01a72661-4cae-4637-8364-242cd0ee1a35.json +1 -0
  8. package/dist/client/_nuxt/entry.a30f63d0.css +1 -0
  9. package/dist/client/_nuxt/entry.c5e613a6.js +108 -0
  10. package/dist/client/_nuxt/{error-404.18456c20.js → error-404.e1564a51.js} +1 -1
  11. package/dist/client/_nuxt/{error-500.a3e12514.js → error-500.90b12af8.js} +1 -1
  12. package/dist/client/_nuxt/vanilla-picker-NKbIFE8h.23409a58.js +8 -0
  13. package/dist/client/index.html +6 -5
  14. package/dist/module.d.mts +63 -48
  15. package/dist/module.d.ts +63 -48
  16. package/dist/module.json +2 -2
  17. package/dist/module.mjs +339 -166
  18. package/dist/runtime/cache.d.ts +7 -10
  19. package/dist/runtime/cache.mjs +40 -27
  20. package/dist/runtime/components/OgImage/OgImage.d.ts +5 -0
  21. package/dist/runtime/components/OgImage/{index.mjs → OgImage.mjs} +1 -1
  22. package/dist/runtime/components/OgImage/OgImageScreenshot.d.ts +5 -0
  23. package/dist/runtime/components/OgImage/{Screenshot.mjs → OgImageScreenshot.mjs} +1 -1
  24. package/dist/runtime/components/Templates/{Official → Community}/BrandedLogo.vue +3 -2
  25. package/dist/runtime/components/Templates/Community/Nuxt.vue +6 -5
  26. package/dist/runtime/components/Templates/Community/NuxtSeo.vue +137 -0
  27. package/dist/runtime/components/Templates/Community/Pergel.vue +104 -0
  28. package/dist/runtime/components/Templates/{Official → Community}/SimpleBlog.vue +7 -5
  29. package/dist/runtime/components/Templates/Community/UnJs.vue +108 -0
  30. package/dist/runtime/components/Templates/{Official → Community}/Wave.vue +3 -2
  31. package/dist/runtime/components/Templates/{Official → Community}/WithEmoji.vue +3 -2
  32. package/dist/runtime/composables/defineOgImage.d.ts +2 -23
  33. package/dist/runtime/composables/defineOgImage.mjs +33 -117
  34. package/dist/runtime/composables/defineOgImageComponent.d.ts +3 -0
  35. package/dist/runtime/composables/defineOgImageComponent.mjs +8 -0
  36. package/dist/runtime/composables/defineOgImageScreenshot.d.ts +2 -0
  37. package/dist/runtime/composables/defineOgImageScreenshot.mjs +13 -0
  38. package/dist/runtime/core/bindings/css-inline/node.d.ts +2 -5
  39. package/dist/runtime/core/bindings/css-inline/node.mjs +2 -10
  40. package/dist/runtime/core/bindings/resvg/wasm-fs.d.ts +40 -0
  41. package/dist/runtime/core/bindings/resvg/wasm-fs.mjs +6 -0
  42. package/dist/runtime/core/bindings/resvg/wasm.mjs +2 -5
  43. package/dist/runtime/core/bindings/satori/wasm-fs.mjs +13 -0
  44. package/dist/runtime/core/bindings/satori/wasm.d.ts +6 -0
  45. package/dist/runtime/core/bindings/satori/wasm.mjs +14 -0
  46. package/dist/runtime/core/cache/emojis.d.ts +1 -0
  47. package/dist/runtime/core/cache/emojis.mjs +5 -0
  48. package/dist/runtime/core/cache/fonts.d.ts +3 -0
  49. package/dist/runtime/core/cache/fonts.mjs +6 -0
  50. package/dist/runtime/core/cache/htmlPayload.d.ts +5 -0
  51. package/dist/runtime/core/cache/htmlPayload.mjs +6 -0
  52. package/dist/runtime/core/cache/prerender.d.ts +1 -5
  53. package/dist/runtime/core/cache/prerender.mjs +1 -2
  54. package/dist/runtime/core/env/assets.d.ts +0 -1
  55. package/dist/runtime/core/env/assets.mjs +0 -4
  56. package/dist/runtime/core/font/fetch.d.ts +2 -3
  57. package/dist/runtime/core/font/fetch.mjs +26 -19
  58. package/dist/runtime/core/html/applyEmojis.d.ts +3 -0
  59. package/dist/runtime/core/html/applyEmojis.mjs +37 -0
  60. package/dist/runtime/core/html/applyInlineCss.d.ts +3 -0
  61. package/dist/runtime/core/html/applyInlineCss.mjs +32 -0
  62. package/dist/runtime/core/html/devIframeTemplate.d.ts +2 -0
  63. package/dist/runtime/core/html/{fetch.mjs → devIframeTemplate.mjs} +33 -42
  64. package/dist/runtime/core/html/fetchIsland.d.ts +3 -0
  65. package/dist/runtime/core/html/fetchIsland.mjs +17 -0
  66. package/dist/runtime/core/options/fetch.d.ts +1 -1
  67. package/dist/runtime/core/options/fetch.mjs +10 -5
  68. package/dist/runtime/core/renderers/chromium/index.mjs +12 -15
  69. package/dist/runtime/core/renderers/chromium/screenshot.d.ts +2 -3
  70. package/dist/runtime/core/renderers/chromium/screenshot.mjs +20 -15
  71. package/dist/runtime/core/renderers/satori/index.d.ts +2 -3
  72. package/dist/runtime/core/renderers/satori/index.mjs +51 -25
  73. package/dist/runtime/core/renderers/satori/instances.d.ts +3 -0
  74. package/dist/runtime/core/renderers/satori/instances.mjs +15 -0
  75. package/dist/runtime/core/renderers/satori/plugins/emojis.mjs +15 -13
  76. package/dist/runtime/core/renderers/satori/plugins/imageSrc.mjs +60 -30
  77. package/dist/runtime/core/renderers/satori/plugins/unocss.d.ts +2 -0
  78. package/dist/runtime/core/renderers/satori/plugins/unocss.mjs +45 -0
  79. package/dist/runtime/core/renderers/satori/utils.d.ts +2 -3
  80. package/dist/runtime/core/renderers/satori/vnodes.d.ts +2 -3
  81. package/dist/runtime/core/renderers/satori/vnodes.mjs +16 -6
  82. package/dist/runtime/core/utils/resolveRendererContext.d.ts +2 -6
  83. package/dist/runtime/core/utils/resolveRendererContext.mjs +47 -29
  84. package/dist/runtime/core/utils/wasm.d.ts +3 -0
  85. package/dist/runtime/core/utils/wasm.mjs +16 -0
  86. package/dist/runtime/nitro/plugins/nuxt-content.mjs +7 -6
  87. package/dist/runtime/nitro/plugins/prerender.d.ts +1 -1
  88. package/dist/runtime/nitro/plugins/prerender.mjs +20 -18
  89. package/dist/runtime/nitro/utils.d.ts +2 -0
  90. package/dist/runtime/nitro/utils.mjs +17 -0
  91. package/dist/runtime/nuxt/plugins/og-image-canonical-urls.server.mjs +43 -0
  92. package/dist/runtime/nuxt/plugins/route-rule-og-image.server.mjs +16 -51
  93. package/dist/runtime/nuxt/utils.d.ts +3 -0
  94. package/dist/runtime/nuxt/utils.mjs +69 -0
  95. package/dist/runtime/server/routes/__og-image__/debug.json.d.ts +2 -3
  96. package/dist/runtime/server/routes/__og-image__/debug.json.mjs +5 -7
  97. package/dist/runtime/server/routes/__og-image__/image.mjs +88 -0
  98. package/dist/runtime/types.d.ts +96 -27
  99. package/dist/runtime/utils.d.ts +4 -0
  100. package/dist/runtime/utils.mjs +11 -0
  101. package/dist/runtime/utils.pure.d.ts +6 -0
  102. package/dist/runtime/utils.pure.mjs +63 -0
  103. package/package.json +33 -38
  104. package/virtual.d.ts +49 -0
  105. package/dist/client/_nuxt/IconCSS.05a4ab6a.js +0 -1
  106. package/dist/client/_nuxt/IconCSS.8f429b14.css +0 -1
  107. package/dist/client/_nuxt/builds/meta/ce33a6eb-5cc2-4c46-8618-9befaa3f226c.json +0 -1
  108. package/dist/client/_nuxt/entry.434c2c45.css +0 -1
  109. package/dist/client/_nuxt/entry.d927023c.js +0 -137
  110. package/dist/client/grid.png +0 -0
  111. package/dist/runtime/components/OgImage/Cached.d.ts +0 -5
  112. package/dist/runtime/components/OgImage/Cached.mjs +0 -10
  113. package/dist/runtime/components/OgImage/Dynamic.d.ts +0 -8
  114. package/dist/runtime/components/OgImage/Dynamic.mjs +0 -10
  115. package/dist/runtime/components/OgImage/Screenshot.d.ts +0 -6
  116. package/dist/runtime/components/OgImage/Static.d.ts +0 -8
  117. package/dist/runtime/components/OgImage/Static.mjs +0 -10
  118. package/dist/runtime/components/OgImage/WithoutCache.d.ts +0 -5
  119. package/dist/runtime/components/OgImage/WithoutCache.mjs +0 -10
  120. package/dist/runtime/components/OgImage/index.d.ts +0 -5
  121. package/dist/runtime/components/Templates/Official/Fallback.vue +0 -147
  122. package/dist/runtime/core/bindings/css-inline/mock.d.ts +0 -5
  123. package/dist/runtime/core/bindings/css-inline/mock.mjs +0 -3
  124. package/dist/runtime/core/bindings/satori/yoga-wasm.mjs +0 -7
  125. package/dist/runtime/core/bindings/sharp/wasm.d.ts +0 -2
  126. package/dist/runtime/core/bindings/sharp/wasm.mjs +0 -2
  127. package/dist/runtime/core/font/cache.d.ts +0 -1
  128. package/dist/runtime/core/font/cache.mjs +0 -1
  129. package/dist/runtime/core/html/fetch.d.ts +0 -3
  130. package/dist/runtime/core/options/normalise.d.ts +0 -2
  131. package/dist/runtime/core/options/normalise.mjs +0 -26
  132. package/dist/runtime/core/renderers/satori/fonts.d.ts +0 -3
  133. package/dist/runtime/core/renderers/satori/fonts.mjs +0 -8
  134. package/dist/runtime/nuxt/plugins/nuxt-content-canonical-urls.mjs +0 -29
  135. package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
  136. package/dist/runtime/server/routes/__og-image__/image-[path]-og.[extension].mjs +0 -45
  137. package/dist/runtime/utilts.d.ts +0 -2
  138. package/dist/runtime/utilts.mjs +0 -8
  139. /package/dist/runtime/core/bindings/satori/{yoga-wasm.d.ts → wasm-fs.d.ts} +0 -0
  140. /package/dist/runtime/nuxt/plugins/{nuxt-content-canonical-urls.d.ts → og-image-canonical-urls.server.d.ts} +0 -0
  141. /package/dist/runtime/{public-assets-optional/inter-font → server/assets}/inter-latin-ext-400-normal.woff +0 -0
  142. /package/dist/runtime/{public-assets-optional/inter-font → server/assets}/inter-latin-ext-700-normal.woff +0 -0
  143. /package/dist/runtime/server/routes/__og-image__/{image-[path]-og.[extension].d.ts → image.d.ts} +0 -0
package/dist/module.mjs CHANGED
@@ -1,15 +1,20 @@
1
1
  import * as fs from 'node:fs';
2
2
  import { existsSync } from 'node:fs';
3
- import { useNuxt, createResolver, addTemplate, addServerPlugin, defineNuxtModule, useLogger, hasNuxtModule, addPlugin, addServerHandler, addImports, addComponentsDir, addComponent } from '@nuxt/kit';
4
- import { installNuxtSiteConfig } from 'nuxt-site-config-kit';
5
- import { provider, env } from 'std-env';
3
+ import { useNuxt, addTemplate, loadNuxtModuleInstance, createResolver, resolvePath, defineNuxtModule, useLogger, tryResolveModule, hasNuxtModule, addServerPlugin, addServerHandler, addImports, addComponentsDir, addComponent, addPlugin } from '@nuxt/kit';
4
+ import { assertSiteConfig, installNuxtSiteConfig } from 'nuxt-site-config-kit';
5
+ import { provider, env, isDevelopment, isCI } from 'std-env';
6
6
  import { hash } from 'ohash';
7
- import { relative } from 'pathe';
8
- import 'nypm';
7
+ import { relative, dirname } from 'pathe';
9
8
  import { defu } from 'defu';
9
+ import { Launcher } from 'chrome-launcher';
10
+ import { ensureDependencyInstalled } from 'nypm';
10
11
  import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
12
+ import { readFile, writeFile } from 'node:fs/promises';
13
+ import { createHash } from 'node:crypto';
14
+ import { execa } from 'execa';
15
+ import terminate from 'terminate';
11
16
 
12
- const version = "3.0.0-beta.9";
17
+ const version = "3.0.0-rc.0";
13
18
 
14
19
  const autodetectableProviders = {
15
20
  azure_static: "azure",
@@ -26,72 +31,69 @@ const autodetectableStaticProviders = {
26
31
  };
27
32
  const NodeRuntime = {
28
33
  // node-server runtime
29
- bindings: {
30
- "chromium": "node",
31
- "css-inline": "node",
32
- "resvg": "node",
33
- "satori": "node",
34
- "sharp": "node"
35
- },
36
- wasmStrategy: "import"
34
+ "chromium": "node",
35
+ "css-inline": "node",
36
+ "resvg": "node",
37
+ "satori": "node",
38
+ "sharp": "node"
39
+ // will be disabled if they're missing the dependency
37
40
  };
38
41
  const cloudflare = {
39
- bindings: {
40
- "chromium": false,
41
- "css-inline": "node",
42
- "resvg": "wasm",
43
- "satori": "node",
44
- "sharp": false
45
- },
46
- wasmStrategy: "import"
42
+ "chromium": false,
43
+ "css-inline": false,
44
+ "resvg": "wasm",
45
+ "satori": "node",
46
+ "sharp": false,
47
+ "wasm": {
48
+ esmImport: true
49
+ }
47
50
  };
48
51
  const awsLambda = {
49
- bindings: {
50
- "chromium": false,
51
- "css-inline": "node",
52
- "resvg": "node",
53
- "satori": "node",
54
- "sharp": "node"
55
- },
56
- wasmStrategy: "inline"
52
+ "chromium": false,
53
+ "css-inline": "node",
54
+ "resvg": "node",
55
+ "satori": "node",
56
+ "sharp": false
57
+ // 0.33.x has issues
57
58
  };
58
59
  const RuntimeCompatibility = {
59
60
  "nitro-dev": NodeRuntime,
60
61
  "nitro-prerender": NodeRuntime,
61
62
  "node-server": NodeRuntime,
62
63
  "stackblitz": {
63
- bindings: {
64
- "chromium": false,
65
- "css-inline": "node",
66
- "resvg": "wasm",
67
- "satori": "node",
68
- "sharp": "node"
69
- },
70
- wasmStrategy: "inline"
64
+ "chromium": false,
65
+ "css-inline": false,
66
+ "resvg": "wasm-fs",
67
+ "satori": "wasm-fs",
68
+ "sharp": false
71
69
  },
72
70
  "aws-lambda": awsLambda,
73
71
  "netlify": awsLambda,
74
72
  "netlify-edge": {
75
- bindings: {
76
- "chromium": false,
77
- "css-inline": "node",
78
- "resvg": "wasm",
79
- "satori": "node",
80
- "sharp": false
81
- },
82
- wasmStrategy: "inline"
73
+ "chromium": false,
74
+ "css-inline": false,
75
+ "resvg": "wasm",
76
+ "satori": "node",
77
+ "sharp": false,
78
+ "wasm": {
79
+ rollup: {
80
+ targetEnv: "auto-inline",
81
+ sync: ["@resvg/resvg-wasm/index_bg.wasm"]
82
+ }
83
+ }
83
84
  },
85
+ "firebase": awsLambda,
84
86
  "vercel": awsLambda,
85
87
  "vercel-edge": {
86
- bindings: {
87
- "chromium": false,
88
- "css-inline": "node",
89
- "resvg": "wasm",
90
- "satori": "node",
91
- "sharp": "node"
92
- },
93
- wasmStrategy: "inline",
94
- wasmImportQuery: "?module"
88
+ "chromium": false,
89
+ "css-inline": false,
90
+ "resvg": "wasm",
91
+ "satori": "node",
92
+ "sharp": false,
93
+ "wasm": {
94
+ // lowers workers kb size
95
+ esmImport: true
96
+ }
95
97
  },
96
98
  "cloudflare-pages": cloudflare,
97
99
  "cloudflare": cloudflare
@@ -116,37 +118,75 @@ function getPresetNitroPresetCompatibility(target) {
116
118
  return compatibility;
117
119
  }
118
120
  function applyNitroPresetCompatibility(nitroConfig, options) {
119
- let compatibility = options?.compatibility;
120
121
  const target = resolveNitroPreset(nitroConfig);
121
- if (!compatibility)
122
- compatibility = getPresetNitroPresetCompatibility(target);
123
- const resolve = options.resolve;
122
+ const compatibility = getPresetNitroPresetCompatibility(target);
123
+ const { resolve } = options;
124
+ const satoriEnabled = typeof options.compatibility?.satori !== "undefined" ? !!options.compatibility.satori : !!compatibility.satori;
125
+ const chromiumEnabled = typeof options.compatibility?.chromium !== "undefined" ? !!options.compatibility.chromium : !!compatibility.chromium;
126
+ nitroConfig.alias["#nuxt-og-image/renderers/satori"] = satoriEnabled ? resolve("./runtime/core/renderers/satori") : "unenv/runtime/mock/empty";
127
+ nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = chromiumEnabled ? resolve("./runtime/core/renderers/chromium") : "unenv/runtime/mock/empty";
128
+ const resolvedCompatibility = {};
124
129
  function applyBinding(key) {
125
- const binding = compatibility.bindings[key];
126
- if (binding === false)
127
- return { [`#nuxt-og-image/bindings/${key}`]: "unenv/runtime/mock/empty" };
128
- return { [`#nuxt-og-image/bindings/${key}`]: resolve(`./runtime/core/bindings/${key}/${binding}`) };
130
+ let binding = options.compatibility?.[key];
131
+ if (typeof binding === "undefined")
132
+ binding = compatibility[key];
133
+ resolvedCompatibility[key] = binding;
134
+ return {
135
+ [`#nuxt-og-image/bindings/${key}`]: binding === false ? "unenv/runtime/mock/empty" : resolve(`./runtime/core/bindings/${key}/${binding}`)
136
+ };
129
137
  }
130
138
  nitroConfig.alias = defu(
131
139
  applyBinding("chromium"),
132
140
  applyBinding("satori"),
133
141
  applyBinding("resvg"),
134
142
  applyBinding("sharp"),
143
+ applyBinding("css-inline"),
135
144
  nitroConfig.alias || {}
136
145
  );
137
- nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
138
- if (target === "netlify-edge") {
139
- nitroConfig.wasm = nitroConfig.wasm || {};
140
- nitroConfig.wasm.rollup = nitroConfig.wasm.rollup || {};
141
- nitroConfig.wasm.rollup.targetEnv = "auto-inline";
142
- }
143
- if (target.includes("edge") || target.includes("cloudflare")) {
146
+ if (Object.values(compatibility).includes("wasm")) {
144
147
  nitroConfig.experimental = nitroConfig.experimental || {};
145
148
  nitroConfig.experimental.wasm = true;
146
149
  }
147
- return compatibility;
150
+ nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
151
+ nitroConfig.wasm = defu(compatibility.wasm, nitroConfig.wasm);
152
+ nitroConfig.virtual["#nuxt-og-image/compatibility"] = () => `export default ${JSON.stringify(resolvedCompatibility)}`;
153
+ addTemplate({
154
+ filename: "nuxt-og-image/compatibility.mjs",
155
+ getContents() {
156
+ return `export default ${JSON.stringify(resolvedCompatibility)}`;
157
+ },
158
+ options: { mode: "server" }
159
+ });
160
+ return resolvedCompatibility;
161
+ }
162
+ function ensureDependencies(dep, nuxt = useNuxt()) {
163
+ return Promise.all(dep.map((d) => {
164
+ return ensureDependencyInstalled(d, {
165
+ cwd: nuxt.options.rootDir,
166
+ dev: true
167
+ });
168
+ }));
148
169
  }
149
170
 
171
+ async function getNuxtModuleOptions(module, nuxt = useNuxt()) {
172
+ const moduleMeta = (typeof module === "string" ? { name: module } : await module.getMeta?.()) || {};
173
+ const { nuxtModule } = await loadNuxtModuleInstance(module, nuxt);
174
+ let moduleEntry;
175
+ for (const m of nuxt.options.modules) {
176
+ if (Array.isArray(m) && m.length >= 2) {
177
+ const _module = m[0];
178
+ const _moduleEntryName = typeof _module === "string" ? _module : (await _module.getMeta?.())?.name || "";
179
+ if (_moduleEntryName === moduleMeta.name)
180
+ moduleEntry = m;
181
+ }
182
+ }
183
+ let inlineOptions = {};
184
+ if (moduleEntry)
185
+ inlineOptions = moduleEntry[1];
186
+ if (nuxtModule.getOptions)
187
+ return nuxtModule.getOptions(inlineOptions, nuxt);
188
+ return inlineOptions;
189
+ }
150
190
  function extendTypes(module, template) {
151
191
  const nuxt = useNuxt();
152
192
  const { resolve } = createResolver(import.meta.url);
@@ -198,9 +238,15 @@ function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
198
238
  rpc.broadcast.refreshRouteData(path).catch(() => {
199
239
  });
200
240
  }
201
- if (options.componentDirs.some((dir) => path.includes(dir)))
202
- rpc.broadcast.refreshGlobalData().catch(() => {
203
- });
241
+ if (options.componentDirs.some((dir) => path.includes(dir))) {
242
+ if (e === "change") {
243
+ rpc.broadcast.refresh().catch(() => {
244
+ });
245
+ } else {
246
+ rpc.broadcast.refreshGlobalData().catch(() => {
247
+ });
248
+ }
249
+ }
204
250
  });
205
251
  });
206
252
  nuxt.hook("devtools:customTabs", (tabs) => {
@@ -222,40 +268,42 @@ function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
222
268
 
223
269
  function setupDevHandler(options, resolve, nuxt = useNuxt()) {
224
270
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
225
- nitroConfig.alias["#nuxt-og-image/renderers/satori"] = options.runtimeSatori ? resolve("./runtime/core/renderers/satori") : "unenv/runtime/mock/empty";
226
- nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = options.runtimeBrowser ? resolve("./runtime/core/renderers/chromium") : "unenv/runtime/mock/empty";
227
- applyNitroPresetCompatibility(nitroConfig, { resolve });
271
+ applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.dev, resolve });
228
272
  });
229
273
  }
230
274
 
231
275
  function setupGenerateHandler(options, resolve, nuxt = useNuxt()) {
232
276
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
233
- nitroConfig.alias["#nuxt-og-image/renderers/satori"] = "unenv/runtime/mock/empty";
234
- nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = "unenv/runtime/mock/empty";
235
277
  applyNitroPresetCompatibility(nitroConfig, {
236
278
  compatibility: {
237
- bindings: {
238
- "css-inline": false,
239
- "chromium": false,
240
- "resvg": false,
241
- "satori": false,
242
- "sharp": false
243
- }
279
+ "chromium": false,
280
+ "satori": false,
281
+ "css-inline": false,
282
+ "resvg": false,
283
+ "sharp": false
244
284
  },
245
285
  resolve
246
286
  });
287
+ assertSiteConfig("nuxt-og-image", {
288
+ url: "OG Image tags are required to be absolute URLs."
289
+ }, {
290
+ throwError: true
291
+ });
247
292
  });
248
293
  }
249
294
 
250
295
  function setupPrerenderHandler(options, resolve, nuxt = useNuxt()) {
251
- addServerPlugin(resolve("./runtime/nitro/plugins/prerender.ts"));
252
296
  nuxt.hooks.hook("nitro:init", async (nitro) => {
253
297
  nitro.hooks.hook("prerender:config", async (nitroConfig) => {
254
- nitroConfig.alias["#nuxt-og-image/renderers/satori"] = resolve("./runtime/core/renderers/satori");
255
- nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = resolve("./runtime/core/renderers/chromium");
256
- applyNitroPresetCompatibility(nitroConfig, { resolve });
298
+ applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility.prerender, resolve });
257
299
  nitroConfig.wasm = nitroConfig.wasm || {};
258
300
  nitroConfig.wasm.esmImport = false;
301
+ const prerenderingPages = (nuxt.options.nitro.prerender?.routes || []).some((r) => r && (!r.includes(".") || r.includes("*")));
302
+ prerenderingPages && assertSiteConfig("nuxt-og-image", {
303
+ url: "OG Image tags are required to be absolute URLs."
304
+ }, {
305
+ throwError: false
306
+ });
259
307
  });
260
308
  });
261
309
  }
@@ -265,44 +313,97 @@ async function setupBuildHandler(config, resolve, nuxt = useNuxt()) {
265
313
  if (typeof config.runtimeCacheStorage === "object")
266
314
  nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
267
315
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
268
- nitroConfig.alias["#nuxt-og-image/renderers/satori"] = config.runtimeSatori ? resolve("./runtime/core/renderers/satori") : "unenv/runtime/mock/empty";
269
- nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = config.runtimeBrowser ? resolve("./runtime/core/renderers/chromium") : "unenv/runtime/mock/empty";
270
- applyNitroPresetCompatibility(nitroConfig, { resolve, compatibility: config.runtimeCompatibility });
316
+ applyNitroPresetCompatibility(nitroConfig, { compatibility: config.compatibility?.runtime, resolve });
271
317
  nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
272
318
  nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
273
319
  nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
274
320
  nitroConfig.alias.queue = "unenv/runtime/mock/proxy-cjs";
275
321
  });
322
+ nuxt.hooks.hook("nitro:init", async (nitro) => {
323
+ nitro.hooks.hook("compiled", async (_nitro) => {
324
+ const target = resolveNitroPreset(_nitro.options);
325
+ const compatibility = getPresetNitroPresetCompatibility(target);
326
+ if (compatibility.wasm?.esmImport !== true)
327
+ return;
328
+ const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
329
+ let serverEntry = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
330
+ if (target === "cloudflare-pages")
331
+ serverEntry = resolve(dirname(serverEntry), "./chunks/wasm.mjs");
332
+ const contents = await readFile(serverEntry, "utf-8");
333
+ const resvgHash = sha1(await readFile(await resolvePath("@resvg/resvg-wasm/index_bg.wasm")));
334
+ const yogaHash = sha1(await readFile(await resolvePath("yoga-wasm-web/dist/yoga.wasm")));
335
+ const postfix = target === "vercel-edge" ? "?module" : "";
336
+ const path = target === "cloudflare-pages" ? `../wasm/` : `./wasm/`;
337
+ await writeFile(serverEntry, contents.replaceAll('"@resvg/resvg-wasm/index_bg.wasm"', `"${path}index_bg-${resvgHash}.wasm${postfix}"`).replaceAll('"yoga-wasm-web/dist/yoga.wasm"', `"${path}yoga-${yogaHash}.wasm${postfix}"`), { encoding: "utf-8" });
338
+ });
339
+ });
340
+ }
341
+ function sha1(source) {
342
+ return createHash("sha1").update(source).digest("hex").slice(0, 16);
343
+ }
344
+
345
+ async function ensureChromium(logger) {
346
+ logger.info("Ensuring Chromium install for og:image generation...");
347
+ const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
348
+ stdio: "inherit"
349
+ });
350
+ installChromeProcess.stderr?.pipe(process.stderr);
351
+ await new Promise((resolve) => {
352
+ installChromeProcess.on("exit", (e) => {
353
+ if (e !== 0)
354
+ logger.error("Failed to install Playwright dependency for og:image generation. Trying anyway...");
355
+ resolve(true);
356
+ });
357
+ });
358
+ installChromeProcess.pid && terminate(installChromeProcess.pid);
359
+ }
360
+
361
+ function normaliseFontInput(fonts) {
362
+ return fonts.map((f) => {
363
+ if (typeof f === "string") {
364
+ const [name, weight] = f.split(":");
365
+ return {
366
+ cacheKey: f,
367
+ name,
368
+ weight: weight || "400",
369
+ style: "normal",
370
+ path: void 0
371
+ };
372
+ }
373
+ return {
374
+ cacheKey: f.key || `${f.name}:${f.weight}`,
375
+ style: "normal",
376
+ weight: 400,
377
+ ...f
378
+ };
379
+ });
276
380
  }
277
381
 
278
382
  const module = defineNuxtModule({
279
383
  meta: {
280
384
  name: "nuxt-og-image",
281
385
  compatibility: {
282
- nuxt: "^3.7.0",
386
+ nuxt: "^3.8.2",
283
387
  bridge: false
284
388
  },
285
389
  configKey: "ogImage"
286
390
  },
287
- defaults(nuxt) {
391
+ defaults() {
288
392
  return {
289
393
  enabled: true,
290
394
  defaults: {
395
+ emojis: "noto",
291
396
  renderer: "satori",
292
- component: "Fallback",
397
+ component: "NuxtSeo",
293
398
  width: 1200,
294
399
  height: 600,
295
- cache: true,
296
- // default is to cache the image for 1 day (24 hours)
297
- cacheTtl: 24 * 60 * 60 * 1e3
400
+ // default is to cache the image for 3 day (72 hours)
401
+ cacheMaxAgeSeconds: 60 * 60 * 24 * 3
298
402
  },
299
403
  componentDirs: ["OgImage", "OgImageTemplate"],
300
- runtimeSatori: true,
301
- runtimeBrowser: nuxt.options.dev,
302
404
  fonts: [],
303
405
  runtimeCacheStorage: true,
304
- playground: env.NODE_ENV === "development" || nuxt.options.dev,
305
- debug: false
406
+ debug: isDevelopment
306
407
  };
307
408
  },
308
409
  async setup(config, nuxt) {
@@ -318,17 +419,91 @@ const module = defineNuxtModule({
318
419
  }
319
420
  const { resolve } = createResolver(import.meta.url);
320
421
  const preset = resolveNitroPreset(nuxt.options.nitro);
321
- const compatibility = getPresetNitroPresetCompatibility(preset);
322
- config.defaults.extension = "jpg";
323
- if (!compatibility.bindings.sharp)
324
- config.defaults.extension = "png";
422
+ const targetCompatibility = getPresetNitroPresetCompatibility(preset);
423
+ const hasSharpDependency = !!await tryResolveModule("sharp");
424
+ const userConfiguredExtension = config.defaults.extension;
425
+ const hasConfiguredJpegs = userConfiguredExtension && ["jpeg", "jpg"].includes(userConfiguredExtension);
426
+ config.defaults.extension = userConfiguredExtension || (hasSharpDependency && targetCompatibility.sharp ? "jpg" : "png");
427
+ if (hasConfiguredJpegs && config.defaults.renderer !== "chromium") {
428
+ if (hasSharpDependency && !targetCompatibility.sharp) {
429
+ logger.warn(`Rendering JPEGs requires sharp which does not work with ${preset}. Images will be rendered as PNG at runtime.`);
430
+ config.compatibility = defu(config.compatibility, {
431
+ runtime: { sharp: false }
432
+ });
433
+ } else if (!hasSharpDependency) {
434
+ logger.warn("You have enabled `JPEG` images. These require the `sharp` dependency which is missing, installing it for you.");
435
+ await ensureDependencies(["sharp"]);
436
+ logger.warn("Support for `sharp` is limited so check the compatibility guide.");
437
+ }
438
+ } else if (!hasSharpDependency) {
439
+ config.compatibility = defu(config.compatibility, {
440
+ runtime: { sharp: false },
441
+ dev: { sharp: false },
442
+ prerender: { sharp: false }
443
+ });
444
+ }
445
+ let hasChromeLocally = false;
446
+ try {
447
+ hasChromeLocally = !!Launcher.getFirstInstallation();
448
+ } catch {
449
+ }
450
+ const isUndefinedOrTruthy = (v) => typeof v === "undefined" || v !== false;
451
+ if (isUndefinedOrTruthy(config.compatibility?.prerender?.chromium) && isUndefinedOrTruthy(config.compatibility?.runtime?.chromium)) {
452
+ if (isCI)
453
+ await ensureChromium(logger);
454
+ const hasPlaywrightDependency = !!await tryResolveModule("playwright");
455
+ if (hasChromeLocally) {
456
+ config.compatibility = defu(config.compatibility, {
457
+ runtime: { chromium: false },
458
+ dev: { chromium: "node" },
459
+ prerender: { chromium: "node" }
460
+ });
461
+ } else if (hasPlaywrightDependency && targetCompatibility.chromium) {
462
+ config.compatibility = defu(config.compatibility, {
463
+ runtime: { chromium: "node" },
464
+ dev: { chromium: "node" },
465
+ prerender: { chromium: "node" }
466
+ });
467
+ }
468
+ } else if (!hasChromeLocally && nuxt.options.dev && config.compatibility?.dev?.chromium === "node") {
469
+ await ensureChromium(logger);
470
+ }
325
471
  await installNuxtSiteConfig();
326
- if (hasNuxtModule("@nuxt/content")) {
472
+ if (hasNuxtModule("@nuxt/content"))
327
473
  addServerPlugin(resolve("./runtime/nitro/plugins/nuxt-content"));
328
- addPlugin(resolve("./runtime/nuxt/plugins/nuxt-content-canonical-urls"));
329
- }
330
474
  if (!config.fonts.length)
331
475
  config.fonts = ["Inter:400", "Inter:700"];
476
+ if (preset === "cloudflare") {
477
+ config.fonts = config.fonts.filter((f) => {
478
+ if (typeof f !== "string" && f.path) {
479
+ logger.warn(`The ${f.name}:${f.weight} font was skipped because remote fonts are not available in Cloudflare Workers, please use a Google font.`);
480
+ return false;
481
+ }
482
+ return true;
483
+ });
484
+ }
485
+ if (preset === "stackblitz") {
486
+ config.fonts = config.fonts.map((f) => {
487
+ if (typeof f === "string" && f.startsWith("Inter:")) {
488
+ const [_, weight] = f.split(":");
489
+ return {
490
+ name: "Inter",
491
+ weight,
492
+ // nuxt server assets
493
+ key: `nuxt-og-image:fonts:inter-latin-ext-${weight}-normal.woff`
494
+ };
495
+ }
496
+ if (typeof f === "string" || !f.path && !f.key) {
497
+ logger.warn(`The ${typeof f === "string" ? f : `${f.name}:${f.weight}`} font was skipped because remote fonts are not available in StackBlitz, please use a local font.`);
498
+ return false;
499
+ }
500
+ return f;
501
+ }).filter(Boolean);
502
+ nuxt.hooks.hook("nitro:config", (nitroConfig) => {
503
+ nitroConfig.serverAssets = nitroConfig.serverAssets || [];
504
+ nitroConfig.serverAssets.push({ baseName: "nuxt-og-image:fonts", dir: resolve("./runtime/server/assets") });
505
+ });
506
+ }
332
507
  nuxt.options.experimental.componentIslands = true;
333
508
  addServerHandler({
334
509
  lazy: true,
@@ -345,52 +520,34 @@ const module = defineNuxtModule({
345
520
  addServerHandler({
346
521
  lazy: true,
347
522
  route: "/__og-image__/image/**",
348
- handler: resolve("./runtime/server/routes/__og-image__/image-[path]-og.[extension]")
523
+ handler: resolve("./runtime/server/routes/__og-image__/image")
349
524
  });
350
525
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
351
- [
352
- // deprecated
353
- "Dynamic",
354
- "Static",
355
- // new
356
- "index",
357
- "Cached",
358
- "Component",
359
- "WithoutCache",
360
- "Screenshot"
361
- ].forEach((name) => {
362
- name = name === "index" ? "defineOgImage" : `defineOgImage${name}`;
526
+ ["defineOgImage", "defineOgImageComponent", "defineOgImageScreenshot"].forEach((name) => {
363
527
  addImports({
364
528
  name,
365
- from: resolve("./runtime/composables/defineOgImage")
529
+ from: resolve(`./runtime/composables/${name}`)
366
530
  });
367
531
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
368
532
  });
369
533
  await addComponentsDir({
370
534
  path: resolve("./runtime/components/Templates/Community"),
371
- island: true
372
- });
373
- await addComponentsDir({
374
- path: resolve("./runtime/components/Templates/Official"),
375
- island: true
535
+ island: true,
536
+ watch: true
376
537
  });
377
538
  [
378
- // deprecated
379
- "Static",
380
- "Dynamic",
381
539
  // new
382
- "index",
383
- "Cached",
384
- "WithoutCache",
385
- "Screenshot"
540
+ "OgImage",
541
+ "OgImageScreenshot"
386
542
  ].forEach((name) => {
387
543
  addComponent({
388
- global: hasNuxtModule("@nuxt/content"),
389
- name: name === "index" ? "OgImage" : `OgImage${name}`,
390
- filePath: resolve(`./runtime/components/OgImage/${name}`)
544
+ name,
545
+ filePath: resolve(`./runtime/components/OgImage/${name}`),
546
+ ...config.componentOptions
391
547
  });
392
548
  });
393
- addPlugin(resolve("./runtime/nuxt/plugins/route-rule-og-image.server"));
549
+ addPlugin({ mode: "server", src: resolve("./runtime/nuxt/plugins/route-rule-og-image.server") });
550
+ addPlugin({ mode: "server", src: resolve("./runtime/nuxt/plugins/og-image-canonical-urls.server") });
394
551
  const ogImageComponentCtx = { components: [] };
395
552
  nuxt.hook("components:extend", (components) => {
396
553
  ogImageComponentCtx.components = [];
@@ -408,8 +565,6 @@ const module = defineNuxtModule({
408
565
  let category = "app";
409
566
  if (component.filePath.includes(resolve("./runtime/components/Templates/Community")))
410
567
  category = "community";
411
- else if (component.filePath.includes(resolve("./runtime/components/Templates/Official")))
412
- category = "official";
413
568
  const componentFile = fs.readFileSync(component.filePath, "utf-8");
414
569
  const credits = componentFile.split("\n").find((line) => line.startsWith(" * @credits"))?.replace("* @credits", "").trim();
415
570
  ogImageComponentCtx.components.push({
@@ -426,7 +581,7 @@ const module = defineNuxtModule({
426
581
  nuxt.hooks.hook("nuxt-og-image:components", ogImageComponentCtx);
427
582
  });
428
583
  addTemplate({
429
- filename: "og-image-component-names.mjs",
584
+ filename: "nuxt-og-image/components.mjs",
430
585
  getContents() {
431
586
  return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
432
587
  },
@@ -436,6 +591,16 @@ const module = defineNuxtModule({
436
591
  nuxt.options.nitro.virtual["#nuxt-og-image/component-names.mjs"] = () => {
437
592
  return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
438
593
  };
594
+ let unoCssConfig = { theme: {} };
595
+ nuxt.hook("tailwindcss:config", (tailwindConfig) => {
596
+ unoCssConfig = defu(tailwindConfig.theme.extend, { ...tailwindConfig.theme || {}, extend: void 0 });
597
+ });
598
+ nuxt.hook("unocss:config", (_unoCssConfig) => {
599
+ unoCssConfig = { ..._unoCssConfig.theme };
600
+ });
601
+ nuxt.options.nitro.virtual["#nuxt-og-image/unocss-config.mjs"] = () => {
602
+ return `export const theme = ${JSON.stringify(unoCssConfig)}`;
603
+ };
439
604
  extendTypes("nuxt-og-image", ({ typesPath }) => {
440
605
  const componentImports = ogImageComponentCtx.components.map((component) => {
441
606
  const relativeComponentPath = relative(resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"), component.path);
@@ -444,12 +609,17 @@ const module = defineNuxtModule({
444
609
  return `
445
610
  declare module 'nitropack' {
446
611
  interface NitroRouteRules {
447
- ogImage?: false | import('${typesPath}').OgImageOptions
612
+ ogImage?: false | import('${typesPath}').OgImageOptions & Record<string, any>
448
613
  }
449
614
  interface NitroRouteConfig {
450
- ogImage?: false | import('${typesPath}').OgImageOptions
615
+ ogImage?: false | import('${typesPath}').OgImageOptions & Record<string, any>
616
+ }
617
+ interface NitroRuntimeHooks {
618
+ 'nuxt-og-image:context': (ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
619
+ 'nuxt-og-image:satori:vnodes': (vnodes: import('${typesPath}').VNode, ctx: ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
451
620
  }
452
621
  }
622
+
453
623
  declare module '#nuxt-og-image/components' {
454
624
  export interface OgImageComponents {
455
625
  ${componentImports}
@@ -457,42 +627,44 @@ ${componentImports}
457
627
  }
458
628
  `;
459
629
  });
630
+ const cacheEnabled = typeof config.runtimeCacheStorage !== "undefined" && config.runtimeCacheStorage !== false;
631
+ const runtimeCacheStorage = typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver;
632
+ let baseCacheKey = runtimeCacheStorage === "default" ? `/cache/nuxt-og-image@${version}` : `/nuxt-og-image@${version}`;
633
+ if (!cacheEnabled)
634
+ baseCacheKey = false;
460
635
  nuxt.hooks.hook("modules:done", async () => {
461
- nuxt.hooks.callHook("og-image:config", config);
462
- const normalisedFonts = config.fonts.map((f) => {
463
- if (typeof f === "string") {
464
- const [name, weight] = f.split(":");
465
- return {
466
- name,
467
- weight,
468
- path: void 0
469
- };
470
- }
471
- return f;
472
- });
636
+ const normalisedFonts = normaliseFontInput(config.fonts);
473
637
  if (!nuxt.options._generate && nuxt.options.build) {
474
638
  nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
475
639
  nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
476
- normalisedFonts.filter((f) => !f.path).forEach(({ name, weight }) => {
640
+ normalisedFonts.filter((f) => !f.path && !f.key).forEach(({ name, weight }) => {
477
641
  nuxt.options.nitro.prerender.routes.push(`/__og-image__/font/${name}/${weight}.ttf`);
478
642
  });
479
643
  }
480
- nuxt.options.runtimeConfig["nuxt-og-image"] = {
644
+ const hasColorModeModule = hasNuxtModule("@nuxtjs/color-mode");
645
+ const colorModeOptions = hasColorModeModule ? await getNuxtModuleOptions("@nuxtjs/color-mode") : {};
646
+ let colorPreference = colorModeOptions.preference;
647
+ if (!colorPreference || colorPreference === "system")
648
+ colorPreference = colorModeOptions.fallback;
649
+ if (!colorPreference || colorPreference === "system")
650
+ colorPreference = "light";
651
+ const runtimeConfig = {
481
652
  version,
482
653
  // binding options
483
654
  satoriOptions: config.satoriOptions || {},
484
655
  resvgOptions: config.resvgOptions || {},
485
656
  sharpOptions: config.sharpOptions || {},
486
- runtimeSatori: config.runtimeSatori,
487
- runtimeBrowser: config.runtimeBrowser,
488
- // @ts-expect-error runtime type
489
657
  defaults: config.defaults,
658
+ debug: config.debug,
490
659
  // avoid adding credentials
491
- runtimeCacheStorage: typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver,
660
+ baseCacheKey,
492
661
  // convert the fonts to uniform type to fix ts issue
493
662
  fonts: normalisedFonts,
494
- hasNuxtIcon: hasNuxtModule("nuxt-icon")
663
+ hasNuxtIcon: hasNuxtModule("nuxt-icon"),
664
+ colorPreference
495
665
  };
666
+ nuxt.hooks.callHook("nuxt-og-image:runtime-config", runtimeConfig);
667
+ nuxt.options.runtimeConfig["nuxt-og-image"] = runtimeConfig;
496
668
  });
497
669
  if (nuxt.options.dev) {
498
670
  setupDevHandler(config, resolve);
@@ -502,8 +674,9 @@ ${componentImports}
502
674
  } else if (nuxt.options.build) {
503
675
  await setupBuildHandler(config, resolve);
504
676
  }
505
- if (nuxt.options.nitro.prerender?.routes || nuxt.options._generate)
506
- setupPrerenderHandler(config, resolve);
677
+ if (nuxt.options.build)
678
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
679
+ setupPrerenderHandler(config, resolve);
507
680
  }
508
681
  });
509
682