├── .nvmrc
├── .prettierignore
├── .eslintignore
├── assets
└── og-image.png
├── test
├── snapshots
│ ├── OgImage.test.js.snap
│ └── OgImage.test.js.md
├── utils
│ ├── directoriesConfig.js
│ └── testConstructor.js
├── og-test.og.njk
├── sortObject.test.js
├── mergeOptions.test.js
└── OgImage.test.js
├── src
├── utils
│ ├── index.js
│ ├── sortObject.js
│ └── mergeOptions.js
└── OgImage.js
├── .github
├── actions
│ ├── lint
│ │ └── action.yml
│ ├── test
│ │ └── action.yml
│ ├── publint
│ │ └── action.yml
│ ├── example-build
│ │ └── action.yml
│ └── setup
│ │ └── action.yml
├── dependabot.yml
└── workflows
│ ├── review.yml
│ └── release.yml
├── .releaserc
├── .gitignore
├── example
├── example-page.njk
├── example-draft.njk
├── og-image.og.njk
└── .eleventy.js
├── .prettierrc.js
├── .eslintrc.cjs
├── LICENSE
├── index.d.ts
├── package.json
├── .eleventy.js
├── README.md
└── CHANGELOG.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 24.11.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Build results
2 | /example/_site/
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | !.eleventy.js
2 | index.d.ts
3 | .eslintrc.cjs
4 |
--------------------------------------------------------------------------------
/assets/og-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KiwiKilian/eleventy-plugin-og-image/HEAD/assets/og-image.png
--------------------------------------------------------------------------------
/test/snapshots/OgImage.test.js.snap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KiwiKilian/eleventy-plugin-og-image/HEAD/test/snapshots/OgImage.test.js.snap
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | import { mergeOptions } from './mergeOptions.js';
2 | import { sortObject } from './sortObject.js';
3 |
4 | export { mergeOptions, sortObject };
5 |
--------------------------------------------------------------------------------
/test/utils/directoriesConfig.js:
--------------------------------------------------------------------------------
1 | /** @type {import('eleventy-plugin-og-image').DirectoriesConfig} */
2 | export const directoriesConfig = { input: '.', includes: '_includes', data: '_data', output: '_site' };
3 |
--------------------------------------------------------------------------------
/.github/actions/lint/action.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | description: Run linting
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - uses: ./.github/actions/setup
8 | - run: npm run lint
9 | shell: bash
10 |
--------------------------------------------------------------------------------
/.github/actions/test/action.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 | description: Run ava tests
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - uses: ./.github/actions/setup
8 | - run: npm test
9 | shell: bash
10 |
--------------------------------------------------------------------------------
/test/og-test.og.njk:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/actions/publint/action.yml:
--------------------------------------------------------------------------------
1 | name: Publint
2 | description: Run packaging linting
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - uses: ./.github/actions/setup
8 | - run: npx publint --strict
9 | shell: bash
10 |
--------------------------------------------------------------------------------
/.github/actions/example-build/action.yml:
--------------------------------------------------------------------------------
1 | name: Example Build
2 | description: Build the example project
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - uses: ./.github/actions/setup
8 | - run: npm run example:build
9 | shell: bash
10 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "@semantic-release/commit-analyzer",
4 | "@semantic-release/release-notes-generator",
5 | "@semantic-release/changelog",
6 | "@semantic-release/npm",
7 | "@semantic-release/github",
8 | "@semantic-release/git"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build results
2 | /example/_site/
3 | /example/.cache/
4 |
5 | # IDEs
6 | /.idea/
7 | /.vscode/
8 |
9 | # Package managers
10 | /node_modules/
11 | .npm
12 |
13 | # Logs
14 | logs
15 | *.log
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 |
20 | # Artefacts
21 | .DS_Store
22 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup
2 | description: Setup a Node.js and install dependencies
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - uses: actions/setup-node@v6
8 | with:
9 | cache: npm
10 | node-version-file: '.nvmrc'
11 | - run: npm ci
12 | shell: bash
13 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: /
5 | schedule:
6 | interval: weekly
7 | assignees: [KiwiKilian]
8 |
9 | - package-ecosystem: github-actions
10 | directories: [/, ".github/actions/**"]
11 | schedule:
12 | interval: weekly
13 | assignees: [KiwiKilian]
14 |
--------------------------------------------------------------------------------
/test/utils/testConstructor.js:
--------------------------------------------------------------------------------
1 | import { directoriesConfig } from './directoriesConfig.js';
2 | import { mergeOptions } from '../../src/utils/index.js';
3 |
4 | export const testConstructor = {
5 | inputPath: './test/og-test.og.njk',
6 | data: undefined,
7 | options: mergeOptions({
8 | directoriesConfig,
9 | }),
10 | templateConfig: undefined,
11 | };
12 |
--------------------------------------------------------------------------------
/example/example-page.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Eleventy Plugin OG Image
6 |
7 |
8 | {% ogImage "./og-image.og.njk", { title: "Hello World! 👋" } %}
9 |
10 |
11 |
12 | Eleventy Plugin OG Image
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | import prettierConfig from '@kiwikilian/prettier-config' with { type: 'json' };
2 |
3 | /** @type {import('prettier').Config} */
4 | export default {
5 | ...prettierConfig,
6 | plugins: ['prettier-plugin-jsdoc', 'prettier-plugin-jinja-template'],
7 | overrides: [
8 | {
9 | files: ['**/*.njk'],
10 | options: {
11 | parser: 'jinja-template',
12 | },
13 | },
14 | ],
15 | };
16 |
--------------------------------------------------------------------------------
/src/utils/sortObject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sorts an object recursively
3 | *
4 | * @param {object} unordered
5 | * @returns {object}
6 | */
7 | export function sortObject(unordered) {
8 | const keys = Object.keys(unordered).sort();
9 |
10 | return keys.reduce((object, key) => {
11 | object[key] = typeof unordered[key] === 'object' ? sortObject(unordered[key]) : unordered[key];
12 |
13 | return object;
14 | }, {});
15 | }
16 |
--------------------------------------------------------------------------------
/example/example-draft.njk:
--------------------------------------------------------------------------------
1 | ---
2 | permalink: false
3 | ---
4 |
5 |
6 |
7 |
8 |
9 | Eleventy Plugin OG Image
10 |
11 |
12 | {% ogImage "./og-image.og.njk", { title: "This is a draft. 📝" } %}
13 |
14 |
15 |
16 | Eleventy Plugin OG Image
17 |
18 |
19 |
--------------------------------------------------------------------------------
/test/sortObject.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 | import { sortObject } from '../src/utils/index.js';
3 |
4 | test('sorts keys', (t) => {
5 | t.is(
6 | JSON.stringify({
7 | a: 'a',
8 | b: {
9 | a: 'a',
10 | b: 'b',
11 | },
12 | }),
13 | JSON.stringify(
14 | sortObject({
15 | b: {
16 | b: 'b',
17 | a: 'a',
18 | },
19 | a: 'a',
20 | }),
21 | ),
22 | );
23 | });
24 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | require('@kiwikilian/eslint-config/patch/modern-module-resolution');
2 |
3 | module.exports = {
4 | extends: ['@kiwikilian/eslint-config/profile/node'],
5 |
6 | parserOptions: {
7 | ecmaVersion: 'latest',
8 | sourceType: 'module',
9 | },
10 |
11 | rules: {
12 | 'import/extensions': [
13 | 'error',
14 | {
15 | js: 'ignorePackages',
16 | },
17 | ],
18 | 'import/no-extraneous-dependencies': [
19 | 'error',
20 | { devDependencies: ['./example/**', './example/.eleventy.js', './test/**'] },
21 | ],
22 | },
23 |
24 | overrides: [
25 | {
26 | files: '.eleventy.js',
27 | rules: {
28 | 'func-names': ['off'],
29 | 'import/no-default-export': ['off'],
30 | },
31 | },
32 | {
33 | files: './test/**',
34 | rules: {
35 | 'import/no-unresolved': ['off'],
36 | },
37 | },
38 | ],
39 | };
40 |
--------------------------------------------------------------------------------
/.github/workflows/review.yml:
--------------------------------------------------------------------------------
1 | name: Review
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | setup:
8 | name: Setup
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v6
12 | - uses: ./.github/actions/setup
13 | lint:
14 | name: Lint
15 | needs: setup
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v6
19 | - uses: ./.github/actions/lint
20 | publint:
21 | name: Publint
22 | needs: setup
23 | runs-on: ubuntu-latest
24 | steps:
25 | - uses: actions/checkout@v6
26 | - uses: ./.github/actions/publint
27 | test:
28 | name: Test
29 | needs: setup
30 | runs-on: ubuntu-latest
31 | steps:
32 | - uses: actions/checkout@v6
33 | - uses: ./.github/actions/test
34 | example-build:
35 | name: Example Build
36 | needs: setup
37 | runs-on: ubuntu-latest
38 | steps:
39 | - uses: actions/checkout@v6
40 | - uses: ./.github/actions/example-build
41 |
--------------------------------------------------------------------------------
/example/og-image.og.njk:
--------------------------------------------------------------------------------
1 |
36 |
37 |
38 |
{{ title }}
39 |
40 |
{% testShortcode %}
41 |
42 |
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Kilian Finger
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/test/mergeOptions.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 | import { mergeOptions } from '../src/utils/index.js';
3 | import { directoriesConfig } from './utils/directoriesConfig.js';
4 |
5 | test('works without pluginOptions', (t) => {
6 | const { satoriOptions, sharpOptions, ...options } = mergeOptions({ directoriesConfig });
7 |
8 | t.truthy(options);
9 | t.truthy(satoriOptions);
10 | });
11 |
12 | test('creates default options', (t) => {
13 | const { satoriOptions, sharpOptions, ...options } = mergeOptions({ directoriesConfig });
14 |
15 | t.is(options.outputFileExtension, 'png');
16 | t.is(options.inputFileGlob, '**/*.og.*');
17 |
18 | t.is(satoriOptions.width, 1200);
19 | t.is(satoriOptions.height, 630);
20 |
21 | t.falsy(sharpOptions);
22 | });
23 |
24 | test('changes previewDir and urlPath if only outputDir is set', (t) => {
25 | const CUSTOM_OUTPUT_DIR = 'custom-output';
26 |
27 | const { outputDir, previewDir, urlPath } = mergeOptions({
28 | directoriesConfig,
29 | pluginOptions: { outputDir: CUSTOM_OUTPUT_DIR },
30 | });
31 |
32 | t.is(outputDir, `_site/${CUSTOM_OUTPUT_DIR}`);
33 | t.is(previewDir, `_site/${CUSTOM_OUTPUT_DIR}/preview`);
34 | t.is(urlPath, CUSTOM_OUTPUT_DIR);
35 | });
36 |
--------------------------------------------------------------------------------
/test/snapshots/OgImage.test.js.md:
--------------------------------------------------------------------------------
1 | # Snapshot report for `test/OgImage.test.js`
2 |
3 | The actual snapshot is saved in `OgImage.test.js.snap`.
4 |
5 | Generated by [AVA](https://avajs.dev).
6 |
7 | ## html returns html string
8 |
9 | > Snapshot 1
10 |
11 | `␊
19 | ␊
20 | ␊
21 | `
22 |
23 | ## svg returns svg string
24 |
25 | > Snapshot 1
26 |
27 | ''
28 |
--------------------------------------------------------------------------------
/src/utils/mergeOptions.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 |
3 | /**
4 | * Merges the plugin options with defaults
5 | *
6 | * @param {DirectoriesConfig} [directoriesConfig]
7 | * @param {EleventyPluginOgImageOptions} [pluginOptions]
8 | * @returns {EleventyPluginOgImageMergedOptions}
9 | */
10 | export function mergeOptions({ directoriesConfig, pluginOptions }) {
11 | const { outputDir, previewDir, urlPath, OgImage, satoriOptions, ...options } = pluginOptions || {};
12 |
13 | const eleventyOutput = directoriesConfig ? directoriesConfig.output : '';
14 | const joinedOutputDir = path.join(eleventyOutput, outputDir || 'og-images');
15 |
16 | return {
17 | inputFileGlob: '**/*.og.*',
18 | hashLength: 8,
19 | outputFileExtension: 'png',
20 | outputDir: joinedOutputDir,
21 | previewMode: 'auto',
22 | previewDir: path.join(...(previewDir ? [eleventyOutput, previewDir] : [joinedOutputDir, 'preview'])),
23 | urlPath: urlPath || outputDir || 'og-images',
24 |
25 | /** @param {OgImage} ogImage */
26 | outputFileSlug: (ogImage) => ogImage.hash(),
27 |
28 | /** @param {OgImage} ogImage */
29 | shortcodeOutput: async (ogImage) => ``,
30 |
31 | ...options,
32 |
33 | satoriOptions: {
34 | width: 1200,
35 | height: 630,
36 | fonts: [],
37 | ...satoriOptions,
38 | },
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - next
8 | - next-major
9 | - alpha
10 | - beta
11 |
12 | jobs:
13 | setup:
14 | name: Setup
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v6
18 | - uses: ./.github/actions/setup
19 | lint:
20 | name: Lint
21 | needs: setup
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v6
25 | - uses: ./.github/actions/lint
26 | test:
27 | name: Test
28 | needs: setup
29 | runs-on: ubuntu-latest
30 | steps:
31 | - uses: actions/checkout@v6
32 | - uses: ./.github/actions/test
33 | example-build:
34 | name: Example Build
35 | needs: setup
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@v6
39 | - uses: ./.github/actions/example-build
40 | release:
41 | name: Release
42 | needs: [lint, test, example-build]
43 | runs-on: ubuntu-latest
44 | permissions:
45 | contents: write
46 | issues: write
47 | pull-requests: write
48 | id-token: write
49 | steps:
50 | - uses: actions/checkout@v6
51 | - uses: ./.github/actions/setup
52 | - name: Audit Signatures
53 | run: npm audit signatures
54 | - name: Release
55 | env:
56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57 | run: npx semantic-release
58 |
--------------------------------------------------------------------------------
/example/.eleventy.js:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'node:fs';
2 | import module from 'node:module';
3 | import twemoji from 'twemoji';
4 | import EleventyPluginOgImage from '../.eleventy.js';
5 |
6 | const require = module.createRequire(import.meta.url);
7 |
8 | /** @param {import('@11ty/eleventy/src/UserConfig').default} eleventyConfig */
9 | export default async function (eleventyConfig) {
10 | eleventyConfig.addShortcode('testShortcode', () => 'Eleventy Plugin OG Image');
11 |
12 | /** @type {import('eleventy-plugin-og-image').EleventyPluginOgImageOptions} */
13 | const eleventyPluginOgImageOptions = {
14 | satoriOptions: {
15 | fonts: [
16 | {
17 | name: 'Inter',
18 | data: await fs.readFile(require.resolve('@fontsource/inter/files/inter-latin-700-normal.woff')),
19 | weight: 700,
20 | style: 'normal',
21 | },
22 | ],
23 | loadAdditionalAsset: async (languageCode, segment) => {
24 | if (languageCode === 'emoji') {
25 | const emojiUrl = `https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/${twemoji.convert.toCodePoint(
26 | segment,
27 | )}.svg`;
28 | const emojiSvg = await (await fetch(emojiUrl)).text();
29 |
30 | return `data:image/svg+xml;base64,${Buffer.from(emojiSvg).toString('base64')}`;
31 | }
32 |
33 | return segment;
34 | },
35 | },
36 | };
37 |
38 | eleventyConfig.addPlugin(EleventyPluginOgImage, eleventyPluginOgImageOptions);
39 | }
40 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { SatoriOptions } from 'satori';
2 | import { FormatEnum, Sharp } from 'sharp';
3 |
4 | export interface OgImage {
5 | inputPath: string;
6 |
7 | data: Record;
8 |
9 | options: EleventyPluginOgImageMergedOptions;
10 |
11 | templateConfig: typeof import('@11ty/eleventy/src/TemplateConfig').default;
12 |
13 | results: {
14 | html?: string;
15 | svg?: string;
16 | pngBuffer?: Buffer;
17 | };
18 |
19 | html(): Promise;
20 |
21 | svg(): Promise;
22 |
23 | pngBuffer(): Promise;
24 |
25 | render(): Promise;
26 |
27 | hash(): Promise;
28 |
29 | outputFileSlug(): Promise;
30 |
31 | outputFileName(): Promise;
32 |
33 | outputFilePath(): Promise;
34 |
35 | outputUrl(): Promise;
36 |
37 | cacheFilePath(): Promise;
38 |
39 | shortcodeOutput(): Promise;
40 |
41 | previewFilePath(): string;
42 |
43 | previewHtml(): Promise;
44 | }
45 |
46 | type DirectoriesConfig = {
47 | input: string;
48 | includes: string;
49 | data: string;
50 | layouts?: string;
51 | output: string;
52 | };
53 |
54 | type SharpFormatOptions = Parameters[1];
55 |
56 | type EleventyPluginOgImageOptions = {
57 | inputFileGlob?: string;
58 | hashLength?: number;
59 | outputFileExtension?: keyof FormatEnum;
60 | outputDir?: string;
61 | previewMode?: 'auto' | boolean;
62 | previewDir?: string;
63 | urlPath?: string;
64 |
65 | outputFileSlug?(ogImage: OgImage): Promise;
66 | shortcodeOutput?(ogImage: OgImage): Promise;
67 |
68 | OgImage?: OgImage;
69 |
70 | satoriOptions?: Partial;
71 | sharpOptions?: SharpFormatOptions;
72 | };
73 |
74 | type EleventyPluginOgImageMergedOptions = Omit<
75 | Required,
76 | 'OgImage' | 'satoriOptions' | 'sharpOptions'
77 | > &
78 | Pick & {
79 | satoriOptions: SatoriOptions & { width: number; height: number };
80 | };
81 |
82 | export { EleventyPluginOgImageOptions, EleventyPluginOgImageMergedOptions, DirectoriesConfig };
83 |
84 | export default function (
85 | eleventyConfig: typeof import('@11ty/eleventy/src/UserConfig').default,
86 | pluginOptions?: EleventyPluginOgImageOptions,
87 | ): Promise;
88 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eleventy-plugin-og-image",
3 | "version": "4.2.1",
4 | "description": "A plugin to create Open Graph Images from JSX for Eleventy.",
5 | "author": {
6 | "name": "Kilian Finger",
7 | "email": "npm@kilianfinger.com",
8 | "url": "https://www.kilianfinger.com/"
9 | },
10 | "license": "MIT",
11 | "keywords": [
12 | "11ty",
13 | "eleventy",
14 | "eleventy-plugin",
15 | "og:image"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/KiwiKilian/eleventy-plugin-og-image.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/KiwiKilian/eleventy-plugin-og-image/issues"
23 | },
24 | "homepage": "https://github.com/KiwiKilian/eleventy-plugin-og-image#readme",
25 | "main": "./.eleventy.js",
26 | "type": "module",
27 | "exports": {
28 | ".": {
29 | "types": "./index.d.ts",
30 | "default": "./.eleventy.js"
31 | },
32 | "./package.json": "./package.json",
33 | "./og-image": "./src/OgImage.js",
34 | "./utils": "./src/utils/index.js"
35 | },
36 | "types": "./index.d.ts",
37 | "files": [
38 | ".eleventy.js",
39 | "index.d.ts",
40 | "src"
41 | ],
42 | "publishConfig": {
43 | "provenance": true
44 | },
45 | "scripts": {
46 | "semantic-release": "semantic-release",
47 | "test": "ava test/**/*.test.js",
48 | "lint": "eslint .",
49 | "prettier": "prettier --write .",
50 | "example": "npx @11ty/eleventy --config=example/.eleventy.js --input=example --output=example/_site",
51 | "example:start": "npm run example -- --serve",
52 | "example:build": "npm run example",
53 | "example:clean": "rimraf ./example/_site"
54 | },
55 | "engines": {
56 | "node": ">=18"
57 | },
58 | "11ty": {
59 | "compatibility": ">=3.0.0"
60 | },
61 | "peerDependencies": {
62 | "@11ty/eleventy": ">=3.0.0"
63 | },
64 | "dependencies": {
65 | "@11ty/eleventy-utils": "^2.0.1",
66 | "@resvg/resvg-js": "^2.6.2",
67 | "satori": "^0.18.3",
68 | "satori-html": "^0.3.2",
69 | "sharp": "^0.34.1"
70 | },
71 | "devDependencies": {
72 | "@11ty/eleventy": "3.1.2",
73 | "@fontsource/inter": "5.2.8",
74 | "@kiwikilian/eslint-config": "1.1.0",
75 | "@kiwikilian/prettier-config": "1.0.1",
76 | "@semantic-release/changelog": "6.0.3",
77 | "@semantic-release/git": "10.0.1",
78 | "ava": "6.4.1",
79 | "eslint": "8.57.1",
80 | "prettier": "3.6.2",
81 | "prettier-plugin-jinja-template": "2.1.0",
82 | "prettier-plugin-jsdoc": "1.7.0",
83 | "semantic-release": "25.0.2",
84 | "twemoji": "14.0.2"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/test/OgImage.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 | import sharp from 'sharp';
3 | import { OgImage } from '../src/OgImage.js';
4 | import { testConstructor } from './utils/testConstructor.js';
5 |
6 | test('html returns html string', async (t) => {
7 | const ogImage = new OgImage(testConstructor);
8 |
9 | const html = await ogImage.html();
10 |
11 | t.snapshot(html);
12 |
13 | t.is(typeof html, 'string');
14 | t.regex(html, /<\/div>\n$/);
15 | });
16 |
17 | test('svg returns svg string', async (t) => {
18 | const ogImage = new OgImage(testConstructor);
19 |
20 | const svg = await ogImage.svg();
21 |
22 | t.snapshot(svg);
23 |
24 | t.is(typeof svg, 'string');
25 | t.regex(svg, /^