nuxt-og-image 6.7.2 → 6.7.3

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 (33) hide show
  1. package/dist/chunks/tw4.cjs +7 -7
  2. package/dist/chunks/tw4.mjs +7 -7
  3. package/dist/chunks/uno.cjs +6 -6
  4. package/dist/chunks/uno.mjs +6 -6
  5. package/dist/cli.cjs +5 -12
  6. package/dist/cli.mjs +3 -10
  7. package/dist/devtools/lib/og-image/shared/urlEncoding.ts +5 -4
  8. package/dist/module.cjs +5 -5
  9. package/dist/module.json +1 -1
  10. package/dist/module.mjs +5 -5
  11. package/dist/runtime/app/plugins/__zero-runtime/og-image-canonical-urls.server.js +4 -6
  12. package/dist/runtime/app/plugins/__zero-runtime/route-rule-og-image.server.js +4 -6
  13. package/dist/runtime/app/plugins/og-image-canonical-urls.server.d.ts +1 -2
  14. package/dist/runtime/app/plugins/og-image-canonical-urls.server.js +1 -7
  15. package/dist/runtime/app/plugins/route-rule-og-image.server.d.ts +1 -2
  16. package/dist/runtime/app/plugins/route-rule-og-image.server.js +1 -7
  17. package/dist/runtime/app/utils/plugins.d.ts +2 -3
  18. package/dist/runtime/app/utils/plugins.js +5 -5
  19. package/dist/runtime/server/og-image/context.js +3 -3
  20. package/dist/runtime/server/util/cache.js +2 -2
  21. package/dist/runtime/server/util/kit.js +2 -2
  22. package/dist/runtime/shared/hash.d.ts +10 -0
  23. package/dist/runtime/shared/hash.js +5 -0
  24. package/dist/runtime/shared/urlEncoding.d.ts +3 -2
  25. package/dist/runtime/shared/urlEncoding.js +9 -9
  26. package/dist/shared/{nuxt-og-image.BCK8Adm4.cjs → nuxt-og-image.BRBtdkUp.cjs} +570 -81
  27. package/dist/shared/{nuxt-og-image.Pfj5JkJd.mjs → nuxt-og-image.Bv8VSGTj.mjs} +499 -10
  28. package/dist/shared/nuxt-og-image.CJa2KCie.mjs +189 -0
  29. package/dist/shared/nuxt-og-image.CMYbz66P.cjs +193 -0
  30. package/package.json +41 -39
  31. package/types/virtual.d.ts +9 -0
  32. package/dist/shared/nuxt-og-image.DQkJQHaN.cjs +0 -689
  33. package/dist/shared/nuxt-og-image.LgUGeaz-.mjs +0 -660
@@ -6,17 +6,19 @@ const promises = require('node:fs/promises');
6
6
  const node_url = require('node:url');
7
7
  const kit = require('@nuxt/kit');
8
8
  const defu = require('defu');
9
- const kit$1 = require('nuxt-site-config/kit');
10
- const ohash = require('ohash');
9
+ const fnv1a64 = require('fnv1a-64');
10
+ const kit$2 = require('nuxt-site-config/kit');
11
11
  const pathe = require('pathe');
12
12
  const pkgTypes = require('pkg-types');
13
13
  const stdEnv = require('std-env');
14
14
  const oxcWalker = require('oxc-walker');
15
- const dependencies = require('./nuxt-og-image.DQkJQHaN.cjs');
15
+ const kit$1 = require('nuxtseo-shared/kit');
16
+ const logger_js = require('../../dist/runtime/logger.js');
17
+ const chromeLauncher = require('chrome-launcher');
18
+ const nypm = require('nypm');
16
19
  const devtools = require('nuxtseo-shared/devtools');
17
20
  const unstorage = require('unstorage');
18
21
  const fsDriver = require('unstorage/drivers/fs-lite');
19
- const logger_js = require('../../dist/runtime/logger.js');
20
22
  const css_js = require('../../dist/runtime/server/og-image/utils/css.js');
21
23
  const MagicString = require('magic-string');
22
24
  const stripLiteral = require('strip-literal');
@@ -27,7 +29,7 @@ const ofetch = require('ofetch');
27
29
  const ultrahtml = require('ultrahtml');
28
30
  const emojiUtils_js = require('../../dist/runtime/server/og-image/core/transforms/emojis/emoji-utils.js');
29
31
  const exsolve = require('exsolve');
30
- const kit$2 = require('nuxtseo-shared/kit');
32
+ const fonts = require('./nuxt-og-image.CMYbz66P.cjs');
31
33
 
32
34
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
33
35
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -48,6 +50,276 @@ const fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
48
50
  const fsDriver__default = /*#__PURE__*/_interopDefaultCompat(fsDriver);
49
51
  const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
50
52
 
