nuxt-og-image 0.4.7 → 0.5.2
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 +43 -32
- package/dist/module.d.ts +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +11 -25
- package/dist/runtime/{browserService.d.ts → browserUtil.d.ts} +0 -1
- package/dist/runtime/browserUtil.mjs +22 -0
- package/dist/runtime/browsers/default.d.ts +1 -0
- package/dist/runtime/browsers/default.mjs +19 -0
- package/dist/runtime/browsers/lambda.d.ts +1 -0
- package/dist/runtime/browsers/lambda.mjs +9 -0
- package/dist/runtime/composables/defineOgImage.d.ts +1 -0
- package/dist/runtime/composables/defineOgImage.mjs +9 -1
- package/dist/runtime/nitro/image.d.ts +1 -1
- package/dist/runtime/nitro/image.mjs +5 -2
- package/package.json +2 -2
- package/dist/runtime/browserService.mjs +0 -62
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Generate dynamic social share images for you Nuxt v3 app.
|
|
|
18
18
|
<tbody>
|
|
19
19
|
<td align="center">
|
|
20
20
|
<img width="800" height="0" /><br>
|
|
21
|
-
<i>Status:</i>
|
|
21
|
+
<i>Status:</i> Early Access</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,32 +29,39 @@ Generate dynamic social share images for you Nuxt v3 app.
|
|
|
29
29
|
|
|
30
30
|
## Features
|
|
31
31
|
|
|
32
|
-
- 🧙
|
|
33
|
-
- 🎨
|
|
34
|
-
- 📸 OR just
|
|
35
|
-
- 📦
|
|
32
|
+
- 🧙 Pre-render `og:image`'s for your entire site in minutes with minimal config
|
|
33
|
+
- 🎨 Using a Vue component (powered by Nuxt Islands)
|
|
34
|
+
- 📸 OR just generate screenshots
|
|
35
|
+
- 📦 Choose your API: Composition or components
|
|
36
|
+
|
|
37
|
+
⚠️ SSR runtime rendering is experimental.
|
|
36
38
|
|
|
37
39
|
## Install
|
|
38
40
|
|
|
39
|
-
⚠️ This module is
|
|
41
|
+
⚠️ This module is in early access. Please report any issues you find.
|
|
40
42
|
|
|
41
43
|
```bash
|
|
44
|
+
# Install module
|
|
42
45
|
npm install --save-dev nuxt-og-image
|
|
43
46
|
# Using yarn
|
|
44
47
|
yarn add --dev nuxt-og-image
|
|
45
48
|
```
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
If you don't have a chromium binary installed on your system, run `npx playwright install`.
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
installed chrome.
|
|
52
|
+
### CI Build
|
|
51
53
|
|
|
52
|
-
If you
|
|
54
|
+
If you are using this module in a CI context and the images aren't being generated,
|
|
55
|
+
you should replace your build script with the following:
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
_package.json_
|
|
55
58
|
|
|
56
|
-
```
|
|
57
|
-
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "npx playwright install && nuxt build"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
58
65
|
```
|
|
59
66
|
|
|
60
67
|
## Setup
|
|
@@ -108,25 +115,6 @@ export default defineNuxtConfig({
|
|
|
108
115
|
})
|
|
109
116
|
```
|
|
110
117
|
|
|
111
|
-
### Recommended: Enable Nuxt Islands
|
|
112
|
-
|
|
113
|
-
To be able to preview the image in development and generate template images, you'll need
|
|
114
|
-
to enable Nuxt Islands.
|
|
115
|
-
|
|
116
|
-
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).
|
|
117
|
-
|
|
118
|
-
Once that's done, you can enable the flag for islands.
|
|
119
|
-
|
|
120
|
-
_nuxt.config.ts_
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
export default defineNuxtConfig({
|
|
124
|
-
experimental: {
|
|
125
|
-
componentIslands: true
|
|
126
|
-
},
|
|
127
|
-
})
|
|
128
|
-
```
|
|
129
|
-
|
|
130
118
|
## Generating Screenshots
|
|
131
119
|
|
|
132
120
|
_Your page / app.vue / layout_
|
|
@@ -156,6 +144,7 @@ _Your page / app.vue / layout_
|
|
|
156
144
|
// Choose either Composition API
|
|
157
145
|
defineOgImage({
|
|
158
146
|
component: 'OgImageTemplate', // Nuxt Island component
|
|
147
|
+
alt: 'My awesome image', // alt text for image
|
|
159
148
|
// pass in any custom props
|
|
160
149
|
myCustomTitle: 'My Title'
|
|
161
150
|
})
|
|
@@ -168,6 +157,25 @@ defineOgImage({
|
|
|
168
157
|
</template>
|
|
169
158
|
```
|
|
170
159
|
|
|
160
|
+
### Requirements
|
|
161
|
+
|
|
162
|
+
To be able to preview the image in development and generate template images, you'll need
|
|
163
|
+
to enable Nuxt Islands.
|
|
164
|
+
|
|
165
|
+
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).
|
|
166
|
+
|
|
167
|
+
Once that's done, you can enable the flag for islands.
|
|
168
|
+
|
|
169
|
+
_nuxt.config.ts_
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
export default defineNuxtConfig({
|
|
173
|
+
experimental: {
|
|
174
|
+
componentIslands: true
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
171
179
|
### Creating your own template
|
|
172
180
|
|
|
173
181
|
Create a new component with `.island.vue` as the suffix, such as `components/Banner.island.vue`.
|
|
@@ -250,6 +258,9 @@ The host of your site. This is required to generate the absolute path of the og:
|
|
|
250
258
|
Allows you to generate images at runtime in production. This uses a headless browser to generate images
|
|
251
259
|
and may have deployment issues.
|
|
252
260
|
|
|
261
|
+
⚠️ This is experimental and will likely not work in all environments.
|
|
262
|
+
|
|
263
|
+
|
|
253
264
|
## Examples
|
|
254
265
|
|
|
255
266
|
- [Unhead Docs](https://github.com/unjs/unhead/tree/main/docs)
|
package/dist/module.d.ts
CHANGED
|
@@ -36,9 +36,9 @@ interface ModuleOptions extends ScreenshotOptions {
|
|
|
36
36
|
*/
|
|
37
37
|
host: string;
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Are we edge-side rendering images.
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
edgeSideRender: boolean;
|
|
42
42
|
}
|
|
43
43
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
44
44
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -11,29 +11,14 @@ import { join } from 'pathe';
|
|
|
11
11
|
|
|
12
12
|
async function createBrowser() {
|
|
13
13
|
try {
|
|
14
|
-
const playwrightCore = await import(
|
|
15
|
-
|
|
16
|
-
const awsChrome = await import(String("chrome-aws-lambda"));
|
|
17
|
-
return await playwrightCore.chromium.launch({
|
|
18
|
-
args: awsChrome.args,
|
|
19
|
-
executablePath: await awsChrome.executablePath,
|
|
20
|
-
headless: awsChrome.headless
|
|
21
|
-
});
|
|
22
|
-
} catch (e) {
|
|
23
|
-
if (!process.dev)
|
|
24
|
-
console.log("[nuxt-og-image] Skipping chrome-aws-lambda", e);
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
const playwrightCore = await import('playwright-core');
|
|
28
|
-
const { Launcher } = await import('chrome-launcher');
|
|
14
|
+
const playwrightCore = await import(String("playwright-core"));
|
|
15
|
+
const { Launcher } = await import(String("chrome-launcher"));
|
|
29
16
|
const chromePath = Launcher.getFirstInstallation();
|
|
30
17
|
return await playwrightCore.chromium.launch({
|
|
31
18
|
headless: true,
|
|
32
19
|
executablePath: chromePath
|
|
33
20
|
});
|
|
34
21
|
} catch (e) {
|
|
35
|
-
if (!process.dev)
|
|
36
|
-
console.log("[nuxt-og-image] Skipping chrome-launcher", e);
|
|
37
22
|
}
|
|
38
23
|
try {
|
|
39
24
|
const playwright = await import(String("playwright"));
|
|
@@ -41,14 +26,9 @@ async function createBrowser() {
|
|
|
41
26
|
headless: true
|
|
42
27
|
});
|
|
43
28
|
} catch (e) {
|
|
44
|
-
if (!process.dev)
|
|
45
|
-
console.log("[nuxt-og-image] Playwright failed", e);
|
|
46
|
-
throw new Error(`
|
|
47
|
-
Missing chromium binary. You need either 'playwright' or 'chrome-aws-lambda'.
|
|
48
|
-
Please run 'yarn add --dev playwright' or 'npm install --save-dev playwright'
|
|
49
|
-
`);
|
|
50
29
|
}
|
|
51
30
|
}
|
|
31
|
+
|
|
52
32
|
async function screenshot(browser, url, options) {
|
|
53
33
|
const page = await browser.newPage({
|
|
54
34
|
colorScheme: options.colorScheme
|
|
@@ -101,12 +81,13 @@ const module = defineNuxtModule({
|
|
|
101
81
|
height: 630,
|
|
102
82
|
defaultIslandComponent: "OgImageTemplate",
|
|
103
83
|
outputDir: "_og-images",
|
|
104
|
-
|
|
84
|
+
edgeSideRender: nuxt.options.dev || (process.env.NITRO_PRESET || "").includes("edge")
|
|
105
85
|
};
|
|
106
86
|
},
|
|
107
87
|
async setup(config, nuxt) {
|
|
108
88
|
const { resolve } = createResolver(import.meta.url);
|
|
109
89
|
nuxt.options.experimental.componentIslands = true;
|
|
90
|
+
const isEdge = (process.env.NITRO_PRESET || "").includes("edge");
|
|
110
91
|
addTemplate({
|
|
111
92
|
filename: "nuxt-og-image.d.ts",
|
|
112
93
|
getContents: () => {
|
|
@@ -126,7 +107,7 @@ declare module 'nitropack' {
|
|
|
126
107
|
addServerHandler({
|
|
127
108
|
handler: resolve("./runtime/nitro/html")
|
|
128
109
|
});
|
|
129
|
-
if (config.
|
|
110
|
+
if (config.edgeSideRender) {
|
|
130
111
|
addServerHandler({
|
|
131
112
|
handler: resolve("./runtime/nitro/image")
|
|
132
113
|
});
|
|
@@ -164,6 +145,11 @@ declare module 'nitropack' {
|
|
|
164
145
|
inline: [runtimeDir]
|
|
165
146
|
});
|
|
166
147
|
nitroConfig.virtual["#nuxt-og-image/constants"] = constScript;
|
|
148
|
+
nitroConfig.virtual["#nuxt-og-image/browser"] = `export { createBrowser } from '${runtimeDir}/browsers/${isEdge ? "lambda" : "default"}'`;
|
|
149
|
+
if (isEdge) {
|
|
150
|
+
nitroConfig.alias.puppeteer = "unenv/runtime/mock/proxy-cjs";
|
|
151
|
+
nitroConfig.alias.ws = "unenv/runtime/mock/proxy-cjs";
|
|
152
|
+
}
|
|
167
153
|
});
|
|
168
154
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
169
155
|
let entries = [];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Browser } from 'playwright-core';
|
|
3
3
|
import type { ScreenshotOptions } from '../types';
|
|
4
|
-
export declare function createBrowser(): Promise<any>;
|
|
5
4
|
export declare function screenshot(browser: Browser, url: string, options: ScreenshotOptions): Promise<Buffer>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function screenshot(browser, url, options) {
|
|
2
|
+
const page = await browser.newPage({
|
|
3
|
+
colorScheme: options.colorScheme
|
|
4
|
+
});
|
|
5
|
+
await page.setViewportSize({
|
|
6
|
+
width: options.width,
|
|
7
|
+
height: options.height
|
|
8
|
+
});
|
|
9
|
+
await page.goto(url, {
|
|
10
|
+
timeout: 1e4,
|
|
11
|
+
waitUntil: "networkidle"
|
|
12
|
+
});
|
|
13
|
+
if (options.mask) {
|
|
14
|
+
await page.evaluate((mask) => {
|
|
15
|
+
for (const el of document.querySelectorAll(mask))
|
|
16
|
+
el.style.display = "none";
|
|
17
|
+
}, options.mask);
|
|
18
|
+
}
|
|
19
|
+
if (options.selector)
|
|
20
|
+
await page.locator(options.selector).screenshot();
|
|
21
|
+
return await page.screenshot();
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createBrowser(): Promise<any>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export async function createBrowser() {
|
|
2
|
+
try {
|
|
3
|
+
const playwrightCore = await import(String("playwright-core"));
|
|
4
|
+
const { Launcher } = await import(String("chrome-launcher"));
|
|
5
|
+
const chromePath = Launcher.getFirstInstallation();
|
|
6
|
+
return await playwrightCore.chromium.launch({
|
|
7
|
+
headless: true,
|
|
8
|
+
executablePath: chromePath
|
|
9
|
+
});
|
|
10
|
+
} catch (e) {
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const playwright = await import(String("playwright"));
|
|
14
|
+
return await playwright.chromium.launch({
|
|
15
|
+
headless: true
|
|
16
|
+
});
|
|
17
|
+
} catch (e) {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createBrowser(): Promise<import("puppeteer-core").Browser>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import edgeChromium from "chrome-aws-lambda";
|
|
2
|
+
import puppeteer from "puppeteer-core";
|
|
3
|
+
export async function createBrowser() {
|
|
4
|
+
return puppeteer.launch({
|
|
5
|
+
args: edgeChromium.args,
|
|
6
|
+
executablePath: await edgeChromium.executablePath,
|
|
7
|
+
headless: true
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -10,10 +10,18 @@ export function defineOgImage(options = {}) {
|
|
|
10
10
|
const route = router?.currentRoute?.value?.path || "";
|
|
11
11
|
useServerHead({
|
|
12
12
|
meta: [
|
|
13
|
+
{
|
|
14
|
+
property: "twitter:card",
|
|
15
|
+
content: "summary_large_image"
|
|
16
|
+
},
|
|
13
17
|
{
|
|
14
18
|
property: "og:image",
|
|
15
19
|
content: () => options.runtime ? `${route}/${DefaultRuntimeImageSuffix}` : MetaOgImageContentPlaceholder
|
|
16
|
-
}
|
|
20
|
+
},
|
|
21
|
+
options.alt ? {
|
|
22
|
+
property: "og:image:alt",
|
|
23
|
+
content: options.alt
|
|
24
|
+
} : {}
|
|
17
25
|
],
|
|
18
26
|
link: options.component ? [
|
|
19
27
|
{
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { defineEventHandler, getRequestHeader, setHeader } from "h3";
|
|
2
|
-
import {
|
|
1
|
+
import { createError, defineEventHandler, getRequestHeader, setHeader } from "h3";
|
|
2
|
+
import { screenshot } from "../browserUtil.mjs";
|
|
3
3
|
import { DefaultRuntimeImageSuffix, HtmlRendererRoute } from "#nuxt-og-image/constants";
|
|
4
|
+
import { createBrowser } from "#nuxt-og-image/browser";
|
|
4
5
|
export default defineEventHandler(async (e) => {
|
|
5
6
|
if (!e.path?.endsWith(DefaultRuntimeImageSuffix))
|
|
6
7
|
return;
|
|
7
8
|
const path = e.path.replace(DefaultRuntimeImageSuffix, HtmlRendererRoute);
|
|
8
9
|
const host = getRequestHeader(e, "host") || "localhost:3000";
|
|
9
10
|
const browser = await createBrowser();
|
|
11
|
+
if (!browser)
|
|
12
|
+
return createError("Could not create browser");
|
|
10
13
|
setHeader(e, "Content-Type", "image/png");
|
|
11
14
|
return await screenshot(browser, `http${host.startsWith("localhost") ? "" : "s"}://${host}/${path}`, {
|
|
12
15
|
width: 1200,
|
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.5.2",
|
|
5
5
|
"packageManager": "pnpm@7.8.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/harlan-zw",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"fast-glob": "^3.2.12",
|
|
35
35
|
"ohash": "^1.0.0",
|
|
36
36
|
"pathe": "^1.0.0",
|
|
37
|
-
"playwright-core": "^1.28.1",
|
|
38
37
|
"radix3": "^1.0.0",
|
|
39
38
|
"ufo": "^1.0.1"
|
|
40
39
|
},
|
|
@@ -47,6 +46,7 @@
|
|
|
47
46
|
"bumpp": "^8.2.1",
|
|
48
47
|
"eslint": "8.29.0",
|
|
49
48
|
"nuxt": "npm:nuxt3@latest",
|
|
49
|
+
"playwright-core": "^1.28.1",
|
|
50
50
|
"puppeteer": "^19.4.0",
|
|
51
51
|
"vitest": "^0.25.5"
|
|
52
52
|
},
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
export async function createBrowser() {
|
|
2
|
-
try {
|
|
3
|
-
const playwrightCore = await import("playwright-core");
|
|
4
|
-
process.env.AWS_LAMBDA_FUNCTION_NAME = process.env.AWS_LAMBDA_FUNCTION_NAME || "RUNTIME_HACK";
|
|
5
|
-
const awsChrome = await import(String("chrome-aws-lambda"));
|
|
6
|
-
return await playwrightCore.chromium.launch({
|
|
7
|
-
args: awsChrome.args,
|
|
8
|
-
executablePath: await awsChrome.executablePath,
|
|
9
|
-
headless: awsChrome.headless
|
|
10
|
-
});
|
|
11
|
-
} catch (e) {
|
|
12
|
-
if (!process.dev)
|
|
13
|
-
console.log("[nuxt-og-image] Skipping chrome-aws-lambda", e);
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
const playwrightCore = await import("playwright-core");
|
|
17
|
-
const { Launcher } = await import("chrome-launcher");
|
|
18
|
-
const chromePath = Launcher.getFirstInstallation();
|
|
19
|
-
return await playwrightCore.chromium.launch({
|
|
20
|
-
headless: true,
|
|
21
|
-
executablePath: chromePath
|
|
22
|
-
});
|
|
23
|
-
} catch (e) {
|
|
24
|
-
if (!process.dev)
|
|
25
|
-
console.log("[nuxt-og-image] Skipping chrome-launcher", e);
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
const playwright = await import(String("playwright"));
|
|
29
|
-
return await playwright.chromium.launch({
|
|
30
|
-
headless: true
|
|
31
|
-
});
|
|
32
|
-
} catch (e) {
|
|
33
|
-
if (!process.dev)
|
|
34
|
-
console.log("[nuxt-og-image] Playwright failed", e);
|
|
35
|
-
throw new Error(`
|
|
36
|
-
Missing chromium binary. You need either 'playwright' or 'chrome-aws-lambda'.
|
|
37
|
-
Please run 'yarn add --dev playwright' or 'npm install --save-dev playwright'
|
|
38
|
-
`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export async function screenshot(browser, url, options) {
|
|
42
|
-
const page = await browser.newPage({
|
|
43
|
-
colorScheme: options.colorScheme
|
|
44
|
-
});
|
|
45
|
-
await page.setViewportSize({
|
|
46
|
-
width: options.width,
|
|
47
|
-
height: options.height
|
|
48
|
-
});
|
|
49
|
-
await page.goto(url, {
|
|
50
|
-
timeout: 1e4,
|
|
51
|
-
waitUntil: "networkidle"
|
|
52
|
-
});
|
|
53
|
-
if (options.mask) {
|
|
54
|
-
await page.evaluate((mask) => {
|
|
55
|
-
for (const el of document.querySelectorAll(mask))
|
|
56
|
-
el.style.display = "none";
|
|
57
|
-
}, options.mask);
|
|
58
|
-
}
|
|
59
|
-
if (options.selector)
|
|
60
|
-
await page.locator(options.selector).screenshot();
|
|
61
|
-
return await page.screenshot();
|
|
62
|
-
}
|