nuxt-og-image 3.0.0-beta.9 → 3.0.0-rc.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 (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.bca1abaf.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/c430c582-423d-48d2-8592-39b7bfd61658.json +1 -0
  8. package/dist/client/_nuxt/entry.a30f63d0.css +1 -0
  9. package/dist/client/_nuxt/entry.f2e056ce.js +108 -0
  10. package/dist/client/_nuxt/{error-404.18456c20.js → error-404.1b7ec865.js} +1 -1
  11. package/dist/client/_nuxt/{error-500.a3e12514.js → error-500.1f097e6f.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 +346 -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.1";
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,98 @@ 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
+ }
471
+ await import('@resvg/resvg-js').catch(() => {
472
+ logger.warn("ReSVG is missing dependencies for environment. Falling back to WASM version, this may slow down PNG rendering.");
473
+ config.compatibility = defu(config.compatibility, {
474
+ dev: { resvg: "wasm-fs" },
475
+ prerender: { resvg: "wasm-fs" }
476
+ });
477
+ });
325
478
  await installNuxtSiteConfig();
326
- if (hasNuxtModule("@nuxt/content")) {
479
+ if (hasNuxtModule("@nuxt/content"))
327
480
  addServerPlugin(resolve("./runtime/nitro/plugins/nuxt-content"));
328
- addPlugin(resolve("./runtime/nuxt/plugins/nuxt-content-canonical-urls"));
329
- }
330
481
  if (!config.fonts.length)
331
482
  config.fonts = ["Inter:400", "Inter:700"];
483
+ if (preset === "cloudflare") {
484
+ config.fonts = config.fonts.filter((f) => {
485
+ if (typeof f !== "string" && f.path) {
486
+ logger.warn(`The ${f.name}:${f.weight} font was skipped because remote fonts are not available in Cloudflare Workers, please use a Google font.`);
487
+ return false;
488
+ }
489
+ return true;
490
+ });
491
+ }
492
+ if (preset === "stackblitz") {
493
+ config.fonts = config.fonts.map((f) => {
494
+ if (typeof f === "string" && f.startsWith("Inter:")) {
495
+ const [_, weight] = f.split(":");
496
+ return {
497
+ name: "Inter",
498
+ weight,
499
+ // nuxt server assets
500
+ key: `nuxt-og-image:fonts:inter-latin-ext-${weight}-normal.woff`
501
+ };
502
+ }
503
+ if (typeof f === "string" || !f.path && !f.key) {
504
+ 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.`);
505
+ return false;
506
+ }
507
+ return f;
508
+ }).filter(Boolean);
509
+ nuxt.hooks.hook("nitro:config", (nitroConfig) => {
510
+ nitroConfig.serverAssets = nitroConfig.serverAssets || [];
511
+ nitroConfig.serverAssets.push({ baseName: "nuxt-og-image:fonts", dir: resolve("./runtime/server/assets") });
512
+ });
513
+ }
332
514
  nuxt.options.experimental.componentIslands = true;
333
515
  addServerHandler({
334
516
  lazy: true,
@@ -345,52 +527,34 @@ const module = defineNuxtModule({
345
527
  addServerHandler({
346
528
  lazy: true,
347
529
  route: "/__og-image__/image/**",
348
- handler: resolve("./runtime/server/routes/__og-image__/image-[path]-og.[extension]")
530
+ handler: resolve("./runtime/server/routes/__og-image__/image")
349
531
  });
350
532
  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}`;
533
+ ["defineOgImage", "defineOgImageComponent", "defineOgImageScreenshot"].forEach((name) => {
363
534
  addImports({
364
535
  name,
365
- from: resolve("./runtime/composables/defineOgImage")
536
+ from: resolve(`./runtime/composables/${name}`)
366
537
  });
367
538
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
368
539
  });
369
540
  await addComponentsDir({
370
541
  path: resolve("./runtime/components/Templates/Community"),
371
- island: true
372
- });
373
- await addComponentsDir({
374
- path: resolve("./runtime/components/Templates/Official"),
375
- island: true
542
+ island: true,
543
+ watch: true
376
544
  });
377
545
  [
378
- // deprecated
379
- "Static",
380
- "Dynamic",
381
546
  // new
382
- "index",
383
- "Cached",
384
- "WithoutCache",
385
- "Screenshot"
547
+ "OgImage",
548
+ "OgImageScreenshot"
386
549
  ].forEach((name) => {
387
550
  addComponent({
388
- global: hasNuxtModule("@nuxt/content"),
389
- name: name === "index" ? "OgImage" : `OgImage${name}`,
390
- filePath: resolve(`./runtime/components/OgImage/${name}`)
551
+ name,
552
+ filePath: resolve(`./runtime/components/OgImage/${name}`),
553
+ ...config.componentOptions
391
554
  });
392
555
  });
393
- addPlugin(resolve("./runtime/nuxt/plugins/route-rule-og-image.server"));
556
+ addPlugin({ mode: "server", src: resolve("./runtime/nuxt/plugins/route-rule-og-image.server") });
557
+ addPlugin({ mode: "server", src: resolve("./runtime/nuxt/plugins/og-image-canonical-urls.server") });
394
558
  const ogImageComponentCtx = { components: [] };
395
559
  nuxt.hook("components:extend", (components) => {
396
560
  ogImageComponentCtx.components = [];
@@ -408,8 +572,6 @@ const module = defineNuxtModule({
408
572
  let category = "app";
409
573
  if (component.filePath.includes(resolve("./runtime/components/Templates/Community")))
410
574
  category = "community";
411
- else if (component.filePath.includes(resolve("./runtime/components/Templates/Official")))
412
- category = "official";
413
575
  const componentFile = fs.readFileSync(component.filePath, "utf-8");
414
576
  const credits = componentFile.split("\n").find((line) => line.startsWith(" * @credits"))?.replace("* @credits", "").trim();
415
577
  ogImageComponentCtx.components.push({
@@ -426,7 +588,7 @@ const module = defineNuxtModule({
426
588
  nuxt.hooks.hook("nuxt-og-image:components", ogImageComponentCtx);
427
589
  });
428
590
  addTemplate({
429
- filename: "og-image-component-names.mjs",
591
+ filename: "nuxt-og-image/components.mjs",
430
592
  getContents() {
431
593
  return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
432
594
  },
@@ -436,6 +598,16 @@ const module = defineNuxtModule({
436
598
  nuxt.options.nitro.virtual["#nuxt-og-image/component-names.mjs"] = () => {
437
599
  return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
438
600
  };
601
+ let unoCssConfig = { theme: {} };
602
+ nuxt.hook("tailwindcss:config", (tailwindConfig) => {
603
+ unoCssConfig = defu(tailwindConfig.theme.extend, { ...tailwindConfig.theme || {}, extend: void 0 });
604
+ });
605
+ nuxt.hook("unocss:config", (_unoCssConfig) => {
606
+ unoCssConfig = { ..._unoCssConfig.theme };
607
+ });
608
+ nuxt.options.nitro.virtual["#nuxt-og-image/unocss-config.mjs"] = () => {
609
+ return `export const theme = ${JSON.stringify(unoCssConfig)}`;
610
+ };
439
611
  extendTypes("nuxt-og-image", ({ typesPath }) => {
440
612
  const componentImports = ogImageComponentCtx.components.map((component) => {
441
613
  const relativeComponentPath = relative(resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"), component.path);
@@ -444,12 +616,17 @@ const module = defineNuxtModule({
444
616
  return `
445
617
  declare module 'nitropack' {
446
618
  interface NitroRouteRules {
447
- ogImage?: false | import('${typesPath}').OgImageOptions
619
+ ogImage?: false | import('${typesPath}').OgImageOptions & Record<string, any>
448
620
  }
449
621
  interface NitroRouteConfig {
450
- ogImage?: false | import('${typesPath}').OgImageOptions
622
+ ogImage?: false | import('${typesPath}').OgImageOptions & Record<string, any>
623
+ }
624
+ interface NitroRuntimeHooks {
625
+ 'nuxt-og-image:context': (ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
626
+ 'nuxt-og-image:satori:vnodes': (vnodes: import('${typesPath}').VNode, ctx: ctx: import('${typesPath}').OgImageRenderEventContext) => void | Promise<void>
451
627
  }
452
628
  }
629
+
453
630
  declare module '#nuxt-og-image/components' {
454
631
  export interface OgImageComponents {
455
632
  ${componentImports}
@@ -457,42 +634,44 @@ ${componentImports}
457
634
  }
458
635
  `;
459
636
  });
637
+ const cacheEnabled = typeof config.runtimeCacheStorage !== "undefined" && config.runtimeCacheStorage !== false;
638
+ const runtimeCacheStorage = typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver;
639
+ let baseCacheKey = runtimeCacheStorage === "default" ? `/cache/nuxt-og-image@${version}` : `/nuxt-og-image@${version}`;
640
+ if (!cacheEnabled)
641
+ baseCacheKey = false;
460
642
  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
- });
643
+ const normalisedFonts = normaliseFontInput(config.fonts);
473
644
  if (!nuxt.options._generate && nuxt.options.build) {
474
645
  nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
475
646
  nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
476
- normalisedFonts.filter((f) => !f.path).forEach(({ name, weight }) => {
647
+ normalisedFonts.filter((f) => !f.path && !f.key).forEach(({ name, weight }) => {
477
648
  nuxt.options.nitro.prerender.routes.push(`/__og-image__/font/${name}/${weight}.ttf`);
478
649
  });
479
650
  }
480
- nuxt.options.runtimeConfig["nuxt-og-image"] = {
651
+ const hasColorModeModule = hasNuxtModule("@nuxtjs/color-mode");
652
+ const colorModeOptions = hasColorModeModule ? await getNuxtModuleOptions("@nuxtjs/color-mode") : {};
653
+ let colorPreference = colorModeOptions.preference;
654
+ if (!colorPreference || colorPreference === "system")
655
+ colorPreference = colorModeOptions.fallback;
656
+ if (!colorPreference || colorPreference === "system")
657
+ colorPreference = "light";
658
+ const runtimeConfig = {
481
659
  version,
482
660
  // binding options
483
661
  satoriOptions: config.satoriOptions || {},
484
662
  resvgOptions: config.resvgOptions || {},
485
663
  sharpOptions: config.sharpOptions || {},
486
- runtimeSatori: config.runtimeSatori,
487
- runtimeBrowser: config.runtimeBrowser,
488
- // @ts-expect-error runtime type
489
664
  defaults: config.defaults,
665
+ debug: config.debug,
490
666
  // avoid adding credentials
491
- runtimeCacheStorage: typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver,
667
+ baseCacheKey,
492
668
  // convert the fonts to uniform type to fix ts issue
493
669
  fonts: normalisedFonts,
494
- hasNuxtIcon: hasNuxtModule("nuxt-icon")
670
+ hasNuxtIcon: hasNuxtModule("nuxt-icon"),
671
+ colorPreference
495
672
  };
673
+ nuxt.hooks.callHook("nuxt-og-image:runtime-config", runtimeConfig);
674
+ nuxt.options.runtimeConfig["nuxt-og-image"] = runtimeConfig;
496
675
  });
497
676
  if (nuxt.options.dev) {
498
677
  setupDevHandler(config, resolve);
@@ -502,8 +681,9 @@ ${componentImports}
502
681
  } else if (nuxt.options.build) {
503
682
  await setupBuildHandler(config, resolve);
504
683
  }
505
- if (nuxt.options.nitro.prerender?.routes || nuxt.options._generate)
506
- setupPrerenderHandler(config, resolve);
684
+ if (nuxt.options.build)
685
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
686
+ setupPrerenderHandler(config, resolve);
507
687
  }
508
688
  });
509
689