53
+ const isUndefinedOrTruthy = (v) => typeof v === "undefined" || v !== false;
54
+ function checkLocalChrome() {
55
+ if (stdEnv.isCI)
56
+ return false;
57
+ let hasChromeLocally = false;
58
+ try {
59
+ hasChromeLocally = !!chromeLauncher.Launcher.getFirstInstallation();
60
+ } catch {
61
+ }
62
+ return hasChromeLocally;
63
+ }
64
+ async function hasResolvableDependency(dep) {
65
+ return await kit.resolvePath(dep, { fallbackToOriginal: true }).catch(() => null).then((r) => r && r !== dep);
66
+ }
67
+ const VALID_RENDERER_SUFFIXES = ["satori", "browser", "takumi"];
68
+ const RE_RENDERER_SUFFIX_CI = /\.?(satori|browser|takumi)$/i;
69
+ const RE_RENDERER_SUFFIX = /(Satori|Browser|Takumi)$/;
70
+ const RE_LEGACY_SUFFIX = /-legacy$/;
71
+ const RE_WHITESPACE = /\s+/;
72
+ function getRendererFromFilename(filepath) {
73
+ const filename = pathe.basename(filepath).replace(".vue", "");
74
+ for (const suffix of VALID_RENDERER_SUFFIXES) {
75
+ if (filename.endsWith(`.${suffix}`))
76
+ return suffix;
77
+ }
78
+ return null;
79
+ }
80
+ const OGIMAGE_PREFIXES = [
81
+ { prefix: "OgImageCommunity", overlapWord: "Community" },
82
+ { prefix: "OgImageTemplate", overlapWord: "Template" },
83
+ { prefix: "OgImage", overlapWord: "Image" }
84
+ ];
85
+ function getRegisteredBaseNames(registeredPascalName) {
86
+ const stripped = registeredPascalName.replace(RE_RENDERER_SUFFIX_CI, "").replace(RE_RENDERER_SUFFIX, "");
87
+ const names = [];
88
+ for (const { prefix, overlapWord } of OGIMAGE_PREFIXES) {
89
+ if (!stripped.startsWith(prefix))
90
+ continue;
91
+ const withoutPrefix = stripped.slice(prefix.length);
92
+ if (withoutPrefix) {
93
+ names.push(withoutPrefix);
94
+ if (withoutPrefix !== overlapWord)
95
+ names.push(overlapWord + withoutPrefix);
96
+ } else {
97
+ names.push(overlapWord);
98
+ }
99
+ break;
100
+ }
101
+ if (names.length === 0)
102
+ names.push(stripped);
103
+ return names;
104
+ }
105
+
106
+ const NodeRuntime = {
107
+ // node-server runtime
108
+ browser: "on-demand",
109
+ // this gets changed build start
110
+ resvg: "node",
111
+ satori: "node",
112
+ takumi: "node",
113
+ sharp: "node",
114
+ // will be disabled if they're missing the dependency
115
+ emoji: "local"
116
+ // can bundle icons, no size constraints
117
+ };
118
+ const NodeDevRuntime = {
119
+ ...NodeRuntime,
120
+ resvg: "node-dev",
121
+ // use worker to prevent crashes from killing process
122
+ takumi: "node-dev"
123
+ // use worker to prevent crashes from killing process
124
+ };
125
+ const NodePrerenderRuntime = {
126
+ ...NodeRuntime,
127
+ resvg: "node-dev",
128
+ // use worker to prevent crashes from killing prerender process
129
+ takumi: "node-dev"
130
+ // use worker to prevent crashes from killing prerender process
131
+ };
132
+ const cloudflare = {
133
+ browser: "cloudflare",
134
+ resvg: "wasm",
135
+ satori: "wasm",
136
+ takumi: "wasm",
137
+ sharp: false,
138
+ emoji: "fetch",
139
+ // edge size limits - use API instead of bundling 24MB icons
140
+ wasm: {
141
+ esmImport: true
142
+ // Do NOT set lazy here — Nitro's cloudflare preset uses lazy: false because
143
+ // CF Workers require WASM modules to be imported eagerly via top-level await
144
+ }
145
+ };
146
+ const awsLambda = {
147
+ browser: false,
148
+ resvg: "node",
149
+ satori: "node",
150
+ takumi: "node",
151
+ sharp: false,
152
+ // 0.33.x has issues
153
+ emoji: "local"
154
+ // serverless has larger size limits
155
+ };
156
+ const WebContainer = {
157
+ browser: false,
158
+ resvg: "wasm-fs",
159
+ satori: "wasm-fs",
160
+ takumi: "wasm",
161
+ sharp: false,
162
+ emoji: "fetch"
163
+ // webcontainer has size constraints
164
+ };
165
+ const RuntimeCompatibility = {
166
+ "nitro-dev": NodeDevRuntime,
167
+ "nitro-prerender": NodePrerenderRuntime,
168
+ "node-server": NodeRuntime,
169
+ "node-cluster": NodeRuntime,
170
+ "node-listener": NodeRuntime,
171
+ "bun": NodeRuntime,
172
+ "deno-server": NodeRuntime,
173
+ "deno-deploy": NodeRuntime,
174
+ // node-server extending presets
175
+ "alwaysdata": NodeRuntime,
176
+ "cleavr": NodeRuntime,
177
+ "digital-ocean": NodeRuntime,
178
+ "edgio": NodeRuntime,
179
+ "flight-control": NodeRuntime,
180
+ "heroku": NodeRuntime,
181
+ "iis-handler": NodeRuntime,
182
+ "iis-node": NodeRuntime,
183
+ "koyeb": NodeRuntime,
184
+ "platform-sh": NodeRuntime,
185
+ "render-com": NodeRuntime,
186
+ "zeabur": NodeRuntime,
187
+ "zerops": NodeRuntime,
188
+ "stackblitz": WebContainer,
189
+ "codesandbox": WebContainer,
190
+ "aws-amplify": awsLambda,
191
+ "aws-lambda": awsLambda,
192
+ "azure": awsLambda,
193
+ "azure-functions": awsLambda,
194
+ "azure-swa": awsLambda,
195
+ "netlify": awsLambda,
196
+ "netlify-edge": {
197
+ browser: false,
198
+ resvg: "wasm",
199
+ satori: "node",
200
+ takumi: "wasm",
201
+ sharp: false,
202
+ emoji: "fetch",
203
+ // edge size limits
204
+ wasm: {
205
+ rollup: {
206
+ targetEnv: "auto-inline",
207
+ sync: ["@resvg/resvg-wasm/index_bg.wasm"]
208
+ }
209
+ }
210
+ },
211
+ "firebase": awsLambda,
212
+ "firebase-app-hosting": awsLambda,
213
+ "genezio": awsLambda,
214
+ "stormkit": awsLambda,
215
+ "vercel": awsLambda,
216
+ "vercel-edge": {
217
+ browser: false,
218
+ resvg: "wasm",
219
+ satori: "wasm",
220
+ takumi: "wasm",
221
+ sharp: false,
222
+ emoji: "fetch",
223
+ // edge size limits - bundling 24MB icons not viable
224
+ wasm: {
225
+ // lowers workers kb size
226
+ esmImport: true,
227
+ lazy: true
228
+ }
229
+ },
230
+ "cloudflare-pages": cloudflare,
231
+ "cloudflare-pages-static": cloudflare,
232
+ "cloudflare": cloudflare,
233
+ "cloudflare-module": cloudflare,
234
+ "cloudflare-durable": cloudflare
235
+ };
236
+ function resolveOgImagePreset(nitroConfig) {
237
+ const nuxt = kit.useNuxt();
238
+ if (nuxt.options.dev)
239
+ return "nitro-dev";
240
+ if (nuxt.options.nitro.static)
241
+ return "nitro-prerender";
242
+ return kit$1.resolveNitroPreset(nitroConfig);
243
+ }
244
+ function getPresetNitroPresetCompatibility(target) {
245
+ const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
246
+ let compatibility = RuntimeCompatibility[normalizedTarget];
247
+ if (!compatibility) {
248
+ logger_js.logger.warn(`Unknown Nitro preset "${target}" \u2014 falling back to node-server compatibility. Set ogImage.compatibility.runtime to override.`);
249
+ compatibility = NodeRuntime;
250
+ }
251
+ return compatibility;
252
+ }
253
+ async function applyNitroPresetCompatibility(nitroConfig, options) {
254
+ const target = resolveOgImagePreset(nitroConfig);
255
+ const compatibility = getPresetNitroPresetCompatibility(target);
256
+ const { resolve, detectedRenderers } = options;
257
+ const satoriEnabled = detectedRenderers.has("satori");
258
+ const browserEnabled = detectedRenderers.has("browser");
259
+ const takumiEnabled = detectedRenderers.has("takumi");
260
+ for (const renderer of detectedRenderers) {
261
+ if (!compatibility[renderer]) {
262
+ logger_js.logger.warn(`Renderer "${renderer}" detected but not supported on "${target}" preset. OG images using .${renderer}.vue components may fail.`);
263
+ }
264
+ }
265
+ const emptyMock = await resolve.resolvePath("./runtime/mock/empty");
266
+ nitroConfig.alias["#og-image/renderers/satori"] = satoriEnabled ? await resolve.resolvePath("./runtime/server/og-image/satori/renderer") : emptyMock;
267
+ nitroConfig.alias["#og-image/renderers/browser"] = browserEnabled ? await resolve.resolvePath("./runtime/server/og-image/browser/renderer") : emptyMock;
268
+ nitroConfig.alias["#og-image/renderers/takumi"] = takumiEnabled ? await resolve.resolvePath("./runtime/server/og-image/takumi/renderer") : emptyMock;
269
+ const resolvedCompatibility = { ...options.metadata || {} };
270
+ async function applyBinding(key) {
271
+ let binding = options.compatibility?.[key];
272
+ if (typeof binding === "undefined")
273
+ binding = compatibility[key];
274
+ const isRendererBinding = key === "browser" || key === "satori" || key === "takumi";
275
+ if (isRendererBinding && !detectedRenderers.has(key)) {
276
+ binding = false;
277
+ }
278
+ resolvedCompatibility[key] = binding;
279
+ if (!binding && isRendererBinding) {
280
+ nitroConfig.alias[`#og-image/renderers/${key}`] = emptyMock;
281
+ }
282
+ return {
283
+ [`#og-image/bindings/${key}`]: !binding ? emptyMock : await resolve.resolvePath(`./runtime/server/og-image/bindings/${key}/${binding}`)
284
+ };
285
+ }
286
+ const satoriPkgDir = await resolve.resolvePath("satori/package.json").then((p) => p.replace("/package.json", ""));
287
+ const yogaWasmPath = `${satoriPkgDir}/yoga.wasm`;
288
+ nitroConfig.alias = defu.defu(
289
+ { "#og-image/yoga-wasm": `${yogaWasmPath}?module` },
290
+ await applyBinding("browser"),
291
+ await applyBinding("satori"),
292
+ await applyBinding("takumi"),
293
+ await applyBinding("resvg"),
294
+ await applyBinding("sharp"),
295
+ nitroConfig.alias || {}
296
+ );
297
+ if (Object.values(resolvedCompatibility).includes("wasm")) {
298
+ nitroConfig.experimental = nitroConfig.experimental || {};
299
+ nitroConfig.experimental.wasm = true;
300
+ }
301
+ nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
302
+ nitroConfig.wasm = defu.defu(compatibility.wasm, nitroConfig.wasm);
303
+ nitroConfig.virtual["#og-image/compatibility"] = () => `export default ${JSON.stringify(resolvedCompatibility)}`;
304
+ kit.addTemplate({
305
+ filename: "nuxt-og-image/compatibility.mjs",
306
+ getContents() {
307
+ return `export default ${JSON.stringify(resolvedCompatibility)}`;
308
+ },
309
+ options: { mode: "server" }
310
+ });
311
+ return resolvedCompatibility;
312
+ }
313
+ async function ensureDependencies(dep, nuxt = kit.useNuxt()) {
314
+ const { ensureDependencyInstalled } = await import('nypm');
315
+ return Promise.all(dep.map((d) => {
316
+ return ensureDependencyInstalled(d, {
317
+ cwd: nuxt.options.rootDir,
318
+ dev: true
319
+ });
320
+ }));
321
+ }
322
+
51
323
  const RE_REFLECT_HAS_MINIFIED = /Reflect\.has\(([\w$]+),([\w$]+)\)\?Reflect\.get\(\1,\2,([\w$]+)\):Reflect\.get\(([\w$]+),\2,\3\)/g;
