nuxt-og-image 5.1.12 → 6.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 (243) hide show
  1. package/README.md +2 -2
  2. package/bin/cli.mjs +2 -0
  3. package/dist/chunks/tw4-classes.cjs +116 -0
  4. package/dist/chunks/tw4-classes.mjs +113 -0
  5. package/dist/chunks/tw4-generator.cjs +118 -0
  6. package/dist/chunks/tw4-generator.mjs +110 -0
  7. package/dist/cli.cjs +333 -0
  8. package/dist/cli.d.cts +1 -0
  9. package/dist/cli.d.mts +1 -0
  10. package/dist/cli.d.ts +1 -0
  11. package/dist/cli.mjs +330 -0
  12. package/dist/client/200.html +1 -1
  13. package/dist/client/404.html +1 -1
  14. package/dist/client/_fonts/0xp3SbCWC1OhX7q1-uF6kilMZFm-alJNkUtkLTPCy_A-tN9KwPUWhhXvtqh74sU9FIkI4W6hsbm85r0X24hjOfM.woff2 +0 -0
  15. package/dist/client/_fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff +0 -0
  16. package/dist/client/_fonts/4HA9tc4y8BVQeLXvLn3JgQqilAj1xrAnUSprQGHIPSw-ZPswEL_UDOYaxTLQDUySPjoOHDxhD83pD19HMfKfK9s.woff2 +0 -0
  17. package/dist/client/_fonts/Bmul3LaKlc7BUKqJHE_UmEoF40Sg_2ga52yJjwyDcKs-TnYmYl1DNYkiWMu0Vx49DakCPBuiCCj9zoLIuQjUdKY.woff2 +0 -0
  18. package/dist/client/_fonts/DfgmjWGpWte3Q3a54Nevr_BYmMM5YEJXRI1CdI2VwO0-ox5RadQfCyVTmKl_hubTaIJjtRw9oaQz2GDBeZR6l1M.woff2 +0 -0
  19. package/dist/client/_fonts/Lc_5lWuBuZcZ166p1-s-mnGkMJwIYJE_QDCkws8iCkI-r45Qbm2hCykrfOZ0kowz__uTTTUOPDN9hz34QcRNTY4.woff2 +0 -0
  20. package/dist/client/_fonts/iEvApgDRmzKzNqOYocBTrmcHZmuGAJloawKDP1S0nyE-T3oc_9We24QGwfw5naik4cM0g7VxylWVaQwKm4dy3cw.woff2 +0 -0
  21. package/dist/client/_nuxt/0kYTU2a7.js +2 -0
  22. package/dist/client/_nuxt/B0QDx5EE.js +2 -0
  23. package/dist/client/_nuxt/BKqzYw6x.js +3 -0
  24. package/dist/client/_nuxt/C-Ivr2Rl.js +1 -0
  25. package/dist/client/_nuxt/CLgn8DCr.js +1 -0
  26. package/dist/client/_nuxt/CwWm-XE3.js +1 -0
  27. package/dist/client/_nuxt/D9eL2h5z.js +1 -0
  28. package/dist/client/_nuxt/DJXHIJSq.js +1 -0
  29. package/dist/client/_nuxt/DVnX3Z-O.js +1 -0
  30. package/dist/client/_nuxt/DXObZt09.js +184 -0
  31. package/dist/client/_nuxt/Dxi6QG7I.js +1 -0
  32. package/dist/client/_nuxt/E8AZ6HoH.js +1 -0
  33. package/dist/client/_nuxt/IFrameLoader.CGrV1TpP.css +1 -0
  34. package/dist/client/_nuxt/JAMwWy1K.js +3864 -0
  35. package/dist/client/_nuxt/OSectionBlock.BVHnMsIr.css +1 -0
  36. package/dist/client/_nuxt/PONEy9N-.js +1 -0
  37. package/dist/client/_nuxt/UdkqSAsD.js +1 -0
  38. package/dist/client/_nuxt/builds/latest.json +1 -1
  39. package/dist/client/_nuxt/builds/meta/5205806b-8e5a-41ea-90c0-9cd83a75fc0a.json +1 -0
  40. package/dist/client/_nuxt/entry.BEExJd9R.css +2 -0
  41. package/dist/client/_nuxt/error-404.B79WD2X-.css +1 -0
  42. package/dist/client/_nuxt/error-500.DT3Sd0Wu.css +1 -0
  43. package/dist/client/_nuxt/pages.eW3hi7XF.css +1 -0
  44. package/dist/client/_nuxt/templates.dUiUBaip.css +1 -0
  45. package/dist/client/_payload.json +1 -0
  46. package/dist/client/debug/_payload.json +1 -0
  47. package/dist/client/debug/index.html +1 -0
  48. package/dist/client/docs/_payload.json +1 -0
  49. package/dist/client/docs/index.html +1 -0
  50. package/dist/client/fonts/HubotSans-Regular.woff2 +0 -0
  51. package/dist/client/index.html +1 -1
  52. package/dist/client/templates/_payload.json +1 -0
  53. package/dist/client/templates/index.html +1 -0
  54. package/dist/content.cjs +6 -6
  55. package/dist/content.d.cts +13 -13
  56. package/dist/content.d.mts +13 -13
  57. package/dist/content.d.ts +13 -13
  58. package/dist/content.mjs +1 -1
  59. package/dist/module.cjs +36 -1025
  60. package/dist/module.d.cts +63 -23
  61. package/dist/module.d.mts +63 -23
  62. package/dist/module.d.ts +63 -23
  63. package/dist/module.json +1 -1
  64. package/dist/module.mjs +32 -1007
  65. package/dist/runtime/app/components/Templates/Community/Brutalist.satori.d.vue.ts +14 -0
  66. package/dist/runtime/app/components/Templates/Community/Brutalist.satori.vue +51 -0
  67. package/dist/runtime/app/components/Templates/Community/Brutalist.satori.vue.d.ts +14 -0
  68. package/dist/runtime/app/components/Templates/Community/{Frame.vue.d.ts → Frame.satori.d.vue.ts} +2 -2
  69. package/dist/runtime/app/components/Templates/Community/Frame.satori.vue +71 -0
  70. package/dist/runtime/app/components/Templates/Community/{Frame.d.vue.ts → Frame.satori.vue.d.ts} +2 -2
  71. package/dist/runtime/app/components/Templates/Community/Newspaper.satori.d.vue.ts +12 -0
  72. package/dist/runtime/app/components/Templates/Community/Newspaper.satori.vue +70 -0
  73. package/dist/runtime/app/components/Templates/Community/Newspaper.satori.vue.d.ts +12 -0
  74. package/dist/runtime/app/components/Templates/Community/Nuxt.satori.d.vue.ts +12 -0
  75. package/dist/runtime/app/components/Templates/Community/{Nuxt.vue → Nuxt.satori.vue} +3 -3
  76. package/dist/runtime/app/components/Templates/Community/Nuxt.satori.vue.d.ts +12 -0
  77. package/dist/runtime/app/components/Templates/Community/NuxtSeo.satori.d.vue.ts +12 -0
  78. package/dist/runtime/app/components/Templates/Community/NuxtSeo.satori.vue +69 -0
  79. package/dist/runtime/app/components/Templates/Community/NuxtSeo.satori.vue.d.ts +12 -0
  80. package/dist/runtime/app/components/Templates/Community/Pergel.satori.d.vue.ts +12 -0
  81. package/dist/runtime/app/components/Templates/Community/{Pergel.vue → Pergel.satori.vue} +14 -11
  82. package/dist/runtime/app/components/Templates/Community/Pergel.satori.vue.d.ts +12 -0
  83. package/dist/runtime/app/components/Templates/Community/Retro.satori.d.vue.ts +12 -0
  84. package/dist/runtime/app/components/Templates/Community/Retro.satori.vue +64 -0
  85. package/dist/runtime/app/components/Templates/Community/Retro.satori.vue.d.ts +12 -0
  86. package/dist/runtime/app/components/Templates/Community/SaaS.satori.d.vue.ts +12 -0
  87. package/dist/runtime/app/components/Templates/Community/SaaS.satori.vue +60 -0
  88. package/dist/runtime/app/components/Templates/Community/SaaS.satori.vue.d.ts +12 -0
  89. package/dist/runtime/app/components/Templates/Community/SimpleBlog.satori.d.vue.ts +9 -0
  90. package/dist/runtime/app/components/Templates/Community/SimpleBlog.satori.vue +38 -0
  91. package/dist/runtime/app/components/Templates/Community/SimpleBlog.satori.vue.d.ts +9 -0
  92. package/dist/runtime/app/components/Templates/Community/{UnJs.d.vue.ts → UnJs.satori.d.vue.ts} +2 -2
  93. package/dist/runtime/app/components/Templates/Community/{UnJs.vue → UnJs.satori.vue} +41 -33
  94. package/dist/runtime/app/components/Templates/Community/{UnJs.vue.d.ts → UnJs.satori.vue.d.ts} +2 -2
  95. package/dist/runtime/app/components/Templates/Community/WithEmoji.satori.d.vue.ts +13 -0
  96. package/dist/runtime/app/components/Templates/Community/WithEmoji.satori.vue +27 -0
  97. package/dist/runtime/app/components/Templates/Community/WithEmoji.satori.vue.d.ts +13 -0
  98. package/dist/runtime/app/composables/_defineOgImageRaw.d.ts +6 -0
  99. package/dist/runtime/app/composables/_defineOgImageRaw.js +70 -0
  100. package/dist/runtime/app/composables/defineOgImage.d.ts +14 -2
  101. package/dist/runtime/app/composables/defineOgImage.js +13 -42
  102. package/dist/runtime/app/composables/defineOgImageComponent.d.ts +5 -1
  103. package/dist/runtime/app/composables/defineOgImageComponent.js +4 -5
  104. package/dist/runtime/app/composables/defineOgImageScreenshot.d.ts +1 -1
  105. package/dist/runtime/app/composables/defineOgImageScreenshot.js +2 -2
  106. package/dist/runtime/app/composables/mock.d.ts +7 -4
  107. package/dist/runtime/app/composables/mock.js +4 -4
  108. package/dist/runtime/app/utils/plugins.js +22 -28
  109. package/dist/runtime/app/utils.d.ts +15 -1
  110. package/dist/runtime/app/utils.js +74 -46
  111. package/dist/runtime/pure.d.ts +7 -0
  112. package/dist/runtime/pure.js +105 -0
  113. package/dist/runtime/server/og-image/bindings/css-inline/wasm-fs.d.ts +3 -2
  114. package/dist/runtime/server/og-image/bindings/css-inline/wasm.d.ts +3 -2
  115. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.d.ts +3 -0
  116. package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +22 -0
  117. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.d.ts +3 -0
  118. package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +49 -0
  119. package/dist/runtime/server/og-image/bindings/font-assets/node.d.ts +3 -0
  120. package/dist/runtime/server/og-image/bindings/font-assets/node.js +14 -0
  121. package/dist/runtime/server/og-image/bindings/resvg/node-dev.d.ts +5 -0
  122. package/dist/runtime/server/og-image/bindings/resvg/node-dev.js +70 -0
  123. package/dist/runtime/server/og-image/bindings/satori/wasm-fs.d.ts +1 -1
  124. package/dist/runtime/server/og-image/bindings/satori/wasm-fs.js +4 -8
  125. package/dist/runtime/server/og-image/bindings/satori/wasm.d.ts +1 -1
  126. package/dist/runtime/server/og-image/bindings/satori/wasm.js +4 -9
  127. package/dist/runtime/server/og-image/bindings/takumi/node.d.ts +6 -0
  128. package/dist/runtime/server/og-image/bindings/takumi/node.js +5 -0
  129. package/dist/runtime/server/og-image/bindings/takumi/wasm.d.ts +6 -0
  130. package/dist/runtime/server/og-image/bindings/takumi/wasm.js +6 -0
  131. package/dist/runtime/server/og-image/cache/buildCache.d.ts +16 -0
  132. package/dist/runtime/server/og-image/cache/buildCache.js +48 -0
  133. package/dist/runtime/server/og-image/cache/lru.d.ts +2 -2
  134. package/dist/runtime/server/og-image/cache/lru.js +3 -3
  135. package/dist/runtime/server/og-image/cache/mock.d.ts +1 -2
  136. package/dist/runtime/server/og-image/cache/mock.js +0 -1
  137. package/dist/runtime/server/og-image/chromium/screenshot.js +4 -3
  138. package/dist/runtime/server/og-image/context.d.ts +2 -3
  139. package/dist/runtime/server/og-image/context.js +56 -195
  140. package/dist/runtime/server/og-image/devtools.d.ts +10 -0
  141. package/dist/runtime/server/og-image/devtools.js +74 -0
  142. package/dist/runtime/server/og-image/fonts.d.ts +6 -0
  143. package/dist/runtime/server/og-image/fonts.js +41 -0
  144. package/dist/runtime/server/og-image/instances.d.ts +1 -0
  145. package/dist/runtime/server/og-image/instances.js +5 -0
  146. package/dist/runtime/server/og-image/satori/instances.d.ts +1 -36
  147. package/dist/runtime/server/og-image/satori/plugins/emojis.js +83 -4
  148. package/dist/runtime/server/og-image/satori/plugins/encoding.js +11 -1
  149. package/dist/runtime/server/og-image/satori/plugins/imageSrc.js +5 -1
  150. package/dist/runtime/server/og-image/satori/plugins/twClasses.js +35 -0
  151. package/dist/runtime/server/og-image/satori/renderer.js +16 -53
  152. package/dist/runtime/server/og-image/satori/transforms/emojis/emoji-names-minimal.d.ts +1 -0
  153. package/dist/runtime/server/og-image/satori/transforms/emojis/emoji-names-minimal.js +223 -0
  154. package/dist/runtime/server/og-image/satori/transforms/emojis/emoji-utils.d.ts +45 -0
  155. package/dist/runtime/server/og-image/satori/transforms/emojis/emoji-utils.js +13 -0
  156. package/dist/runtime/server/og-image/satori/transforms/emojis/fetch.d.ts +6 -0
  157. package/dist/runtime/server/og-image/satori/transforms/emojis/fetch.js +38 -0
  158. package/dist/runtime/server/og-image/satori/transforms/emojis/index.d.ts +7 -0
  159. package/dist/runtime/server/og-image/satori/transforms/emojis/index.js +64 -0
  160. package/dist/runtime/server/og-image/satori/transforms/emojis/local.d.ts +7 -0
  161. package/dist/runtime/server/og-image/satori/transforms/emojis/local.js +32 -0
  162. package/dist/runtime/server/og-image/satori/utils.js +5 -4
  163. package/dist/runtime/server/og-image/satori/vnodes.js +7 -6
  164. package/dist/runtime/server/og-image/takumi/instances.d.ts +1 -0
  165. package/dist/runtime/server/og-image/takumi/instances.js +6 -0
  166. package/dist/runtime/server/og-image/takumi/nodes.d.ts +12 -0
  167. package/dist/runtime/server/og-image/takumi/nodes.js +86 -0
  168. package/dist/runtime/server/og-image/takumi/renderer.d.ts +3 -0
  169. package/dist/runtime/server/og-image/takumi/renderer.js +45 -0
  170. package/dist/runtime/server/og-image/templates/html.js +32 -23
  171. package/dist/runtime/server/plugins/prerender.d.ts +1 -1
  172. package/dist/runtime/server/plugins/prerender.js +17 -7
  173. package/dist/runtime/server/util/auto-eject.d.ts +2 -0
  174. package/dist/runtime/server/util/auto-eject.js +30 -0
  175. package/dist/runtime/server/util/eventHandlers.d.ts +0 -1
  176. package/dist/runtime/server/util/eventHandlers.js +15 -85
  177. package/dist/runtime/server/util/options.d.ts +7 -2
  178. package/dist/runtime/server/util/options.js +40 -6
  179. package/dist/runtime/server/util/wasm.d.ts +1 -1
  180. package/dist/runtime/server/utils.d.ts +6 -2
  181. package/dist/runtime/server/utils.js +12 -8
  182. package/dist/runtime/shared/urlEncoding.d.ts +64 -0
  183. package/dist/runtime/shared/urlEncoding.js +194 -0
  184. package/dist/runtime/shared.d.ts +4 -9
  185. package/dist/runtime/shared.js +31 -50
  186. package/dist/runtime/types.d.ts +71 -25
  187. package/dist/shared/nuxt-og-image.DroaQ3v-.cjs +2852 -0
  188. package/dist/shared/nuxt-og-image.HMyihp-D.mjs +2825 -0
  189. package/dist/types.d.mts +2 -0
  190. package/package.json +119 -56
  191. package/types/virtual.d.ts +7 -1
  192. package/dist/client/_nuxt/B8PEiB0p.js +0 -1
  193. package/dist/client/_nuxt/CD9VIl4i.js +0 -4062
  194. package/dist/client/_nuxt/CI_lqgv7.js +0 -1
  195. package/dist/client/_nuxt/CVO1_9PV.js +0 -1
  196. package/dist/client/_nuxt/Cp-IABpG.js +0 -1
  197. package/dist/client/_nuxt/D0r3Knsf.js +0 -1
  198. package/dist/client/_nuxt/Dz8kdfgF.js +0 -1
  199. package/dist/client/_nuxt/builds/meta/71b0f7ea-9aae-4c28-bbd9-8eea71b82dfb.json +0 -1
  200. package/dist/client/_nuxt/entry.DNM8P-MU.css +0 -1
  201. package/dist/client/_nuxt/error-404.C2mGI6Vt.css +0 -1
  202. package/dist/client/_nuxt/error-500.ClHbVBiL.css +0 -1
  203. package/dist/client/_nuxt/l5rcX3cq.js +0 -8
  204. package/dist/runtime/app/components/OgImage/OgImage.d.ts +0 -3
  205. package/dist/runtime/app/components/OgImage/OgImage.js +0 -10
  206. package/dist/runtime/app/components/Templates/Community/BrandedLogo.d.vue.ts +0 -13
  207. package/dist/runtime/app/components/Templates/Community/BrandedLogo.vue +0 -22
  208. package/dist/runtime/app/components/Templates/Community/BrandedLogo.vue.d.ts +0 -13
  209. package/dist/runtime/app/components/Templates/Community/Frame.vue +0 -58
  210. package/dist/runtime/app/components/Templates/Community/Nuxt.d.vue.ts +0 -12
  211. package/dist/runtime/app/components/Templates/Community/Nuxt.vue.d.ts +0 -12
  212. package/dist/runtime/app/components/Templates/Community/NuxtSeo.d.vue.ts +0 -15
  213. package/dist/runtime/app/components/Templates/Community/NuxtSeo.vue +0 -103
  214. package/dist/runtime/app/components/Templates/Community/NuxtSeo.vue.d.ts +0 -15
  215. package/dist/runtime/app/components/Templates/Community/Pergel.d.vue.ts +0 -12
  216. package/dist/runtime/app/components/Templates/Community/Pergel.vue.d.ts +0 -12
  217. package/dist/runtime/app/components/Templates/Community/SimpleBlog.d.vue.ts +0 -9
  218. package/dist/runtime/app/components/Templates/Community/SimpleBlog.vue +0 -27
  219. package/dist/runtime/app/components/Templates/Community/SimpleBlog.vue.d.ts +0 -9
  220. package/dist/runtime/app/components/Templates/Community/Wave.d.vue.ts +0 -11
  221. package/dist/runtime/app/components/Templates/Community/Wave.vue +0 -28
  222. package/dist/runtime/app/components/Templates/Community/Wave.vue.d.ts +0 -11
  223. package/dist/runtime/app/components/Templates/Community/WithEmoji.d.vue.ts +0 -13
  224. package/dist/runtime/app/components/Templates/Community/WithEmoji.vue +0 -21
  225. package/dist/runtime/app/components/Templates/Community/WithEmoji.vue.d.ts +0 -13
  226. package/dist/runtime/assets/Inter-normal-400.ttf.base64 +0 -1
  227. package/dist/runtime/assets/Inter-normal-700.ttf.base64 +0 -1
  228. package/dist/runtime/server/og-image/satori/font.d.ts +0 -3
  229. package/dist/runtime/server/og-image/satori/font.js +0 -40
  230. package/dist/runtime/server/og-image/satori/plugins/unocss.js +0 -55
  231. package/dist/runtime/server/og-image/satori/transforms/emojis.d.ts +0 -3
  232. package/dist/runtime/server/og-image/satori/transforms/emojis.js +0 -3595
  233. package/dist/runtime/server/plugins/__zero-runtime/nuxt-content-v2.d.ts +0 -2
  234. package/dist/runtime/server/plugins/__zero-runtime/nuxt-content-v2.js +0 -9
  235. package/dist/runtime/server/plugins/nuxt-content-v2.d.ts +0 -2
  236. package/dist/runtime/server/plugins/nuxt-content-v2.js +0 -5
  237. package/dist/runtime/server/routes/__zero-runtime/font.d.ts +0 -2
  238. package/dist/runtime/server/routes/__zero-runtime/font.js +0 -8
  239. package/dist/runtime/server/routes/font.d.ts +0 -2
  240. package/dist/runtime/server/routes/font.js +0 -3
  241. package/dist/runtime/server/util/plugins.d.ts +0 -2
  242. package/dist/runtime/server/util/plugins.js +0 -56
  243. /package/dist/runtime/server/og-image/satori/plugins/{unocss.d.ts → twClasses.d.ts} +0 -0
