nuxt-og-image 2.2.4 → 3.0.0-beta.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 (173) hide show
  1. package/README.md +2 -2
  2. package/dist/client/200.html +8 -8
  3. package/dist/client/404.html +8 -8
  4. package/dist/client/_nuxt/IconCSS.8f429b14.css +1 -0
  5. package/dist/client/_nuxt/IconCSS.ac398b56.js +1 -0
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/d1d517c3-4927-4803-bbb0-d94e9d3e9581.json +1 -0
  8. package/dist/client/_nuxt/entry.434c2c45.css +1 -0
  9. package/dist/client/_nuxt/entry.bdb8a8d5.js +137 -0
  10. package/dist/client/_nuxt/{error-404.407d76a3.js → error-404.f37119e7.js} +1 -1
  11. package/dist/client/_nuxt/{error-500.531c4147.js → error-500.74b0a30f.js} +1 -1
  12. package/dist/client/grid.png +0 -0
  13. package/dist/client/index.html +8 -8
  14. package/dist/module.d.mts +43 -39
  15. package/dist/module.d.ts +43 -39
  16. package/dist/module.json +1 -1
  17. package/dist/module.mjs +341 -667
  18. package/dist/runtime/cache.d.ts +4 -4
  19. package/dist/runtime/cache.mjs +2 -1
  20. package/dist/runtime/components/OgImage/Cached.mjs +1 -1
  21. package/dist/runtime/components/OgImage/Dynamic.mjs +1 -1
  22. package/dist/runtime/components/OgImage/Screenshot.mjs +1 -1
  23. package/dist/runtime/components/OgImage/Static.mjs +1 -1
  24. package/dist/runtime/components/OgImage/WithoutCache.mjs +1 -1
  25. package/dist/runtime/components/OgImage/index.mjs +1 -1
  26. package/dist/runtime/components/Templates/Community/Nuxt.vue +183 -0
  27. package/dist/runtime/components/Templates/Official/BrandedLogo.vue +28 -0
  28. package/dist/runtime/components/Templates/Official/Fallback.vue +147 -0
  29. package/dist/runtime/components/Templates/Official/SimpleBlog.vue +33 -0
  30. package/dist/runtime/components/Templates/Official/Wave.vue +33 -0
  31. package/dist/runtime/components/Templates/Official/WithEmoji.vue +27 -0
  32. package/dist/runtime/composables/defineOgImage.d.ts +10 -6
  33. package/dist/runtime/composables/defineOgImage.mjs +21 -9
  34. package/dist/runtime/core/bindings/chromium/node.d.ts +2 -0
  35. package/dist/runtime/{nitro/providers/browser/universal.mjs → core/bindings/chromium/node.mjs} +3 -3
  36. package/dist/runtime/core/bindings/resvg/node.d.ts +6 -0
  37. package/dist/runtime/core/bindings/resvg/node.mjs +5 -0
  38. package/dist/runtime/core/bindings/resvg/wasm.d.ts +40 -0
  39. package/dist/runtime/core/bindings/resvg/wasm.mjs +7 -0
  40. package/dist/runtime/core/bindings/satori/node.d.ts +6 -0
  41. package/dist/runtime/core/bindings/satori/node.mjs +5 -0
  42. package/dist/runtime/core/bindings/satori/yoga-wasm.d.ts +6 -0
  43. package/dist/runtime/core/bindings/satori/yoga-wasm.mjs +7 -0
  44. package/dist/runtime/core/bindings/sharp/node.d.ts +2 -0
  45. package/dist/runtime/core/bindings/sharp/node.mjs +2 -0
  46. package/dist/runtime/core/bindings/sharp/wasm.d.ts +2 -0
  47. package/dist/runtime/core/bindings/sharp/wasm.mjs +2 -0
  48. package/dist/runtime/core/cache/prerender.d.ts +6 -0
  49. package/dist/runtime/core/cache/prerender.mjs +6 -0
  50. package/dist/runtime/core/env/assets.d.ts +2 -0
  51. package/dist/runtime/core/env/assets.mjs +15 -0
  52. package/dist/runtime/core/font/cache.d.ts +1 -0
  53. package/dist/runtime/core/font/cache.mjs +1 -0
  54. package/dist/runtime/core/font/fetch.d.ts +3 -0
  55. package/dist/runtime/core/font/fetch.mjs +29 -0
  56. package/dist/runtime/core/html/fetch.d.ts +3 -0
  57. package/dist/runtime/core/html/fetch.mjs +117 -0
  58. package/dist/runtime/core/options/extract.d.ts +3 -0
  59. package/dist/runtime/{nitro/utils-pure.mjs → core/options/extract.mjs} +23 -21
  60. package/dist/runtime/core/options/fetch.d.ts +3 -0
  61. package/dist/runtime/core/options/fetch.mjs +21 -0
  62. package/dist/runtime/core/options/normalise.d.ts +2 -0
  63. package/dist/runtime/{composables/util.mjs → core/options/normalise.mjs} +9 -6
  64. package/dist/runtime/core/renderers/chromium/index.d.ts +3 -0
  65. package/dist/runtime/core/renderers/chromium/index.mjs +26 -0
  66. package/dist/runtime/core/renderers/chromium/screenshot.d.ts +6 -0
  67. package/dist/runtime/core/renderers/chromium/screenshot.mjs +47 -0
  68. package/dist/runtime/core/renderers/satori/fonts.d.ts +3 -0
  69. package/dist/runtime/core/renderers/satori/fonts.mjs +8 -0
  70. package/dist/runtime/core/renderers/satori/index.d.ts +5 -0
  71. package/dist/runtime/core/renderers/satori/index.mjs +53 -0
  72. package/dist/runtime/core/renderers/satori/instances.d.ts +39 -0
  73. package/dist/runtime/core/renderers/satori/instances.mjs +17 -0
  74. package/dist/runtime/{nitro → core}/renderers/satori/plugins/encoding.mjs +1 -1
  75. package/dist/runtime/{nitro → core}/renderers/satori/plugins/imageSrc.mjs +9 -14
  76. package/dist/runtime/{nitro → core}/renderers/satori/plugins/twClasses.mjs +1 -0
  77. package/dist/runtime/core/renderers/satori/utils.d.ts +4 -0
  78. package/dist/runtime/core/renderers/satori/utils.mjs +20 -0
  79. package/dist/runtime/core/renderers/satori/vnodes.d.ts +3 -0
  80. package/dist/runtime/core/renderers/satori/vnodes.mjs +21 -0
  81. package/dist/runtime/core/utils/resolveRendererContext.d.ts +7 -0
  82. package/dist/runtime/core/utils/resolveRendererContext.mjs +76 -0
  83. package/dist/runtime/nitro/plugins/nuxt-content.d.ts +2 -0
  84. package/dist/runtime/nitro/plugins/nuxt-content.mjs +50 -0
  85. package/dist/runtime/nitro/plugins/prerender.d.ts +2 -3
  86. package/dist/runtime/nitro/plugins/prerender.mjs +25 -33
  87. package/dist/runtime/nuxt/plugins/nuxt-content-canonical-urls.mjs +29 -0
  88. package/dist/runtime/nuxt/plugins/route-rule-og-image.server.d.ts +2 -0
  89. package/dist/runtime/nuxt/plugins/route-rule-og-image.server.mjs +72 -0
  90. package/dist/runtime/{nitro/routes/debug.d.ts → server/routes/__og-image__/debug.json.d.ts} +1 -1
  91. package/dist/runtime/{nitro/routes/debug.mjs → server/routes/__og-image__/debug.json.mjs} +3 -2
  92. package/dist/runtime/server/routes/__og-image__/font-[name]-[weight].[extension].mjs +30 -0
  93. package/dist/runtime/server/routes/__og-image__/image-[path]-og.[extension].mjs +44 -0
  94. package/dist/runtime/types.d.ts +29 -24
  95. package/dist/runtime/utilts.d.ts +2 -0
  96. package/dist/runtime/utilts.mjs +8 -0
  97. package/dist/types.d.mts +3 -2
  98. package/dist/types.d.ts +3 -2
  99. package/package.json +37 -22
  100. package/dist/client/_nuxt/IconCSS.4a9d43d0.css +0 -1
  101. package/dist/client/_nuxt/IconCSS.9c30257a.js +0 -1
  102. package/dist/client/_nuxt/ImageLoader.752b0c7a.js +0 -1
  103. package/dist/client/_nuxt/ImageLoader.7571516f.css +0 -1
  104. package/dist/client/_nuxt/builds/meta/bb64bb30-cf6f-4625-97ba-06e6a0d3f8d1.json +0 -1
  105. package/dist/client/_nuxt/entry.39e39f51.css +0 -1
  106. package/dist/client/_nuxt/entry.ac864471.js +0 -135
  107. package/dist/client/_nuxt/index.dc1538d5.js +0 -1
  108. package/dist/client/_nuxt/index.ffbea0a9.css +0 -1
  109. package/dist/client/_nuxt/options.a77f5921.js +0 -1
  110. package/dist/client/_nuxt/png.41e0b446.js +0 -1
  111. package/dist/client/_nuxt/shiki.d4e62362.js +0 -7
  112. package/dist/client/_nuxt/svg.b8198280.js +0 -1
  113. package/dist/client/_nuxt/vnodes.67720126.js +0 -1
  114. package/dist/client/options/index.html +0 -15
  115. package/dist/client/png/index.html +0 -15
  116. package/dist/client/svg/index.html +0 -15
  117. package/dist/client/vnodes/index.html +0 -15
  118. package/dist/runtime/browserUtil.d.ts +0 -5
  119. package/dist/runtime/browserUtil.mjs +0 -41
  120. package/dist/runtime/components/OgImageTemplate/Fallback.vue +0 -161
  121. package/dist/runtime/composables/util.d.ts +0 -2
  122. package/dist/runtime/nitro/middleware/og.png.mjs +0 -69
  123. package/dist/runtime/nitro/middleware/playground.d.ts +0 -2
  124. package/dist/runtime/nitro/middleware/playground.mjs +0 -27
  125. package/dist/runtime/nitro/providers/browser/lambda.d.ts +0 -1
  126. package/dist/runtime/nitro/providers/browser/lambda.mjs +0 -9
  127. package/dist/runtime/nitro/providers/browser/playwright.d.ts +0 -1
  128. package/dist/runtime/nitro/providers/browser/playwright.mjs +0 -22
  129. package/dist/runtime/nitro/providers/browser/universal.d.ts +0 -2
  130. package/dist/runtime/nitro/providers/png/resvg-node.d.ts +0 -4
  131. package/dist/runtime/nitro/providers/png/resvg-node.mjs +0 -6
  132. package/dist/runtime/nitro/providers/png/resvg-wasm.d.ts +0 -3
  133. package/dist/runtime/nitro/providers/png/resvg-wasm.mjs +0 -11
  134. package/dist/runtime/nitro/providers/png/svg2png.d.ts +0 -3
  135. package/dist/runtime/nitro/providers/png/svg2png.mjs +0 -11
  136. package/dist/runtime/nitro/providers/satori/default.d.ts +0 -2
  137. package/dist/runtime/nitro/providers/satori/default.mjs +0 -4
  138. package/dist/runtime/nitro/providers/satori/yoga-wasm.d.ts +0 -3
  139. package/dist/runtime/nitro/providers/satori/yoga-wasm.mjs +0 -10
  140. package/dist/runtime/nitro/renderers/browser.d.ts +0 -3
  141. package/dist/runtime/nitro/renderers/browser.mjs +0 -36
  142. package/dist/runtime/nitro/renderers/satori/index.d.ts +0 -3
  143. package/dist/runtime/nitro/renderers/satori/index.mjs +0 -58
  144. package/dist/runtime/nitro/renderers/satori/utils.d.ts +0 -4
  145. package/dist/runtime/nitro/renderers/satori/utils.mjs +0 -60
  146. package/dist/runtime/nitro/routes/font.mjs +0 -22
  147. package/dist/runtime/nitro/routes/html.d.ts +0 -2
  148. package/dist/runtime/nitro/routes/html.mjs +0 -178
  149. package/dist/runtime/nitro/routes/options.d.ts +0 -3
  150. package/dist/runtime/nitro/routes/options.mjs +0 -35
  151. package/dist/runtime/nitro/routes/svg.mjs +0 -19
  152. package/dist/runtime/nitro/routes/vnode.d.ts +0 -2
  153. package/dist/runtime/nitro/routes/vnode.mjs +0 -19
  154. package/dist/runtime/nitro/utils-pure.d.ts +0 -3
  155. package/dist/runtime/nitro/utils.d.ts +0 -18
  156. package/dist/runtime/nitro/utils.mjs +0 -108
  157. package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
  158. package/dist/runtime/public-assets-optional/svg2png/svg2png.wasm +0 -0
  159. package/dist/runtime/public-assets-optional/yoga/yoga.wasm +0 -0
  160. /package/dist/runtime/{nitro/providers → core/bindings}/css-inline/mock.d.ts +0 -0
  161. /package/dist/runtime/{nitro/providers → core/bindings}/css-inline/mock.mjs +0 -0
  162. /package/dist/runtime/{nitro/providers/css-inline/css-inline.d.ts → core/bindings/css-inline/node.d.ts} +0 -0
  163. /package/dist/runtime/{nitro/providers/css-inline/css-inline.mjs → core/bindings/css-inline/node.mjs} +0 -0
  164. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/emojis.d.ts +0 -0
  165. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/emojis.mjs +0 -0
  166. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/encoding.d.ts +0 -0
  167. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/flex.d.ts +0 -0
  168. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/flex.mjs +0 -0
  169. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/imageSrc.d.ts +0 -0
  170. /package/dist/runtime/{nitro → core}/renderers/satori/plugins/twClasses.d.ts +0 -0
  171. /package/dist/runtime/{nitro/routes/font.d.ts → nuxt/plugins/nuxt-content-canonical-urls.d.ts} +0 -0
  172. /package/dist/runtime/{nitro/middleware/og.png.d.ts → server/routes/__og-image__/font-[name]-[weight].[extension].d.ts} +0 -0
  173. /package/dist/runtime/{nitro/routes/svg.d.ts → server/routes/__og-image__/image-[path]-og.[extension].d.ts} +0 -0