52
324
  const RE_WASM_IMPORT = /import\("(\.\/wasm\/[^"]+\.wasm)"\)/g;
53
325
  async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = kit.useNuxt()) {
@@ -70,9 +342,9 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompa
70
342
  });
71
343
  nuxt.hooks.hook("nitro:init", async (nitro) => {
72
344
  const renderers = getDetectedRenderers();
73
- await dependencies.applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers, metadata: getCompatibilityMeta() });
74
- const target = dependencies.resolveOgImagePreset(nitro.options);
75
- const normalizedTarget = target.replace(dependencies.RE_LEGACY_SUFFIX, "");
345
+ await applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers, metadata: getCompatibilityMeta() });
346
+ const target = resolveOgImagePreset(nitro.options);
347
+ const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
76
348
  const isEdgePreset = ["cloudflare", "cloudflare-pages", "cloudflare-pages-static", "cloudflare-module", "cloudflare-durable", "vercel-edge", "netlify-edge"].includes(normalizedTarget);
77
349
  const isCloudflarePreset = normalizedTarget.startsWith("cloudflare");
78
350
  const isCloudflarePagesOrModule = ["cloudflare-pages", "cloudflare-pages-static", "cloudflare-module", "cloudflare-durable"].includes(normalizedTarget);
@@ -84,7 +356,7 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompa
84
356
  nitro.options.cloudflare.pages.routes.exclude.push("/_og/s/*");
85
357
  }
86
358
  nitro.hooks.hook("compiled", async (_nitro) => {
87
- const compatibility = dependencies.getPresetNitroPresetCompatibility(target);
359
+ const compatibility = getPresetNitroPresetCompatibility(target);
88
360
  if (!isEdgePreset)
89
361
  return;
90
362
  const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
@@ -203,19 +475,211 @@ function patchWebAssemblyInstantiate(code) {
203
475
  return code;
204
476
  }
205
477
 
478
+ const TAKUMI_CORE_PACKAGE = "@takumi-rs/core";
479
+ const TAKUMI_WASM_PACKAGE = "@takumi-rs/wasm";
480
+ const PROVIDER_DEPENDENCIES = [
481
+ {
482
+ name: "satori",
483
+ description: "SVG-based renderer using Satori",
484
+ bindings: {
485
+ "node": [
486
+ { name: "satori", description: "HTML to SVG renderer" },
487
+ { name: "@resvg/resvg-js", description: "SVG to PNG converter (native)" }
488
+ ],
489
+ "wasm": [
490
+ { name: "satori", description: "HTML to SVG renderer" },
491
+ { name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
492
+ ],
493
+ "wasm-fs": [
494
+ { name: "satori", description: "HTML to SVG renderer" },
495
+ { name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
496
+ ]
497
+ }
498
+ },
499
+ {
500
+ name: "takumi",
501
+ description: "Rust-based high-performance renderer (recommended)",
502
+ bindings: {
503
+ "node": [
504
+ { name: TAKUMI_CORE_PACKAGE, description: "Native Takumi renderer" }
505
+ ],
506
+ "wasm": [
507
+ { name: TAKUMI_WASM_PACKAGE, description: "WASM Takumi renderer" }
508
+ ],
509
+ "wasm-fs": [
510
+ { name: TAKUMI_WASM_PACKAGE, description: "WASM Takumi renderer" }
511
+ ]
512
+ }
513
+ },
514
+ {
515
+ name: "browser",
516
+ description: "Browser-based screenshot renderer",
517
+ bindings: {
518
+ node: [
519
+ { name: "playwright-core", description: "Headless browser automation" }
520
+ ]
521
+ }
522
+ }
523
+ ];
524
+ async function getInstalledProviders() {
525
+ const installed = [];
526
+ for (const provider of PROVIDER_DEPENDENCIES) {
527
+ if (provider.bindings.node) {
528
+ const allNodeInstalled = await Promise.all(
529
+ provider.bindings.node.map((dep) => hasResolvableDependency(dep.name))
530
+ );
531
+ if (allNodeInstalled.every(Boolean)) {
532
+ installed.push({ provider: provider.name, binding: "node" });
533
+ continue;
534
+ }
535
+ }
536
+ if (provider.bindings.wasm) {
537
+ const allWasmInstalled = await Promise.all(
538
+ provider.bindings.wasm.map((dep) => hasResolvableDependency(dep.name))
539
+ );
540
+ if (allWasmInstalled.every(Boolean)) {
541
+ installed.push({ provider: provider.name, binding: "wasm" });
542
+ }
543
+ }
544
+ }
545
+ return installed;
546
+ }
547
+ async function getMissingDependencies(provider, binding = "node") {
548
+ const missing = await getMissingProviderDependencyDefinitions(provider, binding);
549
+ return missing.map((d) => d.name);
550
+ }
551
+ async function getMissingProviderDependencyDefinitions(provider, binding = "node") {
552
+ const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
553
+ if (!providerDef)
554
+ return [];
555
+ const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
556
+ const missing = [];
557
+ for (const dep of deps) {
558
+ if (!await hasResolvableDependency(dep.name))
559
+ missing.push(dep);
560
+ }
561
+ return missing;
562
+ }
563
+ function getDependencyDefinitions(provider, binding = "node") {
564
+ const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
565
+ if (!providerDef)
566
+ return [];
567
+ return providerDef.bindings[binding] || providerDef.bindings.node || [];
568
+ }
569
+ function getDependencyInstallSpec(dep) {
570
+ return dep.installSpec || dep.name;
571
+ }
572
+ async function getMissingDependencyInstallSpecs(provider, binding = "node") {
573
+ const missing = await getMissingProviderDependencyDefinitions(provider, binding);
574
+ return missing.map(getDependencyInstallSpec);
575
+ }
576
+ function getProviderDependencyInstallSpecs(provider, binding = "node") {
577
+ return getDependencyDefinitions(provider, binding).map(getDependencyInstallSpec);
578
+ }
579
+ function getRecommendedBindingFromPreset(provider) {
580
+ const preset = resolveOgImagePreset();
581
+ const compatibility = getPresetNitroPresetCompatibility(preset);
582
+ return getRecommendedBinding(provider, compatibility);
583
+ }
584
+ function getRecommendedBinding(provider, compatibility) {
585
+ if (provider === "satori") {
586
+ const resvgBinding = compatibility.resvg;
587
+ if (resvgBinding === "wasm-fs")
588
+ return "wasm-fs";
589
+ if (resvgBinding === "wasm")
590
+ return "wasm";
591
+ return "node";
592
+ }
593
+ if (provider === "takumi") {
594
+ const takumiBinding = compatibility.takumi;
595
+ if (takumiBinding === "wasm")
596
+ return "wasm";
597
+ return "node";
598
+ }
599
+ if (provider === "browser") {
600
+ return "node";
601
+ }
602
+ return "node";
603
+ }
604
+ async function ensureProviderDependencies(provider, binding, nuxt) {
605
+ const missingDeps = await getMissingProviderDependencyDefinitions(provider, binding);
606
+ if (missingDeps.length === 0)
607
+ return { success: true, installed: [] };
608
+ const missingInstallSpecs = missingDeps.map(getDependencyInstallSpec);
609
+ const pm = await nypm.detectPackageManager(nuxt.options.rootDir);
610
+ const pmName = pm?.name || "npm";
611
+ logger_js.logger.info(`Installing ${provider} dependencies: ${missingInstallSpecs.join(", ")}`);
612
+ const installed = [];
613
+ for (const dep of missingDeps) {
614
+ const installSpec = getDependencyInstallSpec(dep);
615
+ const success = await nypm.addDependency(installSpec, {
616
+ cwd: nuxt.options.rootDir,
617
+ dev: false
618
+ }).then(() => {
619
+ installed.push(installSpec);
620
+ return true;
621
+ }).catch(() => {
622
+ logger_js.logger.error(`Failed to install ${installSpec}. Run manually: ${pmName} add ${installSpec}`);
623
+ return false;
624
+ });
625
+ if (!success)
626
+ return { success: false, installed };
627
+ }
628
+ return { success: true, installed };
629
+ }
630
+ function canPromptInteractively(env = {
631
+ hasTTY: stdEnv.hasTTY,
632
+ hasStdinTTY: Boolean(process.stdin?.isTTY),
633
+ isAgent: stdEnv.isAgent,
634
+ isCI: stdEnv.isCI
635
+ }) {
636
+ return env.hasTTY && env.hasStdinTTY && !env.isAgent && !env.isCI;
637
+ }
638
+ async function promptForRendererSelection() {
639
+ logger_js.logger.info("Welcome to Nuxt OG Image! No renderer dependencies detected.");
640
+ const renderer = await logger_js.logger.prompt("Which renderer would you like to use?", {
641
+ type: "select",
642
+ options: PROVIDER_DEPENDENCIES.map((p) => p.name),
643
+ initial: "takumi"
644
+ });
645
+ return renderer || "takumi";
646
+ }
647
+ async function validateProviderSetup(renderer, compatibility) {
648
+ const issues = [];
649
+ const binding = getRecommendedBinding(renderer, compatibility);
650
+ const missing = await getMissingDependencyInstallSpecs(renderer, binding);
651
+ if (missing.length > 0) {
652
+ issues.push(`Missing ${renderer} dependencies: ${missing.join(", ")}`);
653
+ }
654
+ if (renderer === "satori") {
655
+ const hasResvgNode = await hasResolvableDependency("@resvg/resvg-js");
656
+ const hasResvgWasm = await hasResolvableDependency("@resvg/resvg-wasm");
657
+ if (!hasResvgNode && !hasResvgWasm) {
658
+ issues.push("Satori requires either @resvg/resvg-js (node) or @resvg/resvg-wasm to render PNGs");
659
+ }
660
+ }
661
+ if (renderer === "browser") {
662
+ const hasPlaywright = await hasResolvableDependency("playwright-core");
663
+ if (!hasPlaywright && binding === "node") {
664
+ issues.push("Browser renderer requires playwright-core");
665
+ }
666
+ }
667
+ return { valid: issues.length === 0, issues };
668
+ }
669
+
206
670
  function setupDevHandler(options, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = kit.useNuxt()) {
207
671
  nuxt.hooks.hook("nitro:init", async (nitro) => {
208
672
  const detectedRenderers = new Set(getDetectedRenderers());
209
- const targetCompatibility = dependencies.getPresetNitroPresetCompatibility(dependencies.resolveOgImagePreset(nitro.options));
673
+ const targetCompatibility = getPresetNitroPresetCompatibility(resolveOgImagePreset(nitro.options));
210
674
  for (const renderer of ["satori", "takumi", "browser"]) {
211
675
  if (!detectedRenderers.has(renderer)) {
212
- const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
213
- const missing = await dependencies.getMissingDependencies(renderer, binding);
676
+ const binding = getRecommendedBinding(renderer, targetCompatibility);
677
+ const missing = await getMissingDependencies(renderer, binding);
214
678
  if (missing.length === 0)
215
679
  detectedRenderers.add(renderer);
216
680
  }
217
681
  }
218
- await dependencies.applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers, metadata: getCompatibilityMeta() });
682
+ await applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers, metadata: getCompatibilityMeta() });
219
683
  });
220
684
  }
