nuxt-og-image 2.0.0-beta.21 → 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 +82 -15
- 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.97400b2b.js → ImageLoader.9ed308b0.js} +1 -1
- package/dist/client/_nuxt/{entry.7e98a020.css → entry.acc10163.css} +1 -1
- package/dist/client/_nuxt/entry.e7270163.js +5 -0
- package/dist/client/_nuxt/{error-404.7660b68a.js → error-404.dcc06a80.js} +1 -1
- package/dist/client/_nuxt/{error-500.776f22a1.js → error-500.fb40d400.js} +1 -1
- package/dist/client/_nuxt/{error-component.d4668032.js → error-component.9702a511.js} +2 -2
- package/dist/client/_nuxt/{index.77081a6c.js → index.6a247c9d.js} +1 -1
- package/dist/client/_nuxt/{options.b6164a5b.js → options.481faa9f.js} +1 -1
- package/dist/client/_nuxt/{png.b914f6c4.js → png.7e62d84b.js} +1 -1
- package/dist/client/_nuxt/{shiki.ba10b978.js → shiki.0c927d45.js} +1 -1
- package/dist/client/_nuxt/{svg.1e41877e.js → svg.81bf3f5a.js} +1 -1
- package/dist/client/_nuxt/{vnodes.a857bced.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 -5
- package/dist/module.json +1 -1
- package/dist/module.mjs +194 -84
- package/dist/runtime/browserUtil.mjs +5 -3
- package/dist/runtime/composables/defineOgImage.mjs +10 -7
- package/dist/runtime/nitro/middleware/og.png.mjs +43 -15
- package/dist/runtime/nitro/plugins/prerender.mjs +1 -3
- 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/playwright.mjs +22 -0
- package/dist/runtime/nitro/providers/browser/universal.d.ts +1 -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 +7 -6
- package/dist/runtime/nitro/renderers/satori/index.mjs +3 -3
- package/dist/runtime/nitro/routes/debug.d.ts +4 -0
- package/dist/runtime/nitro/routes/debug.mjs +9 -0
- package/dist/runtime/nitro/routes/options.mjs +3 -3
- package/dist/runtime/nitro/utils.d.ts +4 -3
- package/dist/runtime/nitro/utils.mjs +37 -18
- 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 +15 -8
- package/dist/client/_nuxt/IconCSS.ecebf6a5.js +0 -1
- package/dist/client/_nuxt/entry.c8bf4454.js +0 -5
- package/dist/runtime/nitro/providers/svg2png/universal.mjs +0 -12
- /package/dist/runtime/nitro/providers/browser/{node.d.ts → playwright.d.ts} +0 -0
- /package/dist/runtime/nitro/providers/browser/{node.mjs → universal.mjs} +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,19 +1,21 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { defineNuxtModule, createResolver, addTemplate, addServerHandler, addImports, addComponent, addServerPlugin } 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
7
|
import { withBase, joinURL } from 'ufo';
|
|
8
|
-
import { resolve, relative } from 'pathe';
|
|
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
21
|
if (process.dev || process.env.prerender) {
|
|
@@ -67,12 +69,12 @@ async function screenshot(browser, options) {
|
|
|
67
69
|
await page.waitForLoadState("networkidle");
|
|
68
70
|
} else {
|
|
69
71
|
await page.goto(`${options.host}${options.path}`, {
|
|
70
|
-
timeout: 1e4,
|
|
72
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 2e3,
|
|
71
73
|
waitUntil: "networkidle"
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
76
|
const screenshotOptions = {
|
|
75
|
-
timeout: 1e4
|
|
77
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 2e3
|
|
76
78
|
};
|
|
77
79
|
if (options.delay)
|
|
78
80
|
await page.waitForTimeout(options.delay);
|
|
@@ -84,7 +86,9 @@ async function screenshot(browser, options) {
|
|
|
84
86
|
}
|
|
85
87
|
if (options.selector)
|
|
86
88
|
return await page.locator(options.selector).screenshot(screenshotOptions);
|
|
87
|
-
|
|
89
|
+
const screenshot2 = await page.screenshot(screenshotOptions);
|
|
90
|
+
await page.close();
|
|
91
|
+
return screenshot2;
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
function setupPlaygroundRPC(nuxt, config) {
|
|
@@ -206,15 +210,76 @@ function extractOgImageOptions(html) {
|
|
|
206
210
|
return false;
|
|
207
211
|
}
|
|
208
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 });
|
|
278
|
+
}
|
|
279
|
+
|
|
209
280
|
const PATH = "/__nuxt_og_image__";
|
|
210
281
|
const PATH_ENTRY = `${PATH}/entry`;
|
|
211
282
|
const PATH_PLAYGROUND = `${PATH}/client`;
|
|
212
|
-
const edgeProvidersSupported = [
|
|
213
|
-
"cloudflare",
|
|
214
|
-
"cloudflare-pages",
|
|
215
|
-
"vercel-edge",
|
|
216
|
-
"netlify-edge"
|
|
217
|
-
];
|
|
218
283
|
const module = defineNuxtModule({
|
|
219
284
|
meta: {
|
|
220
285
|
name: "nuxt-og-image",
|
|
@@ -226,28 +291,48 @@ const module = defineNuxtModule({
|
|
|
226
291
|
},
|
|
227
292
|
defaults(nuxt) {
|
|
228
293
|
const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || process.env.NUXT_SITE_URL || nuxt.options.runtimeConfig.public?.siteUrl || nuxt.options.runtimeConfig.siteUrl;
|
|
229
|
-
const isEdgeProvider = edgeProvidersSupported.includes(process.env.NITRO_PRESET || "") || edgeProvidersSupported.includes(nuxt.options.nitro.preset);
|
|
230
294
|
return {
|
|
231
|
-
// when we run `nuxi generate` we need to force prerendering
|
|
232
|
-
forcePrerender: !nuxt.options.dev && nuxt.options._generate,
|
|
233
295
|
siteUrl,
|
|
234
296
|
defaults: {
|
|
235
297
|
component: "OgImageBasic",
|
|
236
298
|
width: 1200,
|
|
237
|
-
height: 630
|
|
299
|
+
height: 630,
|
|
300
|
+
cacheTtl: 24 * 60 * 60 * 1e3
|
|
301
|
+
// default is to cache the image for 24 hours
|
|
238
302
|
},
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
browserProvider: !isEdgeProvider,
|
|
303
|
+
runtimeSatori: true,
|
|
304
|
+
runtimeBrowser: nuxt.options.dev,
|
|
242
305
|
fonts: [],
|
|
306
|
+
runtimeCacheStorage: false,
|
|
243
307
|
satoriOptions: {},
|
|
244
|
-
|
|
245
|
-
|
|
308
|
+
playground: process.env.NODE_ENV === "development" || nuxt.options.dev,
|
|
309
|
+
debug: false
|
|
246
310
|
};
|
|
247
311
|
},
|
|
248
312
|
async setup(config, nuxt) {
|
|
313
|
+
const logger = useLogger("nuxt-og-image");
|
|
249
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
|
+
}
|
|
250
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
|
+
}
|
|
251
336
|
if (!config.fonts.length)
|
|
252
337
|
config.fonts = ["Inter:400", "Inter:700"];
|
|
253
338
|
const distResolve = (p) => {
|
|
@@ -279,12 +364,14 @@ export {}
|
|
|
279
364
|
lazy: true,
|
|
280
365
|
handler: resolve("./runtime/nitro/middleware/og.png")
|
|
281
366
|
});
|
|
282
|
-
["html", "options", "svg", "vnode", "font"].forEach((type) => {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
+
}
|
|
288
375
|
});
|
|
289
376
|
nuxt.hook("devtools:customTabs", (iframeTabs) => {
|
|
290
377
|
iframeTabs.push({
|
|
@@ -334,57 +421,76 @@ export {}
|
|
|
334
421
|
const runtimeDir = resolve("./runtime");
|
|
335
422
|
nuxt.options.build.transpile.push(runtimeDir);
|
|
336
423
|
addServerPlugin(resolve("./runtime/nitro/plugins/prerender"));
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
resolve(
|
|
340
|
-
moduleAssetDir
|
|
424
|
+
const customAssetDirs = [
|
|
425
|
+
// allows us to show custom error images
|
|
426
|
+
resolve("./runtime/public-assets")
|
|
341
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
|
+
}
|
|
342
438
|
nuxt.hooks.hook("modules:done", async () => {
|
|
343
439
|
nuxt.hooks.callHook("og-image:config", config);
|
|
344
|
-
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
|
+
};
|
|
345
449
|
});
|
|
346
|
-
const useSatoriWasm = provider === "stackblitz";
|
|
347
450
|
nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
|
|
348
451
|
nitroConfig.externals = defu(nitroConfig.externals || {}, {
|
|
349
452
|
inline: [runtimeDir]
|
|
350
453
|
});
|
|
454
|
+
if (config.runtimeBrowser) {
|
|
455
|
+
nitroConfig.alias = nitroConfig.alias || {};
|
|
456
|
+
nitroConfig.alias.electron = "unenv/runtime/mock/proxy-cjs";
|
|
457
|
+
nitroConfig.alias.bufferutil = "unenv/runtime/mock/proxy-cjs";
|
|
458
|
+
nitroConfig.alias["utf-8-validate"] = "unenv/runtime/mock/proxy-cjs";
|
|
459
|
+
}
|
|
351
460
|
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
|
|
352
|
-
|
|
461
|
+
customAssetDirs.forEach((dir) => {
|
|
462
|
+
nitroConfig.publicAssets.push({ dir, maxAge: 31536e3 });
|
|
463
|
+
});
|
|
353
464
|
const providerPath = `${runtimeDir}/nitro/providers`;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
nitroConfig.virtual["#nuxt-og-image/browser"] = nuxt.options.dev || process.env.prerender || isNodeNitroServer ? `
|
|
358
|
-
import node from '${providerPath}/browser/node'
|
|
359
|
-
|
|
465
|
+
if (config.runtimeBrowser) {
|
|
466
|
+
nitroConfig.virtual["#nuxt-og-image/browser"] = `
|
|
467
|
+
let browser
|
|
360
468
|
export default async function() {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
` : `export default async function() {
|
|
364
|
-
return () => {}
|
|
469
|
+
browser = browser || await import('${providerPath}/browser/${nitroCompatibility.browser}').then((m) => m.default || m)
|
|
470
|
+
return browser
|
|
365
471
|
}
|
|
366
472
|
`;
|
|
367
473
|
}
|
|
368
|
-
if (config.
|
|
369
|
-
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}'
|
|
370
476
|
export default async function() {
|
|
371
477
|
return satori
|
|
372
478
|
}`;
|
|
373
|
-
nitroConfig.virtual["#nuxt-og-image/
|
|
374
|
-
import svg2png from '${providerPath}/svg2png/universal'
|
|
479
|
+
nitroConfig.virtual["#nuxt-og-image/png"] = `import png from '${providerPath}/png/${nitroCompatibility.png}'
|
|
375
480
|
export default async function() {
|
|
376
|
-
return
|
|
377
|
-
}
|
|
481
|
+
return png
|
|
482
|
+
}
|
|
483
|
+
`;
|
|
378
484
|
}
|
|
379
485
|
nitroConfig.virtual["#nuxt-og-image/provider"] = `
|
|
380
|
-
${config.
|
|
381
|
-
${config.
|
|
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"))}'` : ""}
|
|
382
488
|
|
|
383
489
|
export async function useProvider(provider) {
|
|
384
490
|
if (provider === 'satori')
|
|
385
|
-
return ${config.
|
|
491
|
+
return ${config.runtimeSatori ? "satori" : "null"}
|
|
386
492
|
if (provider === 'browser')
|
|
387
|
-
return ${config.
|
|
493
|
+
return ${config.runtimeBrowser ? "browser" : "null"}
|
|
388
494
|
return null
|
|
389
495
|
}
|
|
390
496
|
`;
|
|
@@ -392,36 +498,40 @@ export async function useProvider(provider) {
|
|
|
392
498
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
393
499
|
let screenshotQueue = [];
|
|
394
500
|
nitro.hooks.hook("compiled", async (_nitro) => {
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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;
|
|
402
520
|
}
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
indexFile,
|
|
414
|
-
indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', 'import("./svg2png.wasm").then(m => m.default || m)').replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', 'import("./yoga.wasm").then(m => m.default || m)')
|
|
415
|
-
);
|
|
416
|
-
} else {
|
|
417
|
-
const svg2pngWasm = await readFile(resolve("./runtime/public-assets/svg2png.wasm"), "base64");
|
|
418
|
-
const yogaWasm = await readFile(resolve("./runtime/public-assets/yoga.wasm"), "base64");
|
|
419
|
-
await writeFile(
|
|
420
|
-
indexFile,
|
|
421
|
-
indexContents.replace('"/* NUXT_OG_IMAGE_SVG2PNG_WASM */"', `Buffer.from("${svg2pngWasm}", "base64")`).replace('"/* NUXT_OG_IMAGE_YOGA_WASM */"', `Buffer.from("${yogaWasm}", "base64")`)
|
|
422
|
-
);
|
|
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;
|
|
423
531
|
}
|
|
424
532
|
}
|
|
533
|
+
if (updated)
|
|
534
|
+
await writeFile(path, contents, { encoding: "utf-8" });
|
|
425
535
|
}
|
|
426
536
|
});
|
|
427
537
|
const _routeRulesMatcher = toRouteMatcher(
|
|
@@ -497,8 +607,8 @@ export async function useProvider(provider) {
|
|
|
497
607
|
const entry = screenshotQueue[k];
|
|
498
608
|
const start = Date.now();
|
|
499
609
|
let hasError = false;
|
|
500
|
-
const
|
|
501
|
-
const filename = joinURL(
|
|
610
|
+
const dirname2 = joinURL(nitro.options.output.publicDir, entry.route, "/__og_image__/");
|
|
611
|
+
const filename = joinURL(dirname2, "/og.png");
|
|
502
612
|
try {
|
|
503
613
|
const imgBuffer = await screenshot(browser, {
|
|
504
614
|
...config.defaults || {},
|
|
@@ -506,7 +616,7 @@ export async function useProvider(provider) {
|
|
|
506
616
|
host
|
|
507
617
|
});
|
|
508
618
|
try {
|
|
509
|
-
await mkdirp(
|
|
619
|
+
await mkdirp(dirname2);
|
|
510
620
|
} catch (e) {
|
|
511
621
|
}
|
|
512
622
|
await writeFile(filename, imgBuffer);
|
|
@@ -17,12 +17,12 @@ 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
24
|
const screenshotOptions = {
|
|
25
|
-
timeout: 1e4
|
|
25
|
+
timeout: process.env.prerender || process.dev ? 1e4 : 2e3
|
|
26
26
|
};
|
|
27
27
|
if (options.delay)
|
|
28
28
|
await page.waitForTimeout(options.delay);
|
|
@@ -34,5 +34,7 @@ export async function screenshot(browser, options) {
|
|
|
34
34
|
}
|
|
35
35
|
if (options.selector)
|
|
36
36
|
return await page.locator(options.selector).screenshot(screenshotOptions);
|
|
37
|
-
|
|
37
|
+
const screenshot2 = await page.screenshot(screenshotOptions);
|
|
38
|
+
await page.close();
|
|
39
|
+
return screenshot2;
|
|
38
40
|
}
|
|
@@ -14,23 +14,26 @@ 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
|
+
let resolveSrc = function() {
|
|
35
|
+
return withBase(`${route === "/" ? "" : route}/__og_image__/og.png`, baseUrl);
|
|
36
|
+
};
|
|
34
37
|
const { defaults, siteUrl } = useRuntimeConfig()["nuxt-og-image"];
|
|
35
38
|
const router = useRouter();
|
|
36
39
|
const route = router?.currentRoute?.value?.path || "";
|
|
@@ -43,11 +46,11 @@ export function defineOgImage(options = {}) {
|
|
|
43
46
|
},
|
|
44
47
|
{
|
|
45
48
|
name: "twitter:image:src",
|
|
46
|
-
content:
|
|
49
|
+
content: resolveSrc
|
|
47
50
|
},
|
|
48
51
|
{
|
|
49
52
|
property: "og:image",
|
|
50
|
-
content:
|
|
53
|
+
content: resolveSrc
|
|
51
54
|
},
|
|
52
55
|
{
|
|
53
56
|
property: "og:image:width",
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { createError, defineEventHandler, sendRedirect, setHeader } from "h3";
|
|
3
|
+
import { joinURL, parseURL, withBase, withoutTrailingSlash } from "ufo";
|
|
4
|
+
import { prefixStorage } from "unstorage";
|
|
3
5
|
import { fetchOptions, useHostname } from "../utils.mjs";
|
|
4
6
|
import { useProvider } from "#nuxt-og-image/provider";
|
|
7
|
+
import { useRuntimeConfig, useStorage } from "#imports";
|
|
5
8
|
export default defineEventHandler(async (e) => {
|
|
9
|
+
const { runtimeBrowser, runtimeCacheStorage } = useRuntimeConfig()["nuxt-og-image"];
|
|
6
10
|
const path = parseURL(e.path).pathname;
|
|
7
11
|
if (!path.endsWith("__og_image__/og.png"))
|
|
8
12
|
return;
|
|
9
13
|
const basePath = withoutTrailingSlash(
|
|
10
14
|
path.replace("__og_image__/og.png", "")
|
|
11
15
|
);
|
|
12
|
-
setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
|
|
13
|
-
setHeader(e, "Pragma", "no-cache");
|
|
14
|
-
setHeader(e, "Expires", "0");
|
|
15
16
|
const options = await fetchOptions(e, basePath);
|
|
17
|
+
if (process.env.NODE_ENV === "production" && !process.env.prerender && !runtimeBrowser && options.provider === "browser")
|
|
18
|
+
return sendRedirect(e, joinURL(useHostname(e), "__nuxt_og_image__/browser-provider-not-supported.png"));
|
|
16
19
|
const provider = await useProvider(options.provider);
|
|
17
20
|
if (!provider) {
|
|
18
21
|
throw createError({
|
|
@@ -20,21 +23,46 @@ export default defineEventHandler(async (e) => {
|
|
|
20
23
|
statusMessage: `Provider ${options.provider} is missing.`
|
|
21
24
|
});
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const cache = prefixStorage(useStorage(), "og-image-cache:images");
|
|
27
|
+
const key = options.cacheKey || e.node.req.url;
|
|
28
|
+
let png;
|
|
29
|
+
if (runtimeCacheStorage && await cache.hasItem(key)) {
|
|
30
|
+
const { value, expiresAt } = await cache.getItem(key);
|
|
31
|
+
if (expiresAt > Date.now()) {
|
|
32
|
+
setHeader(e, "Cache-Control", "public, max-age=31536000");
|
|
26
33
|
setHeader(e, "Content-Type", "image/png");
|
|
27
|
-
|
|
34
|
+
png = Buffer.from(value, "base64");
|
|
35
|
+
} else {
|
|
36
|
+
await cache.removeItem(key);
|
|
28
37
|
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
}
|
|
39
|
+
if (!png) {
|
|
40
|
+
try {
|
|
41
|
+
png = await provider.createPng(withBase(basePath, useHostname(e)), options);
|
|
42
|
+
if (png && runtimeCacheStorage && options.static) {
|
|
43
|
+
const base64png = Buffer.from(png).toString("base64");
|
|
44
|
+
await cache.setItem(key, { value: base64png, expiresAt: Date.now() + (options.cacheTtl || 0) });
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
throw createError({
|
|
48
|
+
statusCode: 500,
|
|
49
|
+
statusMessage: `Failed to create og image: ${err.message}`
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (png) {
|
|
54
|
+
if (!process.dev && options.static) {
|
|
55
|
+
setHeader(e, "Cache-Control", "public, max-age=31536000");
|
|
56
|
+
} else {
|
|
57
|
+
setHeader(e, "Cache-Control", "no-cache, no-store, must-revalidate");
|
|
58
|
+
setHeader(e, "Pragma", "no-cache");
|
|
59
|
+
setHeader(e, "Expires", "0");
|
|
60
|
+
}
|
|
61
|
+
setHeader(e, "Content-Type", "image/png");
|
|
62
|
+
return png;
|
|
34
63
|
}
|
|
35
64
|
throw createError({
|
|
36
65
|
statusCode: 500,
|
|
37
66
|
statusMessage: "Failed to create og image, unknown error."
|
|
38
67
|
});
|
|
39
|
-
return false;
|
|
40
68
|
});
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { appendHeader } from "h3";
|
|
2
2
|
import { joinURL } from "ufo";
|
|
3
3
|
import { extractOgImageOptions } from "../utils-pure.mjs";
|
|
4
|
-
import { useRuntimeConfig } from "#imports";
|
|
5
4
|
const OgImagePrenderNitroPlugin = (nitroApp) => {
|
|
6
5
|
if (!process.env.prerender)
|
|
7
6
|
return;
|
|
8
|
-
const { forcePrerender } = useRuntimeConfig()["nuxt-og-image"];
|
|
9
7
|
nitroApp.hooks.hook("render:html", async (ctx, { event }) => {
|
|
10
8
|
const url = event.node.req.url;
|
|
11
9
|
if (url.includes(".") || url.startsWith("/__nuxt_island/"))
|
|
@@ -13,7 +11,7 @@ const OgImagePrenderNitroPlugin = (nitroApp) => {
|
|
|
13
11
|
const options = extractOgImageOptions(ctx.head.join("\n"));
|
|
14
12
|
if (!options)
|
|
15
13
|
return;
|
|
16
|
-
if (
|
|
14
|
+
if (options.static && options.provider === "satori")
|
|
17
15
|
appendHeader(event, "x-nitro-prerender", joinURL(url, "/__og_image__/og.png"));
|
|
18
16
|
});
|
|
19
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function createBrowser(): Promise<import("
|
|
1
|
+
export default function createBrowser(): Promise<import("playwright-core").Browser>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import awsChromium from "chrome-aws-lambda";
|
|
2
|
+
import { chromium } from "playwright-core";
|
|
3
3
|
export default async function createBrowser() {
|
|
4
|
-
return
|
|
5
|
-
args:
|
|
6
|
-
executablePath: await
|
|
4
|
+
return await chromium.launch({
|
|
5
|
+
args: awsChromium.args,
|
|
6
|
+
executablePath: await awsChromium.executablePath,
|
|
7
7
|
headless: true
|
|
8
8
|
});
|
|
9
9
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import playwrightCore from "playwright-core";
|
|
2
|
+
export default async function createBrowser() {
|
|
3
|
+
try {
|
|
4
|
+
return await playwrightCore.chromium.launch({
|
|
5
|
+
headless: true
|
|
6
|
+
});
|
|
7
|
+
} catch (e) {
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const playwright = await import(String("playwright"));
|
|
11
|
+
return await playwright.chromium.launch({
|
|
12
|
+
headless: true
|
|
13
|
+
});
|
|
14
|
+
} catch (e) {
|
|
15
|
+
if (process.dev) {
|
|
16
|
+
console.warn("Failed to load chromium instance. Ensure you have chrome installed, otherwise add the dependency: `npm add -D playwright`.");
|
|
17
|
+
} else {
|
|
18
|
+
console.error("Failed to load browser instance. Please open an issue with the exception: https://github.com/harlan-zw/nuxt-og-image/issues.");
|
|
19
|
+
throw e;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function createBrowser(): Promise<any>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Resvg, initWasm } from "@resvg/resvg-wasm";
|
|
2
|
+
import { wasmLoader } from "../../utils.mjs";
|
|
3
|
+
const ReSvgLoader = wasmLoader("/* NUXT_OG_IMAGE_RESVG_WASM */", "/resvg.wasm");
|
|
4
|
+
export default async function(svg, options) {
|
|
5
|
+
const ReSvgWasm = await ReSvgLoader.load({ baseUrl: options.baseUrl });
|
|
6
|
+
await initWasm(ReSvgWasm).catch(() => {
|
|
7
|
+
});
|
|
8
|
+
const resvgJS = new Resvg(svg, {});
|
|
9
|
+
const pngData = resvgJS.render();
|
|
10
|
+
return pngData.asPng();
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { initialize, svg2png } from "svg2png-wasm";
|
|
2
|
+
import { wasmLoader } from "../../utils.mjs";
|
|
3
|
+
const Svg2PngLoader = wasmLoader("/* NUXT_OG_IMAGE_SVG2PNG_WASM */", "/svg2png.wasm");
|
|
4
|
+
export default async function(svg, options) {
|
|
5
|
+
const Svg2PngWasm = await Svg2PngLoader.load({ baseUrl: options.baseUrl });
|
|
6
|
+
await initialize(Svg2PngWasm).catch((e) => {
|
|
7
|
+
if (!e.message.trim().endsWith("function can be used only once."))
|
|
8
|
+
throw e;
|
|
9
|
+
});
|
|
10
|
+
return await svg2png(svg, options);
|
|
11
|
+
}
|