package/dist/module.mjs CHANGED
@@ -1,351 +1,153 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
2
1
  import * as fs from 'node:fs';
3
2
  import { existsSync } from 'node:fs';
4
- import { useNuxt, createResolver, addTemplate, defineNuxtModule, useLogger, addServerHandler, addImports, addComponent, hasNuxtModule, addServerPlugin } from '@nuxt/kit';
5
- import { execa } from 'execa';
6
- import chalk from 'chalk';
7
- import defu$1, { defu } from 'defu';
8
- import { toRouteMatcher, createRouter } from 'radix3';
9
- import { withBase, parsePath, joinURL } from 'ufo';
10
- import { resolve, relative, dirname } from 'pathe';
11
- import { tinyws } from 'tinyws';
12
- import sirv from 'sirv';
13
- import { pathExists, copy, mkdirp } from 'fs-extra';
14
- import { globby } from 'globby';
15
- import { installNuxtSiteConfig, updateSiteConfig } from 'nuxt-site-config-kit';
16
- import { provider } from 'std-env';
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';
17
6
  import { hash } from 'ohash';
18
- import terminate from 'terminate';
19
- import playwrightCore from 'playwright-core';
20
- import { createBirpcGroup } from 'birpc';
21
- import { stringify, parse } from 'flatted';
22
- import { addDependency } from 'nypm';
7
+ import { relative } from 'pathe';
8
+ import 'nypm';
9
+ import { defu } from 'defu';
10
+ import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
23
11
 
