import { defineConfig, HeadConfig } from 'vitepress'; import githubLinksPlugin from './plugins/github-links'; import { readFileSync } from 'fs'; import { resolve } from 'path'; import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'; import { groupIconMdPlugin, groupIconVitePlugin, localIconLoader } from 'vitepress-plugin-group-icons'; import { team } from './team.ts'; import { adopters } from './adopters.ts'; import { taskDescription, taskName, ogUrl, ogImage } from './meta.ts'; import { fileURLToPath, URL } from 'node:url'; import llmstxt from 'vitepress-plugin-llms'; const version = readFileSync( resolve(__dirname, '../../internal/version/version.txt'), 'utf8' ).trim(); const urlVersion = process.env.NODE_ENV === 'development' ? { current: 'https://taskfile.dev/', next: 'http://localhost:3002/' } : { current: 'https://taskfile.dev/', next: 'https://next.taskfile.dev/' }; // https://vitepress.dev/reference/site-config export default defineConfig({ title: taskName, description: taskDescription, lang: 'en-US', head: [ // Favicon ICO for legacy browsers (auto-discovery) ['link', { rel: 'icon', href: '/favicon.ico', sizes: '48x48' }], // Favicon SVG for modern browsers (scalable) ['link', { rel: 'icon', href: '/img/logo.svg', type: 'image/svg+xml' }], // Apple Touch Icon for iOS devices ['link', { rel: 'apple-touch-icon', href: '/img/logo.png' }], [ 'meta', { name: 'author', content: `${team.map((c) => c.name).join(', ')}` } ], // Open Graph ['meta', { property: 'og:type', content: 'website' }], ['meta', { property: 'og:site_name', content: 'Task' }], ['meta', { property: 'og:image', content: ogImage }], // Twitter Card ['meta', { name: 'twitter:card', content: 'summary_large_image' }], ['meta', { name: 'twitter:site', content: '@taskfiledev' }], ['meta', { name: 'twitter:image', content: ogImage }], [ 'meta', { name: 'keywords', content: 'task runner, build tool, taskfile, yaml build tool, go task runner, make alternative, cross-platform build tool, makefile alternative, automation tool, ci cd pipeline, developer productivity, build automation, command line tool, go binary, yaml configuration' } ], [ "script", { defer: "", src: "https://u.taskfile.dev/script.js", "data-website-id": "084030b0-0e3f-4891-8d2a-0c12c40f5933" } ], [ "script", { type: "application/ld+json" }, JSON.stringify({ "@context": "https://schema.org", "@type": "WebSite", "name": "Task", "url": "https://taskfile.dev/" }) ] ], transformHead({ pageData }) { const head: HeadConfig[] = [] // Canonical URL dynamique const canonicalUrl = `https://taskfile.dev/${pageData.relativePath .replace(/\.md$/, '') .replace(/index$/, '')}` head.push(['link', { rel: 'canonical', href: canonicalUrl }]) // Dynamic Open Graph and Twitter meta tags const isHome = pageData.relativePath === 'index.md'; var pageTitle = pageData.frontmatter.title || pageData.title || taskName; if (!isHome) { pageTitle = `${pageTitle} | ${taskName}`; } const pageDescription = pageData.frontmatter.description || pageData.description || taskDescription head.push(['meta', { property: 'og:title', content: pageTitle }]) head.push(['meta', { property: 'og:description', content: pageDescription }]) head.push(['meta', { property: 'og:url', content: canonicalUrl }]) head.push(['meta', { name: 'twitter:title', content: pageTitle }]) head.push(['meta', { name: 'twitter:description', content: pageDescription }]) // Noindex pour 404 if (pageData.relativePath === '404.md') { head.push(['meta', { name: 'robots', content: 'noindex, nofollow' }]) } // Structured data for the adopters carousel on the homepage: an ItemList // of Organization entities so search engines can surface Task's adopters // directly in rich results. if (isHome) { head.push([ 'script', { type: 'application/ld+json' }, JSON.stringify({ '@context': 'https://schema.org', '@type': 'ItemList', name: 'Organizations and projects using Task', itemListOrder: 'https://schema.org/ItemListUnordered', numberOfItems: adopters.length, itemListElement: adopters.map((a, i) => ({ '@type': 'ListItem', position: i + 1, item: { '@type': 'Organization', name: a.name, url: a.url, logo: a.img, sameAs: [a.url] } })) }) ]) } // On the /adopters page, emit CollectionPage + ItemList (richer than the // homepage snippet because it targets this specific URL) and FAQPage for // the question block at the bottom of the page. Kept in sync by hand with // components/Adopters.vue. if (pageData.relativePath === 'adopters.md') { head.push([ 'script', { type: 'application/ld+json' }, JSON.stringify({ '@context': 'https://schema.org', '@type': 'CollectionPage', name: 'Who uses Task', url: 'https://taskfile.dev/adopters', description: 'Organizations and open source projects that use Task as their build and release runner.', mainEntity: { '@type': 'ItemList', numberOfItems: adopters.length, itemListElement: adopters.map((a, i) => ({ '@type': 'ListItem', position: i + 1, item: { '@type': 'Organization', name: a.name, url: a.url, logo: a.img, description: a.description, sameAs: [a.url] } })) } }) ]) head.push([ 'script', { type: 'application/ld+json' }, JSON.stringify({ '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: [ { '@type': 'Question', name: 'Is Task production-ready?', acceptedAnswer: { '@type': 'Answer', text: 'Yes. Task ships as a single static binary, has been in wide production use since 2018, and powers the release workflows of projects with millions of downloads including Arduino CLI, GoReleaser, FerretDB, and Gogs.' } }, { '@type': 'Question', name: 'Who uses Task in enterprise?', acceptedAnswer: { '@type': 'Answer', text: 'Docker, Vercel, HashiCorp, Microsoft (Azure Sentinel), Google Cloud, AWS, and Anthropic are among the organizations that ship code with a Taskfile.yml. Task is also embedded end-to-end in Arduino’s developer tooling stack across more than 70 repositories.' } }, { '@type': 'Question', name: 'How is Task different from Make?', acceptedAnswer: { '@type': 'Answer', text: 'Task uses plain YAML instead of Make’s tab-sensitive syntax, runs identically on Linux, macOS, and Windows, and provides built-in caching based on file fingerprints. It also comes with an ecosystem of editor and CI integrations that Make lacks by default.' } }, { '@type': 'Question', name: 'Where can I find real-world Taskfile examples?', acceptedAnswer: { '@type': 'Answer', text: 'Every adopter listed above links directly to a public repository containing a production Taskfile.yml. Browsing those is the fastest way to see Task used in real codebases at different scales.' } } ] }) ]) } return head }, srcDir: 'src', cleanUrls: true, markdown: { config: (md) => { md.use(githubLinksPlugin, { baseUrl: 'https://github.com', repo: 'go-task/task' }); md.use(tabsMarkdownPlugin); md.use(groupIconMdPlugin); } }, vite: { plugins: [ llmstxt({ ignoreFiles: [ 'index.md', 'team.md', 'donate.md', 'docs/styleguide.md', 'docs/contributing.md', 'docs/releasing.md', 'docs/changelog.md', 'blog/*' ] }), groupIconVitePlugin({ customIcon: { '.taskrc.yml': localIconLoader( import.meta.url, './theme/icons/task.svg' ), 'Taskfile.yml': localIconLoader( import.meta.url, './theme/icons/task.svg' ) } }) ], resolve: { alias: [ { find: /^.*\/VPTeamMembersItem\.vue$/, replacement: fileURLToPath( new URL('./components/VPTeamMembersItem.vue', import.meta.url) ) } ] } }, themeConfig: { logo: '/img/logo.svg', carbonAds: { code: 'CESI65QJ', placement: 'taskfiledev' }, search: { provider: 'algolia', options: { appId: '7IZIJ13AI7', apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc', indexName: 'taskfile' } }, nav: [ { text: 'Home', link: '/' }, { text: 'Docs', link: '/docs/guide', activeMatch: '^/docs' }, { text: 'Blog', link: '/blog', activeMatch: '^/blog' }, { text: 'Donate', link: '/donate' }, { text: 'Team', link: '/team' }, { text: process.env.NODE_ENV === 'development' ? 'Next' : `v${version}`, items: [ { items: [ { text: `v${version}`, link: urlVersion.current }, { text: 'Next', link: urlVersion.next } ] } ] } ], sidebar: { '/blog/': [ { text: '2026', collapsed: false, items: [ { text: 'go tool task', link: '/blog/go-tool-task' }, { text: 'New "if:" Control and Variable Prompt', link: '/blog/if-and-variable-prompt' } ] }, { text: '2025', collapsed: false, items: [ { text: 'Built-in Core Utilities', link: '/blog/windows-core-utils' } ] }, { text: '2024', collapsed: false, items: [ { text: 'Any Variables', link: '/blog/any-variables' } ] }, { text: '2023', collapsed: false, items: [ { text: 'Introducing Experiments', link: '/blog/task-in-2023' } ] } ], '/': [ { text: 'Installation', link: '/docs/installation' }, { text: 'Getting Started', link: '/docs/getting-started' }, { text: 'Guide', link: '/docs/guide' }, { text: 'Remote Taskfiles', link: '/docs/remote-taskfiles' }, { text: 'Reference', collapsed: true, items: [ { text: 'Taskfile Schema', link: '/docs/reference/schema' }, { text: 'Environment', link: '/docs/reference/environment' }, { text: 'Configuration', link: '/docs/reference/config' }, { text: 'CLI', link: '/docs/reference/cli' }, { text: 'Templating', link: '/docs/reference/templating' }, { text: 'Package API', link: '/docs/reference/package' } ] }, { text: 'Experiments', collapsed: true, link: '/docs/experiments/', items: [ { text: 'Env Precedence (#1038)', link: '/docs/experiments/env-precedence' }, { text: 'Gentle Force (#1200)', link: '/docs/experiments/gentle-force' }, { text: 'Remote Taskfiles (#1317)', link: '/docs/experiments/remote-taskfiles' } ] }, { text: 'Deprecations', collapsed: true, link: '/docs/deprecations/', items: [ { text: 'Completion Scripts', link: '/docs/deprecations/completion-scripts' }, { text: 'Template Functions', link: '/docs/deprecations/template-functions' }, { text: 'Version 2 Schema (#1197)', link: '/docs/deprecations/version-2-schema' } ] }, { text: 'Taskfile Versions', link: '/docs/taskfile-versions' }, { text: 'Integrations', link: '/docs/integrations' }, { text: 'Community', link: '/docs/community' }, { text: 'Style Guide', link: '/docs/styleguide' }, { text: 'Contributing', link: '/docs/contributing' }, { text: 'Releasing', link: '/docs/releasing' }, { text: 'Security', collapsed: true, link: '/docs/security/', items: [ { text: 'Incident Response Plan', link: '/docs/security/incident-response-plan' }, { text: 'Threat Model', link: '/docs/security/threat-model' } ] }, { text: 'Changelog', link: '/docs/changelog' }, { text: 'FAQ', link: '/docs/faq' } ], // Hacky to disable sidebar for these pages '/donate': [], '/team': [], '/adopters': [] }, socialLinks: [ { icon: 'github', link: 'https://github.com/go-task/task' }, { icon: 'discord', link: 'https://discord.gg/6TY36E39UK' }, { icon: 'x', link: 'https://twitter.com/taskfiledev' }, { icon: 'bluesky', link: 'https://bsky.app/profile/taskfile.dev' }, { icon: 'mastodon', link: 'https://fosstodon.org/@task' } ], editLink: { text: 'Edit this page on GitHub', pattern: 'https://github.com/go-task/task/edit/main/website/src/:path' }, footer: { message: 'Built with Netlify' } }, sitemap: { hostname: 'https://taskfile.dev', transformItems: (items) => { return items.map((item) => ({ ...item, lastmod: new Date().toISOString() })); } } });