Satori v0.0.43

This commit is contained in:
Shivam Meena
2022-10-26 11:46:50 +05:30
parent fc1f39d417
commit 75f010156e
4 changed files with 69 additions and 34 deletions

View File

@@ -49,7 +49,7 @@ export const GET: RequestHandler = async () => {
``` ```
Then run `pnpm dev` and access localhost:5173/og, the React element will be rendered and responded as a PNG from that endpoint: Then run `pnpm dev` and access localhost:5173/og, the api/route endpoint be rendered and responded as a PNG from that api/endpoint:
![Rendered OG image](static/demo.png) ![Rendered OG image](static/demo.png)
@@ -62,32 +62,53 @@ Read more about the API, supported features and check out the examples on Satori
## API Reference ## API Reference
The package exposes an `ImageResponse` constructor, with the following options available: The package exposes an `ImageResponse` and `componentToImageResponse` constructors, with the following options available:
```typescript ```typescript
import { ImageResponse } from '@ethercorps/sveltekit-og' import {ImageResponse, componentToImageResponse} from '@ethercorps/sveltekit-og'
import {SvelteComponent} from "svelte";
// ... // ...
new ImageResponse( new ImageResponse(
element: string, element : string,
options: { options : {
width?: number = 1200 width ? : number = 1200
height?: number = 630 height ? : number = 630
fonts?: { fonts ? : {
name: string, name: string,
data: ArrayBuffer, data: ArrayBuffer,
weight: number, weight: number,
style: 'normal' | 'italic' style: 'normal' | 'italic'
}[] }[]
debug?: boolean = false debug ? : boolean = false
graphemeImages?: Record<string, string>; graphemeImages ? : Record<string, string>;
loadAdditionalAsset?: (languageCode: string, segment: string) => Promise<SatoriOptions["fonts"] | string | undefined>; loadAdditionalAsset ? : (languageCode: string, segment: string) => Promise<SatoriOptions["fonts"] | string | undefined>;
// Options that will be passed to the HTTP response // Options that will be passed to the HTTP response
status?: number = 200 status ? : number = 200
statusText?: string statusText ? : string
headers?: Record<string, string> headers ? : Record<string, string>
}, })
)
new componentToImageResponse(
component : typeof SvelteComponent,
props : {}, // All export let example inside prop dictionary
options : {
width ? : number = 1200
height ? : number = 630
fonts ? : {
name: string,
data: ArrayBuffer,
weight: number,
style: 'normal' | 'italic'
}[]
debug ? : boolean = false
graphemeImages ? : Record<string, string>;
loadAdditionalAsset ? : (languageCode: string, segment: string) => Promise<SatoriOptions["fonts"] | string | undefined>;
// Options that will be passed to the HTTP response
status ? : number = 200
statusText ? : string
headers ? : Record<string, string>
})
``` ```
When running in production, these headers will be included by `@ethercorps/sveltekit-og`: When running in production, these headers will be included by `@ethercorps/sveltekit-og`:
@@ -111,6 +132,7 @@ By default, `@ethercorps/sveltekit-og` only has the 'Noto Sans' font included. I
This project will not be possible without the following projects: This project will not be possible without the following projects:
- [Satori & @vercel/og](https://github.com/vercel/satori) - [Satori & @vercel/og](https://github.com/vercel/satori)
- [Satori-Html](https://github.com/natemoo-re/satori-html)
- [Noto by Google Fonts](https://fonts.google.com/noto) - [Noto by Google Fonts](https://fonts.google.com/noto)
- [Resvg.js](https://github.com/yisibl/resvg-js) - [Resvg.js](https://github.com/yisibl/resvg-js)

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ethercorps/sveltekit-og", "name": "@ethercorps/sveltekit-og",
"version": "0.1.1", "version": "0.1.2",
"private": false, "private": false,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
@@ -35,8 +35,9 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@resvg/resvg-wasm": "^2.1.0", "@resvg/resvg-wasm": "^2.1.0",
"satori": "^0.0.42", "satori": "^0.0.43",
"satori-html": "^0.2.0" "satori-html": "^0.2.0",
"yoga-wasm-web": "0.1.2"
}, },
"keywords": [ "keywords": [
"open graph image", "open graph image",

12
pnpm-lock.yaml generated
View File

@@ -21,16 +21,18 @@ specifiers:
tslib: ^2.4.0 tslib: ^2.4.0
typescript: ^4.8.4 typescript: ^4.8.4
vite: ^3.1.8 vite: ^3.1.8
yoga-wasm-web: 0.1.2
dependencies: dependencies:
'@resvg/resvg-wasm': 2.1.0 '@resvg/resvg-wasm': 2.1.0
satori: 0.0.42 satori: 0.0.42
satori-html: 0.2.0 satori-html: 0.2.0
yoga-wasm-web: 0.1.2
devDependencies: devDependencies:
'@playwright/test': 1.27.1 '@playwright/test': 1.27.1
'@sveltejs/adapter-auto': 1.0.0-next.84 '@sveltejs/adapter-auto': 1.0.0-next.84
'@sveltejs/kit': 1.0.0-next.520_svelte@3.52.0+vite@3.1.8 '@sveltejs/kit': 1.0.0-next.522_svelte@3.52.0+vite@3.1.8
'@sveltejs/package': 1.0.0-next.1_besnmoibwkhwtentvwuriss7pa '@sveltejs/package': 1.0.0-next.1_besnmoibwkhwtentvwuriss7pa
'@typescript-eslint/eslint-plugin': 5.40.1_ukgdydjtebaxmxfqp5v5ulh64y '@typescript-eslint/eslint-plugin': 5.40.1_ukgdydjtebaxmxfqp5v5ulh64y
'@typescript-eslint/parser': 5.40.1_z4bbprzjrhnsfa24uvmcbu7f5q '@typescript-eslint/parser': 5.40.1_z4bbprzjrhnsfa24uvmcbu7f5q
@@ -256,8 +258,8 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@sveltejs/kit/1.0.0-next.520_svelte@3.52.0+vite@3.1.8: /@sveltejs/kit/1.0.0-next.522_svelte@3.52.0+vite@3.1.8:
resolution: {integrity: sha512-flroQQEwIHx6IEkskHGQ90Ri+gFCnAs4ZFRsG7QuTbuBu7XSeJNQs3J73pmBQoKp0pWnbwZ3YZMtwBJWF/I7qA==} resolution: {integrity: sha512-dVVrRPbXlAut4vg8kbCeZOjUZnYB7ZvDKp/yAx8InpDF0P1bvUDlc++DXFeMBdXX8bakTA3NGonWnDjqslCsZw==}
engines: {node: '>=16.14'} engines: {node: '>=16.14'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
@@ -2492,3 +2494,7 @@ packages:
dependencies: dependencies:
'@types/yoga-layout': 1.9.2 '@types/yoga-layout': 1.9.2
dev: false dev: false
/yoga-wasm-web/0.1.2:
resolution: {integrity: sha512-8SkgawHcA0RUbMrnhxbaQkZDBi8rMed8pQHixkFF9w32zGhAwZ9/cOHWlpYfr6RCx42Yp3siV45/jPEkJxsk6w==}
dev: false

View File

@@ -2,6 +2,7 @@ import { html as toReactNode } from 'satori-html';
import satori from 'satori'; import satori from 'satori';
import { Resvg, initWasm } from '@resvg/resvg-wasm'; import { Resvg, initWasm } from '@resvg/resvg-wasm';
import type { SatoriOptions } from 'satori'; import type { SatoriOptions } from 'satori';
import type {SvelteComponent} from "svelte";
const resSvgWasm = initWasm(fetch('https://sveltekit-og.ethercorps.io/resvg.wasm')); const resSvgWasm = initWasm(fetch('https://sveltekit-og.ethercorps.io/resvg.wasm'));
const fontFile = await fetch('https://sveltekit-og.ethercorps.io/noto-sans.ttf'); const fontFile = await fetch('https://sveltekit-og.ethercorps.io/noto-sans.ttf');
@@ -27,7 +28,7 @@ export const ImageResponse = class {
] ]
}); });
const pngData = new Resvg(svg, { fitTo: { mode: 'width', value: options.width } }); const pngData = new Resvg(svg, { fitTo: { mode: 'width', value: options.width } });
a.enqueue(await pngData.render().asPng()); a.enqueue(pngData.render().asPng());
a.close(); a.close();
} }
}); });
@@ -47,17 +48,22 @@ export const ImageResponse = class {
}; };
export const componentToImageResponse = class { export const componentToImageResponse = class {
constructor(component, props = {}, optionsByUser: ImageResponseOptions) { constructor(component: typeof SvelteComponent, props = {}, optionsByUser: ImageResponseOptions) {
const SvelteRenderedMarkup = component.render(props); const htmlTemplate = componentToMarkup(component, props)
let htmlTemplate = `${SvelteRenderedMarkup.html}`;
if (SvelteRenderedMarkup && SvelteRenderedMarkup.css && SvelteRenderedMarkup.css.code) {
htmlTemplate = `${SvelteRenderedMarkup.html}<style>${SvelteRenderedMarkup.css.code}</style>`;
}
return new ImageResponse(htmlTemplate, optionsByUser); return new ImageResponse(htmlTemplate, optionsByUser);
} }
}; };
export type ImageResponseOptions = ConstructorParameters<typeof Response>[1] & ImageOptions; const componentToMarkup = (component: typeof SvelteComponent, props={}) => {
const SvelteRenderedMarkup = (component as any).render(props);
let htmlTemplate = `${SvelteRenderedMarkup.html}`;
if (SvelteRenderedMarkup && SvelteRenderedMarkup.css && SvelteRenderedMarkup.css.code) {
htmlTemplate = `${SvelteRenderedMarkup.html}<style>${SvelteRenderedMarkup.css.code}</style>`;
}
return htmlTemplate
}
type ImageResponseOptions = ConstructorParameters<typeof Response>[1] & ImageOptions;
type ImageOptions = { type ImageOptions = {
width?: number; width?: number;