From 3d5ef91380185fb9e3b61f5b2065b6d2a6652007 Mon Sep 17 00:00:00 2001 From: Mihkel Martin Kasterpalu <43957228+MihkelMK@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:53:59 +0200 Subject: [PATCH] restore README, add disclaimer about project focus --- README.md | 203 ++++++++++++++++++++++++++++++++++++++++-------- static/demo.png | Bin 0 -> 12657 bytes 2 files changed, 169 insertions(+), 34 deletions(-) create mode 100644 static/demo.png diff --git a/README.md b/README.md index 4fee31f..aaed041 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,193 @@ -# create-svelte +# SvelteKit Open Graph Image Generation -Everything you need to build a Svelte library, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). +Dynamically generate Open Graph images from an HTML+CSS template or Svelte component using fast and efficient conversion from HTML > SVG > PNG. Based on [Satori](https://github.com/vercel/satori#documentation). No headless browser required. -Read more about creating a library [in the docs](https://kit.svelte.dev/docs/packaging). +## Disclaimer -## Creating a project +This documentation is currently unmaintained, as the development focus is compatibility with cloudflare, vercel and vercel edge. -If you're seeing this, you've probably already done this step. Congrats! +## Installation ```bash -# create a new project in the current directory -npm create svelte@latest - -# create a new project in my-app -npm create svelte@latest my-app +pnpm install -D @ethercorps/sveltekit-og ``` -## Developing - -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +> Using with Cloudflare Pages or Workers then you have to provide `url` polyfill by just installing it as `devDependency`. ```bash -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open +pnpm i -D url ``` -Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app. +## Usage -## Building +Create a file at `/src/routes/og/+server.ts`. Alternatively, you can use JavaScript by removing the types from this example. -To build your library: +```typescript +// src/routes/og/+server.ts +import { ImageResponse } from '@ethercorps/sveltekit-og'; +import { RequestHandler } from './$types'; -```bash -npm run package +const template = ` +
+
+

+ Ready to dive in? + Start your free trial today. +

+
+
+ Get started +
+
+ Learn more +
+
+
+
+`; + +const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-400-normal.woff'); +const fontData: ArrayBuffer = await fontFile.arrayBuffer(); + +export const GET: RequestHandler = async () => { + return await ImageResponse(template, { + height: 630, + width: 1200, + fonts: [ + { + name: 'Inter Latin', + data: fontData, + weight: 400 + } + ] + }); +}; ``` -To create a production version of your showcase app: +Then run `npm dev` and visit `localhost:5173/og` to view your generated PNG. Remember that hot module reloading does not work with server routes, so if you change your HTML or CSS, hard refresh the route to see changes. -```bash -npm run build +## Example Output + +![Rendered OG image](static/demo.png) + +## Headers + +When run in development, image headers contain `cache-control: no-cache, no-store`. In production, image headers contain `'cache-control': 'public, immutable, no-transform, max-age=31536000'`, which caches the image for 1 year. In both cases, the `'content-type': 'image/png'` is used. + +## Styling + +Notice that our example uses TailwindCSS classes (e.g. `tw="bg-gray-50"`). Alternatively, your HTML can contain style attributes using any of [the subset of CSS supported by Satori](https://github.com/vercel/satori#css). + +Satori supports only a subset of HTML and CSS. For full details, see [Satori’s documentation](https://github.com/vercel/satori#documentation). Notably, Satori only supports flex-based layouts. + +## Fonts + +Satori supports `ttf`, `otf`, and `woff` font formats; `woff2` is not supported. To maximize the font parsing speed, `ttf` or `otf` are recommended over `woff`. + +By default, `@ethercorps/sveltekit-og` includes only 'Noto Sans' font. If you need to use other fonts, you can specify them as shown in the example. Notably, you can also import a font file that is stored locally within your project and are not required to use fetch. + +## Examples + +- `ImageResponse` · [_source_](/src/routes/new/+server.ts) · [_demo_](https://sveltekit-og-five.vercel.app/new) +- `componentToImageResponse` · [_source_](/src/routes/component-og/) · [_demo_](https://sveltekit-og-five.vercel.app/component-og) + +## API Reference + +The package exposes an `ImageResponse` and `componentToImageResponse` constructors, with the following options available: + +```typescript +import {ImageResponse, componentToImageResponse} from '@ethercorps/sveltekit-og' +import {SvelteComponent} from "svelte"; + +// ... +ImageResponse( + element : string, + options : { + width ? : number = 1200 + height ? : number = 630, + backgroundColor ? : string = "#fff" + fonts ? : { + name: string, + data: ArrayBuffer, + weight: number, + style: 'normal' | 'italic' + }[] + debug ? : boolean = false + graphemeImages ? : Record; + loadAdditionalAsset ? : (languageCode: string, segment: string) => Promise; + // Options that will be passed to the HTTP response + status ? : number = 200 + statusText ? : string + headers ? : Record + }) + +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; + loadAdditionalAsset ? : (languageCode: string, segment: string) => Promise; + // Options that will be passed to the HTTP response + status ? : number = 200 + statusText ? : string + headers ? : Record + }) ``` -You can preview the production build with `npm run preview`. +## Changelog -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. +### v1.2.3 Update (Breaking Changes) -## Publishing +> Now you have to install dependency by yourself which will make it easier to build for all plateforms. -Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)). - -To publish your library to [npm](https://www.npmjs.com): - -```bash -npm publish ``` +npm i @resvg/resvg-js +``` + +``` +npm i satori +``` + +> From now on their will be no issues related to build, and soon this library going to have its own documentation. + +### v1.2.2 Update (Breaking Change) + +- We don't provide access to satori from `@ethercorps/sveltekit-og`. + +### v1.0.0 Update (Breaking Changes) + +Finally, We have added html to react like element like object converter out of the box and with svelte compiler. +Now you can use `{ toReactElement }` with `"@ethercorps/sveltekit-og"` like: + +- We have changed to function based instead of class based ImageResponse and componentToImageResponse. +- Removed `@resvg/resvg-wasm` with `@resvg/resvg-js` because of internal errors. +- Removed `satori-html` because now we have `toReactElement` out of the box with svelte compiler. + > If you find a problem related to undefined a please check [_vite.config.js_](/vite.config.ts) and add ` define: { _a: 'undefined' } in config.` + +> If you find any issue and have suggestion for this project please open a ticket and if you want to contribute please create a new discussion. + +## Acknowledgements + +This project will not be possible without the following projects: + +- [Satori & @vercel/og](https://github.com/vercel/satori) +- [Noto by Google Fonts](https://fonts.google.com/noto) +- [svg2png-wasm](https://github.com/ssssota/svg2png-wasm) + +## Authors + +- [@theetherGit](https://www.github.com/theetherGit) +- [@etherCorps](https://www.github.com/etherCorps) + +## Contributors + +- [@jasongitmail](https://github.com/jasongitmail) diff --git a/static/demo.png b/static/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..bb74d9ccf7e1715b35996c670ba65620f0e55a36 GIT binary patch literal 12657 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 NAUa0cm734P{u>#%4lV!y literal 0 HcmV?d00001