nuxt-og-image 2.0.0-beta.3 → 2.0.0-beta.31
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.1dce7adc.js +1 -0
- package/dist/client/_nuxt/IconCSS.f16db5c2.css +1 -0
- package/dist/client/_nuxt/ImageLoader.163d23b3.js +1 -0
- package/dist/client/_nuxt/ImageLoader.7571516f.css +1 -0
- package/dist/client/_nuxt/entry.23646169.js +5 -0
- package/dist/client/_nuxt/entry.3d2654cc.css +1 -0
- package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.c0f2dfff.js} +1 -1
- package/dist/client/_nuxt/{error-500.f7d30da5.js → error-500.6a9dcc3d.js} +1 -1
- package/dist/client/_nuxt/{error-component.cf7543e5.js → error-component.0db07bb1.js} +2 -2
- package/dist/client/_nuxt/index.403133d8.css +1 -0
- package/dist/client/_nuxt/index.531c9ed8.js +1 -0
- package/dist/client/_nuxt/{options.56a3e5f9.js → options.fd71c26a.js} +1 -1
- package/dist/client/_nuxt/{png.37f3e77b.js → png.5b6830ea.js} +1 -1
- package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.1be71764.js} +1 -1
- package/dist/client/_nuxt/{svg.186c6bd1.js → svg.165cf4fc.js} +1 -1
- package/dist/client/_nuxt/{vnodes.a799f183.js → vnodes.11ff70a7.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 +252 -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/dist/types.d.ts +6 -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,123 @@ 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
|
+
// always add runtime dirs for prerendering to work, these are just used as scan roots
|
|
456
|
+
resolve("./runtime/public-assets-optional/inter-font"),
|
|
457
|
+
resolve("./runtime/public-assets-optional/resvg"),
|
|
458
|
+
resolve("./runtime/public-assets-optional/yoga"),
|
|
459
|
+
resolve("./runtime/public-assets-optional/svg2png")
|
|
460
|
+
]
|
|
461
|
+
};
|
|
334
462
|
});
|
|
335
|
-
const useSatoriWasm = provider === "stackblitz";
|
|
336
463
|
nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
|
|
337
464
|
nitroConfig.externals = defu(nitroConfig.externals || {}, {
|
|
338
465
|
inline: [runtimeDir]
|
|
339
466
|
});
|
|
340
|
-
if (config.
|
|
467
|
+
if (config.runtimeBrowser) {
|
|
341
468
|
nitroConfig.alias = nitroConfig.alias || {};
|
|
342
469
|
nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
|
|
343
470
|
nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
|
|
344
471
|
nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
|
|
345
472
|
}
|
|
346
473
|
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
|
|
347
|
-
|
|
474
|
+
customAssetDirs.forEach((dir) => {
|
|
475
|
+
nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
|
|
476
|
+
});
|
|
348
477
|
const providerPath = `${runtimeDir}/nitro/providers`;
|
|
349
|
-
if (config.
|
|
350
|
-
nitroConfig.virtual["#nuxt-og-image/browser"] =
|
|
351
|
-
|
|
352
|
-
|
|
478
|
+
if (config.runtimeBrowser) {
|
|
479
|
+
nitroConfig.virtual["#nuxt-og-image/browser"] = `
|
|
480
|
+
let browser
|
|
353
481
|
export default async function() {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
` : `export default async function() {
|
|
357
|
-
return () => {}
|
|
482
|
+
browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
|
|
483
|
+
return browser
|
|
358
484
|
}
|
|
359
485
|
`;
|
|
360
486
|
}
|
|
361
|
-
if (config.
|
|
362
|
-
nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${
|
|
487
|
+
if (config.runtimeSatori) {
|
|
488
|
+
nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
|
|
363
489
|
export default async function() {
|
|
364
490
|
return satori
|
|
365
491
|
}`;
|
|
366
|
-
nitroConfig.virtual["#nuxt-og-image/
|
|
367
|
-
import svg2png from '${providerPath}/svg2png/universal'
|
|
492
|
+
nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
|
|
368
493
|
export default async function() {
|
|
369
|
-
return
|
|
370
|
-
}
|
|
494
|
+
return png
|
|
495
|
+
}
|
|
496
|
+
`;
|
|
371
497
|
}
|
|
372
498
|
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"))}'
|
|
499
|
+
${config.runtimeSatori ? `import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'` : ""}
|
|
500
|
+
${config.runtimeBrowser ? `import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'` : ""}
|
|
375
501
|
|
|
376
502
|
export async function useProvider(provider) {
|
|
377
503
|
if (provider === 'satori')
|
|
378
|
-
return satori
|
|
504
|
+
return ${config.runtimeSatori ? "satori" : "null"}
|
|
379
505
|
if (provider === 'browser')
|
|
380
|
-
return browser
|
|
506
|
+
return ${config.runtimeBrowser ? "browser" : "null"}
|
|
507
|
+
return null
|
|
381
508
|
}
|
|
382
509
|
`;
|
|
383
510
|
});
|
|
384
511
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
385
512
|
let screenshotQueue = [];
|
|
386
513
|
nitro.hooks.hook("compiled", async (_nitro) => {
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
514
|
+
if (!config.runtimeSatori || nuxt.options.dev)
|
|
515
|
+
return;
|
|
516
|
+
if (config.fonts.includes("Inter:400"))
|
|
517
|
+
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"));
|
|
518
|
+
if (config.fonts.includes("Inter:700"))
|
|
519
|
+
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"));
|
|
520
|
+
const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
|
|
521
|
+
const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
|
|
522
|
+
const paths = [wasmProviderPath];
|
|
523
|
+
const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
|
|
524
|
+
paths.push(...chunks);
|
|
525
|
+
for (const path of paths) {
|
|
526
|
+
if (!await pathExists(path))
|
|
527
|
+
continue;
|
|
528
|
+
let contents = await readFile(path, "utf-8");
|
|
529
|
+
let updated = false;
|
|
530
|
+
if (_nitro.options.preset.includes("vercel") && path === wasmProviderPath) {
|
|
531
|
+
contents = contents.replace(".cwd(),", '?.cwd || "/",');
|
|
532
|
+
updated = true;
|
|
394
533
|
}
|
|
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
|
-
);
|
|
534
|
+
for (const wasm of Wasms) {
|
|
535
|
+
if (contents.includes(wasm.placeholder)) {
|
|
536
|
+
if (nitroCompatibility.wasm === "import") {
|
|
537
|
+
contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
|
|
538
|
+
await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
|
|
539
|
+
} else if (nitroCompatibility.wasm === "inline") {
|
|
540
|
+
const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`), "base64");
|
|
541
|
+
contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
|
|
542
|
+
}
|
|
543
|
+
updated = true;
|
|
410
544
|
}
|
|
411
545
|
}
|
|
546
|
+
if (updated)
|
|
547
|
+
await writeFile(path, contents, { encoding: "utf-8" });
|
|
412
548
|
}
|
|
413
549
|
});
|
|
414
550
|
const _routeRulesMatcher = toRouteMatcher(
|
|
@@ -421,7 +557,6 @@ export async function useProvider(provider) {
|
|
|
421
557
|
if (!html)
|
|
422
558
|
return;
|
|
423
559
|
const extractedOptions = extractOgImageOptions(html);
|
|
424
|
-
ctx.contents = stripOgImageOptions(html);
|
|
425
560
|
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
|
|
426
561
|
if (!extractedOptions || routeRules.ogImage === false)
|
|
427
562
|
return;
|
|
@@ -440,21 +575,6 @@ export async function useProvider(provider) {
|
|
|
440
575
|
await nuxt.callHook("og-image:prerenderScreenshots", screenshotQueue);
|
|
441
576
|
if (screenshotQueue.length === 0)
|
|
442
577
|
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
578
|
nitro.logger.info("Ensuring chromium install for og:image generation...");
|
|
459
579
|
const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
|
|
460
580
|
stdio: "inherit"
|
|
@@ -481,12 +601,27 @@ export async function useProvider(provider) {
|
|
|
481
601
|
browser = await createBrowser();
|
|
482
602
|
if (browser) {
|
|
483
603
|
nitro.logger.info(`Prerendering ${screenshotQueue.length} og:image screenshots...`);
|
|
604
|
+
for (const entry of screenshotQueue) {
|
|
605
|
+
if (entry.route && Object.keys(entry).length === 1) {
|
|
606
|
+
const html = await $fetch(entry.route, { baseURL: withBase(nuxt.options.app.baseURL, host) });
|
|
607
|
+
const extractedOptions = extractOgImageOptions(html);
|
|
608
|
+
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
|
|
609
|
+
Object.assign(entry, {
|
|
610
|
+
// @ts-expect-error runtime
|
|
611
|
+
path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
|
|
612
|
+
...extractedOptions,
|
|
613
|
+
...routeRules.ogImage || {}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
if (entry.component)
|
|
617
|
+
entry.html = await globalThis.$fetch(entry.path);
|
|
618
|
+
}
|
|
484
619
|
for (const k in screenshotQueue) {
|
|
485
620
|
const entry = screenshotQueue[k];
|
|
486
621
|
const start = Date.now();
|
|
487
622
|
let hasError = false;
|
|
488
|
-
const
|
|
489
|
-
const filename = joinURL(
|
|
623
|
+
const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
|
|
624
|
+
const filename = joinURL(dirname2, "/og.png");
|
|
490
625
|
try {
|
|
491
626
|
const imgBuffer = await screenshot(browser, {
|
|
492
627
|
...config.defaults || {},
|
|
@@ -494,7 +629,7 @@ export async function useProvider(provider) {
|
|
|
494
629
|
host
|
|
495
630
|
});
|
|
496
631
|
try {
|
|
497
|
-
await mkdirp(
|
|
632
|
+
await mkdirp(dirname2);
|
|
498
633
|
} catch (e) {
|
|
499
634
|
}
|
|
500
635
|
await writeFile(filename, imgBuffer);
|
|
@@ -518,7 +653,7 @@ export async function useProvider(provider) {
|
|
|
518
653
|
}
|
|
519
654
|
screenshotQueue = [];
|
|
520
655
|
};
|
|
521
|
-
if (
|
|
656
|
+
if (nuxt.options._generate) {
|
|
522
657
|
nitro.hooks.hook("rollup:before", async () => {
|
|
523
658
|
await captureScreenshots();
|
|
524
659
|
});
|
|
@@ -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",
|