nuxt-og-image 0.3.4 → 0.4.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 CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
  <p align="center">
13
- Generate social share images for your pre-rendered Nuxt v3 app.
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</b> <br>
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,19 +29,33 @@ Generate social share images for your pre-rendered Nuxt v3 app.
29
29
 
30
30
  ## Features
31
31
 
32
- - 🔄 Configure using route rules
33
- - 📸 Generates site screenshots
34
- - 🎨 OR build your own template with Vue (powered by Nuxt islands)
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
-
41
42
  # Using yarn
42
43
  yarn add --dev nuxt-og-image
43
44
  ```
44
45
 
46
+ ### Chromium Dependency
47
+
48
+ By default, this module does not install chromium binaries for you, instead relying on locally
49
+ installed chrome.
50
+
51
+ If you want to use this module in a CI or SSR environment, you will need the binaries.
52
+
53
+ You can either use `puppeteer` or `chrome-aws-lambda`.
54
+
55
+ ```bash
56
+ npm install --save-dev puppeteer # or chrome-aws-lambda
57
+ ```
58
+
45
59
  ## Setup
46
60
 
47
61
  _nuxt.config.ts_
@@ -54,7 +68,29 @@ export default defineNuxtConfig({
54
68
  })
55
69
  ```
56
70
 
57
- To have routes included for og:image creation automatically, they need to be pre-rendered by Nitro.
71
+ ### Add your host name
72
+
73
+ The `og:image` meta tag requires the full URL, so you must provide your site host.
74
+
75
+ _nuxt.config.ts_
76
+
77
+ ```ts
78
+ export default defineNuxtConfig({
79
+ // Recommended
80
+ runtimeConfig: {
81
+ siteUrl: 'https://example.com',
82
+ },
83
+ // OR
84
+ ogImage: {
85
+ host: 'https://example.com',
86
+ },
87
+ })
88
+ ```
89
+
90
+ ### Pre-render routes
91
+
92
+ While the module is in early access, you should ensure that you pre-render any pages you want to
93
+ generate images for.
58
94
 
59
95
  ```ts
60
96
  export default defineNuxtConfig({
@@ -71,38 +107,73 @@ export default defineNuxtConfig({
71
107
  })
72
108
  ```
73
109
 
74
- ## Default Behaviour
110
+ ### Recommended: Enable Nuxt Islands
75
111
 
76
- By default, all pre-rendered routes will generate an og:image of a screenshot of the page.
112
+ To be able to preview the image in development and generate template images, you'll need
113
+ to enable Nuxt Islands.
77
114
 
78
- ## Using a template
115
+ 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
116
 
80
- You can create your own template to use for generating og:image. This is done with
81
- Nuxt islands.
117
+ Once that's done, you can enable the flag for islands.
82
118
 
83
- ### Requirements
119
+ _nuxt.config.ts_
84
120
 
