nuxt-og-image 5.0.5 → 5.1.1
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/dist/client/200.html +10 -10
- package/dist/client/404.html +10 -10
- package/dist/client/_nuxt/{KOcez9Jj.js → BKhpHsoX.js} +1 -1
- package/dist/client/_nuxt/BLmTiKMJ.js +1 -0
- package/dist/client/_nuxt/{mfh_Mgvx.js → DVcaHLff.js} +1 -1
- package/dist/client/_nuxt/DpHmQ6sx.js +3991 -0
- package/dist/client/_nuxt/Dy0dDCjl.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/c5f3b216-b4a4-46fd-8c8f-d7974f7d29a9.json +1 -0
- package/dist/client/_nuxt/entry.Dw_RMJvc.css +1 -0
- package/dist/client/_nuxt/error-404.Db0f7OxX.css +1 -0
- package/dist/client/_nuxt/error-500.CgBP5IsV.css +1 -0
- package/dist/client/index.html +10 -10
- package/dist/module.cjs +215 -147
- package/dist/module.d.cts +1 -1
- package/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +217 -149
- package/dist/runtime/app/composables/defineOgImage.js +0 -4
- package/dist/runtime/app/composables/mock.js +14 -0
- package/dist/runtime/app/utils/plugins.js +2 -0
- package/dist/runtime/server/og-image/context.js +19 -9
- package/dist/runtime/server/og-image/satori/plugins/nuxt-icon.d.ts +2 -0
- package/dist/runtime/server/og-image/satori/plugins/nuxt-icon.js +20 -0
- package/dist/runtime/server/og-image/satori/transforms/inlineCss.js +1 -1
- package/dist/runtime/server/og-image/satori/vnodes.js +3 -1
- package/dist/runtime/server/util/encoding.js +2 -2
- package/dist/runtime/types.d.ts +1 -0
- package/package.json +12 -12
- package/virtual.d.ts +2 -2
- package/dist/client/_nuxt/CRW4c9S-.js +0 -3979
- package/dist/client/_nuxt/DGRTSKzu.js +0 -1
- package/dist/client/_nuxt/DTAJTTim.js +0 -1
- package/dist/client/_nuxt/builds/meta/63cde3fb-9e50-40ee-a6eb-2bc55a19f306.json +0 -1
- package/dist/client/_nuxt/entry.DPIkPTtM.css +0 -1
- package/dist/client/_nuxt/error-404.BCnUGQCq.css +0 -1
- package/dist/client/_nuxt/error-500.DaZuPQqN.css +0 -1
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { resolvePath, useNuxt, addTemplate, loadNuxtModuleInstance, createResolver, defineNuxtModule, addImports, addBuildPlugin, hasNuxtModule, hasNuxtModuleCompatibility, addServerPlugin,
|
|
4
|
+
import { resolvePath, useNuxt, addTemplate, updateTemplates, loadNuxtModuleInstance, createResolver, defineNuxtModule, addImports, addBuildPlugin, addServerHandler, hasNuxtModule, hasNuxtModuleCompatibility, addServerPlugin, addComponentsDir, addComponent, addPlugin } from '@nuxt/kit';
|
|
5
5
|
import { defu } from 'defu';
|
|
6
6
|
import { installNuxtSiteConfig } from 'nuxt-site-config/kit';
|
|
7
7
|
import { hash } from 'ohash';
|
|
@@ -186,10 +186,11 @@ async function applyNitroPresetCompatibility(nitroConfig, options) {
|
|
|
186
186
|
const { resolve } = options;
|
|
187
187
|
const satoriEnabled = typeof options.compatibility?.satori !== "undefined" ? !!options.compatibility.satori : !!compatibility.satori;
|
|
188
188
|
const chromiumEnabled = typeof options.compatibility?.chromium !== "undefined" ? !!options.compatibility.chromium : !!compatibility.chromium;
|
|
189
|
-
|
|
190
|
-
nitroConfig.alias["#og-image/renderers/
|
|
189
|
+
const emptyMock = await resolve.resolvePath("./runtime/mock/empty");
|
|
190
|
+
nitroConfig.alias["#og-image/renderers/satori"] = satoriEnabled ? await resolve.resolvePath("./runtime/server/og-image/satori/renderer") : emptyMock;
|
|
191
|
+
nitroConfig.alias["#og-image/renderers/chromium"] = chromiumEnabled ? await resolve.resolvePath("./runtime/server/og-image/chromium/renderer") : emptyMock;
|
|
191
192
|
const resolvedCompatibility = {};
|
|
192
|
-
function applyBinding(key) {
|
|
193
|
+
async function applyBinding(key) {
|
|
193
194
|
let binding = options.compatibility?.[key];
|
|
194
195
|
if (typeof binding === "undefined")
|
|
195
196
|
binding = compatibility[key];
|
|
@@ -202,15 +203,15 @@ async function applyNitroPresetCompatibility(nitroConfig, options) {
|
|
|
202
203
|
}
|
|
203
204
|
resolvedCompatibility[key] = binding;
|
|
204
205
|
return {
|
|
205
|
-
[`#og-image/bindings/${key}`]: binding === false ?
|
|
206
|
+
[`#og-image/bindings/${key}`]: binding === false ? emptyMock : await resolve.resolvePath(`./runtime/server/og-image/bindings/${key}/${binding}`)
|
|
206
207
|
};
|
|
207
208
|
}
|
|
208
209
|
nitroConfig.alias = defu(
|
|
209
|
-
applyBinding("chromium"),
|
|
210
|
-
applyBinding("satori"),
|
|
211
|
-
applyBinding("resvg"),
|
|
212
|
-
applyBinding("sharp"),
|
|
213
|
-
applyBinding("css-inline"),
|
|
210
|
+
await applyBinding("chromium"),
|
|
211
|
+
await applyBinding("satori"),
|
|
212
|
+
await applyBinding("resvg"),
|
|
213
|
+
await applyBinding("sharp"),
|
|
214
|
+
await applyBinding("css-inline"),
|
|
214
215
|
nitroConfig.alias || {}
|
|
215
216
|
);
|
|
216
217
|
if (Object.values(compatibility).includes("wasm")) {
|
|
@@ -244,11 +245,11 @@ async function setupBuildHandler(config, resolve, nuxt = useNuxt()) {
|
|
|
244
245
|
nuxt.options.nitro.storage["og-image"] = config.runtimeCacheStorage;
|
|
245
246
|
nuxt.hooks.hook("nitro:config", async (nitroConfig) => {
|
|
246
247
|
await applyNitroPresetCompatibility(nitroConfig, { compatibility: config.compatibility?.runtime, resolve });
|
|
247
|
-
nitroConfig.alias.electron = resolve("./runtime/mock/proxy-cjs");
|
|
248
|
-
nitroConfig.alias.bufferutil = resolve("./runtime/mock/proxy-cjs");
|
|
249
|
-
nitroConfig.alias["utf-8-validate"] = resolve("./runtime/mock/proxy-cjs");
|
|
250
|
-
nitroConfig.alias.queue = resolve("./runtime/mock/proxy-cjs");
|
|
251
|
-
nitroConfig.alias["chromium-bidi"] = resolve("./runtime/mock/proxy-cjs");
|
|
248
|
+
nitroConfig.alias.electron = await resolve.resolvePath("./runtime/mock/proxy-cjs");
|
|
249
|
+
nitroConfig.alias.bufferutil = await resolve.resolvePath("./runtime/mock/proxy-cjs");
|
|
250
|
+
nitroConfig.alias["utf-8-validate"] = await resolve.resolvePath("./runtime/mock/proxy-cjs");
|
|
251
|
+
nitroConfig.alias.queue = await resolve.resolvePath("./runtime/mock/proxy-cjs");
|
|
252
|
+
nitroConfig.alias["chromium-bidi"] = await resolve.resolvePath("./runtime/mock/proxy-cjs");
|
|
252
253
|
});
|
|
253
254
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
254
255
|
const target = resolveNitroPreset(nitro.options);
|
|
@@ -265,12 +266,12 @@ async function setupBuildHandler(config, resolve, nuxt = useNuxt()) {
|
|
|
265
266
|
if (compatibility.wasm?.esmImport !== true)
|
|
266
267
|
return;
|
|
267
268
|
const configuredEntry = nitro.options.rollupConfig?.output.entryFileNames;
|
|
268
|
-
const serverEntry = resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
|
|
269
|
+
const serverEntry = resolve.resolve(_nitro.options.output.serverDir, typeof configuredEntry === "string" ? configuredEntry : "index.mjs");
|
|
269
270
|
const wasmEntries = [serverEntry];
|
|
270
271
|
if (isCloudflarePagesOrModule) {
|
|
271
|
-
wasmEntries.push(resolve(dirname(serverEntry), "./chunks/wasm.mjs"));
|
|
272
|
-
wasmEntries.push(resolve(dirname(serverEntry), "./chunks/_/wasm.mjs"));
|
|
273
|
-
wasmEntries.push(resolve(dirname(serverEntry), "./chunks/index_bg.mjs"));
|
|
272
|
+
wasmEntries.push(resolve.resolve(dirname(serverEntry), "./chunks/wasm.mjs"));
|
|
273
|
+
wasmEntries.push(resolve.resolve(dirname(serverEntry), "./chunks/_/wasm.mjs"));
|
|
274
|
+
wasmEntries.push(resolve.resolve(dirname(serverEntry), "./chunks/index_bg.mjs"));
|
|
274
275
|
}
|
|
275
276
|
const resvgHash = await resolveFilePathSha1("@resvg/resvg-wasm/index_bg.wasm");
|
|
276
277
|
const yogaHash = await resolveFilePathSha1("yoga-wasm-web/dist/yoga.wasm");
|
|
@@ -325,8 +326,27 @@ function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
|
|
|
325
326
|
};
|
|
326
327
|
});
|
|
327
328
|
}
|
|
329
|
+
const useNitro = new Promise((resolve2) => {
|
|
330
|
+
nuxt.hooks.hook("nitro:init", resolve2);
|
|
331
|
+
});
|
|
328
332
|
onDevToolsInitialized(async () => {
|
|
329
|
-
const rpc = extendServerRpc("nuxt-og-image", {
|
|
333
|
+
const rpc = extendServerRpc("nuxt-og-image", {
|
|
334
|
+
async ejectCommunityTemplate(path) {
|
|
335
|
+
const [dirName, componentName] = path.split("/");
|
|
336
|
+
const dir = resolve(nuxt.options.rootDir, "components", dirName);
|
|
337
|
+
if (!existsSync(dir)) {
|
|
338
|
+
mkdirSync(dir);
|
|
339
|
+
}
|
|
340
|
+
const newPath = resolve(dir, componentName);
|
|
341
|
+
const templatePath = resolve(`./runtime/app/components/Templates/Community/${componentName}`);
|
|
342
|
+
const template = (await readFile(templatePath, "utf-8")).replace("{{ title }}", `{{ title }} - Ejected!`);
|
|
343
|
+
await writeFile(newPath, template, { encoding: "utf-8" });
|
|
344
|
+
await updateTemplates({ filter: (t) => t.filename.includes("nuxt-og-image/components.mjs") });
|
|
345
|
+
const nitro = await useNitro;
|
|
346
|
+
await nitro.hooks.callHook("rollup:reload");
|
|
347
|
+
return newPath;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
330
350
|
nuxt.hook("builder:watch", (e, path) => {
|
|
331
351
|
path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path));
|
|
332
352
|
if ((e === "change" || e.includes("link")) && (path.startsWith("pages") || path.startsWith("content"))) {
|
|
@@ -526,6 +546,10 @@ function normaliseFontInput(fonts) {
|
|
|
526
546
|
});
|
|
527
547
|
}
|
|
528
548
|
|
|
549
|
+
function isProviderEnabledForEnv(provider, nuxt, config) {
|
|
550
|
+
return nuxt.options.dev && config.compatibility?.dev?.[provider] !== false || !nuxt.options.dev && (config.compatibility?.runtime?.[provider] !== false || config.compatibility?.prerender?.[provider] !== false);
|
|
551
|
+
}
|
|
552
|
+
const defaultComponentDirs = ["OgImage", "og-image", "OgImageTemplate"];
|
|
529
553
|
const module = defineNuxtModule({
|
|
530
554
|
meta: {
|
|
531
555
|
name: "nuxt-og-image",
|
|
@@ -548,14 +572,15 @@ const module = defineNuxtModule({
|
|
|
548
572
|
// default is to cache the image for 3 day (72 hours)
|
|
549
573
|
cacheMaxAgeSeconds: 60 * 60 * 24 * 3
|
|
550
574
|
},
|
|
551
|
-
componentDirs:
|
|
575
|
+
componentDirs: defaultComponentDirs,
|
|
552
576
|
fonts: [],
|
|
553
577
|
runtimeCacheStorage: true,
|
|
554
578
|
debug: isDevelopment
|
|
555
579
|
};
|
|
556
580
|
},
|
|
557
581
|
async setup(config, nuxt) {
|
|
558
|
-
const
|
|
582
|
+
const resolver = createResolver(import.meta.url);
|
|
583
|
+
const { resolve } = resolver;
|
|
559
584
|
const { version } = await readPackageJSON(resolve("../package.json"));
|
|
560
585
|
logger.level = config.debug || nuxt.options.debug ? 4 : 3;
|
|
561
586
|
if (config.enabled === false) {
|
|
@@ -587,68 +612,152 @@ const module = defineNuxtModule({
|
|
|
587
612
|
nuxt.options.alias["#og-image-cache"] = resolve("./runtime/server/og-image/cache/mock");
|
|
588
613
|
}
|
|
589
614
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
615
|
+
const basePath = config.zeroRuntime ? "./runtime/server/routes/__zero-runtime" : "./runtime/server/routes";
|
|
616
|
+
let publicDirAbs = nuxt.options.dir.public;
|
|
617
|
+
if (!isAbsolute(publicDirAbs)) {
|
|
618
|
+
publicDirAbs = publicDirAbs in nuxt.options.alias ? nuxt.options.alias[publicDirAbs] : resolve(nuxt.options.rootDir, publicDirAbs);
|
|
619
|
+
}
|
|
620
|
+
if (isProviderEnabledForEnv("satori", nuxt, config)) {
|
|
621
|
+
let isUsingSharp = false;
|
|
622
|
+
if (isProviderEnabledForEnv("sharp", nuxt, config)) {
|
|
623
|
+
const userConfiguredExtension = config.defaults.extension;
|
|
624
|
+
const hasConfiguredJpegs = userConfiguredExtension && ["jpeg", "jpg"].includes(userConfiguredExtension);
|
|
625
|
+
if (!!config.sharpOptions || hasConfiguredJpegs && config.defaults.renderer !== "chromium") {
|
|
626
|
+
isUsingSharp = true;
|
|
627
|
+
const hasSharpDependency = await hasResolvableDependency("sharp");
|
|
628
|
+
if (hasSharpDependency && !targetCompatibility.sharp) {
|
|
629
|
+
logger.warn(`Rendering JPEGs requires sharp which does not work with ${preset}. Images will be rendered as PNG at runtime.`);
|
|
630
|
+
config.compatibility = defu(config.compatibility, {
|
|
631
|
+
runtime: { sharp: false }
|
|
632
|
+
});
|
|
633
|
+
} else if (!hasSharpDependency) {
|
|
634
|
+
logger.warn("You have enabled `JPEG` images. These require the `sharp` dependency which is missing, installing it for you.");
|
|
635
|
+
await ensureDependencies(["sharp"]);
|
|
636
|
+
logger.warn("Support for `sharp` is limited so check the compatibility guide.");
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (!isUsingSharp) {
|
|
598
641
|
config.compatibility = defu(config.compatibility, {
|
|
599
|
-
runtime: { sharp: false }
|
|
642
|
+
runtime: { sharp: false },
|
|
643
|
+
dev: { sharp: false },
|
|
644
|
+
prerender: { sharp: false }
|
|
600
645
|
});
|
|
601
|
-
} else if (!hasSharpDependency) {
|
|
602
|
-
logger.warn("You have enabled `JPEG` images. These require the `sharp` dependency which is missing, installing it for you.");
|
|
603
|
-
await ensureDependencies(["sharp"]);
|
|
604
|
-
logger.warn("Support for `sharp` is limited so check the compatibility guide.");
|
|
605
646
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
647
|
+
if (isProviderEnabledForEnv("resvg", nuxt, config)) {
|
|
648
|
+
await import('@resvg/resvg-js').catch(() => {
|
|
649
|
+
logger.warn("ReSVG is missing dependencies for environment. Falling back to WASM version, this may slow down PNG rendering.");
|
|
650
|
+
config.compatibility = defu(config.compatibility, {
|
|
651
|
+
dev: { resvg: "wasm-fs" },
|
|
652
|
+
prerender: { resvg: "wasm-fs" }
|
|
653
|
+
});
|
|
654
|
+
if (targetCompatibility.resvg === "node") {
|
|
655
|
+
config.compatibility = defu(config.compatibility, {
|
|
656
|
+
runtime: { resvg: "wasm" }
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
if (!config.fonts.length) {
|
|
662
|
+
config.fonts = [
|
|
663
|
+
{
|
|
664
|
+
name: "Inter",
|
|
665
|
+
weight: 400,
|
|
666
|
+
path: resolve("./runtime/assets/Inter-normal-400.ttf.base64"),
|
|
667
|
+
absolutePath: true
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: "Inter",
|
|
671
|
+
weight: 700,
|
|
672
|
+
path: resolve("./runtime/assets/Inter-normal-700.ttf.base64"),
|
|
673
|
+
absolutePath: true
|
|
674
|
+
}
|
|
675
|
+
];
|
|
676
|
+
}
|
|
677
|
+
const serverFontsDir = resolve(nuxt.options.buildDir, "cache", `nuxt-og-image@${version}`, "_fonts");
|
|
678
|
+
const fontStorage = createStorage({
|
|
679
|
+
driver: fsDriver({
|
|
680
|
+
base: serverFontsDir
|
|
681
|
+
})
|
|
682
|
+
});
|
|
683
|
+
config.fonts = (await Promise.all(normaliseFontInput(config.fonts).map(async (f) => {
|
|
684
|
+
const fontKey = `${f.name}:${f.style}:${f.weight}`;
|
|
685
|
+
const fontFileBase = fontKey.replaceAll(":", "-");
|
|
686
|
+
if (!f.key && !f.path) {
|
|
687
|
+
if (preset === "stackblitz") {
|
|
688
|
+
logger.warn(`The ${fontKey} font was skipped because remote fonts are not available in StackBlitz, please use a local font.`);
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
if (await downloadFont(f, fontStorage, config.googleFontMirror)) {
|
|
692
|
+
f.key = `nuxt-og-image:fonts:${fontFileBase}.ttf.base64`;
|
|
693
|
+
} else {
|
|
694
|
+
logger.warn(`Failed to download font ${fontKey}. You may be offline or behind a firewall blocking Google. Consider setting \`googleFontMirror: true\`.`);
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
} else if (f.path) {
|
|
698
|
+
const extension = basename(f.path.replace(".base64", "")).split(".").pop();
|
|
699
|
+
if (!["woff", "ttf", "otf"].includes(extension)) {
|
|
700
|
+
logger.warn(`The ${fontKey} font was skipped because the file extension ${extension} is not supported. Only woff, ttf and otf are supported.`);
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
if (!f.absolutePath)
|
|
704
|
+
f.path = resolve(publicDirAbs, withoutLeadingSlash(f.path));
|
|
705
|
+
if (!existsSync(f.path)) {
|
|
706
|
+
logger.warn(`The ${fontKey} font was skipped because the file does not exist at path ${f.path}.`);
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
const fontData = await readFile(f.path, f.path.endsWith(".base64") ? "utf-8" : "base64");
|
|
710
|
+
f.key = `nuxt-og-image:fonts:${fontFileBase}.${extension}.base64`;
|
|
711
|
+
await fontStorage.setItem(`${fontFileBase}.${extension}.base64`, fontData);
|
|
712
|
+
delete f.path;
|
|
713
|
+
delete f.absolutePath;
|
|
714
|
+
}
|
|
715
|
+
return f;
|
|
716
|
+
}))).filter(Boolean);
|
|
717
|
+
const fontKeys = config.fonts.map((f) => f.key?.split(":").pop());
|
|
718
|
+
const fontStorageKeys = await fontStorage.getKeys();
|
|
719
|
+
await Promise.all(fontStorageKeys.filter((key) => !fontKeys.includes(key)).map(async (key) => {
|
|
720
|
+
logger.info(`Nuxt OG Image removing outdated cached font file \`${key}\``);
|
|
721
|
+
await fontStorage.removeItem(key);
|
|
722
|
+
}));
|
|
723
|
+
if (!config.zeroRuntime) {
|
|
724
|
+
nuxt.options.nitro.serverAssets = nuxt.options.nitro.serverAssets || [];
|
|
725
|
+
nuxt.options.nitro.serverAssets.push({ baseName: "nuxt-og-image:fonts", dir: serverFontsDir });
|
|
726
|
+
}
|
|
727
|
+
addServerHandler({
|
|
728
|
+
lazy: true,
|
|
729
|
+
route: "/__og-image__/font/**",
|
|
730
|
+
handler: resolve(`${basePath}/font`)
|
|
612
731
|
});
|
|
613
732
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if (
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
runtime: { chromium: chromiumBinding.runtime },
|
|
637
|
-
dev: { chromium: chromiumBinding.dev },
|
|
638
|
-
prerender: { chromium: chromiumBinding.prerender }
|
|
639
|
-
});
|
|
640
|
-
await import('@resvg/resvg-js').catch(() => {
|
|
641
|
-
logger.warn("ReSVG is missing dependencies for environment. Falling back to WASM version, this may slow down PNG rendering.");
|
|
733
|
+
if (isProviderEnabledForEnv("chromium", nuxt, config)) {
|
|
734
|
+
const hasChromeLocally = checkLocalChrome();
|
|
735
|
+
const hasPlaywrightDependency = await hasResolvableDependency("playwright");
|
|
736
|
+
const chromeCompatibilityFlags = {
|
|
737
|
+
prerender: config.compatibility?.prerender?.chromium,
|
|
738
|
+
dev: config.compatibility?.dev?.chromium,
|
|
739
|
+
runtime: config.compatibility?.runtime?.chromium
|
|
740
|
+
};
|
|
741
|
+
const chromiumBinding = {
|
|
742
|
+
dev: null,
|
|
743
|
+
prerender: null,
|
|
744
|
+
runtime: null
|
|
745
|
+
};
|
|
746
|
+
if (nuxt.options.dev) {
|
|
747
|
+
if (isUndefinedOrTruthy(chromeCompatibilityFlags.dev))
|
|
748
|
+
chromiumBinding.dev = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
|
|
749
|
+
} else {
|
|
750
|
+
if (isUndefinedOrTruthy(chromeCompatibilityFlags.prerender))
|
|
751
|
+
chromiumBinding.prerender = hasChromeLocally ? "chrome-launcher" : hasPlaywrightDependency ? "playwright" : "on-demand";
|
|
752
|
+
if (isUndefinedOrTruthy(chromeCompatibilityFlags.runtime))
|
|
753
|
+
chromiumBinding.runtime = hasPlaywrightDependency ? "playwright" : null;
|
|
754
|
+
}
|
|
642
755
|
config.compatibility = defu(config.compatibility, {
|
|
643
|
-
|
|
644
|
-
|
|
756
|
+
runtime: { chromium: chromiumBinding.runtime },
|
|
757
|
+
dev: { chromium: chromiumBinding.dev },
|
|
758
|
+
prerender: { chromium: chromiumBinding.prerender }
|
|
645
759
|
});
|
|
646
|
-
|
|
647
|
-
config.compatibility = defu(config.compatibility, {
|
|
648
|
-
runtime: { resvg: "wasm" }
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
});
|
|
760
|
+
}
|
|
652
761
|
await installNuxtSiteConfig();
|
|
653
762
|
const usingNuxtContent = hasNuxtModule("@nuxt/content");
|
|
654
763
|
const isNuxtContentV3 = usingNuxtContent && await hasNuxtModuleCompatibility("@nuxt/content", "^3");
|
|
@@ -660,73 +769,7 @@ const module = defineNuxtModule({
|
|
|
660
769
|
} else if (isNuxtContentV2) {
|
|
661
770
|
addServerPlugin(resolve("./runtime/server/plugins/nuxt-content-v2"));
|
|
662
771
|
}
|
|
663
|
-
if (!config.fonts.length) {
|
|
664
|
-
config.fonts = [
|
|
665
|
-
{ name: "Inter", weight: 400, path: resolve("./runtime/assets/Inter-normal-400.ttf.base64"), absolutePath: true },
|
|
666
|
-
{ name: "Inter", weight: 700, path: resolve("./runtime/assets/Inter-normal-700.ttf.base64"), absolutePath: true }
|
|
667
|
-
];
|
|
668
|
-
}
|
|
669
|
-
let publicDirAbs = nuxt.options.dir.public;
|
|
670
|
-
if (!isAbsolute(publicDirAbs)) {
|
|
671
|
-
publicDirAbs = publicDirAbs in nuxt.options.alias ? nuxt.options.alias[publicDirAbs] : resolve(nuxt.options.rootDir, publicDirAbs);
|
|
672
|
-
}
|
|
673
|
-
const serverFontsDir = resolve(nuxt.options.buildDir, "cache", `nuxt-og-image@${version}`, "_fonts");
|
|
674
|
-
const fontStorage = createStorage({
|
|
675
|
-
driver: fsDriver({
|
|
676
|
-
base: serverFontsDir
|
|
677
|
-
})
|
|
678
|
-
});
|
|
679
|
-
config.fonts = (await Promise.all(normaliseFontInput(config.fonts).map(async (f) => {
|
|
680
|
-
const fontKey = `${f.name}:${f.style}:${f.weight}`;
|
|
681
|
-
const fontFileBase = fontKey.replaceAll(":", "-");
|
|
682
|
-
if (!f.key && !f.path) {
|
|
683
|
-
if (preset === "stackblitz") {
|
|
684
|
-
logger.warn(`The ${fontKey} font was skipped because remote fonts are not available in StackBlitz, please use a local font.`);
|
|
685
|
-
return false;
|
|
686
|
-
}
|
|
687
|
-
if (await downloadFont(f, fontStorage, config.googleFontMirror)) {
|
|
688
|
-
f.key = `nuxt-og-image:fonts:${fontFileBase}.ttf.base64`;
|
|
689
|
-
} else {
|
|
690
|
-
logger.warn(`Failed to download font ${fontKey}. You may be offline or behind a firewall blocking Google. Consider setting \`googleFontMirror: true\`.`);
|
|
691
|
-
return false;
|
|
692
|
-
}
|
|
693
|
-
} else if (f.path) {
|
|
694
|
-
const extension = basename(f.path.replace(".base64", "")).split(".").pop();
|
|
695
|
-
if (!["woff", "ttf", "otf"].includes(extension)) {
|
|
696
|
-
logger.warn(`The ${fontKey} font was skipped because the file extension ${extension} is not supported. Only woff, ttf and otf are supported.`);
|
|
697
|
-
return false;
|
|
698
|
-
}
|
|
699
|
-
if (!f.absolutePath)
|
|
700
|
-
f.path = resolve(publicDirAbs, withoutLeadingSlash(f.path));
|
|
701
|
-
if (!existsSync(f.path)) {
|
|
702
|
-
logger.warn(`The ${fontKey} font was skipped because the file does not exist at path ${f.path}.`);
|
|
703
|
-
return false;
|
|
704
|
-
}
|
|
705
|
-
const fontData = await readFile(f.path, f.path.endsWith(".base64") ? "utf-8" : "base64");
|
|
706
|
-
f.key = `nuxt-og-image:fonts:${fontFileBase}.${extension}.base64`;
|
|
707
|
-
await fontStorage.setItem(`${fontFileBase}.${extension}.base64`, fontData);
|
|
708
|
-
delete f.path;
|
|
709
|
-
delete f.absolutePath;
|
|
710
|
-
}
|
|
711
|
-
return f;
|
|
712
|
-
}))).filter(Boolean);
|
|
713
|
-
const fontKeys = config.fonts.map((f) => f.key?.split(":").pop());
|
|
714
|
-
const fontStorageKeys = await fontStorage.getKeys();
|
|
715
|
-
await Promise.all(fontStorageKeys.filter((key) => !fontKeys.includes(key)).map(async (key) => {
|
|
716
|
-
logger.info(`Nuxt OG Image removing outdated cached font file \`${key}\``);
|
|
717
|
-
await fontStorage.removeItem(key);
|
|
718
|
-
}));
|
|
719
|
-
if (!config.zeroRuntime) {
|
|
720
|
-
nuxt.options.nitro.serverAssets = nuxt.options.nitro.serverAssets || [];
|
|
721
|
-
nuxt.options.nitro.serverAssets.push({ baseName: "nuxt-og-image:fonts", dir: serverFontsDir });
|
|
722
|
-
}
|
|
723
772
|
nuxt.options.experimental.componentIslands ||= true;
|
|
724
|
-
const basePath = config.zeroRuntime ? "./runtime/server/routes/__zero-runtime" : "./runtime/server/routes";
|
|
725
|
-
addServerHandler({
|
|
726
|
-
lazy: true,
|
|
727
|
-
route: "/__og-image__/font/**",
|
|
728
|
-
handler: resolve(`${basePath}/font`)
|
|
729
|
-
});
|
|
730
773
|
if (config.debug || nuxt.options.dev) {
|
|
731
774
|
addServerHandler({
|
|
732
775
|
lazy: true,
|
|
@@ -747,7 +790,18 @@ const module = defineNuxtModule({
|
|
|
747
790
|
if (!nuxt.options.dev) {
|
|
748
791
|
nuxt.options.optimization.treeShake.composables.client["nuxt-og-image"] = [];
|
|
749
792
|
}
|
|
750
|
-
[
|
|
793
|
+
[
|
|
794
|
+
"defineOgImage",
|
|
795
|
+
"defineOgImageComponent",
|
|
796
|
+
{ name: "defineOgImageScreenshot", enabled: isProviderEnabledForEnv("chromium", nuxt, config) }
|
|
797
|
+
].forEach((name) => {
|
|
798
|
+
if (typeof name === "object") {
|
|
799
|
+
if (!name.enabled) {
|
|
800
|
+
addImports({ name: name.name, from: resolve(`./runtime/app/composables/mock`) });
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
name = name.name;
|
|
804
|
+
}
|
|
751
805
|
addImports({
|
|
752
806
|
name,
|
|
753
807
|
from: resolve(`./runtime/app/composables/${name}`)
|
|
@@ -777,6 +831,17 @@ const module = defineNuxtModule({
|
|
|
777
831
|
const basePluginPath = `./runtime/app/plugins${config.zeroRuntime ? "/__zero-runtime" : ""}`;
|
|
778
832
|
addPlugin({ mode: "server", src: resolve(`${basePluginPath}/route-rule-og-image.server`) });
|
|
779
833
|
addPlugin({ mode: "server", src: resolve(`${basePluginPath}/og-image-canonical-urls.server`) });
|
|
834
|
+
for (const componentDir of config.componentDirs) {
|
|
835
|
+
const path = resolve(nuxt.options.srcDir, "components", componentDir);
|
|
836
|
+
if (existsSync(path)) {
|
|
837
|
+
addComponentsDir({
|
|
838
|
+
path,
|
|
839
|
+
island: true
|
|
840
|
+
});
|
|
841
|
+
} else if (!defaultComponentDirs.includes(componentDir)) {
|
|
842
|
+
logger.warn(`The configured component directory \`./${relative$1(nuxt.options.rootDir, path)}\` does not exist. Skipping.`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
780
845
|
const ogImageComponentCtx = { components: [] };
|
|
781
846
|
nuxt.hook("components:extend", (components) => {
|
|
782
847
|
ogImageComponentCtx.components = [];
|
|
@@ -905,20 +970,23 @@ declare module '#og-image/unocss-config' {
|
|
|
905
970
|
// @ts-expect-error runtime type
|
|
906
971
|
isNuxtContentDocumentDriven: config.strictNuxtContentPaths || !!nuxt.options.content?.documentDriven
|
|
907
972
|
};
|
|
973
|
+
if (nuxt.options.dev) {
|
|
974
|
+
runtimeConfig.componentDirs = config.componentDirs;
|
|
975
|
+
}
|
|
908
976
|
nuxt.hooks.callHook("nuxt-og-image:runtime-config", runtimeConfig);
|
|
909
977
|
nuxt.options.runtimeConfig["nuxt-og-image"] = runtimeConfig;
|
|
910
978
|
});
|
|
911
979
|
if (nuxt.options.dev) {
|
|
912
|
-
setupDevHandler(config,
|
|
980
|
+
setupDevHandler(config, resolver);
|
|
913
981
|
setupDevToolsUI(config, resolve);
|
|
914
982
|
} else if (isNuxtGenerate()) {
|
|
915
|
-
setupGenerateHandler(config,
|
|
983
|
+
setupGenerateHandler(config, resolver);
|
|
916
984
|
} else if (nuxt.options.build) {
|
|
917
|
-
await setupBuildHandler(config,
|
|
985
|
+
await setupBuildHandler(config, resolver);
|
|
918
986
|
}
|
|
919
987
|
if (nuxt.options.build)
|
|
920
988
|
addServerPlugin(resolve("./runtime/server/plugins/prerender"));
|
|
921
|
-
setupPrerenderHandler(config,
|
|
989
|
+
setupPrerenderHandler(config, resolver);
|
|
922
990
|
}
|
|
923
991
|
});
|
|
924
992
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { injectHead } from "#imports";
|
|
2
|
-
import { TemplateParamsPlugin } from "@unhead/vue/plugins";
|
|
3
1
|
import { defu } from "defu";
|
|
4
2
|
import { appendHeader } from "h3";
|
|
5
3
|
import { createError, useNuxtApp, useRequestEvent, useRoute, useState } from "nuxt/app";
|
|
@@ -10,8 +8,6 @@ import { createOgImageMeta, normaliseOptions } from "../utils.js";
|
|
|
10
8
|
export function defineOgImage(_options = {}) {
|
|
11
9
|
const nuxtApp = useNuxtApp();
|
|
12
10
|
const route = useRoute();
|
|
13
|
-
const head = injectHead();
|
|
14
|
-
head.use(TemplateParamsPlugin);
|
|
15
11
|
const basePath = route.path || "/";
|
|
16
12
|
if (nuxtApp.payload.path === basePath) {
|
|
17
13
|
const state = import.meta.dev ? useState(`og-image:ssr-exists:${basePath}`, () => false) : ref(false);
|
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "nuxt/app";
|
|
1
2
|
export function defineOgImage(_options = {}) {
|
|
3
|
+
if (import.meta.dev) {
|
|
4
|
+
console.warn("`defineOgImage()` is skipped as the OG Image module is not enabled.");
|
|
5
|
+
}
|
|
2
6
|
}
|
|
3
7
|
export function defineOgImageComponent(component, props = {}, options = {}) {
|
|
8
|
+
if (import.meta.dev) {
|
|
9
|
+
console.warn("`defineOgImageComponent()` is skipped as the OG Image module is not enabled.");
|
|
10
|
+
}
|
|
4
11
|
}
|
|
5
12
|
export function defineOgImageScreenshot(options = {}) {
|
|
13
|
+
if (import.meta.dev) {
|
|
14
|
+
if (useRuntimeConfig()["nuxt-og-image"]) {
|
|
15
|
+
console.warn("`defineOgImageScreenshot()` is skipped as the `chromium` compatibility is disabled.");
|
|
16
|
+
} else {
|
|
17
|
+
console.warn("`defineOgImageScreenshot()` is skipped as the OG Image module is not enabled.");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
6
20
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useRequestEvent, withSiteUrl } from "#imports";
|
|
2
|
+
import { TemplateParamsPlugin } from "@unhead/vue/plugins";
|
|
2
3
|
import { defu } from "defu";
|
|
3
4
|
import { parse, stringify } from "devalue";
|
|
4
5
|
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
@@ -14,6 +15,7 @@ export function ogImageCanonicalUrls(nuxtApp) {
|
|
|
14
15
|
const path = parseURL(e.path).pathname;
|
|
15
16
|
if (isInternalRoute(path))
|
|
16
17
|
return;
|
|
18
|
+
ssrContext?.head.use(TemplateParamsPlugin);
|
|
17
19
|
ssrContext?.head.use({
|
|
18
20
|
key: "nuxt-og-image:overrides-and-canonical-urls",
|
|
19
21
|
hooks: {
|
|
@@ -99,10 +99,10 @@ export async function resolveContext(e) {
|
|
|
99
99
|
renderer = await useChromiumRenderer();
|
|
100
100
|
break;
|
|
101
101
|
}
|
|
102
|
-
if (!renderer || renderer.
|
|
102
|
+
if (!renderer || renderer.__mock__) {
|
|
103
103
|
throw createError({
|
|
104
104
|
statusCode: 400,
|
|
105
|
-
statusMessage: `[Nuxt OG Image] Renderer ${options.renderer} is
|
|
105
|
+
statusMessage: `[Nuxt OG Image] Renderer ${options.renderer} is not enabled.`
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
const unocss = await createGenerator({ theme }, {
|
|
@@ -133,11 +133,9 @@ function getPayloadFromHtml(html) {
|
|
|
133
133
|
}
|
|
134
134
|
export function extractAndNormaliseOgImageOptions(html) {
|
|
135
135
|
const _payload = getPayloadFromHtml(html);
|
|
136
|
-
if (!_payload)
|
|
137
|
-
return false;
|
|
138
136
|
let options = false;
|
|
139
137
|
try {
|
|
140
|
-
const payload2 = parse(_payload);
|
|
138
|
+
const payload2 = parse(_payload || "{}");
|
|
141
139
|
Object.entries(payload2).forEach(([key, value]) => {
|
|
142
140
|
if (!value && value !== 0)
|
|
143
141
|
delete payload2[key];
|
|
@@ -147,9 +145,7 @@ export function extractAndNormaliseOgImageOptions(html) {
|
|
|
147
145
|
if (import.meta.dev)
|
|
148
146
|
console.warn("Failed to parse #nuxt-og-image-options", e, options);
|
|
149
147
|
}
|
|
150
|
-
if (
|
|
151
|
-
return false;
|
|
152
|
-
if (typeof options.props?.description === "undefined") {
|
|
148
|
+
if (options && typeof options?.props?.description === "undefined") {
|
|
153
149
|
const description = html.match(/<meta[^>]+name="description"[^>]*>/)?.[0];
|
|
154
150
|
if (description) {
|
|
155
151
|
const [, content] = description.match(/content="([^"]+)"/) || [];
|
|
@@ -157,7 +153,7 @@ export function extractAndNormaliseOgImageOptions(html) {
|
|
|
157
153
|
options.props.description = content;
|
|
158
154
|
}
|
|
159
155
|
}
|
|
160
|
-
const payload = decodeObjectHtmlEntities(options);
|
|
156
|
+
const payload = decodeObjectHtmlEntities(options || {});
|
|
161
157
|
if (import.meta.dev) {
|
|
162
158
|
const socialPreview = {};
|
|
163
159
|
const socialMetaTags = html.match(/<meta[^>]+(property|name)="(twitter|og):([^"]+)"[^>]*>/g);
|
|
@@ -235,6 +231,20 @@ async function fetchPathHtmlAndExtractOptions(e, path, key) {
|
|
|
235
231
|
});
|
|
236
232
|
}
|
|
237
233
|
if (!_payload) {
|
|
234
|
+
const payload2 = extractAndNormaliseOgImageOptions(html);
|
|
235
|
+
if (payload2?.socialPreview?.og?.image) {
|
|
236
|
+
const p = {
|
|
237
|
+
custom: true,
|
|
238
|
+
url: payload2.socialPreview.og.image
|
|
239
|
+
};
|
|
240
|
+
if (payload2.socialPreview.og.image["image:width"]) {
|
|
241
|
+
p.width = payload2.socialPreview.og.image["image:width"];
|
|
242
|
+
}
|
|
243
|
+
if (payload2.socialPreview.og.image["image:height"]) {
|
|
244
|
+
p.height = payload2.socialPreview.og.image["image:height"];
|
|
245
|
+
}
|
|
246
|
+
return p;
|
|
247
|
+
}
|
|
238
248
|
return createError({
|
|
239
249
|
statusCode: 500,
|
|
240
250
|
statusMessage: `[Nuxt OG Image] HTML response from ${path} is missing the #nuxt-og-image-options script tag. Make sure you have defined an og image for this page.`
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { logger } from "../../../util/logger.js";
|
|
2
|
+
import { defineSatoriTransformer } from "../utils.js";
|
|
3
|
+
export default defineSatoriTransformer([
|
|
4
|
+
// need to make sure parent div has flex for the emoji to render inline
|
|
5
|
+
{
|
|
6
|
+
filter: (node) => node.type === "span" && node.props?.class?.includes("iconify"),
|
|
7
|
+
transform: (node, e) => {
|
|
8
|
+
if (import.meta.dev) {
|
|
9
|
+
logger.warn(`When using the Nuxt Icon components in \`${e.options.component}\` you must provide \`mode="svg"\` to ensure correct rendering.`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
// need to make sure parent div has flex for the emoji to render inline
|
|
14
|
+
{
|
|
15
|
+
filter: (node) => node.type === "svg" && node.props?.class?.includes("iconify"),
|
|
16
|
+
transform: (node) => {
|
|
17
|
+
node.props.class = String(node.props.class).split(" ").filter((c) => !c.startsWith("iconify")).join(" ");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]);
|
|
@@ -31,7 +31,7 @@ export async function applyInlineCss(ctx, island) {
|
|
|
31
31
|
if (!css.trim().length)
|
|
32
32
|
return false;
|
|
33
33
|
const cssInline = await useCssInline();
|
|
34
|
-
if (!cssInline || cssInline?.
|
|
34
|
+
if (!cssInline || cssInline?.__mock__) {
|
|
35
35
|
if (componentInlineStyles.length) {
|
|
36
36
|
const logger = createConsola().withTag("Nuxt OG Image");
|
|
37
37
|
logger.warn("To have inline styles applied you need to install either the `@css-inline/css-inline` or `@css-inline/css-inline-wasm` package.");
|
|
@@ -6,6 +6,7 @@ import emojis from "./plugins/emojis.js";
|
|
|
6
6
|
import encoding from "./plugins/encoding.js";
|
|
7
7
|
import flex from "./plugins/flex.js";
|
|
8
8
|
import imageSrc from "./plugins/imageSrc.js";
|
|
9
|
+
import nuxtIcon from "./plugins/nuxt-icon.js";
|
|
9
10
|
import unocss from "./plugins/unocss.js";
|
|
10
11
|
import { applyEmojis } from "./transforms/emojis.js";
|
|
11
12
|
import { applyInlineCss } from "./transforms/inlineCss.js";
|
|
@@ -28,7 +29,8 @@ export async function createVNodes(ctx) {
|
|
|
28
29
|
emojis,
|
|
29
30
|
classes,
|
|
30
31
|
flex,
|
|
31
|
-
encoding
|
|
32
|
+
encoding,
|
|
33
|
+
nuxtIcon
|
|
32
34
|
]);
|
|
33
35
|
await Promise.all(walkSatoriTree(ctx, satoriTree, [
|
|
34
36
|
unocss,
|