nuxt-og-image 0.6.0 → 1.0.0-beta.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/README.md +53 -52
- package/dist/client/200.html +7 -0
- package/dist/client/404.html +7 -0
- package/dist/client/_nuxt/Icon.4a9650c6.js +1 -0
- package/dist/client/_nuxt/Icon.e17ad835.css +1 -0
- package/dist/client/_nuxt/entry.0827acf4.css +1 -0
- package/dist/client/_nuxt/entry.ce848650.js +4 -0
- package/dist/client/_nuxt/error-404.556d8899.js +1 -0
- package/dist/client/_nuxt/error-404.68aa58b4.css +1 -0
- package/dist/client/_nuxt/error-500.70731718.js +1 -0
- package/dist/client/_nuxt/error-500.dc5710d1.css +1 -0
- package/dist/client/_nuxt/error-component.418ebd67.js +3 -0
- package/dist/client/index.html +7 -0
- package/dist/module.d.ts +13 -12
- package/dist/module.json +1 -1
- package/dist/module.mjs +199 -102
- package/dist/runtime/components/{OgImage.d.ts → OgImageDynamic.d.ts} +0 -0
- package/dist/runtime/components/OgImageDynamic.mjs +9 -0
- package/dist/runtime/components/OgImageStatic.d.ts +5 -0
- package/dist/runtime/components/{OgImage.mjs → OgImageStatic.mjs} +3 -3
- package/dist/runtime/components/OgImageTemplate.island.vue +8 -83
- package/dist/runtime/composables/defineOgImage.d.ts +2 -0
- package/dist/runtime/composables/defineOgImage.mjs +34 -10
- package/dist/runtime/{browsers → nitro/browsers}/default.d.ts +0 -0
- package/dist/runtime/{browsers → nitro/browsers}/default.mjs +0 -0
- package/dist/runtime/{browsers → nitro/browsers}/lambda.d.ts +0 -0
- package/dist/runtime/{browsers → nitro/browsers}/lambda.mjs +0 -0
- package/dist/runtime/nitro/providers/browser.d.ts +3 -0
- package/dist/runtime/nitro/providers/browser.mjs +16 -0
- package/dist/runtime/nitro/providers/satori.d.ts +3 -0
- package/dist/runtime/nitro/providers/satori.mjs +48 -0
- package/dist/runtime/nitro/routes/__og_image__/html.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/html.mjs +43 -0
- package/dist/runtime/nitro/routes/__og_image__/index.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/index.mjs +26 -0
- package/dist/runtime/nitro/routes/__og_image__/og.png.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/og.png.mjs +23 -0
- package/dist/runtime/nitro/{html.d.ts → routes/__og_image__/payload.d.ts} +2 -1
- package/dist/runtime/nitro/routes/__og_image__/payload.mjs +53 -0
- package/dist/runtime/nitro/routes/__og_image__/svg.d.ts +2 -0
- package/dist/runtime/nitro/routes/__og_image__/svg.mjs +16 -0
- package/dist/runtime/nitro/utils.d.ts +8 -0
- package/dist/runtime/nitro/utils.mjs +17 -0
- package/dist/runtime/public/inter-latin-ext-400-normal.woff +0 -0
- package/dist/runtime/public/inter-latin-ext-700-normal.woff +0 -0
- package/package.json +18 -7
- package/dist/runtime/nitro/html.mjs +0 -55
- package/dist/runtime/nitro/image.d.ts +0 -3
- package/dist/runtime/nitro/image.mjs +0 -18
package/dist/module.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { useNuxt, addTemplate, defineNuxtModule, createResolver, addServerHandler, addImports, addComponent } from '@nuxt/kit';
|
|
3
4
|
import { execa } from 'execa';
|
|
4
|
-
import { hash } from 'ohash';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import defu from 'defu';
|
|
7
7
|
import { toRouteMatcher, createRouter } from 'radix3';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
8
|
+
import { joinURL } from 'ufo';
|
|
9
|
+
import { resolve, relative } from 'pathe';
|
|
10
|
+
import { tinyws } from 'tinyws';
|
|
11
|
+
import sirv from 'sirv';
|
|
12
|
+
import { createBirpcGroup } from 'birpc';
|
|
13
|
+
import { stringify, parse } from 'flatted';
|
|
11
14
|
|
|
12
15
|
async function createBrowser() {
|
|
13
16
|
try {
|
|
@@ -54,19 +57,104 @@ async function screenshot(browser, url, options) {
|
|
|
54
57
|
return await page.screenshot();
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const HtmlRendererRoute = "__og_image";
|
|
58
60
|
const PayloadScriptId = "nuxt-og-image-payload";
|
|
59
|
-
const MetaOgImageContentPlaceholder = "__NUXT_OG_IMAGE_PLACEHOLDER__";
|
|
60
|
-
const LinkPrerenderId = "nuxt-og-image-screenshot-path";
|
|
61
|
-
const DefaultRuntimeImageSuffix = "og-image.png";
|
|
62
61
|
const Constants = {
|
|
63
|
-
|
|
64
|
-
PayloadScriptId,
|
|
65
|
-
MetaOgImageContentPlaceholder,
|
|
66
|
-
LinkPrerenderId,
|
|
67
|
-
DefaultRuntimeImageSuffix
|
|
62
|
+
PayloadScriptId
|
|
68
63
|
};
|
|
69
64
|
|
|
65
|
+
function exposeConfig(alias, filename, config) {
|
|
66
|
+
const exports = Object.entries(config).map(([k, v]) => `export const ${k} = '${v}'`).join("\n");
|
|
67
|
+
useNuxt().options.alias[alias] = addTemplate({
|
|
68
|
+
filename,
|
|
69
|
+
getContents: () => exports
|
|
70
|
+
}).dst;
|
|
71
|
+
useNuxt().hooks.hook("nitro:config", (nitroConfig) => {
|
|
72
|
+
nitroConfig.virtual[alias] = exports;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setupPlaygroundRPC(nuxt, config) {
|
|
77
|
+
const serverFunctions = {
|
|
78
|
+
getConfig() {
|
|
79
|
+
return config;
|
|
80
|
+
},
|
|
81
|
+
async openInEditor(input) {
|
|
82
|
+
if (input.startsWith("./"))
|
|
83
|
+
input = resolve(process.cwd(), input);
|
|
84
|
+
const match = input.match(/^(.*?)([:\d]*)$/);
|
|
85
|
+
let suffix = "";
|
|
86
|
+
if (match) {
|
|
87
|
+
input = match[1];
|
|
88
|
+
suffix = match[2];
|
|
89
|
+
}
|
|
90
|
+
const file = [
|
|
91
|
+
input,
|
|
92
|
+
`${input}.js`,
|
|
93
|
+
`${input}.mjs`,
|
|
94
|
+
`${input}.ts`
|
|
95
|
+
].find((i) => existsSync(i));
|
|
96
|
+
if (file) {
|
|
97
|
+
await import('launch-editor').then((r) => (r.default || r)(file + suffix));
|
|
98
|
+
} else {
|
|
99
|
+
console.error("File not found:", input);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const clients = /* @__PURE__ */ new Set();
|
|
104
|
+
const birpc = createBirpcGroup(serverFunctions, []);
|
|
105
|
+
nuxt.hook("builder:watch", (e, path) => {
|
|
106
|
+
if (e === "change")
|
|
107
|
+
birpc.boardcast.refresh.asEvent(path);
|
|
108
|
+
});
|
|
109
|
+
const middleware = async (req, res) => {
|
|
110
|
+
if (req.ws) {
|
|
111
|
+
const ws = await req.ws();
|
|
112
|
+
clients.add(ws);
|
|
113
|
+
const channel = {
|
|
114
|
+
post: (d) => ws.send(d),
|
|
115
|
+
on: (fn) => ws.on("message", fn),
|
|
116
|
+
serialize: stringify,
|
|
117
|
+
deserialize: parse
|
|
118
|
+
};
|
|
119
|
+
birpc.updateChannels((c) => {
|
|
120
|
+
c.push(channel);
|
|
121
|
+
});
|
|
122
|
+
ws.on("close", () => {
|
|
123
|
+
clients.delete(ws);
|
|
124
|
+
birpc.updateChannels((c) => {
|
|
125
|
+
const index = c.indexOf(channel);
|
|
126
|
+
if (index >= 0)
|
|
127
|
+
c.splice(index, 1);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
} else if (req.method === "POST") {
|
|
131
|
+
const body = await getBodyJson(req);
|
|
132
|
+
if (body.method === "setPayload") ; else {
|
|
133
|
+
res.statusCode = 400;
|
|
134
|
+
}
|
|
135
|
+
res.end();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
middleware,
|
|
140
|
+
birpc
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function getBodyJson(req) {
|
|
144
|
+
return new Promise((resolve2, reject) => {
|
|
145
|
+
let body = "";
|
|
146
|
+
req.on("data", (chunk) => body += chunk);
|
|
147
|
+
req.on("error", reject);
|
|
148
|
+
req.on("end", () => {
|
|
149
|
+
try {
|
|
150
|
+
resolve2(JSON.parse(body) || {});
|
|
151
|
+
} catch (e) {
|
|
152
|
+
reject(e);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
70
158
|
function extractOgPayload(html) {
|
|
71
159
|
const payload = html.match(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.+?)<\/script>`))?.[1];
|
|
72
160
|
if (payload) {
|
|
@@ -74,6 +162,9 @@ function extractOgPayload(html) {
|
|
|
74
162
|
}
|
|
75
163
|
return false;
|
|
76
164
|
}
|
|
165
|
+
const PATH = "/__nuxt_og_image__";
|
|
166
|
+
const PATH_ENTRY = `${PATH}/entry`;
|
|
167
|
+
const PATH_PLAYGROUND = `${PATH}/client`;
|
|
77
168
|
const module = defineNuxtModule({
|
|
78
169
|
meta: {
|
|
79
170
|
name: "nuxt-og-image",
|
|
@@ -85,57 +176,74 @@ const module = defineNuxtModule({
|
|
|
85
176
|
},
|
|
86
177
|
defaults(nuxt) {
|
|
87
178
|
return {
|
|
179
|
+
experimentalNitroBrowser: false,
|
|
180
|
+
forcePrerender: !nuxt.options.dev && nuxt.options._generate,
|
|
88
181
|
host: nuxt.options.runtimeConfig.public?.siteUrl,
|
|
89
182
|
width: 1200,
|
|
90
|
-
height: 630
|
|
91
|
-
defaultIslandComponent: "OgImageTemplate",
|
|
92
|
-
outputDir: "_og-images",
|
|
93
|
-
serverSideRender: nuxt.options.dev || (process.env.NITRO_PRESET || "").includes("edge")
|
|
183
|
+
height: 630
|
|
94
184
|
};
|
|
95
185
|
},
|
|
96
186
|
async setup(config, nuxt) {
|
|
97
187
|
const { resolve } = createResolver(import.meta.url);
|
|
188
|
+
const distResolve = (p) => {
|
|
189
|
+
const cwd = resolve(".");
|
|
190
|
+
if (cwd.endsWith("/dist"))
|
|
191
|
+
return resolve(p);
|
|
192
|
+
return resolve(`../dist/${p}`);
|
|
193
|
+
};
|
|
98
194
|
nuxt.options.experimental.componentIslands = true;
|
|
99
195
|
const isEdge = (process.env.NITRO_PRESET || "").includes("edge");
|
|
100
196
|
addTemplate({
|
|
101
197
|
filename: "nuxt-og-image.d.ts",
|
|
102
198
|
getContents: () => {
|
|
103
199
|
return `// Generated by nuxt-og-image
|
|
200
|
+
interface NuxtOgImageNitroRules {
|
|
201
|
+
ogImage?: false | Record<string, any>
|
|
202
|
+
}
|
|
104
203
|
declare module 'nitropack' {
|
|
105
|
-
interface NitroRouteRules {
|
|
106
|
-
|
|
107
|
-
}
|
|
204
|
+
interface NitroRouteRules extends NuxtOgImageNitroRules {}
|
|
205
|
+
interface NitroRouteConfig extends NuxtOgImageNitroRules {}
|
|
108
206
|
}
|
|
207
|
+
export {}
|
|
109
208
|
`;
|
|
110
209
|
}
|
|
111
210
|
});
|
|
112
211
|
nuxt.hooks.hook("prepare:types", ({ references }) => {
|
|
113
212
|
references.push({ path: resolve(nuxt.options.buildDir, "nuxt-og-image.d.ts") });
|
|
114
213
|
});
|
|
115
|
-
|
|
214
|
+
["html", "payload", "svg", "og.png"].forEach((type) => {
|
|
116
215
|
addServerHandler({
|
|
117
|
-
handler: resolve(
|
|
216
|
+
handler: resolve(`./runtime/nitro/routes/__og_image__/${type}`)
|
|
118
217
|
});
|
|
119
|
-
if (config.serverSideRender) {
|
|
120
|
-
addServerHandler({
|
|
121
|
-
handler: resolve("./runtime/nitro/image")
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
addImports({
|
|
126
|
-
name: "defineOgImage",
|
|
127
|
-
from: resolve("./runtime/composables/defineOgImage")
|
|
128
218
|
});
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
219
|
+
if (nuxt.options.dev) {
|
|
220
|
+
const playgroundDir = distResolve("./client");
|
|
221
|
+
const {
|
|
222
|
+
middleware: rpcMiddleware
|
|
223
|
+
} = setupPlaygroundRPC(nuxt, config);
|
|
224
|
+
nuxt.hook("vite:serverCreated", (server) => {
|
|
225
|
+
server.middlewares.use(PATH_ENTRY, tinyws());
|
|
226
|
+
server.middlewares.use(PATH_ENTRY, rpcMiddleware);
|
|
227
|
+
if (existsSync(playgroundDir))
|
|
228
|
+
server.middlewares.use(PATH_PLAYGROUND, sirv(playgroundDir, { single: true, dev: true }));
|
|
229
|
+
});
|
|
230
|
+
addServerHandler({
|
|
231
|
+
handler: resolve("./runtime/nitro/routes/__og_image__/index")
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
["defineOgImageDynamic", "defineOgImageStatic", "defineOgImageScreenshot"].forEach((name) => {
|
|
235
|
+
addImports({
|
|
236
|
+
name,
|
|
237
|
+
from: resolve("./runtime/composables/defineOgImage")
|
|
238
|
+
});
|
|
132
239
|
});
|
|
133
240
|
await addComponent({
|
|
134
241
|
name: "OgImageTemplate",
|
|
135
242
|
filePath: resolve("./runtime/components/OgImageTemplate.island.vue"),
|
|
243
|
+
global: true,
|
|
136
244
|
island: true
|
|
137
245
|
});
|
|
138
|
-
["
|
|
246
|
+
["OgImageStatic", "OgImageDynamic", "OgImageScreenshot"].forEach((name) => {
|
|
139
247
|
addComponent({
|
|
140
248
|
name,
|
|
141
249
|
filePath: resolve(`./runtime/components/${name}`),
|
|
@@ -144,67 +252,65 @@ declare module 'nitropack' {
|
|
|
144
252
|
});
|
|
145
253
|
const runtimeDir = resolve("./runtime");
|
|
146
254
|
nuxt.options.build.transpile.push(runtimeDir);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
filename: "nuxt-og-image-constants.mjs",
|
|
150
|
-
getContents: () => constScript
|
|
151
|
-
}).dst;
|
|
255
|
+
exposeConfig("#nuxt-og-image/constants", "nuxt-og-image-constants.mjs", Constants);
|
|
256
|
+
exposeConfig("#nuxt-og-image/config", "nuxt-og-image-config.mjs", config);
|
|
152
257
|
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
153
258
|
nitroConfig.externals = defu(nitroConfig.externals || {}, {
|
|
154
259
|
inline: [runtimeDir]
|
|
155
260
|
});
|
|
156
|
-
nitroConfig.
|
|
157
|
-
nitroConfig.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
261
|
+
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
|
|
262
|
+
nitroConfig.publicAssets.push({ dir: resolve("./runtime/public"), maxAge: 31536e3 });
|
|
263
|
+
nitroConfig.virtual["#nuxt-og-image/browser"] = `export { createBrowser } from '${runtimeDir}/nitro/browsers/${isEdge ? "lambda" : "default"}'`;
|
|
264
|
+
nitroConfig.virtual["#nuxt-og-image/provider"] = `
|
|
265
|
+
import satori from '${runtimeDir}/nitro/providers/satori'
|
|
266
|
+
import browser from '${runtimeDir}/nitro/providers/browser'
|
|
267
|
+
|
|
268
|
+
export function useProvider(provider) {
|
|
269
|
+
if (provider === 'satori')
|
|
270
|
+
return satori
|
|
271
|
+
if (provider === 'browser')
|
|
272
|
+
return browser
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
if (config.experimentalNitroBrowser) {
|
|
276
|
+
nitroConfig.virtual["#nuxt-og-image/providers/browser"] = `export * from '${runtimeDir}/nitro/providers/browser'`;
|
|
277
|
+
if (isEdge) {
|
|
278
|
+
["puppeteer", "bufferutil", "utf-8-validate"].forEach((name) => {
|
|
279
|
+
nitroConfig.alias[name] = "unenv/runtime/mock/proxy";
|
|
280
|
+
});
|
|
281
|
+
}
|
|
162
282
|
}
|
|
163
283
|
});
|
|
164
284
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
165
|
-
let
|
|
166
|
-
let cleanupEntries = [];
|
|
285
|
+
let screenshotQueue = [];
|
|
167
286
|
const _routeRulesMatcher = toRouteMatcher(
|
|
168
287
|
createRouter({ routes: nitro.options.routeRules })
|
|
169
288
|
);
|
|
170
|
-
const outputPath = join(nitro.options.output.publicDir, config.outputDir);
|
|
171
289
|
nitro.hooks.hook("prerender:generate", async (ctx) => {
|
|
172
|
-
if (ctx.route.includes(".") || ctx.route.endsWith(
|
|
290
|
+
if (ctx.route.includes(".") || ctx.route.endsWith("__og_image__/html"))
|
|
173
291
|
return;
|
|
174
|
-
|
|
292
|
+
const html = ctx.contents;
|
|
175
293
|
if (!html)
|
|
176
294
|
return;
|
|
177
|
-
|
|
178
|
-
|
|
295
|
+
const extractedPayload = extractOgPayload(html);
|
|
296
|
+
ctx.contents = html.replace(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.*?)<\/script>`), "");
|
|
179
297
|
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
|
|
180
|
-
if (routeRules.ogImage === false)
|
|
298
|
+
if (!extractedPayload || routeRules.ogImage === false)
|
|
181
299
|
return;
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
absoluteUrl,
|
|
188
|
-
outputPath: joinURL(nitro.options.output.publicDir, config.outputDir, fileName),
|
|
189
|
-
linkingHtml: joinURL(nitro.options.output.publicDir, ctx.fileName),
|
|
190
|
-
route: ctx.route,
|
|
191
|
-
payload: extractOgPayload(ctx._contents),
|
|
192
|
-
routeRules: routeRules.ogImage || "",
|
|
193
|
-
screenshotPath: screenshotPath || ctx.route
|
|
300
|
+
const payload = {
|
|
301
|
+
path: ctx.route,
|
|
302
|
+
...extractedPayload,
|
|
303
|
+
...routeRules.ogImage || {},
|
|
304
|
+
ctx
|
|
194
305
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
ctx.contents = html;
|
|
306
|
+
if ((nuxt.options._generate || payload.prerender) && payload.provider === "browser")
|
|
307
|
+
screenshotQueue.push(payload);
|
|
198
308
|
});
|
|
199
309
|
if (nuxt.options.dev)
|
|
200
310
|
return;
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
311
|
+
const captureScreenshots = async () => {
|
|
312
|
+
if (screenshotQueue.length === 0)
|
|
203
313
|
return;
|
|
204
|
-
try {
|
|
205
|
-
await mkdir(outputPath, { recursive: true });
|
|
206
|
-
} catch (e) {
|
|
207
|
-
}
|
|
208
314
|
const previewProcess = execa("npx", ["serve", nitro.options.output.publicDir]);
|
|
209
315
|
let browser = null;
|
|
210
316
|
try {
|
|
@@ -218,24 +324,30 @@ declare module 'nitropack' {
|
|
|
218
324
|
})).trim();
|
|
219
325
|
browser = await createBrowser();
|
|
220
326
|
if (browser) {
|
|
221
|
-
nitro.logger.info(`
|
|
222
|
-
for (const k in
|
|
223
|
-
const entry =
|
|
327
|
+
nitro.logger.info(`Pre-rendering ${screenshotQueue.length} og:image screenshots...`);
|
|
328
|
+
for (const k in screenshotQueue) {
|
|
329
|
+
const entry = screenshotQueue[k];
|
|
224
330
|
const start = Date.now();
|
|
225
331
|
let hasError = false;
|
|
332
|
+
const dirname = joinURL(nitro.options.output.publicDir, `${entry.ctx.fileName.replace("index.html", "")}__og_image__/`);
|
|
333
|
+
const filename = joinURL(dirname, "/og.png");
|
|
226
334
|
try {
|
|
227
|
-
const imgBuffer = await screenshot(browser, `${host}${entry.
|
|
335
|
+
const imgBuffer = await screenshot(browser, `${host}${entry.path}`, {
|
|
228
336
|
...config,
|
|
229
|
-
...entry
|
|
337
|
+
...entry
|
|
230
338
|
});
|
|
231
|
-
|
|
339
|
+
try {
|
|
340
|
+
await mkdir(dirname, { recursive: true });
|
|
341
|
+
} catch (e) {
|
|
342
|
+
}
|
|
343
|
+
await writeFile(filename, imgBuffer);
|
|
232
344
|
} catch (e) {
|
|
233
345
|
hasError = true;
|
|
234
346
|
console.error(e);
|
|
235
347
|
}
|
|
236
348
|
const generateTimeMS = Date.now() - start;
|
|
237
349
|
nitro.logger.log(chalk[hasError ? "red" : "gray"](
|
|
238
|
-
` ${Number(k) ===
|
|
350
|
+
` ${Number(k) === screenshotQueue.length - 1 ? "\u2514\u2500" : "\u251C\u2500"} ${relative(nitro.options.output.publicDir, filename)} (${generateTimeMS}ms) ${Math.round((Number(k) + 1) / screenshotQueue.length * 100)}%`
|
|
239
351
|
));
|
|
240
352
|
}
|
|
241
353
|
} else {
|
|
@@ -247,28 +359,13 @@ declare module 'nitropack' {
|
|
|
247
359
|
await browser?.close();
|
|
248
360
|
previewProcess.kill();
|
|
249
361
|
}
|
|
250
|
-
|
|
251
|
-
entries = [];
|
|
362
|
+
screenshotQueue = [];
|
|
252
363
|
};
|
|
253
364
|
nitro.hooks.hook("rollup:before", async () => {
|
|
254
|
-
await
|
|
365
|
+
await captureScreenshots();
|
|
255
366
|
});
|
|
256
367
|
nitro.hooks.hook("close", async () => {
|
|
257
|
-
await
|
|
258
|
-
for (const entry of cleanupEntries) {
|
|
259
|
-
try {
|
|
260
|
-
const html = await readFile(entry.linkingHtml, "utf-8");
|
|
261
|
-
const newHtml = html.replace("__OG_IMAGE_SCREENSHOT_ALT", `Web page screenshot of ${entry.route}.`).replace(new RegExp(`<link id="${LinkPrerenderId}" rel="prerender" href="(.*?)">`), "").replace(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.*?)<\/script>`), "").replace("\n\n", "\n");
|
|
262
|
-
if (html !== newHtml) {
|
|
263
|
-
await writeFile(entry.linkingHtml, newHtml, { encoding: "utf-8" });
|
|
264
|
-
}
|
|
265
|
-
} catch (e) {
|
|
266
|
-
console.error(e);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
const ogImageFolders = await fg([`**/${HtmlRendererRoute}`], { cwd: nitro.options.output.publicDir, onlyDirectories: true });
|
|
270
|
-
for (const ogImageFolder of ogImageFolders)
|
|
271
|
-
await rm(join(nitro.options.output.publicDir, ogImageFolder), { recursive: true, force: true });
|
|
368
|
+
await captureScreenshots();
|
|
272
369
|
});
|
|
273
370
|
});
|
|
274
371
|
}
|
|
File without changes
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OgImagePayload } from '../../types';
|
|
2
|
+
declare const _default: import("vue").DefineComponent<OgImagePayload, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<OgImagePayload>>, {
|
|
3
|
+
[x: string]: any;
|
|
4
|
+
}>;
|
|
5
|
+
export default _default;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineComponent } from "vue";
|
|
2
|
-
import {
|
|
2
|
+
import { defineOgImageStatic } from "#imports";
|
|
3
3
|
export default defineComponent({
|
|
4
|
-
name: "
|
|
4
|
+
name: "OgImageStatic",
|
|
5
5
|
setup(_, { attrs }) {
|
|
6
|
-
|
|
6
|
+
defineOgImageStatic(attrs);
|
|
7
7
|
return () => null;
|
|
8
8
|
}
|
|
9
9
|
});
|
|
@@ -7,89 +7,14 @@ const props = defineProps({
|
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<template>
|
|
10
|
-
<div
|
|
11
|
-
<div
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<p
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<strong>Payload</strong>
|
|
19
|
-
<code>
|
|
20
|
-
<pre>{{ props }}</pre>
|
|
21
|
-
</code>
|
|
10
|
+
<div :style="{ padding: '0 60px', width: '100%', height: '100%', backgroundColor: '#0c0c0c', backgroundImage: 'linear-gradient(to bottom, #dbf4ff, #fff1f1)', display: 'flex', alignItems: 'center' }">
|
|
11
|
+
<div :style="{ padding: '0 30px', display: 'flex', flexDirection: 'column' }">
|
|
12
|
+
<p :style="{ fontSize: '60px', fontWeight: 'bold', marginBottom: '20px' }">
|
|
13
|
+
{{ title }}
|
|
14
|
+
</p>
|
|
15
|
+
<p :style="{ fontSize: '26px' }">
|
|
16
|
+
{{ description }}
|
|
17
|
+
</p>
|
|
22
18
|
</div>
|
|
23
19
|
</div>
|
|
24
20
|
</template>
|
|
25
|
-
|
|
26
|
-
<style scoped>
|
|
27
|
-
.wrap {
|
|
28
|
-
width: 100%;
|
|
29
|
-
height: 100%;
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
flex-direction: column;
|
|
33
|
-
color: white;
|
|
34
|
-
font-weight: bold;
|
|
35
|
-
font-family: sans-serif;
|
|
36
|
-
background-color: #0c0c0c;
|
|
37
|
-
position: relative;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.bg1 {
|
|
41
|
-
top: 0;
|
|
42
|
-
left: 0;
|
|
43
|
-
display: block;
|
|
44
|
-
position: absolute;
|
|
45
|
-
width: 100%;
|
|
46
|
-
height: 100%;
|
|
47
|
-
padding: 0 !important;
|
|
48
|
-
margin: 0 !important;
|
|
49
|
-
background-color: #0c0c0c;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.bg2 {
|
|
53
|
-
top: 0;
|
|
54
|
-
left: 0;
|
|
55
|
-
z-index: 1;
|
|
56
|
-
display: block;
|
|
57
|
-
position: absolute;
|
|
58
|
-
width: 100%;
|
|
59
|
-
height: 100%;
|
|
60
|
-
padding: 0 !important;
|
|
61
|
-
margin: 0 !important;
|
|
62
|
-
background: radial-gradient(at 100% 100%, #0f766e, rgba(12, 12, 12, 0.1) 60%);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
a {
|
|
66
|
-
color: inherit;
|
|
67
|
-
padding-bottom: 3px;
|
|
68
|
-
text-decoration: none;
|
|
69
|
-
border-bottom: 3px solid #ff8235;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.wrap > div {
|
|
73
|
-
z-index: 2;
|
|
74
|
-
padding: 2rem;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
code pre {
|
|
78
|
-
background: #333;
|
|
79
|
-
color: white;
|
|
80
|
-
padding: 1rem;
|
|
81
|
-
border-radius: 0.5rem;
|
|
82
|
-
font-weight: lighter;
|
|
83
|
-
font-size: 1.1rem;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
p {
|
|
87
|
-
font-size: 1.5em;
|
|
88
|
-
font-weight: normal;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
h1 {
|
|
92
|
-
font-size: 4rem;
|
|
93
|
-
margin: 0;
|
|
94
|
-
}
|
|
95
|
-
</style>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { OgImagePayload, OgImageScreenshotPayload } from '../../types';
|
|
2
2
|
export declare function defineOgImageScreenshot(options?: OgImageScreenshotPayload): void;
|
|
3
|
+
export declare function defineOgImageDynamic(options?: OgImageScreenshotPayload): void;
|
|
4
|
+
export declare function defineOgImageStatic(options?: OgImageScreenshotPayload): void;
|
|
3
5
|
export declare function defineOgImage(options?: OgImagePayload): void;
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import { useServerHead } from "@vueuse/head";
|
|
2
|
+
import { withBase } from "ufo";
|
|
3
|
+
import { useRequestEvent } from "#app";
|
|
2
4
|
import { useRouter } from "#imports";
|
|
3
|
-
import {
|
|
5
|
+
import { PayloadScriptId } from "#nuxt-og-image/constants";
|
|
6
|
+
import { forcePrerender, height, host, width } from "#nuxt-og-image/config";
|
|
4
7
|
export function defineOgImageScreenshot(options = {}) {
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
const route = router?.currentRoute?.value?.path || "";
|
|
5
10
|
defineOgImage({
|
|
6
|
-
alt: "
|
|
11
|
+
alt: `Web page screenshot${route ? ` of ${route}` : ""}.`,
|
|
12
|
+
provider: "browser",
|
|
13
|
+
prerender: true,
|
|
14
|
+
...options
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function defineOgImageDynamic(options = {}) {
|
|
18
|
+
defineOgImage({
|
|
19
|
+
provider: "satori",
|
|
20
|
+
...options
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function defineOgImageStatic(options = {}) {
|
|
24
|
+
defineOgImage({
|
|
25
|
+
provider: "satori",
|
|
26
|
+
prerender: true,
|
|
7
27
|
...options
|
|
8
28
|
});
|
|
9
29
|
}
|
|
@@ -11,6 +31,9 @@ export function defineOgImage(options = {}) {
|
|
|
11
31
|
if (process.server) {
|
|
12
32
|
const router = useRouter();
|
|
13
33
|
const route = router?.currentRoute?.value?.path || "";
|
|
34
|
+
const e = useRequestEvent();
|
|
35
|
+
if ((forcePrerender || options.prerender) && options.provider === "satori")
|
|
36
|
+
e.res.setHeader("x-nitro-prerender", `${route === "/" ? "" : route}/__og_image__/og.png`);
|
|
14
37
|
const meta = [
|
|
15
38
|
{
|
|
16
39
|
property: "twitter:card",
|
|
@@ -18,7 +41,15 @@ export function defineOgImage(options = {}) {
|
|
|
18
41
|
},
|
|
19
42
|
{
|
|
20
43
|
property: "og:image",
|
|
21
|
-
content: () =>
|
|
44
|
+
content: () => withBase(`${route}/__og_image__/og.png`, host)
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
property: "og:image:width",
|
|
48
|
+
content: width
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
property: "og:image:height",
|
|
52
|
+
content: height
|
|
22
53
|
}
|
|
23
54
|
];
|
|
24
55
|
if (options.alt) {
|
|
@@ -29,13 +60,6 @@ export function defineOgImage(options = {}) {
|
|
|
29
60
|
}
|
|
30
61
|
useServerHead({
|
|
31
62
|
meta,
|
|
32
|
-
link: !options.runtime && options.component ? [
|
|
33
|
-
{
|
|
34
|
-
id: LinkPrerenderId,
|
|
35
|
-
rel: "prerender",
|
|
36
|
-
href: `${route}/${HtmlRendererRoute}`
|
|
37
|
-
}
|
|
38
|
-
] : [],
|
|
39
63
|
script: [
|
|
40
64
|
{
|
|
41
65
|
id: PayloadScriptId,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { screenshot } from "../../browserUtil.mjs";
|
|
2
|
+
import { height, width } from "#nuxt-og-image/config";
|
|
3
|
+
import { createBrowser } from "#nuxt-og-image/browser";
|
|
4
|
+
export default {
|
|
5
|
+
name: "browser",
|
|
6
|
+
createSvg: function createSvg() {
|
|
7
|
+
throw new Error("Browser provider isn't able to create SVGs.");
|
|
8
|
+
},
|
|
9
|
+
createPng: async function createPng(basePath) {
|
|
10
|
+
const browser = await createBrowser();
|
|
11
|
+
return screenshot(browser, basePath, {
|
|
12
|
+
width: Number(width),
|
|
13
|
+
height: Number(height)
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
};
|