Skip to content

Commit 7c00332

Browse files
committed
feat(website): add release OG image generator
1 parent 82085ba commit 7c00332

11 files changed

Lines changed: 799 additions & 15 deletions

File tree

pnpm-lock.yaml

Lines changed: 308 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/dictionary.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ antd
44
apng
55
apos
66
applescript
7+
artboard
78
Asus
89
atrules
910
autodocs
@@ -63,6 +64,7 @@ fnames
6364
frontends
6465
fullhash
6566
gzipped
67+
Grotesk
6668
icss
6769
idents
6870
iife
@@ -105,6 +107,7 @@ onclosetag
105107
onopentag
106108
ontext
107109
opencode
110+
Optim
108111
osascript
109112
outbase
110113
outro
@@ -117,6 +120,7 @@ PIPEWRAP
117120
pjpeg
118121
pluggable
119122
pmmmwh
123+
pngquant
120124
pnpx
121125
postcssrc
122126
preact
@@ -130,7 +134,9 @@ pxtorem
130134
quasis
131135
quxx
132136
RANDOMBYTESREQUEST
137+
rasterizes
133138
rebranded
139+
resvg
134140
rolldown
135141
rootdir
136142
rsbuild
@@ -151,6 +157,7 @@ SIGNREQUEST
151157
sirv
152158
sokra
153159
speedscope
160+
Squoosh
154161
srcset
155162
stacktracey
156163
styl