@@ -0,0 +1,2852 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const promises = require('node:fs/promises');
5
+ const kit = require('@nuxt/kit');
6
+ const defu = require('defu');
7
+ const jiti = require('jiti');
8
+ const kit$1 = require('nuxt-site-config/kit');
9
+ const ohash = require('ohash');
10
+ const pathe = require('pathe');
11
+ const pkgTypes = require('pkg-types');
12
+ const stdEnv = require('std-env');
13
+ const node_crypto = require('node:crypto');
14
+ const nypm = require('nypm');
15
+ const logger_js = require('../../dist/runtime/logger.js');
16
+ const chromeLauncher = require('chrome-launcher');
17
+ const node_path = require('node:path');
18
+ const devtoolsKit = require('@nuxt/devtools-kit');
19
+ const node_url = require('node:url');
20
+ const MagicString = require('magic-string');
21
+ const stripLiteral = require('strip-literal');
22
+ const ufo = require('ufo');
23
+ const unplugin = require('unplugin');
24
+ const postcss = require('postcss');
25
+ const postcssCalc = require('postcss-calc');
26
+ const tailwindcss = require('tailwindcss');
27
+ const culori = require('culori');
28
+ const exsolve = require('exsolve');
29
+ const twColors = require('tailwindcss/colors');
30
+ const emojiUtils_js = require('../../dist/runtime/server/og-image/satori/transforms/emojis/emoji-utils.js');
31
+ const compilerSfc = require('@vue/compiler-sfc');
32
+ const magicast = require('magicast');
33
+ const helpers = require('magicast/helpers');
34
+
35
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
36
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
37
+
38
+ function _interopNamespaceCompat(e) {
39
+ if (e && typeof e === 'object' && 'default' in e) return e;
40
+ const n = Object.create(null);
41
+ if (e) {
42
+ for (const k in e) {
43
+ n[k] = e[k];
44
+ }
45
+ }
46
+ n.default = e;
47
+ return n;
48
+ }
49
+
50
+ const fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
51
+ const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
52
+ const postcss__default = /*#__PURE__*/_interopDefaultCompat(postcss);
53
+ const postcssCalc__default = /*#__PURE__*/_interopDefaultCompat(postcssCalc);
54
+ const twColors__default = /*#__PURE__*/_interopDefaultCompat(twColors);
55
+
56
+ const isUndefinedOrTruthy = (v) => typeof v === "undefined" || v !== false;
57
+ function checkLocalChrome() {
58
+ if (stdEnv.isCI)
59
+ return false;
60
+ let hasChromeLocally = false;
61
+ try {
62
+ hasChromeLocally = !!chromeLauncher.Launcher.getFirstInstallation();
63
+ } catch {
64
+ }
65
+ return hasChromeLocally;
66
+ }
67
+ async function hasResolvableDependency(dep) {
68
+ return await kit.resolvePath(dep, { fallbackToOriginal: true }).catch(() => null).then((r) => r && r !== dep);
69
+ }
70
+ const VALID_RENDERER_SUFFIXES = ["satori", "chromium", "takumi"];
71
+ function getRendererFromFilename(filepath) {
72
+ const filename = filepath.split("/").pop()?.replace(".vue", "") || "";
73
+ for (const suffix of VALID_RENDERER_SUFFIXES) {
74
+ if (filename.endsWith(`.${suffix}`))
75
+ return suffix;
76
+ }
77
+ return null;
78
+ }
79
+ function stripRendererSuffix(name) {
80
+ for (const suffix of VALID_RENDERER_SUFFIXES) {
81
+ if (name.endsWith(`.${suffix}`) || name.endsWith(suffix.charAt(0).toUpperCase() + suffix.slice(1)))
82
+ return name.replace(new RegExp(`[.]?${suffix}$`, "i"), "");
83
+ }
84
+ return name;
85
+ }
86
+
87
+ const autodetectableProviders = {
88
+ azure_static: "azure",
89
+ cloudflare_pages: "cloudflare-pages",
90
+ netlify: "netlify",
91
+ stormkit: "stormkit",
92
+ vercel: "vercel",
93
+ cleavr: "cleavr",
94
+ stackblitz: "stackblitz"
95
+ };
96
+ const autodetectableStaticProviders = {
97
+ netlify: "netlify-static",
98
+ vercel: "vercel-static"
99
+ };
100
+ const NodeRuntime = {
101
+ // node-server runtime
102
+ "chromium": "on-demand",
103
+ // this gets changed build start
104
+ "css-inline": "node",
105
+ "resvg": "node",
106
+ "satori": "node",
107
+ "takumi": "node",
108
+ "sharp": "node",
109
+ // will be disabled if they're missing the dependency
110
+ "emoji": "local"
111
+ // can bundle icons, no size constraints
112
+ };
113
+ const NodeDevRuntime = {
114
+ ...NodeRuntime,
115
+ resvg: "node-dev"
116
+ // use worker to prevent crashes from killing process
117
+ };
118
+ const cloudflare = {
119
+ "chromium": false,
120
+ "css-inline": false,
121
+ "resvg": "wasm",
122
+ "satori": "node",
123
+ "takumi": "wasm",
124
+ "sharp": false,
125
+ "emoji": "fetch",
126
+ // edge size limits - use API instead of bundling 24MB icons
127
+ "wasm": {
128
+ esmImport: true,
129
+ lazy: true
130
+ }
131
+ };
132
+ const awsLambda = {
133
+ "chromium": false,
134
+ "css-inline": "wasm",
135
+ "resvg": "node",
136
+ "satori": "node",
137
+ "takumi": "node",
138
+ "sharp": false,
139
+ // 0.33.x has issues
140
+ "emoji": "local"
141
+ // serverless has larger size limits
142
+ };
143
+ const WebContainer = {
144
+ "chromium": false,
145
+ "css-inline": "wasm-fs",
146
+ "resvg": "wasm-fs",
147
+ "satori": "wasm-fs",
148
+ "takumi": "wasm",
149
+ "sharp": false,
150
+ "emoji": "fetch"
151
+ // webcontainer has size constraints
152
+ };
153
+ const RuntimeCompatibility = {
154
+ "nitro-dev": NodeDevRuntime,
155
+ "nitro-prerender": NodeRuntime,
156
+ "node-server": NodeRuntime,
157
+ "stackblitz": WebContainer,
158
+ "codesandbox": WebContainer,
159
+ "aws-lambda": awsLambda,
160
+ "netlify": awsLambda,
161
+ "netlify-edge": {
162
+ "chromium": false,
163
+ "css-inline": "wasm",
164
+ "resvg": "wasm",
165
+ "satori": "node",
166
+ "takumi": "wasm",
167
+ "sharp": false,
168
+ "emoji": "fetch",
169
+ // edge size limits
170
+ "wasm": {
171
+ rollup: {
172
+ targetEnv: "auto-inline",
173
+ sync: ["@resvg/resvg-wasm/index_bg.wasm"]
174
+ }
175
+ }
176
+ },
177
+ "firebase": awsLambda,
178
+ "vercel": awsLambda,
179
+ "vercel-edge": {
180
+ "chromium": false,
181
+ "css-inline": false,
182
+ // size constraint (2mb is max)
183
+ "resvg": "wasm",
184
+ "satori": "node",
185
+ "takumi": "wasm",
186
+ "sharp": false,
187
+ "emoji": "fetch",
188
+ // edge size limits - bundling 24MB icons not viable
189
+ "wasm": {
190
+ // lowers workers kb size
191
+ esmImport: true,
192
+ lazy: true
193
+ }
194
+ },
195
+ "cloudflare-pages": cloudflare,
196
+ "cloudflare": cloudflare,
197
+ "cloudflare-module": cloudflare
198
+ };
199
+ function detectTarget(options = {}) {
200
+ return options?.static ? autodetectableStaticProviders[stdEnv.provider] : autodetectableProviders[stdEnv.provider];
201
+ }
202
+ function resolveNitroPreset(nitroConfig) {
203
+ if (stdEnv.provider === "stackblitz" || stdEnv.provider === "codesandbox")
204
+ return stdEnv.provider;
205
+ const nuxt = kit.useNuxt();
206
+ if (nuxt.options.dev)
207
+ return "nitro-dev";
208
+ if (nuxt.options.nitro.static)
209
+ return "nitro-prerender";
210
+ let preset;
211
+ if (nitroConfig && nitroConfig?.preset)
212
+ preset = nitroConfig.preset;
213
+ if (!preset)
214
+ preset = stdEnv.env.NITRO_PRESET || stdEnv.env.SERVER_PRESET || detectTarget() || "node-server";
215
+ return preset.replace("_", "-");
216
+ }
217
+ function getPresetNitroPresetCompatibility(target) {
218
+ const normalizedTarget = target.replace(/-legacy$/, "");
219
+ let compatibility = RuntimeCompatibility[normalizedTarget];
220
+ if (!compatibility)
221
+ compatibility = RuntimeCompatibility["nitro-dev"];
222
+ return compatibility;
223
+ }
224
+ async function applyNitroPresetCompatibility(nitroConfig, options) {
225
+ const target = resolveNitroPreset(nitroConfig);
226
+ const compatibility = getPresetNitroPresetCompatibility(target);
227
+ const hasCssInlineNode = await hasResolvableDependency("@css-inline/css-inline");
228
+ const hasCssInlineWasm = await hasResolvableDependency("@css-inline/css-inline-wasm");
229
+ const { resolve, detectedRenderers } = options;
230
+ const satoriEnabled = detectedRenderers.has("satori");
231
+ const chromiumEnabled = detectedRenderers.has("chromium");
232
+ const takumiEnabled = detectedRenderers.has("takumi");
233
+ for (const renderer of detectedRenderers) {
234
+ if (!compatibility[renderer]) {
235
+ logger_js.logger.warn(`Renderer "${renderer}" detected but not supported on "${target}" preset. OG images using .${renderer}.vue components may fail.`);
236
+ }
237
+ }
238
+ const emptyMock = await resolve.resolvePath("./runtime/mock/empty");
239
+ nitroConfig.alias["#og-image/renderers/satori"] = satoriEnabled ? await resolve.resolvePath("./runtime/server/og-image/satori/renderer") : emptyMock;
240
+ nitroConfig.alias["#og-image/renderers/chromium"] = chromiumEnabled ? await resolve.resolvePath("./runtime/server/og-image/chromium/renderer") : emptyMock;
241
+ nitroConfig.alias["#og-image/renderers/takumi"] = takumiEnabled ? await resolve.resolvePath("./runtime/server/og-image/takumi/renderer") : emptyMock;
242
+ const resolvedCompatibility = {};
243
+ async function applyBinding(key) {
244
+ let binding = options.compatibility?.[key];
245
+ if (typeof binding === "undefined")
246
+ binding = compatibility[key];
247
+ if (key === "css-inline" && typeof binding === "string") {
248
+ if (binding === "node" && !hasCssInlineNode || ["wasm", "wasm-fs"].includes(binding) && !hasCssInlineWasm) {
249
+ binding = false;
250
+ }
251
+ }
252
+ resolvedCompatibility[key] = binding;
253
+ return {
254
+ [`#og-image/bindings/${key}`]: binding === false ? emptyMock : await resolve.resolvePath(`./runtime/server/og-image/bindings/${key}/${binding}`)
255
+ };
256
+ }
257
+ nitroConfig.alias = defu.defu(
258
+ await applyBinding("chromium"),
259
+ await applyBinding("satori"),
260
+ await applyBinding("takumi"),
261
+ await applyBinding("resvg"),
262
+ await applyBinding("sharp"),
263
+ await applyBinding("css-inline"),
264
+ nitroConfig.alias || {}
265
+ );
266
+ if (Object.values(compatibility).includes("wasm")) {
267
+ nitroConfig.experimental = nitroConfig.experimental || {};
268
+ nitroConfig.experimental.wasm = true;
269
+ }
270
+ nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
271
+ nitroConfig.wasm = defu.defu(compatibility.wasm, nitroConfig.wasm);
272
+ const normalizedTarget = target.replace(/-legacy$/, "");
273
+ const isEdgePreset = ["cloudflare", "cloudflare-pages", "cloudflare-module", "vercel-edge", "netlify-edge"].includes(normalizedTarget);
274
+ if (isEdgePreset) {
275
+ const mockCode = `import proxy from 'mocked-exports/proxy';export default proxy;export * from 'mocked-exports/proxy';`;
276
+ nitroConfig.virtual = nitroConfig.virtual || {};
277
+ nitroConfig.virtual.canvas = mockCode;
278
+ }
279
+ nitroConfig.virtual["#og-image/compatibility"] = () => `export default ${JSON.stringify(resolvedCompatibility)}`;
280
+ kit.addTemplate({
281
+ filename: "nuxt-og-image/compatibility.mjs",
282
+ getContents() {
283
+ return `export default ${JSON.stringify(resolvedCompatibility)}`;
284
+ },
285
+ options: { mode: "server" }
286
+ });
287
+ return resolvedCompatibility;
288
+ }
289
+ function ensureDependencies(dep, nuxt = kit.useNuxt()) {
290
+ return Promise.all(dep.map((d) => {
291
+ return nypm.ensureDependencyInstalled(d, {
292
+ cwd: nuxt.options.rootDir,
293
+ dev: true
294
+ });
295
+ }));
296
+ }
297
+
298
+ async function setupBuildHandler(config, resolve, getDetectedRenderers, nuxt = kit.useNuxt()) {
299
+ nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
300
+ if (typeof config.runtimeCacheStorage === "object")
301
+ nuxt.options.nitro.storage["nuxt-og-image"] = config.runtimeCacheStorage;
302
+ nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
303
+ const mockCode = `import proxy from 'mocked-exports/proxy';export default proxy;export * from 'mocked-exports/proxy';`;
304
+ nitroConfig.virtual = nitroConfig.virtual || {};
305
+ nitroConfig.virtual["playwright-core"] = mockCode;
306
+ nitroConfig.virtual.electron = mockCode;
307
+ nitroConfig.virtual["electron/index"] = mockCode;
308
+ nitroConfig.virtual["electron/index.js"] = mockCode;
309
+ nitroConfig.virtual.bufferutil = mockCode;
310
+ nitroConfig.virtual["utf-8-validate"] = mockCode;
311
+ nitroConfig.virtual["chromium-bidi"] = mockCode;
312
+ nitroConfig.virtual["chromium-bidi/lib/cjs/bidiMapper/BidiMapper"] = mockCode;
313
+ nitroConfig.virtual["chromium-bidi/lib/cjs/bidiMapper/BidiMapper.js"] = mockCode;
314
+ nitroConfig.virtual.queue = mockCode;
315
+ });
316
+ nuxt.hooks.hook("nitro:init", async (nitro) => {
317
+ const renderers = getDetectedRenderers();
318
+ await applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers });
319
+ const target = resolveNitroPreset(nitro.options);
320
+ const normalizedTarget = target.replace(/-legacy$/, "");
321
+ const isCloudflarePagesOrModule = normalizedTarget === "cloudflare-pages" || normalizedTarget === "cloudflare-module";
322
+ if (isCloudflarePagesOrModule) {
323
+ nitro.options.cloudflare = nitro.options.cloudflare || {};
324
+ nitro.options.cloudflare.pages = nitro.options.cloudflare.pages || {};
325
+ nitro.options.cloudflare.pages.routes = nitro.options.cloudflare.pages.routes || { exclude: [] };
326
+ nitro.options.cloudflare.pages.routes.exclude = nitro.options.cloudflare.pages.routes.exclude || [];
327
+ nitro.options.cloudflare.pages.routes.exclude.push("/_og/s/*");
328
+ }
329
+ nitro.hooks.hook("compiled", async (_nitro) => {
330
+ const compatibility = getPresetNitroPresetCompatibility(target);
331
+ if (compatibility.wasm?.esmImport !== true)
332
+ return;
333
+ const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
334
+ const serverEntry = pathe.join(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
335
+ const wasmEntries = [serverEntry];
336
+ if (isCloudflarePagesOrModule) {
337
+ wasmEntries.push(pathe.join(pathe.dirname(serverEntry), "chunks/wasm.mjs"));
338
+ wasmEntries.push(pathe.join(pathe.dirname(serverEntry), "chunks/_/wasm.mjs"));
339
+ wasmEntries.push(pathe.join(pathe.dirname(serverEntry), "chunks/index_bg.mjs"));
340
+ }
341
+ const resvgHash = await resolveFilePathSha1("@resvg/resvg-wasm/index_bg.wasm");
342
+ const yogaHash = await resolveFilePathSha1("yoga-wasm-web/dist/yoga.wasm");
343
+ const cssInlineHash = await resolveFilePathSha1("@css-inline/css-inline-wasm/index_bg.wasm");
344
+ for (const entry of wasmEntries) {
345
+ if (!fs.existsSync(entry))
346
+ continue;
347
+ const contents = await promises.readFile(entry, "utf-8");
348
+ const postfix = target === "vercel-edge" ? "?module" : "";
349
+ const wasmPath = isCloudflarePagesOrModule ? `../wasm/` : `./wasm/`;
350
+ await promises.writeFile(entry, contents.replaceAll('"@resvg/resvg-wasm/index_bg.wasm?module"', `"${wasmPath}index_bg-${resvgHash}.wasm${postfix}"`).replaceAll('"@css-inline/css-inline-wasm/index_bg.wasm?module"', `"${wasmPath}index_bg-${cssInlineHash}.wasm${postfix}"`).replaceAll('"yoga-wasm-web/dist/yoga.wasm?module"', `"${wasmPath}yoga-${yogaHash}.wasm${postfix}"`), { encoding: "utf-8" });
351
+ }
352
+ });
353
+ });
354
+ }
355
+ async function resolveFilePathSha1(path) {
356
+ const _path = await kit.resolvePath(path);
357
+ return sha1(fs.existsSync(_path) ? await promises.readFile(_path) : Buffer.from(path));
358
+ }
359
+ function sha1(source) {
360
+ return node_crypto.createHash("sha1").update(source).digest("hex").slice(0, 16);
361
+ }
362
+
363
+ function setupDevHandler(options, resolve, getDetectedRenderers, nuxt = kit.useNuxt()) {
364
+ nuxt.hooks.hook("nitro:init", async (nitro) => {
365
+ await applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers: getDetectedRenderers() });
366
+ });
367
+ }
368
+
369
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-og-image";
370
+ const DEVTOOLS_UI_LOCAL_PORT = 3030;
371
+ function setupDevToolsUI(options, resolve, nuxt = kit.useNuxt()) {
372
+ const clientPath = resolve("./client");
373
+ const isProductionBuild = fs.existsSync(clientPath);
374
+ if (isProductionBuild) {
375
+ nuxt.hook("vite:serverCreated", async (server) => {
376
+ const sirv = await import('sirv').then((r) => r.default || r);
377
+ server.middlewares.use(
378
+ DEVTOOLS_UI_ROUTE,
379
+ sirv(clientPath, { dev: true, single: true })
380
+ );
381
+ });
382
+ } else {
383
+ nuxt.hook("vite:extendConfig", (config) => {
384
+ if (!config.server) {
385
+ config.server = {};
386
+ }
387
+ config.server.proxy ||= {};
388
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
389
+ target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
390
+ changeOrigin: true,
391
+ followRedirects: true,
392
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
393
+ };
394
+ });
395
+ }
396
+ const useNitro = new Promise((resolve2) => {
397
+ nuxt.hooks.hook("nitro:init", resolve2);
398
+ });
399
+ devtoolsKit.onDevToolsInitialized(async () => {
400
+ const rpc = devtoolsKit.extendServerRpc("nuxt-og-image", {
401
+ async ejectCommunityTemplate(path) {
402
+ const [dirName, componentName] = path.split("/");
403
+ const dir = resolve(nuxt.options.srcDir, "components", dirName || "");
404
+ if (!fs.existsSync(dir)) {
405
+ fs.mkdirSync(dir);
406
+ }
407
+ const newPath = resolve(dir, componentName || "");
408
+ const templatePath = resolve(`./runtime/app/components/Templates/Community/${componentName}`);
409
+ const template = (await promises.readFile(templatePath, "utf-8")).replace("{{ title }}", `{{ title }} - Ejected!`);
410
+ await promises.writeFile(newPath, template, { encoding: "utf-8" });
411
+ await kit.updateTemplates({ filter: (t) => t.filename.includes("nuxt-og-image/components.mjs") });
412
+ const nitro = await useNitro;
413
+ await nitro.hooks.callHook("rollup:reload");
414
+ return newPath;
415
+ }
416
+ });
417
+ nuxt.hook("builder:watch", (e, path) => {
418
+ path = node_path.relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path));
419
+ if ((e === "change" || e.includes("link")) && (path.startsWith("pages") || path.startsWith("content"))) {
420
+ rpc.broadcast.refreshRouteData(path).catch(() => {
421
+ });
422
+ }
423
+ if (options.componentDirs.some((dir) => path.includes(dir))) {
424
+ if (e === "change") {
425
+ rpc.broadcast.refresh().catch(() => {
426
+ });
427
+ } else {
428
+ rpc.broadcast.refreshGlobalData().catch(() => {
429
+ });
430
+ }
431
+ }
432
+ });
433
+ });
434
+ devtoolsKit.addCustomTab({
435
+ name: "nuxt-og-image",
436
+ title: "OG Image",
437
+ icon: "carbon:image-search",
438
+ view: {
439
+ type: "iframe",
440
+ src: DEVTOOLS_UI_ROUTE
441
+ }
442
+ });
443
+ }
444
+
445
+ function setupGenerateHandler(_options, resolve, _getDetectedRenderers, nuxt = kit.useNuxt()) {
446
+ nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
447
+ await applyNitroPresetCompatibility(nitroConfig, {
448
+ compatibility: {
449
+ "chromium": false,
450
+ "satori": false,
451
+ "css-inline": false,
452
+ "resvg": false,
453
+ "sharp": false
454
+ },
455
+ resolve,
456
+ detectedRenderers: /* @__PURE__ */ new Set()
457
+ });
458
+ });
459
+ }
460
+
461
+ const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
462
+ function setupPrerenderHandler(options, resolve, getDetectedRenderers, nuxt = kit.useNuxt()) {
463
+ nuxt.hooks.hook("nitro:init", async (nitro) => {
464
+ nitro.hooks.hook("prerender:config", async (nitroConfig) => {
465
+ await applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers() });
466
+ nitroConfig.wasm = nitroConfig.wasm || {};
467
+ nitroConfig.wasm.esmImport = false;
468
+ });
469
+ nitro.hooks.hook("prerender:done", async () => {
470
+ const buildCachePath = typeof options.buildCache === "object" && options.buildCache.base ? options.buildCache.base : "node_modules/.cache/nuxt-seo/og-image";
471
+ const buildCacheDir = options.buildCache ? pathe.join(nuxt.options.rootDir, buildCachePath) : null;
472
+ if (!buildCacheDir || !fs.existsSync(buildCacheDir))
473
+ return;
474
+ const files = fs.readdirSync(buildCacheDir);
475
+ const now = Date.now();
476
+ let cleanedCount = 0;
477
+ for (const file of files) {
478
+ if (file.startsWith("."))
479
+ continue;
480
+ const filePath = pathe.join(buildCacheDir, file);
481
+ const content = JSON.parse(fs.readFileSync(filePath, "utf-8"));
482
+ const createdAt = content.createdAt || fs.statSync(filePath).mtimeMs;
483
+ const expiresAt = content.expiresAt || 0;
484
+ const isExpired = expiresAt < now;
485
+ const isOld = now - createdAt > ONE_WEEK_MS;
486
+ if (isExpired && isOld) {
487
+ fs.rmSync(filePath);
488
+ cleanedCount++;
489
+ }
490
+ }
491
+ if (cleanedCount > 0)
492
+ logger_js.logger.info(`Cleaned ${cleanedCount} orphaned OG image cache file${cleanedCount > 1 ? "s" : ""}.`);
493
+ });
494
+ });
495
+ }
496
+
497
+ function isVue(id, opts = {}) {
498
+ const { search } = ufo.parseURL(decodeURIComponent(node_url.pathToFileURL(id).href));
499
+ if (id.endsWith(".vue") && !search) {
500
+ return true;
501
+ }
502
+ if (!search) {
503
+ return false;
504
+ }
505
+ const query = ufo.parseQuery(search);
506
+ if (query.nuxt_component) {
507
+ return false;
508
+ }
509
+ if (query.macro && (search === "?macro=true" || !opts.type || opts.type.includes("script"))) {
510
+ return true;
511
+ }
512
+ const type = "setup" in query ? "script" : query.type;
513
+ if (!("vue" in query) || opts.type && !opts.type.includes(type)) {
514
+ return false;
515
+ }
516
+ return true;
517
+ }
518
+ const JS_RE = /\.(?:[cm]?j|t)sx?$/;
519
+ function isJS(id) {
520
+ const { pathname } = ufo.parseURL(decodeURIComponent(node_url.pathToFileURL(id).href));
521
+ return JS_RE.test(pathname);
522
+ }
523
+ const TreeShakeComposablesPlugin = unplugin.createUnplugin(() => {
524
+ const composableNames = [
525
+ "defineOgImage",
526
+ "defineOgImageComponent",
527
+ "defineOgImageScreenshot"
528
+ ];
529
+ const regexp = `(^\\s*)(${composableNames.join("|")})(?=\\((?!\\) \\{))`;
530
+ const COMPOSABLE_RE = new RegExp(regexp, "m");
531
+ const COMPOSABLE_RE_GLOBAL = new RegExp(regexp, "gm");
532
+ return {
533
+ name: "nuxt-og-image:zero-runtime:transform",
534
+ enforce: "pre",
535
+ transformInclude(id) {
536
+ return isVue(id, { type: ["script"] }) || isJS(id);
537
+ },
538
+ transform(code, id) {
539
+ const s = new MagicString__default(code);
540
+ if (id.endsWith("components.islands.mjs")) ; else {
541
+ const strippedCode = stripLiteral.stripLiteral(code);
542
+ if (!COMPOSABLE_RE.test(code)) {
543
+ return;
544
+ }
545
+ for (const match of strippedCode.matchAll(COMPOSABLE_RE_GLOBAL)) {
546
+ s.overwrite(match.index, match.index + match[0].length, `${match[1]} import.meta.prerender && ${match[2]}`);
547
+ }
548
+ }
549
+ if (s.hasChanged()) {
550
+ return {
551
+ code: s.toString(),
552
+ map: s.generateMap({ hires: true })
553
+ };
554
+ }
555
+ }
556
+ };
557
+ });
558
+
559
+ const toSrgbGamut = culori.toGamut("rgb", "oklch");
560
+ function decodeCssClassName(selector) {
561
+ let className = selector.startsWith(".") ? selector.slice(1) : selector;
562
+ className = className.replace(/\\([0-9a-f]+)\s?/gi, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)));
563
+ className = className.replace(/\\(.)/g, "$1");
564
+ return className;
565
+ }
566
+ function createStylesheetLoader(baseDir) {
567
+ return async (id, base) => {
568
+ if (id === "tailwindcss") {
569
+ const twPath = exsolve.resolveModulePath("tailwindcss/index.css", { from: baseDir });
570
+ if (twPath) {
571
+ const content = await promises.readFile(twPath, "utf-8").catch(() => "");
572
+ if (content)
573
+ return { path: twPath, base: pathe.dirname(twPath), content };
574
+ }
575
+ return {
576
+ path: "virtual:tailwindcss",
577
+ base,
578
+ content: "@layer theme, base, components, utilities;\n@layer utilities { @tailwind utilities; }"
579
+ };
580
+ }
581
+ if (id.startsWith("./") || id.startsWith("../")) {
582
+ const resolved2 = pathe.join(base || baseDir, id);
583
+ const content = await promises.readFile(resolved2, "utf-8").catch(() => "");
584
+ return { path: resolved2, base: pathe.dirname(resolved2), content };
585
+ }
586
+ const resolved = exsolve.resolveModulePath(id, { from: base || baseDir, conditions: ["style"] });
587
+ if (resolved) {
588
+ const content = await promises.readFile(resolved, "utf-8").catch(() => "");
589
+ return { path: resolved, base: pathe.dirname(resolved), content };
590
+ }
591
+ return { path: id, base, content: "" };
592
+ };
593
+ }
594
+ function convertColorToHex(value) {
595
+ if (!value || value.includes("var("))
596
+ return value;
597
+ const color = culori.parse(value);
598
+ if (!color)
599
+ return value;
600
+ const mapped = toSrgbGamut(color);
601
+ return culori.formatHex(mapped) || value;
602
+ }
603
+ function convertOklchToHex(css) {
604
+ return css.replace(/oklch\([^)]+\)/g, (match) => convertColorToHex(match));
605
+ }
606
+ const COLOR_PROPERTIES = /* @__PURE__ */ new Set([
607
+ "color",
608
+ "background-color",
609
+ "background-image",
610
+ "border-color",
611
+ "border-top-color",
612
+ "border-right-color",
613
+ "border-bottom-color",
614
+ "border-left-color",
615
+ "outline-color",
616
+ "fill",
617
+ "stroke",
618
+ "text-decoration-color",
619
+ "caret-color",
620
+ "accent-color"
621
+ ]);
622
+ function buildNuxtUiVars(vars, nuxtUiColors) {
623
+ const colors = twColors__default;
624
+ const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
625
+ for (const [semantic, colorName] of Object.entries(nuxtUiColors)) {
626
+ for (const shade of shades) {
627
+ const varName = `--ui-color-${semantic}-${shade}`;
628
+ const value = colors[colorName]?.[shade];
629
+ if (value && !vars.has(varName))
630
+ vars.set(varName, value);
631
+ }
632
+ if (!vars.has(`--ui-${semantic}`))
633
+ vars.set(`--ui-${semantic}`, colors[colorName]?.[500] || "");
634
+ }
635
+ const neutral = nuxtUiColors.neutral || "slate";
636
+ const neutralColors = colors[neutral];
637
+ const semanticVars = {
638
+ "--ui-text-dimmed": neutralColors?.[400] || "",
639
+ "--ui-text-muted": neutralColors?.[500] || "",
640
+ "--ui-text-toned": neutralColors?.[600] || "",
641
+ "--ui-text": neutralColors?.[700] || "",
642
+ "--ui-text-highlighted": neutralColors?.[900] || "",
643
+ "--ui-text-inverted": "#ffffff",
644
+ "--ui-bg": "#ffffff",
645
+ "--ui-bg-muted": neutralColors?.[50] || "",
646
+ "--ui-bg-elevated": neutralColors?.[100] || "",
647
+ "--ui-bg-accented": neutralColors?.[200] || "",
648
+ "--ui-bg-inverted": neutralColors?.[900] || "",
649
+ "--ui-border": neutralColors?.[200] || "",
650
+ "--ui-border-muted": neutralColors?.[200] || "",
651
+ "--ui-border-accented": neutralColors?.[300] || "",
652
+ "--ui-border-inverted": neutralColors?.[900] || ""
653
+ };
654
+ for (const [name, value] of Object.entries(semanticVars)) {
655
+ if (value && !vars.has(name))
656
+ vars.set(name, value);
657
+ }
658
+ }
659
+ function walkTemplateAst(nodes, visitor) {
660
+ for (const node of nodes) {
661
+ visitor(node);
662
+ if ("children" in node && Array.isArray(node.children))
663
+ walkTemplateAst(node.children, visitor);
664
+ }
665
+ }
666
+
667
+ let cachedCompiler = null;
668
+ let cachedCssPath = null;
669
+ let cachedVars = null;
670
+ const resolvedStyleCache = /* @__PURE__ */ new Map();
671
+ async function getCompiler(cssPath, nuxtUiColors) {
672
+ if (cachedCompiler && cachedCssPath === cssPath)
673
+ return { compiler: cachedCompiler, vars: cachedVars };
674
+ const userCss = await promises.readFile(cssPath, "utf-8");
675
+ const baseDir = pathe.dirname(cssPath);
676
+ const compiler = await tailwindcss.compile(userCss, {
677
+ loadStylesheet: createStylesheetLoader(baseDir)
678
+ });
679
+ const vars = /* @__PURE__ */ new Map();
680
+ if (nuxtUiColors)
681
+ buildNuxtUiVars(vars, nuxtUiColors);
682
+ cachedCompiler = compiler;
683
+ cachedCssPath = cssPath;
684
+ cachedVars = vars;
685
+ resolvedStyleCache.clear();
686
+ return { compiler, vars };
687
+ }
688
+ function parseCssOutput(css, vars) {
689
+ const classes = /* @__PURE__ */ new Map();
690
+ const root = postcss__default.parse(css);
691
+ root.walkRules((rule) => {
692
+ if (rule.selector.includes(":root") || rule.selector.includes(":host")) {
693
+ rule.walkDecls((decl) => {
694
+ if (decl.prop.startsWith("--") && !vars.has(decl.prop))
695
+ vars.set(decl.prop, decl.value);
696
+ });
697
+ }
698
+ });
699
+ root.walkRules((rule) => {
700
+ const sel = rule.selector;
701
+ if (!sel.startsWith(".") || sel.includes(":") || sel.includes(" ") || sel.includes(">"))
702
+ return;
703
+ const className = decodeCssClassName(sel);
704
+ const styles = {};
705
+ rule.walkDecls((decl) => {
706
+ if (decl.prop.startsWith("--tw-"))
707
+ return;
708
+ styles[decl.prop] = decl.value;
709
+ });
710
+ if (Object.keys(styles).length)
711
+ classes.set(className, styles);
712
+ });
713
+ return classes;
714
+ }
715
+ function evaluateCalc(value) {
716
+ if (!value.includes("calc("))
717
+ return value;
718
+ const fakeCss = `.x{v:${value}}`;
719
+ const result = postcss__default([postcssCalc__default({})]).process(fakeCss, { from: void 0 });
720
+ const match = result.css.match(/\.x\{v:(.+)\}/);
721
+ return match?.[1] ?? value;
722
+ }
723
+ function parseVarExpression(value, startIndex) {
724
+ if (!value.startsWith("var(", startIndex))
725
+ return null;
726
+ let depth = 1;
727
+ let i = startIndex + 4;
728
+ let varName = "";
729
+ let fallback = null;
730
+ let inFallback = false;
731
+ while (i < value.length && depth > 0) {
732
+ const char = value[i];
733
+ if (char === "(") {
734
+ depth++;
735
+ if (inFallback)
736
+ fallback += char;
737
+ } else if (char === ")") {
738
+ depth--;
739
+ if (depth > 0 && inFallback)
740
+ fallback += char;
741
+ } else if (char === "," && depth === 1 && !inFallback) {
742
+ inFallback = true;
743
+ fallback = "";
744
+ i++;
745
+ while (i < value.length && value[i] === " ")
746
+ i++;
747
+ continue;
748
+ } else if (inFallback && char) {
749
+ fallback += char;
750
+ } else {
751
+ varName += char;
752
+ }
753
+ i++;
754
+ }
755
+ if (depth !== 0 || !varName.startsWith("--"))
756
+ return null;
757
+ return {
758
+ varName: varName.trim(),
759
+ fallback: fallback?.trim() || null,
760
+ endIndex: i
761
+ };
762
+ }
763
+ function resolveVars(value, vars, depth = 0) {
764
+ if (depth > 10 || !value.includes("var("))
765
+ return evaluateCalc(value);
766
+ const calcMatch = value.match(/calc\(var\((--[\w-]+)\)\s*\*\s*([\d.]+)\)/);
767
+ if (calcMatch?.[1] && calcMatch?.[2]) {
768
+ const varValue = vars.get(calcMatch[1]);
769
+ if (varValue) {
770
+ const numMatch = varValue.match(/([\d.]+)(rem|px|em|%)/);
771
+ if (numMatch?.[1] && numMatch?.[2]) {
772
+ const computed = Number.parseFloat(numMatch[1]) * Number.parseFloat(calcMatch[2]);
773
+ return `${computed}${numMatch[2]}`;
774
+ }
775
+ }
776
+ }
777
+ let result = value;
778
+ let iterations = 0;
779
+ const maxIterations = 50;
780
+ while (result.includes("var(") && iterations < maxIterations) {
781
+ const varIndex = result.indexOf("var(");
782
+ if (varIndex === -1)
783
+ break;
784
+ const parsed = parseVarExpression(result, varIndex);
785
+ if (!parsed) {
786
+ result = result.slice(0, varIndex) + result.slice(varIndex + 4);
787
+ iterations++;
788
+ continue;
789
+ }
790
+ const resolved = vars.get(parsed.varName) ?? parsed.fallback;
791
+ if (resolved) {
792
+ result = result.slice(0, varIndex) + resolved + result.slice(parsed.endIndex);
793
+ } else {
794
+ result = result.slice(0, varIndex) + result.slice(parsed.endIndex);
795
+ }
796
+ iterations++;
797
+ }
798
+ if (result.includes("var(") && depth < 10)
799
+ return resolveVars(result, vars, depth + 1);
800
+ return evaluateCalc(result);
801
+ }
802
+ function convertGradientColors(value) {
803
+ return value.replace(/oklch\([^)]+\)/g, (match) => {
804
+ const hex = convertColorToHex(match);
805
+ return hex.startsWith("#") ? hex : match;
806
+ });
807
+ }
808
+ const LINEAR_GRADIENT_DIRECTIONS = {
809
+ "bg-gradient-to-t": "to top",
810
+ "bg-gradient-to-tr": "to top right",
811
+ "bg-gradient-to-r": "to right",
812
+ "bg-gradient-to-br": "to bottom right",
813
+ "bg-gradient-to-b": "to bottom",
814
+ "bg-gradient-to-bl": "to bottom left",
815
+ "bg-gradient-to-l": "to left",
816
+ "bg-gradient-to-tl": "to top left"
817
+ };
818
+ const RADIAL_GRADIENT_SHAPES = {
819
+ "bg-radial": "circle at center",
820
+ "bg-radial-at-t": "circle at top",
821
+ "bg-radial-at-tr": "circle at top right",
822
+ "bg-radial-at-r": "circle at right",
823
+ "bg-radial-at-br": "circle at bottom right",
824
+ "bg-radial-at-b": "circle at bottom",
825
+ "bg-radial-at-bl": "circle at bottom left",
826
+ "bg-radial-at-l": "circle at left",
827
+ "bg-radial-at-tl": "circle at top left",
828
+ "bg-radial-at-c": "circle at center"
829
+ };
830
+ function resolveGradientColor(cls, vars) {
831
+ const colorName = cls.replace(/^(from|via|to)-/, "");
832
+ const shadeMatch = colorName.match(/^(.+)-(\d+)$/);
833
+ if (shadeMatch?.[1] && shadeMatch?.[2]) {
834
+ const varName2 = `--color-${shadeMatch[1]}-${shadeMatch[2]}`;
835
+ const value2 = vars.get(varName2);
836
+ if (value2) {
837
+ const resolved = resolveVars(value2, vars);
838
+ if (!resolved.includes("var("))
839
+ return convertColorToHex(resolved);
840
+ }
841
+ }
842
+ const varName = `--color-${colorName}`;
843
+ const value = vars.get(varName);
844
+ if (value) {
845
+ const resolved = resolveVars(value, vars);
846
+ if (!resolved.includes("var("))
847
+ return convertColorToHex(resolved);
848
+ }
849
+ return null;
850
+ }
851
+ function buildGradient(classes, vars) {
852
+ const linearDir = classes.find((c) => LINEAR_GRADIENT_DIRECTIONS[c]);
853
+ const radialShape = classes.find((c) => RADIAL_GRADIENT_SHAPES[c]);
854
+ const fromClass = classes.find((c) => c.startsWith("from-"));
855
+ const viaClass = classes.find((c) => c.startsWith("via-"));
856
+ const toClass = classes.find((c) => c.startsWith("to-") && !/^to-(?:[tblr]|tl|tr|bl|br)$/.test(c));
857
+ if (!linearDir && !radialShape)
858
+ return null;
859
+ if (!fromClass && !toClass)
860
+ return null;
861
+ const fromColor = fromClass ? resolveGradientColor(fromClass, vars) : null;
862
+ const viaColor = viaClass ? resolveGradientColor(viaClass, vars) : null;
863
+ const toColor = toClass ? resolveGradientColor(toClass, vars) : null;
864
+ if (!fromColor && !toColor)
865
+ return null;
866
+ const stops = [fromColor, viaColor, toColor].filter(Boolean).join(", ");
867
+ const colorClasses = [fromClass, viaClass, toClass].filter((c) => !!c);
868
+ if (linearDir) {
869
+ const direction = LINEAR_GRADIENT_DIRECTIONS[linearDir];
870
+ return {
871
+ gradientClass: linearDir,
872
+ value: `linear-gradient(${direction}, ${stops})`,
873
+ colorClasses
874
+ };
875
+ }
876
+ if (radialShape) {
877
+ const shape = RADIAL_GRADIENT_SHAPES[radialShape];
878
+ return {
879
+ gradientClass: radialShape,
880
+ value: `radial-gradient(${shape}, ${stops})`,
881
+ colorClasses
882
+ };
883
+ }
884
+ return null;
885
+ }
886
+ async function resolveClassesToStyles(classes, options) {
887
+ const { compiler, vars } = await getCompiler(options.cssPath, options.nuxtUiColors);
888
+ const uncached = classes.filter((c) => !resolvedStyleCache.has(c));
889
+ if (uncached.length > 0) {
890
+ const outputCss = compiler.build(uncached);
891
+ const parsedClasses = parseCssOutput(outputCss, vars);
892
+ for (const [className, rawStyles] of parsedClasses) {
893
+ const styles = {};
894
+ for (const [prop, rawValue] of Object.entries(rawStyles)) {
895
+ let value = resolveVars(rawValue, vars);
896
+ if (value.includes("var("))
897
+ continue;
898
+ if (COLOR_PROPERTIES.has(prop)) {
899
+ if (prop === "background-image" && value.includes("gradient")) {
900
+ value = convertGradientColors(value);
901
+ } else {
902
+ value = convertColorToHex(value);
903
+ }
904
+ }
905
+ styles[prop] = value;
906
+ }
907
+ resolvedStyleCache.set(className, Object.keys(styles).length ? styles : null);
908
+ }
909
+ for (const c of uncached) {
910
+ if (!resolvedStyleCache.has(c))
911
+ resolvedStyleCache.set(c, null);
912
+ }
913
+ }
914
+ const result = {};
915
+ for (const cls of classes) {
916
+ const resolved = resolvedStyleCache.get(cls);
917
+ if (resolved)
918
+ result[cls] = resolved;
919
+ }
920
+ const gradient = buildGradient(classes, vars);
921
+ if (gradient) {
922
+ result[gradient.gradientClass] = { "background-image": gradient.value };
923
+ for (const colorClass of gradient.colorClasses) {
924
+ result[colorClass] = {};
925
+ }
926
+ }
927
+ return result;
928
+ }
929
+ async function extractTw4Metadata(options) {
930
+ const { compiler, vars } = await getCompiler(options.cssPath, options.nuxtUiColors);
931
+ const themeCss = compiler.build([]);
932
+ parseCssOutput(themeCss, vars);
933
+ const fontVars = {};
934
+ const breakpoints = {};
935
+ const colors = {};
936
+ for (const [name, value] of vars) {
937
+ const resolvedValue = resolveVars(value, vars);
938
+ if (name.startsWith("--font-")) {
939
+ fontVars[name.slice(2)] = resolvedValue;
940
+ } else if (name.startsWith("--breakpoint-")) {
941
+ const px = Number.parseInt(resolvedValue.replace("px", ""), 10);
942
+ if (!Number.isNaN(px))
943
+ breakpoints[name.slice(13)] = px;
944
+ } else if (name.startsWith("--color-") && !resolvedValue.includes("var(")) {
945
+ const colorPath = name.slice(8);
946
+ const shadeMatch = colorPath.match(/^(.+)-(\d+)$/);
947
+ const hexValue = convertColorToHex(resolvedValue);
948
+ if (shadeMatch) {
949
+ const [, colorName, shade] = shadeMatch;
950
+ if (colorName) {
951
+ if (!colors[colorName])
952
+ colors[colorName] = {};
953
+ if (typeof colors[colorName] === "object")
954
+ colors[colorName][shade] = hexValue;
955
+ }
956
+ } else {
957
+ colors[colorPath] = hexValue;
958
+ }
959
+ }
960
+ }
961
+ return { fontVars, breakpoints, colors };
962
+ }
963
+ function clearTw4Cache() {
964
+ cachedCompiler = null;
965
+ cachedCssPath = null;
966
+ cachedVars = null;
967
+ resolvedStyleCache.clear();
968
+ }
969
+
970
+ const ELEMENT_NODE = 1;
971
+ const ATTRIBUTE_NODE = 6;
972
+ function escapeAttrValue(value) {
973
+ return value.replace(/"/g, "&quot;");
974
+ }
975
+ async function transformVueTemplate(code, options) {
976
+ const { descriptor } = compilerSfc.parse(code);
977
+ if (!descriptor.template?.ast)
978
+ return void 0;
979
+ const s = new MagicString__default(code);
980
+ const collectors = [];
981
+ walkTemplateAst(descriptor.template.ast.children, (node) => {
982
+ if (node.type !== ELEMENT_NODE)
983
+ return;
984
+ const el = node;
985
+ const collector = {
986
+ classes: [],
987
+ elementLoc: { start: el.loc.start.offset, end: el.loc.end.offset }
988
+ };
989
+ for (const prop of el.props) {
990
+ if (prop.type === ATTRIBUTE_NODE && prop.name === "class" && prop.value) {
991
+ collector.classes = prop.value.content.split(/\s+/).filter(Boolean);
992
+ collector.classLoc = {
993
+ start: prop.loc.start.offset,
994
+ end: prop.loc.end.offset
995
+ };
996
+ }
997
+ if (prop.type === ATTRIBUTE_NODE && prop.name === "style" && prop.value) {
998
+ collector.existingStyle = prop.value.content;
999
+ collector.styleLoc = {
1000
+ start: prop.loc.start.offset,
1001
+ end: prop.loc.end.offset
1002
+ };
1003
+ }
1004
+ }
1005
+ if (collector.classes.length > 0) {
1006
+ collectors.push(collector);
1007
+ }
1008
+ });
1009
+ if (collectors.length === 0)
1010
+ return void 0;
1011
+ let hasChanges = false;
1012
+ for (const collector of collectors.reverse()) {
1013
+ const styleMap = await options.resolveStyles(collector.classes);
1014
+ const styleProps = {};
1015
+ const unresolvedClasses = [];
1016
+ for (const cls of collector.classes) {
1017
+ const resolved = styleMap[cls];
1018
+ if (resolved && Object.keys(resolved).length > 0) {
1019
+ Object.assign(styleProps, resolved);
1020
+ } else if (!resolved) {
1021
+ unresolvedClasses.push(cls);
1022
+ }
1023
+ }
1024
+ if (collector.existingStyle) {
1025
+ for (const decl of collector.existingStyle.split(";")) {
1026
+ const [prop, ...valParts] = decl.split(":");
1027
+ const value = valParts.join(":").trim();
1028
+ if (prop?.trim() && value) {
1029
+ styleProps[prop.trim()] = value;
1030
+ }
1031
+ }
1032
+ }
1033
+ const styleStr = Object.entries(styleProps).map(([prop, value]) => `${prop}: ${escapeAttrValue(value)}`).join("; ");
1034
+ const hasResolvedStyles = Object.keys(styleProps).length > 0;
1035
+ const hasUnresolved = unresolvedClasses.length > 0;
1036
+ const resolvedSome = unresolvedClasses.length < collector.classes.length;
1037
+ if (!resolvedSome)
1038
+ continue;
1039
+ hasChanges = true;
1040
+ if (!collector.classLoc)
1041
+ continue;
1042
+ if (hasUnresolved) {
1043
+ s.overwrite(collector.classLoc.start, collector.classLoc.end, `class="${unresolvedClasses.join(" ")}"`);
1044
+ } else {
1045
+ s.remove(collector.classLoc.start, collector.classLoc.end);
1046
+ }
1047
+ if (hasResolvedStyles) {
1048
+ if (collector.styleLoc) {
1049
+ s.overwrite(collector.styleLoc.start, collector.styleLoc.end, `style="${styleStr}"`);
1050
+ } else {
1051
+ s.appendLeft(collector.classLoc.start, `style="${styleStr}" `);
1052
+ }
1053
+ }
1054
+ }
1055
+ if (!hasChanges)
1056
+ return void 0;
1057
+ return {
1058
+ code: s.toString(),
1059
+ map: s.generateMap({ hires: true })
1060
+ };
1061
+ }
1062
+
1063
+ let svgCounter = 0;
1064
+ function makeIdsUnique(svg) {
1065
+ const prefix = `og${svgCounter++}_`;
1066
+ const ids = /* @__PURE__ */ new Set();
1067
+ svg.replace(/\bid="([^"]+)"/g, (_, id) => {
1068
+ ids.add(id);
1069
+ return "";
1070
+ });
1071
+ let result = svg;
1072
+ for (const id of ids) {
1073
+ result = result.replace(new RegExp(`id="${id}"`, "g"), `id="${prefix}${id}"`).replace(new RegExp(`url\\(#${id}\\)`, "g"), `url(#${prefix}${id})`).replace(new RegExp(`href="#${id}"`, "g"), `href="#${prefix}${id}"`);
1074
+ }
1075
+ return result;
1076
+ }
1077
+ function wrapDefsElements(body) {
1078
+ const defsElements = ["linearGradient", "radialGradient", "filter", "clipPath", "mask", "pattern"];
1079
+ const defsRegex = new RegExp(`<(${defsElements.join("|")})[\\s\\S]*?<\\/\\1>`, "g");
1080
+ if (body.includes("<defs>") || body.includes("<defs "))
1081
+ return body;
1082
+ const foundDefs = [];
1083
+ const result = body.replace(defsRegex, (match) => {
1084
+ foundDefs.push(match);
1085
+ return "";
1086
+ });
1087
+ if (foundDefs.length > 0)
1088
+ return `<defs>${foundDefs.join("")}</defs>${result}`;
1089
+ return body;
1090
+ }
1091
+ function buildIconSvg(iconData, defaultWidth, defaultHeight, attrs) {
1092
+ const body = wrapDefsElements(iconData.body || "");
1093
+ const width = iconData.width || defaultWidth;
1094
+ const height = iconData.height || defaultHeight;
1095
+ const attrPairs = attrs.matchAll(/(\w+(?:-\w+)*)="([^"]*)"/g);
1096
+ const filteredAttrs = [];
1097
+ for (const [, key, value] of attrPairs) {
1098
+ if (key !== "name")
1099
+ filteredAttrs.push(`${key}="${value}"`);
1100
+ }
1101
+ const attrsStr = filteredAttrs.length > 0 ? ` ${filteredAttrs.join(" ")}` : "";
1102
+ let svg = `<span${attrsStr} style="display:flex"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="100%" height="100%" fill="currentColor">${body}</svg></span>`;
1103
+ svg = makeIdsUnique(svg);
1104
+ return svg;
1105
+ }
1106
+ const iconCache = /* @__PURE__ */ new Map();
1107
+ async function loadIconSet(prefix) {
1108
+ if (iconCache.has(prefix))
1109
+ return iconCache.get(prefix);
1110
+ const icons = await import(`@iconify-json/${prefix}/icons.json`, { with: { type: 'json' } }).then((m) => m.default).catch(() => null);
1111
+ iconCache.set(prefix, icons);
1112
+ return icons;
1113
+ }
1114
+ function buildEmojiSvg(emoji, icons, emojiSet) {
1115
+ const codePoint = emojiUtils_js.getEmojiCodePoint(emoji);
1116
+ const possibleNames = emojiUtils_js.getEmojiIconNames(codePoint, emojiSet);
1117
+ for (const iconName of possibleNames) {
1118
+ const iconData = icons.icons?.[iconName];
1119
+ if (iconData) {
1120
+ const body = wrapDefsElements(iconData.body || "");
1121
+ const width = iconData.width || icons.width || 128;
1122
+ const height = iconData.height || icons.height || 128;
1123
+ let svg = `<span style="display:flex"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="1em" height="1em">${body}</svg></span>`;
1124
+ svg = makeIdsUnique(svg);
1125
+ return svg;
1126
+ }
1127
+ }
1128
+ return null;
1129
+ }
1130
+ function getMimeType(ext) {
1131
+ const mimeTypes = {
1132
+ png: "image/png",
1133
+ jpg: "image/jpeg",
1134
+ jpeg: "image/jpeg",
1135
+ gif: "image/gif",
1136
+ webp: "image/webp",
1137
+ svg: "image/svg+xml"
1138
+ };
1139
+ return mimeTypes[ext] || "application/octet-stream";
1140
+ }
1141
+ const SATORI_UNSUPPORTED_PATTERNS = [
1142
+ /^ring(-|$)/,
1143
+ // Ring utilities - not supported
1144
+ /^divide(-|$)/,
1145
+ // Divide utilities - not supported
1146
+ /^space-(x|y)(-|$)/,
1147
+ // Space utilities - use gap instead
1148
+ /^backdrop-/,
1149
+ // Backdrop utilities - not supported
1150
+ // Note: filter utilities (blur, brightness, etc.) ARE supported by Satori
1151
+ // Note: skew transforms ARE supported by Satori
1152
+ /^transition(-|$)/,
1153
+ // Animation (static image)
1154
+ /^animate(-|$)/,
1155
+ /^duration(-|$)/,
1156
+ /^ease(-|$)/,
1157
+ /^delay(-|$)/,
1158
+ /^scroll(-|$)/,
1159
+ // Scroll utilities
1160
+ /^snap(-|$)/,
1161
+ /^touch(-|$)/,
1162
+ // Touch/pointer
1163
+ /^pointer-events(-|$)/,
1164
+ /^cursor(-|$)/,
1165
+ /^select(-|$)/,
1166
+ // User select
1167
+ /^will-change(-|$)/,
1168
+ /^placeholder(-|$)/,
1169
+ /^caret(-|$)/,
1170
+ /^accent(-|$)/,
1171
+ /^columns(-|$)/,
1172
+ /^break-(before|after|inside)(-|$)/,
1173
+ /^hyphens(-|$)/,
1174
+ /^content-(?!center|start|end|between|around|evenly|stretch)/
1175
+ // content-* except flex alignment
1176
+ ];
1177
+ function isSatoriUnsupported(cls) {
1178
+ return SATORI_UNSUPPORTED_PATTERNS.some((p) => p.test(cls));
1179
+ }
1180
+ const AssetTransformPlugin = unplugin.createUnplugin((options) => {
1181
+ let emojiIcons = null;
1182
+ return {
1183
+ name: "nuxt-og-image:asset-transform",
1184
+ enforce: "pre",
1185
+ transformInclude(id) {
1186
+ if (!id.endsWith(".vue") || id.includes("node_modules"))
1187
+ return false;
1188
+ return options.ogComponentPaths.some((dir) => id.startsWith(`${dir}/`) || id.startsWith(`${dir}\\`));
1189
+ },
1190
+ async transform(code, id) {
1191
+ const templateMatch = code.match(/<template>([\s\S]*?)<\/template>/);
1192
+ if (!templateMatch)
1193
+ return;
1194
+ const s = new MagicString__default(code);
1195
+ const templateStart = code.indexOf("<template>") + "<template>".length;
1196
+ const templateEnd = code.indexOf("</template>");
1197
+ let template = code.slice(templateStart, templateEnd);
1198
+ let hasChanges = false;
1199
+ if (options.emojiSet && emojiUtils_js.RE_MATCH_EMOJIS.test(template)) {
1200
+ if (!emojiIcons) {
1201
+ emojiIcons = await import(`@iconify-json/${options.emojiSet}/icons.json`, { with: { type: 'json' } }).then((m) => m.default).catch(() => null);
1202
+ }
1203
+ if (emojiIcons) {
1204
+ emojiUtils_js.RE_MATCH_EMOJIS.lastIndex = 0;
1205
+ template = template.replace(/>([^<]*)</g, (fullMatch, textContent) => {
1206
+ if (!textContent)
1207
+ return fullMatch;
1208
+ emojiUtils_js.RE_MATCH_EMOJIS.lastIndex = 0;
1209
+ const emojiMatches = [...textContent.matchAll(emojiUtils_js.RE_MATCH_EMOJIS)];
1210
+ if (!emojiMatches.length)
1211
+ return fullMatch;
1212
+ let newTextContent = textContent;
1213
+ for (const match of emojiMatches) {
1214
+ const emoji = match[0];
1215
+ const svg = buildEmojiSvg(emoji, emojiIcons, options.emojiSet);
1216
+ if (svg) {
1217
+ hasChanges = true;
1218
+ const escaped = emoji.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1219
+ newTextContent = newTextContent.replace(new RegExp(escaped, "g"), svg);
1220
+ }
1221
+ }
1222
+ return `>${newTextContent}<`;
1223
+ });
1224
+ }
1225
+ }
1226
+ const iconRegex = /<(Icon|UIcon)\s+([^>]*name="([^"]+)"[^>]*)\/?>/g;
1227
+ if (iconRegex.test(template)) {
1228
+ iconRegex.lastIndex = 0;
1229
+ const prefixes = /* @__PURE__ */ new Set();
1230
+ let match;
1231
+ while ((match = iconRegex.exec(template)) !== null) {
1232
+ const iconName = match[3];
1233
+ if (!iconName || iconName.includes("{"))
1234
+ continue;
1235
+ const [prefix] = iconName.split(":");
1236
+ if (prefix)
1237
+ prefixes.add(prefix);
1238
+ }
1239
+ const iconSets = /* @__PURE__ */ new Map();
1240
+ for (const prefix of prefixes) {
1241
+ const icons = await loadIconSet(prefix);
1242
+ if (icons)
1243
+ iconSets.set(prefix, icons);
1244
+ }
1245
+ if (iconSets.size > 0) {
1246
+ iconRegex.lastIndex = 0;
1247
+ template = template.replace(iconRegex, (fullMatch, _tag, attrs, iconName) => {
1248
+ if (!iconName || iconName.includes("{"))
1249
+ return fullMatch;
1250
+ const [prefix, name] = iconName.split(":");
1251
+ if (!prefix || !name)
1252
+ return fullMatch;
1253
+ const icons = iconSets.get(prefix);
1254
+ if (!icons)
1255
+ return fullMatch;
1256
+ const iconData = icons.icons?.[name];
1257
+ if (!iconData)
1258
+ return fullMatch;
1259
+ hasChanges = true;
1260
+ return buildIconSvg(iconData, icons.width || 24, icons.height || 24, attrs);
1261
+ });
1262
+ }
1263
+ }
1264
+ if (options.initTw4)
1265
+ await options.initTw4();
1266
+ if (options.tw4StyleMap && Object.keys(options.tw4StyleMap).length > 0) {
1267
+ try {
1268
+ const styleMap = options.tw4StyleMap;
1269
+ const fullCode = code.slice(0, templateStart) + template + code.slice(templateEnd);
1270
+ const hasGradientClasses = options.tw4CssPath && (template.includes("bg-gradient") || template.includes("bg-radial") || template.includes("from-") || template.includes("to-") || template.includes("via-"));
1271
+ const result = await transformVueTemplate(fullCode, {
1272
+ resolveStyles: async (classes) => {
1273
+ const supported = classes.filter((cls) => !isSatoriUnsupported(cls));
1274
+ const unsupported = classes.filter((cls) => isSatoriUnsupported(cls));
1275
+ if (unsupported.length > 0) {
1276
+ const componentName = id.split("/").pop();
1277
+ console.warn(`[nuxt-og-image] ${componentName}: Filtered unsupported Satori classes: ${unsupported.join(", ")}`);
1278
+ }
1279
+ if (hasGradientClasses && options.tw4CssPath) {
1280
+ const hasElementGradient = supported.some(
1281
+ (c) => c.startsWith("bg-gradient") || c.startsWith("bg-radial") || c.startsWith("from-") || c.startsWith("to-") || c.startsWith("via-")
1282
+ );
1283
+ if (hasElementGradient) {
1284
+ const nuxtUiColors = await options.loadNuxtUiColors?.();
1285
+ return resolveClassesToStyles(supported, {
1286
+ cssPath: options.tw4CssPath,
1287
+ nuxtUiColors
1288
+ });
1289
+ }
1290
+ }
1291
+ const resolved = {};
1292
+ for (const cls of supported) {
1293
+ const baseClass = cls.replace(/^(sm|md|lg|xl|2xl):/, "");
1294
+ if (styleMap[baseClass]) {
1295
+ resolved[cls] = styleMap[baseClass];
1296
+ } else if (styleMap[cls]) {
1297
+ resolved[cls] = styleMap[cls];
1298
+ }
1299
+ }
1300
+ return resolved;
1301
+ }
1302
+ });
1303
+ if (result) {
1304
+ const newTemplateStart = result.code.indexOf("<template>") + "<template>".length;
1305
+ const newTemplateEnd = result.code.indexOf("</template>");
1306
+ template = result.code.slice(newTemplateStart, newTemplateEnd);
1307
+ hasChanges = true;
1308
+ }
1309
+ } catch (err) {
1310
+ const componentName = id.split("/").pop();
1311
+ console.warn(`[nuxt-og-image] ${componentName}: TW4 template transform failed, using original template`, err.message);
1312
+ }
1313
+ }
1314
+ if (!template.includes("data-no-inline")) {
1315
+ const imgRegex = /(?<!:)src="((?:\/|~\/|@\/)[^"]+\.(png|jpg|jpeg|gif|webp|svg))"/g;
1316
+ if (imgRegex.test(template)) {
1317
+ imgRegex.lastIndex = 0;
1318
+ const componentDir = pathe.dirname(id);
1319
+ const replacements = [];
1320
+ let match;
1321
+ while ((match = imgRegex.exec(template)) !== null) {
1322
+ const [fullMatch, srcPath, ext] = match;
1323
+ if (!srcPath || !ext)
1324
+ continue;
1325
+ if (srcPath.startsWith("data:") || srcPath.startsWith("http"))
1326
+ continue;
1327
+ let resolvedPath;
1328
+ if (srcPath.startsWith("/")) {
1329
+ resolvedPath = pathe.join(options.publicDir, srcPath);
1330
+ } else if (srcPath.startsWith("~/") || srcPath.startsWith("@/")) {
1331
+ resolvedPath = pathe.join(options.srcDir, srcPath.slice(2));
1332
+ } else if (!pathe.isAbsolute(srcPath)) {
1333
+ resolvedPath = pathe.resolve(componentDir, srcPath);
1334
+ } else {
1335
+ continue;
1336
+ }
1337
+ const fileBuffer = await promises.readFile(resolvedPath).catch(() => null);
1338
+ if (!fileBuffer)
1339
+ continue;
1340
+ const mimeType = getMimeType(ext);
1341
+ let dataUri;
1342
+ if (ext === "svg") {
1343
+ const svgContent = fileBuffer.toString("utf-8");
1344
+ dataUri = `data:${mimeType},${encodeURIComponent(svgContent)}`;
1345
+ } else {
1346
+ const base64 = fileBuffer.toString("base64");
1347
+ dataUri = `data:${mimeType};base64,${base64}`;
1348
+ }
1349
+ replacements.push({ from: fullMatch, to: `src="${dataUri}"` });
1350
+ hasChanges = true;
1351
+ }
1352
+ for (const { from, to } of replacements) {
1353
+ template = template.replace(from, to);
1354
+ }
1355
+ }
1356
+ }
1357
+ if (!hasChanges)
1358
+ return;
1359
+ s.overwrite(templateStart, templateEnd, template);
1360
+ return {
1361
+ code: s.toString(),
1362
+ map: s.generateMap({ hires: true })
1363
+ };
1364
+ }
1365
+ };
1366
+ });
1367
+
1368
+ async function getNuxtModuleOptions(module, nuxt = kit.useNuxt()) {
1369
+ const moduleMeta = ({ name: module } ) || {};
1370
+ const { nuxtModule } = await kit.loadNuxtModuleInstance(module, nuxt);
1371
+ let moduleEntry;
1372
+ for (const m of nuxt.options.modules) {
1373
+ if (Array.isArray(m) && m.length >= 2) {
1374
+ const _module = m[0];
1375
+ const _moduleEntryName = typeof _module === "string" ? _module : (await _module.getMeta?.())?.name || "";
1376
+ if (_moduleEntryName === moduleMeta.name)
1377
+ moduleEntry = m;
1378
+ }
1379
+ }
1380
+ let inlineOptions = {};
1381
+ if (moduleEntry)
1382
+ inlineOptions = moduleEntry[1];
1383
+ if (nuxtModule.getOptions)
1384
+ return nuxtModule.getOptions(inlineOptions, nuxt);
1385
+ return inlineOptions;
1386
+ }
1387
+ function isNuxtGenerate(nuxt = kit.useNuxt()) {
1388
+ return nuxt.options.nitro.static || nuxt.options.nitro.preset === "static";
1389
+ }
1390
+
1391
+ function parseFontString(font) {
1392
+ const parts = font.split(":");
1393
+ if (parts.length === 3) {
1394
+ return {
1395
+ name: parts[0] || font,
1396
+ style: parts[1] === "ital" ? "italic" : "normal",
1397
+ weight: Number.parseInt(parts[2] || "") || 400
1398
+ };
1399
+ }
1400
+ return {
1401
+ name: parts[0] || font,
1402
+ weight: Number.parseInt(parts[1] || "") || 400,
1403
+ style: "normal"
1404
+ };
1405
+ }
1406
+ function groupFontsByFamily(fonts) {
1407
+ const families = /* @__PURE__ */ new Map();
1408
+ for (const font of fonts) {
1409
+ const parsed = parseFontString(font);
1410
+ const existing = families.get(parsed.name);
1411
+ if (existing) {
1412
+ existing.weights.add(parsed.weight);
1413
+ existing.styles.add(parsed.style);
1414
+ } else {
1415
+ families.set(parsed.name, {
1416
+ weights: /* @__PURE__ */ new Set([parsed.weight]),
1417
+ styles: /* @__PURE__ */ new Set([parsed.style])
1418
+ });
1419
+ }
1420
+ }
1421
+ return Array.from(families.entries()).map(([name, { weights, styles }]) => ({
1422
+ name,
1423
+ weights: Array.from(weights).sort((a, b) => a - b),
1424
+ styles: Array.from(styles)
1425
+ }));
1426
+ }
1427
+ async function migrateFontsConfig(rootDir) {
1428
+ const configPaths = [
1429
+ "nuxt.config.ts",
1430
+ "nuxt.config.js",
1431
+ "nuxt.config.mjs"
1432
+ ];
1433
+ let configPath;
1434
+ for (const p of configPaths) {
1435
+ const fullPath = pathe.join(rootDir, p);
1436
+ if (fs.existsSync(fullPath)) {
1437
+ configPath = fullPath;
1438
+ break;
1439
+ }
1440
+ }
1441
+ if (!configPath) {
1442
+ return { migrated: false, message: "No nuxt.config found" };
1443
+ }
1444
+ const mod = await magicast.loadFile(configPath);
1445
+ const config = mod.exports.default;
1446
+ if (!config?.ogImage?.fonts) {
1447
+ return { migrated: false, message: "No ogImage.fonts config found" };
1448
+ }
1449
+ const oldFonts = config.ogImage.fonts;
1450
+ if (!Array.isArray(oldFonts) || oldFonts.length === 0) {
1451
+ return { migrated: false, message: "ogImage.fonts is empty or invalid" };
1452
+ }
1453
+ const groupedFonts = groupFontsByFamily(oldFonts);
1454
+ if (!config.fonts) {
1455
+ config.fonts = {};
1456
+ }
1457
+ if (!config.fonts.families) {
1458
+ config.fonts.families = [];
1459
+ }
1460
+ const existingFamilies = config.fonts.families;
1461
+ for (const font of groupedFonts) {
1462
+ const existing = existingFamilies.find((f) => f.name === font.name);
1463
+ if (existing) {
1464
+ const existingWeights = new Set(existing.weights || []);
1465
+ for (const w of font.weights) {
1466
+ existingWeights.add(w);
1467
+ }
1468
+ existing.weights = Array.from(existingWeights).sort((a, b) => a - b);
1469
+ if (font.styles.includes("italic")) {
1470
+ const existingStyles = new Set(existing.styles || ["normal"]);
1471
+ existingStyles.add("italic");
1472
+ existing.styles = Array.from(existingStyles);
1473
+ }
1474
+ existing.global = true;
1475
+ } else {
1476
+ const family = {
1477
+ name: font.name,
1478
+ weights: font.weights,
1479
+ global: true
1480
+ };
1481
+ if (font.styles.includes("italic")) {
1482
+ family.styles = font.styles;
1483
+ }
1484
+ existingFamilies.push(family);
1485
+ }
1486
+ }
1487
+ helpers.addNuxtModule(mod, "@nuxt/fonts");
1488
+ delete config.ogImage.fonts;
1489
+ if (Object.keys(config.ogImage).length === 0) {
1490
+ delete config.ogImage;
1491
+ }
1492
+ await magicast.writeFile(mod, configPath);
1493
+ const familyNames = groupedFonts.map((f) => f.name).join(", ");
1494
+ return {
1495
+ migrated: true,
1496
+ message: `Migrated fonts (${familyNames}) from ogImage.fonts to @nuxt/fonts`
1497
+ };
1498
+ }
1499
+ async function promptFontsMigration(rootDir) {
1500
+ logger_js.logger.info("");
1501
+ logger_js.logger.info("Detected deprecated ogImage.fonts configuration.");
1502
+ logger_js.logger.info("");
1503
+ logger_js.logger.info("This config has been replaced with @nuxt/fonts integration.");
1504
+ logger_js.logger.info("");
1505
+ logger_js.logger.info("Before:");
1506
+ logger_js.logger.info(" ogImage: {");
1507
+ logger_js.logger.info(" fonts: ['Inter:400', 'Inter:700']");
1508
+ logger_js.logger.info(" }");
1509
+ logger_js.logger.info("");
1510
+ logger_js.logger.info("After:");
1511
+ logger_js.logger.info(" modules: ['@nuxt/fonts', 'nuxt-og-image'],");
1512
+ logger_js.logger.info(" fonts: {");
1513
+ logger_js.logger.info(" families: [");
1514
+ logger_js.logger.info(" { name: 'Inter', weights: [400, 700], global: true }");
1515
+ logger_js.logger.info(" ]");
1516
+ logger_js.logger.info(" }");
1517
+ logger_js.logger.info("");
1518
+ const migrate = await logger_js.logger.prompt("Automatically migrate nuxt.config?", {
1519
+ type: "confirm",
1520
+ initial: true
1521
+ });
1522
+ if (migrate) {
1523
+ const result = await migrateFontsConfig(rootDir).catch((err) => {
1524
+ logger_js.logger.error(`Migration failed: ${err.message}`);
1525
+ return { migrated: false, message: err.message };
1526
+ });
1527
+ if (result.migrated) {
1528
+ logger_js.logger.success(result.message);
1529
+ logger_js.logger.info("");
1530
+ logger_js.logger.info("Please install @nuxt/fonts:");
1531
+ logger_js.logger.info(" npm add @nuxt/fonts");
1532
+ } else {
1533
+ logger_js.logger.warn(`Could not migrate: ${result.message}`);
1534
+ logger_js.logger.info("Please migrate manually.");
1535
+ }
1536
+ } else {
1537
+ logger_js.logger.info("Skipping automatic migration. Please migrate manually.");
1538
+ }
1539
+ }
1540
+
1541
+ const PROVIDER_DEPENDENCIES = [
1542
+ {
1543
+ name: "satori",
1544
+ description: "SVG-based renderer using Satori (default)",
1545
+ bindings: {
1546
+ "node": [
1547
+ { name: "satori", description: "HTML to SVG renderer" },
1548
+ { name: "@resvg/resvg-js", description: "SVG to PNG converter (native)" }
1549
+ ],
1550
+ "wasm": [
1551
+ { name: "satori", description: "HTML to SVG renderer" },
1552
+ { name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
1553
+ ],
1554
+ "wasm-fs": [
1555
+ { name: "satori", description: "HTML to SVG renderer" },
1556
+ { name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
1557
+ ]
1558
+ }
1559
+ },
1560
+ {
1561
+ name: "takumi",
1562
+ description: "Rust-based high-performance renderer",
1563
+ bindings: {
1564
+ "node": [
1565
+ { name: "@takumi-rs/core", description: "Native Takumi renderer" }
1566
+ ],
1567
+ "wasm": [
1568
+ { name: "@takumi-rs/wasm", description: "WASM Takumi renderer" }
1569
+ ],
1570
+ "wasm-fs": [
1571
+ { name: "@takumi-rs/wasm", description: "WASM Takumi renderer" }
1572
+ ]
1573
+ }
1574
+ },
1575
+ {
1576
+ name: "chromium",
1577
+ description: "Browser-based screenshot renderer",
1578
+ bindings: {
1579
+ node: [
1580
+ { name: "playwright-core", description: "Headless browser automation" }
1581
+ ]
1582
+ }
1583
+ }
1584
+ ];
1585
+ async function isPackageInstalled(packageName) {
1586
+ try {
1587
+ await import(packageName);
1588
+ return true;
1589
+ } catch {
1590
+ return false;
1591
+ }
1592
+ }
1593
+ async function getInstalledProviders() {
1594
+ const installed = [];
1595
+ for (const provider of PROVIDER_DEPENDENCIES) {
1596
+ if (provider.bindings.node) {
1597
+ const allNodeInstalled = await Promise.all(
1598
+ provider.bindings.node.map((dep) => isPackageInstalled(dep.name))
1599
+ );
1600
+ if (allNodeInstalled.every(Boolean)) {
1601
+ installed.push({ provider: provider.name, binding: "node" });
1602
+ continue;
1603
+ }
1604
+ }
1605
+ if (provider.bindings.wasm) {
1606
+ const allWasmInstalled = await Promise.all(
1607
+ provider.bindings.wasm.map((dep) => isPackageInstalled(dep.name))
1608
+ );
1609
+ if (allWasmInstalled.every(Boolean)) {
1610
+ installed.push({ provider: provider.name, binding: "wasm" });
1611
+ }
1612
+ }
1613
+ }
1614
+ return installed;
1615
+ }
1616
+ async function getMissingDependencies(provider, binding = "node") {
1617
+ const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
1618
+ if (!providerDef)
1619
+ return [];
1620
+ const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
1621
+ const missing = [];
1622
+ for (const dep of deps) {
1623
+ if (!await isPackageInstalled(dep.name))
1624
+ missing.push(dep.name);
1625
+ }
1626
+ return missing;
1627
+ }
1628
+ function getProviderDependencies(provider, binding = "node") {
1629
+ const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
1630
+ if (!providerDef)
1631
+ return [];
1632
+ const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
1633
+ return deps.map((d) => d.name);
1634
+ }
1635
+ function getRecommendedBindingFromPreset(provider) {
1636
+ const preset = resolveNitroPreset();
1637
+ const compatibility = getPresetNitroPresetCompatibility(preset);
1638
+ return getRecommendedBinding(provider, compatibility);
1639
+ }
1640
+ function getRecommendedBinding(provider, compatibility) {
1641
+ if (provider === "satori") {
1642
+ const resvgBinding = compatibility.resvg;
1643
+ if (resvgBinding === "wasm-fs")
1644
+ return "wasm-fs";
1645
+ if (resvgBinding === "wasm")
1646
+ return "wasm";
1647
+ return "node";
1648
+ }
1649
+ if (provider === "takumi") {
1650
+ const takumiBinding = compatibility.takumi;
1651
+ if (takumiBinding === "wasm")
1652
+ return "wasm";
1653
+ return "node";
1654
+ }
1655
+ if (provider === "chromium") {
1656
+ return "node";
1657
+ }
1658
+ return "node";
1659
+ }
1660
+ async function ensureProviderDependencies(provider, binding, nuxt) {
1661
+ const missing = await getMissingDependencies(provider, binding);
1662
+ if (missing.length === 0)
1663
+ return { success: true, installed: [] };
1664
+ const pm = await nypm.detectPackageManager(nuxt.options.rootDir);
1665
+ const pmName = pm?.name || "npm";
1666
+ logger_js.logger.info(`Installing ${provider} dependencies: ${missing.join(", ")}`);
1667
+ const installed = [];
1668
+ for (const pkg of missing) {
1669
+ const success = await nypm.addDependency(pkg, {
1670
+ cwd: nuxt.options.rootDir,
1671
+ dev: false
1672
+ }).then(() => {
1673
+ installed.push(pkg);
1674
+ return true;
1675
+ }).catch(() => {
1676
+ logger_js.logger.error(`Failed to install ${pkg}. Run manually: ${pmName} add ${pkg}`);
1677
+ return false;
1678
+ });
1679
+ if (!success)
1680
+ return { success: false, installed };
1681
+ }
1682
+ return { success: true, installed };
1683
+ }
1684
+ async function validateProviderSetup(renderer, compatibility) {
1685
+ const issues = [];
1686
+ const binding = getRecommendedBinding(renderer, compatibility);
1687
+ const missing = await getMissingDependencies(renderer, binding);
1688
+ if (missing.length > 0) {
1689
+ issues.push(`Missing ${renderer} dependencies: ${missing.join(", ")}`);
1690
+ }
1691
+ if (renderer === "satori") {
1692
+ const hasResvgNode = await isPackageInstalled("@resvg/resvg-js");
1693
+ const hasResvgWasm = await isPackageInstalled("@resvg/resvg-wasm");
1694
+ if (!hasResvgNode && !hasResvgWasm) {
1695
+ issues.push("Satori requires either @resvg/resvg-js (node) or @resvg/resvg-wasm to render PNGs");
1696
+ }
1697
+ }
1698
+ if (renderer === "chromium") {
1699
+ const hasPlaywright = await isPackageInstalled("playwright-core");
1700
+ if (!hasPlaywright && binding === "node") {
1701
+ issues.push("Chromium renderer requires playwright-core");
1702
+ }
1703
+ }
1704
+ return { valid: issues.length === 0, issues };
1705
+ }
1706
+
1707
+ const DEPRECATED_CONFIG = {
1708
+ playground: "Removed - use Nuxt DevTools",
1709
+ host: "Use site.url or NUXT_SITE_URL",
1710
+ siteUrl: "Use site.url or NUXT_SITE_URL",
1711
+ runtimeBrowser: "Use compatibility.runtime.chromium",
1712
+ runtimeSatori: "Use compatibility.runtime.satori",
1713
+ cacheTtl: "Use cacheMaxAgeSeconds",
1714
+ cache: "Use cacheMaxAgeSeconds",
1715
+ cacheKey: "Removed",
1716
+ static: "Removed - use zeroRuntime",
1717
+ fonts: "Use @nuxt/fonts module instead"
1718
+ };
1719
+ const DEPRECATED_COMPOSABLES = [
1720
+ "defineOgImageStatic",
1721
+ "defineOgImageDynamic",
1722
+ "defineOgImageCached",
1723
+ "defineOgImageWithoutCache",
1724
+ "OgImageStatic",
1725
+ "OgImageDynamic",
1726
+ "OgImageCached",
1727
+ "OgImageWithoutCache"
1728
+ ];
1729
+ async function onInstall(nuxt) {
1730
+ if (process.env.NUXT_OG_IMAGE_SKIP_ONBOARDING === "1") {
1731
+ logger_js.logger.info("Skipping onboarding (NUXT_OG_IMAGE_SKIP_ONBOARDING=1)");
1732
+ return;
1733
+ }
1734
+ if (nuxt.options._prepare)
1735
+ return;
1736
+ if (stdEnv.isCI) {
1737
+ const installedProviders2 = await getInstalledProviders();
1738
+ if (installedProviders2.length === 0) {
1739
+ throw new Error(
1740
+ "[nuxt-og-image] No OG image provider dependencies found. Install a provider before running in CI:\n npm add @resvg/resvg-js satori yoga-wasm-web # for satori\n npm add @playwright/core # for chromium\nSee: https://nuxtseo.com/og-image/getting-started"
1741
+ );
1742
+ }
1743
+ return;
1744
+ }
1745
+ logger_js.logger.info("");
1746
+ logger_js.logger.info("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
1747
+ logger_js.logger.info("\u2502 nuxt-og-image \u2502");
1748
+ logger_js.logger.info("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
1749
+ logger_js.logger.info("");
1750
+ const state = {};
1751
+ const installedProviders = await getInstalledProviders();
1752
+ if (installedProviders.length > 0) {
1753
+ logger_js.logger.info("Detected installed providers:");
1754
+ for (const { provider, binding } of installedProviders) {
1755
+ logger_js.logger.info(` - ${provider} (${binding})`);
1756
+ }
1757
+ const useExisting = await logger_js.logger.prompt("Use detected providers?", {
1758
+ type: "confirm",
1759
+ initial: true
1760
+ });
1761
+ if (useExisting && installedProviders[0]) {
1762
+ state.selectedRenderer = installedProviders[0].provider;
1763
+ state.selectedBinding = installedProviders[0].binding;
1764
+ }
1765
+ }
1766
+ if (!state.selectedRenderer) {
1767
+ const rendererChoice = await logger_js.logger.prompt("Select a renderer:", {
1768
+ type: "select",
1769
+ options: PROVIDER_DEPENDENCIES.map((p) => ({
1770
+ label: p.name,
1771
+ value: p.name,
1772
+ hint: p.description
1773
+ }))
1774
+ });
1775
+ if (typeof rendererChoice === "symbol") {
1776
+ logger_js.logger.warn("Onboarding cancelled");
1777
+ return;
1778
+ }
1779
+ state.selectedRenderer = rendererChoice;
1780
+ }
1781
+ if (!state.selectedBinding) {
1782
+ const recommendedBinding = getRecommendedBindingFromPreset(state.selectedRenderer);
1783
+ const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === state.selectedRenderer);
1784
+ const hasNodeBinding = !!providerDef?.bindings.node;
1785
+ const hasWasmBinding = !!providerDef?.bindings.wasm || !!providerDef?.bindings["wasm-fs"];
1786
+ if (hasNodeBinding && hasWasmBinding) {
1787
+ const options = [];
1788
+ if (recommendedBinding === "node" || recommendedBinding === "wasm-fs") {
1789
+ options.push({ label: "node (Recommended)", value: "node", hint: "Native bindings (faster)" });
1790
+ options.push({ label: "wasm", value: recommendedBinding === "wasm-fs" ? "wasm-fs" : "wasm", hint: "WASM bindings (portable)" });
1791
+ } else {
1792
+ options.push({ label: "wasm (Recommended)", value: "wasm", hint: "WASM bindings (required for your platform)" });
1793
+ options.push({ label: "node", value: "node", hint: "Native bindings (may not work on edge)" });
1794
+ }
1795
+ const bindingChoice = await logger_js.logger.prompt("Select binding type:", {
1796
+ type: "select",
1797
+ options
1798
+ });
1799
+ if (typeof bindingChoice === "symbol") {
1800
+ logger_js.logger.warn("Onboarding cancelled");
1801
+ return;
1802
+ }
1803
+ state.selectedBinding = bindingChoice;
1804
+ } else {
1805
+ state.selectedBinding = hasNodeBinding ? "node" : "wasm";
1806
+ }
1807
+ }
1808
+ const missing = await getMissingDependencies(state.selectedRenderer, state.selectedBinding);
1809
+ if (missing.length > 0) {
1810
+ logger_js.logger.info(`Required dependencies for ${state.selectedRenderer} (${state.selectedBinding}):`);
1811
+ for (const pkg of missing) {
1812
+ logger_js.logger.info(` - ${pkg}`);
1813
+ }
1814
+ const installDeps = await logger_js.logger.prompt("Install missing dependencies?", {
1815
+ type: "confirm",
1816
+ initial: true
1817
+ });
1818
+ if (installDeps) {
1819
+ const result = await ensureProviderDependencies(
1820
+ state.selectedRenderer,
1821
+ state.selectedBinding,
1822
+ nuxt
1823
+ );
1824
+ if (result.success) {
1825
+ logger_js.logger.success(`Installed: ${result.installed.join(", ")}`);
1826
+ } else {
1827
+ logger_js.logger.error("Failed to install some dependencies. Install manually:");
1828
+ const allDeps = getProviderDependencies(state.selectedRenderer, state.selectedBinding);
1829
+ logger_js.logger.info(` npm add ${allDeps.join(" ")}`);
1830
+ }
1831
+ } else {
1832
+ const allDeps = getProviderDependencies(state.selectedRenderer, state.selectedBinding);
1833
+ logger_js.logger.warn("Dependencies required. Install manually:");
1834
+ logger_js.logger.info(` npm add ${allDeps.join(" ")}`);
1835
+ }
1836
+ } else {
1837
+ logger_js.logger.success(`All ${state.selectedRenderer} dependencies installed`);
1838
+ }
1839
+ const createComponent = await logger_js.logger.prompt("Create starter OgImage component?", {
1840
+ type: "confirm",
1841
+ initial: true
1842
+ });
1843
+ if (createComponent) {
1844
+ await createStarterComponent(nuxt);
1845
+ }
1846
+ const hasSharp = await isPackageInstalled("sharp");
1847
+ if (!hasSharp && state.selectedRenderer === "satori") {
1848
+ const wantJpeg = await logger_js.logger.prompt("Install sharp for JPEG output support?", {
1849
+ type: "confirm",
1850
+ initial: false
1851
+ });
1852
+ if (wantJpeg) {
1853
+ await ensureProviderDependencies("satori", "node", nuxt).catch(() => logger_js.logger.warn("Failed to install sharp, JPEG output unavailable"));
1854
+ }
1855
+ }
1856
+ logger_js.logger.info("");
1857
+ logger_js.logger.info("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
1858
+ logger_js.logger.info("\u2502 Setup Complete \u2502");
1859
+ logger_js.logger.info("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
1860
+ logger_js.logger.info(`${`\u2502 Renderer: ${state.selectedRenderer} (${state.selectedBinding})`.padEnd(42)}\u2502`);
1861
+ logger_js.logger.info("\u2502 \u2502");
1862
+ logger_js.logger.info("\u2502 Next steps: \u2502");
1863
+ logger_js.logger.info("\u2502 1. Add site.url or NUXT_SITE_URL \u2502");
1864
+ logger_js.logger.info("\u2502 2. Open DevTools \u2192 OG Image \u2502");
1865
+ logger_js.logger.info("\u2502 \u2502");
1866
+ logger_js.logger.info("\u2502 https://nuxtseo.com/og-image \u2502");
1867
+ logger_js.logger.info("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
1868
+ }
1869
+ async function onUpgrade(nuxt, _options, previousVersion) {
1870
+ logger_js.logger.info("");
1871
+ logger_js.logger.info("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
1872
+ logger_js.logger.info("\u2502 nuxt-og-image upgraded \u2502");
1873
+ logger_js.logger.info(`${`\u2502 ${previousVersion} \u2192 current`.padEnd(42)}\u2502`);
1874
+ logger_js.logger.info("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
1875
+ const issues = [];
1876
+ const ogImageConfig = nuxt.options.ogImage;
1877
+ if (ogImageConfig) {
1878
+ for (const [key, replacement] of Object.entries(DEPRECATED_CONFIG)) {
1879
+ if (key in ogImageConfig) {
1880
+ issues.push(`Config \`${key}\` is deprecated: ${replacement}`);
1881
+ }
1882
+ }
1883
+ if ("fonts" in ogImageConfig && !nuxt.options._prepare && !stdEnv.isCI) {
1884
+ await promptFontsMigration(nuxt.options.rootDir);
1885
+ }
1886
+ }
1887
+ if (issues.length > 0) {
1888
+ logger_js.logger.warn("Deprecated configuration detected:");
1889
+ for (const issue of issues) {
1890
+ logger_js.logger.warn(` - ${issue}`);
1891
+ }
1892
+ }
1893
+ logger_js.logger.info("Check your code for deprecated composables:");
1894
+ for (const composable of DEPRECATED_COMPOSABLES) {
1895
+ logger_js.logger.info(` - ${composable}`);
1896
+ }
1897
+ const renderer = ogImageConfig?.defaults?.renderer || "satori";
1898
+ const preset = resolveNitroPreset();
1899
+ const compatibility = getPresetNitroPresetCompatibility(preset);
1900
+ const validation = await validateProviderSetup(renderer, compatibility);
1901
+ if (!validation.valid) {
1902
+ logger_js.logger.warn("Provider setup issues:");
1903
+ for (const issue of validation.issues) {
1904
+ logger_js.logger.warn(` - ${issue}`);
1905
+ }
1906
+ }
1907
+ logger_js.logger.info("Migration guide: https://nuxtseo.com/og-image/migration-guide");
1908
+ }
1909
+ async function createStarterComponent(nuxt) {
1910
+ const componentDir = pathe.join(nuxt.options.srcDir, "components", "OgImage");
1911
+ const componentPath = pathe.join(componentDir, "Default.vue");
1912
+ if (fs.existsSync(componentPath)) {
1913
+ logger_js.logger.info("OgImage/Default.vue already exists, skipping");
1914
+ return;
1915
+ }
1916
+ const template = `<script setup lang="ts">
1917
+ defineProps<{
1918
+ title: string
1919
+ description?: string
1920
+ }>()
1921
+ <\/script>
1922
+
1923
+ <template>
1924
+ <div class="w-full h-full flex flex-col justify-center items-center bg-gradient-to-br from-green-400 to-blue-500 text-white p-16">
1925
+ <h1 class="text-6xl font-bold mb-4">{{ title }}</h1>
1926
+ <p v-if="description" class="text-2xl opacity-80">{{ description }}</p>
1927
+ </div>
1928
+ </template>
1929
+ `;
1930
+ if (!fs.existsSync(componentDir))
1931
+ await promises.mkdir(componentDir, { recursive: true }).catch(() => {
1932
+ });
1933
+ await promises.writeFile(componentPath, template).then(() => logger_js.logger.success("Created components/OgImage/Default.vue")).catch(() => logger_js.logger.error("Failed to create starter component"));
1934
+ }
1935
+
1936
+ function registerTypeTemplates(ctx) {
1937
+ const { nuxt, config, componentCtx } = ctx;
1938
+ kit.addTypeTemplate({
1939
+ filename: "module/nuxt-og-image-components.d.ts",
1940
+ getContents: () => {
1941
+ const componentImports = componentCtx.components.map((component) => {
1942
+ const relativeComponentPath = pathe.relative(
1943
+ pathe.resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"),
1944
+ component.path
1945
+ );
1946
+ const name = config.componentDirs.sort((a, b) => b.length - a.length).reduce((n, dir) => n.replace(new RegExp(`^${dir}`), ""), component.pascalName);
1947
+ return ` '${name}': typeof import('${relativeComponentPath}')['default']`;
1948
+ }).join("\n");
1949
+ return `declare module '#og-image/components' {
1950
+ export interface OgImageComponents {
1951
+ ${componentImports}
1952
+ }
1953
+ }
1954
+ `;
1955
+ }
1956
+ }, { nuxt: true });
1957
+ kit.addTemplate({
1958
+ filename: "types/og-image-virtual.d.ts",
1959
+ getContents: (data) => {
1960
+ const typesPath = pathe.relative(pathe.resolve(data.nuxt.options.rootDir, data.nuxt.options.buildDir, "types"), pathe.resolve("runtime/types"));
1961
+ return `declare module '#og-image-virtual/public-assets.mjs' {
1962
+ import type { H3Event } from 'h3'
1963
+ import type { FontConfig } from '${typesPath}'
1964
+ export function resolve(event: H3Event, font: FontConfig): Promise<BufferSource>
1965
+ }
1966
+
1967
+ declare module '#og-image/fonts' {
1968
+ import type { FontConfig } from '${typesPath}'
1969
+ const fonts: FontConfig[]
1970
+ export default fonts
1971
+ }
1972
+
1973
+ declare module '#og-image-virtual/unocss-config.mjs' {
1974
+ export const theme: Record<string, any>
1975
+ }
1976
+
1977
+ declare module '#og-image-virtual/iconify-json-icons.mjs' {
1978
+ export const icons: Record<string, { body: string, width?: number, height?: number }>
1979
+ export const width: number
1980
+ export const height: number
1981
+ }
1982
+
1983
+ declare module '#og-image-virtual/component-names.mjs' {
1984
+ import type { OgImageComponent } from '${typesPath}'
1985
+ export const componentNames: OgImageComponent[]
1986
+ }
1987
+
1988
+ declare module '#og-image-virtual/build-dir.mjs' {
1989
+ export const buildDir: string
1990
+ }
1991
+
1992
+ declare module '#og-image/compatibility' {
1993
+ import type { RuntimeCompatibilitySchema } from '${typesPath}'
1994
+ const compatibility: Partial<Omit<RuntimeCompatibilitySchema, 'wasm'>>
1995
+ export default compatibility
1996
+ }
1997
+ declare module '#og-image-virtual/tw4-theme.mjs' {
1998
+ export const tw4FontVars: Record<string, string>
1999
+ export const tw4Breakpoints: Record<string, number>
2000
+ export const tw4Colors: Record<string, string | Record<string, string>>
2001
+ }
2002
+ `;
2003
+ }
2004
+ });
2005
+ kit.addTemplate({
2006
+ filename: "types/og-image-bindings.d.ts",
2007
+ getContents: (data) => {
2008
+ const typesPath = pathe.relative(pathe.resolve(data.nuxt.options.rootDir, data.nuxt.options.buildDir, "types"), pathe.resolve("runtime/types"));
2009
+ return `declare module '#og-image/bindings/chromium' {
2010
+ export function createBrowser(): Promise<any>
2011
+ }
2012
+
2013
+ declare module '#og-image/bindings/satori' {
2014
+ import satori from 'satori'
2015
+ const _default: { initWasmPromise: Promise<void>, satori: typeof satori }
2016
+ export default _default
2017
+ }
2018
+
2019
+ declare module '#og-image/bindings/resvg' {
2020
+ import { Resvg } from '@resvg/resvg-js'
2021
+ const _default: { initWasmPromise: Promise<void>, Resvg: typeof Resvg }
2022
+ export default _default
2023
+ }
2024
+
2025
+ declare module '#og-image/bindings/sharp' {
2026
+ import sharp from 'sharp'
2027
+ const _default: typeof sharp
2028
+ export default _default
2029
+ }
2030
+
2031
+ declare module '#og-image/bindings/css-inline' {
2032
+ import cssInline from '@css-inline/css-inline'
2033
+ const _default: { initWasmPromise: Promise<void>, cssInline: typeof cssInline }
2034
+ export default _default
2035
+ }
2036
+
2037
+ declare module '#og-image/bindings/takumi' {
2038
+ const _default: any
2039
+ export default _default
2040
+ }
2041
+
2042
+ declare module '#og-image/renderers/satori' {
2043
+ import type { Renderer } from '${typesPath}'
2044
+ const _default: Renderer
2045
+ export default _default
2046
+ }
2047
+
2048
+ declare module '#og-image/renderers/chromium' {
2049
+ import type { Renderer } from '${typesPath}'
2050
+ const _default: Renderer
2051
+ export default _default
2052
+ }
2053
+
2054
+ declare module '#og-image/renderers/takumi' {
2055
+ import type { Renderer } from '${typesPath}'
2056
+ const _default: Renderer
2057
+ export default _default
2058
+ }
2059
+
2060
+ declare module '#og-image/emoji-transform' {
2061
+ import type { OgImageRenderEventContext } from '${typesPath}'
2062
+ export function getEmojiSvg(ctx: OgImageRenderEventContext, emoji: string): Promise<string | null>
2063
+ }
2064
+
2065
+ declare module '#og-image-cache' {
2066
+ export { htmlPayloadCache, prerenderOptionsCache, emojiCache, fontCache } from '${typesPath.replace("/types", "")}/server/og-image/cache/lru'
2067
+ }
2068
+ `;
2069
+ }
2070
+ });
2071
+ kit.addTypeTemplate({
2072
+ filename: "types/og-image-augments.d.ts",
2073
+ getContents: (data) => {
2074
+ const typesPath = pathe.relative(pathe.resolve(data.nuxt.options.rootDir, data.nuxt.options.buildDir, "types"), pathe.resolve("runtime/types"));
2075
+ return `/// <reference path="./og-image-virtual.d.ts" />
2076
+ /// <reference path="./og-image-bindings.d.ts" />
2077
+ import type { OgImageOptions, OgImageRenderEventContext, VNode } from '${typesPath}'
2078
+
2079
+ declare module 'nitropack' {
2080
+ interface NitroRouteRules {
2081
+ ogImage?: false | OgImageOptions & Record<string, any>
2082
+ }
2083
+ interface NitroRouteConfig {
2084
+ ogImage?: false | OgImageOptions & Record<string, any>
2085
+ }
2086
+ interface NitroRuntimeHooks {
2087
+ 'nuxt-og-image:context': (ctx: OgImageRenderEventContext) => void | Promise<void>
2088
+ 'nuxt-og-image:satori:vnodes': (vnodes: VNode, ctx: OgImageRenderEventContext) => void | Promise<void>
2089
+ }
2090
+ }
2091
+
2092
+ declare module 'nitropack/types' {
2093
+ interface NitroRouteRules {
2094
+ ogImage?: false | OgImageOptions & Record<string, any>
2095
+ }
2096
+ interface NitroRouteConfig {
2097
+ ogImage?: false | OgImageOptions & Record<string, any>
2098
+ }
2099
+ interface NitroRuntimeHooks {
2100
+ 'nuxt-og-image:context': (ctx: OgImageRenderEventContext) => void | Promise<void>
2101
+ 'nuxt-og-image:satori:vnodes': (vnodes: VNode, ctx: OgImageRenderEventContext) => void | Promise<void>
2102
+ }
2103
+ }
2104
+
2105
+ export {}
2106
+ `;
2107
+ }
2108
+ });
2109
+ }
2110
+
2111
+ const IS_MODULE_DEVELOPMENT = undefined.endsWith(".ts");
2112
+ function isProviderEnabledForEnv(provider, nuxt, config) {
2113
+ return nuxt.options.dev && config.compatibility?.dev?.[provider] !== false || !nuxt.options.dev && (config.compatibility?.runtime?.[provider] !== false || config.compatibility?.prerender?.[provider] !== false);
2114
+ }
2115
+ const defaultComponentDirs = ["OgImage", "OgImageCommunity", "og-image", "OgImageTemplate"];
2116
+ const module$1 = kit.defineNuxtModule({
2117
+ meta: {
2118
+ name: "nuxt-og-image",
2119
+ compatibility: {
2120
+ nuxt: ">=3.16.0"
2121
+ },
2122
+ configKey: "ogImage"
2123
+ },
2124
+ moduleDependencies: {
2125
+ "@nuxt/content": {
2126
+ version: ">=3",
2127
+ optional: true
2128
+ },
2129
+ "@nuxt/fonts": {
2130
+ version: ">=0.13.0"
2131
+ }
2132
+ },
2133
+ defaults() {
2134
+ return {
2135
+ enabled: true,
2136
+ defaults: {
2137
+ emojis: "noto",
2138
+ component: "NuxtSeo",
2139
+ extension: "png",
2140
+ width: 1200,
2141
+ height: 600,
2142
+ // default is to cache the image for 3 day (72 hours)
2143
+ cacheMaxAgeSeconds: 60 * 60 * 24 * 3
2144
+ },
2145
+ componentDirs: defaultComponentDirs,
2146
+ runtimeCacheStorage: true,
2147
+ debug: stdEnv.isDevelopment
2148
+ };
2149
+ },
2150
+ async onInstall(nuxt) {
2151
+ await onInstall(nuxt);
2152
+ },
2153
+ async onUpgrade(nuxt, options, previousVersion) {
2154
+ await onUpgrade(nuxt, options, previousVersion);
2155
+ },
2156
+ async setup(config, nuxt) {
2157
+ const _resolver = kit.createResolver((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/nuxt-og-image.DroaQ3v-.cjs', document.baseURI).href)));
2158
+ const fixSharedPath = (p) => p.includes("/shared/runtime/") ? p.replace("/shared/runtime/", "/runtime/") : p;
2159
+ const resolve = (path) => fixSharedPath(_resolver.resolve(path));
2160
+ const resolver = {
2161
+ ..._resolver,
2162
+ resolve,
2163
+ resolvePath: async (path, opts) => fixSharedPath(await _resolver.resolvePath(path, opts))
2164
+ };
2165
+ const { version } = await pkgTypes.readPackageJSON(resolve("../package.json"));
2166
+ const userAppPkgJson = await pkgTypes.readPackageJSON(nuxt.options.rootDir).catch(() => ({ dependencies: {}, devDependencies: {} }));
2167
+ logger_js.logger.level = config.debug || nuxt.options.debug ? 4 : 3;
2168
+ if (config.enabled === false) {
2169
+ logger_js.logger.info("The module is disabled, skipping setup.");
2170
+ ["defineOgImage", "defineOgImageComponent", "defineOgImageScreenshot"].forEach((name) => {
2171
+ kit.addImports({ name, from: resolve(`./runtime/app/composables/mock`) });
2172
+ });
2173
+ return;
2174
+ }
2175
+ if (config.enabled && !nuxt.options.ssr) {
2176
+ logger_js.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 }`).");
2177
+ return;
2178
+ }
2179
+ const routeRules = nuxt.options.routeRules || {};
2180
+ const wildcardPatterns = ["/**", "/*", "*"];
2181
+ for (const pattern of wildcardPatterns) {
2182
+ const rule = routeRules[pattern];
2183
+ if (rule && (rule.swr || rule.isr || rule.cache)) {
2184
+ logger_js.logger.warn(`Wildcard route rule \`${pattern}\` with caching (swr/isr/cache) may break og-image routes.`);
2185
+ logger_js.logger.info("See: https://nuxtseo.com/og-image/guides/route-rules#wildcard-route-rules-warning");
2186
+ break;
2187
+ }
2188
+ }
2189
+ nuxt.options.alias["#og-image"] = resolve("./runtime");
2190
+ nuxt.options.alias["#og-image-cache"] = resolve("./runtime/server/og-image/cache/lru");
2191
+ const preset = resolveNitroPreset(nuxt.options.nitro);
2192
+ const targetCompatibility = getPresetNitroPresetCompatibility(preset);
2193
+ const emojiPkg = `@iconify-json/${config.defaults.emojis}`;
2194
+ let hasLocalIconify = await hasResolvableDependency(emojiPkg);
2195
+ let finalEmojiStrategy = config.emojiStrategy || "auto";
2196
+ if (!hasLocalIconify && !nuxt.options._prepare && nuxt.options.dev) {
2197
+ const shouldInstall = await logger_js.logger.prompt(`Install ${emojiPkg} for local emoji support?`, {
2198
+ type: "confirm",
2199
+ initial: true
2200
+ });
2201
+ if (shouldInstall) {
2202
+ await ensureDependencies([emojiPkg], nuxt).then(() => {
2203
+ hasLocalIconify = true;
2204
+ logger_js.logger.success(`Installed ${emojiPkg}`);
2205
+ }).catch(() => logger_js.logger.warn(`Failed to install ${emojiPkg}, using API fallback`));
2206
+ }
2207
+ }
2208
+ if (finalEmojiStrategy === "auto") {
2209
+ finalEmojiStrategy = hasLocalIconify ? "local" : "fetch";
2210
+ }
2211
+ if (finalEmojiStrategy === "local" && !hasLocalIconify) {
2212
+ logger_js.logger.warn(`emojiStrategy is set to 'local' but ${emojiPkg} is not installed. Falling back to 'fetch'.`);
2213
+ finalEmojiStrategy = "fetch";
2214
+ }
2215
+ const runtimeEmojiStrategy = targetCompatibility.emoji === "fetch" ? "fetch" : finalEmojiStrategy;
2216
+ if (runtimeEmojiStrategy === "local") {
2217
+ if (nuxt.options.dev)
2218
+ logger_js.logger.debug(`Using local dependency \`${emojiPkg}\` for emoji rendering.`);
2219
+ else if (nuxt.options.build && !nuxt.options._prepare)
2220
+ logger_js.logger.info(`Using local dependency \`${emojiPkg}\` for emoji rendering.`);
2221
+ nuxt.options.alias["#og-image/emoji-transform"] = resolve("./runtime/server/og-image/satori/transforms/emojis/local");
2222
+ nuxt.options.nitro.virtual = nuxt.options.nitro.virtual || {};
2223
+ nuxt.options.nitro.virtual["#og-image-virtual/iconify-json-icons.mjs"] = () => {
2224
+ return `export { icons, width, height } from '${emojiPkg}/icons.json'`;
2225
+ };
2226
+ } else {
2227
+ if (targetCompatibility.emoji === "fetch" && finalEmojiStrategy === "local") {
2228
+ logger_js.logger.info(`Using iconify API for runtime emojis on ${preset} (local icons used at build time).`);
2229
+ } else {
2230
+ logger_js.logger.info(`Using iconify API for emojis${hasLocalIconify ? " (emojiStrategy: fetch)" : `, install ${emojiPkg} for local support`}.`);
2231
+ }
2232
+ nuxt.options.alias["#og-image/emoji-transform"] = resolve("./runtime/server/og-image/satori/transforms/emojis/fetch");
2233
+ }
2234
+ const tw4State = {
2235
+ styleMap: {},
2236
+ cssPath: void 0,
2237
+ fontVars: {},
2238
+ breakpoints: {},
2239
+ colors: {},
2240
+ nuxtUiColors: void 0,
2241
+ initialized: false
2242
+ };
2243
+ let tw4InitPromise;
2244
+ const nuxtUiDefaults = {
2245
+ primary: "green",
2246
+ secondary: "blue",
2247
+ success: "green",
2248
+ info: "blue",
2249
+ warning: "yellow",
2250
+ error: "red",
2251
+ neutral: "slate"
2252
+ };
2253
+ async function loadNuxtUiColors() {
2254
+ if (tw4State.nuxtUiColors)
2255
+ return tw4State.nuxtUiColors;
2256
+ if (!kit.hasNuxtModule("@nuxt/ui"))
2257
+ return void 0;
2258
+ const appConfigPath = pathe.join(nuxt.options.buildDir, "app.config.mjs");
2259
+ if (!fs.existsSync(appConfigPath))
2260
+ return { ...nuxtUiDefaults };
2261
+ const rawContent = await promises.readFile(appConfigPath, "utf-8");
2262
+ const strippedContent = rawContent.replace(/\/\*\* client \*\*\/[\s\S]*?\/\*\* client-end \*\*\//g, "");
2263
+ const jiti$1 = jiti.createJiti(nuxt.options.buildDir, {
2264
+ interopDefault: true,
2265
+ moduleCache: false
2266
+ });
2267
+ const mergedAppConfig = await jiti$1.evalModule(strippedContent, { filename: appConfigPath });
2268
+ tw4State.nuxtUiColors = { ...nuxtUiDefaults, ...mergedAppConfig?.ui?.colors };
2269
+ logger_js.logger.debug(`Nuxt UI colors: ${JSON.stringify(tw4State.nuxtUiColors)}`);
2270
+ return tw4State.nuxtUiColors;
2271
+ }
2272
+ async function detectTailwindCssPath() {
2273
+ for (const cssEntry of nuxt.options.css) {
2274
+ const cssPath = typeof cssEntry === "string" ? cssEntry : cssEntry?.src;
2275
+ if (!cssPath || !cssPath.endsWith(".css"))
2276
+ continue;
2277
+ const resolved = await resolver.resolvePath(cssPath).catch(() => null);
2278
+ if (!resolved || !fs.existsSync(resolved))
2279
+ continue;
2280
+ const content = await promises.readFile(resolved, "utf-8");
2281
+ if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'"))
2282
+ return resolved;
2283
+ }
2284
+ }
2285
+ async function initTw4() {
2286
+ if (tw4State.initialized)
2287
+ return;
2288
+ if (tw4InitPromise)
2289
+ return tw4InitPromise;
2290
+ tw4InitPromise = (async () => {
2291
+ const resolvedCssPath = config.tailwindCss ? await resolver.resolvePath(config.tailwindCss) : nuxt.options.alias["#tailwindcss"] ?? await detectTailwindCssPath();
2292
+ tw4State.cssPath = resolvedCssPath;
2293
+ if (!resolvedCssPath || !fs.existsSync(resolvedCssPath)) {
2294
+ tw4State.initialized = true;
2295
+ return;
2296
+ }
2297
+ const tw4CssContent = await promises.readFile(resolvedCssPath, "utf-8");
2298
+ if (!tw4CssContent.includes("@theme") && !tw4CssContent.includes('@import "tailwindcss"')) {
2299
+ tw4State.initialized = true;
2300
+ return;
2301
+ }
2302
+ const nuxtUiColors = await loadNuxtUiColors();
2303
+ const metadata = await extractTw4Metadata({
2304
+ cssPath: resolvedCssPath,
2305
+ nuxtUiColors
2306
+ }).catch((e) => {
2307
+ logger_js.logger.warn(`TW4 metadata extraction failed: ${e.message}`);
2308
+ return { fontVars: {}, breakpoints: {}, colors: {} };
2309
+ });
2310
+ tw4State.fontVars = metadata.fontVars;
2311
+ tw4State.breakpoints = metadata.breakpoints;
2312
+ tw4State.colors = metadata.colors;
2313
+ try {
2314
+ const { scanComponentClasses, filterProcessableClasses } = await import('../chunks/tw4-classes.cjs');
2315
+ const { generateStyleMap } = await import('../chunks/tw4-generator.cjs');
2316
+ const allClasses = await scanComponentClasses(config.componentDirs, nuxt.options.srcDir, logger_js.logger);
2317
+ const processableClasses = filterProcessableClasses(allClasses);
2318
+ if (processableClasses.length > 0) {
2319
+ logger_js.logger.debug(`TW4: Found ${processableClasses.length} unique classes in OG components`);
2320
+ const styleMap = await generateStyleMap({
2321
+ cssPath: resolvedCssPath,
2322
+ classes: processableClasses,
2323
+ nuxtUiColors
2324
+ });
2325
+ for (const [cls, styles] of styleMap.classes) {
2326
+ tw4State.styleMap[cls] = styles;
2327
+ }
2328
+ logger_js.logger.debug(`TW4: Generated style map with ${Object.keys(tw4State.styleMap).length} resolved classes`);
2329
+ }
2330
+ } catch (e) {
2331
+ logger_js.logger.warn(`TW4 style map generation failed: ${e.message}`);
2332
+ }
2333
+ logger_js.logger.debug(`TW4 enabled from ${pathe.relative(nuxt.options.rootDir, resolvedCssPath)}`);
2334
+ tw4State.initialized = true;
2335
+ })();
2336
+ return tw4InitPromise;
2337
+ }
2338
+ const resolvedOgComponentPaths = [];
2339
+ nuxt.hook("modules:done", () => {
2340
+ kit.addVitePlugin(AssetTransformPlugin.vite({
2341
+ emojiSet: finalEmojiStrategy === "local" ? config.defaults.emojis || "noto" : void 0,
2342
+ get ogComponentPaths() {
2343
+ return resolvedOgComponentPaths;
2344
+ },
2345
+ // Resolved OG component directory paths
2346
+ rootDir: nuxt.options.rootDir,
2347
+ srcDir: nuxt.options.srcDir,
2348
+ publicDir: pathe.join(nuxt.options.srcDir, nuxt.options.dir.public || "public"),
2349
+ get tw4StyleMap() {
2350
+ return tw4State.styleMap;
2351
+ },
2352
+ // Getter to access populated map
2353
+ initTw4,
2354
+ // Lazy initializer - called on first transform
2355
+ get tw4CssPath() {
2356
+ return tw4State.cssPath;
2357
+ },
2358
+ // Getter for gradient resolution
2359
+ loadNuxtUiColors
2360
+ // Lazy loader for Nuxt UI colors from .nuxt/app.config.mjs
2361
+ }));
2362
+ });
2363
+ if (config.zeroRuntime) {
2364
+ config.compatibility = defu.defu(config.compatibility, {
2365
+ runtime: {
2366
+ chromium: false,
2367
+ // should already be false
2368
+ satori: false
2369
+ }
2370
+ });
2371
+ if (!nuxt.options.dev) {
2372
+ kit.addBuildPlugin(TreeShakeComposablesPlugin, { server: true, client: true, build: true });
2373
+ nuxt.options.alias["#og-image-cache"] = resolve("./runtime/server/og-image/cache/mock");
2374
+ }
2375
+ }
2376
+ const basePath = config.zeroRuntime ? "./runtime/server/routes/__zero-runtime" : "./runtime/server/routes";
2377
+ let publicDirAbs = nuxt.options.dir.public;
2378
+ if (!pathe.isAbsolute(publicDirAbs)) {
2379
+ publicDirAbs = (publicDirAbs in nuxt.options.alias ? nuxt.options.alias[publicDirAbs] : pathe.join(nuxt.options.rootDir, publicDirAbs)) || "";
2380
+ }
2381
+ if (isProviderEnabledForEnv("satori", nuxt, config)) {
2382
+ let attemptSharpUsage = false;
2383
+ if (isProviderEnabledForEnv("sharp", nuxt, config)) {
2384
+ const userConfiguredExtension = config.defaults.extension;
2385
+ const hasConfiguredJpegs = userConfiguredExtension && ["jpeg", "jpg"].includes(userConfiguredExtension);
2386
+ const allDeps = {
2387
+ ...userAppPkgJson.dependencies || {},
2388
+ ...userAppPkgJson.devDependencies || {}
2389
+ };
2390
+ const hasExplicitSharpDependency = !!config.sharpOptions || "sharp" in allDeps || hasConfiguredJpegs;
2391
+ if (hasExplicitSharpDependency) {
2392
+ if (!targetCompatibility.sharp) {
2393
+ logger_js.logger.warn(`Rendering JPEGs requires sharp which does not work with ${preset}. Images will be rendered as PNG at runtime.`);
2394
+ config.compatibility = defu.defu(config.compatibility, {
2395
+ runtime: { sharp: false }
2396
+ });
2397
+ } else {
2398
+ await import('sharp').catch(() => {
2399
+ }).then(() => {
2400
+ attemptSharpUsage = true;
2401
+ });
2402
+ }
2403
+ } else if (hasConfiguredJpegs) {
2404
+ logger_js.logger.warn("You have enabled `JPEG` images. These require the `sharp` dependency which is missing, installing it for you.");
2405
+ await ensureDependencies(["sharp"]);
2406
+ logger_js.logger.warn("Support for `sharp` is limited so check the compatibility guide.");
2407
+ attemptSharpUsage = true;
2408
+ }
2409
+ }
2410
+ if (!attemptSharpUsage) {
2411
+ config.compatibility = defu.defu(config.compatibility, {
2412
+ runtime: { sharp: false },
2413
+ dev: { sharp: false },
2414
+ prerender: { sharp: false }
2415
+ });
2416
+ }
2417
+ if (isProviderEnabledForEnv("resvg", nuxt, config)) {
2418
+ await import('@resvg/resvg-js').catch(() => {
2419
+ logger_js.logger.warn("ReSVG is missing dependencies for environment. Falling back to WASM version, this may slow down PNG rendering.");
2420
+ config.compatibility = defu.defu(config.compatibility, {
2421
+ dev: { resvg: "wasm-fs" },
2422
+ prerender: { resvg: "wasm-fs" }
2423
+ });
2424
+ if (targetCompatibility.resvg === "node") {
2425
+ config.compatibility = defu.defu(config.compatibility, {
2426
+ runtime: { resvg: "wasm" }
2427
+ });
2428
+ }
2429
+ });
2430
+ }
2431
+ }
2432
+ if (isProviderEnabledForEnv("chromium", nuxt, config)) {
2433
+ const hasChromeLocally = checkLocalChrome();
2434
+ const hasPlaywrightDependency = await hasResolvableDependency("playwright");
2435
+ const chromeCompatibilityFlags = {
2436
+ prerender: config.compatibility?.prerender?.chromium,
2437
+ dev: config.compatibility?.dev?.chromium,
2438
+ runtime: config.compatibility?.runtime?.chromium
2439
+ };
2440
+ const chromiumBinding = {
2441
+ dev: null,
2442
+ prerender: null,
2443
+ runtime: null
2444
+ };
2445
+ if (nuxt.options.dev) {
2446
+ if (isUndefinedOrTruthy(chromeCompatibilityFlags.dev))
2447
+ chromiumBinding.dev = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
2448
+ } else {
2449
+ if (isUndefinedOrTruthy(chromeCompatibilityFlags.prerender))
2450
+ chromiumBinding.prerender = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
2451
+ if (isUndefinedOrTruthy(chromeCompatibilityFlags.runtime))
2452
+ chromiumBinding.runtime = hasPlaywrightDependency ? "playwright" : null;
2453
+ }
2454
+ config.compatibility = defu.defu(config.compatibility, {
2455
+ runtime: { chromium: chromiumBinding.runtime },
2456
+ dev: { chromium: chromiumBinding.dev },
2457
+ prerender: { chromium: chromiumBinding.prerender }
2458
+ });
2459
+ }
2460
+ await kit$1.installNuxtSiteConfig();
2461
+ nuxt.options.experimental.componentIslands ||= true;
2462
+ if (config.debug || nuxt.options.dev) {
2463
+ kit.addServerHandler({
2464
+ lazy: true,
2465
+ route: "/_og/debug.json",
2466
+ handler: resolve("./runtime/server/routes/debug.json")
2467
+ });
2468
+ }
2469
+ kit.addServerHandler({
2470
+ lazy: true,
2471
+ route: "/_og/d/**",
2472
+ handler: resolve(`${basePath}/image`)
2473
+ });
2474
+ kit.addServerHandler({
2475
+ lazy: true,
2476
+ route: "/_og/s/**",
2477
+ handler: resolve(`${basePath}/image`)
2478
+ });
2479
+ if (!nuxt.options.dev) {
2480
+ nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
2481
+ }
2482
+ [
2483
+ "defineOgImage",
2484
+ "defineOgImageComponent",
2485
+ { name: "defineOgImageScreenshot", enabled: isProviderEnabledForEnv("chromium", nuxt, config) }
2486
+ ].forEach((name) => {
2487
+ if (typeof name === "object") {
2488
+ if (!name.enabled) {
2489
+ kit.addImports({ name: name.name, from: resolve(`./runtime/app/composables/mock`) });
2490
+ return;
2491
+ }
2492
+ name = name.name;
2493
+ }
2494
+ kit.addImports({
2495
+ name,
2496
+ from: resolve(`./runtime/app/composables/${name}`)
2497
+ });
2498
+ if (!nuxt.options.dev) {
2499
+ nuxt.options.optimization.treeShake.composables.client = nuxt.options.optimization.treeShake.composables.client || {};
2500
+ nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] || [];
2501
+ nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"].push(name);
2502
+ }
2503
+ });
2504
+ if (nuxt.options.dev) {
2505
+ kit.addComponentsDir({
2506
+ path: resolve("./runtime/app/components/Templates/Community"),
2507
+ island: true,
2508
+ watch: IS_MODULE_DEVELOPMENT
2509
+ });
2510
+ }
2511
+ kit.addComponent({
2512
+ name: "OgImageScreenshot",
2513
+ filePath: resolve(`./runtime/app/components/OgImage/OgImageScreenshot`),
2514
+ ...config.componentOptions
2515
+ });
2516
+ const basePluginPath = `./runtime/app/plugins${config.zeroRuntime ? "/__zero-runtime" : ""}`;
2517
+ kit.addPlugin({ mode: "server", src: resolve(`${basePluginPath}/route-rule-og-image.server`) });
2518
+ kit.addPlugin({ mode: "server", src: resolve(`${basePluginPath}/og-image-canonical-urls.server`) });
2519
+ const componentRoots = await Promise.all(
2520
+ nuxt.options.components?.dirs?.map(async (dir) => {
2521
+ const dirPath = typeof dir === "string" ? dir : dir.path;
2522
+ return resolver.resolvePath(dirPath).catch(() => null);
2523
+ }) || []
2524
+ ).then((paths) => paths.filter(Boolean));
2525
+ for (const componentDir of config.componentDirs) {
2526
+ let found = false;
2527
+ for (const root of componentRoots) {
2528
+ const path = pathe.join(root, componentDir);
2529
+ if (fs.existsSync(path)) {
2530
+ found = true;
2531
+ resolvedOgComponentPaths.push(path);
2532
+ kit.addComponentsDir({
2533
+ path,
2534
+ island: true,
2535
+ watch: IS_MODULE_DEVELOPMENT,
2536
+ prefix: componentDir === "OgImageCommunity" ? "OgImage" : void 0
2537
+ });
2538
+ }
2539
+ }
2540
+ if (!found && !defaultComponentDirs.includes(componentDir)) {
2541
+ logger_js.logger.warn(`The configured component directory \`${componentDir}\` does not exist in any component root. Skipping.`);
2542
+ }
2543
+ }
2544
+ const builtinTemplatesDir = resolve("./runtime/app/components/Templates");
2545
+ if (fs__namespace.existsSync(builtinTemplatesDir)) {
2546
+ resolvedOgComponentPaths.push(builtinTemplatesDir);
2547
+ }
2548
+ const ogImageComponentCtx = { components: [], detectedRenderers: /* @__PURE__ */ new Set() };
2549
+ for (const componentDir of config.componentDirs) {
2550
+ for (const root of componentRoots) {
2551
+ const path = pathe.join(root, componentDir);
2552
+ if (fs__namespace.existsSync(path)) {
2553
+ const files = fs__namespace.readdirSync(path).filter((f) => f.endsWith(".vue"));
2554
+ for (const file of files) {
2555
+ const renderer = getRendererFromFilename(file);
2556
+ if (renderer)
2557
+ ogImageComponentCtx.detectedRenderers.add(renderer);
2558
+ }
2559
+ }
2560
+ }
2561
+ }
2562
+ const communityDir = resolve("./runtime/app/components/Templates/Community");
2563
+ if (fs__namespace.existsSync(communityDir)) {
2564
+ const files = fs__namespace.readdirSync(communityDir).filter((f) => f.endsWith(".vue"));
2565
+ for (const file of files) {
2566
+ const renderer = getRendererFromFilename(file);
2567
+ if (renderer)
2568
+ ogImageComponentCtx.detectedRenderers.add(renderer);
2569
+ }
2570
+ }
2571
+ nuxt.hook("components:extend", (components) => {
2572
+ ogImageComponentCtx.components = [];
2573
+ const invalidComponents = [];
2574
+ const baseNameToRenderer = /* @__PURE__ */ new Map();
2575
+ components.forEach((component) => {
2576
+ let valid = false;
2577
+ config.componentDirs.forEach((dir) => {
2578
+ if (component.pascalName.startsWith(dir) || component.kebabName.startsWith(dir) || component.shortPath.includes(`components/${dir}/`)) {
2579
+ valid = true;
2580
+ }
2581
+ });
2582
+ if (component.filePath.includes(resolve("./runtime/app/components/Templates")))
2583
+ valid = true;
2584
+ if (valid && fs__namespace.existsSync(component.filePath)) {
2585
+ const renderer = getRendererFromFilename(component.filePath);
2586
+ if (!renderer) {
2587
+ invalidComponents.push(component.filePath);
2588
+ return;
2589
+ }
2590
+ const baseName = stripRendererSuffix(component.pascalName);
2591
+ const existing = baseNameToRenderer.get(baseName);
2592
+ if (existing && existing.renderer !== renderer) {
2593
+ logger_js.logger.warn(`Duplicate OG Image component "${baseName}" with different renderers:
2594
+ ${existing.path} (${existing.renderer})
2595
+ ${component.filePath} (${renderer})`);
2596
+ }
2597
+ baseNameToRenderer.set(baseName, { renderer, path: component.filePath });
2598
+ ogImageComponentCtx.detectedRenderers.add(renderer);
2599
+ component.island = true;
2600
+ component.mode = "server";
2601
+ let category = "app";
2602
+ if (component.filePath.includes(resolve("./runtime/app/components/Templates/Community")))
2603
+ category = "community";
2604
+ const componentFile = fs__namespace.readFileSync(component.filePath, "utf-8");
2605
+ const credits = componentFile.split("\n").find((line) => line.startsWith(" * @credits"))?.replace("* @credits", "").trim();
2606
+ ogImageComponentCtx.components.push({
2607
+ hash: ohash.hash(componentFile).replaceAll("_", "-"),
2608
+ pascalName: component.pascalName,
2609
+ kebabName: component.kebabName,
2610
+ path: component.filePath,
2611
+ category,
2612
+ credits,
2613
+ renderer
2614
+ });
2615
+ }
2616
+ });
2617
+ if (!nuxt.options.dev) {
2618
+ const communityDir2 = resolve("./runtime/app/components/Templates/Community");
2619
+ if (fs__namespace.existsSync(communityDir2)) {
2620
+ fs__namespace.readdirSync(communityDir2).filter((f) => f.endsWith(".vue")).forEach((file) => {
2621
+ const renderer = getRendererFromFilename(file);
2622
+ if (!renderer)
2623
+ return;
2624
+ const name = file.replace(".vue", "");
2625
+ const filePath = resolve(communityDir2);
2626
+ if (ogImageComponentCtx.components.some((c) => c.pascalName === name))
2627
+ return;
2628
+ ogImageComponentCtx.components.push({
2629
+ hash: "",
2630
+ pascalName: name,
2631
+ kebabName: name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
2632
+ path: filePath,
2633
+ category: "community",
2634
+ renderer
2635
+ });
2636
+ ogImageComponentCtx.detectedRenderers.add(renderer);
2637
+ });
2638
+ }
2639
+ }
2640
+ if (invalidComponents.length > 0) {
2641
+ const message = `OG Image components missing renderer suffix (.satori.vue, .chromium.vue, .takumi.vue):
2642
+ ${invalidComponents.map((c) => ` ${c}`).join("\n")}
2643
+
2644
+ Run: npx nuxt-og-image migrate v6`;
2645
+ if (nuxt.options.dev) {
2646
+ if (config.warnMissingSuffix !== false)
2647
+ logger_js.logger.warn(message);
2648
+ } else {
2649
+ throw new Error(message);
2650
+ }
2651
+ }
2652
+ nuxt.hooks.hook("nuxt-og-image:components", ogImageComponentCtx);
2653
+ });
2654
+ kit.addTemplate({
2655
+ filename: "nuxt-og-image/components.mjs",
2656
+ getContents() {
2657
+ return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
2658
+ },
2659
+ options: { mode: "server" }
2660
+ });
2661
+ nuxt.options.nitro.virtual = nuxt.options.nitro.virtual || {};
2662
+ nuxt.options.nitro.virtual["#og-image-virtual/component-names.mjs"] = () => {
2663
+ return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
2664
+ };
2665
+ nuxt.options.nitro.virtual["#og-image-virtual/public-assets.mjs"] = async () => {
2666
+ const isCloudflare = ["cloudflare", "cloudflare-pages", "cloudflare-module"].includes(preset);
2667
+ if (isCloudflare) {
2668
+ const devBinding = resolver.resolve("./runtime/server/og-image/bindings/font-assets/dev-prerender");
2669
+ const cfBinding = resolver.resolve("./runtime/server/og-image/bindings/font-assets/cloudflare");
2670
+ return `
2671
+ import { resolve as devResolve } from '${devBinding}'
2672
+ import { resolve as cfResolve } from '${cfBinding}'
2673
+ export const resolve = (import.meta.dev || import.meta.prerender) ? devResolve : cfResolve
2674
+ `;
2675
+ }
2676
+ return `export { resolve } from '${resolver.resolve("./runtime/server/og-image/bindings/font-assets/dev-prerender")}'`;
2677
+ };
2678
+ nuxt.options.nitro.virtual["#og-image/fonts"] = async () => {
2679
+ const templates = nuxt.options.build.templates;
2680
+ const nuxtFontsTemplate = templates.find((t) => t.filename?.endsWith("nuxt-fonts-global.css"));
2681
+ if (!nuxtFontsTemplate?.getContents) {
2682
+ return `export default []`;
2683
+ }
2684
+ const contents = await nuxtFontsTemplate.getContents({});
2685
+ const fontFaceRegex = /@font-face\s*\{([^}]+)\}/g;
2686
+ const fonts = [];
2687
+ for (const match of contents.matchAll(fontFaceRegex)) {
2688
+ const block = match[1];
2689
+ if (!block)
2690
+ continue;
2691
+ const family = block.match(/font-family:\s*['"]?([^'";]+)['"]?/)?.[1]?.trim();
2692
+ const src = block.match(/url\(["']?([^)"']+)["']?\)/)?.[1];
2693
+ const weight = Number.parseInt(block.match(/font-weight:\s*(\d+)/)?.[1] || "400");
2694
+ const style = block.match(/font-style:\s*(\w+)/)?.[1] || "normal";
2695
+ if (family && src) {
2696
+ fonts.push({ family, src, weight, style });
2697
+ }
2698
+ }
2699
+ const familiesWithNonWoff2 = new Set(fonts.filter((f) => !f.src.endsWith(".woff2")).map((f) => f.family));
2700
+ const warnedFamilies = /* @__PURE__ */ new Set();
2701
+ for (const f of fonts) {
2702
+ if (f.src.endsWith(".woff2") && !familiesWithNonWoff2.has(f.family) && !warnedFamilies.has(f.family)) {
2703
+ warnedFamilies.add(f.family);
2704
+ logger_js.logger.warn(`WOFF2-only font detected (${f.family}). Satori renderer does not support WOFF2 - use Takumi renderer or provide WOFF/TTF alternatives.`);
2705
+ }
2706
+ }
2707
+ logger_js.logger.debug(`Extracted fonts from @nuxt/fonts: ${JSON.stringify(fonts)}`);
2708
+ return `export default ${JSON.stringify(fonts)}`;
2709
+ };
2710
+ nuxt.options.nitro.virtual["#og-image-virtual/tw4-theme.mjs"] = () => {
2711
+ return `export const tw4FontVars = ${JSON.stringify(tw4State.fontVars)}
2712
+ export const tw4Breakpoints = ${JSON.stringify(tw4State.breakpoints)}
2713
+ export const tw4Colors = ${JSON.stringify(tw4State.colors)}`;
2714
+ };
2715
+ nuxt.options.nitro.virtual["#og-image-virtual/build-dir.mjs"] = () => {
2716
+ return `export const buildDir = ${JSON.stringify(nuxt.options.buildDir)}`;
2717
+ };
2718
+ let fontContext = null;
2719
+ nuxt.hook("fonts:public-asset-context", (ctx) => {
2720
+ fontContext = ctx;
2721
+ });
2722
+ nuxt.hook("vite:compiled", () => {
2723
+ if (fontContext?.renderedFontURLs.size) {
2724
+ const cacheDir = pathe.join(nuxt.options.buildDir, "cache", "og-image");
2725
+ fs__namespace.mkdirSync(cacheDir, { recursive: true });
2726
+ const mapping = Object.fromEntries(fontContext.renderedFontURLs);
2727
+ fs__namespace.writeFileSync(pathe.join(cacheDir, "font-urls.json"), JSON.stringify(mapping));
2728
+ logger_js.logger.debug(`Persisted ${fontContext.renderedFontURLs.size} font URLs for prerender`);
2729
+ }
2730
+ });
2731
+ registerTypeTemplates({
2732
+ nuxt,
2733
+ config,
2734
+ componentCtx: ogImageComponentCtx
2735
+ });
2736
+ const cacheEnabled = typeof config.runtimeCacheStorage !== "undefined" && config.runtimeCacheStorage !== false;
2737
+ const cacheVersion = config.cacheVersion === false ? "" : config.cacheVersion ?? version;
2738
+ const versionSuffix = cacheVersion ? `/${cacheVersion}` : "";
2739
+ let baseCacheKey;
2740
+ if (config.runtimeCacheStorage === true) {
2741
+ baseCacheKey = `/cache/nuxt-og-image${versionSuffix}`;
2742
+ } else if (typeof config.runtimeCacheStorage === "string") {
2743
+ baseCacheKey = `/${config.runtimeCacheStorage}/nuxt-og-image${versionSuffix}`;
2744
+ } else if (typeof config.runtimeCacheStorage === "object") {
2745
+ baseCacheKey = `/nuxt-og-image${versionSuffix}`;
2746
+ if (!nuxt.options.dev) {
2747
+ nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
2748
+ nuxt.options.nitro.storage["nuxt-og-image"] = config.runtimeCacheStorage;
2749
+ }
2750
+ } else {
2751
+ baseCacheKey = false;
2752
+ }
2753
+ if (!cacheEnabled)
2754
+ baseCacheKey = false;
2755
+ const buildCachePath = typeof config.buildCache === "object" && config.buildCache.base ? config.buildCache.base : "node_modules/.cache/nuxt-seo/og-image";
2756
+ const buildCacheDir = config.buildCache ? pathe.join(nuxt.options.rootDir, buildCachePath) : void 0;
2757
+ nuxt.hooks.hook("modules:done", async () => {
2758
+ if (!isNuxtGenerate() && nuxt.options.build) {
2759
+ nuxt.options.nitro = nuxt.options.nitro || {};
2760
+ nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
2761
+ nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
2762
+ }
2763
+ const hasColorModeModule = kit.hasNuxtModule("@nuxtjs/color-mode");
2764
+ const colorModeOptions = hasColorModeModule ? await getNuxtModuleOptions("@nuxtjs/color-mode") : {};
2765
+ let colorPreference = colorModeOptions.preference;
2766
+ if (!colorPreference || colorPreference === "system")
2767
+ colorPreference = colorModeOptions.fallback;
2768
+ if (!colorPreference || colorPreference === "system")
2769
+ colorPreference = "light";
2770
+ const runtimeConfig = {
2771
+ version,
2772
+ // binding options
2773
+ satoriOptions: config.satoriOptions || {},
2774
+ resvgOptions: config.resvgOptions || {},
2775
+ sharpOptions: config.sharpOptions === true ? {} : config.sharpOptions || {},
2776
+ publicStoragePath: `root${publicDirAbs.replace(nuxt.options.rootDir, "").replaceAll("/", ":")}`,
2777
+ defaults: config.defaults,
2778
+ debug: config.debug,
2779
+ // avoid adding credentials
2780
+ baseCacheKey,
2781
+ buildCacheDir,
2782
+ hasNuxtIcon: kit.hasNuxtModule("nuxt-icon") || kit.hasNuxtModule("@nuxt/icon"),
2783
+ colorPreference,
2784
+ // @ts-expect-error runtime type
2785
+ isNuxtContentDocumentDriven: !!nuxt.options.content?.documentDriven,
2786
+ cacheQueryParams: config.cacheQueryParams ?? false
2787
+ };
2788
+ if (nuxt.options.dev) {
2789
+ runtimeConfig.componentDirs = config.componentDirs;
2790
+ runtimeConfig.srcDir = nuxt.options.srcDir;
2791
+ runtimeConfig.communityTemplatesDir = resolve("./runtime/app/components/Templates/Community");
2792
+ }
2793
+ nuxt.hooks.callHook("nuxt-og-image:runtime-config", runtimeConfig);
2794
+ nuxt.options.runtimeConfig["nuxt-og-image"] = runtimeConfig;
2795
+ });
2796
+ const getDetectedRenderers = () => ogImageComponentCtx.detectedRenderers;
2797
+ if (nuxt.options.dev) {
2798
+ setupDevHandler(config, resolver, getDetectedRenderers);
2799
+ setupDevToolsUI(config, resolve);
2800
+ const useNitro = new Promise((resolveNitro) => {
2801
+ nuxt.hooks.hook("nitro:init", resolveNitro);
2802
+ });
2803
+ nuxt.hook("builder:watch", async (_event, relativePath) => {
2804
+ const absolutePath = pathe.join(nuxt.options.rootDir, relativePath);
2805
+ const isTw4CssChange = tw4State.cssPath && absolutePath === tw4State.cssPath;
2806
+ const isOgImageComponent = config.componentDirs.some((dir) => {
2807
+ const componentDir = pathe.join(nuxt.options.srcDir, "components", dir);
2808
+ return absolutePath.startsWith(componentDir) && absolutePath.endsWith(".vue");
2809
+ });
2810
+ if (isTw4CssChange || isOgImageComponent) {
2811
+ clearTw4Cache();
2812
+ if (tw4State.cssPath && fs.existsSync(tw4State.cssPath)) {
2813
+ const { scanComponentClasses, filterProcessableClasses } = await import('../chunks/tw4-classes.cjs');
2814
+ const { generateStyleMap } = await import('../chunks/tw4-generator.cjs');
2815
+ const allClasses = await scanComponentClasses(config.componentDirs, nuxt.options.srcDir, logger_js.logger);
2816
+ const processableClasses = filterProcessableClasses(allClasses);
2817
+ if (processableClasses.length > 0) {
2818
+ const nuxtUiColors = await loadNuxtUiColors();
2819
+ const styleMap = await generateStyleMap({
2820
+ cssPath: tw4State.cssPath,
2821
+ classes: processableClasses,
2822
+ nuxtUiColors
2823
+ });
2824
+ tw4State.styleMap = {};
2825
+ for (const [cls, styles] of styleMap.classes) {
2826
+ tw4State.styleMap[cls] = styles;
2827
+ }
2828
+ logger_js.logger.info(`HMR: Regenerated TW4 style map (${Object.keys(tw4State.styleMap).length} classes)`);
2829
+ }
2830
+ }
2831
+ await kit.updateTemplates({ filter: (t) => t.filename.includes("nuxt-og-image") });
2832
+ const nitro = await useNitro;
2833
+ await nitro.hooks.callHook("rollup:reload");
2834
+ }
2835
+ });
2836
+ } else if (isNuxtGenerate()) {
2837
+ setupGenerateHandler(config, resolver);
2838
+ } else if (nuxt.options.build) {
2839
+ await setupBuildHandler(config, resolver, getDetectedRenderers);
2840
+ }
2841
+ if (nuxt.options.build)
2842
+ kit.addServerPlugin(resolve("./runtime/server/plugins/prerender"));
2843
+ setupPrerenderHandler(config, resolver, getDetectedRenderers);
2844
+ }
2845
+ });
2846
+
2847
+ exports.buildNuxtUiVars = buildNuxtUiVars;
2848
+ exports.convertOklchToHex = convertOklchToHex;
2849
+ exports.createStylesheetLoader = createStylesheetLoader;
2850
+ exports.decodeCssClassName = decodeCssClassName;
2851
+ exports.module = module$1;
2852
+ exports.walkTemplateAst = walkTemplateAst;