221
685
 
@@ -341,9 +805,9 @@ ${content}`, "utf-8");
341
805
  if (nameWithoutExt.includes(".")) {
342
806
  dotNotationName = `${nameWithoutExt}.vue`;
343
807
  } else {
344
- const rendererMatch = nameWithoutExt.match(dependencies.RE_RENDERER_SUFFIX);
808
+ const rendererMatch = nameWithoutExt.match(RE_RENDERER_SUFFIX);
345
809
  const renderer = rendererMatch?.[1]?.toLowerCase() || "satori";
346
- const baseName = nameWithoutExt.replace(dependencies.RE_RENDERER_SUFFIX, "");
810
+ const baseName = nameWithoutExt.replace(RE_RENDERER_SUFFIX, "");
347
811
  dotNotationName = `${baseName}.${renderer}.vue`;
348
812
  }
349
813
  const dir = pathe.join(nuxt.options.srcDir, "components", dirName || "");
@@ -1024,7 +1488,7 @@ async function downlevelColor(prop, value) {
1024
1488
  function convertIndividualTransforms(styles) {
1025
1489
  const parts = [];
1026
1490
  if (styles.translate) {
1027
- const vals = styles.translate.trim().split(dependencies.RE_WHITESPACE);
1491
+ const vals = styles.translate.trim().split(RE_WHITESPACE);
1028
1492
  parts.push(vals.length === 2 ? `translate(${vals[0]}, ${vals[1]})` : `translateX(${vals[0]})`);
1029
1493
  delete styles.translate;
1030
1494
  }
@@ -1033,7 +1497,7 @@ function convertIndividualTransforms(styles) {
1033
1497
  delete styles.rotate;
1034
1498
  }
1035
1499
  if (styles.scale) {
1036
- const vals = styles.scale.trim().split(dependencies.RE_WHITESPACE).filter(Boolean);
1500
+ const vals = styles.scale.trim().split(RE_WHITESPACE).filter(Boolean);
1037
1501
  parts.push(`scale(${vals.join(", ")})`);
1038
1502
  delete styles.scale;
1039
1503
  }
@@ -1666,7 +2130,7 @@ function resolveWeightsFromFontEntry(fontWeight, requestedWeights) {
1666
2130
  min = Number(fontWeight[0]);
1667
2131
  max = Number(fontWeight[1]);
1668
2132
  } else if (typeof fontWeight === "string") {
1669
- const parts = fontWeight.split(dependencies.RE_WHITESPACE).map(Number);
2133
+ const parts = fontWeight.split(RE_WHITESPACE).map(Number);
1670
2134
  if (parts.length >= 2 && !Number.isNaN(parts[0]) && !Number.isNaN(parts[1])) {
1671
2135
  min = parts[0];
1672
2136
  max = parts[1];
@@ -2005,7 +2469,7 @@ ${familyBreakdown}`);
2005
2469
 
