nuxt-og-image 2.0.0-beta.3 → 2.0.0-beta.30
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 +229 -44
- package/dist/client/200.html +2 -2
- package/dist/client/404.html +2 -2
- package/dist/client/_nuxt/IconCSS.565f8e72.css +1 -0
- package/dist/client/_nuxt/IconCSS.7e52d29b.js +1 -0
- package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
- package/dist/client/_nuxt/ImageLoader.e52725bc.js +1 -0
- package/dist/client/_nuxt/entry.0d82ebde.css +1 -0
- package/dist/client/_nuxt/entry.ec623b49.js +5 -0
- package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.1a38651e.js} +1 -1
- package/dist/client/_nuxt/{error-500.f7d30da5.js → error-500.0fdf46db.js} +1 -1
- package/dist/client/_nuxt/{error-component.cf7543e5.js → error-component.c2f3b155.js} +2 -2
- package/dist/client/_nuxt/index.403133d8.css +1 -0
- package/dist/client/_nuxt/index.4ecbc4d7.js +1 -0
- package/dist/client/_nuxt/{options.56a3e5f9.js → options.b6c39737.js} +1 -1
- package/dist/client/_nuxt/{png.37f3e77b.js → png.620a2171.js} +1 -1
- package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.fc3f3455.js} +1 -1
- package/dist/client/_nuxt/{svg.186c6bd1.js → svg.3ef91b53.js} +1 -1
- package/dist/client/_nuxt/{vnodes.a799f183.js → vnodes.83d00cfc.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/options/index.html +2 -2
- package/dist/client/png/index.html +2 -2
- package/dist/client/svg/index.html +2 -2
- package/dist/client/vnodes/index.html +2 -2
- package/dist/module.d.ts +21 -7
- package/dist/module.json +1 -1
- package/dist/module.mjs +247 -117
- package/dist/runtime/browserUtil.d.ts +1 -0
- package/dist/runtime/browserUtil.mjs +10 -5
- package/dist/runtime/composables/defineOgImage.mjs +14 -12
- package/dist/runtime/nitro/middleware/og.png.mjs +50 -7
- package/dist/runtime/nitro/middleware/playground.mjs +4 -3
- package/dist/runtime/nitro/plugins/prerender.d.ts +3 -0
- package/dist/runtime/nitro/plugins/prerender.mjs +26 -0
- package/dist/runtime/nitro/providers/browser/lambda.d.ts +1 -1
- package/dist/runtime/nitro/providers/browser/lambda.mjs +5 -5
- package/dist/runtime/nitro/providers/browser/{node.mjs → playwright.mjs} +0 -9
- package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -0
- package/dist/runtime/nitro/providers/browser/universal.mjs +33 -0
- package/dist/runtime/nitro/providers/png/resvg.d.ts +4 -0
- package/dist/runtime/nitro/providers/png/resvg.mjs +11 -0
- package/dist/runtime/nitro/providers/png/svg2png.mjs +11 -0
- package/dist/runtime/nitro/providers/satori/{webworker.mjs → yoga-wasm.mjs} +4 -5
- package/dist/runtime/nitro/renderers/browser.mjs +12 -6
- package/dist/runtime/nitro/renderers/satori/index.mjs +16 -13
- package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +2 -1
- package/dist/runtime/nitro/renderers/satori/plugins/imageSrc.mjs +27 -3
- package/dist/runtime/nitro/renderers/satori/utils.d.ts +2 -2
- package/dist/runtime/nitro/renderers/satori/utils.mjs +20 -11
- package/dist/runtime/nitro/routes/debug.d.ts +4 -0
- package/dist/runtime/nitro/routes/debug.mjs +9 -0
- package/dist/runtime/nitro/routes/html.mjs +52 -14
- package/dist/runtime/nitro/routes/options.mjs +19 -15
- package/dist/runtime/nitro/routes/svg.mjs +3 -1
- package/dist/runtime/nitro/routes/vnode.mjs +3 -1
- package/dist/runtime/nitro/util-hostname.d.ts +2 -0
- package/dist/runtime/nitro/util-hostname.mjs +15 -0
- package/dist/runtime/nitro/utils-pure.d.ts +3 -2
- package/dist/runtime/nitro/utils-pure.mjs +16 -13
- package/dist/runtime/nitro/utils.d.ts +6 -8
- package/dist/runtime/nitro/utils.mjs +47 -47
- package/dist/runtime/public-assets/__nuxt_og_image__/browser-provider-not-supported.png +0 -0
- package/dist/runtime/public-assets-optional/resvg/resvg.wasm +0 -0
- package/package.json +28 -19
- package/dist/client/_nuxt/IconCSS.a041aca0.js +0 -1
- package/dist/client/_nuxt/ImageLoader.9bf39d71.js +0 -1
- package/dist/client/_nuxt/entry.74018bda.js +0 -5
- package/dist/client/_nuxt/entry.7a8c1ab2.css +0 -1
- package/dist/client/_nuxt/index.3f356409.js +0 -1
- package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -9
- /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
- /package/dist/runtime/nitro/providers/{svg2png/universal.d.ts → png/svg2png.d.ts} +0 -0
- /package/dist/runtime/nitro/providers/satori/{node.d.ts → default.d.ts} +0 -0
- /package/dist/runtime/nitro/providers/satori/{node.mjs → default.mjs} +0 -0
- /package/dist/runtime/nitro/providers/satori/{webworker.d.ts → yoga-wasm.d.ts} +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-400-normal.woff +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/inter-font}/inter-latin-ext-700-normal.woff +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/svg2png}/svg2png.wasm +0 -0
- /package/dist/runtime/{public-assets → public-assets-optional/yoga}/yoga.wasm +0 -0
package/dist/module.mjs
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { defineNuxtModule, createResolver, addTemplate, addServerHandler, addImports, addComponent } from '@nuxt/kit';
|
|
2
|
+
import { defineNuxtModule, useLogger, createResolver, addTemplate, addServerHandler, addImports, addComponent, addServerPlugin } from '@nuxt/kit';
|
|
3
3
|
import { execa } from 'execa';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import defu from 'defu';
|
|
6
6
|
import { toRouteMatcher, createRouter } from 'radix3';
|
|
7
|
-
import { joinURL } from 'ufo';
|
|
8
|
-
import { resolve, relative } from 'pathe';
|
|
7
|
+
import { withBase, joinURL } from 'ufo';
|
|
8
|
+
import { resolve, relative, dirname } from 'pathe';
|
|
9
9
|
import { tinyws } from 'tinyws';
|
|
10
10
|
import sirv from 'sirv';
|
|
11
11
|
import { pathExists, copy, mkdirp } from 'fs-extra';
|
|
12
|
-
import {
|
|
12
|
+
import { globby } from 'globby';
|
|
13
13
|
import playwrightCore from 'playwright-core';
|
|
14
14
|
import { existsSync } from 'node:fs';
|
|
15
15
|
import { createBirpcGroup } from 'birpc';
|
|
16
16
|
import { stringify, parse } from 'flatted';
|
|
17
|
+
import { addDependency } from 'nypm';
|
|
18
|
+
import { provider } from 'std-env';
|
|
17
19
|
|
|
18
20
|
async function createBrowser() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
if (process.dev || process.env.prerender) {
|
|
22
|
+
try {
|
|
23
|
+
const { Launcher } = await import(String("chrome-launcher"));
|
|
24
|
+
const chromePath = Launcher.getFirstInstallation();
|
|
25
|
+
return await playwrightCore.chromium.launch({
|
|
26
|
+
headless: true,
|
|
27
|
+
executablePath: chromePath
|
|
28
|
+
});
|
|
29
|
+
} catch (e) {
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
try {
|
|
29
33
|
return await playwrightCore.chromium.launch({
|
|
@@ -54,9 +58,9 @@ async function screenshot(browser, options) {
|
|
|
54
58
|
width: options.width || 1200,
|
|
55
59
|
height: options.height || 630
|
|
56
60
|
});
|
|
57
|
-
const isHtml = options.path
|
|
61
|
+
const isHtml = options.html || options.path?.startsWith("html:");
|
|
58
62
|
if (isHtml) {
|
|
59
|
-
const html = options.html || options.path
|
|
63
|
+
const html = options.html || options.path?.substring(5);
|
|
60
64
|
await page.evaluate((html2) => {
|
|
61
65
|
document.open("text/html");
|
|
62
66
|
document.write(html2);
|
|
@@ -65,10 +69,13 @@ async function screenshot(browser, options) {
|
|
|
65
69
|
await page.waitForLoadState("networkidle");
|
|
66
70
|
} else {
|
|
67
71
|
await page.goto(`${options.host}${options.path}`, {
|
|
68
|
-
timeout: 1e4,
|
|
72
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 3500,
|
|
69
73
|
waitUntil: "networkidle"
|
|
70
74
|
});
|
|
71
75
|
}
|
|
76
|
+
const screenshotOptions = {
|
|
77
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 3500
|
|
78
|
+
};
|
|
72
79
|
if (options.delay)
|
|
73
80
|
await page.waitForTimeout(options.delay);
|
|
74
81
|
if (options.mask) {
|
|
@@ -78,8 +85,10 @@ async function screenshot(browser, options) {
|
|
|
78
85
|
}, options.mask);
|
|
79
86
|
}
|
|
80
87
|
if (options.selector)
|
|
81
|
-
return await page.locator(options.selector).screenshot();
|
|
82
|
-
|
|
88
|
+
return await page.locator(options.selector).screenshot(screenshotOptions);
|
|
89
|
+
const screenshot2 = await page.screenshot(screenshotOptions);
|
|
90
|
+
await page.close();
|
|
91
|
+
return screenshot2;
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
function setupPlaygroundRPC(nuxt, config) {
|
|
@@ -113,7 +122,7 @@ function setupPlaygroundRPC(nuxt, config) {
|
|
|
113
122
|
const birpc = createBirpcGroup(serverFunctions, []);
|
|
114
123
|
nuxt.hook("builder:watch", (e, path) => {
|
|
115
124
|
if (e === "change")
|
|
116
|
-
birpc.
|
|
125
|
+
birpc.broadcast.refresh.asEvent(path);
|
|
117
126
|
});
|
|
118
127
|
const middleware = async (req, res) => {
|
|
119
128
|
if (req.ws) {
|
|
@@ -164,11 +173,15 @@ function getBodyJson(req) {
|
|
|
164
173
|
});
|
|
165
174
|
}
|
|
166
175
|
|
|
167
|
-
function
|
|
176
|
+
function decodeHtml(html) {
|
|
177
|
+
return html.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/¢/g, "\xA2").replace(/£/g, "\xA3").replace(/¥/g, "\xA5").replace(/€/g, "\u20AC").replace(/©/g, "\xA9").replace(/®/g, "\xAE").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(///g, "/").replace(/&#([0-9]+);/g, (full, int) => {
|
|
178
|
+
return String.fromCharCode(parseInt(int));
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function decodeObjectHtmlEntities(obj) {
|
|
168
182
|
Object.entries(obj).forEach(([key, value]) => {
|
|
169
|
-
if (typeof value === "string")
|
|
170
|
-
obj[key] = value
|
|
171
|
-
}
|
|
183
|
+
if (typeof value === "string")
|
|
184
|
+
obj[key] = decodeHtml(value);
|
|
172
185
|
});
|
|
173
186
|
return obj;
|
|
174
187
|
}
|
|
@@ -185,27 +198,92 @@ function extractOgImageOptions(html) {
|
|
|
185
198
|
console.warn("Failed to parse #nuxt-og-image-options", e, options);
|
|
186
199
|
}
|
|
187
200
|
if (options) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
201
|
+
if (!options.description) {
|
|
202
|
+
const description = html.match(/<meta property="og:description" content="(.*?)">/)?.[1];
|
|
203
|
+
if (description)
|
|
204
|
+
options.description = description;
|
|
205
|
+
else
|
|
206
|
+
options.description = html.match(/<meta name="description" content="(.*?)">/)?.[1];
|
|
207
|
+
}
|
|
208
|
+
return decodeObjectHtmlEntities(options);
|
|
194
209
|
}
|
|
195
210
|
return false;
|
|
196
211
|
}
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
|
|
213
|
+
const SVG2PNGWasmPlaceholder = '"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"';
|
|
214
|
+
const YogaWasmPlaceholder = '"/* NUXT_OG_IMAGE_YOGA_WASM */"';
|
|
215
|
+
const ReSVGWasmPlaceholder = '"/* NUXT_OG_IMAGE_RESVG_WASM */"';
|
|
216
|
+
const Wasms = [
|
|
217
|
+
{
|
|
218
|
+
placeholder: SVG2PNGWasmPlaceholder,
|
|
219
|
+
path: "svg2png/svg2png.wasm",
|
|
220
|
+
file: "svg2png.wasm"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
placeholder: ReSVGWasmPlaceholder,
|
|
224
|
+
path: "resvg/resvg.wasm",
|
|
225
|
+
file: "resvg.wasm"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
placeholder: YogaWasmPlaceholder,
|
|
229
|
+
path: "yoga/yoga.wasm",
|
|
230
|
+
file: "yoga.wasm"
|
|
231
|
+
}
|
|
232
|
+
];
|
|
233
|
+
const DefaultRuntimeCompatibility = {
|
|
234
|
+
// node-server runtime
|
|
235
|
+
browser: "playwright",
|
|
236
|
+
satori: "default",
|
|
237
|
+
wasm: "fetch",
|
|
238
|
+
png: "resvg"
|
|
239
|
+
};
|
|
240
|
+
const RuntimeCompatibility = {
|
|
241
|
+
"stackblitz": {
|
|
242
|
+
browser: false,
|
|
243
|
+
satori: "yoga-wasm",
|
|
244
|
+
wasm: "inline"
|
|
245
|
+
},
|
|
246
|
+
"netlify": {
|
|
247
|
+
browser: "lambda",
|
|
248
|
+
wasm: "inline"
|
|
249
|
+
},
|
|
250
|
+
"cloudflare-pages": {
|
|
251
|
+
browser: false,
|
|
252
|
+
wasm: "import"
|
|
253
|
+
},
|
|
254
|
+
"cloudflare": {
|
|
255
|
+
browser: false,
|
|
256
|
+
wasm: "import"
|
|
257
|
+
},
|
|
258
|
+
"vercel-edge": {
|
|
259
|
+
browser: false,
|
|
260
|
+
wasm: "import",
|
|
261
|
+
wasmImportQuery: "?module"
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
function getNitroProviderCompatibility(nuxt) {
|
|
266
|
+
if (nuxt.options.dev || nuxt.options._prepare || nuxt.options._generate) {
|
|
267
|
+
return defu({
|
|
268
|
+
wasm: "fetch",
|
|
269
|
+
browser: "universal"
|
|
270
|
+
}, DefaultRuntimeCompatibility);
|
|
271
|
+
}
|
|
272
|
+
if (provider === "stackblitz")
|
|
273
|
+
return defu(RuntimeCompatibility.stackblitz, DefaultRuntimeCompatibility);
|
|
274
|
+
const target = process.env.NITRO_PRESET || nuxt.options.nitro.preset;
|
|
275
|
+
const compatibility = RuntimeCompatibility[target];
|
|
276
|
+
if (compatibility === false)
|
|
277
|
+
return false;
|
|
278
|
+
return defu(compatibility || {}, DefaultRuntimeCompatibility);
|
|
279
|
+
}
|
|
280
|
+
function ensureDependency(nuxt, dep) {
|
|
281
|
+
return addDependency(dep, { cwd: nuxt.options.rootDir });
|
|
199
282
|
}
|
|
200
283
|
|
|
201
284
|
const PATH = "/__nuxt_og_image__";
|
|
202
285
|
const PATH_ENTRY = `${PATH}/entry`;
|
|
203
286
|
const PATH_PLAYGROUND = `${PATH}/client`;
|
|
204
|
-
const edgeProvidersSupported = [
|
|
205
|
-
"cloudflare",
|
|
206
|
-
"vercel-edge",
|
|
207
|
-
"netlify-edge"
|
|
208
|
-
];
|
|
209
287
|
const module = defineNuxtModule({
|
|
210
288
|
meta: {
|
|
211
289
|
name: "nuxt-og-image",
|
|
@@ -218,26 +296,51 @@ const module = defineNuxtModule({
|
|
|
218
296
|
defaults(nuxt) {
|
|
219
297
|
const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.NUXT_SITE_URL || nuxt.options.runtimeConfig.public?.siteUrl || nuxt.options.runtimeConfig.siteUrl;
|
|
220
298
|
return {
|
|
221
|
-
// when we run `nuxi generate` we need to force prerendering
|
|
222
|
-
forcePrerender: !nuxt.options.dev && nuxt.options._generate,
|
|
223
299
|
siteUrl,
|
|
224
300
|
defaults: {
|
|
225
301
|
component: "OgImageBasic",
|
|
226
302
|
width: 1200,
|
|
227
|
-
height: 630
|
|
303
|
+
height: 630,
|
|
304
|
+
cacheTtl: 24 * 60 * 60 * 1e3
|
|
305
|
+
// default is to cache the image for 24 hours
|
|
228
306
|
},
|
|
229
|
-
|
|
230
|
-
|
|
307
|
+
runtimeSatori: true,
|
|
308
|
+
runtimeBrowser: nuxt.options.dev,
|
|
231
309
|
fonts: [],
|
|
310
|
+
runtimeCacheStorage: false,
|
|
232
311
|
satoriOptions: {},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
playground: process.env.NODE_ENV === "development" || nuxt.options.dev
|
|
312
|
+
playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
|
|
313
|
+
debug: false
|
|
236
314
|
};
|
|
237
315
|
},
|
|
238
316
|
async setup(config, nuxt) {
|
|
317
|
+
const logger = useLogger("nuxt-og-image");
|
|
239
318
|
const { resolve } = createResolver(import.meta.url);
|
|
319
|
+
const nitroCompatibility = getNitroProviderCompatibility(nuxt);
|
|
320
|
+
if (!nitroCompatibility) {
|
|
321
|
+
const target = process.env.NITRO_PRESET || nuxt.options.nitro.preset;
|
|
322
|
+
logger.warn(`It looks like the nitro target ${target} doesn't support \`nuxt-og-image\`.`);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (!nitroCompatibility.browser && config.runtimeBrowser) {
|
|
326
|
+
config.runtimeBrowser = false;
|
|
327
|
+
logger.warn("It looks like you're using a nitro target that does not support the browser provider, disabling `runtimeBrowser`.");
|
|
328
|
+
}
|
|
329
|
+
if (config.runtimeBrowser && nitroCompatibility.browser === "lambda") {
|
|
330
|
+
logger.info("It looks like you're deploying to an environment which requires `chrome-aws-lambda`, checking for dependency...");
|
|
331
|
+
await ensureDependency(nuxt, "chrome-aws-lambda");
|
|
332
|
+
}
|
|
240
333
|
config.siteUrl = config.siteUrl || config.host;
|
|
334
|
+
if (!nuxt.options.dev && nuxt.options._generate && !config.siteUrl)
|
|
335
|
+
logger.warn("Missing `ogImage.siteUrl` and site is being prerendered. This will result in broken og images.");
|
|
336
|
+
nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
|
|
337
|
+
if (nuxt.options._generate) {
|
|
338
|
+
nuxt.options.nitro.storage["og-image"] = {
|
|
339
|
+
driver: "memory"
|
|
340
|
+
};
|
|
341
|
+
} else if (config.runtimeCacheStorage && !nuxt.options.dev) {
|
|
342
|
+
nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
|
|
343
|
+
}
|
|
241
344
|
if (!config.fonts.length)
|
|
242
345
|
config.fonts = ["Inter:400", "Inter:700"];
|
|
243
346
|
const distResolve = (p) => {
|
|
@@ -269,12 +372,14 @@ export {}
|
|
|
269
372
|
lazy: true,
|
|
270
373
|
handler: resolve("./runtime/nitro/middleware/og.png")
|
|
271
374
|
});
|
|
272
|
-
["html", "options", "svg", "vnode", "font"].forEach((type) => {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
375
|
+
["html", "options", "svg", "vnode", "font", "debug"].forEach((type) => {
|
|
376
|
+
if (type !== "debug" || config.debug) {
|
|
377
|
+
addServerHandler({
|
|
378
|
+
lazy: true,
|
|
379
|
+
route: `/api/og-image-${type}`,
|
|
380
|
+
handler: resolve(`./runtime/nitro/routes/${type}`)
|
|
381
|
+
});
|
|
382
|
+
}
|
|
278
383
|
});
|
|
279
384
|
nuxt.hook("devtools:customTabs", (iframeTabs) => {
|
|
280
385
|
iframeTabs.push({
|
|
@@ -323,92 +428,118 @@ export {}
|
|
|
323
428
|
});
|
|
324
429
|
const runtimeDir = resolve("./runtime");
|
|
325
430
|
nuxt.options.build.transpile.push(runtimeDir);
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
431
|
+
addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
|
|
432
|
+
const customAssetDirs = [
|
|
433
|
+
// allows us to show custom error images
|
|
434
|
+
resolve("./runtime/public-assets")
|
|
330
435
|
];
|
|
436
|
+
if (config.runtimeSatori) {
|
|
437
|
+
if (config.fonts.includes("Inter:400"))
|
|
438
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/inter-font"));
|
|
439
|
+
if (nitroCompatibility.png === "resvg" && nitroCompatibility.wasm === "fetch")
|
|
440
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/resvg"));
|
|
441
|
+
else if (nitroCompatibility.png === "svg2png" && nitroCompatibility.wasm === "fetch")
|
|
442
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/svg2png"));
|
|
443
|
+
if (nitroCompatibility.satori === "yoga-wasm")
|
|
444
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/yoga"));
|
|
445
|
+
}
|
|
331
446
|
nuxt.hooks.hook("modules:done", async () => {
|
|
332
447
|
nuxt.hooks.callHook("og-image:config", config);
|
|
333
|
-
nuxt.options.runtimeConfig["nuxt-og-image"] = {
|
|
448
|
+
nuxt.options.runtimeConfig["nuxt-og-image"] = {
|
|
449
|
+
...config,
|
|
450
|
+
// avoid adding credentials
|
|
451
|
+
runtimeCacheStorage: Boolean(config.runtimeCacheStorage),
|
|
452
|
+
assetDirs: [
|
|
453
|
+
resolve(nuxt.options.rootDir, nuxt.options.dir.public),
|
|
454
|
+
...customAssetDirs
|
|
455
|
+
]
|
|
456
|
+
};
|
|
334
457
|
});
|
|
335
|
-
const useSatoriWasm = provider === "stackblitz";
|
|
336
458
|
nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
|
|
337
459
|
nitroConfig.externals = defu(nitroConfig.externals || {}, {
|
|
338
460
|
inline: [runtimeDir]
|
|
339
461
|
});
|
|
340
|
-
if (config.
|
|
462
|
+
if (config.runtimeBrowser) {
|
|
341
463
|
nitroConfig.alias = nitroConfig.alias || {};
|
|
342
464
|
nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
|
|
343
465
|
nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
|
|
344
466
|
nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
|
|
345
467
|
}
|
|
346
468
|
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
|
|
347
|
-
|
|
469
|
+
customAssetDirs.forEach((dir) => {
|
|
470
|
+
nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
|
|
471
|
+
});
|
|
348
472
|
const providerPath = `${runtimeDir}/nitro/providers`;
|
|
349
|
-
if (config.
|
|
350
|
-
nitroConfig.virtual["#nuxt-og-image/browser"] =
|
|
351
|
-
|
|
352
|
-
|
|
473
|
+
if (config.runtimeBrowser) {
|
|
474
|
+
nitroConfig.virtual["#nuxt-og-image/browser"] = `
|
|
475
|
+
let browser
|
|
353
476
|
export default async function() {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
` : `export default async function() {
|
|
357
|
-
return () => {}
|
|
477
|
+
browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
|
|
478
|
+
return browser
|
|
358
479
|
}
|
|
359
480
|
`;
|
|
360
481
|
}
|
|
361
|
-
if (config.
|
|
362
|
-
nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${
|
|
482
|
+
if (config.runtimeSatori) {
|
|
483
|
+
nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
|
|
363
484
|
export default async function() {
|
|
364
485
|
return satori
|
|
365
486
|
}`;
|
|
366
|
-
nitroConfig.virtual["#nuxt-og-image/
|
|
367
|
-
import svg2png from '${providerPath}/svg2png/universal'
|
|
487
|
+
nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
|
|
368
488
|
export default async function() {
|
|
369
|
-
return
|
|
370
|
-
}
|
|
489
|
+
return png
|
|
490
|
+
}
|
|
491
|
+
`;
|
|
371
492
|
}
|
|
372
493
|
nitroConfig.virtual["#nuxt-og-image/provider"] = `
|
|
373
|
-
import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'
|
|
374
|
-
import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'
|
|
494
|
+
${config.runtimeSatori ? `import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'` : ""}
|
|
495
|
+
${config.runtimeBrowser ? `import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'` : ""}
|
|
375
496
|
|
|
376
497
|
export async function useProvider(provider) {
|
|
377
498
|
if (provider === 'satori')
|
|
378
|
-
return satori
|
|
499
|
+
return ${config.runtimeSatori ? "satori" : "null"}
|
|
379
500
|
if (provider === 'browser')
|
|
380
|
-
return browser
|
|
501
|
+
return ${config.runtimeBrowser ? "browser" : "null"}
|
|
502
|
+
return null
|
|
381
503
|
}
|
|
382
504
|
`;
|
|
383
505
|
});
|
|
384
506
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
385
507
|
let screenshotQueue = [];
|
|
386
508
|
nitro.hooks.hook("compiled", async (_nitro) => {
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
509
|
+
if (!config.runtimeSatori || nuxt.options.dev)
|
|
510
|
+
return;
|
|
511
|
+
if (config.fonts.includes("Inter:400"))
|
|
512
|
+
await copy(resolve("./runtime/public-assets-optional/inter-font/inter-latin-ext-400-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-400-normal.woff"));
|
|
513
|
+
if (config.fonts.includes("Inter:700"))
|
|
514
|
+
await copy(resolve("./runtime/public-assets-optional/inter-font/inter-latin-ext-700-normal.woff"), resolve(_nitro.options.output.publicDir, "inter-latin-ext-700-normal.woff"));
|
|
515
|
+
const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
|
|
516
|
+
const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
|
|
517
|
+
const paths = [wasmProviderPath];
|
|
518
|
+
const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
|
|
519
|
+
paths.push(...chunks);
|
|
520
|
+
for (const path of paths) {
|
|
521
|
+
if (!await pathExists(path))
|
|
522
|
+
continue;
|
|
523
|
+
let contents = await readFile(path, "utf-8");
|
|
524
|
+
let updated = false;
|
|
525
|
+
if (_nitro.options.preset.includes("vercel") && path === wasmProviderPath) {
|
|
526
|
+
contents = contents.replace(".cwd(),", '?.cwd || "/",');
|
|
527
|
+
updated = true;
|
|
394
528
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const yogaWasm = await readFile(resolve("./runtime/public-assets/yoga.wasm"), "base64");
|
|
406
|
-
await writeFile(
|
|
407
|
-
indexFile,
|
|
408
|
-
indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', `Buffer.from("${svg2pngWasm}", "base64")`).replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', `Buffer.from("${yogaWasm}", "base64")`)
|
|
409
|
-
);
|
|
529
|
+
for (const wasm of Wasms) {
|
|
530
|
+
if (contents.includes(wasm.placeholder)) {
|
|
531
|
+
if (nitroCompatibility.wasm === "import") {
|
|
532
|
+
contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
|
|
533
|
+
await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
|
|
534
|
+
} else if (nitroCompatibility.wasm === "inline") {
|
|
535
|
+
const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`), "base64");
|
|
536
|
+
contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
|
|
537
|
+
}
|
|
538
|
+
updated = true;
|
|
410
539
|
}
|
|
411
540
|
}
|
|
541
|
+
if (updated)
|
|
542
|
+
await writeFile(path, contents, { encoding: "utf-8" });
|
|
412
543
|
}
|
|
413
544
|
});
|
|
414
545
|
const _routeRulesMatcher = toRouteMatcher(
|
|
@@ -421,7 +552,6 @@ export async function useProvider(provider) {
|
|
|
421
552
|
if (!html)
|
|
422
553
|
return;
|
|
423
554
|
const extractedOptions = extractOgImageOptions(html);
|
|
424
|
-
ctx.contents = stripOgImageOptions(html);
|
|
425
555
|
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
|
|
426
556
|
if (!extractedOptions || routeRules.ogImage === false)
|
|
427
557
|
return;
|
|
@@ -440,21 +570,6 @@ export async function useProvider(provider) {
|
|
|
440
570
|
await nuxt.callHook("og-image:prerenderScreenshots", screenshotQueue);
|
|
441
571
|
if (screenshotQueue.length === 0)
|
|
442
572
|
return;
|
|
443
|
-
for (const entry of screenshotQueue) {
|
|
444
|
-
if (entry.route && Object.keys(entry).length === 1) {
|
|
445
|
-
const html = await $fetch(entry.route);
|
|
446
|
-
const extractedOptions = extractOgImageOptions(html);
|
|
447
|
-
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
|
|
448
|
-
Object.assign(entry, {
|
|
449
|
-
// @ts-expect-error runtime
|
|
450
|
-
path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
|
|
451
|
-
...extractedOptions,
|
|
452
|
-
...routeRules.ogImage || {}
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
if (entry.component)
|
|
456
|
-
entry.html = await $fetch(entry.path);
|
|
457
|
-
}
|
|
458
573
|
nitro.logger.info("Ensuring chromium install for og:image generation...");
|
|
459
574
|
const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
|
|
460
575
|
stdio: "inherit"
|
|
@@ -481,12 +596,27 @@ export async function useProvider(provider) {
|
|
|
481
596
|
browser = await createBrowser();
|
|
482
597
|
if (browser) {
|
|
483
598
|
nitro.logger.info(`Prerendering ${screenshotQueue.length} og:image screenshots...`);
|
|
599
|
+
for (const entry of screenshotQueue) {
|
|
600
|
+
if (entry.route && Object.keys(entry).length === 1) {
|
|
601
|
+
const html = await $fetch(entry.route, { baseURL: withBase(nuxt.options.app.baseURL, host) });
|
|
602
|
+
const extractedOptions = extractOgImageOptions(html);
|
|
603
|
+
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
|
|
604
|
+
Object.assign(entry, {
|
|
605
|
+
// @ts-expect-error runtime
|
|
606
|
+
path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
|
|
607
|
+
...extractedOptions,
|
|
608
|
+
...routeRules.ogImage || {}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
if (entry.component)
|
|
612
|
+
entry.html = await globalThis.$fetch(entry.path);
|
|
613
|
+
}
|
|
484
614
|
for (const k in screenshotQueue) {
|
|
485
615
|
const entry = screenshotQueue[k];
|
|
486
616
|
const start = Date.now();
|
|
487
617
|
let hasError = false;
|
|
488
|
-
const
|
|
489
|
-
const filename = joinURL(
|
|
618
|
+
const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
|
|
619
|
+
const filename = joinURL(dirname2, "/og.png");
|
|
490
620
|
try {
|
|
491
621
|
const imgBuffer = await screenshot(browser, {
|
|
492
622
|
...config.defaults || {},
|
|
@@ -494,7 +624,7 @@ export async function useProvider(provider) {
|
|
|
494
624
|
host
|
|
495
625
|
});
|
|
496
626
|
try {
|
|
497
|
-
await mkdirp(
|
|
627
|
+
await mkdirp(dirname2);
|
|
498
628
|
} catch (e) {
|
|
499
629
|
}
|
|
500
630
|
await writeFile(filename, imgBuffer);
|
|
@@ -518,7 +648,7 @@ export async function useProvider(provider) {
|
|
|
518
648
|
}
|
|
519
649
|
screenshotQueue = [];
|
|
520
650
|
};
|
|
521
|
-
if (
|
|
651
|
+
if (nuxt.options._generate) {
|
|
522
652
|
nitro.hooks.hook("rollup:before", async () => {
|
|
523
653
|
await captureScreenshots();
|
|
524
654
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
import type { Buffer } from 'node:buffer';
|
|
2
3
|
import type { Browser } from 'playwright-core';
|
|
3
4
|
import type { ScreenshotOptions } from '../types';
|
|
4
5
|
export declare function screenshot(browser: Browser, options: Partial<ScreenshotOptions> & Record<string, any>): Promise<Buffer>;
|
|
@@ -6,9 +6,9 @@ export async function screenshot(browser, options) {
|
|
|
6
6
|
width: options.width || 1200,
|
|
7
7
|
height: options.height || 630
|
|
8
8
|
});
|
|
9
|
-
const isHtml = options.path
|
|
9
|
+
const isHtml = options.html || options.path?.startsWith("html:");
|
|
10
10
|
if (isHtml) {
|
|
11
|
-
const html = options.html || options.path
|
|
11
|
+
const html = options.html || options.path?.substring(5);
|
|
12
12
|
await page.evaluate((html2) => {
|
|
13
13
|
document.open("text/html");
|
|
14
14
|
document.write(html2);
|
|
@@ -17,10 +17,13 @@ export async function screenshot(browser, options) {
|
|
|
17
17
|
await page.waitForLoadState("networkidle");
|
|
18
18
|
} else {
|
|
19
19
|
await page.goto(`${options.host}${options.path}`, {
|
|
20
|
-
timeout: 1e4,
|
|
20
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 3500,
|
|
21
21
|
waitUntil: "networkidle"
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
+
const screenshotOptions = {
|
|
25
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 3500
|
|
26
|
+
};
|
|
24
27
|
if (options.delay)
|
|
25
28
|
await page.waitForTimeout(options.delay);
|
|
26
29
|
if (options.mask) {
|
|
@@ -30,6 +33,8 @@ export async function screenshot(browser, options) {
|
|
|
30
33
|
}, options.mask);
|
|
31
34
|
}
|
|
32
35
|
if (options.selector)
|
|
33
|
-
return await page.locator(options.selector).screenshot();
|
|
34
|
-
|
|
36
|
+
return await page.locator(options.selector).screenshot(screenshotOptions);
|
|
37
|
+
const screenshot2 = await page.screenshot(screenshotOptions);
|
|
38
|
+
await page.close();
|
|
39
|
+
return screenshot2;
|
|
35
40
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useServerHead } from "@vueuse/head";
|
|
2
1
|
import { withBase } from "ufo";
|
|
3
2
|
import { useRequestEvent } from "#app";
|
|
4
|
-
import {
|
|
3
|
+
import { useHostname } from "../nitro/util-hostname.mjs";
|
|
4
|
+
import { useRouter, useRuntimeConfig, useServerHead } from "#imports";
|
|
5
5
|
export function defineOgImageScreenshot(options = {}) {
|
|
6
6
|
const router = useRouter();
|
|
7
7
|
const route = router?.currentRoute?.value?.path || "";
|
|
@@ -14,29 +14,31 @@ export function defineOgImageScreenshot(options = {}) {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
export function defineOgImageDynamic(options = {}) {
|
|
17
|
-
const {
|
|
17
|
+
const { runtimeSatori } = useRuntimeConfig()["nuxt-og-image"];
|
|
18
18
|
defineOgImage({
|
|
19
|
-
provider:
|
|
20
|
-
static:
|
|
19
|
+
provider: runtimeSatori ? "satori" : "browser",
|
|
20
|
+
static: false,
|
|
21
21
|
...options
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
export function defineOgImageStatic(options = {}) {
|
|
25
|
-
const {
|
|
25
|
+
const { runtimeSatori } = useRuntimeConfig()["nuxt-og-image"];
|
|
26
26
|
defineOgImage({
|
|
27
|
-
provider:
|
|
27
|
+
provider: runtimeSatori ? "satori" : "browser",
|
|
28
28
|
static: true,
|
|
29
29
|
...options
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
export function defineOgImage(options = {}) {
|
|
33
33
|
if (process.server) {
|
|
34
|
-
|
|
34
|
+
let resolveSrc = function() {
|
|
35
|
+
return withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, baseUrl);
|
|
36
|
+
};
|
|
37
|
+
const { defaults, siteUrl } = useRuntimeConfig()["nuxt-og-image"];
|
|
35
38
|
const router = useRouter();
|
|
36
39
|
const route = router?.currentRoute?.value?.path || "";
|
|
37
40
|
const e = useRequestEvent();
|
|
38
|
-
|
|
39
|
-
e.res.setHeader("x-nitro-prerender", `${route === "/" ? "" : route}/__og_image__/og.png`);
|
|
41
|
+
const baseUrl = process.env.prerender ? siteUrl : useHostname(e);
|
|
40
42
|
const meta = [
|
|
41
43
|
{
|
|
42
44
|
name: "twitter:card",
|
|
@@ -44,11 +46,11 @@ export function defineOgImage(options = {}) {
|
|
|
44
46
|
},
|
|
45
47
|
{
|
|
46
48
|
name: "twitter:image:src",
|
|
47
|
-
content:
|
|
49
|
+
content: resolveSrc
|
|
48
50
|
},
|
|
49
51
|
{
|
|
50
52
|
property: "og:image",
|
|
51
|
-
content:
|
|
53
|
+
content: resolveSrc
|
|
52
54
|
},
|
|
53
55
|
{
|
|
54
56
|
property: "og:image:width",
|