nuxt-og-image 0.3.3 → 0.4.0
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 +102 -131
- package/dist/module.d.ts +1 -6
- package/dist/module.json +1 -1
- package/dist/module.mjs +50 -25
- package/dist/runtime/browserService.d.ts +5 -0
- package/dist/runtime/browserService.mjs +33 -0
- package/dist/runtime/composables/defineOgImage.d.ts +0 -5
- package/dist/runtime/composables/defineOgImage.mjs +2 -6
- package/dist/runtime/nitro/html.d.ts +0 -2
- package/dist/runtime/nitro/html.mjs +5 -6
- package/dist/runtime/nitro/image.d.ts +0 -6
- package/dist/runtime/nitro/image.mjs +2 -35
- package/dist/types.d.ts +1 -1
- package/package.json +2 -8
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
Generate social share images for
|
|
13
|
+
Generate dynamic social share images for you Nuxt v3 app.
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
@@ -18,7 +18,7 @@ Generate social share images for your pre-rendered Nuxt v3 app.
|
|
|
18
18
|
<tbody>
|
|
19
19
|
<td align="center">
|
|
20
20
|
<img width="800" height="0" /><br>
|
|
21
|
-
<i>Status:</i> Early Access
|
|
21
|
+
<i>Status:</i> 🤫 Early Access - Active Development 🤫</b> <br>
|
|
22
22
|
<sup> Please report any issues 🐛</sup><br>
|
|
23
23
|
<sub>Made possible by my <a href="https://github.com/sponsors/harlan-zw">Sponsor Program 💖</a><br> Follow me <a href="https://twitter.com/harlan_zw">@harlan_zw</a> 🐦 • Join <a href="https://discord.gg/275MBUBvgP">Discord</a> for help</sub><br>
|
|
24
24
|
<img width="800" height="0" />
|
|
@@ -29,12 +29,14 @@ Generate social share images for your pre-rendered Nuxt v3 app.
|
|
|
29
29
|
|
|
30
30
|
## Features
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
32
|
+
- 🧙 Generate images for your entire site in minutes with minimal config
|
|
33
|
+
- 🎨 Build your own template with Vue (powered by Nuxt Islands)
|
|
34
|
+
- 📸 OR just generates page screenshots
|
|
35
35
|
|
|
36
36
|
## Install
|
|
37
37
|
|
|
38
|
+
⚠️ This module is still in development. Please get in touch with me over [Twitter](https://twitter.com/harlan_zw) or Discord if you are going to attempt to use this.
|
|
39
|
+
|
|
38
40
|
```bash
|
|
39
41
|
npm install --save-dev nuxt-og-image
|
|
40
42
|
|
|
@@ -54,7 +56,29 @@ export default defineNuxtConfig({
|
|
|
54
56
|
})
|
|
55
57
|
```
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
### Add your host name
|
|
60
|
+
|
|
61
|
+
The `og:image` meta tag requires the full URL, so you must provide your site host.
|
|
62
|
+
|
|
63
|
+
_nuxt.config.ts_
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
export default defineNuxtConfig({
|
|
67
|
+
// Recommended
|
|
68
|
+
runtimeConfig: {
|
|
69
|
+
siteUrl: 'https://example.com',
|
|
70
|
+
},
|
|
71
|
+
// OR
|
|
72
|
+
ogImage: {
|
|
73
|
+
host: 'https://example.com',
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Pre-render routes
|
|
79
|
+
|
|
80
|
+
While the module is in early access, you should ensure that you pre-render any pages you want to
|
|
81
|
+
generate images for.
|
|
58
82
|
|
|
59
83
|
```ts
|
|
60
84
|
export default defineNuxtConfig({
|
|
@@ -71,38 +95,73 @@ export default defineNuxtConfig({
|
|
|
71
95
|
})
|
|
72
96
|
```
|
|
73
97
|
|
|
74
|
-
|
|
98
|
+
### Recommended: Enable Nuxt Islands
|
|
75
99
|
|
|
76
|
-
|
|
100
|
+
To be able to preview the image in development and generate template images, you'll need
|
|
101
|
+
to enable Nuxt Islands.
|
|
77
102
|
|
|
78
|
-
|
|
103
|
+
If you're using Nuxt 3.0.0, you will need to switch to the [edge-release channel](https://nuxt.com/docs/guide/going-further/edge-channel#edge-release-channel).
|
|
79
104
|
|
|
80
|
-
|
|
81
|
-
Nuxt islands.
|
|
105
|
+
Once that's done, you can enable the flag for islands.
|
|
82
106
|
|
|
83
|
-
|
|
107
|
+
_nuxt.config.ts_
|
|
84
108
|
|
|
85
|
-
|
|
109
|
+
```ts
|
|
110
|
+
export default defineNuxtConfig({
|
|
111
|
+
experimental: {
|
|
112
|
+
componentIslands: true
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
```
|
|
86
116
|
|
|
87
|
-
|
|
117
|
+
## Generating Screenshots
|
|
88
118
|
|
|
89
|
-
|
|
119
|
+
```vue
|
|
120
|
+
<script lang="ts" setup>
|
|
121
|
+
defineOgImageScreenshot()
|
|
122
|
+
</script>
|
|
123
|
+
<template>
|
|
124
|
+
<div>
|
|
125
|
+
<!-- Your page / app.vue / layout -->
|
|
126
|
+
</div>
|
|
127
|
+
</template>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Generating Template Images
|
|
131
|
+
|
|
132
|
+
The template image generator is powered by Nuxt Islands. This means that you can use any Vue
|
|
133
|
+
component you want to generate your images.
|
|
134
|
+
|
|
135
|
+
```vue
|
|
136
|
+
<script lang="ts" setup>
|
|
137
|
+
defineOgImage({
|
|
138
|
+
component: 'OgImage', // Nuxt Island component
|
|
139
|
+
// pass in any custom props
|
|
140
|
+
myCustomTitle: 'My Title'
|
|
141
|
+
})
|
|
142
|
+
</script>
|
|
143
|
+
<template>
|
|
144
|
+
<div>
|
|
145
|
+
<!-- Your page / app.vue / layout -->
|
|
146
|
+
</div>
|
|
147
|
+
</template>
|
|
148
|
+
```
|
|
90
149
|
|
|
91
|
-
|
|
150
|
+
### Creating your own template
|
|
92
151
|
|
|
93
|
-
|
|
152
|
+
Create a new component with `.island.vue` as the suffix, such as `components/Banner.island.vue`.
|
|
94
153
|
|
|
95
|
-
|
|
154
|
+
Use the below template to test it works, then modify it how you like.
|
|
96
155
|
|
|
97
156
|
```vue
|
|
98
157
|
<script setup lang="ts">
|
|
99
158
|
const props = defineProps({
|
|
100
|
-
//
|
|
159
|
+
// these will always be provided
|
|
101
160
|
path: String,
|
|
102
161
|
title: String,
|
|
103
162
|
description: String,
|
|
104
|
-
//
|
|
105
|
-
|
|
163
|
+
// anything custom comes here
|
|
164
|
+
backgroundImage: String
|
|
106
165
|
})
|
|
107
166
|
</script>
|
|
108
167
|
|
|
@@ -125,143 +184,50 @@ const props = defineProps({
|
|
|
125
184
|
align-items: center;
|
|
126
185
|
justify-content: center;
|
|
127
186
|
color: white;
|
|
128
|
-
font-weight: bold;
|
|
129
|
-
font-family: sans-serif;
|
|
130
187
|
background: linear-gradient(to bottom, #30e8bf, #ff8235);
|
|
131
188
|
}
|
|
132
189
|
|
|
133
190
|
h1 {
|
|
134
191
|
font-size: 4rem;
|
|
135
|
-
margin: 0;
|
|
136
192
|
}
|
|
137
193
|
</style>
|
|
138
194
|
```
|
|
139
195
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
Within a page
|
|
143
|
-
|
|
144
|
-
### Set host
|
|
196
|
+
Make sure you reference this component when using `defineOgImage` and any props to pass.
|
|
145
197
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
runtimeConfig: {
|
|
152
|
-
siteUrl: 'https://example.com',
|
|
153
|
-
},
|
|
154
|
-
// OR
|
|
155
|
-
sitemap: {
|
|
156
|
-
hostname: 'https://example.com',
|
|
157
|
-
},
|
|
158
|
-
})
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
## Route Rules
|
|
163
|
-
|
|
164
|
-
To change the behavior of the sitemap, you can use route rules. Route rules are provided as [Nitro route rules](https://v3.nuxtjs.org/docs/directory-structure/nitro/#route-rules).
|
|
165
|
-
|
|
166
|
-
_nuxt.config.ts_
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
export default defineNuxtConfig({
|
|
170
|
-
routeRules: {
|
|
171
|
-
// Don't add any /secret/** URLs to the sitemap
|
|
172
|
-
'/secret/**': { index: false },
|
|
173
|
-
// modify the sitemap entry for specific URLs
|
|
174
|
-
'/about': { sitemap: { changefreq: 'daily', priority: 0.3 } }
|
|
175
|
-
}
|
|
198
|
+
```vue
|
|
199
|
+
<script lang="ts" setup>
|
|
200
|
+
defineOgImage({
|
|
201
|
+
component: 'Banner',
|
|
202
|
+
backgroundImage: 'https://example.com/my-background-image.jpg',
|
|
176
203
|
})
|
|
204
|
+
</script>
|
|
177
205
|
```
|
|
178
206
|
|
|
179
|
-
|
|
207
|
+
## Previewing Images
|
|
180
208
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
- `
|
|
209
|
+
Once you have defined the og:image using the composable, you can preview the image by visiting
|
|
210
|
+
the following URLs:
|
|
211
|
+
- `/your-path/__og-image` Renders the HTML output
|
|
212
|
+
- `/your-path/og-image.png` Renders the og:image
|
|
184
213
|
|
|
185
214
|
## Module Config
|
|
186
215
|
|
|
187
|
-
If you need further control over the sitemap URLs, you can provide config on the `sitemap` key.
|
|
188
|
-
|
|
189
216
|
### `host`
|
|
190
217
|
|
|
191
218
|
- Type: `string`
|
|
192
219
|
- Default: `undefined`
|
|
193
220
|
- Required: `true`
|
|
194
221
|
|
|
195
|
-
The host of your site. This is required to generate the
|
|
196
|
-
|
|
197
|
-
### `trailingSlash`
|
|
198
|
-
|
|
199
|
-
- Type: `boolean`
|
|
200
|
-
- Default: `false`
|
|
201
|
-
|
|
202
|
-
Whether to add a trailing slash to the URLs in the sitemap.xml.
|
|
222
|
+
The host of your site. This is required to generate the absolute path of the og:image.
|
|
203
223
|
|
|
204
|
-
### `
|
|
224
|
+
### `runtimeImages`
|
|
205
225
|
|
|
206
226
|
- Type: `boolean`
|
|
207
|
-
- Default: `
|
|
208
|
-
|
|
209
|
-
Whether to generate the sitemap.xml.
|
|
210
|
-
|
|
211
|
-
### `include`
|
|
212
|
-
|
|
213
|
-
- Type: `string[]`
|
|
214
|
-
- Default: `undefined`
|
|
215
|
-
|
|
216
|
-
Filter routes that match the given rules.
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
export default defineNuxtConfig({
|
|
220
|
-
sitemap: {
|
|
221
|
-
include: [
|
|
222
|
-
'/my-hidden-url'
|
|
223
|
-
]
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### `exclude`
|
|
229
|
-
|
|
230
|
-
- Type: `string[]`
|
|
231
|
-
- Default: `undefined`
|
|
232
|
-
|
|
233
|
-
Filter routes that match the given rules.
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
export default defineNuxtConfig({
|
|
237
|
-
sitemap: {
|
|
238
|
-
exclude: [
|
|
239
|
-
'/my-secret-section/**'
|
|
240
|
-
]
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Additional config extends [sitemap.js](https://github.com/ekalinin/sitemap.js).
|
|
227
|
+
- Default: `process.dev`
|
|
246
228
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
### Add custom routes without pre-rendering
|
|
250
|
-
|
|
251
|
-
```ts
|
|
252
|
-
export default defineNuxtConfig({
|
|
253
|
-
hooks: {
|
|
254
|
-
'sitemap:generate': (ctx) => {
|
|
255
|
-
// add custom URLs
|
|
256
|
-
ctx.urls.push({
|
|
257
|
-
url: '/my-custom-url',
|
|
258
|
-
changefreq: 'daily',
|
|
259
|
-
priority: 0.3
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
})
|
|
264
|
-
```
|
|
229
|
+
Allows you to generate images at runtime in production. This uses a headless browser to generate images
|
|
230
|
+
and may have deployment issues.
|
|
265
231
|
|
|
266
232
|
## Sponsors
|
|
267
233
|
|
|
@@ -271,6 +237,11 @@ export default defineNuxtConfig({
|
|
|
271
237
|
</a>
|
|
272
238
|
</p>
|
|
273
239
|
|
|
240
|
+
## Credits
|
|
241
|
+
|
|
242
|
+
- Pooya Parsa [Kachick](https://github.com/unjs/kachik)
|
|
243
|
+
- Nuxt Team
|
|
244
|
+
|
|
274
245
|
|
|
275
246
|
## License
|
|
276
247
|
|
package/dist/module.d.ts
CHANGED
|
@@ -25,7 +25,6 @@ declare module 'nitropack' {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface ModuleOptions extends ScreenshotOptions {
|
|
28
|
-
defaultIslandComponent: string;
|
|
29
28
|
/**
|
|
30
29
|
* The directory within `public` where the og images will be stored.
|
|
31
30
|
*
|
|
@@ -41,10 +40,6 @@ interface ModuleOptions extends ScreenshotOptions {
|
|
|
41
40
|
*/
|
|
42
41
|
runtimeImages: boolean;
|
|
43
42
|
}
|
|
44
|
-
declare const HtmlRendererRoute = "__og_image";
|
|
45
|
-
declare const PayloadScriptId = "nuxt-og-image-payload";
|
|
46
|
-
declare const MetaOgImageContentPlaceholder = "__NUXT_OG_IMAGE_PLACEHOLDER__";
|
|
47
|
-
declare const LinkPrerenderId = "nuxt-og-image-screenshot-path";
|
|
48
43
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
49
44
|
|
|
50
|
-
export {
|
|
45
|
+
export { ModuleOptions, _default as default };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -5,8 +5,9 @@ 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 { withBase } from 'ufo';
|
|
8
|
+
import { withBase, joinURL } from 'ufo';
|
|
9
9
|
import fg from 'fast-glob';
|
|
10
|
+
import { join } from 'pathe';
|
|
10
11
|
|
|
11
12
|
async function createBrowser() {
|
|
12
13
|
if (!process.env.AWS_LAMBDA_FUNCTION_NAME)
|
|
@@ -46,6 +47,15 @@ const HtmlRendererRoute = "__og_image";
|
|
|
46
47
|
const PayloadScriptId = "nuxt-og-image-payload";
|
|
47
48
|
const MetaOgImageContentPlaceholder = "__NUXT_OG_IMAGE_PLACEHOLDER__";
|
|
48
49
|
const LinkPrerenderId = "nuxt-og-image-screenshot-path";
|
|
50
|
+
const DefaultRuntimeImageSuffix = "og-image.png";
|
|
51
|
+
const Constants = {
|
|
52
|
+
HtmlRendererRoute,
|
|
53
|
+
PayloadScriptId,
|
|
54
|
+
MetaOgImageContentPlaceholder,
|
|
55
|
+
LinkPrerenderId,
|
|
56
|
+
DefaultRuntimeImageSuffix
|
|
57
|
+
};
|
|
58
|
+
|
|
49
59
|
const module = defineNuxtModule({
|
|
50
60
|
meta: {
|
|
51
61
|
name: "nuxt-og-image",
|
|
@@ -106,19 +116,32 @@ declare module 'nitropack' {
|
|
|
106
116
|
filePath: resolve("./runtime/components/OgImage.vue"),
|
|
107
117
|
island: true
|
|
108
118
|
});
|
|
119
|
+
const runtimeDir = resolve("./runtime");
|
|
120
|
+
nuxt.options.build.transpile.push(runtimeDir);
|
|
121
|
+
const constScript = Object.entries(Constants).map(([k, v]) => `export const ${k} = '${v}'`).join("\n");
|
|
122
|
+
nuxt.options.alias["#nuxt-og-image/constants"] = addTemplate({
|
|
123
|
+
filename: "nuxt-og-image-constants.mjs",
|
|
124
|
+
getContents: () => constScript
|
|
125
|
+
}).dst;
|
|
126
|
+
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
127
|
+
nitroConfig.externals = defu(nitroConfig.externals || {}, {
|
|
128
|
+
inline: [runtimeDir]
|
|
129
|
+
});
|
|
130
|
+
nitroConfig.virtual["#nuxt-og-image/constants"] = constScript;
|
|
131
|
+
});
|
|
109
132
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
110
133
|
let entries = [];
|
|
111
134
|
const _routeRulesMatcher = toRouteMatcher(
|
|
112
135
|
createRouter({ routes: nitro.options.routeRules })
|
|
113
136
|
);
|
|
114
|
-
const outputPath =
|
|
137
|
+
const outputPath = join(nitro.options.output.publicDir, config.outputDir);
|
|
115
138
|
nitro.hooks.hook("prerender:generate", async (ctx) => {
|
|
116
139
|
if (ctx.route.includes(".") || ctx.route.endsWith(HtmlRendererRoute))
|
|
117
140
|
return;
|
|
118
141
|
let html = ctx.contents;
|
|
119
142
|
if (!html)
|
|
120
143
|
return;
|
|
121
|
-
if (!html.includes(
|
|
144
|
+
if (!html.includes(`id="${PayloadScriptId}"`))
|
|
122
145
|
return;
|
|
123
146
|
const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
|
|
124
147
|
if (routeRules.ogImage === false)
|
|
@@ -129,8 +152,8 @@ declare module 'nitropack' {
|
|
|
129
152
|
const entry = {
|
|
130
153
|
fileName,
|
|
131
154
|
absoluteUrl,
|
|
132
|
-
outputPath:
|
|
133
|
-
linkingHtml: ctx.fileName,
|
|
155
|
+
outputPath: joinURL(nitro.options.output.publicDir, config.outputDir, fileName),
|
|
156
|
+
linkingHtml: joinURL(nitro.options.output.publicDir, ctx.fileName),
|
|
134
157
|
route: ctx.route,
|
|
135
158
|
hasPayload: screenshotPath,
|
|
136
159
|
routeRules: routeRules.ogImage || "",
|
|
@@ -149,7 +172,8 @@ declare module 'nitropack' {
|
|
|
149
172
|
await mkdir(outputPath, { recursive: true });
|
|
150
173
|
} catch (e) {
|
|
151
174
|
}
|
|
152
|
-
const previewProcess = execa("npx", ["serve",
|
|
175
|
+
const previewProcess = execa("npx", ["serve", nitro.options.output.publicDir]);
|
|
176
|
+
let browser = null;
|
|
153
177
|
try {
|
|
154
178
|
previewProcess.stderr?.pipe(process.stderr);
|
|
155
179
|
const host = (await new Promise((resolve2) => {
|
|
@@ -159,39 +183,40 @@ declare module 'nitropack' {
|
|
|
159
183
|
}
|
|
160
184
|
});
|
|
161
185
|
})).trim();
|
|
162
|
-
|
|
186
|
+
browser = await createBrowser();
|
|
163
187
|
nitro.logger.info(`Generating ${entries.length} og:images...`);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
188
|
+
for (const k in entries) {
|
|
189
|
+
const entry = entries[k];
|
|
190
|
+
const start = Date.now();
|
|
191
|
+
let hasError = false;
|
|
192
|
+
try {
|
|
168
193
|
const imgBuffer = await screenshot(browser, `${host}${entry.screenshotPath}`, config);
|
|
169
194
|
await writeFile(entry.outputPath, imgBuffer);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
));
|
|
195
|
+
} catch (e) {
|
|
196
|
+
hasError = true;
|
|
197
|
+
console.error(e);
|
|
174
198
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
199
|
+
const generateTimeMS = Date.now() - start;
|
|
200
|
+
nitro.logger.log(chalk[hasError ? "red" : "gray"](
|
|
201
|
+
` ${Number(k) === entries.length - 1 ? "\u2514\u2500" : "\u251C\u2500"} /${config.outputDir}/${entry.fileName} (${generateTimeMS}ms) ${Math.round(Number(k) / (entries.length - 1) * 100)}%`
|
|
202
|
+
));
|
|
179
203
|
}
|
|
180
204
|
} catch (e) {
|
|
181
205
|
console.error(e);
|
|
182
206
|
} finally {
|
|
207
|
+
await browser?.close();
|
|
183
208
|
previewProcess.kill();
|
|
184
209
|
}
|
|
185
|
-
for (const entry of entries
|
|
186
|
-
const html = await readFile(
|
|
210
|
+
for (const entry of entries) {
|
|
211
|
+
const html = await readFile(entry.linkingHtml, "utf-8");
|
|
187
212
|
const newHtml = html.replace(new RegExp(`<link id="${LinkPrerenderId}" rel="prerender" href="(.*?)">`), "").replace(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.*?)<\/script>`), "").replace("\n\n", "\n");
|
|
188
213
|
if (html !== newHtml) {
|
|
189
|
-
await writeFile(
|
|
214
|
+
await writeFile(entry.linkingHtml, newHtml, { encoding: "utf-8" });
|
|
190
215
|
}
|
|
191
216
|
}
|
|
192
|
-
const ogImageFolders = await fg([`**/${HtmlRendererRoute}`], { cwd: nitro.options.output.
|
|
217
|
+
const ogImageFolders = await fg([`**/${HtmlRendererRoute}`], { cwd: nitro.options.output.publicDir, onlyDirectories: true });
|
|
193
218
|
for (const ogImageFolder of ogImageFolders)
|
|
194
|
-
await rm(
|
|
219
|
+
await rm(join(nitro.options.output.publicDir, ogImageFolder), { recursive: true, force: true });
|
|
195
220
|
entries = [];
|
|
196
221
|
};
|
|
197
222
|
nitro.hooks.hook("rollup:before", async () => {
|
|
@@ -204,4 +229,4 @@ declare module 'nitropack' {
|
|
|
204
229
|
}
|
|
205
230
|
});
|
|
206
231
|
|
|
207
|
-
export {
|
|
232
|
+
export { module as default };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { Browser } from 'playwright-core';
|
|
3
|
+
import type { ScreenshotOptions } from '../types';
|
|
4
|
+
export declare function createBrowser(): Promise<Browser>;
|
|
5
|
+
export declare function screenshot(browser: Browser, url: string, options: ScreenshotOptions): Promise<Buffer>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export async function createBrowser() {
|
|
2
|
+
if (!process.env.AWS_LAMBDA_FUNCTION_NAME)
|
|
3
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME = "test";
|
|
4
|
+
const playwright = await import("playwright-core");
|
|
5
|
+
const awsChrome = await import("chrome-aws-lambda");
|
|
6
|
+
return await playwright.chromium.launch({
|
|
7
|
+
args: awsChrome.args,
|
|
8
|
+
executablePath: await awsChrome.executablePath,
|
|
9
|
+
headless: awsChrome.headless
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export async function screenshot(browser, url, options) {
|
|
13
|
+
const page = await browser.newPage({
|
|
14
|
+
colorScheme: options.colorScheme
|
|
15
|
+
});
|
|
16
|
+
await page.setViewportSize({
|
|
17
|
+
width: options.width,
|
|
18
|
+
height: options.height
|
|
19
|
+
});
|
|
20
|
+
await page.goto(url, {
|
|
21
|
+
timeout: 1e4,
|
|
22
|
+
waitUntil: "networkidle"
|
|
23
|
+
});
|
|
24
|
+
if (options.mask) {
|
|
25
|
+
await page.evaluate((mask) => {
|
|
26
|
+
for (const el of document.querySelectorAll(mask))
|
|
27
|
+
el.style.display = "none";
|
|
28
|
+
}, options.mask);
|
|
29
|
+
}
|
|
30
|
+
if (options.selector)
|
|
31
|
+
await page.locator(options.selector).screenshot();
|
|
32
|
+
return await page.screenshot();
|
|
33
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
export declare const HtmlRendererRoute = "__og_image";
|
|
2
|
-
export declare const RuntimeImageSuffix = "og-image.png";
|
|
3
|
-
export declare const PayloadScriptId = "nuxt-og-image-payload";
|
|
4
|
-
export declare const MetaOgImageContentPlaceholder = "__NUXT_OG_IMAGE_PLACEHOLDER__";
|
|
5
|
-
export declare const LinkPrerenderId = "nuxt-og-image-screenshot-path";
|
|
6
1
|
export interface OgImagePayload {
|
|
7
2
|
runtime?: boolean;
|
|
8
3
|
title?: string;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { useServerHead } from "@vueuse/head";
|
|
2
2
|
import { useRouter } from "#imports";
|
|
3
|
-
|
|
4
|
-
export const RuntimeImageSuffix = "og-image.png";
|
|
5
|
-
export const PayloadScriptId = "nuxt-og-image-payload";
|
|
6
|
-
export const MetaOgImageContentPlaceholder = "__NUXT_OG_IMAGE_PLACEHOLDER__";
|
|
7
|
-
export const LinkPrerenderId = "nuxt-og-image-screenshot-path";
|
|
3
|
+
import { DefaultRuntimeImageSuffix, HtmlRendererRoute, LinkPrerenderId, MetaOgImageContentPlaceholder, PayloadScriptId } from "#nuxt-og-image/constants";
|
|
8
4
|
export function defineOgImageScreenshot() {
|
|
9
5
|
defineOgImage();
|
|
10
6
|
}
|
|
@@ -16,7 +12,7 @@ export function defineOgImage(options = {}) {
|
|
|
16
12
|
meta: [
|
|
17
13
|
{
|
|
18
14
|
property: "og:image",
|
|
19
|
-
content: () => options.runtime ? `${route}/${
|
|
15
|
+
content: () => options.runtime ? `${route}/${DefaultRuntimeImageSuffix}` : MetaOgImageContentPlaceholder
|
|
20
16
|
}
|
|
21
17
|
],
|
|
22
18
|
link: options.component ? [
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export declare const HtmlRendererRoute = "__og_image";
|
|
2
|
-
export declare const PayloadScriptId = "nuxt-og-image-payload";
|
|
3
1
|
export declare const extractOgPayload: (html: string) => any;
|
|
4
2
|
export declare const inferOgPayload: (html: string) => Record<string, any>;
|
|
5
3
|
declare const _default: import("h3").EventHandler<string | undefined>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { withQuery } from "ufo";
|
|
1
|
+
import { withQuery, withoutTrailingSlash } from "ufo";
|
|
2
2
|
import { renderSSRHead } from "@unhead/ssr";
|
|
3
3
|
import { createHeadCore } from "@unhead/vue";
|
|
4
4
|
import { defineEventHandler, getQuery } from "h3";
|
|
5
|
-
|
|
6
|
-
export const PayloadScriptId = "nuxt-og-image-payload";
|
|
5
|
+
import { HtmlRendererRoute, PayloadScriptId } from "#nuxt-og-image/constants";
|
|
7
6
|
export const extractOgPayload = (html) => {
|
|
8
7
|
const payload = html.match(new RegExp(`<script id="${PayloadScriptId}" type="application/json">(.+?)<\/script>`))?.[1];
|
|
9
8
|
if (payload) {
|
|
@@ -24,8 +23,8 @@ export const inferOgPayload = (html) => {
|
|
|
24
23
|
export default defineEventHandler(async (req) => {
|
|
25
24
|
if (!req.path?.endsWith(HtmlRendererRoute))
|
|
26
25
|
return;
|
|
27
|
-
const path = req.path.replace(
|
|
28
|
-
const html = await $fetch(path);
|
|
26
|
+
const path = req.path.replace(HtmlRendererRoute, "");
|
|
27
|
+
const html = await $fetch(withoutTrailingSlash(path));
|
|
29
28
|
const payload = {
|
|
30
29
|
path,
|
|
31
30
|
title: "Hello World",
|
|
@@ -35,7 +34,7 @@ export default defineEventHandler(async (req) => {
|
|
|
35
34
|
...inferOgPayload(html),
|
|
36
35
|
...getQuery(req)
|
|
37
36
|
};
|
|
38
|
-
const result = await $fetch(withQuery(`/__nuxt_island/${payload.
|
|
37
|
+
const result = await $fetch(withQuery(`/__nuxt_island/${payload.component || "OgImage"}`, {
|
|
39
38
|
props: JSON.stringify(payload)
|
|
40
39
|
}));
|
|
41
40
|
const head = createHeadCore();
|
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import type { Browser } from 'playwright-core';
|
|
3
|
-
import type { ScreenshotOptions } from '../../types';
|
|
4
|
-
export declare const HtmlRendererRoute = "__og_image";
|
|
5
|
-
export declare const RuntimeImageSuffix = "og-image.png";
|
|
6
|
-
export declare function screenshot(browser: Browser, url: string, options: ScreenshotOptions): Promise<Buffer>;
|
|
7
|
-
export declare function createBrowser(): Promise<Browser>;
|
|
8
2
|
declare const _default: import("h3").EventHandler<Buffer | undefined>;
|
|
9
3
|
export default _default;
|
|
@@ -1,39 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler, getRequestHeader, setHeader } from "h3";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export async function screenshot(browser, url, options) {
|
|
5
|
-
const page = await browser.newPage({
|
|
6
|
-
colorScheme: options.colorScheme
|
|
7
|
-
});
|
|
8
|
-
await page.setViewportSize({
|
|
9
|
-
width: options.width,
|
|
10
|
-
height: options.height
|
|
11
|
-
});
|
|
12
|
-
await page.goto(url, {
|
|
13
|
-
timeout: 1e4,
|
|
14
|
-
waitUntil: "networkidle"
|
|
15
|
-
});
|
|
16
|
-
if (options.mask) {
|
|
17
|
-
await page.evaluate((mask) => {
|
|
18
|
-
for (const el of document.querySelectorAll(mask))
|
|
19
|
-
el.style.display = "none";
|
|
20
|
-
}, options.mask);
|
|
21
|
-
}
|
|
22
|
-
if (options.selector)
|
|
23
|
-
await page.locator(options.selector).screenshot();
|
|
24
|
-
return await page.screenshot();
|
|
25
|
-
}
|
|
26
|
-
export async function createBrowser() {
|
|
27
|
-
if (!process.env.AWS_LAMBDA_FUNCTION_NAME)
|
|
28
|
-
process.env.AWS_LAMBDA_FUNCTION_NAME = "test";
|
|
29
|
-
const playwright = await import("playwright-core");
|
|
30
|
-
const awsChrome = await import("chrome-aws-lambda");
|
|
31
|
-
return await playwright.chromium.launch({
|
|
32
|
-
args: awsChrome.args,
|
|
33
|
-
executablePath: await awsChrome.executablePath,
|
|
34
|
-
headless: awsChrome.headless
|
|
35
|
-
});
|
|
36
|
-
}
|
|
2
|
+
import { createBrowser, screenshot } from "../browserService.mjs";
|
|
3
|
+
import { HtmlRendererRoute, RuntimeImageSuffix } from "#nuxt-og-image/constants";
|
|
37
4
|
export default defineEventHandler(async (e) => {
|
|
38
5
|
if (!e.path?.endsWith(RuntimeImageSuffix))
|
|
39
6
|
return;
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-og-image",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"packageManager": "pnpm@7.8.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/harlan-zw",
|
|
@@ -33,16 +33,11 @@
|
|
|
33
33
|
"execa": "^6.1.0",
|
|
34
34
|
"fast-glob": "^3.2.12",
|
|
35
35
|
"ohash": "^1.0.0",
|
|
36
|
+
"pathe": "^1.0.0",
|
|
36
37
|
"playwright-core": "^1.28.1",
|
|
37
38
|
"radix3": "^1.0.0",
|
|
38
39
|
"ufo": "^1.0.1"
|
|
39
40
|
},
|
|
40
|
-
"unbuild": {
|
|
41
|
-
"externals": [
|
|
42
|
-
"playwright-core",
|
|
43
|
-
"chrome-aws-lambda"
|
|
44
|
-
]
|
|
45
|
-
},
|
|
46
41
|
"devDependencies": {
|
|
47
42
|
"@antfu/eslint-config": "^0.33.1",
|
|
48
43
|
"@nuxt/kit": "3.0.0",
|
|
@@ -52,7 +47,6 @@
|
|
|
52
47
|
"bumpp": "^8.2.1",
|
|
53
48
|
"eslint": "8.29.0",
|
|
54
49
|
"nuxt": "npm:nuxt3@latest",
|
|
55
|
-
"pathe": "^1.0.0",
|
|
56
50
|
"puppeteer": "^19.4.0",
|
|
57
51
|
"vitest": "^0.25.5"
|
|
58
52
|
},
|