2006
2470
  function setupGenerateHandler(_options, resolve, _getDetectedRenderers, nuxt = kit.useNuxt()) {
2007
2471
  nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
2008
- await dependencies.applyNitroPresetCompatibility(nitroConfig, {
2472
+ await applyNitroPresetCompatibility(nitroConfig, {
2009
2473
  compatibility: {
2010
2474
  browser: false,
2011
2475
  satori: false,
@@ -2022,7 +2486,7 @@ const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
2022
2486
  function setupPrerenderHandler(options, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = kit.useNuxt()) {
2023
2487
  nuxt.hooks.hook("nitro:init", async (nitro) => {
2024
2488
  nitro.hooks.hook("prerender:config", async (nitroConfig) => {
2025
- await dependencies.applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers(), metadata: getCompatibilityMeta() });
2489
+ await applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers(), metadata: getCompatibilityMeta() });
2026
2490
  nitroConfig.wasm = nitroConfig.wasm || {};
2027
2491
  nitroConfig.wasm.esmImport = false;
2028
2492
  nitroConfig.prerender = nitroConfig.prerender || {};
@@ -2233,7 +2697,7 @@ async function extractFontRequirementsFromVue(code) {
2233
2697
  const el = node;
2234
2698
  for (const prop of el.props) {
2235
2699
  if (prop.type === ATTRIBUTE_NODE$1 && prop.name === "class" && prop.value) {
2236
- for (const cls of prop.value.content.split(dependencies.RE_WHITESPACE)) {
2700
+ for (const cls of prop.value.content.split(RE_WHITESPACE)) {
2237
2701
  extractFontWeightFromClass(cls, weights, styles);
2238
2702
  extractFontFamilyFromClass(cls, familyClasses, familyNames);
2239
2703
  }
@@ -2560,7 +3024,7 @@ async function transformVueTemplate(code, options) {
2560
3024
  };
2561
3025
  for (const prop of el.props) {
2562
3026
  if (prop.type === ATTRIBUTE_NODE && prop.name === "class" && prop.value) {
2563
- collector.classes = prop.value.content.split(dependencies.RE_WHITESPACE).filter(Boolean);
3027
+ collector.classes = prop.value.content.split(RE_WHITESPACE).filter(Boolean);
2564
3028
  collector.classLoc = {
2565
3029
  start: prop.loc.start.offset,
2566
3030
  end: prop.loc.end.offset
@@ -3053,7 +3517,7 @@ const AssetTransformPlugin = unplugin.createUnplugin((options) => {
3053
3517
  const [fullMatch, , attrsStr, classValue] = elMatch;
3054
3518
  if (!classValue)
3055
3519
  continue;
3056
- const classes = classValue.split(dependencies.RE_WHITESPACE).filter(Boolean);
3520
+ const classes = classValue.split(RE_WHITESPACE).filter(Boolean);
3057
3521
  let iconPrefix;
3058
3522
  let iconName;
3059
3523
  for (const cls of classes) {
@@ -3272,7 +3736,7 @@ const AssetTransformPlugin = unplugin.createUnplugin((options) => {
3272
3736
  const classAttr = el.attributes?.class;
3273
3737
  if (!classAttr)
3274
3738
  return;
3275
- const elClasses = classAttr.split(dependencies.RE_WHITESPACE).filter(Boolean);
3739
+ const elClasses = classAttr.split(RE_WHITESPACE).filter(Boolean);
3276
3740
  const matchedStyles = {};
3277
3741
  const consumedClasses = /* @__PURE__ */ new Set();
3278
3742
  for (const cls of elClasses) {
@@ -3776,11 +4240,11 @@ async function onInstall(nuxt) {
3776
4240
  if (nuxt.options._prepare)
3777
4241
  return;
3778
4242
  if (stdEnv.isCI) {
3779
- const installedProviders2 = await dependencies.getInstalledProviders();
4243
+ const installedProviders2 = await getInstalledProviders();
3780
4244
  if (installedProviders2.length === 0) {
3781
4245
  throw new Error(
3782
4246
  `[nuxt-og-image] No OG image provider dependencies found. Install a provider before running in CI:
3783
- npm add ${dependencies.TAKUMI_CORE_INSTALL_SPEC} # for takumi
4247
+ npm add ${TAKUMI_CORE_PACKAGE} # for takumi
3784
4248
  npm add @resvg/resvg-js satori yoga-wasm-web # for satori
3785
4249
  npm add playwright-core # for browser
3786
4250
  See: https://nuxtseo.com/og-image/getting-started`
@@ -3788,7 +4252,7 @@ See: https://nuxtseo.com/og-image/getting-started`
3788
4252
  }
3789
4253
  return;
3790
4254
  }
3791
- if (!dependencies.canPromptInteractively()) {
4255
+ if (!canPromptInteractively()) {
3792
4256
  logger_js.logger.info("Skipping interactive onboarding (non-interactive environment). Defaulting to the takumi renderer.");
3793
4257
  return;
3794
4258
  }
@@ -3798,7 +4262,7 @@ See: https://nuxtseo.com/og-image/getting-started`
3798
4262
  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");
3799
4263
  logger_js.logger.info("");
3800
4264
  const state = {};
3801
- const installedProviders = await dependencies.getInstalledProviders();
4265
+ const installedProviders = await getInstalledProviders();
3802
4266
  if (installedProviders.length > 0) {
3803
4267
  logger_js.logger.info("Detected installed providers:");
3804
4268
  for (const { provider, binding } of installedProviders) {
@@ -3816,7 +4280,7 @@ See: https://nuxtseo.com/og-image/getting-started`
3816
4280
  if (!state.selectedRenderer) {
3817
4281
  const rendererChoice = await logger_js.logger.prompt("Select a renderer:", {
3818
4282
  type: "select",
3819
- options: dependencies.PROVIDER_DEPENDENCIES.map((p) => ({
4283
+ options: PROVIDER_DEPENDENCIES.map((p) => ({
3820
4284
  label: p.name,
3821
4285
  value: p.name,
3822
4286
  hint: p.description
@@ -3829,8 +4293,8 @@ See: https://nuxtseo.com/og-image/getting-started`
3829
4293
  state.selectedRenderer = rendererChoice;
3830
4294
  }
3831
4295
  if (!state.selectedBinding) {
3832
- const recommendedBinding = dependencies.getRecommendedBindingFromPreset(state.selectedRenderer);
3833
- const providerDef = dependencies.PROVIDER_DEPENDENCIES.find((p) => p.name === state.selectedRenderer);
4296
+ const recommendedBinding = getRecommendedBindingFromPreset(state.selectedRenderer);
4297
+ const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === state.selectedRenderer);
3834
4298
  const hasNodeBinding = !!providerDef?.bindings.node;
3835
4299
  const hasWasmBinding = !!providerDef?.bindings.wasm || !!providerDef?.bindings["wasm-fs"];
3836
4300
  if (hasNodeBinding && hasWasmBinding) {
@@ -3855,7 +4319,7 @@ See: https://nuxtseo.com/og-image/getting-started`
3855
4319
  state.selectedBinding = hasNodeBinding ? "node" : "wasm";
3856
4320
  }
3857
4321
  }
3858
- const missingInstallSpecs = await dependencies.getMissingDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
4322
+ const missingInstallSpecs = await getMissingDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
3859
4323
  if (missingInstallSpecs.length > 0) {
3860
4324
  logger_js.logger.info(`Required dependencies for ${state.selectedRenderer} (${state.selectedBinding}):`);
3861
4325
  for (const pkg of missingInstallSpecs) {
@@ -3866,7 +4330,7 @@ See: https://nuxtseo.com/og-image/getting-started`
3866
4330
  initial: true
3867
4331
  });
3868
4332
  if (installDeps) {
3869
- const result = await dependencies.ensureProviderDependencies(
4333
+ const result = await ensureProviderDependencies(
3870
4334
  state.selectedRenderer,
3871
4335
  state.selectedBinding,
3872
4336
  nuxt
@@ -3875,11 +4339,11 @@ See: https://nuxtseo.com/og-image/getting-started`
3875
4339
  logger_js.logger.success(`Installed: ${result.installed.join(", ")}`);
3876
4340
  } else {
3877
4341
  logger_js.logger.error("Failed to install some dependencies. Install manually:");
3878
- const allDeps = dependencies.getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
4342
+ const allDeps = getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
3879
4343
  logger_js.logger.info(` npm add ${allDeps.join(" ")}`);
3880
4344
  }
3881
4345
  } else {
3882
- const allDeps = dependencies.getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
4346
+ const allDeps = getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
3883
4347
  logger_js.logger.warn("Dependencies required. Install manually:");
3884
4348
  logger_js.logger.info(` npm add ${allDeps.join(" ")}`);
3885
4349
  }
@@ -3893,14 +4357,14 @@ See: https://nuxtseo.com/og-image/getting-started`
3893
4357
  if (createComponent) {
3894
4358
  await createStarterComponent(nuxt);
3895
4359
  }
3896
- const hasSharp = await dependencies.hasResolvableDependency("sharp");
4360
+ const hasSharp = await hasResolvableDependency("sharp");
3897
4361
  if (!hasSharp && state.selectedRenderer === "satori") {
3898
4362
  const wantJpeg = await logger_js.logger.prompt("Install sharp for JPEG output support?", {
3899
4363
  type: "confirm",
3900
4364
  initial: false
3901
4365
  });
3902
4366
  if (wantJpeg) {
3903
- await dependencies.ensureProviderDependencies("satori", "node", nuxt).catch(() => logger_js.logger.warn("Failed to install sharp, JPEG output unavailable"));
4367
+ await ensureProviderDependencies("satori", "node", nuxt).catch(() => logger_js.logger.warn("Failed to install sharp, JPEG output unavailable"));
3904
4368
  }
3905
4369
  }
3906
4370
  logger_js.logger.info("");
@@ -3935,7 +4399,7 @@ async function onUpgrade(nuxt, _options, previousVersion) {
3935
4399
  issues.push(`Config \`defaults.component\` is deprecated: Removed \u2014 rename component to OgImage/Default.{renderer}.vue`);
3936
4400
  }
3937
4401
  if ("fonts" in ogImageConfig && !nuxt.options._prepare && !stdEnv.isCI) {
3938
- await dependencies.promptFontsMigration(nuxt.options.rootDir);
4402
+ await fonts.promptFontsMigration(nuxt.options.rootDir);
3939
4403
  }
3940
4404
  }
3941
4405
  if (issues.length > 0) {
@@ -3952,17 +4416,17 @@ async function onUpgrade(nuxt, _options, previousVersion) {
3952
4416
  const ogComponentDir = pathe.join(nuxt.options.srcDir, "components", "OgImage");
3953
4417
  if (fs.existsSync(ogComponentDir)) {
3954
4418
  for (const file of fs.readdirSync(ogComponentDir).filter((f) => f.endsWith(".vue"))) {
3955
- const renderer = dependencies.getRendererFromFilename(file);
4419
+ const renderer = getRendererFromFilename(file);
3956
4420
  if (renderer)
3957
4421
  detectedRenderers.add(renderer);
3958
4422
  }
3959
4423
  }
3960
4424
  if (detectedRenderers.size === 0)
3961
4425
  detectedRenderers.add("satori");
3962
- const preset = dependencies.resolveOgImagePreset();
3963
- const compatibility = dependencies.getPresetNitroPresetCompatibility(preset);
4426
+ const preset = resolveOgImagePreset();
4427
+ const compatibility = getPresetNitroPresetCompatibility(preset);
3964
4428
  for (const renderer of detectedRenderers) {
3965
- const validation = await dependencies.validateProviderSetup(renderer, compatibility);
4429
+ const validation = await validateProviderSetup(renderer, compatibility);
3966
4430
  if (!validation.valid) {
3967
4431
  logger_js.logger.warn(`Provider setup issues for ${renderer}:`);
3968
4432
  for (const issue of validation.issues) {
@@ -4021,7 +4485,7 @@ function registerTypeTemplates(ctx) {
4021
4485
  const sortedDirs = config.componentDirs.sort((a, b) => b.length - a.length);
4022
4486
  const baseNameRenderers = /* @__PURE__ */ new Map();
4023
4487
  for (const component of componentCtx.components) {
4024
- for (const baseName of dependencies.getRegisteredBaseNames(component.pascalName)) {
4488
+ for (const baseName of getRegisteredBaseNames(component.pascalName)) {
4025
4489
  const renderers = baseNameRenderers.get(baseName) || [];
4026
4490
  renderers.push(component.renderer);
4027
4491
  baseNameRenderers.set(baseName, renderers);
@@ -4032,7 +4496,7 @@ function registerTypeTemplates(ctx) {
4032
4496
  const importType = getComponentTypeExpression(component, nuxt);
4033
4497
  const strippedName = sortedDirs.reduce((n, dir) => n.replace(new RegExp(`^${dir}`), ""), component.pascalName);
4034
4498
  const renderer = component.renderer;
4035
- const baseNames = dependencies.getRegisteredBaseNames(component.pascalName);
4499
+ const baseNames = getRegisteredBaseNames(component.pascalName);
4036
4500
  lines.push(` '${strippedName}': ${importType}`);
4037
4501
  const seen = /* @__PURE__ */ new Set([strippedName]);
4038
4502
  for (const baseName of baseNames) {
@@ -4094,6 +4558,15 @@ declare module '#og-image/font-requirements' {
4094
4558
  export const hasNuxtFonts: boolean
4095
4559
  }
4096
4560
 
4561
+ declare module '#og-image/island-hash' {
4562
+ export function getIslandHash(input: {
4563
+ name: string
4564
+ props?: Record<string, any> | string | null
4565
+ context?: Record<string, any>
4566
+ source?: string
4567
+ }): string
4568
+ }
4569
+
4097
4570
  declare module '#og-image-virtual/unocss-config.mjs' {
4098
4571
  export const theme: Record<string, any>
4099
4572
  }
@@ -4317,7 +4790,7 @@ const module$1 = kit.defineNuxtModule({
4317
4790
  await onUpgrade(nuxt, options, previousVersion);
4318
4791
  },
4319
4792
  async setup(config, nuxt) {
4320
- 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.BCK8Adm4.cjs', document.baseURI).href)));
4793
+ 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.BRBtdkUp.cjs', document.baseURI).href)));
4321
4794
  const fixSharedPath = (p) => {
4322
4795
  return p.replace(/\/shared\/runtime(\/|$)/, "/runtime$1").replace(/\/shared\/client(\/|$)/, "/client$1").replace(/\/shared\/devtools(\/|$)/, "/devtools$1");
4323
4796
  };
@@ -4409,10 +4882,10 @@ Or pin to a specific version in package.json:
4409
4882
  }
4410
4883
  nuxt.options.alias["#og-image"] = resolve("./runtime");
4411
4884
  nuxt.options.alias["#og-image-cache"] = resolve("./runtime/server/og-image/cache/lru");
4412
- const preset = dependencies.resolveOgImagePreset(nuxt.options.nitro);
4413
- const targetCompatibility = dependencies.getPresetNitroPresetCompatibility(preset);
4885
+ const preset = resolveOgImagePreset(nuxt.options.nitro);
4886
+ const targetCompatibility = getPresetNitroPresetCompatibility(preset);
4414
4887
  const runtimeCompatibilityMeta = {};
4415
- const normalizedPreset = preset.replace(dependencies.RE_LEGACY_SUFFIX, "");
4888
+ const normalizedPreset = preset.replace(RE_LEGACY_SUFFIX, "");
4416
4889
  const isCloudflareWorkers = ["cloudflare", "cloudflare-module", "cloudflare-durable"].includes(normalizedPreset);
4417
4890
  if (isCloudflareWorkers) {
4418
4891
  nuxt.hooks.hook("nitro:init", (nitro) => {
@@ -4683,7 +5156,7 @@ ${familyInfo}`);
4683
5156
  }
4684
5157
  } else if (hasConfiguredJpegs) {
4685
5158
  logger_js.logger.warn("You have enabled `JPEG` images. These require the `sharp` dependency which is missing, installing it for you.");
4686
- await dependencies.ensureDependencies(["sharp"]);
5159
+ await ensureDependencies(["sharp"]);
4687
5160
  logger_js.logger.warn("Support for `sharp` is limited so check the compatibility guide.");
4688
5161
  attemptSharpUsage = true;
4689
5162
  }
@@ -4697,8 +5170,8 @@ ${familyInfo}`);
4697
5170
  }
4698
5171
  }
4699
5172
  if (isProviderEnabledForEnv("browser", nuxt, config)) {
4700
- const hasChromeLocally = dependencies.checkLocalChrome();
4701
- const hasPlaywrightDependency = await dependencies.hasResolvableDependency("playwright");
5173
+ const hasChromeLocally = checkLocalChrome();
5174
+ const hasPlaywrightDependency = await hasResolvableDependency("playwright");
4702
5175
  const browserCompatibilityFlags = {
4703
5176
  prerender: config.compatibility?.prerender?.browser,
4704
5177
  dev: config.compatibility?.dev?.browser,
@@ -4715,7 +5188,7 @@ ${familyInfo}`);
4715
5188
  if (!browserConfig.binding) {
4716
5189
  throw new Error("[nuxt-og-image] `ogImage.browser.binding` is required when provider is cloudflare");
4717
5190
  }
4718
- const hasCloudfarePuppeteer = await dependencies.hasResolvableDependency("@cloudflare/puppeteer");
5191
+ const hasCloudfarePuppeteer = await hasResolvableDependency("@cloudflare/puppeteer");
4719
5192
  if (!hasCloudfarePuppeteer) {
4720
5193
  throw new Error(
4721
5194
  "[nuxt-og-image] Missing @cloudflare/puppeteer dependency. Install it with: pnpm add @cloudflare/puppeteer"
@@ -4725,12 +5198,12 @@ ${familyInfo}`);
4725
5198
  browserBinding.prerender = hasPlaywrightDependency ? "playwright" : hasChromeLocally ? "chrome-launcher" : "on-demand";
4726
5199
  browserBinding.runtime = "cloudflare";
4727
5200
  } else if (nuxt.options.dev) {
4728
- if (dependencies.isUndefinedOrTruthy(browserCompatibilityFlags.dev))
5201
+ if (isUndefinedOrTruthy(browserCompatibilityFlags.dev))
4729
5202
  browserBinding.dev = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
4730
5203
  } else {
4731
- if (dependencies.isUndefinedOrTruthy(browserCompatibilityFlags.prerender))
5204
+ if (isUndefinedOrTruthy(browserCompatibilityFlags.prerender))
4732
5205
  browserBinding.prerender = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
4733
- if (dependencies.isUndefinedOrTruthy(browserCompatibilityFlags.runtime))
5206
+ if (isUndefinedOrTruthy(browserCompatibilityFlags.runtime))
4734
5207
  browserBinding.runtime = hasPlaywrightDependency ? "playwright" : null;
4735
5208
  }
4736
5209
  config.compatibility = defu.defu(config.compatibility, {
@@ -4739,7 +5212,7 @@ ${familyInfo}`);
4739
5212
  prerender: { browser: browserBinding.prerender }
4740
5213
  });
4741
5214
  }
4742
- await kit$1.installNuxtSiteConfig();
5215
+ await kit$2.installNuxtSiteConfig();
4743
5216
  nuxt.options.experimental.componentIslands ||= true;
4744
5217
  if (config.debug || nuxt.options.dev) {
4745
5218
  kit.addServerHandler({
@@ -4852,7 +5325,7 @@ ${familyInfo}`);
4852
5325
  if (isUserDir && fs__namespace.existsSync(resolvedPath)) {
4853
5326
  const files = fs__namespace.readdirSync(resolvedPath).filter((f) => f.endsWith(".vue"));
4854
5327
  for (const file of files) {
4855
- const renderer = dependencies.getRendererFromFilename(file);
5328
+ const renderer = getRendererFromFilename(file);
4856
5329
  if (renderer) {
4857
5330
  ogImageComponentCtx.detectedRenderers.add(renderer);
4858
5331
  hasUserComponents = true;
@@ -4861,32 +5334,32 @@ ${familyInfo}`);
4861
5334
  }
4862
5335
  }
4863
5336
  if (!nuxt.options._prepare && !hasUserComponents) {
4864
- const installedProviders = await dependencies.getInstalledProviders();
5337
+ const installedProviders = await getInstalledProviders();
4865
5338
  const preferred = installedProviders.find((p) => p.provider === "takumi") || installedProviders[0];
4866
5339
  if (preferred) {
4867
5340
  ogImageComponentCtx.detectedRenderers.add(preferred.provider);
4868
5341
  logger_js.logger.debug(`Using ${preferred.provider} renderer`);
4869
- } else if (nuxt.options.dev && !nuxt.options._prepare && dependencies.canPromptInteractively()) {
4870
- const renderer = await dependencies.promptForRendererSelection();
5342
+ } else if (nuxt.options.dev && !nuxt.options._prepare && canPromptInteractively()) {
5343
+ const renderer = await promptForRendererSelection();
4871
5344
  ogImageComponentCtx.detectedRenderers.add(renderer);
4872
5345
  logger_js.logger.debug(`Using ${renderer} renderer`);
4873
5346
  } else {
4874
5347
  ogImageComponentCtx.detectedRenderers.add("takumi");
4875
- logger_js.logger.warn(`No OG image renderer dependency detected. Defaulting to \`takumi\` (non-interactive environment). Install \`${dependencies.TAKUMI_CORE_INSTALL_SPEC}\`, or add a renderer component (e.g. components/OgImage/Default.satori.vue) to choose explicitly.`);
5348
+ logger_js.logger.warn(`No OG image renderer dependency detected. Defaulting to \`takumi\` (non-interactive environment). Install \`${TAKUMI_CORE_PACKAGE}\`, or add a renderer component (e.g. components/OgImage/Default.satori.vue) to choose explicitly.`);
4876
5349
  }
4877
5350
  }
4878
5351
  const availableRenderers = /* @__PURE__ */ new Set();
4879
5352
  if (!nuxt.options._prepare) {
4880
5353
  if (!config.zeroRuntime) {
4881
5354
  for (const renderer of ogImageComponentCtx.detectedRenderers) {
4882
- const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
4883
- const missing = await dependencies.getMissingDependencies(renderer, binding);
5355
+ const binding = getRecommendedBinding(renderer, targetCompatibility);
5356
+ const missing = await getMissingDependencies(renderer, binding);
4884
5357
  if (missing.length === 0) {
4885
5358
  availableRenderers.add(renderer);
4886
5359
  } else if (nuxt.options.dev && !nuxt.options._prepare) {
4887
- const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
5360
+ const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
4888
5361
  logger_js.logger.warn(`${renderer} renderer requires: ${installSpecs.join(", ")}`);
4889
- const { success } = await dependencies.ensureProviderDependencies(renderer, binding, nuxt);
5362
+ const { success } = await ensureProviderDependencies(renderer, binding, nuxt);
4890
5363
  if (success) {
4891
5364
  availableRenderers.add(renderer);
4892
5365
  } else if (stdEnv.isAgent) {
@@ -4895,10 +5368,10 @@ ${familyInfo}`);
4895
5368
  logger_js.logger.error(`Failed to install ${renderer} dependencies. Templates using this renderer won't work.`);
4896
5369
  }
4897
5370
  } else if (stdEnv.isAgent) {
4898
- const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
5371
+ const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
4899
5372
  throw new Error(`[nuxt-og-image] ${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
4900
5373
  } else {
4901
- const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
5374
+ const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
4902
5375
  logger_js.logger.error(`${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
4903
5376
  }
4904
5377
  if (renderer === "satori" && availableRenderers.has(renderer) && binding !== "node") {
@@ -4921,7 +5394,7 @@ ${familyInfo}`);
4921
5394
  }
4922
5395
  runtimeCompatibilityMeta.takumiVersion = await detectTakumiVersion(
4923
5396
  resolver,
4924
- dependencies.getRecommendedBinding("takumi", targetCompatibility)
5397
+ getRecommendedBinding("takumi", targetCompatibility)
4925
5398
  );
4926
5399
  if (ogImageComponentCtx.detectedRenderers.has("takumi") && !runtimeCompatibilityMeta.takumiVersion) {
4927
5400
  logger_js.logger.warn("Unable to detect @takumi-rs version. Falling back to Takumi v1 renderer compatibility.");
@@ -4930,8 +5403,8 @@ ${familyInfo}`);
4930
5403
  if (nuxt.options.dev) {
4931
5404
  const communityRenderers = /* @__PURE__ */ new Set();
4932
5405
  for (const renderer of ["satori", "takumi", "browser"]) {
4933
- const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
4934
- const missing = await dependencies.getMissingDependencies(renderer, binding);
5406
+ const binding = getRecommendedBinding(renderer, targetCompatibility);
5407
+ const missing = await getMissingDependencies(renderer, binding);
4935
5408
  if (missing.length === 0)
4936
5409
  communityRenderers.add(renderer);
4937
5410
  }
@@ -4962,7 +5435,7 @@ ${familyInfo}`);
4962
5435
  if (valid && fs__namespace.existsSync(component.filePath)) {
4963
5436
  if (!component.filePath.endsWith(".vue"))
4964
5437
  return;
4965
- const renderer = dependencies.getRendererFromFilename(component.filePath);
5438
+ const renderer = getRendererFromFilename(component.filePath);
4966
5439
  if (!renderer) {
4967
5440
  invalidComponents.push(component.filePath);
4968
5441
  return;
@@ -4978,7 +5451,7 @@ ${familyInfo}`);
4978
5451
  const credits = componentFile.split("\n").find((line) => line.startsWith(" * @credits"))?.replace("* @credits", "").trim();
4979
5452
  const propNames = extractPropNamesFromVue(componentFile);
4980
5453
  ogImageComponentCtx.components.push({
4981
- hash: ohash.hash(componentFile).replaceAll("_", "-"),
5454
+ hash: fnv1a64.fnv1a64Base36(componentFile),
4982
5455
  pascalName: component.pascalName,
4983
5456
  kebabName: component.kebabName,
4984
5457
  path: component.filePath,
@@ -4993,18 +5466,18 @@ ${familyInfo}`);
4993
5466
  const communityDir = resolve("./runtime/app/components/Templates/Community");
4994
5467
  if (fs__namespace.existsSync(communityDir)) {
4995
5468
  fs__namespace.readdirSync(communityDir).filter((f) => f.endsWith(".vue")).forEach((file) => {
4996
- const renderer = dependencies.getRendererFromFilename(file);
5469
+ const renderer = getRendererFromFilename(file);
4997
5470
  if (!renderer)
4998
5471
  return;
4999
5472
  const pascalName = file.replace(".vue", "").split(".").map((s, i) => i === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1)).join("");
5000
5473
  const filePath = resolve(communityDir);
5001
- const communityBaseNames = dependencies.getRegisteredBaseNames(pascalName);
5474
+ const communityBaseNames = getRegisteredBaseNames(pascalName);
5002
5475
  if (ogImageComponentCtx.components.some((c) => {
5003
5476
  if (c.pascalName === pascalName)
5004
5477
  return true;
5005
5478
  if (c.renderer !== renderer)
5006
5479
  return false;
5007
- const existingBaseNames = dependencies.getRegisteredBaseNames(c.pascalName);
5480
+ const existingBaseNames = getRegisteredBaseNames(c.pascalName);
5008
5481
  return communityBaseNames.some((cb) => existingBaseNames.includes(cb));
5009
5482
  })) {
5010
5483
  return;
@@ -5025,10 +5498,10 @@ ${familyInfo}`);
5025
5498
  if (nuxt.options.dev) {
5026
5499
  for (const renderer of ogImageComponentCtx.detectedRenderers) {
5027
5500
  if (!availableRenderers.has(renderer)) {
5028
- const binding = dependencies.getRecommendedBinding(renderer, targetCompatibility);
5029
- dependencies.getMissingDependencies(renderer, binding).then(async (missing) => {
5501
+ const binding = getRecommendedBinding(renderer, targetCompatibility);
5502
+ getMissingDependencies(renderer, binding).then(async (missing) => {
5030
5503
  if (missing.length > 0) {
5031
- const installSpecs = await dependencies.getMissingDependencyInstallSpecs(renderer, binding);
5504
+ const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
5032
5505
  logger_js.logger.warn(`New ${renderer} component detected but dependencies missing: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
5033
5506
  }
5034
5507
  });
@@ -5063,6 +5536,22 @@ Run: npx nuxt-og-image migrate v6`;
5063
5536
  nuxt.options.nitro.virtual["#og-image-virtual/component-names.mjs"] = () => {
5064
5537
  return `export const componentNames = ${JSON.stringify(ogImageComponentCtx.components)}`;
5065
5538
  };
5539
+ nuxt.options.nitro.virtual["#og-image/island-hash"] = () => {
5540
+ const islandHashFile = pathe.join(nuxt.options.appDir, "island-hash.js");
5541
+ const [nuxtMajor = 0, nuxtMinor = 0] = kit.getNuxtVersion(nuxt).split(".").map((v) => Number.parseInt(v));
5542
+ if (nuxtMajor > 4 || nuxtMajor === 4 && nuxtMinor >= 5)
5543
+ return `export { getIslandHash } from '${islandHashFile}'`;
5544
+ if (fs.existsSync(islandHashFile)) {
5545
+ return `import { computeIslandHash } from '${islandHashFile}'
5546
+ export function getIslandHash({ name, props, context, source }) {
5547
+ return computeIslandHash(name, props ?? {}, context ?? {}, source)
5548
+ }`;
5549
+ }
5550
+ return `import { hash } from 'ohash'
5551
+ export function getIslandHash({ name, props, context, source }) {
5552
+ return hash([name, props ?? {}, context ?? {}, source]).replace(/[-_]/g, '')
5553
+ }`;
5554
+ };
5066
5555
  nuxt.options.nitro.publicAssets ||= [];
5067
5556
  const staticFontsTtfDir = pathe.join(nuxt.options.buildDir, "cache", "og-image", "fonts-ttf");
5068
5557
  nuxt.options.nitro.publicAssets.push({
@@ -5076,7 +5565,7 @@ Run: npx nuxt-og-image migrate v6`;
5076
5565
  maxAge: 60 * 60 * 24 * 365
5077
5566
  });
5078
5567
  nuxt.options.nitro.virtual["#og-image-virtual/public-assets.mjs"] = async () => {
5079
- const normalizedPreset2 = preset.replace(dependencies.RE_LEGACY_SUFFIX, "");
5568
+ const normalizedPreset2 = preset.replace(RE_LEGACY_SUFFIX, "");
5080
5569
  const isCloudflare = normalizedPreset2 === "cloudflare" || normalizedPreset2.startsWith("cloudflare-");
5081
5570
  if (isCloudflare) {
5082
5571
  const devBinding2 = resolver.resolve("./runtime/server/og-image/bindings/font-assets/dev-prerender");
@@ -5232,7 +5721,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5232
5721
  const buildCacheDir = config.buildCache ? pathe.join(nuxt.options.rootDir, buildCachePath) : void 0;
5233
5722
  nuxt.hooks.hook("modules:done", async () => {
5234
5723
  const hasColorModeModule = kit.hasNuxtModule("@nuxtjs/color-mode");
5235
- const colorModeOptions = hasColorModeModule ? await kit$2.getNuxtModuleOptions("@nuxtjs/color-mode") : {};
5724
+ const colorModeOptions = hasColorModeModule ? await kit$1.getNuxtModuleOptions("@nuxtjs/color-mode") : {};
5236
5725
  let colorPreference = colorModeOptions.preference;
5237
5726
  if (!colorPreference || colorPreference === "system")
5238
5727
  colorPreference = colorModeOptions.fallback;
@@ -5336,7 +5825,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
5336
5825
  await nitro.hooks.callHook("rollup:reload");
5337
5826
  }
5338
5827
  });
5339
- } else if (kit$2.isNuxtGenerate()) {
5828
+ } else if (kit$1.isNuxtGenerate()) {
5340
5829
  setupGenerateHandler(config, resolver);
5341
5830
  } else if (nuxt.options.build) {
5342
5831
  await setupBuildHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);