24
- const version = "2.2.4";
12
+ const version = "3.0.0-beta.1";
25
13
 
26
- async function createBrowser() {
27
- if (process.dev || process.env.prerender) {
28
- try {
29
- const { Launcher } = await import(String("chrome-launcher"));
30
- const chromePath = Launcher.getFirstInstallation();
31
- return await playwrightCore.chromium.launch({
32
- headless: true,
33
- executablePath: chromePath
34
- });
35
- } catch (e) {
36
- }
37
- }
38
- try {
39
- return await playwrightCore.chromium.launch({
40
- headless: true
41
- });
42
- } catch (e) {
43
- }
44
- try {
45
- const playwright = await import(String("playwright"));
46
- return await playwright.chromium.launch({
47
- headless: true
48
- });
49
- } catch (e) {
50
- if (process.dev) {
51
- console.warn("Failed to load chromium instance. Ensure you have chrome installed, otherwise add the dependency: `npm add -D playwright`.");
52
- } else {
53
- console.error("Failed to load browser instance. Please open an issue with the exception: https://github.com/harlan-zw/nuxt-og-image/issues.");
54
- throw e;
55
- }
56
- }
57
- }
58
-
59
- async function screenshot(browser, options) {
60
- const page = await browser.newPage({
61
- colorScheme: options.colorScheme
62
- });
63
- await page.setViewportSize({
64
- width: options.width || 1200,
65
- height: options.height || 630
66
- });
67
- const isHtml = options.html || options.path?.startsWith("html:");
68
- if (isHtml) {
69
- const html = options.html || options.path?.substring(5);
70
- await page.evaluate((html2) => {
71
- document.open("text/html");
72
- document.write(html2);
73
- document.close();
74
- }, html);
75
- await page.waitForLoadState("networkidle");
76
- } else {
77
- await page.goto(withBase(options.path, options.host), {
78
- timeout: 1e4,
79
- waitUntil: "networkidle"
80
- });
81
- }
82
- const screenshotOptions = {
83
- timeout: 1e4
84
- };
85
- if (options.delay)
86
- await page.waitForTimeout(options.delay);
87
- if (options.mask) {
88
- await page.evaluate((mask) => {
89
- for (const el of document.querySelectorAll(mask))
90
- el.style.display = "none";
91
- }, options.mask);
92
- }
93
- if (options.selector)
94
- return await page.locator(options.selector).screenshot(screenshotOptions);
95
- const screenshot2 = await page.screenshot(screenshotOptions);
96
- await page.close();
97
- return screenshot2;
98
- }
99
-
100
- function setupPlaygroundRPC(nuxt, config) {
101
- const serverFunctions = {
102
- getConfig() {
103
- return config;
104
- },
105
- async openInEditor(input) {
106
- if (input.startsWith("./"))
107
- input = resolve(process.cwd(), input);
108
- const match = input.match(/^(.*?)([:\d]*)$/);
109
- let suffix = "";
110
- if (match) {
111
- input = match[1];
112
- suffix = match[2];
113
- }
114
- const file = [
115
- input,
116
- `${input}.js`,
117
- `${input}.mjs`,
118
- `${input}.ts`
119
- ].find((i) => existsSync(i));
120
- if (file) {
121
- await import('launch-editor').then((r) => (r.default || r)(file + suffix));
122
- } else {
123
- console.error("File not found:", input);
124
- }
125
- }
126
- };
127
- const clients = /* @__PURE__ */ new Set();
128
- const birpc = createBirpcGroup(serverFunctions, []);
129
- nuxt.hook("builder:watch", (e, path) => {
130
- if (e === "change")
131
- birpc.broadcast.refresh.asEvent(path);
132
- });
133
- const middleware = async (req, res) => {
134
- if (req.ws) {
135
- const ws = await req.ws();
136
- clients.add(ws);
137
- const channel = {
138
- post: (d) => ws.send(d),
139
- on: (fn) => ws.on("message", fn),
140
- serialize: stringify,
141
- deserialize: parse
142
- };
143
- birpc.updateChannels((c) => {
144
- c.push(channel);
145
- });
146
- ws.on("close", () => {
147
- clients.delete(ws);
148
- birpc.updateChannels((c) => {
149
- const index = c.indexOf(channel);
150
- if (index >= 0)
151
- c.splice(index, 1);
152
- });
153
- });
154
- } else if (req.method === "POST") {
155
- const body = await getBodyJson(req);
156
- if (body.method === "setPayload") ; else {
157
- res.statusCode = 400;
158
- }
159
- res.end();
160
- }
161
- };
162
- return {
163
- middleware,
164
- birpc
165
- };
166
- }
167
- function getBodyJson(req) {
168
- return new Promise((resolve2, reject) => {
169
- let body = "";
170
- req.on("data", (chunk) => body += chunk);
171
- req.on("error", reject);
172
- req.on("end", () => {
173
- try {
174
- resolve2(JSON.parse(body) || {});
175
- } catch (e) {
176
- reject(e);
177
- }
178
- });
179
- });
180
- }
181
-
182
- function decodeHtml(html) {
183
- 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) => {
184
- return String.fromCharCode(Number.parseInt(int));
185
- });
186
- }
187
- function decodeObjectHtmlEntities(obj) {
188
- Object.entries(obj).forEach(([key, value]) => {
189
- if (typeof value === "string")
190
- obj[key] = decodeHtml(value);
191
- });
192
- return obj;
193
- }
194
- function extractAndNormaliseOgImageOptions(path, html, routeRules, defaults) {
195
- const htmlPayload = html.match(/<script.+id="nuxt-og-image-options"[^>]*>(.+?)<\/script>/)?.[1];
196
- if (!htmlPayload)
197
- return false;
198
- let options;
199
- try {
200
- const payload = JSON.parse(htmlPayload);
201
- Object.entries(payload).forEach(([key, value]) => {
202
- if (!value)
203
- delete payload[key];
204
- });
205
- options = defu(payload, routeRules);
206
- } catch (e) {
207
- options = routeRules;
208
- if (process.dev)
209
- console.warn("Failed to parse #nuxt-og-image-options", e, options);
210
- }
211
- if (!options)
212
- return false;
213
- if (!options.description) {
214
- const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
215
- if (description)
216
- options.description = description;
217
- else
218
- options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
219
- }
220
- const decoded = decodeObjectHtmlEntities(options);
221
- return defu(
222
- decoded,
223
- // runtime options
224
- { path },
225
- defaults
226
- );
227
- }
228
-
229
- const SVG2PNGWasmPlaceholder = '"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"';
230
- const YogaWasmPlaceholder = '"/* NUXT_OG_IMAGE_YOGA_WASM */"';
231
- const ReSVGWasmPlaceholder = '"/* NUXT_OG_IMAGE_RESVG_WASM */"';
232
- const Wasms = [
233
- {
234
- placeholder: SVG2PNGWasmPlaceholder,
235
- path: "svg2png/svg2png.wasm",
236
- file: "svg2png.wasm"
237
- },
238
- {
239
- placeholder: ReSVGWasmPlaceholder,
240
- path: "resvg/resvg.wasm",
241
- file: "resvg.wasm"
242
- },
243
- {
244
- placeholder: YogaWasmPlaceholder,
245
- path: "yoga/yoga.wasm",
246
- file: "yoga.wasm"
247
- }
248
- ];
249
- const DefaultRuntimeCompatibility = {
14
+ const autodetectableProviders = {
15
+ azure_static: "azure",
16
+ cloudflare_pages: "cloudflare-pages",
17
+ netlify: "netlify",
18
+ stormkit: "stormkit",
19
+ vercel: "vercel",
20
+ cleavr: "cleavr",
21
+ stackblitz: "stackblitz"
22
+ };
23
+ const autodetectableStaticProviders = {
24
+ netlify: "netlify-static",
25
+ vercel: "vercel-static"
26
+ };
27
+ const NodeRuntime = {
250
28
  // node-server runtime
251
- browser: "playwright",
252
- satori: "default",
253
- wasm: "fetch",
254
- png: "resvg-node",
255
- node: true,
256
- cssInline: true
29
+ bindings: {
30
+ "chromium": "node",
31
+ "css-inline": "node",
32
+ "resvg": "node",
33
+ "satori": "node",
34
+ "sharp": "node"
35
+ },
36
+ wasmStrategy: "import"
257
37
  };
258
38
  const cloudflare = {
259
- browser: false,
260
- wasm: "import",
261
- png: "resvg-wasm",
262
- node: false,
263
- cssInline: false
39
+ bindings: {
40
+ "chromium": false,
41
+ "css-inline": "node",
42
+ "resvg": "wasm",
43
+ "satori": "node",
44
+ "sharp": false
45
+ },
46
+ wasmStrategy: "import"
264
47
  };
265
48
  const awsLambda = {
266
- browser: false,
267
- // too difficult to support
268
- wasm: "inline",
269
- cssInline: false
49
+ bindings: {
50
+ "chromium": false,
51
+ "css-inline": "node",
52
+ "resvg": "wasm",
53
+ "satori": "node",
54
+ "sharp": "node"
55
+ },
56
+ wasmStrategy: "inline"
270
57
  };
271
58
  const RuntimeCompatibility = {
272
- "nitro-dev": {
273
- wasm: "fetch",
274
- browser: "universal"
275
- },
59
+ "nitro-dev": NodeRuntime,
60
+ "nitro-prerender": NodeRuntime,
61
+ "node-server": NodeRuntime,
276
62
  "stackblitz": {
277
- browser: false,
278
- satori: "yoga-wasm",
279
- wasm: "inline",
280
- png: "resvg-wasm",
281
- cssInline: false
63
+ bindings: {
64
+ "chromium": false,
65
+ "css-inline": "node",
66
+ "resvg": "wasm",
67
+ "satori": "node",
68
+ "sharp": "node"
69
+ },
70
+ wasmStrategy: "inline"
282
71
  },
283
72
  "aws-lambda": awsLambda,
284
73
  "netlify": awsLambda,
285
74
  "netlify-edge": {
286
- wasm: "inline",
287
- png: "resvg-wasm",
288
- node: false,
289
- cssInline: false
75
+ bindings: {
76
+ "chromium": false,
77
+ "css-inline": "node",
78
+ "resvg": "wasm",
79
+ "satori": "node",
80
+ "sharp": "node"
81
+ },
82
+ wasmStrategy: "inline"
290
83
  },
291
84
  "vercel": {
292
- // exceeds 50mb limit
293
- browser: false
85
+ bindings: {
86
+ "chromium": false,
87
+ "css-inline": "node",
88
+ "resvg": "wasm",
89
+ "satori": "node",
90
+ "sharp": "node"
91
+ },
92
+ wasmStrategy: "inline"
294
93
  },
295
94
  "vercel-edge": {
296
- browser: false,
297
- wasm: "import",
298
- wasmImportQuery: "?module",
299
- png: "resvg-wasm",
300
- node: false,
301
- cssInline: false
95
+ bindings: {
96
+ "chromium": false,
97
+ "css-inline": "node",
98
+ "resvg": "wasm",
99
+ "satori": "node",
100
+ "sharp": "node"
101
+ },
102
+ wasmStrategy: "inline",
103
+ wasmImportQuery: "?module"
302
104
  },
303
105
  "cloudflare-pages": cloudflare,
304
106
  "cloudflare": cloudflare
305
107
  };
306
-
307
- const autodetectableProviders = {
308
- azure_static: "azure",
309
- cloudflare_pages: "cloudflare-pages",
310
- netlify: "netlify",
311
- stormkit: "stormkit",
312
- vercel: "vercel",
313
- cleavr: "cleavr",
314
- stackblitz: "stackblitz"
315
- };
316
- const autodetectableStaticProviders = {
317
- netlify: "netlify-static",
318
- vercel: "vercel-static"
319
- };
320
108
  function detectTarget(options = {}) {
321
109
  return options?.static ? autodetectableStaticProviders[provider] : autodetectableProviders[provider];
322
110
  }
323
- function getNitroPreset(nuxt = useNuxt()) {
324
- return process.env.NITRO_PRESET || nuxt.options.nitro.preset || detectTarget() || "node-server";
111
+ function resolveNitroPreset(nitroConfig) {
112
+ if (provider === "stackblitz")
113
+ return "stackblitz";
114
+ let preset;
115
+ if (nitroConfig && nitroConfig?.preset)
116
+ preset = nitroConfig.preset;
117
+ if (!preset)
118
+ preset = env.NITRO_PRESET || detectTarget() || "node-server";
119
+ return preset.replace("_", "-");
325
120
  }
326
- function getNitroProviderCompatibility(overrides, nuxt = useNuxt()) {
327
- let compatibility;
328
- if (provider === "stackblitz") {
329
- compatibility = RuntimeCompatibility.stackblitz;
330
- } else if (nuxt.options.dev || nuxt.options._prepare || nuxt.options._generate) {
331
- compatibility = {
332
- wasm: "fetch",
333
- browser: "universal"
334
- };
335
- } else {
336
- const target = getNitroPreset(nuxt);
337
- const lookup = RuntimeCompatibility[target];
338
- if (lookup === false)
339
- return false;
340
- compatibility = lookup || {};
341
- }
342
- compatibility = defu$1(overrides, compatibility, DefaultRuntimeCompatibility);
121
+ function getPresetNitroPresetCompatibility(target) {
122
+ let compatibility = RuntimeCompatibility[target];
123
+ if (!compatibility)
124
+ compatibility = RuntimeCompatibility["nitro-dev"];
343
125
  return compatibility;
344
126
  }
345
- function ensureDependencies(nuxt, dep) {
346
- return Promise.all(dep.map((d) => {
347
- return addDependency(d, { cwd: nuxt.options.rootDir });
348
- }));
127
+ function applyNitroPresetCompatibility(nitroConfig, options) {
128
+ let compatibility = options?.compatibility;
129
+ const target = resolveNitroPreset(nitroConfig);
130
+ if (!compatibility)
131
+ compatibility = getPresetNitroPresetCompatibility(target);
132
+ const resolve = options.resolve;
133
+ function applyBinding(key) {
134
+ const binding = compatibility.bindings[key];
135
+ if (binding === false)
136
+ return { [`#nuxt-og-image/bindings/${key}`]: "unenv/runtime/mock/empty" };
137
+ return { [`#nuxt-og-image/bindings/${key}`]: resolve(`./runtime/core/bindings/${key}/${binding}`) };
138
+ }
139
+ nitroConfig.alias = defu(
140
+ applyBinding("chromium"),
141
+ applyBinding("satori"),
142
+ applyBinding("resvg"),
143
+ applyBinding("sharp"),
144
+ nitroConfig.alias || {}
145
+ );
146
+ if (target.includes("cloudflare")) {
147
+ nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
148
+ nitroConfig.rollupConfig.output.inlineDynamicImports = true;
149
+ }
150
+ return compatibility;
349
151
  }
350
152
 
351
153
  function extendTypes(module, template) {
@@ -367,9 +169,113 @@ export {}
367
169
  });
368
170
  }
369
171
 
370
- const PATH = "/__nuxt_og_image__";
371
- const PATH_ENTRY = `${PATH}/entry`;
372
- const PATH_PLAYGROUND = `${PATH}/client`;
172
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-og-image";
173
+ const DEVTOOLS_UI_LOCAL_PORT = 3030;
174
+ function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
175
+ const clientPath = resolve("./client");
176
+ const isProductionBuild = existsSync(clientPath);
177
+ if (isProductionBuild) {
178
+ nuxt.hook("vite:serverCreated", async (server) => {
179
+ const sirv = await import('sirv').then((r) => r.default || r);
180
+ server.middlewares.use(
181
+ DEVTOOLS_UI_ROUTE,
182
+ sirv(clientPath, { dev: true, single: true })
183
+ );
184
+ });
185
+ } else {
186
+ nuxt.hook("vite:extendConfig", (config) => {
187
+ config.server = config.server || {};
188
+ config.server.proxy = config.server.proxy || {};
189
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
190
+ target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
191
+ changeOrigin: true,
192
+ followRedirects: true,
193
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
194
+ };
195
+ });
196
+ }
197
+ onDevToolsInitialized(async () => {
198
+ const rpc = extendServerRpc("nuxt-og-image", {});
199
+ nuxt.hook("builder:watch", (e, path) => {
200
+ if ((e === "change" || e.includes("link")) && path.startsWith("pages")) {
201
+ rpc.broadcast.refreshRouteData(path).catch(() => {
202
+ });
203
+ }
204
+ if (options.componentDirs.some((dir) => path.includes(dir)))
205
+ rpc.broadcast.refreshGlobalData().catch(() => {
206
+ });
207
+ });
208
+ });
209
+ nuxt.hook("devtools:customTabs", (tabs) => {
210
+ tabs.push({
211
+ // unique identifier
212
+ name: "nuxt-og-image",
213
+ // title to display in the tab
214
+ title: "OG Image",
215
+ // any icon from Iconify, or a URL to an image
216
+ icon: "carbon:image-search",
217
+ // iframe view
218
+ view: {
219
+ type: "iframe",
220
+ src: DEVTOOLS_UI_ROUTE
221
+ }
222
+ });
223
+ });
224
+ }
225
+
226
+ function setupDevHandler(options, resolve, nuxt = useNuxt()) {
227
+ nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
228
+ nitroConfig.alias["#nuxt-og-image/renderers/satori"] = options.runtimeSatori ? resolve("./runtime/core/renderers/satori") : "unenv/runtime/mock/empty";
229
+ nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = options.runtimeBrowser ? resolve("./runtime/core/renderers/chromium") : "unenv/runtime/mock/empty";
230
+ applyNitroPresetCompatibility(nitroConfig, { resolve });
231
+ });
232
+ }
233
+
234
+ function setupGenerateHandler(options, resolve, nuxt = useNuxt()) {
235
+ nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
236
+ nitroConfig.alias["#nuxt-og-image/renderers/satori"] = "unenv/runtime/mock/empty";
237
+ nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = "unenv/runtime/mock/empty";
238
+ applyNitroPresetCompatibility(nitroConfig, {
239
+ compatibility: {
240
+ bindings: {
241
+ "css-inline": false,
242
+ "chromium": false,
243
+ "resvg": false,
244
+ "satori": false,
245
+ "sharp": false
246
+ }
247
+ },
248
+ resolve
249
+ });
250
+ });
251
+ }
252
+
253
+ function setupPrerenderHandler(options, resolve, nuxt = useNuxt()) {
254
+ addServerPlugin(resolve("./runtime/nitro/plugins/prerender.ts"));
255
+ nuxt.hooks.hook("nitro:init", async (nitro) => {
256
+ nitro.hooks.hook("prerender:config", async (nitroConfig) => {
257
+ nitroConfig.alias["#nuxt-og-image/renderers/satori"] = resolve("./runtime/core/renderers/satori");
258
+ nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = resolve("./runtime/core/renderers/chromium");
259
+ applyNitroPresetCompatibility(nitroConfig, { resolve });
260
+ });
261
+ });
262
+ }
263
+
264
+ async function setupBuildHandler(config, resolve, nuxt = useNuxt()) {
265
+ nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
266
+ if (typeof config.runtimeCacheStorage === "object")
267
+ nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
268
+ nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
269
+ nitroConfig.alias["#nuxt-og-image/renderers/satori"] = config.runtimeSatori ? resolve("./runtime/core/renderers/satori") : "unenv/runtime/mock/empty";
270
+ nitroConfig.alias["#nuxt-og-image/renderers/chromium"] = config.runtimeBrowser ? resolve("./runtime/core/renderers/chromium") : "unenv/runtime/mock/empty";
271
+ applyNitroPresetCompatibility(nitroConfig, { resolve });
272
+ nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
273
+ nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
274
+ nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
275
+ nitroConfig.alias.queue = "unenv/runtime/mock/proxy-cjs";
276
+ });
277
+ }
278
+
373
279
  const module = defineNuxtModule({
374
280
  meta: {
375
281
  name: "nuxt-og-image",
@@ -383,10 +289,10 @@ const module = defineNuxtModule({
383
289
  return {
384
290
  enabled: true,
385
291
  defaults: {
386
- provider: "satori",
387
- component: "OgImageTemplateFallback",
292
+ renderer: "satori",
293
+ component: "Fallback",
388
294
  width: 1200,
389
- height: 630,
295
+ height: 600,
390
296
  cache: true,
391
297
  // default is to cache the image for 1 day (24 hours)
392
298
  cacheTtl: 24 * 60 * 60 * 1e3
@@ -396,8 +302,7 @@ const module = defineNuxtModule({
396
302
  runtimeBrowser: nuxt.options.dev,
397
303
  fonts: [],
398
304
  runtimeCacheStorage: true,
399
- satoriOptions: {},
400
- playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
305
+ playground: env.NODE_ENV === "development" || nuxt.options.dev,
401
306
  debug: false
402
307
  };
403
308
  },
@@ -408,95 +313,41 @@ const module = defineNuxtModule({
408
313
  logger.debug("The module is disabled, skipping setup.");
409
314
  return;
410
315
  }
411
- const { resolve } = createResolver(import.meta.url);
412
- logger.debug("Using Nitro preset", getNitroPreset());
413
- const nitroCompatibility = getNitroProviderCompatibility(config.runtimeCompatibility || {});
414
- logger.debug("Nitro compatibility", nitroCompatibility);
415
- const nitroTarget = process.env.NITRO_PRESET || nuxt.options.nitro.preset || provider;
416
- if (!nitroCompatibility) {
417
- logger.warn(`\`nuxt-og-image\` does not support the nitro preset \`${nitroTarget}\`. Please make an issue. `);
316
+ if (config.enabled && !nuxt.options.ssr) {
317
+ logger.warn("Nuxt OG Image is enabled but SSR is disabled.\n\nYou should enable SSR (`ssr: true`) or disable the module (`ogImage: { enabled: false }`).");
418
318
  return;
419
319
  }
420
- if (!nitroCompatibility.browser && config.runtimeBrowser) {
421
- config.runtimeBrowser = false;
422
- logger.warn(`\`nuxt-og-image\` does not support the nitro target \`${nitroTarget}\` with the runtime browser. Set runtimeBrowser: false to stop seeing this.`);
423
- }
424
- if (config.runtimeBrowser && nitroCompatibility.browser === "lambda") {
425
- logger.info(`\`nuxt-og-image\` is deploying to nitro target \`${nitroTarget}\` that installs extra dependencies.`);
426
- await ensureDependencies(nuxt, ["puppeteer-core@14.1.1", "@sparticuz/chrome-aws-lambda@14.1.1"]);
427
- }
320
+ const { resolve } = createResolver(import.meta.url);
321
+ const preset = resolveNitroPreset(nuxt.options.nitro);
322
+ const compatibility = getPresetNitroPresetCompatibility(preset);
323
+ config.defaults.extension = "jpg";
324
+ if (!compatibility.bindings.sharp)
325
+ config.defaults.extension = "png";
428
326
  await installNuxtSiteConfig();
429
- updateSiteConfig({
430
- _context: "nuxt-og-image:config",
431
- url: config.siteUrl || config.host
432
- });
433
- nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
434
- if (nuxt.options._generate) {
435
- nuxt.options.nitro.storage["og-image"] = {
436
- driver: "memory"
437
- };
438
- } else if (config.runtimeCacheStorage && !nuxt.options.dev && typeof config.runtimeCacheStorage === "object") {
439
- nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
327
+ if (hasNuxtModule("@nuxt/content")) {
328
+ addServerPlugin(resolve("./runtime/nitro/plugins/nuxt-content"));
329
+ addPlugin(resolve("./runtime/nuxt/plugins/nuxt-content-canonical-urls"));
440
330
  }
441
331
  if (!config.fonts.length)
442
332
  config.fonts = ["Inter:400", "Inter:700"];
443
- const distResolve = (p) => {
444
- const cwd = resolve(".");
445
- if (cwd.endsWith("/dist"))
446
- return resolve(p);
447
- return resolve(`../dist/${p}`);
448
- };
449
333
  nuxt.options.experimental.componentIslands = true;
450
- extendTypes("nuxt-og-image", ({ typesPath }) => {
451
- return `
452
- declare module 'nitropack' {
453
- interface NitroRouteRules {
454
- ogImage?: false | import('${typesPath}').OgImageOptions
455
- }
456
- interface NitroRouteConfig {
457
- ogImage?: false | import('${typesPath}').OgImageOptions
458
- }
459
- }`;
460
- });
461
334
  addServerHandler({
462
335
  lazy: true,
463
- handler: resolve("./runtime/nitro/middleware/og.png")
464
- });
465
- ["html", "options", "svg", "vnode", "font", "debug"].forEach((type) => {
466
- if (type !== "debug" || config.debug) {
467
- addServerHandler({
468
- lazy: true,
469
- route: `/api/og-image-${type}`,
470
- handler: resolve(`./runtime/nitro/routes/${type}`)
471
- });
472
- }
473
- });
474
- nuxt.hook("devtools:customTabs", (iframeTabs) => {
475
- iframeTabs.push({
476
- name: "ogimage",
477
- title: "OG Image",
478
- icon: "carbon:image-search",
479
- view: {
480
- type: "iframe",
481
- src: "/__nuxt_og_image__/client/"
482
- }
483
- });
336
+ route: "/__og-image__/font/**",
337
+ handler: resolve("./runtime/server/routes/__og-image__/font-[name]-[weight].[extension]")
484
338
  });
485
- if (config.playground) {
486
- const playgroundDir = distResolve("./client");
487
- const {
488
- middleware: rpcMiddleware
489
- } = setupPlaygroundRPC(nuxt, config);
490
- nuxt.hook("vite:serverCreated", async (server) => {
491
- server.middlewares.use(PATH_ENTRY, tinyws());
492
- server.middlewares.use(PATH_ENTRY, rpcMiddleware);
493
- if (await pathExists(playgroundDir))
494
- server.middlewares.use(PATH_PLAYGROUND, sirv(playgroundDir, { single: true, dev: true }));
495
- });
339
+ if (config.debug || nuxt.options.dev) {
496
340
  addServerHandler({
497
- handler: resolve("./runtime/nitro/middleware/playground")
341
+ lazy: true,
342
+ route: "/__og-image__/debug.json",
343
+ handler: resolve("./runtime/server/routes/__og-image__/debug.json")
498
344
  });
499
345
  }
346
+ addServerHandler({
347
+ lazy: true,
348
+ route: "/__og-image__/image/**",
349
+ handler: resolve("./runtime/server/routes/__og-image__/image-[path]-og.[extension]")
350
+ });
500
351
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
501
352
  [
502
353
  // deprecated
@@ -505,6 +356,7 @@ declare module 'nitropack' {
505
356
  // new
506
357
  "index",
507
358
  "Cached",
359
+ "Component",
508
360
  "WithoutCache",
509
361
  "Screenshot"
510
362
  ].forEach((name) => {
@@ -515,9 +367,12 @@ declare module 'nitropack' {
515
367
  });
516
368
  nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
517
369
  });
518
- await addComponent({
519
- name: "OgImageTemplateFallback",
520
- filePath: resolve("./runtime/components/OgImageTemplate/Fallback.vue"),
370
+ await addComponentsDir({
371
+ path: resolve("./runtime/components/Templates/Community"),
372
+ island: true
373
+ });
374
+ await addComponentsDir({
375
+ path: resolve("./runtime/components/Templates/Official"),
521
376
  island: true
522
377
  });
523
378
  [
@@ -536,55 +391,99 @@ declare module 'nitropack' {
536
391
  filePath: resolve(`./runtime/components/OgImage/${name}`)
537
392
  });
538
393
  });
539
- const ogImageComponents = [];
394
+ addPlugin(resolve("./runtime/nuxt/plugins/route-rule-og-image.server"));
395
+ const ogImageComponentCtx = { components: [] };
540
396
  nuxt.hook("components:extend", (components) => {
397
+ ogImageComponentCtx.components = [];
541
398
  components.forEach((component) => {
542
399
  let valid = false;
543
400
  config.componentDirs.forEach((dir) => {
544
401
  if (component.pascalName.startsWith(dir) || component.kebabName.startsWith(dir) || component.shortPath.includes(`/${dir}/`))
545
402
  valid = true;
546
403
  });
547
- if (valid || component.pascalName === "OgImageTemplateFoo") {
404
+ if (component.filePath.includes(resolve("./runtime/components/Templates")))
405
+ valid = true;
406
+ if (valid && fs.existsSync(component.filePath)) {
548
407
  component.island = true;
549
408
  component.mode = "server";
550
- ogImageComponents.push({
409
+ let category = "app";
410
+ if (component.filePath.includes(resolve("./runtime/components/Templates/Community")))
411
+ category = "community";
412
+ else if (component.filePath.includes(resolve("./runtime/components/Templates/Official")))
413
+ category = "official";
414
+ const componentFile = fs.readFileSync(component.filePath, "utf-8");
415
+ const credits = componentFile.split("\n").find((line) => line.startsWith(" * @credits"))?.replace("* @credits", "").trim();
416
+ ogImageComponentCtx.components.push({
551
417
  // purge cache when component changes
552
- hash: hash(fs.readFileSync(component.filePath, "utf-8")),
418
+ hash: hash(componentFile),
553
419
  pascalName: component.pascalName,
554
- kebabName: component.kebabName
420
+ kebabName: component.kebabName,
421
+ path: nuxt.options.dev ? component.filePath : void 0,
422
+ category,
423
+ credits
555
424
  });
556
425
  }
557
426
  });
427
+ nuxt.hooks.hook("nuxt-og-image:components", ogImageComponentCtx);
558
428
  });
559
429
  addTemplate({
560
430
  filename: "og-image-component-names.mjs",
561
431
  getContents() {
562
- return `export const componentNames = ${JSON.stringify(ogImageComponents)}`;
432
+ return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
563
433
  },
564
434
  options: { mode: "server" }
565
435
  });
566
- const runtimeDir = resolve("./runtime");
567
- nuxt.options.build.transpile.push(runtimeDir);
568
- addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
569
- const customAssetDirs = [
570
- // allows us to show custom error images
571
- resolve("./runtime/public-assets")
572
- ];
573
- if (config.runtimeSatori) {
574
- if (config.fonts.includes("Inter:400"))
575
- customAssetDirs.push(resolve("./runtime/public-assets-optional/inter-font"));
576
- if (nitroCompatibility.png === "resvg-wasm" && nitroCompatibility.wasm === "fetch")
577
- customAssetDirs.push(resolve("./runtime/public-assets-optional/resvg"));
578
- else if (nitroCompatibility.png === "svg2png" && nitroCompatibility.wasm === "fetch")
579
- customAssetDirs.push(resolve("./runtime/public-assets-optional/svg2png"));
580
- if (nitroCompatibility.satori === "yoga-wasm")
581
- customAssetDirs.push(resolve("./runtime/public-assets-optional/yoga"));
582
- }
436
+ nuxt.options.nitro.virtual = nuxt.options.nitro.virtual || {};
437
+ nuxt.options.nitro.virtual["#nuxt-og-image/component-names.mjs"] = () => {
438
+ return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
439
+ };
440
+ extendTypes("nuxt-og-image", ({ typesPath }) => {
441
+ const componentImports = ogImageComponentCtx.components.map((component) => {
442
+ const relativeComponentPath = relative(resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"), component.path);
443
+ return ` '${component.pascalName}': typeof import('${relativeComponentPath}')['default']`;
444
+ }).join("\n");
445
+ return `
446
+ declare module 'nitropack' {
447
+ interface NitroRouteRules {
448
+ ogImage?: false | import('${typesPath}').OgImageOptions
449
+ }
450
+ interface NitroRouteConfig {
451
+ ogImage?: false | import('${typesPath}').OgImageOptions
452
+ }
453
+ }
454
+ declare module '#nuxt-og-image/components' {
455
+ export interface OgImageComponents {
456
+ ${componentImports}
457
+ }
458
+ }
459
+ `;
460
+ });
583
461
  nuxt.hooks.hook("modules:done", async () => {
584
462
  nuxt.hooks.callHook("og-image:config", config);
463
+ const normalisedFonts = config.fonts.map((f) => {
464
+ if (typeof f === "string") {
465
+ const [name, weight] = f.split(":");
466
+ return {
467
+ name,
468
+ weight,
469
+ path: void 0
470
+ };
471
+ }
472
+ return f;
473
+ });
474
+ if (!nuxt.options._generate && nuxt.options.build) {
475
+ nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
476
+ nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
477
+ normalisedFonts.filter((f) => !f.path).forEach(({ name, weight }) => {
478
+ nuxt.options.nitro.prerender.routes.push(`/__og-image__/font/${name}/${weight}.ttf`);
479
+ });
480
+ }
585
481
  nuxt.options.runtimeConfig["nuxt-og-image"] = {
586
482
  version,
587
- satoriOptions: config.satoriOptions,
483
+ // binding options
484
+ satoriOptions: config.satoriOptions || {},
485
+ resvgOptions: config.resvgOptions || {},
486
+ sharpOptions: config.sharpOptions || {},
588
487
  runtimeSatori: config.runtimeSatori,
589
488
  runtimeBrowser: config.runtimeBrowser,
590
489
  // @ts-expect-error runtime type
@@ -592,247 +491,22 @@ declare module 'nitropack' {
592
491
  // avoid adding credentials
593
492
  runtimeCacheStorage: typeof config.runtimeCacheStorage === "boolean" ? "default" : config.runtimeCacheStorage.driver,
594
493
  // convert the fonts to uniform type to fix ts issue
595
- fonts: config.fonts.map((f) => {
596
- if (typeof f === "string") {
597
- const [name, weight] = f.split(":");
598
- return {
599
- name,
600
- weight
601
- };
602
- }
603
- return f;
604
- }),
605
- assetDirs: [
606
- resolve(nuxt.options.srcDir, nuxt.options.dir.public),
607
- ...customAssetDirs,
608
- // always add runtime dirs for prerendering to work, these are just used as scan roots
609
- resolve("./runtime/public-assets-optional/inter-font"),
610
- resolve("./runtime/public-assets-optional/resvg"),
611
- resolve("./runtime/public-assets-optional/yoga"),
612
- resolve("./runtime/public-assets-optional/svg2png")
613
- ]
614
- };
615
- });
616
- nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
617
- nitroConfig.externals = defu$1(nitroConfig.externals || {}, {
618
- inline: [runtimeDir]
619
- });
620
- if (!nitroCompatibility.node) {
621
- nitroConfig.alias = nitroConfig.alias || {};
622
- if (config.runtimeBrowser) {
623
- nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
624
- nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
625
- nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
626
- }
627
- nitroConfig.alias.queue = "unenv/runtime/mock/proxy-cjs";
628
- }
629
- if (nitroCompatibility.png === "resvg-wasm")
630
- nitroConfig.alias["@resvg/resvg-js"] = "unenv/runtime/mock/proxy-cjs";
631
- nitroConfig.publicAssets = nitroConfig.publicAssets || [];
632
- customAssetDirs.forEach((dir) => {
633
- nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
634
- });
635
- const providerPath = `${runtimeDir}/nitro/providers`;
636
- nitroConfig.virtual["#nuxt-og-image/css-inline"] = `import cssInline from '${providerPath}/css-inline/${nitroCompatibility.cssInline ? "css-inline" : "mock"}'
637
- export default function() {
638
- return cssInline
639
- }
640
- `;
641
- if (config.runtimeBrowser) {
642
- nitroConfig.virtual["#nuxt-og-image/browser"] = `
643
- let browser
644
- export default async function() {
645
- browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
646
- return browser
647
- }
648
- `;
649
- }
650
- if (config.runtimeSatori) {
651
- nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
652
- export default function() {
653
- return satori
654
- }`;
655
- nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
656
- export default function() {
657
- return png
658
- }
659
- `;
660
- }
661
- const rendererPath = `${runtimeDir}/nitro/renderers`;
662
- nitroConfig.virtual["#nuxt-og-image/provider"] = `
663
- ${config.runtimeSatori ? `import satori from '${rendererPath}/satori'` : ""}
664
- ${config.runtimeBrowser ? `import browser from '${rendererPath}/browser'` : ""}
665
-
666
- export async function useProvider(provider) {
667
- if (provider === 'satori')
668
- return ${config.runtimeSatori ? "satori" : "null"}
669
- if (provider === 'browser')
670
- return ${config.runtimeBrowser ? "browser" : "null"}
671
- return null
672
- }
673
- `;
674
- });
675
- nuxt.hooks.hook("nitro:init", async (nitro) => {
676
- let screenshotQueue = [];
677
- nitro.hooks.hook("compiled", async (_nitro) => {
678
- if (!config.runtimeSatori || nuxt.options.dev)
679
- return;
680
- if (config.fonts.includes("Inter:400"))
681
- 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"));
682
- if (config.fonts.includes("Inter:700"))
683
- 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"));
684
- const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
685
- const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
686
- const paths = [wasmProviderPath];
687
- const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
688
- paths.push(...chunks);
689
- for (const path of paths) {
690
- if (!await pathExists(path))
691
- continue;
692
- let contents = await readFile(path, "utf-8");
693
- let updated = false;
694
- for (const wasm of Wasms) {
695
- if (contents.includes(wasm.placeholder)) {
696
- if (nitroCompatibility.wasm === "import") {
697
- contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
698
- await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
699
- } else if (nitroCompatibility.wasm === "inline") {
700
- const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`));
701
- contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
702
- }
703
- updated = true;
704
- }
705
- }
706
- if (updated)
707
- await writeFile(path, contents, { encoding: "utf-8" });
708
- }
709
- });
710
- const _routeRulesMatcher = toRouteMatcher(
711
- createRouter({ routes: nitro.options.routeRules })
712
- );
713
- nitro.hooks.hook("prerender:generate", async (ctx) => {
714
- if (ctx.route.includes("."))
715
- return;
716
- const html = ctx.contents;
717
- if (!html)
718
- return;
719
- const routeRules = defu$1({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
720
- const extractedOptions = extractAndNormaliseOgImageOptions(ctx.route, html, routeRules.ogImage || {}, config.defaults);
721
- if (!extractedOptions || routeRules.ogImage === false)
722
- return;
723
- const isPageScreenshot = extractedOptions.component === "PageScreenshot";
724
- const entry = {
725
- route: parsePath(ctx.route).pathname,
726
- // drop hash and query
727
- path: !isPageScreenshot ? `/api/og-image-html?path=${ctx.route}` : ctx.route,
728
- ...extractedOptions
729
- };
730
- if (screenshotQueue.some((r) => r.route === entry.route))
731
- return;
732
- if ((nuxt.options._generate || entry.cache) && entry.provider === "browser")
733
- screenshotQueue.push(entry);
734
- });
735
- if (nuxt.options.dev)
736
- return;
737
- const captureScreenshots = async () => {
738
- await nuxt.callHook("og-image:prerenderScreenshots", screenshotQueue);
739
- if (screenshotQueue.length === 0)
740
- return;
741
- nitro.logger.info("Ensuring chromium install for og:image generation...");
742
- const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
743
- stdio: "inherit"
744
- });
745
- installChromeProcess.stderr?.pipe(process.stderr);
746
- await new Promise((resolve2) => {
747
- installChromeProcess.on("exit", (e) => {
748
- if (e !== 0)
749
- nitro.logger.error("Failed to install Playwright dependency for og:image generation. Trying anyway...");
750
- resolve2(true);
751
- });
752
- });
753
- installChromeProcess.pid && terminate(installChromeProcess.pid);
754
- const browser = await createBrowser();
755
- if (!browser) {
756
- nitro.logger.log(chalk.red("Failed to create a browser to create og:images."));
757
- return;
758
- }
759
- nitro.logger.info("Creating server for og:image generation...");
760
- const previewProcess = execa("npx", ["serve", nitro.options.output.publicDir]);
761
- try {
762
- const host = (await new Promise((resolve2) => {
763
- previewProcess.stdout?.on("data", (data) => {
764
- if (data.includes("Accepting connections at")) {
765
- resolve2(data.toString().split("Accepting connections at ")[1]);
766
- }
767
- });
768
- })).trim();
769
- previewProcess.removeAllListeners("data");
770
- nitro.logger.info(`Prerendering ${screenshotQueue.length} og:image screenshots...`);
771
- for (const k in screenshotQueue) {
772
- let entry = screenshotQueue[k];
773
- if (entry.route && Object.keys(entry).length === 1) {
774
- const html = await $fetch(entry.route, { baseURL: withBase(nuxt.options.app.baseURL, host) });
775
- const routeRules = defu$1({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
776
- const extractedOptions = extractAndNormaliseOgImageOptions(entry.route, html, routeRules.ogImage || {}, {
777
- ...config.defaults,
778
- component: "PageScreenshot"
779
- });
780
- if (!extractedOptions || routeRules.ogImage === false) {
781
- entry.skip = true;
782
- continue;
783
- }
784
- screenshotQueue[k] = entry = defu$1(
785
- { path: extractedOptions.component !== "PageScreenshot" ? `/api/og-image-html?path=${entry.route}` : entry.route },
786
- entry,
787
- extractedOptions
788
- );
789
- }
790
- if (!entry.skip && entry.component !== "PageScreenshot")
791
- entry.html = await globalThis.$fetch(entry.path);
792
- }
793
- for (const k in screenshotQueue) {
794
- const entry = screenshotQueue[k];
795
- if (entry.skip)
796
- continue;
797
- const start = Date.now();
798
- let hasError = false;
799
- const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
800
- const filename = joinURL(dirname2, "/og.png");
801
- try {
802
- const imgBuffer = await screenshot(browser, {
803
- ...config.defaults || {},
804
- ...entry || {},
805
- host
806
- });
807
- try {
808
- await mkdirp(dirname2);
809
- } catch (e) {
810
- }
811
- await writeFile(filename, imgBuffer);
812
- } catch (e) {
813
- hasError = true;
814
- console.error(e);
815
- }
816
- const generateTimeMS = Date.now() - start;
817
- nitro.logger.log(chalk[hasError ? "red" : "gray"](
818
- ` ${Number(k) === screenshotQueue.length - 1 ? "\u2514\u2500" : "\u251C\u2500"} /${relative(nitro.options.output.publicDir, filename)} (${generateTimeMS}ms) ${Math.round((Number(k) + 1) / screenshotQueue.length * 100)}%`
819
- ));
820
- }
821
- } catch (e) {
822
- console.error(e);
823
- } finally {
824
- await browser?.close();
825
- previewProcess.pid && terminate(previewProcess.pid);
826
- }
827
- screenshotQueue = [];
494
+ fonts: normalisedFonts,
495
+ hasNuxtIcon: hasNuxtModule("nuxt-icon")
828
496
  };
829
- nitro.hooks.hook("rollup:before", async () => {
830
- await captureScreenshots();
831
- });
832
- nitro.hooks.hook("close", async () => {
833
- await captureScreenshots();
834
- });
835
497
  });
498
+ nuxt.options.nitro.experimental = nuxt.options.nitro.experimental || {};
499
+ nuxt.options.nitro.experimental.wasm = true;
500
+ if (nuxt.options.dev) {
501
+ setupDevHandler(config, resolve);
502
+ setupDevToolsUI(config, resolve);
503
+ } else if (nuxt.options._generate) {
504
+ setupGenerateHandler(config, resolve);
505
+ } else if (nuxt.options.build) {
506
+ await setupBuildHandler(config, resolve);
507
+ }
508
+ if (nuxt.options.nitro.prerender?.routes || nuxt.options._generate)
509
+ setupPrerenderHandler(config, resolve);
836
510
  }
837
511
  });
838
512