nuxt-og-image 6.5.3 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/chunks/tw4.cjs +7 -7
- package/dist/chunks/tw4.mjs +7 -7
- package/dist/chunks/uno.cjs +6 -6
- package/dist/chunks/uno.mjs +6 -6
- package/dist/cli.cjs +12 -5
- package/dist/cli.mjs +10 -3
- package/dist/devtools/components/og-image/AddComponentDialog.vue +85 -0
- package/dist/devtools/components/og-image/BlueskyCardRenderer.vue +134 -0
- package/dist/devtools/components/og-image/CreateOgImageDialog.vue +56 -0
- package/dist/devtools/components/og-image/DiscordCardRenderer.vue +125 -0
- package/dist/devtools/components/og-image/FacebookCardRenderer.vue +128 -0
- package/dist/devtools/components/og-image/IFrameLoader.vue +93 -0
- package/dist/devtools/components/og-image/ImageLoader.vue +197 -0
- package/dist/devtools/components/og-image/LinkedInCardRenderer.vue +100 -0
- package/dist/devtools/components/og-image/RendererSelectModal.vue +356 -0
- package/dist/devtools/components/og-image/SlackCardRenderer.vue +140 -0
- package/dist/devtools/components/og-image/TemplateComponentPreview.vue +186 -0
- package/dist/devtools/components/og-image/TwitterCardRenderer.vue +170 -0
- package/dist/devtools/components/og-image/WhatsAppRenderer.vue +294 -0
- package/dist/devtools/lib/og-image/keys.ts +8 -0
- package/dist/devtools/lib/og-image/og-image.ts +536 -0
- package/dist/devtools/lib/og-image/renderer-select.ts +3 -0
- package/dist/devtools/lib/og-image/rpc-types.ts +2 -0
- package/dist/devtools/lib/og-image/rpc.ts +73 -0
- package/dist/devtools/lib/og-image/runtime-types.ts +10 -0
- package/dist/devtools/lib/og-image/shared/urlEncoding.ts +502 -0
- package/dist/devtools/lib/og-image/shared.ts +30 -0
- package/dist/devtools/lib/og-image/templates.ts +9 -0
- package/dist/devtools/lib/og-image/types.ts +38 -0
- package/dist/devtools/lib/og-image/util/logic.ts +21 -0
- package/dist/devtools/nuxt.config.ts +6 -0
- package/dist/devtools/pages/og-image/debug.vue +32 -0
- package/dist/devtools/pages/og-image/docs.vue +3 -0
- package/dist/devtools/pages/og-image/index.vue +1682 -0
- package/dist/devtools/pages/og-image/templates.vue +150 -0
- package/dist/devtools/pages/og-image.vue +184 -0
- package/dist/module.cjs +5 -5
- package/dist/module.d.cts +5 -3
- package/dist/module.d.mts +5 -3
- package/dist/module.d.ts +5 -3
- package/dist/module.json +1 -1
- package/dist/module.mjs +5 -5
- package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/font-assets/cloudflare.js +6 -1
- package/dist/runtime/server/og-image/bindings/font-assets/dev-prerender.js +13 -6
- package/dist/runtime/server/og-image/bindings/font-assets/external-url.d.ts +36 -0
- package/dist/runtime/server/og-image/bindings/font-assets/external-url.js +47 -0
- package/dist/runtime/server/og-image/bindings/font-assets/node.d.ts +1 -1
- package/dist/runtime/server/og-image/bindings/font-assets/node.js +14 -7
- package/dist/runtime/server/og-image/bindings/takumi/node-dev.js +7 -1
- package/dist/runtime/server/og-image/core/plugins/imageSrc.js +2 -2
- package/dist/runtime/server/og-image/fonts.d.ts +4 -0
- package/dist/runtime/server/og-image/fonts.js +21 -5
- package/dist/runtime/server/og-image/satori/instances.d.ts +1 -2
- package/dist/runtime/server/og-image/takumi/renderer.js +61 -19
- package/dist/runtime/server/util/ssrf.d.ts +10 -0
- package/dist/runtime/server/util/ssrf.js +9 -1
- package/dist/runtime/types.d.ts +13 -0
- package/dist/shared/{nuxt-og-image.DdbTs-xp.mjs → nuxt-og-image.CPSxZhVO.mjs} +122 -496
- package/dist/shared/nuxt-og-image.DM30kcDz.mjs +660 -0
- package/dist/shared/nuxt-og-image.RI9DgjgV.cjs +689 -0
- package/dist/shared/{nuxt-og-image.CfTPCtaS.cjs → nuxt-og-image.yDJBWXBt.cjs} +181 -555
- package/package.json +48 -45
- package/dist/devtools/200.html +0 -1
- package/dist/devtools/404.html +0 -1
- package/dist/devtools/_fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 +0 -0
- package/dist/devtools/_fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 +0 -0
- package/dist/devtools/_fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 +0 -0
- package/dist/devtools/_fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 +0 -0
- package/dist/devtools/_fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 +0 -0
- package/dist/devtools/_fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 +0 -0
- package/dist/devtools/_nuxt/BZ_CG9ll.js +0 -30
- package/dist/devtools/_nuxt/BdMalyEU.js +0 -4
- package/dist/devtools/_nuxt/BhkGaWEe.js +0 -3
- package/dist/devtools/_nuxt/Bm4Q-nTW.js +0 -3
- package/dist/devtools/_nuxt/BsRKQH5X.js +0 -1
- package/dist/devtools/_nuxt/C5797Ieg.js +0 -1
- package/dist/devtools/_nuxt/CBcJg2GG.js +0 -2
- package/dist/devtools/_nuxt/CDaXKRRu.js +0 -1
- package/dist/devtools/_nuxt/CM-t-6ZT.js +0 -1
- package/dist/devtools/_nuxt/CP0tQR2M.js +0 -1
- package/dist/devtools/_nuxt/CXAM78_A.js +0 -1
- package/dist/devtools/_nuxt/CYDI7SLa.js +0 -1
- package/dist/devtools/_nuxt/CkbDfutK.js +0 -152
- package/dist/devtools/_nuxt/Co8Tk_C2.js +0 -1
- package/dist/devtools/_nuxt/CqjbkMN9.js +0 -3
- package/dist/devtools/_nuxt/D49I50CU.js +0 -1
- package/dist/devtools/_nuxt/DevtoolsSection.DkT2bJJh.css +0 -1
- package/dist/devtools/_nuxt/DevtoolsSnippet.zV_SDojn.css +0 -1
- package/dist/devtools/_nuxt/HWyD49jH.js +0 -1
- package/dist/devtools/_nuxt/IFrameLoader.k_861Nnq.css +0 -1
- package/dist/devtools/_nuxt/PONEy9N-.js +0 -1
- package/dist/devtools/_nuxt/builds/latest.json +0 -1
- package/dist/devtools/_nuxt/builds/meta/0f3dbc15-8308-4956-81b9-5507cac7018c.json +0 -1
- package/dist/devtools/_nuxt/entry.D7u2asd2.css +0 -2
- package/dist/devtools/_nuxt/fira-code.Bc8wnsZt.woff2 +0 -0
- package/dist/devtools/_nuxt/hubot-sans.DLGyhQVu.woff2 +0 -0
- package/dist/devtools/_nuxt/pO9PmtPW.js +0 -6
- package/dist/devtools/_nuxt/pages.0Jk10xlb.css +0 -1
- package/dist/devtools/_nuxt/renderer-select.CN5IWe60.css +0 -1
- package/dist/devtools/_nuxt/templates.BByln3BG.css +0 -1
- package/dist/devtools/debug/index.html +0 -1
- package/dist/devtools/docs/index.html +0 -1
- package/dist/devtools/index.html +0 -1
- package/dist/devtools/templates/index.html +0 -1
- package/dist/shared/nuxt-og-image.CJa2KCie.mjs +0 -189
- package/dist/shared/nuxt-og-image.CMYbz66P.cjs +0 -193
|
@@ -1,23 +1,21 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
1
2
|
import * as fs from 'node:fs';
|
|
2
3
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, rmSync } from 'node:fs';
|
|
3
4
|
import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import {
|
|
6
|
+
import { useNuxt, resolvePath, updateTemplates, addTypeTemplate, addTemplate, defineNuxtModule, createResolver, addImports, hasNuxtModule, hasNuxtModuleCompatibility, getNuxtModuleVersion, addVitePlugin, addBuildPlugin, addServerHandler, addPlugin, addComponentsDir, addServerPlugin } from '@nuxt/kit';
|
|
6
7
|
import { defu } from 'defu';
|
|
7
8
|
import { installNuxtSiteConfig } from 'nuxt-site-config/kit';
|
|
8
9
|
import { hash } from 'ohash';
|
|
9
|
-
import {
|
|
10
|
+
import { join, dirname, relative, isAbsolute, resolve, basename } from 'pathe';
|
|
10
11
|
import { readPackageJSON } from 'pkg-types';
|
|
11
|
-
import { isCI, isAgent
|
|
12
|
-
import { createHash } from 'node:crypto';
|
|
12
|
+
import { isCI, isAgent } from 'std-env';
|
|
13
13
|
import { parseAndWalk } from 'oxc-walker';
|
|
14
|
-
import {
|
|
15
|
-
import { logger } from '../../dist/runtime/logger.js';
|
|
16
|
-
import { Launcher } from 'chrome-launcher';
|
|
17
|
-
import { detectPackageManager, addDependency } from 'nypm';
|
|
14
|
+
import { c as applyNitroPresetCompatibility, r as resolveOgImagePreset, R as RE_LEGACY_SUFFIX, g as getPresetNitroPresetCompatibility, d as getRecommendedBinding, e as getMissingDependencies, f as RE_RENDERER_SUFFIX, h as RE_WHITESPACE, p as promptFontsMigration, i as getRendererFromFilename, v as validateProviderSetup, j as getInstalledProviders, T as TAKUMI_CORE_INSTALL_SPEC, k as canPromptInteractively, P as PROVIDER_DEPENDENCIES, l as getRecommendedBindingFromPreset, n as getMissingDependencyInstallSpecs, o as ensureProviderDependencies, q as getProviderDependencyInstallSpecs, s as hasResolvableDependency, t as getRegisteredBaseNames, u as ensureDependencies, w as checkLocalChrome, x as promptForRendererSelection, y as isUndefinedOrTruthy } from './nuxt-og-image.DM30kcDz.mjs';
|
|
18
15
|
import { setupDevToolsUI as setupDevToolsUI$1, setupDevToolsRpc } from 'nuxtseo-shared/devtools';
|
|
19
16
|
import { createStorage } from 'unstorage';
|
|
20
17
|
import fsDriver from 'unstorage/drivers/fs-lite';
|
|
18
|
+
import { logger } from '../../dist/runtime/logger.js';
|
|
21
19
|
import { resolveColorMix, GRADIENT_COLOR_SPACE_RE, splitCssDeclarations } from '../../dist/runtime/server/og-image/utils/css.js';
|
|
22
20
|
import MagicString from 'magic-string';
|
|
23
21
|
import { stripLiteral } from 'strip-literal';
|
|
@@ -28,281 +26,11 @@ import { ofetch } from 'ofetch';
|
|
|
28
26
|
import { parse as parse$1, walkSync, ELEMENT_NODE as ELEMENT_NODE$2, renderSync } from 'ultrahtml';
|
|
29
27
|
import { RE_MATCH_EMOJIS, getEmojiCodePoint, getEmojiIconNames } from '../../dist/runtime/server/og-image/core/transforms/emojis/emoji-utils.js';
|
|
30
28
|
import { resolveModulePath } from 'exsolve';
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
const isUndefinedOrTruthy = (v) => typeof v === "undefined" || v !== false;
|
|
34
|
-
function checkLocalChrome() {
|
|
35
|
-
if (isCI)
|
|
36
|
-
return false;
|
|
37
|
-
let hasChromeLocally = false;
|
|
38
|
-
try {
|
|
39
|
-
hasChromeLocally = !!Launcher.getFirstInstallation();
|
|
40
|
-
} catch {
|
|
41
|
-
}
|
|
42
|
-
return hasChromeLocally;
|
|
43
|
-
}
|
|
44
|
-
async function hasResolvableDependency(dep) {
|
|
45
|
-
return await resolvePath(dep, { fallbackToOriginal: true }).catch(() => null).then((r) => r && r !== dep);
|
|
46
|
-
}
|
|
47
|
-
const VALID_RENDERER_SUFFIXES = ["satori", "browser", "takumi"];
|
|
48
|
-
const RE_RENDERER_SUFFIX_CI = /\.?(satori|browser|takumi)$/i;
|
|
49
|
-
const RE_RENDERER_SUFFIX = /(Satori|Browser|Takumi)$/;
|
|
50
|
-
const RE_LEGACY_SUFFIX = /-legacy$/;
|
|
51
|
-
const RE_WHITESPACE = /\s+/;
|
|
52
|
-
function getRendererFromFilename(filepath) {
|
|
53
|
-
const filename = basename(filepath).replace(".vue", "");
|
|
54
|
-
for (const suffix of VALID_RENDERER_SUFFIXES) {
|
|
55
|
-
if (filename.endsWith(`.${suffix}`))
|
|
56
|
-
return suffix;
|
|
57
|
-
}
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const OGIMAGE_PREFIXES = [
|
|
61
|
-
{ prefix: "OgImageCommunity", overlapWord: "Community" },
|
|
62
|
-
{ prefix: "OgImageTemplate", overlapWord: "Template" },
|
|
63
|
-
{ prefix: "OgImage", overlapWord: "Image" }
|
|
64
|
-
];
|
|
65
|
-
function getRegisteredBaseNames(registeredPascalName) {
|
|
66
|
-
const stripped = registeredPascalName.replace(RE_RENDERER_SUFFIX_CI, "").replace(RE_RENDERER_SUFFIX, "");
|
|
67
|
-
const names = [];
|
|
68
|
-
for (const { prefix, overlapWord } of OGIMAGE_PREFIXES) {
|
|
69
|
-
if (!stripped.startsWith(prefix))
|
|
70
|
-
continue;
|
|
71
|
-
const withoutPrefix = stripped.slice(prefix.length);
|
|
72
|
-
if (withoutPrefix) {
|
|
73
|
-
names.push(withoutPrefix);
|
|
74
|
-
if (withoutPrefix !== overlapWord)
|
|
75
|
-
names.push(overlapWord + withoutPrefix);
|
|
76
|
-
} else {
|
|
77
|
-
names.push(overlapWord);
|
|
78
|
-
}
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
if (names.length === 0)
|
|
82
|
-
names.push(stripped);
|
|
83
|
-
return names;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const NodeRuntime = {
|
|
87
|
-
// node-server runtime
|
|
88
|
-
browser: "on-demand",
|
|
89
|
-
// this gets changed build start
|
|
90
|
-
resvg: "node",
|
|
91
|
-
satori: "node",
|
|
92
|
-
takumi: "node",
|
|
93
|
-
sharp: "node",
|
|
94
|
-
// will be disabled if they're missing the dependency
|
|
95
|
-
emoji: "local"
|
|
96
|
-
// can bundle icons, no size constraints
|
|
97
|
-
};
|
|
98
|
-
const NodeDevRuntime = {
|
|
99
|
-
...NodeRuntime,
|
|
100
|
-
resvg: "node-dev",
|
|
101
|
-
// use worker to prevent crashes from killing process
|
|
102
|
-
takumi: "node-dev"
|
|
103
|
-
// use worker to prevent crashes from killing process
|
|
104
|
-
};
|
|
105
|
-
const NodePrerenderRuntime = {
|
|
106
|
-
...NodeRuntime,
|
|
107
|
-
resvg: "node-dev",
|
|
108
|
-
// use worker to prevent crashes from killing prerender process
|
|
109
|
-
takumi: "node-dev"
|
|
110
|
-
// use worker to prevent crashes from killing prerender process
|
|
111
|
-
};
|
|
112
|
-
const cloudflare = {
|
|
113
|
-
browser: "cloudflare",
|
|
114
|
-
resvg: "wasm",
|
|
115
|
-
satori: "wasm",
|
|
116
|
-
takumi: "wasm",
|
|
117
|
-
sharp: false,
|
|
118
|
-
emoji: "fetch",
|
|
119
|
-
// edge size limits - use API instead of bundling 24MB icons
|
|
120
|
-
wasm: {
|
|
121
|
-
esmImport: true
|
|
122
|
-
// Do NOT set lazy here — Nitro's cloudflare preset uses lazy: false because
|
|
123
|
-
// CF Workers require WASM modules to be imported eagerly via top-level await
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
const awsLambda = {
|
|
127
|
-
browser: false,
|
|
128
|
-
resvg: "node",
|
|
129
|
-
satori: "node",
|
|
130
|
-
takumi: "node",
|
|
131
|
-
sharp: false,
|
|
132
|
-
// 0.33.x has issues
|
|
133
|
-
emoji: "local"
|
|
134
|
-
// serverless has larger size limits
|
|
135
|
-
};
|
|
136
|
-
const WebContainer = {
|
|
137
|
-
browser: false,
|
|
138
|
-
resvg: "wasm-fs",
|
|
139
|
-
satori: "wasm-fs",
|
|
140
|
-
takumi: "wasm",
|
|
141
|
-
sharp: false,
|
|
142
|
-
emoji: "fetch"
|
|
143
|
-
// webcontainer has size constraints
|
|
144
|
-
};
|
|
145
|
-
const RuntimeCompatibility = {
|
|
146
|
-
"nitro-dev": NodeDevRuntime,
|
|
147
|
-
"nitro-prerender": NodePrerenderRuntime,
|
|
148
|
-
"node-server": NodeRuntime,
|
|
149
|
-
"node-cluster": NodeRuntime,
|
|
150
|
-
"node-listener": NodeRuntime,
|
|
151
|
-
"bun": NodeRuntime,
|
|
152
|
-
"deno-server": NodeRuntime,
|
|
153
|
-
"deno-deploy": NodeRuntime,
|
|
154
|
-
// node-server extending presets
|
|
155
|
-
"alwaysdata": NodeRuntime,
|
|
156
|
-
"cleavr": NodeRuntime,
|
|
157
|
-
"digital-ocean": NodeRuntime,
|
|
158
|
-
"edgio": NodeRuntime,
|
|
159
|
-
"flight-control": NodeRuntime,
|
|
160
|
-
"heroku": NodeRuntime,
|
|
161
|
-
"iis-handler": NodeRuntime,
|
|
162
|
-
"iis-node": NodeRuntime,
|
|
163
|
-
"koyeb": NodeRuntime,
|
|
164
|
-
"platform-sh": NodeRuntime,
|
|
165
|
-
"render-com": NodeRuntime,
|
|
166
|
-
"zeabur": NodeRuntime,
|
|
167
|
-
"zerops": NodeRuntime,
|
|
168
|
-
"stackblitz": WebContainer,
|
|
169
|
-
"codesandbox": WebContainer,
|
|
170
|
-
"aws-amplify": awsLambda,
|
|
171
|
-
"aws-lambda": awsLambda,
|
|
172
|
-
"azure": awsLambda,
|
|
173
|
-
"azure-functions": awsLambda,
|
|
174
|
-
"azure-swa": awsLambda,
|
|
175
|
-
"netlify": awsLambda,
|
|
176
|
-
"netlify-edge": {
|
|
177
|
-
browser: false,
|
|
178
|
-
resvg: "wasm",
|
|
179
|
-
satori: "node",
|
|
180
|
-
takumi: "wasm",
|
|
181
|
-
sharp: false,
|
|
182
|
-
emoji: "fetch",
|
|
183
|
-
// edge size limits
|
|
184
|
-
wasm: {
|
|
185
|
-
rollup: {
|
|
186
|
-
targetEnv: "auto-inline",
|
|
187
|
-
sync: ["@resvg/resvg-wasm/index_bg.wasm"]
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
"firebase": awsLambda,
|
|
192
|
-
"firebase-app-hosting": awsLambda,
|
|
193
|
-
"genezio": awsLambda,
|
|
194
|
-
"stormkit": awsLambda,
|
|
195
|
-
"vercel": awsLambda,
|
|
196
|
-
"vercel-edge": {
|
|
197
|
-
browser: false,
|
|
198
|
-
resvg: "wasm",
|
|
199
|
-
satori: "wasm",
|
|
200
|
-
takumi: "wasm",
|
|
201
|
-
sharp: false,
|
|
202
|
-
emoji: "fetch",
|
|
203
|
-
// edge size limits - bundling 24MB icons not viable
|
|
204
|
-
wasm: {
|
|
205
|
-
// lowers workers kb size
|
|
206
|
-
esmImport: true,
|
|
207
|
-
lazy: true
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
"cloudflare-pages": cloudflare,
|
|
211
|
-
"cloudflare-pages-static": cloudflare,
|
|
212
|
-
"cloudflare": cloudflare,
|
|
213
|
-
"cloudflare-module": cloudflare,
|
|
214
|
-
"cloudflare-durable": cloudflare
|
|
215
|
-
};
|
|
216
|
-
function resolveOgImagePreset(nitroConfig) {
|
|
217
|
-
const nuxt = useNuxt();
|
|
218
|
-
if (nuxt.options.dev)
|
|
219
|
-
return "nitro-dev";
|
|
220
|
-
if (nuxt.options.nitro.static)
|
|
221
|
-
return "nitro-prerender";
|
|
222
|
-
return resolveNitroPreset(nitroConfig);
|
|
223
|
-
}
|
|
224
|
-
function getPresetNitroPresetCompatibility(target) {
|
|
225
|
-
const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
|
|
226
|
-
let compatibility = RuntimeCompatibility[normalizedTarget];
|
|
227
|
-
if (!compatibility) {
|
|
228
|
-
logger.warn(`Unknown Nitro preset "${target}" \u2014 falling back to node-server compatibility. Set ogImage.compatibility.runtime to override.`);
|
|
229
|
-
compatibility = NodeRuntime;
|
|
230
|
-
}
|
|
231
|
-
return compatibility;
|
|
232
|
-
}
|
|
233
|
-
async function applyNitroPresetCompatibility(nitroConfig, options) {
|
|
234
|
-
const target = resolveOgImagePreset(nitroConfig);
|
|
235
|
-
const compatibility = getPresetNitroPresetCompatibility(target);
|
|
236
|
-
const { resolve, detectedRenderers } = options;
|
|
237
|
-
const satoriEnabled = detectedRenderers.has("satori");
|
|
238
|
-
const browserEnabled = detectedRenderers.has("browser");
|
|
239
|
-
const takumiEnabled = detectedRenderers.has("takumi");
|
|
240
|
-
for (const renderer of detectedRenderers) {
|
|
241
|
-
if (!compatibility[renderer]) {
|
|
242
|
-
logger.warn(`Renderer "${renderer}" detected but not supported on "${target}" preset. OG images using .${renderer}.vue components may fail.`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
const emptyMock = await resolve.resolvePath("./runtime/mock/empty");
|
|
246
|
-
nitroConfig.alias["#og-image/renderers/satori"] = satoriEnabled ? await resolve.resolvePath("./runtime/server/og-image/satori/renderer") : emptyMock;
|
|
247
|
-
nitroConfig.alias["#og-image/renderers/browser"] = browserEnabled ? await resolve.resolvePath("./runtime/server/og-image/browser/renderer") : emptyMock;
|
|
248
|
-
nitroConfig.alias["#og-image/renderers/takumi"] = takumiEnabled ? await resolve.resolvePath("./runtime/server/og-image/takumi/renderer") : emptyMock;
|
|
249
|
-
const resolvedCompatibility = {};
|
|
250
|
-
async function applyBinding(key) {
|
|
251
|
-
let binding = options.compatibility?.[key];
|
|
252
|
-
if (typeof binding === "undefined")
|
|
253
|
-
binding = compatibility[key];
|
|
254
|
-
const isRendererBinding = key === "browser" || key === "satori" || key === "takumi";
|
|
255
|
-
if (isRendererBinding && !detectedRenderers.has(key)) {
|
|
256
|
-
binding = false;
|
|
257
|
-
}
|
|
258
|
-
resolvedCompatibility[key] = binding;
|
|
259
|
-
if (!binding && isRendererBinding) {
|
|
260
|
-
nitroConfig.alias[`#og-image/renderers/${key}`] = emptyMock;
|
|
261
|
-
}
|
|
262
|
-
return {
|
|
263
|
-
[`#og-image/bindings/${key}`]: !binding ? emptyMock : await resolve.resolvePath(`./runtime/server/og-image/bindings/${key}/${binding}`)
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
const satoriPkgDir = await resolve.resolvePath("satori/package.json").then((p) => p.replace("/package.json", ""));
|
|
267
|
-
const yogaWasmPath = `${satoriPkgDir}/yoga.wasm`;
|
|
268
|
-
nitroConfig.alias = defu(
|
|
269
|
-
{ "#og-image/yoga-wasm": `${yogaWasmPath}?module` },
|
|
270
|
-
await applyBinding("browser"),
|
|
271
|
-
await applyBinding("satori"),
|
|
272
|
-
await applyBinding("takumi"),
|
|
273
|
-
await applyBinding("resvg"),
|
|
274
|
-
await applyBinding("sharp"),
|
|
275
|
-
nitroConfig.alias || {}
|
|
276
|
-
);
|
|
277
|
-
if (Object.values(resolvedCompatibility).includes("wasm")) {
|
|
278
|
-
nitroConfig.experimental = nitroConfig.experimental || {};
|
|
279
|
-
nitroConfig.experimental.wasm = true;
|
|
280
|
-
}
|
|
281
|
-
nitroConfig.rollupConfig = nitroConfig.rollupConfig || {};
|
|
282
|
-
nitroConfig.wasm = defu(compatibility.wasm, nitroConfig.wasm);
|
|
283
|
-
nitroConfig.virtual["#og-image/compatibility"] = () => `export default ${JSON.stringify(resolvedCompatibility)}`;
|
|
284
|
-
addTemplate({
|
|
285
|
-
filename: "nuxt-og-image/compatibility.mjs",
|
|
286
|
-
getContents() {
|
|
287
|
-
return `export default ${JSON.stringify(resolvedCompatibility)}`;
|
|
288
|
-
},
|
|
289
|
-
options: { mode: "server" }
|
|
290
|
-
});
|
|
291
|
-
return resolvedCompatibility;
|
|
292
|
-
}
|
|
293
|
-
async function ensureDependencies(dep, nuxt = useNuxt()) {
|
|
294
|
-
const { ensureDependencyInstalled } = await import('nypm');
|
|
295
|
-
return Promise.all(dep.map((d) => {
|
|
296
|
-
return ensureDependencyInstalled(d, {
|
|
297
|
-
cwd: nuxt.options.rootDir,
|
|
298
|
-
dev: true
|
|
299
|
-
});
|
|
300
|
-
}));
|
|
301
|
-
}
|
|
29
|
+
import { getNuxtModuleOptions, isNuxtGenerate } from 'nuxtseo-shared/kit';
|
|
302
30
|
|
|
303
31
|
const RE_REFLECT_HAS_MINIFIED = /Reflect\.has\(([\w$]+),([\w$]+)\)\?Reflect\.get\(\1,\2,([\w$]+)\):Reflect\.get\(([\w$]+),\2,\3\)/g;
|
|
304
32
|
const RE_WASM_IMPORT = /import\("(\.\/wasm\/[^"]+\.wasm)"\)/g;
|
|
305
|
-
async function setupBuildHandler(config, resolve, getDetectedRenderers, nuxt = useNuxt()) {
|
|
33
|
+
async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = useNuxt()) {
|
|
306
34
|
nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
|
|
307
35
|
if (typeof config.runtimeCacheStorage === "object")
|
|
308
36
|
nuxt.options.nitro.storage["nuxt-og-image"] = config.runtimeCacheStorage;
|
|
@@ -322,7 +50,7 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, nuxt = u
|
|
|
322
50
|
});
|
|
323
51
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
324
52
|
const renderers = getDetectedRenderers();
|
|
325
|
-
await applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers });
|
|
53
|
+
await applyNitroPresetCompatibility(nitro.options, { compatibility: config.compatibility?.runtime, resolve, detectedRenderers: renderers, metadata: getCompatibilityMeta() });
|
|
326
54
|
const target = resolveOgImagePreset(nitro.options);
|
|
327
55
|
const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
|
|
328
56
|
const isEdgePreset = ["cloudflare", "cloudflare-pages", "cloudflare-pages-static", "cloudflare-module", "cloudflare-durable", "vercel-edge", "netlify-edge"].includes(normalizedTarget);
|
|
@@ -455,182 +183,7 @@ function patchWebAssemblyInstantiate(code) {
|
|
|
455
183
|
return code;
|
|
456
184
|
}
|
|
457
185
|
|
|
458
|
-
|
|
459
|
-
{
|
|
460
|
-
name: "satori",
|
|
461
|
-
description: "SVG-based renderer using Satori",
|
|
462
|
-
bindings: {
|
|
463
|
-
"node": [
|
|
464
|
-
{ name: "satori", description: "HTML to SVG renderer" },
|
|
465
|
-
{ name: "@resvg/resvg-js", description: "SVG to PNG converter (native)" }
|
|
466
|
-
],
|
|
467
|
-
"wasm": [
|
|
468
|
-
{ name: "satori", description: "HTML to SVG renderer" },
|
|
469
|
-
{ name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
|
|
470
|
-
],
|
|
471
|
-
"wasm-fs": [
|
|
472
|
-
{ name: "satori", description: "HTML to SVG renderer" },
|
|
473
|
-
{ name: "@resvg/resvg-wasm", description: "SVG to PNG converter (WASM)" }
|
|
474
|
-
]
|
|
475
|
-
}
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
name: "takumi",
|
|
479
|
-
description: "Rust-based high-performance renderer (recommended)",
|
|
480
|
-
bindings: {
|
|
481
|
-
"node": [
|
|
482
|
-
{ name: "@takumi-rs/core", description: "Native Takumi renderer" }
|
|
483
|
-
],
|
|
484
|
-
"wasm": [
|
|
485
|
-
{ name: "@takumi-rs/wasm", description: "WASM Takumi renderer" }
|
|
486
|
-
],
|
|
487
|
-
"wasm-fs": [
|
|
488
|
-
{ name: "@takumi-rs/wasm", description: "WASM Takumi renderer" }
|
|
489
|
-
]
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
name: "browser",
|
|
494
|
-
description: "Browser-based screenshot renderer",
|
|
495
|
-
bindings: {
|
|
496
|
-
node: [
|
|
497
|
-
{ name: "playwright-core", description: "Headless browser automation" }
|
|
498
|
-
]
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
];
|
|
502
|
-
async function getInstalledProviders() {
|
|
503
|
-
const installed = [];
|
|
504
|
-
for (const provider of PROVIDER_DEPENDENCIES) {
|
|
505
|
-
if (provider.bindings.node) {
|
|
506
|
-
const allNodeInstalled = await Promise.all(
|
|
507
|
-
provider.bindings.node.map((dep) => hasResolvableDependency(dep.name))
|
|
508
|
-
);
|
|
509
|
-
if (allNodeInstalled.every(Boolean)) {
|
|
510
|
-
installed.push({ provider: provider.name, binding: "node" });
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (provider.bindings.wasm) {
|
|
515
|
-
const allWasmInstalled = await Promise.all(
|
|
516
|
-
provider.bindings.wasm.map((dep) => hasResolvableDependency(dep.name))
|
|
517
|
-
);
|
|
518
|
-
if (allWasmInstalled.every(Boolean)) {
|
|
519
|
-
installed.push({ provider: provider.name, binding: "wasm" });
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return installed;
|
|
524
|
-
}
|
|
525
|
-
async function getMissingDependencies(provider, binding = "node") {
|
|
526
|
-
const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
|
|
527
|
-
if (!providerDef)
|
|
528
|
-
return [];
|
|
529
|
-
const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
|
|
530
|
-
const missing = [];
|
|
531
|
-
for (const dep of deps) {
|
|
532
|
-
if (!await hasResolvableDependency(dep.name))
|
|
533
|
-
missing.push(dep.name);
|
|
534
|
-
}
|
|
535
|
-
return missing;
|
|
536
|
-
}
|
|
537
|
-
function getProviderDependencies(provider, binding = "node") {
|
|
538
|
-
const providerDef = PROVIDER_DEPENDENCIES.find((p) => p.name === provider);
|
|
539
|
-
if (!providerDef)
|
|
540
|
-
return [];
|
|
541
|
-
const deps = providerDef.bindings[binding] || providerDef.bindings.node || [];
|
|
542
|
-
return deps.map((d) => d.name);
|
|
543
|
-
}
|
|
544
|
-
function getRecommendedBindingFromPreset(provider) {
|
|
545
|
-
const preset = resolveOgImagePreset();
|
|
546
|
-
const compatibility = getPresetNitroPresetCompatibility(preset);
|
|
547
|
-
return getRecommendedBinding(provider, compatibility);
|
|
548
|
-
}
|
|
549
|
-
function getRecommendedBinding(provider, compatibility) {
|
|
550
|
-
if (provider === "satori") {
|
|
551
|
-
const resvgBinding = compatibility.resvg;
|
|
552
|
-
if (resvgBinding === "wasm-fs")
|
|
553
|
-
return "wasm-fs";
|
|
554
|
-
if (resvgBinding === "wasm")
|
|
555
|
-
return "wasm";
|
|
556
|
-
return "node";
|
|
557
|
-
}
|
|
558
|
-
if (provider === "takumi") {
|
|
559
|
-
const takumiBinding = compatibility.takumi;
|
|
560
|
-
if (takumiBinding === "wasm")
|
|
561
|
-
return "wasm";
|
|
562
|
-
return "node";
|
|
563
|
-
}
|
|
564
|
-
if (provider === "browser") {
|
|
565
|
-
return "node";
|
|
566
|
-
}
|
|
567
|
-
return "node";
|
|
568
|
-
}
|
|
569
|
-
async function ensureProviderDependencies(provider, binding, nuxt) {
|
|
570
|
-
const missing = await getMissingDependencies(provider, binding);
|
|
571
|
-
if (missing.length === 0)
|
|
572
|
-
return { success: true, installed: [] };
|
|
573
|
-
const pm = await detectPackageManager(nuxt.options.rootDir);
|
|
574
|
-
const pmName = pm?.name || "npm";
|
|
575
|
-
logger.info(`Installing ${provider} dependencies: ${missing.join(", ")}`);
|
|
576
|
-
const installed = [];
|
|
577
|
-
for (const pkg of missing) {
|
|
578
|
-
const success = await addDependency(pkg, {
|
|
579
|
-
cwd: nuxt.options.rootDir,
|
|
580
|
-
dev: false
|
|
581
|
-
}).then(() => {
|
|
582
|
-
installed.push(pkg);
|
|
583
|
-
return true;
|
|
584
|
-
}).catch(() => {
|
|
585
|
-
logger.error(`Failed to install ${pkg}. Run manually: ${pmName} add ${pkg}`);
|
|
586
|
-
return false;
|
|
587
|
-
});
|
|
588
|
-
if (!success)
|
|
589
|
-
return { success: false, installed };
|
|
590
|
-
}
|
|
591
|
-
return { success: true, installed };
|
|
592
|
-
}
|
|
593
|
-
function canPromptInteractively(env = {
|
|
594
|
-
hasTTY,
|
|
595
|
-
hasStdinTTY: Boolean(process.stdin?.isTTY),
|
|
596
|
-
isAgent,
|
|
597
|
-
isCI
|
|
598
|
-
}) {
|
|
599
|
-
return env.hasTTY && env.hasStdinTTY && !env.isAgent && !env.isCI;
|
|
600
|
-
}
|
|
601
|
-
async function promptForRendererSelection() {
|
|
602
|
-
logger.info("Welcome to Nuxt OG Image! No renderer dependencies detected.");
|
|
603
|
-
const renderer = await logger.prompt("Which renderer would you like to use?", {
|
|
604
|
-
type: "select",
|
|
605
|
-
options: PROVIDER_DEPENDENCIES.map((p) => p.name),
|
|
606
|
-
initial: "takumi"
|
|
607
|
-
});
|
|
608
|
-
return renderer || "takumi";
|
|
609
|
-
}
|
|
610
|
-
async function validateProviderSetup(renderer, compatibility) {
|
|
611
|
-
const issues = [];
|
|
612
|
-
const binding = getRecommendedBinding(renderer, compatibility);
|
|
613
|
-
const missing = await getMissingDependencies(renderer, binding);
|
|
614
|
-
if (missing.length > 0) {
|
|
615
|
-
issues.push(`Missing ${renderer} dependencies: ${missing.join(", ")}`);
|
|
616
|
-
}
|
|
617
|
-
if (renderer === "satori") {
|
|
618
|
-
const hasResvgNode = await hasResolvableDependency("@resvg/resvg-js");
|
|
619
|
-
const hasResvgWasm = await hasResolvableDependency("@resvg/resvg-wasm");
|
|
620
|
-
if (!hasResvgNode && !hasResvgWasm) {
|
|
621
|
-
issues.push("Satori requires either @resvg/resvg-js (node) or @resvg/resvg-wasm to render PNGs");
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
if (renderer === "browser") {
|
|
625
|
-
const hasPlaywright = await hasResolvableDependency("playwright-core");
|
|
626
|
-
if (!hasPlaywright && binding === "node") {
|
|
627
|
-
issues.push("Browser renderer requires playwright-core");
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
return { valid: issues.length === 0, issues };
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
function setupDevHandler(options, resolve, getDetectedRenderers, nuxt = useNuxt()) {
|
|
186
|
+
function setupDevHandler(options, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = useNuxt()) {
|
|
634
187
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
635
188
|
const detectedRenderers = new Set(getDetectedRenderers());
|
|
636
189
|
const targetCompatibility = getPresetNitroPresetCompatibility(resolveOgImagePreset(nitro.options));
|
|
@@ -642,7 +195,7 @@ function setupDevHandler(options, resolve, getDetectedRenderers, nuxt = useNuxt(
|
|
|
642
195
|
detectedRenderers.add(renderer);
|
|
643
196
|
}
|
|
644
197
|
}
|
|
645
|
-
await applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers });
|
|
198
|
+
await applyNitroPresetCompatibility(nitro.options, { compatibility: options.compatibility?.dev, resolve, detectedRenderers, metadata: getCompatibilityMeta() });
|
|
646
199
|
});
|
|
647
200
|
}
|
|
648
201
|
|
|
@@ -2446,10 +1999,10 @@ function setupGenerateHandler(_options, resolve, _getDetectedRenderers, nuxt = u
|
|
|
2446
1999
|
}
|
|
2447
2000
|
|
|
2448
2001
|
const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2449
|
-
function setupPrerenderHandler(options, resolve, getDetectedRenderers, nuxt = useNuxt()) {
|
|
2002
|
+
function setupPrerenderHandler(options, resolve, getDetectedRenderers, getCompatibilityMeta = () => ({}), nuxt = useNuxt()) {
|
|
2450
2003
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
2451
2004
|
nitro.hooks.hook("prerender:config", async (nitroConfig) => {
|
|
2452
|
-
await applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers() });
|
|
2005
|
+
await applyNitroPresetCompatibility(nitroConfig, { compatibility: options.compatibility?.prerender, resolve, detectedRenderers: getDetectedRenderers(), metadata: getCompatibilityMeta() });
|
|
2453
2006
|
nitroConfig.wasm = nitroConfig.wasm || {};
|
|
2454
2007
|
nitroConfig.wasm.esmImport = false;
|
|
2455
2008
|
nitroConfig.prerender = nitroConfig.prerender || {};
|
|
@@ -2537,6 +2090,15 @@ function extractPropNamesFromVue(code) {
|
|
|
2537
2090
|
}
|
|
2538
2091
|
}
|
|
2539
2092
|
|
|
2093
|
+
function resolveSigningSecret(configSecret, envSecret, generate) {
|
|
2094
|
+
if (configSecret === false)
|
|
2095
|
+
return { secret: "", hasExplicit: false, optOut: true, generated: false };
|
|
2096
|
+
const explicit = configSecret || "" || (envSecret || "");
|
|
2097
|
+
if (explicit)
|
|
2098
|
+
return { secret: explicit, hasExplicit: true, optOut: false, generated: false };
|
|
2099
|
+
return { secret: generate(), hasExplicit: false, optOut: false, generated: true };
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2540
2102
|
function isVue(id, opts = {}) {
|
|
2541
2103
|
const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
|
|
2542
2104
|
if (id.endsWith(".vue") && !search) {
|
|
@@ -4197,7 +3759,11 @@ async function onInstall(nuxt) {
|
|
|
4197
3759
|
const installedProviders2 = await getInstalledProviders();
|
|
4198
3760
|
if (installedProviders2.length === 0) {
|
|
4199
3761
|
throw new Error(
|
|
4200
|
-
|
|
3762
|
+
`[nuxt-og-image] No OG image provider dependencies found. Install a provider before running in CI:
|
|
3763
|
+
npm add ${TAKUMI_CORE_INSTALL_SPEC} # for takumi
|
|
3764
|
+
npm add @resvg/resvg-js satori yoga-wasm-web # for satori
|
|
3765
|
+
npm add playwright-core # for browser
|
|
3766
|
+
See: https://nuxtseo.com/og-image/getting-started`
|
|
4201
3767
|
);
|
|
4202
3768
|
}
|
|
4203
3769
|
return;
|
|
@@ -4269,10 +3835,10 @@ async function onInstall(nuxt) {
|
|
|
4269
3835
|
state.selectedBinding = hasNodeBinding ? "node" : "wasm";
|
|
4270
3836
|
}
|
|
4271
3837
|
}
|
|
4272
|
-
const
|
|
4273
|
-
if (
|
|
3838
|
+
const missingInstallSpecs = await getMissingDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
|
|
3839
|
+
if (missingInstallSpecs.length > 0) {
|
|
4274
3840
|
logger.info(`Required dependencies for ${state.selectedRenderer} (${state.selectedBinding}):`);
|
|
4275
|
-
for (const pkg of
|
|
3841
|
+
for (const pkg of missingInstallSpecs) {
|
|
4276
3842
|
logger.info(` - ${pkg}`);
|
|
4277
3843
|
}
|
|
4278
3844
|
const installDeps = await logger.prompt("Install missing dependencies?", {
|
|
@@ -4289,11 +3855,11 @@ async function onInstall(nuxt) {
|
|
|
4289
3855
|
logger.success(`Installed: ${result.installed.join(", ")}`);
|
|
4290
3856
|
} else {
|
|
4291
3857
|
logger.error("Failed to install some dependencies. Install manually:");
|
|
4292
|
-
const allDeps =
|
|
3858
|
+
const allDeps = getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
|
|
4293
3859
|
logger.info(` npm add ${allDeps.join(" ")}`);
|
|
4294
3860
|
}
|
|
4295
3861
|
} else {
|
|
4296
|
-
const allDeps =
|
|
3862
|
+
const allDeps = getProviderDependencyInstallSpecs(state.selectedRenderer, state.selectedBinding);
|
|
4297
3863
|
logger.warn("Dependencies required. Install manually:");
|
|
4298
3864
|
logger.info(` npm add ${allDeps.join(" ")}`);
|
|
4299
3865
|
}
|
|
@@ -4528,8 +4094,8 @@ declare module '#og-image-virtual/build-dir.mjs' {
|
|
|
4528
4094
|
}
|
|
4529
4095
|
|
|
4530
4096
|
declare module '#og-image/compatibility' {
|
|
4531
|
-
import type {
|
|
4532
|
-
const compatibility:
|
|
4097
|
+
import type { RuntimeCompatibilityPayload } from '${typesPath}'
|
|
4098
|
+
const compatibility: RuntimeCompatibilityPayload
|
|
4533
4099
|
export default compatibility
|
|
4534
4100
|
}
|
|
4535
4101
|
declare module '#og-image-virtual/tw4-theme.mjs' {
|
|
@@ -4645,6 +4211,44 @@ const RE_INLINE_CONFIG = /const\s+inlineConfig\s*=\s*(\{[\s\S]*?\n\})/;
|
|
|
4645
4211
|
const RE_PASCAL_TO_KEBAB = /([a-z])([A-Z])/g;
|
|
4646
4212
|
const RE_TILDE_PREFIX = /^~\//;
|
|
4647
4213
|
const IS_MODULE_DEVELOPMENT = import.meta.filename.endsWith(".ts");
|
|
4214
|
+
async function findPackageVersionFromEntry(pkg, resolver) {
|
|
4215
|
+
const entry = await resolver.resolvePath(pkg, { fallbackToOriginal: false }).catch(() => null);
|
|
4216
|
+
if (!entry || entry === pkg)
|
|
4217
|
+
return;
|
|
4218
|
+
let dir = dirname(entry);
|
|
4219
|
+
while (dir && dir !== dirname(dir)) {
|
|
4220
|
+
const pkgPath = join(dir, "package.json");
|
|
4221
|
+
if (existsSync(pkgPath)) {
|
|
4222
|
+
const pkgJson = await readPackageJSON(pkgPath).catch(() => null);
|
|
4223
|
+
if (pkgJson?.name === pkg)
|
|
4224
|
+
return pkgJson.version;
|
|
4225
|
+
}
|
|
4226
|
+
dir = dirname(dir);
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
function normalizeTakumiVersion(version) {
|
|
4230
|
+
const major = version ? Number.parseInt(version.split(".")[0], 10) : Number.NaN;
|
|
4231
|
+
if (major >= 2)
|
|
4232
|
+
return 2;
|
|
4233
|
+
if (major === 1)
|
|
4234
|
+
return 1;
|
|
4235
|
+
}
|
|
4236
|
+
async function detectTakumiVersion(resolver, binding) {
|
|
4237
|
+
const packages = binding === "wasm" ? ["@takumi-rs/wasm", "@takumi-rs/core"] : ["@takumi-rs/core", "@takumi-rs/wasm"];
|
|
4238
|
+
const versions = (await Promise.all(packages.map(async (pkg) => {
|
|
4239
|
+
const version = await findPackageVersionFromEntry(pkg, resolver);
|
|
4240
|
+
const major = normalizeTakumiVersion(version);
|
|
4241
|
+
return version && major ? { pkg, version, major } : void 0;
|
|
4242
|
+
}))).filter(Boolean);
|
|
4243
|
+
const detectedMajors = new Set(versions.map(({ major }) => major));
|
|
4244
|
+
if (detectedMajors.size > 1) {
|
|
4245
|
+
const preferred = versions[0];
|
|
4246
|
+
logger.warn(
|
|
4247
|
+
`Mismatched @takumi-rs package major versions detected (${versions.map(({ pkg, version }) => `${pkg}@${version}`).join(", ")}). Using ${preferred.pkg}@${preferred.version} for Takumi v${preferred.major} compatibility.`
|
|
4248
|
+
);
|
|
4249
|
+
}
|
|
4250
|
+
return versions[0]?.major;
|
|
4251
|
+
}
|
|
4648
4252
|
function isProviderEnabledForEnv(provider, nuxt, config) {
|
|
4649
4253
|
return nuxt.options.dev && config.compatibility?.dev?.[provider] !== false || !nuxt.options.dev && (config.compatibility?.runtime?.[provider] !== false || config.compatibility?.prerender?.[provider] !== false);
|
|
4650
4254
|
}
|
|
@@ -4724,23 +4328,30 @@ const module$1 = defineNuxtModule({
|
|
|
4724
4328
|
if (config.debug && !nuxt.options.dev) {
|
|
4725
4329
|
logger.warn("`ogImage.debug` is enabled in production. This exposes the `/_og/debug.json` endpoint and should not be enabled in production. Disable it before deploying.");
|
|
4726
4330
|
}
|
|
4727
|
-
const
|
|
4728
|
-
|
|
4331
|
+
const signing = resolveSigningSecret(
|
|
4332
|
+
config.security?.secret,
|
|
4333
|
+
process.env.NUXT_OG_IMAGE_SECRET,
|
|
4334
|
+
() => randomBytes(32).toString("base64url")
|
|
4335
|
+
);
|
|
4336
|
+
const resolvedSecret = signing.secret;
|
|
4337
|
+
if (config.security?.strict && !signing.hasExplicit) {
|
|
4729
4338
|
throw new Error("[nuxt-og-image] `security.strict` requires a signing secret. Generate one with: npx nuxt-og-image generate-secret");
|
|
4730
4339
|
}
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
"
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4340
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
4341
|
+
const hasServerRuntime = !nitro.options.static && !nuxt.options._generate;
|
|
4342
|
+
if (!nuxt.options.dev || config.zeroRuntime || !hasServerRuntime)
|
|
4343
|
+
return;
|
|
4344
|
+
if (signing.optOut) {
|
|
4345
|
+
logger.warn("OG image URL signing is disabled (`security.secret: false`). Anyone can craft arbitrary image generation requests.");
|
|
4346
|
+
} else if (signing.generated) {
|
|
4347
|
+
logger.warn([
|
|
4348
|
+
"OG image URLs are signed with an auto-generated secret that changes every build.",
|
|
4349
|
+
"This is fine for single-instance deploys; for rolling or multi-instance deploys set a stable secret:",
|
|
4350
|
+
" NUXT_OG_IMAGE_SECRET=<secret>",
|
|
4351
|
+
" Generate one with: npx nuxt-og-image generate-secret"
|
|
4352
|
+
].join("\n"));
|
|
4353
|
+
}
|
|
4354
|
+
});
|
|
4744
4355
|
const ogImageConfig = config;
|
|
4745
4356
|
for (const key of Object.keys(REMOVED_CONFIG)) {
|
|
4746
4357
|
if (key !== "chromium-node" && key !== "browser-node" && key in ogImageConfig && ogImageConfig[key] !== void 0) {
|
|
@@ -4780,6 +4391,7 @@ Or pin to a specific version in package.json:
|
|
|
4780
4391
|
nuxt.options.alias["#og-image-cache"] = resolve("./runtime/server/og-image/cache/lru");
|
|
4781
4392
|
const preset = resolveOgImagePreset(nuxt.options.nitro);
|
|
4782
4393
|
const targetCompatibility = getPresetNitroPresetCompatibility(preset);
|
|
4394
|
+
const runtimeCompatibilityMeta = {};
|
|
4783
4395
|
const normalizedPreset = preset.replace(RE_LEGACY_SUFFIX, "");
|
|
4784
4396
|
const isCloudflareWorkers = ["cloudflare", "cloudflare-module", "cloudflare-durable"].includes(normalizedPreset);
|
|
4785
4397
|
if (isCloudflareWorkers) {
|
|
@@ -5240,7 +4852,7 @@ ${familyInfo}`);
|
|
|
5240
4852
|
logger.debug(`Using ${renderer} renderer`);
|
|
5241
4853
|
} else {
|
|
5242
4854
|
ogImageComponentCtx.detectedRenderers.add("takumi");
|
|
5243
|
-
logger.warn(
|
|
4855
|
+
logger.warn(`No OG image renderer dependency detected. Defaulting to \`takumi\` (non-interactive environment). Install \`${TAKUMI_CORE_INSTALL_SPEC}\`, or add a renderer component (e.g. components/OgImage/Default.satori.vue) to choose explicitly.`);
|
|
5244
4856
|
}
|
|
5245
4857
|
}
|
|
5246
4858
|
const availableRenderers = /* @__PURE__ */ new Set();
|
|
@@ -5252,19 +4864,22 @@ ${familyInfo}`);
|
|
|
5252
4864
|
if (missing.length === 0) {
|
|
5253
4865
|
availableRenderers.add(renderer);
|
|
5254
4866
|
} else if (nuxt.options.dev && !nuxt.options._prepare) {
|
|
5255
|
-
|
|
4867
|
+
const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
|
|
4868
|
+
logger.warn(`${renderer} renderer requires: ${installSpecs.join(", ")}`);
|
|
5256
4869
|
const { success } = await ensureProviderDependencies(renderer, binding, nuxt);
|
|
5257
4870
|
if (success) {
|
|
5258
4871
|
availableRenderers.add(renderer);
|
|
5259
4872
|
} else if (isAgent) {
|
|
5260
|
-
throw new Error(`[nuxt-og-image] Failed to install ${renderer} dependencies: ${missing.join(", ")}. Install manually: npx nypm add ${
|
|
4873
|
+
throw new Error(`[nuxt-og-image] Failed to install ${renderer} dependencies: ${missing.join(", ")}. Install manually: npx nypm add ${installSpecs.join(" ")}`);
|
|
5261
4874
|
} else {
|
|
5262
4875
|
logger.error(`Failed to install ${renderer} dependencies. Templates using this renderer won't work.`);
|
|
5263
4876
|
}
|
|
5264
4877
|
} else if (isAgent) {
|
|
5265
|
-
|
|
4878
|
+
const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
|
|
4879
|
+
throw new Error(`[nuxt-og-image] ${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
|
|
5266
4880
|
} else {
|
|
5267
|
-
|
|
4881
|
+
const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
|
|
4882
|
+
logger.error(`${renderer} renderer missing dependencies: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
|
|
5268
4883
|
}
|
|
5269
4884
|
if (renderer === "satori" && availableRenderers.has(renderer) && binding !== "node") {
|
|
5270
4885
|
logger.warn("ReSVG native binding not available. Falling back to WASM version, this may slow down PNG rendering.");
|
|
@@ -5284,6 +4899,14 @@ ${familyInfo}`);
|
|
|
5284
4899
|
availableRenderers.add(renderer);
|
|
5285
4900
|
}
|
|
5286
4901
|
}
|
|
4902
|
+
runtimeCompatibilityMeta.takumiVersion = await detectTakumiVersion(
|
|
4903
|
+
resolver,
|
|
4904
|
+
getRecommendedBinding("takumi", targetCompatibility)
|
|
4905
|
+
);
|
|
4906
|
+
if (ogImageComponentCtx.detectedRenderers.has("takumi") && !runtimeCompatibilityMeta.takumiVersion) {
|
|
4907
|
+
logger.warn("Unable to detect @takumi-rs version. Falling back to Takumi v1 renderer compatibility.");
|
|
4908
|
+
runtimeCompatibilityMeta.takumiVersion = 1;
|
|
4909
|
+
}
|
|
5287
4910
|
if (nuxt.options.dev) {
|
|
5288
4911
|
const communityRenderers = /* @__PURE__ */ new Set();
|
|
5289
4912
|
for (const renderer of ["satori", "takumi", "browser"]) {
|
|
@@ -5383,9 +5006,11 @@ ${familyInfo}`);
|
|
|
5383
5006
|
for (const renderer of ogImageComponentCtx.detectedRenderers) {
|
|
5384
5007
|
if (!availableRenderers.has(renderer)) {
|
|
5385
5008
|
const binding = getRecommendedBinding(renderer, targetCompatibility);
|
|
5386
|
-
getMissingDependencies(renderer, binding).then((missing) => {
|
|
5387
|
-
if (missing.length > 0)
|
|
5388
|
-
|
|
5009
|
+
getMissingDependencies(renderer, binding).then(async (missing) => {
|
|
5010
|
+
if (missing.length > 0) {
|
|
5011
|
+
const installSpecs = await getMissingDependencyInstallSpecs(renderer, binding);
|
|
5012
|
+
logger.warn(`New ${renderer} component detected but dependencies missing: ${missing.join(", ")}. Install with: npx nypm add ${installSpecs.join(" ")}`);
|
|
5013
|
+
}
|
|
5389
5014
|
});
|
|
5390
5015
|
}
|
|
5391
5016
|
}
|
|
@@ -5623,7 +5248,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
|
|
|
5623
5248
|
imageFetchTimeout: config.security?.imageFetchTimeout ?? 3e3,
|
|
5624
5249
|
maxQueryParamSize: config.security?.maxQueryParamSize ?? (config.security?.strict ? 2048 : null),
|
|
5625
5250
|
restrictRuntimeImagesToOrigin: config.security?.restrictRuntimeImagesToOrigin === true || config.security?.strict && config.security?.restrictRuntimeImagesToOrigin == null ? [] : config.security?.restrictRuntimeImagesToOrigin || false,
|
|
5626
|
-
secret:
|
|
5251
|
+
secret: resolvedSecret
|
|
5627
5252
|
}
|
|
5628
5253
|
};
|
|
5629
5254
|
if (nuxt.options.dev) {
|
|
@@ -5637,7 +5262,7 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
|
|
|
5637
5262
|
const existingOgImageCfg = nuxt.options.runtimeConfig.ogImage;
|
|
5638
5263
|
nuxt.options.runtimeConfig.ogImage = {
|
|
5639
5264
|
...existingOgImageCfg && typeof existingOgImageCfg === "object" ? existingOgImageCfg : {},
|
|
5640
|
-
secret: existingOgImageCfg?.secret ||
|
|
5265
|
+
secret: existingOgImageCfg?.secret || (signing.hasExplicit ? signing.secret : "")
|
|
5641
5266
|
};
|
|
5642
5267
|
nuxt.options.runtimeConfig.public = {
|
|
5643
5268
|
...nuxt.options.runtimeConfig.public,
|
|
@@ -5658,8 +5283,9 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
|
|
|
5658
5283
|
};
|
|
5659
5284
|
});
|
|
5660
5285
|
const getDetectedRenderers = () => ogImageComponentCtx.detectedRenderers;
|
|
5286
|
+
const getCompatibilityMeta = () => runtimeCompatibilityMeta;
|
|
5661
5287
|
if (nuxt.options.dev) {
|
|
5662
|
-
setupDevHandler(config, resolver, getDetectedRenderers);
|
|
5288
|
+
setupDevHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);
|
|
5663
5289
|
setupDevToolsUI(config, resolve, nuxt, cssFramework || "none");
|
|
5664
5290
|
const useNitro = new Promise((resolveNitro) => {
|
|
5665
5291
|
nuxt.hooks.hook("nitro:init", resolveNitro);
|
|
@@ -5693,11 +5319,11 @@ export const rootDir = ${JSON.stringify(nuxt.options.rootDir)}`;
|
|
|
5693
5319
|
} else if (isNuxtGenerate()) {
|
|
5694
5320
|
setupGenerateHandler(config, resolver);
|
|
5695
5321
|
} else if (nuxt.options.build) {
|
|
5696
|
-
await setupBuildHandler(config, resolver, getDetectedRenderers);
|
|
5322
|
+
await setupBuildHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);
|
|
5697
5323
|
}
|
|
5698
5324
|
if (nuxt.options.build)
|
|
5699
5325
|
addServerPlugin(resolve("./runtime/server/plugins/prerender"));
|
|
5700
|
-
setupPrerenderHandler(config, resolver, getDetectedRenderers);
|
|
5326
|
+
setupPrerenderHandler(config, resolver, getDetectedRenderers, getCompatibilityMeta);
|
|
5701
5327
|
if (nuxt.options.dev && hasWarnings()) {
|
|
5702
5328
|
nuxt.hook("ready", () => {
|
|
5703
5329
|
emitWarnings();
|