diff --git a/.changeset/add-plugin-og-image.md b/.changeset/add-plugin-og-image.md new file mode 100644 index 000000000..966967b3f --- /dev/null +++ b/.changeset/add-plugin-og-image.md @@ -0,0 +1,7 @@ +--- +'@rspress/plugin-og-image': minor +--- + +feat: add plugin-og-image for dynamic Open Graph image generation + +This plugin automatically generates OG images for each page during build. It uses Satori for SVG rendering and Sharp for PNG conversion. Images are placed at `/og/{route-path}.png` and og:image meta tags are automatically injected into the HTML. diff --git a/e2e/fixtures/plugin-og-image/doc/blog/first-post.md b/e2e/fixtures/plugin-og-image/doc/blog/first-post.md new file mode 100644 index 000000000..9573c5cab --- /dev/null +++ b/e2e/fixtures/plugin-og-image/doc/blog/first-post.md @@ -0,0 +1,8 @@ +--- +title: My First Post +description: This is my first blog post +--- + +# My first post + +Welcome to my blog! diff --git a/e2e/fixtures/plugin-og-image/doc/guide.md b/e2e/fixtures/plugin-og-image/doc/guide.md new file mode 100644 index 000000000..836d9e7b7 --- /dev/null +++ b/e2e/fixtures/plugin-og-image/doc/guide.md @@ -0,0 +1,8 @@ +--- +title: Getting Started +description: Learn how to get started with our platform +--- + +# Getting started + +This guide will help you get started. diff --git a/e2e/fixtures/plugin-og-image/doc/index.md b/e2e/fixtures/plugin-og-image/doc/index.md new file mode 100644 index 000000000..3bcaeca88 --- /dev/null +++ b/e2e/fixtures/plugin-og-image/doc/index.md @@ -0,0 +1,3 @@ +# Welcome + +This is the home page. diff --git a/e2e/fixtures/plugin-og-image/fixture.json b/e2e/fixtures/plugin-og-image/fixture.json new file mode 100644 index 000000000..95b9a35c6 --- /dev/null +++ b/e2e/fixtures/plugin-og-image/fixture.json @@ -0,0 +1,5 @@ +{ + "base": "/", + "siteUrl": "http://localhost:4173/", + "title": "OG Image Test Site" +} diff --git a/e2e/fixtures/plugin-og-image/index.test.ts b/e2e/fixtures/plugin-og-image/index.test.ts new file mode 100644 index 000000000..36f9d8a2a --- /dev/null +++ b/e2e/fixtures/plugin-og-image/index.test.ts @@ -0,0 +1,63 @@ +import { existsSync } from 'node:fs'; +import { readFile } from 'node:fs/promises'; +import * as NodePath from 'node:path'; +import { expect, test } from '@playwright/test'; +import { runBuildCommand } from '../../utils/runCommands'; + +const appDir = __dirname; + +test.describe('plugin og-image test', async () => { + test.beforeAll(async () => { + await runBuildCommand(appDir); + }); + + test('should generate og image for home page', async () => { + const imagePath = NodePath.resolve(appDir, 'doc_build/og/index.png'); + expect(existsSync(imagePath)).toBe(true); + + // Verify it's a valid PNG + const buffer = await readFile(imagePath); + expect(buffer.subarray(0, 8).toString('hex')).toBe('89504e470d0a1a0a'); // PNG signature + }); + + test('should generate og image for guide page', async () => { + const imagePath = NodePath.resolve(appDir, 'doc_build/og/guide.png'); + expect(existsSync(imagePath)).toBe(true); + + const buffer = await readFile(imagePath); + expect(buffer.subarray(0, 8).toString('hex')).toBe('89504e470d0a1a0a'); // PNG signature + }); + + test('should generate og image for blog post', async () => { + const imagePath = NodePath.resolve( + appDir, + 'doc_build/og/blog/first-post.png', + ); + expect(existsSync(imagePath)).toBe(true); + + const buffer = await readFile(imagePath); + expect(buffer.subarray(0, 8).toString('hex')).toBe('89504e470d0a1a0a'); // PNG signature + }); + + test('should have correct image dimensions', async () => { + const imagePath = NodePath.resolve(appDir, 'doc_build/og/index.png'); + const buffer = await readFile(imagePath); + + // PNG files store width and height at specific byte positions + // Width is at bytes 16-19, height at bytes 20-23 (big-endian) + const width = buffer.readUInt32BE(16); + const height = buffer.readUInt32BE(20); + + expect(width).toBe(1200); // Default width + expect(height).toBe(630); // Default height + }); + + test('should include og:image meta tag in HTML', async () => { + const htmlPath = NodePath.resolve(appDir, 'doc_build/guide.html'); + const html = await readFile(htmlPath, 'utf-8'); + + // Check that og:image meta tag is in the HTML + expect(html).toContain('og:image'); + expect(html).toContain('og/guide.png'); + }); +}); diff --git a/e2e/fixtures/plugin-og-image/package.json b/e2e/fixtures/plugin-og-image/package.json new file mode 100644 index 000000000..f2d29964c --- /dev/null +++ b/e2e/fixtures/plugin-og-image/package.json @@ -0,0 +1,14 @@ +{ + "name": "@rspress-fixture/plugin-og-image", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "rspress build", + "dev": "rspress dev", + "preview": "rspress preview" + }, + "dependencies": { + "@rspress/core": "workspace:*", + "@rspress/plugin-og-image": "workspace:*" + } +} diff --git a/e2e/fixtures/plugin-og-image/rspress.config.ts b/e2e/fixtures/plugin-og-image/rspress.config.ts new file mode 100644 index 000000000..9466770d2 --- /dev/null +++ b/e2e/fixtures/plugin-og-image/rspress.config.ts @@ -0,0 +1,15 @@ +import * as NodePath from 'node:path'; +import { defineConfig } from '@rspress/core'; +import { pluginOgImage } from '@rspress/plugin-og-image'; +import fixture from './fixture.json'; + +export default defineConfig({ + root: NodePath.resolve(__dirname, 'doc'), + title: fixture.title, + base: fixture.base, + plugins: [ + pluginOgImage({ + siteUrl: fixture.siteUrl, + }), + ], +}); diff --git a/e2e/fixtures/plugin-og-image/tsconfig.json b/e2e/fixtures/plugin-og-image/tsconfig.json new file mode 100644 index 000000000..618c6c3e9 --- /dev/null +++ b/e2e/fixtures/plugin-og-image/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tsconfig.json" +} diff --git a/packages/plugin-og-image/LICENSE b/packages/plugin-og-image/LICENSE new file mode 100644 index 000000000..b6bac59cb --- /dev/null +++ b/packages/plugin-og-image/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2024 jl917 +Copyright (c) 2025-present Bytedance, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/plugin-og-image/README.md b/packages/plugin-og-image/README.md new file mode 100644 index 000000000..e8137954c --- /dev/null +++ b/packages/plugin-og-image/README.md @@ -0,0 +1,106 @@ +# @rspress/plugin-og-image + +A plugin for Rspress to dynamically generate Open Graph (OG) images for each page. + +## Installation + +```bash +npm install @rspress/plugin-og-image +# or +pnpm add @rspress/plugin-og-image +# or +yarn add @rspress/plugin-og-image +``` + +## Usage + +Add the plugin to your `rspress.config.ts`: + +```ts +import { defineConfig } from '@rspress/core'; +import { pluginOgImage } from '@rspress/plugin-og-image'; + +export default defineConfig({ + plugins: [ + pluginOgImage({ + siteUrl: 'https://your-site.com', + }), + ], +}); +``` + +## Options + +### `siteUrl` + +- Type: `string` +- Required: `true` + +The base URL of your site. Used to generate absolute URLs for OG images. + +### `ogImage` + +- Type: `OgImageOptions` +- Required: `false` + +Options for OG image generation. + +#### `ogImage.width` + +- Type: `number` +- Default: `1200` + +Width of the generated OG image in pixels. + +#### `ogImage.height` + +- Type: `number` +- Default: `630` + +Height of the generated OG image in pixels (630px is the recommended size for OG images). + +#### `ogImage.template` + +- Type: `(data: OgImageTemplateData) => string | Promise` +- Required: `false` + +Custom template function to generate the image. Receives page data and should return a React-like JSX structure compatible with [Satori](https://github.com/vercel/satori). + +#### `ogImage.filter` + +- Type: `(pageData: PageIndexInfo) => boolean` +- Default: `() => true` + +Filter function to determine which pages should have OG images generated. By default, all pages get OG images. + +## Frontmatter options + +You can customize OG images per page using frontmatter: + +```md +--- +title: My Page +description: A description of my page +ogBackgroundColor: '#1a1a1a' +ogTextColor: '#ffffff' +siteName: My Site +--- +``` + +## Generated URLs + +OG images are generated with URLs matching your page routes: + +- Page: `https://your-site.com/guide/getting-started` +- OG Image: `https://your-site.com/og/guide/getting-started.png` + +## How it works + +1. During the build process, the plugin generates OG images for each page +2. Images are created using [Satori](https://github.com/vercel/satori) (SVG generation) and [Sharp](https://sharp.pixelplumbing.com/) (PNG conversion) +3. OG image meta tags are automatically added to each page's frontmatter +4. Images are saved to the output directory in the `/og` folder + +## License + +MIT diff --git a/packages/plugin-og-image/package.json b/packages/plugin-og-image/package.json new file mode 100644 index 000000000..a95cb98e7 --- /dev/null +++ b/packages/plugin-og-image/package.json @@ -0,0 +1,51 @@ +{ + "name": "@rspress/plugin-og-image", + "version": "2.0.0-rc.1", + "description": "A plugin for rspress to generate dynamic OG (Open Graph) images", + "bugs": "https://github.com/web-infra-dev/rspress/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/web-infra-dev/rspress.git", + "directory": "packages/plugin-og-image" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist", + "static" + ], + "scripts": { + "build": "rslib build", + "dev": "rslib build -w", + "reset": "rimraf ./**/node_modules" + }, + "dependencies": { + "satori": "^0.11.2", + "sharp": "^0.33.5" + }, + "devDependencies": { + "@microsoft/api-extractor": "^7.55.0", + "@rslib/core": "0.17.2", + "@types/node": "^22.8.1", + "rsbuild-plugin-publint": "^0.3.3" + }, + "peerDependencies": { + "@rspress/core": "workspace:^2.0.0-rc.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + } +} diff --git a/packages/plugin-og-image/rslib.config.ts b/packages/plugin-og-image/rslib.config.ts new file mode 100644 index 000000000..3f49351d1 --- /dev/null +++ b/packages/plugin-og-image/rslib.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from '@rslib/core'; +import { pluginPublint } from 'rsbuild-plugin-publint'; + +export default defineConfig({ + plugins: [pluginPublint()], + lib: [ + { bundle: true, syntax: 'es2022', format: 'esm', dts: { bundle: true } }, + ], +}); diff --git a/packages/plugin-og-image/src/generator.ts b/packages/plugin-og-image/src/generator.ts new file mode 100644 index 000000000..d888bdc8c --- /dev/null +++ b/packages/plugin-og-image/src/generator.ts @@ -0,0 +1,62 @@ +import { readFile } from 'node:fs/promises'; +import satori from 'satori'; +import sharp from 'sharp'; +import { defaultTemplate } from './template'; +import type { OgImageOptions, OgImageTemplateData } from './types'; + +let cachedFont: Buffer | null = null; + +/** + * Get font data for Satori + */ +async function getFontData(): Promise { + if (cachedFont) { + return cachedFont; + } + + try { + // Try to use a system font or bundled font + // For now, we'll use a simple approach - in production this might need bundled fonts + const fontPath = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'; + const fontData = await readFile(fontPath); + cachedFont = Buffer.from(fontData); + return cachedFont; + } catch (error) { + // Fallback: return empty buffer and Satori will use default + console.warn('Could not load font, using Satori defaults'); + return Buffer.from([]); + } +} + +/** + * Generate OG image PNG from template data + */ +export async function generateOgImage( + data: OgImageTemplateData, + options: OgImageOptions = {}, +): Promise { + const { width = 1200, height = 630, template = defaultTemplate } = options; + + // Get the template (either custom or default) + const templateResult = + typeof template === 'function' ? await template(data) : template; + + // Convert template to SVG using Satori + const svg = await satori(templateResult, { + width, + height, + fonts: [ + { + name: 'sans-serif', + data: await getFontData(), + weight: 400, + style: 'normal', + }, + ], + }); + + // Convert SVG to PNG using Sharp + const png = await sharp(Buffer.from(svg)).png().toBuffer(); + + return png; +} diff --git a/packages/plugin-og-image/src/index.ts b/packages/plugin-og-image/src/index.ts new file mode 100644 index 000000000..24c3b6335 --- /dev/null +++ b/packages/plugin-og-image/src/index.ts @@ -0,0 +1,6 @@ +export { pluginOgImage } from './plugin'; +export type { + OgImageOptions, + OgImageTemplateData, + PluginOgImageOptions, +} from './types'; diff --git a/packages/plugin-og-image/src/plugin.ts b/packages/plugin-og-image/src/plugin.ts new file mode 100644 index 000000000..9b7d1a865 --- /dev/null +++ b/packages/plugin-og-image/src/plugin.ts @@ -0,0 +1,123 @@ +import { mkdir, writeFile } from 'node:fs/promises'; +import { dirname, isAbsolute, join } from 'node:path'; +import type { PageIndexInfo, RspressPlugin } from '@rspress/core'; +import { generateOgImage } from './generator'; +import type { OgImageTemplateData, PluginOgImageOptions } from './types'; + +interface PageOgImageInfo { + routePath: string; + imagePath: string; + imageUrl: string; + templateData: OgImageTemplateData; +} + +export function pluginOgImage(options: PluginOgImageOptions): RspressPlugin { + const { siteUrl, ogImage = {} } = options; + const { filter = () => true } = ogImage; + + // Store page info for image generation + const pageInfoMap = new Map(); + + return { + name: '@rspress/plugin-og-image', + + config(config) { + // Extend config.head to add og:image meta tags + const originalHead = config.head || []; + config.head = [ + ...originalHead, + route => { + const pageInfo = pageInfoMap.get(route.routePath); + if (pageInfo) { + return [ + 'meta', + { + property: 'og:image', + content: pageInfo.imageUrl, + }, + ] as [string, Record]; + } + return undefined; + }, + ]; + return config; + }, + + async extendPageData(pageData: PageIndexInfo, isProd: boolean) { + if (!isProd || !filter(pageData)) { + return; + } + + const { routePath, title, frontmatter } = pageData; + + // Get description from frontmatter or default + const description = frontmatter?.description as string | undefined; + + // Generate OG image path based on route + // Example: /guide/getting-started -> /og/guide/getting-started.png + const cleanPath = routePath.replace(/^\//, '').replace(/\/$/, ''); + const imagePath = cleanPath ? `og/${cleanPath}.png` : 'og/index.png'; + const imageUrl = `${siteUrl.replace(/\/$/, '')}/${imagePath}`; + + // Store template data + const templateData: OgImageTemplateData = { + title: title || 'Untitled', + description, + siteName: frontmatter?.siteName as string | undefined, + logo: frontmatter?.logo as string | undefined, + backgroundColor: frontmatter?.ogBackgroundColor as string | undefined, + textColor: frontmatter?.ogTextColor as string | undefined, + }; + + pageInfoMap.set(routePath, { + routePath, + imagePath, + imageUrl, + templateData, + }); + }, + + async afterBuild(config, isProd) { + if (!isProd || pageInfoMap.size === 0) { + return; + } + + // Get output directory + const distPathRoot = + typeof config.builderConfig?.output?.distPath === 'string' + ? config.builderConfig?.output?.distPath + : config.builderConfig?.output?.distPath?.root; + const configPath = config.outDir || distPathRoot; + const outputDir = isAbsolute(configPath || '') + ? configPath + : `./${configPath || 'doc_build'}`; + + // Generate all OG images + console.log(`Generating ${pageInfoMap.size} OG images...`); + + for (const pageInfo of pageInfoMap.values()) { + try { + // Generate the image + const imageBuffer = await generateOgImage( + pageInfo.templateData, + ogImage, + ); + + // Write to disk + const fullPath = join(outputDir!, pageInfo.imagePath); + await mkdir(dirname(fullPath), { recursive: true }); + await writeFile(fullPath, imageBuffer); + + console.log(`Generated OG image: ${pageInfo.imagePath}`); + } catch (error) { + console.error( + `Failed to generate OG image for ${pageInfo.routePath}:`, + error, + ); + } + } + + console.log('OG image generation complete!'); + }, + }; +} diff --git a/packages/plugin-og-image/src/template.ts b/packages/plugin-og-image/src/template.ts new file mode 100644 index 000000000..c08865306 --- /dev/null +++ b/packages/plugin-og-image/src/template.ts @@ -0,0 +1,95 @@ +import type { OgImageTemplateData } from './types'; + +/** + * Default OG image template + * Returns a React-like JSX structure that Satori can render + */ +export function defaultTemplate(data: OgImageTemplateData): any { + const { + title, + description, + siteName = 'Rspress', + backgroundColor = '#1a1a1a', + textColor = '#ffffff', + } = data; + + return { + type: 'div', + props: { + style: { + display: 'flex', + flexDirection: 'column', + width: '100%', + height: '100%', + backgroundColor, + padding: '80px', + fontFamily: 'system-ui, -apple-system, sans-serif', + position: 'relative', + }, + children: [ + // Main content + { + type: 'div', + props: { + style: { + display: 'flex', + flexDirection: 'column', + flex: 1, + justifyContent: 'center', + }, + children: [ + // Title + { + type: 'div', + props: { + style: { + fontSize: '72px', + fontWeight: 'bold', + color: textColor, + lineHeight: 1.2, + marginBottom: '24px', + maxWidth: '900px', + display: 'flex', + flexWrap: 'wrap', + }, + children: title, + }, + }, + // Description + description + ? { + type: 'div', + props: { + style: { + fontSize: '32px', + color: textColor, + opacity: 0.8, + lineHeight: 1.4, + maxWidth: '900px', + }, + children: description, + }, + } + : null, + ].filter(Boolean), + }, + }, + // Footer with site name + { + type: 'div', + props: { + style: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + fontSize: '28px', + color: textColor, + opacity: 0.7, + }, + children: siteName, + }, + }, + ], + }, + }; +} diff --git a/packages/plugin-og-image/src/types.ts b/packages/plugin-og-image/src/types.ts new file mode 100644 index 000000000..98e46b3eb --- /dev/null +++ b/packages/plugin-og-image/src/types.ts @@ -0,0 +1,70 @@ +import type { PageIndexInfo } from '@rspress/core'; + +/** + * OG image template data + */ +export interface OgImageTemplateData { + /** + * Page title + */ + title: string; + /** + * Page description + */ + description?: string; + /** + * Site name + */ + siteName?: string; + /** + * Site logo URL or path + */ + logo?: string; + /** + * Custom background color + */ + backgroundColor?: string; + /** + * Custom text color + */ + textColor?: string; +} + +/** + * OG image generation options + */ +export interface OgImageOptions { + /** + * Width of the generated image in pixels + * @default 1200 + */ + width?: number; + /** + * Height of the generated image in pixels + * @default 630 + */ + height?: number; + /** + * Custom template function to generate the SVG/JSX template + */ + template?: (data: OgImageTemplateData) => string | Promise; + /** + * Filter function to determine which pages should have OG images generated + * @default () => true (all pages) + */ + filter?: (pageData: PageIndexInfo) => boolean; +} + +/** + * Plugin options for pluginOgImage + */ +export interface PluginOgImageOptions { + /** + * Site URL (required for generating absolute URLs) + */ + siteUrl: string; + /** + * OG image generation options + */ + ogImage?: OgImageOptions; +} diff --git a/packages/plugin-og-image/tsconfig.json b/packages/plugin-og-image/tsconfig.json new file mode 100644 index 000000000..6435d4337 --- /dev/null +++ b/packages/plugin-og-image/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e1a584d4..a13a3319b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -509,6 +509,15 @@ importers: specifier: ^22.8.1 version: 22.10.2 + e2e/fixtures/plugin-og-image: + dependencies: + '@rspress/core': + specifier: workspace:* + version: link:../../../packages/core + '@rspress/plugin-og-image': + specifier: workspace:* + version: link:../../../packages/plugin-og-image + e2e/fixtures/plugin-playground: dependencies: '@rspress/core': @@ -1299,6 +1308,31 @@ importers: specifier: ^6.0.3 version: 6.0.3 + packages/plugin-og-image: + dependencies: + '@rspress/core': + specifier: workspace:^2.0.0-rc.1 + version: link:../core + satori: + specifier: ^0.11.2 + version: 0.11.3 + sharp: + specifier: ^0.33.5 + version: 0.33.5 + devDependencies: + '@microsoft/api-extractor': + specifier: ^7.55.0 + version: 7.55.0(@types/node@22.10.2) + '@rslib/core': + specifier: 0.17.2 + version: 0.17.2(@microsoft/api-extractor@7.55.0(@types/node@22.10.2))(typescript@5.8.2) + '@types/node': + specifier: ^22.8.1 + version: 22.10.2 + rsbuild-plugin-publint: + specifier: ^0.3.3 + version: 0.3.3(@rsbuild/core@1.6.6) + packages/plugin-playground: dependencies: '@mdx-js/mdx': @@ -2600,6 +2634,123 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@inquirer/external-editor@1.0.1': resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==} engines: {node: '>=18'} @@ -3370,6 +3521,11 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@shuding/opentype.js@1.4.0-beta.0': + resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==} + engines: {node: '>= 8.0.0'} + hasBin: true + '@sinclair/typebox@0.34.41': resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} @@ -3919,6 +4075,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@0.0.8: + resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} + engines: {node: '>= 0.4'} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -4003,6 +4163,9 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + caniuse-lite@1.0.30001751: resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} @@ -4103,6 +4266,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -4231,9 +4401,26 @@ packages: engines: {node: '>=20'} hasBin: true + css-background-parser@0.1.0: + resolution: {integrity: sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==} + + css-box-shadow@1.0.0-3: + resolution: {integrity: sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==} + + css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + css-gradient-parser@0.0.16: + resolution: {integrity: sha512-3O5QdqgFRUbXvK1x5INf1YkBz1UKSWqrd63vWsum8MNHDBYD5urm3QtxZbKU259OrEXNM26lP/MPY3d1IGkBgA==} + engines: {node: '>=16'} + css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -4322,6 +4509,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-newline@4.0.1: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4501,6 +4692,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -4617,6 +4811,9 @@ packages: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} + fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -4920,6 +5117,10 @@ packages: resolution: {integrity: sha512-kHDUzStHy3OIysI4sxJsfQ7KNrRKEudT7YgWRwFMFVKN7/0CINF/l3prqN8eLgo7k18Vvfz+xRcEhR/mUVlCaQ==} hasBin: true + hex-rgb@4.3.0: + resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} + engines: {node: '>=6'} + highlight.js@11.8.0: resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==} engines: {node: '>=12.0.0'} @@ -5033,6 +5234,9 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.4: + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -5226,6 +5430,9 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} + linebreak@1.1.0: + resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -5865,6 +6072,9 @@ packages: package-manager-detector@1.2.0: resolution: {integrity: sha512-PutJepsOtsqVfUsxCzgTTpyXmiAgvKptIgY4th5eq5UXXFhj5PxfQ9hnGkypMeovpAvVshFRItoFHYO18TCOqA==} + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -5873,6 +6083,9 @@ packages: resolution: {integrity: sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==} engines: {node: '>=8'} + parse-css-color@0.2.1: + resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==} + parse-entities@4.0.1: resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} @@ -6522,6 +6735,10 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + satori@0.11.3: + resolution: {integrity: sha512-Wg7sls0iYAEETzi9YYcY16QVIqXjZT06XjkwondC5CGhw1mhmgKBCub8cCmkxdl/naXXQD+m29CFgn8pwtYCnA==} + engines: {node: '>=16'} + sax@1.3.0: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} @@ -6576,6 +6793,10 @@ packages: resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==} engines: {node: '>=11.0'} + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -6600,6 +6821,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.4: + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -6718,6 +6942,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string.prototype.codepointat@0.2.1: + resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -6871,6 +7098,9 @@ packages: resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} engines: {node: '>=18'} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -7004,6 +7234,9 @@ packages: unhead@2.0.19: resolution: {integrity: sha512-gEEjkV11Aj+rBnY6wnRfsFtF2RxKOLaPN4i+Gx3UhBxnszvV6ApSNZbGk7WKyy/lErQ6ekPN63qdFL7sa1leow==} + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -7326,6 +7559,9 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + yoga-wasm-web@0.3.3: + resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} + zod@4.1.11: resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} @@ -8261,6 +8497,81 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.5.0 + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + '@inquirer/external-editor@1.0.1(@types/node@22.10.2)': dependencies: chardet: 2.1.0 @@ -9098,6 +9409,11 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@shuding/opentype.js@1.4.0-beta.0': + dependencies: + fflate: 0.7.4 + string.prototype.codepointat: 0.2.1 + '@sinclair/typebox@0.34.41': {} '@sindresorhus/merge-streams@2.3.0': {} @@ -9711,6 +10027,8 @@ snapshots: balanced-match@1.0.2: {} + base64-js@0.0.8: {} + base64-js@1.5.1: {} base64id@2.0.0: {} @@ -9788,6 +10106,8 @@ snapshots: camelcase@6.3.0: {} + camelize@1.0.1: {} + caniuse-lite@1.0.30001751: {} ccount@2.0.1: {} @@ -9889,6 +10209,16 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.4 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + colorette@2.0.20: {} colorjs.io@0.5.2: {} @@ -10050,6 +10380,14 @@ snapshots: semver: 7.7.3 tinyglobby: 0.2.15 + css-background-parser@0.1.0: {} + + css-box-shadow@1.0.0-3: {} + + css-color-keywords@1.0.0: {} + + css-gradient-parser@0.0.16: {} + css-select@5.1.0: dependencies: boolbase: 1.0.0 @@ -10058,6 +10396,12 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 @@ -10118,6 +10462,8 @@ snapshots: detect-libc@1.0.3: optional: true + detect-libc@2.1.2: {} + detect-newline@4.0.1: {} devlop@1.1.0: @@ -10363,6 +10709,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@5.0.0: {} @@ -10473,6 +10821,8 @@ snapshots: dependencies: xml-js: 1.6.11 + fflate@0.7.4: {} + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -10908,6 +11258,8 @@ snapshots: heading-case@1.0.3: {} + hex-rgb@4.3.0: {} + highlight.js@11.8.0: {} hookable@5.5.3: {} @@ -11004,6 +11356,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.4: {} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 @@ -11159,6 +11513,11 @@ snapshots: lilconfig@3.1.3: {} + linebreak@1.1.0: + dependencies: + base64-js: 0.0.8 + unicode-trie: 2.0.0 + lines-and-columns@1.2.4: {} lines-and-columns@2.0.3: {} @@ -12285,6 +12644,8 @@ snapshots: package-manager-detector@1.2.0: {} + pako@0.2.9: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -12293,6 +12654,11 @@ snapshots: dependencies: callsites: 3.1.0 + parse-css-color@0.2.1: + dependencies: + color-name: 1.1.4 + hex-rgb: 4.3.0 + parse-entities@4.0.1: dependencies: '@types/unist': 2.0.7 @@ -12984,6 +13350,20 @@ snapshots: '@parcel/watcher': 2.5.1 optional: true + satori@0.11.3: + dependencies: + '@shuding/opentype.js': 1.4.0-beta.0 + css-background-parser: 0.1.0 + css-box-shadow: 1.0.0-3 + css-gradient-parser: 0.0.16 + css-to-react-native: 3.2.0 + emoji-regex: 10.4.0 + escape-html: 1.0.3 + linebreak: 1.1.0 + parse-css-color: 0.2.1 + postcss-value-parser: 4.2.0 + yoga-wasm-web: 0.3.3 + sax@1.3.0: {} scheduler@0.27.0: {} @@ -13033,6 +13413,32 @@ snapshots: is-plain-object: 2.0.4 is-primitive: 3.0.1 + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -13063,6 +13469,10 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.4: + dependencies: + is-arrayish: 0.3.4 + slash@3.0.0: {} slash@5.1.0: {} @@ -13209,6 +13619,8 @@ snapshots: get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 + string.prototype.codepointat@0.2.1: {} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -13377,6 +13789,8 @@ snapshots: throttleit@2.1.0: {} + tiny-inflate@1.0.3: {} + tinybench@2.9.0: {} tinyexec@0.3.1: {} @@ -13496,6 +13910,11 @@ snapshots: dependencies: hookable: 5.5.3 + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + unicorn-magic@0.1.0: {} unified-lint-rule@3.0.1: @@ -13881,6 +14300,8 @@ snapshots: yocto-queue@1.0.0: {} + yoga-wasm-web@0.3.3: {} + zod@4.1.11: {} zwitch@2.0.4: {} diff --git a/scripts/dictionary.txt b/scripts/dictionary.txt index f0d6ccd99..5fc217e44 100644 --- a/scripts/dictionary.txt +++ b/scripts/dictionary.txt @@ -1,4 +1,23 @@ # Custom Dictionary Words +Bluch +Bytedance +Chunktmp +Français +Geass +Kinsta +Lamperouge +Lelouch +MDSSG +Mdxjs +Pipeable +Println +Publint +Qrcode +Rustify +SSGMD +Sizefor +Twoslasher +Unhead algoliasearch alinks analyse @@ -12,15 +31,12 @@ autohide autoreleasepool bilibili biomejs -Bluch brotli browserslistrc bundleless bunx -Bytedance caniuse chunkhash -Chunktmp classname codesandbox compat @@ -34,6 +50,7 @@ craco crossorigin datauri deepmerge +dejavu directorytest docgen docsite @@ -45,11 +62,9 @@ flexbox flexbugs flexsearch fnames -Français frontmatter fullhash fumadocs -Geass htmr iconify icss @@ -66,18 +81,13 @@ jsesc jsxs juejin katex -Kinsta koppers -Lamperouge -Lelouch lightningcss llms longpaths manypkg mattcompiles mdast -MDSSG -Mdxjs mdxrs menlo metastring @@ -101,7 +111,6 @@ pathinfo perfetto picocolors pinkpurple -Pipeable pjpeg pluggable pmmmwh @@ -112,10 +121,7 @@ prebundle preflights prefresh preprocessors -Println -Publint pxtorem -Qrcode rebranded rehype rsbuild @@ -128,16 +134,14 @@ rspack rspress rstack rstest -Rustify +satori selfsign shiki shikijs sirv -Sizefor sokra speedscope srcset -SSGMD stacktracey styl subdir @@ -156,8 +160,6 @@ treeshaking tsbuildinfo tsdoc twoslash -Twoslasher -Unhead unocss unpatch unplugin