nuxt-og-image 2.0.0-beta.2 → 2.0.0-beta.22
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 +172 -44
- package/dist/client/200.html +2 -2
- package/dist/client/404.html +2 -2
- package/dist/client/_nuxt/IconCSS.779331aa.js +1 -0
- package/dist/client/_nuxt/{ImageLoader.9bf39d71.js → ImageLoader.9ed308b0.js} +1 -1
- package/dist/client/_nuxt/entry.acc10163.css +1 -0
- package/dist/client/_nuxt/entry.e7270163.js +5 -0
- package/dist/client/_nuxt/{error-404.1ff52902.js → error-404.dcc06a80.js} +1 -1
- package/dist/client/_nuxt/{error-500.f7d30da5.js → error-500.fb40d400.js} +1 -1
- package/dist/client/_nuxt/{error-component.cf7543e5.js → error-component.9702a511.js} +2 -2
- package/dist/client/_nuxt/{index.3f356409.js → index.6a247c9d.js} +1 -1
- package/dist/client/_nuxt/{options.56a3e5f9.js → options.481faa9f.js} +1 -1
- package/dist/client/_nuxt/{png.37f3e77b.js → png.7e62d84b.js} +1 -1
- package/dist/client/_nuxt/{shiki.3a930bb8.js → shiki.0c927d45.js} +1 -1
- package/dist/client/_nuxt/{svg.186c6bd1.js → svg.81bf3f5a.js} +1 -1
- package/dist/client/_nuxt/{vnodes.a799f183.js → vnodes.096af306.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 +15 -6
- package/dist/module.json +1 -1
- package/dist/module.mjs +239 -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 +18 -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 +3 -3
- package/dist/runtime/nitro/renderers/satori/plugins/encoding.mjs +2 -1
- package/dist/runtime/nitro/renderers/satori/utils.mjs +1 -0
- 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 +11 -8
- package/dist/runtime/nitro/routes/options.mjs +9 -6
- 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 -4
- package/dist/runtime/nitro/utils.mjs +44 -33
- 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 +27 -19
- package/dist/client/_nuxt/IconCSS.a041aca0.js +0 -1
- package/dist/client/_nuxt/entry.74018bda.js +0 -5
- package/dist/client/_nuxt/entry.7a8c1ab2.css +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 : 2e3,
|
|
69
73
|
waitUntil: "networkidle"
|
|
70
74
|
});
|
|
71
75
|
}
|
|
76
|
+
const screenshotOptions = {
|
|
77
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 2e3
|
|
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,88 @@ 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
|
+
browser: "playwright",
|
|
235
|
+
satori: "default",
|
|
236
|
+
wasm: "import",
|
|
237
|
+
png: "resvg"
|
|
238
|
+
};
|
|
239
|
+
const RuntimeCompatibility = {
|
|
240
|
+
"stackblitz": {
|
|
241
|
+
browser: false,
|
|
242
|
+
satori: "yoga-wasm",
|
|
243
|
+
wasm: "inline"
|
|
244
|
+
},
|
|
245
|
+
"netlify": {
|
|
246
|
+
browser: "lambda",
|
|
247
|
+
wasm: "inline"
|
|
248
|
+
},
|
|
249
|
+
"cloudflare-pages": {
|
|
250
|
+
browser: false
|
|
251
|
+
},
|
|
252
|
+
"cloudflare": {
|
|
253
|
+
browser: false
|
|
254
|
+
},
|
|
255
|
+
"vercel-edge": {
|
|
256
|
+
browser: false,
|
|
257
|
+
wasmImportQuery: "?module"
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
function getNitroProviderCompatibility(nuxt) {
|
|
262
|
+
if (nuxt.options.dev || nuxt.options._prepare) {
|
|
263
|
+
return defu({
|
|
264
|
+
wasm: "fetch",
|
|
265
|
+
browser: "universal"
|
|
266
|
+
}, DefaultRuntimeCompatibility);
|
|
267
|
+
}
|
|
268
|
+
if (provider === "stackblitz")
|
|
269
|
+
return defu(RuntimeCompatibility.stackblitz, DefaultRuntimeCompatibility);
|
|
270
|
+
const target = process.env.NITRO_PRESET || nuxt.options.nitro.preset;
|
|
271
|
+
const compatibility = RuntimeCompatibility[target];
|
|
272
|
+
if (compatibility === false)
|
|
273
|
+
return false;
|
|
274
|
+
return defu(compatibility || {}, DefaultRuntimeCompatibility);
|
|
275
|
+
}
|
|
276
|
+
function ensureDependency(nuxt, dep) {
|
|
277
|
+
return addDependency(dep, { cwd: nuxt.options.rootDir });
|
|
199
278
|
}
|
|
200
279
|
|
|
201
280
|
const PATH = "/__nuxt_og_image__";
|
|
202
281
|
const PATH_ENTRY = `${PATH}/entry`;
|
|
203
282
|
const PATH_PLAYGROUND = `${PATH}/client`;
|
|
204
|
-
const edgeProvidersSupported = [
|
|
205
|
-
"cloudflare",
|
|
206
|
-
"vercel-edge",
|
|
207
|
-
"netlify-edge"
|
|
208
|
-
];
|
|
209
283
|
const module = defineNuxtModule({
|
|
210
284
|
meta: {
|
|
211
285
|
name: "nuxt-og-image",
|
|
@@ -218,26 +292,47 @@ const module = defineNuxtModule({
|
|
|
218
292
|
defaults(nuxt) {
|
|
219
293
|
const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.NUXT_SITE_URL || nuxt.options.runtimeConfig.public?.siteUrl || nuxt.options.runtimeConfig.siteUrl;
|
|
220
294
|
return {
|
|
221
|
-
// when we run `nuxi generate` we need to force prerendering
|
|
222
|
-
forcePrerender: !nuxt.options.dev && nuxt.options._generate,
|
|
223
295
|
siteUrl,
|
|
224
296
|
defaults: {
|
|
225
297
|
component: "OgImageBasic",
|
|
226
298
|
width: 1200,
|
|
227
|
-
height: 630
|
|
299
|
+
height: 630,
|
|
300
|
+
cacheTtl: 24 * 60 * 60 * 1e3
|
|
301
|
+
// default is to cache the image for 24 hours
|
|
228
302
|
},
|
|
229
|
-
|
|
230
|
-
|
|
303
|
+
runtimeSatori: true,
|
|
304
|
+
runtimeBrowser: nuxt.options.dev,
|
|
231
305
|
fonts: [],
|
|
306
|
+
runtimeCacheStorage: false,
|
|
232
307
|
satoriOptions: {},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
playground: process.env.NODE_ENV === "development" || nuxt.options.dev
|
|
308
|
+
playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
|
|
309
|
+
debug: false
|
|
236
310
|
};
|
|
237
311
|
},
|
|
238
312
|
async setup(config, nuxt) {
|
|
313
|
+
const logger = useLogger("nuxt-og-image");
|
|
239
314
|
const { resolve } = createResolver(import.meta.url);
|
|
315
|
+
const nitroCompatibility = getNitroProviderCompatibility(nuxt);
|
|
316
|
+
if (!nitroCompatibility) {
|
|
317
|
+
const target = process.env.NITRO_PRESET || nuxt.options.nitro.preset;
|
|
318
|
+
logger.warn(`It looks like the nitro target ${target} doesn't support \`nuxt-og-image\`.`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (!nitroCompatibility.browser && config.runtimeBrowser) {
|
|
322
|
+
config.runtimeBrowser = false;
|
|
323
|
+
logger.warn("It looks like you're using a nitro target that does not support the browser provider, disabling `runtimeBrowser`.");
|
|
324
|
+
}
|
|
325
|
+
if (nitroCompatibility.browser === "lambda") {
|
|
326
|
+
logger.info("It looks like you're deploying to an environment which requires `chrome-aws-lambda`, checking for dependency...");
|
|
327
|
+
await ensureDependency(nuxt, "chrome-aws-lambda");
|
|
328
|
+
}
|
|
240
329
|
config.siteUrl = config.siteUrl || config.host;
|
|
330
|
+
if (!nuxt.options.dev && nuxt.options._generate && !config.siteUrl)
|
|
331
|
+
logger.warn("Nuxt is being prerendered but no siteUrl is set. This will result in broken images.");
|
|
332
|
+
if (config.runtimeCacheStorage && !nuxt.options.dev) {
|
|
333
|
+
nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
|
|
334
|
+
nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
|
|
335
|
+
}
|
|
241
336
|
if (!config.fonts.length)
|
|
242
337
|
config.fonts = ["Inter:400", "Inter:700"];
|
|
243
338
|
const distResolve = (p) => {
|
|
@@ -269,12 +364,14 @@ export {}
|
|
|
269
364
|
lazy: true,
|
|
270
365
|
handler: resolve("./runtime/nitro/middleware/og.png")
|
|
271
366
|
});
|
|
272
|
-
["html", "options", "svg", "vnode", "font"].forEach((type) => {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
367
|
+
["html", "options", "svg", "vnode", "font", "debug"].forEach((type) => {
|
|
368
|
+
if (type !== "debug" || config.debug) {
|
|
369
|
+
addServerHandler({
|
|
370
|
+
lazy: true,
|
|
371
|
+
route: `/api/og-image-${type}`,
|
|
372
|
+
handler: resolve(`./runtime/nitro/routes/${type}`)
|
|
373
|
+
});
|
|
374
|
+
}
|
|
278
375
|
});
|
|
279
376
|
nuxt.hook("devtools:customTabs", (iframeTabs) => {
|
|
280
377
|
iframeTabs.push({
|
|
@@ -323,92 +420,118 @@ export {}
|
|
|
323
420
|
});
|
|
324
421
|
const runtimeDir = resolve("./runtime");
|
|
325
422
|
nuxt.options.build.transpile.push(runtimeDir);
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
423
|
+
addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
|
|
424
|
+
const customAssetDirs = [
|
|
425
|
+
// allows us to show custom error images
|
|
426
|
+
resolve("./runtime/public-assets")
|
|
330
427
|
];
|
|
428
|
+
if (config.runtimeSatori) {
|
|
429
|
+
if (config.fonts.includes("Inter:400"))
|
|
430
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/inter-font"));
|
|
431
|
+
if (nitroCompatibility.png === "resvg" && nitroCompatibility.wasm === "fetch")
|
|
432
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/resvg"));
|
|
433
|
+
else if (nitroCompatibility.png === "svg2png" && nitroCompatibility.wasm === "fetch")
|
|
434
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/svg2png"));
|
|
435
|
+
if (nitroCompatibility.satori === "yoga-wasm")
|
|
436
|
+
customAssetDirs.push(resolve("./runtime/public-assets-optional/yoga"));
|
|
437
|
+
}
|
|
331
438
|
nuxt.hooks.hook("modules:done", async () => {
|
|
332
439
|
nuxt.hooks.callHook("og-image:config", config);
|
|
333
|
-
nuxt.options.runtimeConfig["nuxt-og-image"] = {
|
|
440
|
+
nuxt.options.runtimeConfig["nuxt-og-image"] = {
|
|
441
|
+
...config,
|
|
442
|
+
// avoid adding credentials
|
|
443
|
+
runtimeCacheStorage: Boolean(config.runtimeCacheStorage),
|
|
444
|
+
assetDirs: [
|
|
445
|
+
resolve(nuxt.options.rootDir, nuxt.options.dir.public),
|
|
446
|
+
...customAssetDirs
|
|
447
|
+
]
|
|
448
|
+
};
|
|
334
449
|
});
|
|
335
|
-
const useSatoriWasm = provider === "stackblitz";
|
|
336
450
|
nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
|
|
337
451
|
nitroConfig.externals = defu(nitroConfig.externals || {}, {
|
|
338
452
|
inline: [runtimeDir]
|
|
339
453
|
});
|
|
340
|
-
if (config.
|
|
454
|
+
if (config.runtimeBrowser) {
|
|
341
455
|
nitroConfig.alias = nitroConfig.alias || {};
|
|
342
456
|
nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
|
|
343
457
|
nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
|
|
344
458
|
nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
|
|
345
459
|
}
|
|
346
460
|
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
|
|
347
|
-
|
|
461
|
+
customAssetDirs.forEach((dir) => {
|
|
462
|
+
nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
|
|
463
|
+
});
|
|
348
464
|
const providerPath = `${runtimeDir}/nitro/providers`;
|
|
349
|
-
if (config.
|
|
350
|
-
nitroConfig.virtual["#nuxt-og-image/browser"] =
|
|
351
|
-
|
|
352
|
-
|
|
465
|
+
if (config.runtimeBrowser) {
|
|
466
|
+
nitroConfig.virtual["#nuxt-og-image/browser"] = `
|
|
467
|
+
let browser
|
|
353
468
|
export default async function() {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
` : `export default async function() {
|
|
357
|
-
return () => {}
|
|
469
|
+
browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
|
|
470
|
+
return browser
|
|
358
471
|
}
|
|
359
472
|
`;
|
|
360
473
|
}
|
|
361
|
-
if (config.
|
|
362
|
-
nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${
|
|
474
|
+
if (config.runtimeSatori) {
|
|
475
|
+
nitroConfig.virtual["#nuxt-og-image/satori"] = `import satori from '${providerPath}/satori/${nitroCompatibility.satori}'
|
|
363
476
|
export default async function() {
|
|
364
477
|
return satori
|
|
365
478
|
}`;
|
|
366
|
-
nitroConfig.virtual["#nuxt-og-image/
|
|
367
|
-
import svg2png from '${providerPath}/svg2png/universal'
|
|
479
|
+
nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
|
|
368
480
|
export default async function() {
|
|
369
|
-
return
|
|
370
|
-
}
|
|
481
|
+
return png
|
|
482
|
+
}
|
|
483
|
+
`;
|
|
371
484
|
}
|
|
372
485
|
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"))}'
|
|
486
|
+
${config.runtimeSatori ? `import satori from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/satori"))}'` : ""}
|
|
487
|
+
${config.runtimeBrowser ? `import browser from '${relative(nuxt.options.rootDir, resolve("./runtime/nitro/renderers/browser"))}'` : ""}
|
|
375
488
|
|
|
376
489
|
export async function useProvider(provider) {
|
|
377
490
|
if (provider === 'satori')
|
|
378
|
-
return satori
|
|
491
|
+
return ${config.runtimeSatori ? "satori" : "null"}
|
|
379
492
|
if (provider === 'browser')
|
|
380
|
-
return browser
|
|
493
|
+
return ${config.runtimeBrowser ? "browser" : "null"}
|
|
494
|
+
return null
|
|
381
495
|
}
|
|
382
496
|
`;
|
|
383
497
|
});
|
|
384
498
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
385
499
|
let screenshotQueue = [];
|
|
386
500
|
nitro.hooks.hook("compiled", async (_nitro) => {
|
|
387
|
-
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
501
|
+
if (!config.runtimeSatori || nuxt.options.dev)
|
|
502
|
+
return;
|
|
503
|
+
if (config.fonts.includes("Inter:400"))
|
|
504
|
+
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"));
|
|
505
|
+
if (config.fonts.includes("Inter:700"))
|
|
506
|
+
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"));
|
|
507
|
+
const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
|
|
508
|
+
const wasmProviderPath = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
|
|
509
|
+
const paths = [wasmProviderPath];
|
|
510
|
+
const chunks = await globby([`${_nitro.options.output.serverDir}/chunks/**/*.mjs`], { absolute: true });
|
|
511
|
+
paths.push(...chunks);
|
|
512
|
+
for (const path of paths) {
|
|
513
|
+
if (!await pathExists(path))
|
|
514
|
+
continue;
|
|
515
|
+
let contents = await readFile(path, "utf-8");
|
|
516
|
+
let updated = false;
|
|
517
|
+
if (_nitro.options.preset.includes("vercel") && path === wasmProviderPath) {
|
|
518
|
+
contents = contents.replace(".cwd(),", '?.cwd || "/",');
|
|
519
|
+
updated = true;
|
|
394
520
|
}
|
|
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
|
-
);
|
|
521
|
+
for (const wasm of Wasms) {
|
|
522
|
+
if (contents.includes(wasm.placeholder)) {
|
|
523
|
+
if (nitroCompatibility.wasm === "import") {
|
|
524
|
+
contents = contents.replace(wasm.placeholder, `import("./${wasm.file}${nitroCompatibility.wasmImportQuery || ""}").then(m => m.default || m)`);
|
|
525
|
+
await copy(resolve(`./runtime/public-assets-optional/${wasm.path}`), resolve(dirname(path), wasm.file));
|
|
526
|
+
} else if (nitroCompatibility.wasm === "inline") {
|
|
527
|
+
const wasmBuffer = await readFile(resolve(`./runtime/public-assets-optional/${wasm.path}`), "base64");
|
|
528
|
+
contents = contents.replace(wasm.placeholder, `Buffer.from("${wasmBuffer}", "base64")`);
|
|
529
|
+
}
|
|
530
|
+
updated = true;
|
|
410
531
|
}
|
|
411
532
|
}
|
|
533
|
+
if (updated)
|
|
534
|
+
await writeFile(path, contents, { encoding: "utf-8" });
|
|
412
535
|
}
|
|
413
536
|
});
|
|
414
537
|
const _routeRulesMatcher = toRouteMatcher(
|
|
@@ -421,7 +544,6 @@ export async function useProvider(provider) {
|
|
|
421
544
|
if (!html)
|
|
422
545
|
return;
|
|
423
546
|
const extractedOptions = extractOgImageOptions(html);
|
|
424
|
-
ctx.contents = stripOgImageOptions(html);
|
|
425
547
|
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
|
|
426
548
|
if (!extractedOptions || routeRules.ogImage === false)
|
|
427
549
|
return;
|
|
@@ -440,21 +562,6 @@ export async function useProvider(provider) {
|
|
|
440
562
|
await nuxt.callHook("og-image:prerenderScreenshots", screenshotQueue);
|
|
441
563
|
if (screenshotQueue.length === 0)
|
|
442
564
|
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
565
|
nitro.logger.info("Ensuring chromium install for og:image generation...");
|
|
459
566
|
const installChromeProcess = execa("npx", ["playwright", "install", "chromium"], {
|
|
460
567
|
stdio: "inherit"
|
|
@@ -481,12 +588,27 @@ export async function useProvider(provider) {
|
|
|
481
588
|
browser = await createBrowser();
|
|
482
589
|
if (browser) {
|
|
483
590
|
nitro.logger.info(`Prerendering ${screenshotQueue.length} og:image screenshots...`);
|
|
591
|
+
for (const entry of screenshotQueue) {
|
|
592
|
+
if (entry.route && Object.keys(entry).length === 1) {
|
|
593
|
+
const html = await $fetch(entry.route, { baseURL: withBase(nuxt.options.app.baseURL, host) });
|
|
594
|
+
const extractedOptions = extractOgImageOptions(html);
|
|
595
|
+
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(entry.route).reverse());
|
|
596
|
+
Object.assign(entry, {
|
|
597
|
+
// @ts-expect-error runtime
|
|
598
|
+
path: extractedOptions.component ? `/api/og-image-html?path=${entry.route}` : entry.route,
|
|
599
|
+
...extractedOptions,
|
|
600
|
+
...routeRules.ogImage || {}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
if (entry.component)
|
|
604
|
+
entry.html = await globalThis.$fetch(entry.path);
|
|
605
|
+
}
|
|
484
606
|
for (const k in screenshotQueue) {
|
|
485
607
|
const entry = screenshotQueue[k];
|
|
486
608
|
const start = Date.now();
|
|
487
609
|
let hasError = false;
|
|
488
|
-
const
|
|
489
|
-
const filename = joinURL(
|
|
610
|
+
const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
|
|
611
|
+
const filename = joinURL(dirname2, "/og.png");
|
|
490
612
|
try {
|
|
491
613
|
const imgBuffer = await screenshot(browser, {
|
|
492
614
|
...config.defaults || {},
|
|
@@ -494,7 +616,7 @@ export async function useProvider(provider) {
|
|
|
494
616
|
host
|
|
495
617
|
});
|
|
496
618
|
try {
|
|
497
|
-
await mkdirp(
|
|
619
|
+
await mkdirp(dirname2);
|
|
498
620
|
} catch (e) {
|
|
499
621
|
}
|
|
500
622
|
await writeFile(filename, imgBuffer);
|
|
@@ -518,7 +640,7 @@ export async function useProvider(provider) {
|
|
|
518
640
|
}
|
|
519
641
|
screenshotQueue = [];
|
|
520
642
|
};
|
|
521
|
-
if (
|
|
643
|
+
if (nuxt.options._generate) {
|
|
522
644
|
nitro.hooks.hook("rollup:before", async () => {
|
|
523
645
|
await captureScreenshots();
|
|
524
646
|
});
|
|
@@ -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 : 2e3,
|
|
21
21
|
waitUntil: "networkidle"
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
+
const screenshotOptions = {
|
|
25
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 2e3
|
|
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",
|