Added Example

This commit is contained in:
Shivam Meena
2022-10-31 00:29:51 +05:30
parent 1ae2620d7b
commit 46c22edd60
24 changed files with 5155 additions and 83 deletions

View File

@@ -4,7 +4,6 @@ about: Create a report to help us improve
title: '' title: ''
labels: '' labels: ''
assignees: '' assignees: ''
--- ---
**Describe the bug** **Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce** **To Reproduce**
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
@@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):** **Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari] - OS: [e.g. iOS]
- Version [e.g. 22] - Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):** **Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1] - Device: [e.g. iPhone6]
- Browser [e.g. stock browser, safari] - OS: [e.g. iOS8.1]
- Version [e.g. 22] - Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context** **Additional context**
Add any other context about the problem here. Add any other context about the problem here.

View File

@@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: '' title: ''
labels: '' labels: ''
assignees: '' assignees: ''
--- ---
**Is your feature request related to a problem? Please describe.** **Is your feature request related to a problem? Please describe.**

View File

@@ -9,8 +9,8 @@ Install `@ethercorps/sveltekit-og`, then use it inside a server endpoint route (
```typescript ```typescript
// /routes/og/+server.ts // /routes/og/+server.ts
import {ImageResponse} from '@ethercorps/sveltekit-og'; import { ImageResponse } from '@ethercorps/sveltekit-og';
import {RequestHandler} from "./$types"; import { RequestHandler } from './$types';
const template = ` const template = `
<div tw="bg-gray-50 flex w-full h-full items-center justify-center"> <div tw="bg-gray-50 flex w-full h-full items-center justify-center">
@@ -34,19 +34,18 @@ const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-4
const fontData: ArrayBuffer = await fontFile.arrayBuffer(); const fontData: ArrayBuffer = await fontFile.arrayBuffer();
export const GET: RequestHandler = async () => { export const GET: RequestHandler = async () => {
return new ImageResponse(template, { return new ImageResponse(template, {
height: 250, height: 250,
width: 500, width: 500,
fonts: [ fonts: [
{ {
name: 'Inter Latin', name: 'Inter Latin',
data: fontData, data: fontData,
weight: 400 weight: 400
} }
] ]
}); });
}; };
``` ```
Then run `pnpm dev` and access localhost:5173/og, the api/route endpoint be rendered and responded as a PNG from that api/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:
@@ -56,10 +55,10 @@ Then run `pnpm dev` and access localhost:5173/og, the api/route endpoint be rend
Read more about the API, supported features and check out the examples on Satori Playground. Read more about the API, supported features and check out the examples on Satori Playground.
## Examples: ## Examples:
- `ImageResponse` · [_source_](/src/routes/+server.ts) · [_demo_](https://sveltekit-og-five.vercel.app) - `ImageResponse` · [_source_](/src/routes/+server.ts) · [_demo_](https://sveltekit-og-five.vercel.app)
- `componentToImageResponse` · [_source_](/src/routes/component-og/) · [_demo_](https://sveltekit-og-five.vercel.app/component-og) - `componentToImageResponse` · [_source_](/src/routes/component-og/) · [_demo_](https://sveltekit-og-five.vercel.app/component-og)
## API Reference ## API Reference
The package exposes an `ImageResponse` and `componentToImageResponse` constructors, with the following options available: The package exposes an `ImageResponse` and `componentToImageResponse` constructors, with the following options available:
@@ -126,7 +125,6 @@ Please refer to [Satoris documentation](https://github.com/vercel/satori#docu
By default, `@ethercorps/sveltekit-og` only has the 'Noto Sans' font included. If you need to use other fonts, you can pass them in the `fonts` option. By default, `@ethercorps/sveltekit-og` only has the 'Noto Sans' font included. If you need to use other fonts, you can pass them in the `fonts` option.
## Acknowledgements ## Acknowledgements
This project will not be possible without the following projects: This project will not be possible without the following projects:
@@ -136,7 +134,6 @@ This project will not be possible without the following projects:
- [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)
## Authors ## Authors
- [@theetherGit](https://www.github.com/theetherGit) - [@theetherGit](https://www.github.com/theetherGit)

13
examples/.eslintignore Normal file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

15
examples/.eslintrc.cjs Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
plugins: ['svelte3'],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
browser: true,
es2017: true,
node: true
}
};

8
examples/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example

1
examples/.npmrc Normal file
View File

@@ -0,0 +1 @@
engine-strict=true

13
examples/.prettierignore Normal file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

9
examples/.prettierrc Normal file
View File

@@ -0,0 +1,9 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

38
examples/README.md Normal file
View File

@@ -0,0 +1,38 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```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
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

4846
examples/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
examples/package.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "examples",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"test": "playwright test",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@playwright/test": "^1.25.0",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-svelte3": "^4.0.0",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"svelte": "^3.44.0",
"vite": "^3.1.0"
},
"type": "module",
"dependencies": {
"@ethercorps/sveltekit-og": "^0.1.5"
}
}

View File

@@ -0,0 +1,9 @@
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173
}
};
export default config;

12
examples/src/app.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body>
<div>%sveltekit.body%</div>
</body>
</html>

View File

@@ -0,0 +1,37 @@
import { ImageResponse } from '@ethercorps/sveltekit-og';
import type { RequestHandler } from './$types';
const template = `
<div tw="bg-gray-50 flex w-full h-full items-center justify-center">
<div tw="flex flex-col md:flex-row w-full py-12 px-4 md:items-center justify-between p-8">
<h2 tw="flex flex-col text-3xl sm:text-4xl font-bold tracking-tight text-gray-900 text-left">
<span>Ready to dive in?</span>
<span tw="text-indigo-600">Start your free trial today.</span>
</h2>
<div tw="mt-8 flex md:mt-0">
<div tw="flex rounded-md shadow">
<a href="#" tw="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-5 py-3 text-base font-medium text-white">Get started</a>
</div>
<div tw="ml-3 flex rounded-md shadow">
<a href="#" tw="flex items-center justify-center rounded-md border border-transparent bg-white px-5 py-3 text-base font-medium text-indigo-600">Learn more</a>
</div>
</div>
</div>
</div>
`;
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 new ImageResponse(template, {
height: 400,
width: 800,
fonts: [
{
name: 'Inter',
data: fontData,
weight: 400
}
]
});
};

BIN
examples/static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

10
examples/svelte.config.js Normal file
View File

@@ -0,0 +1,10 @@
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter()
}
};
export default config;

6
examples/tests/test.js Normal file
View File

@@ -0,0 +1,6 @@
import { expect, test } from '@playwright/test';
test('index page has expected h1', async ({ page }) => {
await page.goto('/');
expect(await page.textContent('h1')).toBe('Welcome to SvelteKit');
});

7
examples/vite.config.js Normal file
View File

@@ -0,0 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
const config = {
plugins: [sveltekit()]
};
export default config;

View File

@@ -7,22 +7,28 @@
<!-- HTML Meta Tags --> <!-- HTML Meta Tags -->
<title>@ethercorps/sveltekit-og</title> <title>@ethercorps/sveltekit-og</title>
<meta name="description" content="Demo example for @ethercorps/sveltekit-og"> <meta name="description" content="Demo example for @ethercorps/sveltekit-og" />
<!-- Facebook Meta Tags --> <!-- Facebook Meta Tags -->
<meta property="og:url" content="https://sveltekit-og.ethercorps.io/"> <meta property="og:url" content="https://sveltekit-og.ethercorps.io/" />
<meta property="og:type" content="website"> <meta property="og:type" content="website" />
<meta property="og:title" content="@ethercorps/sveltekit-og"> <meta property="og:title" content="@ethercorps/sveltekit-og" />
<meta property="og:description" content="A typescript implementation of vercel/og for sveltekit."> <meta
<meta property="og:image" content="https://sveltekit-og.ethercorps.io/"> property="og:description"
content="A typescript implementation of vercel/og for sveltekit."
/>
<meta property="og:image" content="https://sveltekit-og.ethercorps.io/" />
<!-- Twitter Meta Tags --> <!-- Twitter Meta Tags -->
<meta name="twitter:card" content="https://sveltekit-og.ethercorps.io/"> <meta name="twitter:card" content="https://sveltekit-og.ethercorps.io/" />
<meta property="twitter:domain" content="sveltekit-og.ethercorps.io"> <meta property="twitter:domain" content="sveltekit-og.ethercorps.io" />
<meta property="twitter:url" content="https://sveltekit-og.ethercorps.io/"> <meta property="twitter:url" content="https://sveltekit-og.ethercorps.io/" />
<meta name="twitter:title" content="undefined"> <meta name="twitter:title" content="undefined" />
<meta name="twitter:description" content="A typescript implementation of vercel/og for sveltekit."> <meta
<meta name="twitter:image" content="https://sveltekit-og.ethercorps.io/"> name="twitter:description"
content="A typescript implementation of vercel/og for sveltekit."
/>
<meta name="twitter:image" content="https://sveltekit-og.ethercorps.io/" />
%sveltekit.head% %sveltekit.head%
</head> </head>
<body> <body>

View File

@@ -2,13 +2,14 @@ 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"; import type { SvelteComponent } from 'svelte';
import { dev } from '$app/environment';
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');
const fontData: ArrayBuffer = await fontFile.arrayBuffer(); const fontData: ArrayBuffer = await fontFile.arrayBuffer();
class ImageResponse { class ImageResponse {
constructor(htmlTemplate: string, optionsByUser: ImageResponseOptions) { constructor(htmlTemplate: string, optionsByUser: ImageResponseOptions) {
const options = Object.assign({ width: 1200, height: 630, debug: !1 }, optionsByUser); const options = Object.assign({ width: 1200, height: 630, debug: !1 }, optionsByUser);
const png = new ReadableStream({ const png = new ReadableStream({
@@ -35,7 +36,7 @@ class ImageResponse {
return new Response(png, { return new Response(png, {
headers: { headers: {
'Content-Type': 'image/png', 'Content-Type': 'image/png',
'cache-control': import.meta.env.DEV 'cache-control': dev
? 'no-cache, no-store' ? 'no-cache, no-store'
: 'public, immutable, no-transform, max-age=31536000', : 'public, immutable, no-transform, max-age=31536000',
...options.headers ...options.headers
@@ -48,20 +49,20 @@ class ImageResponse {
} }
class componentToImageResponse { class componentToImageResponse {
constructor(component: typeof SvelteComponent, props = {}, optionsByUser: ImageResponseOptions) { constructor(component: typeof SvelteComponent, props = {}, optionsByUser: ImageResponseOptions) {
const htmlTemplate = componentToMarkup(component, props) const htmlTemplate = componentToMarkup(component, props);
return new ImageResponse(htmlTemplate, optionsByUser); return new ImageResponse(htmlTemplate, optionsByUser);
} }
} }
const componentToMarkup = (component: typeof SvelteComponent, props={}) => { const componentToMarkup = (component: typeof SvelteComponent, props = {}) => {
const SvelteRenderedMarkup = (component as any).render(props); const SvelteRenderedMarkup = (component as any).render(props);
let htmlTemplate = `${SvelteRenderedMarkup.html}`; let htmlTemplate = `${SvelteRenderedMarkup.html}`;
if (SvelteRenderedMarkup && SvelteRenderedMarkup.css && SvelteRenderedMarkup.css.code) { if (SvelteRenderedMarkup && SvelteRenderedMarkup.css && SvelteRenderedMarkup.css.code) {
htmlTemplate = `${SvelteRenderedMarkup.html}<style>${SvelteRenderedMarkup.css.code}</style>`; htmlTemplate = `${SvelteRenderedMarkup.html}<style>${SvelteRenderedMarkup.css.code}</style>`;
} }
return htmlTemplate return htmlTemplate;
} };
type ImageResponseOptions = ConstructorParameters<typeof Response>[1] & ImageOptions; type ImageResponseOptions = ConstructorParameters<typeof Response>[1] & ImageOptions;
@@ -71,7 +72,10 @@ type ImageOptions = {
debug?: boolean; debug?: boolean;
fonts?: SatoriOptions['fonts']; fonts?: SatoriOptions['fonts'];
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>;
}; };
export {componentToImageResponse, ImageResponse} export { componentToImageResponse, ImageResponse };

View File

@@ -1,5 +1,5 @@
import { ImageResponse } from '$lib'; import { ImageResponse } from '$lib';
import type {RequestHandler} from "@sveltejs/kit"; import type { RequestHandler } from '@sveltejs/kit';
const template = ` const template = `
<div tw="bg-gray-50 flex w-full h-full items-center justify-center"> <div tw="bg-gray-50 flex w-full h-full items-center justify-center">

View File

@@ -1,21 +1,24 @@
import OG from "./OG.svelte"; import OG from './OG.svelte';
import {componentToImageResponse} from "$lib"; import { componentToImageResponse } from '$lib';
import type {RequestHandler} from "@sveltejs/kit"; import type { RequestHandler } from '@sveltejs/kit';
const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-700-normal.woff'); const fontFile = await fetch('https://og-playground.vercel.app/inter-latin-ext-700-normal.woff');
const fontData: ArrayBuffer = await fontFile.arrayBuffer(); const fontData: ArrayBuffer = await fontFile.arrayBuffer();
export const GET: RequestHandler = async () => { export const GET: RequestHandler = async () => {
return new componentToImageResponse(OG, {text: 'Ready to dive in?', spanText: 'Start your free trial today.'}, { return new componentToImageResponse(
height: 250, OG,
width: 500, { text: 'Ready to dive in?', spanText: 'Start your free trial today.' },
fonts: [ {
{ height: 250,
name: 'Inter Latin', width: 500,
data: fontData, fonts: [
weight: 700 {
} name: 'Inter Latin',
] data: fontData,
}); weight: 700
}
]
}
);
}; };

View File

@@ -1,21 +1,29 @@
<div tw="bg-gray-50 flex w-full h-full items-center justify-center">
<div tw="flex flex-col md:flex-row w-full py-12 px-4 md:items-center justify-between p-8">
<h2 tw="flex flex-col text-3xl sm:text-4xl font-bold tracking-tight text-gray-900 text-left">
<span>{text}</span>
<span tw="text-indigo-600">{spanText}</span>
</h2>
<div tw="mt-8 flex md:mt-0">
<div tw="flex rounded-md shadow">
<a href="#" tw="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-5 py-3 text-base font-medium text-white">Get started</a>
</div>
<div tw="ml-3 flex rounded-md shadow">
<a href="#" tw="flex items-center justify-center rounded-md border border-transparent bg-white px-5 py-3 text-base font-medium text-indigo-600">Learn more</a>
</div>
</div>
</div>
</div>
<script> <script>
export let text; export let text;
export let spanText; export let spanText;
</script> </script>
<div tw="bg-gray-50 flex w-full h-full items-center justify-center">
<div tw="flex flex-col md:flex-row w-full py-12 px-4 md:items-center justify-between p-8">
<h2 tw="flex flex-col text-3xl sm:text-4xl font-bold tracking-tight text-gray-900 text-left">
<span>{text}</span>
<span tw="text-indigo-600">{spanText}</span>
</h2>
<div tw="mt-8 flex md:mt-0">
<div tw="flex rounded-md shadow">
<a
href="#"
tw="flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-5 py-3 text-base font-medium text-white"
>Get started</a
>
</div>
<div tw="ml-3 flex rounded-md shadow">
<a
href="#"
tw="flex items-center justify-center rounded-md border border-transparent bg-white px-5 py-3 text-base font-medium text-indigo-600"
>Learn more</a
>
</div>
</div>
</div>
</div>