// @ts-check import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import { rehypeHeadingIds } from '@astrojs/markdown-remark'; import rehypeAutolinkHeadings from 'rehype-autolink-headings'; import locales from './locales.json'; import starlightLinksValidator from 'starlight-links-validator'; import starlightSidebarTopics from 'starlight-sidebar-topics'; import starlightBlog from 'starlight-blog'; import serviceWorker from 'astrojs-service-worker'; import astroD2 from 'astro-d2'; import path from 'path'; import { fileURLToPath } from 'url'; import lunaria from '@lunariajs/starlight'; import { readFileSync } from 'fs'; const authors = { nothingismagick: { name: 'Daniel Thompson-Yvetot', title: 'Tauri Co-Founder', picture: '/authors/nothingismagick.jpeg', }, lucasfernog: { name: 'Lucas Nogueira', title: 'Tauri Co-Founder', picture: '/authors/lucasfernog.jpeg', }, beanow: { name: 'Robin van Boven', title: 'Tauri Board Director', picture: '/authors/Beanow.png', }, jbolda: { name: 'Jacob Bolda', title: 'Tauri Board Director', picture: '/authors/jbolda.jpeg', }, lorenzolewis: { name: 'Lorenzo Lewis', title: 'Tauri Community Lead', picture: '/authors/lorenzolewis.png', }, tweidinger: { name: 'Tillmann Weidinger', title: 'Tauri Security', picture: '/authors/tweidinger.png', }, amrbashir: { name: 'Amr Bashir', title: 'Tauri Development', picture: '/authors/amrbashir.png', }, wusyong: { name: 'Wu Yu Wei', title: 'Tauri Development Lead', picture: '/authors/wusyong.png', }, chip: { name: 'Chip Reed', title: 'Tauri Security', picture: '/authors/chip.png', }, tony: { name: 'Tony', title: 'Tauri Development', picture: '/authors/tony.jpeg', }, }; const site = 'https://v2.tauri.app'; // https://astro.build/config export default defineConfig({ site, trailingSlash: 'always', integrations: [ starlight({ plugins: [ starlightBlog({ authors }), starlightSidebarTopics( [ { label: { en: 'Guides', 'zh-CN': '指引', es: 'Guías', }, id: 'guides', link: '/start/', icon: 'open-book', items: [ { label: 'Quick Start', translations: { 'zh-CN': '快速开始', es: 'Guía rápida', }, collapsed: true, items: [ 'start', 'start/prerequisites', 'start/create-project', 'start/project-structure', // { // label: 'What is Tauri?', // // translations: { // // 'zh-CN': '什么是 Tauri?', // // es: '¿Qué es Tauri?', // // }, // link: '/start/', // }, // { // label: 'Prerequisites', // // translations: { // // 'zh-CN': '前置条件', // // es: 'Requisitos previos', // // }, // link: '/start/prerequisites/', // }, // { // label: 'Create a Project', // // translations: { // // 'zh-CN': '创建项目', // // es: 'Crea un proyecto', // // }, // link: '/start/create-project/', // }, { label: 'Frontend Configuration', translations: { 'zh-CN': '前端配置', es: 'Configuración del frontend', }, collapsed: true, autogenerate: { directory: 'start/frontend' }, }, { label: 'Upgrade & Migrate', translations: { 'zh-CN': '升级和迁移', es: 'Actualizar y migrar', }, collapsed: true, autogenerate: { directory: 'start/migrate' }, }, ], }, { label: 'Core Concepts', translations: { 'zh-CN': '核心概念', es: 'Conceptos básicos', }, collapsed: true, autogenerate: { directory: 'concept' }, }, { label: 'Security', translations: { 'zh-CN': '安全', es: 'Seguridad', }, collapsed: true, autogenerate: { directory: 'security' }, }, { label: 'Develop', translations: { 'zh-CN': '开发', es: 'Desarrollo', }, collapsed: true, items: [ 'develop', 'develop/configuration-files', 'develop/calling-rust', 'develop/calling-frontend', 'develop/resources', 'develop/sidecar', 'develop/state-management', 'develop/updating-dependencies', 'develop/icons', { label: 'Debug', collapsed: true, autogenerate: { directory: 'develop/Debug' }, }, { label: 'Plugins', collapsed: true, autogenerate: { directory: 'develop/Plugins' }, }, { label: 'Tests', collapsed: true, autogenerate: { directory: 'develop/Tests' }, }, ], }, { label: 'Distribute', translations: { 'zh-CN': '分发', es: 'Distribuir', }, collapsed: true, autogenerate: { directory: 'distribute' }, }, { label: 'Learn', translations: { 'zh-CN': '学习', es: 'Aprende', }, collapsed: true, autogenerate: { directory: 'learn' }, }, { label: 'Plugins', translations: { 'zh-CN': '插件', es: 'Plugins', }, collapsed: true, autogenerate: { directory: 'plugin' }, }, { label: 'About', translations: { 'zh-CN': '关于', es: 'Acerca de', }, collapsed: true, autogenerate: { directory: 'about' }, }, ], }, { label: { en: 'References', 'zh-CN': '参考', es: 'Referencias' }, id: 'references', link: '/reference/cli', icon: 'document', items: [ { label: 'Command Line Interface', translations: { 'zh-CN': '命令行接口(CLI)', es: 'Interfaz de línea de comandos', }, link: '/reference/cli/', }, { label: 'Security', translations: { 'zh-CN': '安全', es: 'Seguridad', }, collapsed: true, autogenerate: { directory: 'reference/acl' }, }, { label: 'Configuration', translations: { 'zh-CN': '配置', es: 'Configuración', }, link: '/reference/config/', }, { label: 'Environment Variables', translations: { 'zh-CN': '环境变量', es: 'Variables de entorno', }, link: '/reference/environment-variables/', }, { label: 'Webview Versions', translations: { 'zh-CN': 'Webview 版本', es: 'Versiones de Webview', }, link: '/reference/webview-versions/', }, { label: 'Releases', translations: { 'zh-CN': '发行版', es: 'Lanzamientos', }, collapsed: true, autogenerate: { directory: 'release' }, }, { label: 'JavaScript', collapsed: true, autogenerate: { directory: 'reference/javascript' }, }, { label: 'Rust (docs.rs)', link: 'https://docs.rs/tauri/~2/', }, ], }, { label: 'Blog', id: 'blog', link: '/blog/', icon: 'pen', // Empty item to instruct it that is is local files, not an external link // this is actually filled in through the topics dir for `blog` below items: [], }, ], { exclude: ['**/_*/**'], topics: { blog: ['/blog', '/blog/*', '/blog/**/*', '**/blog', '**/blog/*', '**/blog/**/*'], }, } ), starlightLinksValidator({ errorOnFallbackPages: false, errorOnRelativeLinks: false, exclude: ['/plugin/*/#default-permission', '/plugin/*/#permission-table'], }), lunaria({ configPath: './lunaria.config.json', route: '/contribute/translate-status' }), ], title: 'Tauri', description: 'The cross-platform app building toolkit', logo: { dark: './src/assets/logo.svg', light: './src/assets/logo_light.svg', replacesTitle: true, }, social: [ { icon: 'github', label: 'GitHub', href: 'https://github.com/tauri-apps/tauri' }, { icon: 'discord', label: 'Discord', href: 'https://discord.com/invite/tauri' }, { icon: 'twitter', label: 'Twitter', href: 'https://twitter.com/TauriApps' }, { icon: 'blueSky', label: 'Bluesky', href: 'https://bsky.app/profile/tauri.app' }, { icon: 'mastodon', label: 'Mastodon', href: 'https://fosstodon.org/@TauriApps' }, { icon: 'rss', label: 'RSS', href: `${site}/rss` }, ], components: { Header: './src/components/overrides/Header.astro', Footer: 'src/components/overrides/Footer.astro', ThemeSelect: 'src/components/overrides/ThemeSelect.astro', }, head: [ { tag: 'meta', attrs: { property: 'og:image', content: site + '/og.png?v=1' }, }, { tag: 'meta', attrs: { property: 'twitter:image', content: site + '/og.png?v=1' }, }, { tag: 'script', attrs: { src: '/navigate.js', }, }, { tag: 'link', attrs: { rel: 'manifest', href: '/manifest.json', }, }, { tag: 'meta', attrs: { name: 'theme-color', content: '#181818' }, }, ], editLink: { baseUrl: process.env.NODE_ENV === 'development' ? `vscode://file/${path.dirname(fileURLToPath(import.meta.url))}` : 'https://github.com/tauri-apps/tauri-docs/edit/v2', }, customCss: ['./src/styles/custom.scss'], expressiveCode: { styleOverrides: { borderRadius: '0.5rem' }, }, locales, lastUpdated: true, }), astroD2({ skipGeneration: process.env.CONTEXT !== 'd2', theme: { default: '105', }, }), serviceWorker({ workbox: { cleanupOutdatedCaches: true, clientsClaim: true, inlineWorkboxRuntime: true, skipWaiting: true, globIgnores: ['**_redirects**', '**_headers**'], globPatterns: ['**/*.js', '**/*.css'], runtimeCaching: [ { urlPattern: new RegExp('.*'), handler: 'CacheFirst', options: { cacheName: 'tauri-runtime', expiration: { maxAgeSeconds: 30 * 60, // 30 minutes }, }, }, ], }, }), ], image: { domains: ['tauri.app', 'images.opencollective.com', 'avatars.githubusercontent.com'], }, markdown: { shikiConfig: { langs: ['powershell', 'ts', 'rust', 'bash', 'json', 'toml', 'html', 'js'], }, rehypePlugins: [ rehypeHeadingIds, [ rehypeAutolinkHeadings, { behavior: 'wrap', properties: { ariaHidden: true, tabIndex: -1, class: 'heading-link' }, }, ], ], }, redirects: { // Old blog url schema redirects '/blog/2022/06/19/tauri-1-0': '/blog/tauri-1-0', '/blog/tauri_1_0': '/blog/tauri-1-0', '/blog/2022/07/12/tauri-programme-turns-1-and-board-elections': '/blog/tauri-programme-turns-1-and-board-elections', '/blog/2022/09/15/tauri-1-1': '/blog/tauri-1-1', '/blog/2022/09/19/tauri-egui-0-1': '/blog/tauri-egui-0-1', '/blog/2022/11/18/tauri-1-2': '/blog/tauri-1-2', '/blog/2022/12/09/tauri-mobile-alpha': '/blog/tauri-mobile-alpha', '/blog/2023/02/03/tauri-2-0-0-alpha-3': '/blog/tauri-2-0-0-alpha-3', '/blog/2023/02/09/tauri-community-growth-and-feedback': '/blog/tauri-community-growth-and-feedback', '/blog/2023/03/01/create-tauri-app-version-3-released': '/blog/create-tauri-app-version-3-released', '/blog/2023/03/20/tauri-2-0-0-alpha-4': '/blog/tauri-2-0-0-alpha-4', '/blog/2023/05/03/tauri-1-3': '/blog/tauri-1-3', '/blog/2023/06/14/tauri-1-4': '/blog/tauri-1-4', '/blog/2023/06/15/tauri-board-elections-and-governance-updates': '/blog/tauri-board-elections-and-governance-updates', '/about/intro': '/about/philosophy', // v1 /guides/debugging -> /guides/debug ...i18nRedirect('/v1/guides/debugging/application', '/guides/debug/application'), ...i18nRedirect('/v1/guides/debugging/vs-code', '/guides/debug/vs-code'), ...i18nRedirect('/v1/guides/debugging/rustrover', '/guides/debug/rustrover'), // v1 /guides/development -> /guides/develop ...i18nRedirect( '/v1/guides/development/development-cycle', '/guides/develop/development-cycle' ), ...i18nRedirect( '/v1/guides/development/updating-dependencies', '/guides/develop/updating-dependencies' ), // v1 /guides/distribution -> /guides/distribute ...i18nRedirect('/v1/guides/distribution/publishing', '/guides/distribute/'), ...i18nRedirect('/v1/guides/distribution/updater', '/plugin/updater/'), // v1 /guides/testing -> /guides/test ...i18nRedirect('/v1/guides/testing/mocking', '/guides/test/mocking'), ...i18nRedirect('/v1/guides/testing/webdriver/ci', '/guides/test/webdriver/ci'), ...i18nRedirect('/v1/guides/testing/webdriver/introduction', '/guides/test/webdriver/'), ...i18nRedirect( '/v1/guides/testing/webdriver/example/setup', '/guides/test/webdriver/example/' ), ...i18nRedirect( '/v1/guides/testing/webdriver/example/selenium', '/guides/test/webdriver/example/selenium' ), ...i18nRedirect( '/v1/guides/testing/webdriver/example/webdriverio', '/test/webdriver/example/webdriverio' ), // v1 /references ...i18nRedirect('/v1/references', '/concepts'), ...i18nRedirect('/v1/reference/architecture', '/concepts/architecture'), ...i18nRedirect('/v1/reference/architecture/process-model', '/concepts/process-model'), ...i18nRedirect('/v1/reference/architecture/security', '/concepts/tauri-security'), ...i18nRedirect( '/v1/reference/architecture/inter-process-communication', '/concepts/inter-process-communication' ), ...i18nRedirect( '/v1/reference/architecture/inter-process-communication/brownfield', '/concepts/inter-process-communication/brownfield' ), ...i18nRedirect( '/v1/reference/architecture/inter-process-communication/isolation', '/concepts/inter-process-communication/isolation' ), ...i18nRedirect('/v1/reference/security', '/concepts/development-security'), ...i18nRedirect('/v1/reference/configuration-files', '/reference/configuration-files'), ...i18nRedirect('/v1/reference/webview-versions', '/reference/webview-versions'), // Decommissioned locales -> refer to /public/_redirects file // '/ko/[...slug]': '/[...slug]', // '/it/[...slug]': '/[...slug]', }, server: { headers: readHeaders(), }, }); // Generates a redirect for each locale. /** * @param {string} from * @param {string} to */ function i18nRedirect(from, to) { const routes = {}; Object.keys(locales).map((locale) => locale === 'root' ? (routes[from] = to) : (routes[`/${locale}/${from.replaceAll(/^\/*/g, '')}`] = `/${locale}/${to.replaceAll( /^\/*/g, '' )}`) ); return routes; } // Read the HTTP header file in `public/_headers` function readHeaders() { const header_file = readFileSync('public/_headers', { encoding: 'utf8' }) .split('\n') .filter(Boolean); const headers = {}; for (const line of header_file) { const [key, val] = line.trim().split(/\s*:\s*(.+)/); if (key != undefined && val != undefined) { headers[key] = val.toString(); } } return headers; }