Files
paseo/packages/website/vite.config.ts
Mohamed Boudra b5e9747e2a Add the reviewed plugin registry and the unlinked plugin directory pages (#5986)
* feat(plugins): install reviewed artifacts from open registries

* fix(website): build protocol before deployment checks

* fix(website): make plugin pages discoverable to search engines

Resolve missing registry entries in route loaders to return HTTP 404. Serve a registry-backed sitemap so newly listed plugins and authors are discoverable without rebuilding the website.

* feat(website): keep plugin directory unlinked for coordinated launch

Preserve direct routes and SEO while retaining the partner directory link. Cover nested Git artifacts through registry installation and pinned website commands.

* fix(website): link plugin cards by registry owner and stub the clipboard in e2e

Plugin and author URLs come from the owner segment of the plugin ID, not the author's GitHub login, so a listed plugin always resolves. The Playwright spec records clipboard writes instead of granting a permission WebKit does not have.
2026-10-03 16:30:13 +01:00

136 lines
3.6 KiB
TypeScript

import fs from "node:fs";
import path from "node:path";
import { defineConfig, type UserConfig } from "vite";
import tsConfigPaths from "vite-tsconfig-paths";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
const repoRoot = path.resolve(__dirname, "../..");
const siteHost = "https://paseo.sh";
function discoverDocsRoutes(): string[] {
const docsDir = path.join(repoRoot, "public-docs");
if (!fs.existsSync(docsDir)) return ["/docs"];
const routes = new Set<string>(["/docs"]);
const walk = (dir: string) => {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(full);
continue;
}
if (!entry.name.endsWith(".md")) continue;
const rel = path
.relative(docsDir, full)
.replace(/\.md$/, "")
.replace(/\/index$/, "");
if (rel === "index" || rel === "") continue;
routes.add(`/docs/${rel.split(path.sep).join("/")}`);
}
};
walk(docsDir);
return [...routes].sort();
}
function discoverAgentRoutes(): string[] {
const routesDir = path.join(__dirname, "src/routes");
const reserved = new Set([
"__root",
"agents",
"blog",
"changelog",
"docs",
"download",
"hub",
"index",
"sponsor",
"privacy",
"terms",
]);
return fs
.readdirSync(routesDir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(".tsx"))
.map((entry) => entry.name.replace(/\.tsx$/, ""))
.filter((name) => !reserved.has(name))
.sort()
.map((slug) => `/${slug}`);
}
function discoverAlternativeRoutes(): string[] {
const alternativesDir = path.join(__dirname, "src/routes/alternatives");
if (!fs.existsSync(alternativesDir)) return [];
const slugs = fs
.readdirSync(alternativesDir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(".tsx"))
.map((entry) => entry.name.replace(/\.tsx$/, ""))
.sort();
return ["/alternatives", ...slugs.map((slug) => `/alternatives/${slug}`)];
}
function discoverBlogRoutes(): string[] {
const postsDir = path.join(__dirname, "posts");
if (!fs.existsSync(postsDir)) return ["/blog"];
const slugs = fs
.readdirSync(postsDir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
.map((entry) => entry.name.replace(/\.md$/, ""))
.sort();
return ["/blog", ...slugs.map((slug) => `/blog/${slug}`)];
}
// Author and detail URLs are served by /sitemap-plugins.xml as the registry changes.
function discoverPluginRoutes(): string[] {
return ["/plugins"];
}
const sitemapPages = [
"/",
"/agents",
"/changelog",
"/download",
"/hub",
"/privacy",
"/terms",
...discoverAgentRoutes(),
...discoverAlternativeRoutes(),
...discoverPluginRoutes(),
...discoverDocsRoutes(),
...discoverBlogRoutes(),
].map((routePath) => ({
path: routePath,
}));
export default defineConfig((): UserConfig => {
return {
server: {
host: "0.0.0.0",
port: 8082,
strictPort: false,
fs: {
allow: [repoRoot],
},
watch: {
ignored: ["**/.tanstack/**"],
},
},
plugins: [
cloudflare({ viteEnvironment: { name: "ssr" } }),
tsConfigPaths(),
tanstackStart({
router: {
quoteStyle: "double",
semicolons: true,
},
pages: sitemapPages,
sitemap: {
host: siteHost,
},
}),
react(),
tailwindcss(),
],
};
});