85
- To use this feature you will need to opt in to the edge channel, see [the instructions](https://nuxt.com/docs/guide/going-further/edge-channel#edge-release-channel).
121
+ ```ts
122
+ export default defineNuxtConfig({
123
+ experimental: {
124
+ componentIslands: true
125
+ },
126
+ })
127
+ ```
86
128
 
87
- The `componentIslands` experimental feature is required for this module to work and will is enabled for you.
129
+ ## Generating Screenshots
88
130
 
89
- ### Setup
131
+ ```vue
132
+ <script lang="ts" setup>
133
+ defineOgImageScreenshot()
134
+ </script>
135
+ <template>
136
+ <div>
137
+ <!-- Your page / app.vue / layout -->
138
+ </div>
139
+ </template>
140
+ ```
141
+
142
+ ## Generating Template Images
143
+
144
+ The template image generator is powered by Nuxt Islands. This means that you can use any Vue
145
+ component you want to generate your images.
146
+
147
+ ```vue
148
+ <script lang="ts" setup>
149
+ defineOgImage({
150
+ component: 'OgImage', // Nuxt Island component
151
+ // pass in any custom props
152
+ myCustomTitle: 'My Title'
153
+ })
154
+ </script>
155
+ <template>
156
+ <div>
157
+ <!-- Your page / app.vue / layout -->
158
+ </div>
159
+ </template>
160
+ ```
90
161
 
91
- #### Create the Island component
162
+ ### Creating your own template
92
163
 
93
- Firstly, you're going to create the Vue component to be used to render the og:image.
164
+ Create a new component with `.island.vue` as the suffix, such as `components/Banner.island.vue`.
94
165
 
95
- Create the file at `components/islands/OgImageDefault.vue`.
166
+ Use the below template to test it works, then modify it how you like.
96
167
 
97
168
  ```vue
98
169
  <script setup lang="ts">
99
170
  const props = defineProps({
100
- // payload props
171
+ // these will always be provided
101
172
  path: String,
102
173
  title: String,
103
174
  description: String,
104
- // add your own props here
105
- myCustomProp: String
175
+ // anything custom comes here
176
+ backgroundImage: String
106
177
  })
107
178
  </script>
108
179
 
@@ -125,143 +196,50 @@ const props = defineProps({
125
196
  align-items: center;
126
197
  justify-content: center;
127
198
  color: white;
128
- font-weight: bold;
129
- font-family: sans-serif;
130
199
  background: linear-gradient(to bottom, #30e8bf, #ff8235);
131
200
  }
132
201
 
133
202
  h1 {
134
203
  font-size: 4rem;
135
- margin: 0;
136
204
  }
137
205
  </style>
138
206
  ```
139
207
 
140
- #### Configure the payload
141
-
142
- Within a page
208
+ Make sure you reference this component when using `defineOgImage` and any props to pass.
143
209
 
144
- ### Set host
145
-
146
- You'll need to provide the host of your site in order to generate the sitemap.xml.
147
-
148
- ```ts
149
- export default defineNuxtConfig({
150
- // Recommended
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
- }
210
+ ```vue
211
+ <script lang="ts" setup>
212
+ defineOgImage({
213
+ component: 'Banner',
214
+ backgroundImage: 'https://example.com/my-background-image.jpg',
176
215
  })
216
+ </script>
177
217
  ```
178
218
 
179
- The following options are available for each route rule:
219
+ ## Previewing Images
180
220
 
181
- - `index`: Whether to include the route in the sitemap.xml. Defaults to `true`.
182
- - `sitemap.changefreq`: The change frequency of the route.
183
- - `sitemap.priority`: The priority of the route.
221
+ Once you have defined the og:image using the composable, you can preview the image by visiting
222
+ the following URLs:
223
+ - `/your-path/__og-image` Renders the HTML output
224
+ - `/your-path/og-image.png` Renders the og:image
184
225
 
185
226
  ## Module Config
186
227
 
187
- If you need further control over the sitemap URLs, you can provide config on the `sitemap` key.
188
-
189
228
  ### `host`
190
229
 
191
230
  - Type: `string`
192
231
  - Default: `undefined`
193
232
  - Required: `true`
194
233
 
195
- The host of your site. This is required to generate the sitemap.xml.
234
+ The host of your site. This is required to generate the absolute path of the og:image.
196
235
 
197
- ### `trailingSlash`
236
+ ### `runtimeImages`
198
237
 
199
238
  - Type: `boolean`
200
- - Default: `false`
201
-
202
- Whether to add a trailing slash to the URLs in the sitemap.xml.
203
-
204
- ### `enabled`
205
-
206
- - Type: `boolean`
207
- - Default: `true`
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.
239
+ - Default: `process.dev`
217
240
 
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).
246
-
247
- ## Examples
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
- ```
241
+ Allows you to generate images at runtime in production. This uses a headless browser to generate images
242
+ and may have deployment issues.
265
243
 
266
244
  ## Sponsors
267
245
 
@@ -271,6 +249,11 @@ export default defineNuxtConfig({
271
249
  </a>
272
250
  </p>
273
251
 
252
+ ## Credits
253
+
254
+ - Pooya Parsa [Kachick](https://github.com/unjs/kachik)
255
+ - Nuxt Team
256
+
274
257
 
275
258
  ## License
276
259
 
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 { HtmlRendererRoute, LinkPrerenderId, MetaOgImageContentPlaceholder, ModuleOptions, PayloadScriptId, _default as default };
45
+ export { ModuleOptions, _default as default };
package/dist/module.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "bridge": false
6
6
  },
7
7
  "configKey": "ogImage",
8
- "version": "0.3.4"
8
+ "version": "0.4.2"
9
9
  }
package/dist/module.mjs CHANGED
@@ -5,19 +5,43 @@ 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
- if (!process.env.AWS_LAMBDA_FUNCTION_NAME)
13
- process.env.AWS_LAMBDA_FUNCTION_NAME = "test";
14
- const playwright = await import('playwright-core');
15
- const awsChrome = await import('chrome-aws-lambda');
16
- return await playwright.chromium.launch({
17
- args: awsChrome.args,
18
- executablePath: await awsChrome.executablePath,
19
- headless: awsChrome.headless
20
- });
13
+ try {
14
+ const playwrightCore = await import('playwright-core');
15
+ process.env.AWS_LAMBDA_FUNCTION_NAME = process.env.AWS_LAMBDA_FUNCTION_NAME || "RUNTIME_HACK";
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
+ }
24
+ try {
25
+ const playwrightCore = await import('playwright-core');
26
+ const { Launcher } = await import('chrome-launcher');
27
+ const chromePath = Launcher.getFirstInstallation();
28
+ return await playwrightCore.chromium.launch({
29
+ headless: true,
30
+ executablePath: chromePath
31
+ });
32
+ } catch (e) {
33
+ }
34
+ try {
35
+ const playwright = await import(String("playwright"));
36
+ return await playwright.chromium.launch({
37
+ headless: true
38
+ });
39
+ } catch (e) {
40
+ throw new Error(`
41
+ Missing chromium binary. You need either 'playwright' or 'chrome-aws-lambda'.
42
+ Please run 'yarn add --dev playwright' or 'npm install --save-dev playwright'
43
+ `);
44
+ }
21
45
  }
22
46
  async function screenshot(browser, url, options) {
23
47
  const page = await browser.newPage({
@@ -46,6 +70,15 @@ const HtmlRendererRoute = "__og_image";
46
70
  const PayloadScriptId = "nuxt-og-image-payload";
47
71
  const MetaOgImageContentPlaceholder = "__NUXT_OG_IMAGE_PLACEHOLDER__";
48
72
  const LinkPrerenderId = "nuxt-og-image-screenshot-path";
73
+ const DefaultRuntimeImageSuffix = "og-image.png";
74
+ const Constants = {
75
+ HtmlRendererRoute,
76
+ PayloadScriptId,
77
+ MetaOgImageContentPlaceholder,
78
+ LinkPrerenderId,
79
+ DefaultRuntimeImageSuffix
80
+ };
81
+
49
82
  const module = defineNuxtModule({
50
83
  meta: {
51
84
  name: "nuxt-og-image",
@@ -106,19 +139,32 @@ declare module 'nitropack' {
106
139
  filePath: resolve("./runtime/components/OgImage.vue"),
107
140
  island: true
108
141
  });
142
+ const runtimeDir = resolve("./runtime");
143
+ nuxt.options.build.transpile.push(runtimeDir);
144
+ const constScript = Object.entries(Constants).map(([k, v]) => `export const ${k} = '${v}'`).join("\n");
145
+ nuxt.options.alias["#nuxt-og-image/constants"] = addTemplate({
146
+ filename: "nuxt-og-image-constants.mjs",
147
+ getContents: () => constScript
148
+ }).dst;
149
+ nuxt.hooks.hook("nitro:config", (nitroConfig) => {
150
+ nitroConfig.externals = defu(nitroConfig.externals || {}, {
151
+ inline: [runtimeDir]
152
+ });
153
+ nitroConfig.virtual["#nuxt-og-image/constants"] = constScript;
154
+ });
109
155
  nuxt.hooks.hook("nitro:init", async (nitro) => {
110
156
  let entries = [];
111
157
  const _routeRulesMatcher = toRouteMatcher(
112
158
  createRouter({ routes: nitro.options.routeRules })
113
159
  );
114
- const outputPath = `${nitro.options.output.dir}/public/${config.outputDir}`;
160
+ const outputPath = join(nitro.options.output.publicDir, config.outputDir);
115
161
  nitro.hooks.hook("prerender:generate", async (ctx) => {
116
162
  if (ctx.route.includes(".") || ctx.route.endsWith(HtmlRendererRoute))
117
163
  return;
118
164
  let html = ctx.contents;
119
165
  if (!html)
120
166
  return;
121
- if (!html.includes('id="nuxt-og-image-payload"'))
167
+ if (!html.includes(`id="${PayloadScriptId}"`))
122
168
  return;
123
169
  const routeRules = defu({}, ..._routeRulesMatcher.matchAll(ctx.route).reverse());
124
170
  if (routeRules.ogImage === false)
@@ -129,8 +175,8 @@ declare module 'nitropack' {
129
175
  const entry = {
130
176
  fileName,
131
177
  absoluteUrl,
132
- outputPath: `${nitro.options.output.dir}/public/${config.outputDir}/${fileName}`,
133
- linkingHtml: ctx.fileName,
178
+ outputPath: joinURL(nitro.options.output.publicDir, config.outputDir, fileName),
179
+ linkingHtml: joinURL(nitro.options.output.publicDir, ctx.fileName),
134
180
  route: ctx.route,
135
181
  hasPayload: screenshotPath,
136
182
  routeRules: routeRules.ogImage || "",
@@ -149,7 +195,8 @@ declare module 'nitropack' {
149
195
  await mkdir(outputPath, { recursive: true });
150
196
  } catch (e) {
151
197
  }
152
- const previewProcess = execa("npx", ["serve", `${nitro.options.output.dir}/public`]);
198
+ const previewProcess = execa("npx", ["serve", nitro.options.output.publicDir]);
199
+ let browser = null;
153
200
  try {
154
201
  previewProcess.stderr?.pipe(process.stderr);
155
202
  const host = (await new Promise((resolve2) => {
@@ -159,39 +206,42 @@ declare module 'nitropack' {
159
206
  }
160
207
  });
161
208
  })).trim();
162
- const browser = await createBrowser();
163
- nitro.logger.info(`Generating ${entries.length} og:images...`);
164
- try {
209
+ browser = await createBrowser();
210
+ if (browser) {
211
+ nitro.logger.info(`Generating ${entries.length} og:images...`);
165
212
  for (const k in entries) {
166
213
  const entry = entries[k];
167
214
  const start = Date.now();
168
- const imgBuffer = await screenshot(browser, `${host}${entry.screenshotPath}`, config);
169
- await writeFile(entry.outputPath, imgBuffer);
215
+ let hasError = false;
216
+ try {
217
+ const imgBuffer = await screenshot(browser, `${host}${entry.screenshotPath}`, config);
218
+ await writeFile(entry.outputPath, imgBuffer);
219
+ } catch (e) {
220
+ hasError = true;
221
+ console.error(e);
222
+ }
170
223
  const generateTimeMS = Date.now() - start;
171
- nitro.logger.log(chalk.gray(
224
+ nitro.logger.log(chalk[hasError ? "red" : "gray"](
172
225
  ` ${Number(k) === entries.length - 1 ? "\u2514\u2500" : "\u251C\u2500"} /${config.outputDir}/${entry.fileName} (${generateTimeMS}ms) ${Math.round(Number(k) / (entries.length - 1) * 100)}%`
173
226
  ));
174
227
  }
175
- } catch (e) {
176
- console.error(e);
177
- } finally {
178
- await browser.close();
179
228
  }
180
229
  } catch (e) {
181
230
  console.error(e);
182
231
  } finally {
232
+ await browser?.close();
183
233
  previewProcess.kill();
184
234
  }
185
- for (const entry of entries.filter((e) => e.hasPayload)) {
186
- const html = await readFile(`${nitro.options.output.dir}/public${entry.linkingHtml}`, "utf-8");
235
+ for (const entry of entries) {
236
+ const html = await readFile(entry.linkingHtml, "utf-8");
187
237
  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
238
  if (html !== newHtml) {
189
- await writeFile(`${nitro.options.output.dir}/public${entry.linkingHtml}`, newHtml, { encoding: "utf-8" });
239
+ await writeFile(entry.linkingHtml, newHtml, { encoding: "utf-8" });
190
240
  }
191
241
  }
192
- const ogImageFolders = await fg([`**/${HtmlRendererRoute}`], { cwd: nitro.options.output.dir, onlyDirectories: true });
242
+ const ogImageFolders = await fg([`**/${HtmlRendererRoute}`], { cwd: nitro.options.output.publicDir, onlyDirectories: true });
193
243
  for (const ogImageFolder of ogImageFolders)
194
- await rm(`${nitro.options.output.dir}/${ogImageFolder}`, { recursive: true, force: true });
244
+ await rm(join(nitro.options.output.publicDir, ogImageFolder), { recursive: true, force: true });
195
245
  entries = [];
196
246
  };
197
247
  nitro.hooks.hook("rollup:before", async () => {
@@ -204,4 +254,4 @@ declare module 'nitropack' {
204
254
  }
205
255
  });
206
256
 
207
- export { HtmlRendererRoute, LinkPrerenderId, MetaOgImageContentPlaceholder, PayloadScriptId, module as default };
257
+ 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<any>;
5
+ export declare function screenshot(browser: Browser, url: string, options: ScreenshotOptions): Promise<Buffer>;
@@ -0,0 +1,56 @@
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
+ }
13
+ try {
14
+ const playwrightCore = await import("playwright-core");
15
+ const { Launcher } = await import("chrome-launcher");
16
+ const chromePath = Launcher.getFirstInstallation();
17
+ return await playwrightCore.chromium.launch({
18
+ headless: true,
19
+ executablePath: chromePath
20
+ });
21
+ } catch (e) {
22
+ }
23
+ try {
24
+ const playwright = await import(String("playwright"));
25
+ return await playwright.chromium.launch({
26
+ headless: true
27
+ });
28
+ } catch (e) {
29
+ throw new Error(`
30
+ Missing chromium binary. You need either 'playwright' or 'chrome-aws-lambda'.
31
+ Please run 'yarn add --dev playwright' or 'npm install --save-dev playwright'
32
+ `);
33
+ }
34
+ }
35
+ export async function screenshot(browser, url, options) {
36
+ const page = await browser.newPage({
37
+ colorScheme: options.colorScheme
38
+ });
39
+ await page.setViewportSize({
40
+ width: options.width,
41
+ height: options.height
42
+ });
43
+ await page.goto(url, {
44
+ timeout: 1e4,
45
+ waitUntil: "networkidle"
46
+ });
47
+ if (options.mask) {
48
+ await page.evaluate((mask) => {
49
+ for (const el of document.querySelectorAll(mask))
50
+ el.style.display = "none";
51
+ }, options.mask);
52
+ }
53
+ if (options.selector)
54
+ await page.locator(options.selector).screenshot();
55
+ return await page.screenshot();
56
+ }
@@ -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
- export const HtmlRendererRoute = "__og_image";
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}/${RuntimeImageSuffix}` : MetaOgImageContentPlaceholder
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 { withoutTrailingSlash, 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
- export const HtmlRendererRoute = "__og_image";
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) {
@@ -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,43 +1,10 @@
1
1
  import { defineEventHandler, getRequestHeader, setHeader } from "h3";
2
- export const HtmlRendererRoute = "__og_image";
3
- export const RuntimeImageSuffix = "og-image.png";
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 { DefaultRuntimeImageSuffix, HtmlRendererRoute } from "#nuxt-og-image/constants";
37
4
  export default defineEventHandler(async (e) => {
38
- if (!e.path?.endsWith(RuntimeImageSuffix))
5
+ if (!e.path?.endsWith(DefaultRuntimeImageSuffix))
39
6
  return;
40
- const path = e.path.replace(RuntimeImageSuffix, HtmlRendererRoute);
7
+ const path = e.path.replace(DefaultRuntimeImageSuffix, HtmlRendererRoute);
41
8
  const host = getRequestHeader(e, "host") || "localhost:3000";
42
9
  const browser = await createBrowser();
43
10
  setHeader(e, "Content-Type", "image/png");
package/dist/types.d.ts CHANGED
@@ -7,4 +7,4 @@ declare module '@nuxt/schema' {
7
7
  }
8
8
 
9
9
 
10
- export { HtmlRendererRoute, LinkPrerenderId, MetaOgImageContentPlaceholder, ModuleOptions, PayloadScriptId, default } from './module'
10
+ export { ModuleOptions, default } from './module'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-og-image",
3
3
  "type": "module",
4
- "version": "0.3.4",
4
+ "version": "0.4.2",
5
5
  "packageManager": "pnpm@7.8.0",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/harlan-zw",
@@ -28,21 +28,16 @@
28
28
  "dependencies": {
29
29
  "@nuxt/kit": "3.0.0",
30
30
  "chalk": "^5.2.0",
31
- "chrome-aws-lambda": "^10.1.0",
31
+ "chrome-launcher": "^0.15.1",
32
32
  "defu": "^6.1.1",
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
  },