website/AGENTS.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,36 @@ This is the documentation website for Rstest, built with [Rspress](https://rspre
1414
pnpm dev # Start dev server
1515
pnpm build # Build for production
1616
pnpm preview # Preview production build
17+
pnpm gen:og # Generate a release Open Graph image (see below)
1718
```
1819

20+
## Open Graph image generation
21+
22+
Per-release og images live in [rstackjs/rstack-design-resources](https://github.com/rstackjs/rstack-design-resources) and are served by the `assets.rspack.rs` CDN. The template lives **in this repo** to keep design-resources as a passive PNG store.
23+
24+
- `scripts/og-image/cli.mts` — entry, parses `--version`/`--description`/`--out`
25+
- `scripts/og-image/render.mts` — fetches the Rstest logo SVG → rasterizes → composes with [satori](https://github.com/vercel/satori) → renders with [@resvg/resvg-js](https://github.com/yisibl/resvg-js) at 2x zoom for retina
26+
- `scripts/og-image/template.mts`[satori-html](https://github.com/natemoo-re/satori-html) template, modeled after the `Rsbuild og image 1.0` artboard in design-resources
27+
28+
### Release workflow
29+
30+
1. Run `pnpm gen:og --version <ver> --description "<tagline>"` from `website/`. Use `--out` to write directly into a local clone of the design-resources repo at `rstest/assets/rstest-og-image-v{version-with-hyphens}.png` (e.g. `v0-5.png`). The background gradient is randomized (color scheme, blob count, placement) on every run and there is no seed flag — re-run until you get a composition you like before committing.
31+
2. Commit the PNG in the design-resources repo and open a PR — that repo is the only place release PNGs are stored.
32+
3. After CDN deploy, the PNG is reachable at `assets.rspack.rs/rstest/assets/rstest-og-image-v0-5.png`. Wiring it up per blog `routePath` in `rspress.config.ts` is a separate follow-up — the site currently sets a single static `og:image` via `pluginOpenGraph`.
33+
34+
### Do
35+
36+
- Use Space Grotesk (committed under `scripts/og-image/assets/fonts/` with SIL OFL license)
37+
- Render at 2x via `Resvg({ fitTo: { mode: 'zoom', value: 2 } })` so the PNG stays crisp on retina displays
38+
- Before committing the PNG to design-resources, run it through [TinyPNG](https://tinypng.com) (or Squoosh / ImageOptim / `pngquant`) — the raw resvg output is ~300 KB and palette quantization typically drops it to ~1/4 the size with no visible loss
39+
- Fetch the logo from the canonical CDN URL at generation time, not from a committed copy
40+
41+
### Don't
42+
43+
- Don't depend on packages like `geist` that pull in framework peer deps (`next>=13.2`); commit raw `.ttf` files directly instead
44+
- Don't write generated PNGs into this repo; they belong in design-resources
45+
- Don't bake the logo into a static asset; always fetch the SVG so logo updates propagate automatically
46+
1947
## Writing style guidelines
2048

2149
When writing or editing documentation, follow these principles:

website/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@ The same as Rspack: [Writing style guide](https://github.com/web-infra-dev/rspac
1515
For images you use in the document, it's better to upload them to the [rstackjs/rstack-design-resources](https://github.com/rstackjs/rstack-design-resources) repository, so the size of the current repository doesn't get too big.
1616

1717
After you upload the images there, they will be automatically deployed under the <https://assets.rspack.rs/>.
18+
19+
## Open Graph images
20+
21+
`scripts/og-image/` generates per-release Open Graph images used by the `og:image` and `twitter:image` meta tags on each release blog post. See [AGENTS.md](./AGENTS.md#open-graph-image-generation) for the template architecture and release workflow.

website/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
"build": "rspress build",
1010
"build:agent-install": "node scripts/buildAgentInstall.mjs",
1111
"dev": "rspress dev",
12+
"gen:og": "node --experimental-strip-types scripts/og-image/cli.mts",
1213
"preview": "rspress preview"
1314
},
1415
"devDependencies": {
16+
"@resvg/resvg-js": "^2.6.2",
1517
"@rsbuild/plugin-sass": "^2.0.0",
1618
"@rspress/core": "2.0.15",
1719
"@rspress/plugin-algolia": "2.0.15",
@@ -26,6 +28,8 @@
2628
"rsbuild-plugin-open-graph": "^1.1.3",
2729
"rspress-plugin-font-open-sans": "^1.0.4",
2830
"rspress-plugin-sitemap": "^1.2.1",
31+
"satori": "^0.26.0",
32+
"satori-html": "^0.3.2",
2933
"typescript": "^6.0.3"
3034
}
3135
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk)
2+
3+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
4+
This license is copied below, and is also available with a FAQ at:
5+
http://scripts.sil.org/OFL
6+
7+
8+
-----------------------------------------------------------
9+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10+
-----------------------------------------------------------
11+
12+
PREAMBLE
13+
The goals of the Open Font License (OFL) are to stimulate worldwide
14+
development of collaborative font projects, to support the font creation
15+
efforts of academic and linguistic communities, and to provide a free and
16+
open framework in which fonts may be shared and improved in partnership
17+
with others.
18+
19+
The OFL allows the licensed fonts to be used, studied, modified and
20+
redistributed freely as long as they are not sold by themselves. The
21+
fonts, including any derivative works, can be bundled, embedded,
22+
redistributed and/or sold with any software provided that any reserved
23+
names are not used by derivative works. The fonts and derivatives,
24+
however, cannot be released under any other type of license. The
25+
requirement for fonts to remain under this license does not apply
26+
to any document created using the fonts or their derivatives.
27+
28+
DEFINITIONS
29+
"Font Software" refers to the set of files released by the Copyright
30+
Holder(s) under this license and clearly marked as such. This may
31+
include source files, build scripts and documentation.
32+
33+
"Reserved Font Name" refers to any names specified as such after the
34+
copyright statement(s).
35+
36+
"Original Version" refers to the collection of Font Software components as
37+
distributed by the Copyright Holder(s).
38+
39+
"Modified Version" refers to any derivative made by adding to, deleting,
40+
or substituting -- in part or in whole -- any of the components of the
41+
Original Version, by changing formats or by porting the Font Software to a
42+
new environment.
43+
44+
"Author" refers to any designer, engineer, programmer, technical
45+
writer or other person who contributed to the Font Software.
46+
47+
PERMISSION & CONDITIONS
48+
Permission is hereby granted, free of charge, to any person obtaining
49+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
50+
redistribute, and sell modified and unmodified copies of the Font
51+
Software, subject to the following conditions:
52+
53+
1) Neither the Font Software nor any of its individual components,
54+
in Original or Modified Versions, may be sold by itself.
55+
56+
2) Original or Modified Versions of the Font Software may be bundled,
57+
redistributed and/or sold with any software, provided that each copy
58+
contains the above copyright notice and this license. These can be
59+
included either as stand-alone text files, human-readable headers or
60+
in the appropriate machine-readable metadata fields within text or
61+
binary files as long as those fields can be easily viewed by the user.
62+
63+
3) No Modified Version of the Font Software may use the Reserved Font
64+
Name(s) unless explicit written permission is granted by the corresponding
65+
Copyright Holder. This restriction only applies to the primary font name as
66+
presented to the users.
67+
68+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69+
Software shall not be used to promote, endorse or advertise any
70+
Modified Version, except to acknowledge the contribution(s) of the
71+
Copyright Holder(s) and the Author(s) or with their explicit written
72+
permission.
73+
74+
5) The Font Software, modified or unmodified, in part or in whole,
75+
must be distributed entirely under this license, and must not be
76+
distributed under any other license. The requirement for fonts to
77+
remain under this license does not apply to any document created
78+
using the Font Software.
79+
80+
TERMINATION
81+
This license becomes null and void if any of the above conditions are
82+
not met.
83+
84+
DISCLAIMER
85+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93+
OTHER DEALINGS IN THE FONT SOFTWARE.
113 KB
Binary file not shown.
112 KB
Binary file not shown.

website/scripts/og-image/cli.mts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { mkdir, writeFile } from 'node:fs/promises';
2+
import path from 'node:path';
3+
import { parseArgs } from 'node:util';
4+
import { renderOgImage } from './render.mts';
5+
6+
const { values } = parseArgs({
7+
options: {
8+
version: { type: 'string', short: 'v' },
9+
description: { type: 'string' },
10+
out: { type: 'string', short: 'o' },
11+
help: { type: 'boolean', short: 'h' },
12+
},
13+
});
14+
15+
if (values.help || !values.version) {
16+
console.log(`Usage: pnpm gen:og --version <ver> [options]
17+
18+
Options:
19+
--version, -v Release version, e.g. 0.5 (required)
20+
--description Optional tagline rendered below the version
21+
--out, -o Output PNG path
22+
(default: rstest-og-image-v<ver>.png in cwd)
23+
--help, -h Show this help
24+
25+
After generating, commit the PNG to rstackjs/rstack-design-resources
26+
under rstest/assets/ so the assets.rspack.rs CDN can serve it.`);
27+
process.exit(values.help ? 0 : 1);
28+
}
29+
30+
const versionSlug = values.version.replace(/\./g, '-');
31+
const outPath = path.resolve(
32+
values.out ?? `rstest-og-image-v${versionSlug}.png`,
33+
);
34+
35+
const png = await renderOgImage({
36+
version: values.version,
37+
description: values.description,
38+
});
39+
40+
await mkdir(path.dirname(outPath), { recursive: true });
41+
await writeFile(outPath, png);
42+
43+
console.log(`Wrote ${outPath} (${(png.length / 1024).toFixed(1)} KB)`);
44+
console.log(
45+
'Tip: compress the PNG before committing — typically drops the file to ~1/4 the size with no visible loss.',
46+
);
47+
console.log(' https://tinypng.com (or Squoosh / ImageOptim)');

0 commit comments

Comments
 (0)