chore(site): move to netlify (#381)

This commit is contained in:
Darius Cepulis
2026-02-02 15:45:41 -06:00
committed by GitHub
parent bfaf93fda9
commit 9ed608d6d4
9 changed files with 2784 additions and 385 deletions
+3
View File
@@ -120,6 +120,9 @@ Pods/
# Vercel
.vercel
# Netlify
.netlify
# -------------------------
# Package Manager Locks
# Keep only one when enforcing workspace consistency
+2703 -272
View File
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -3,11 +3,9 @@
import process from 'node:process';
import mdx from '@astrojs/mdx';
import netlify from '@astrojs/netlify';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
import vercel from '@astrojs/vercel';
import sentry from '@sentry/astro';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig, fontProviders } from 'astro/config';
@@ -25,9 +23,9 @@ const SITE_URL = 'https://v10.videojs.org';
export default defineConfig({
site: SITE_URL,
trailingSlash: 'never',
adapter: vercel(),
adapter: netlify(),
redirects: {
// Redirects are configured in vercel.json
// Redirects are configured in netlify.toml
},
integrations: [
sentry({
+40 -69
View File
@@ -37,7 +37,7 @@ const V8_URLS = [
'https://videojs.org/blog/the-end-of-html-first/',
'https://videojs.org/blog/video-js-5-11-0-prelease/',
'https://videojs.org/blog/video-js-5-s-fluid-mode-and-playlist-picker/',
'https://videojs.org/blog/video-js-5-the-only-thing-thats-changed-is-everything-except-for-like-3-things-that-didn-t-including-the-name/',
"https://videojs.org/blog/video-js-5-the-only-thing-that's-changed-is-everything-except-for-like-3-things-that-didn-t-including-the-name/",
'https://videojs.org/blog/it-s-here-5-0-release-candidates/',
'https://videojs.org/blog/video-js-4-12-the-last-of-the-4-minors/',
'https://videojs.org/blog/video-js-4-9-now-can-join-the-party/',
@@ -178,35 +178,40 @@ interface V8UrlStatus {
status: 'migrated' | 'redirected' | 'needs migration';
}
interface VercelRedirect {
source: string;
destination: string;
statusCode?: number;
permanent?: boolean;
interface NetlifyRedirect {
from: string;
to: string;
status: number;
}
/**
* Check if a pathname matches a Vercel redirect source pattern
* Supports exact matches and :path(.*) wildcard patterns
*/
function matchesVercelPattern(pathname: string, pattern: string): boolean {
// Remove trailing slash for consistent matching
function parseNetlifyToml(content: string): NetlifyRedirect[] {
const redirects: NetlifyRedirect[] = [];
const redirectRegex = /\[\[redirects\]\]\s+from\s*=\s*"([^"]+)"\s+to\s*=\s*"([^"]+)"\s+status\s*=\s*(\d+)/g;
let match: RegExpExecArray | null;
while ((match = redirectRegex.exec(content)) !== null) {
redirects.push({
from: match[1],
to: match[2],
status: parseInt(match[3], 10),
});
}
return redirects;
}
function matchesNetlifyPattern(pathname: string, pattern: string): boolean {
const normalizedPath = pathname.replace(/\/$/, '');
const normalizedPattern = pattern.replace(/\/$/, '');
// Exact match
if (normalizedPattern === normalizedPath) {
return true;
}
// Handle :path(.*) wildcard pattern
// Convert /tags/:path(.*) to a regex that matches /tags/anything
if (normalizedPattern.includes(':path(')) {
const regexPattern = normalizedPattern
.replace(/:[^/]+\(\.\*\)/g, '.*') // :path(.*) -> .*
.replace(/\//g, '\\/'); // Escape slashes
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(normalizedPath);
// Handle * splat pattern (e.g., /tags/* matches /tags/anything)
if (normalizedPattern.endsWith('/*')) {
const prefix = normalizedPattern.slice(0, -2);
return normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`);
}
return false;
@@ -220,20 +225,18 @@ export default function checkV8Urls(): AstroIntegration {
// Convert URL to file path
const buildDir = fileURLToPath(dir);
// Check for Vercel config files
// buildDir is dist/client/, so go up two levels to site/
const siteDir = resolve(buildDir, '..', '..');
const vercelConfigPath = resolve(siteDir, '.vercel', 'output', 'config.json');
const vercelJsonPath = resolve(siteDir, 'vercel.json');
// buildDir is dist/, so go up one level to site/
const siteDir = resolve(buildDir, '..');
const netlifyTomlPath = resolve(siteDir, 'netlify.toml');
// Read vercel.json redirects if it exists
let vercelJsonRedirects: VercelRedirect[] = [];
if (existsSync(vercelJsonPath)) {
// Read netlify.toml redirects if it exists
let netlifyRedirects: NetlifyRedirect[] = [];
if (existsSync(netlifyTomlPath)) {
try {
const vercelJson = JSON.parse(readFileSync(vercelJsonPath, 'utf-8'));
vercelJsonRedirects = vercelJson.redirects || [];
const content = readFileSync(netlifyTomlPath, 'utf-8');
netlifyRedirects = parseNetlifyToml(content);
} catch {
// Ignore JSON parse errors
// Ignore parse errors
}
}
@@ -254,49 +257,17 @@ export default function checkV8Urls(): AstroIntegration {
if (existsSync(htmlPath1) || existsSync(htmlPath2)) {
status = 'migrated';
} else {
// Check for redirects in vercel.json first
const hasVercelJsonRedirect = vercelJsonRedirects.some((redirect) => {
// Check if redirect has a redirect status code (or permanent flag)
const isRedirect = redirect.statusCode
? redirect.statusCode >= 301 && redirect.statusCode <= 308
: redirect.permanent !== undefined;
// Check for redirects in netlify.toml
const hasNetlifyRedirect = netlifyRedirects.some((redirect) => {
const isRedirect = redirect.status >= 301 && redirect.status <= 308;
if (isRedirect) {
return matchesVercelPattern(pathname, redirect.source);
return matchesNetlifyPattern(pathname, redirect.from);
}
return false;
});
if (hasVercelJsonRedirect) {
if (hasNetlifyRedirect) {
status = 'redirected';
} else {
// Fall back to checking .vercel/output/config.json (adapter-generated)
if (existsSync(vercelConfigPath)) {
try {
const vercelConfig = JSON.parse(readFileSync(vercelConfigPath, 'utf-8'));
const routes = vercelConfig.routes || [];
// Check if this pathname has a redirect (status 301/302/307/308)
const hasRedirect = routes.some((route: any) => {
if (route.status && route.status >= 301 && route.status <= 308) {
// Route has a redirect status code
const src = route.src;
// Match against pathname (src is a regex pattern)
// Simple check: convert pathname to regex pattern
const pathPattern = `^${pathname.replace(/\/$/, '')}$`;
const pathPatternWithSlash = `^${pathname}$`;
return src === pathPattern || src === pathPatternWithSlash;
}
return false;
});
if (hasRedirect) {
status = 'redirected';
}
} catch {
// Ignore JSON parse errors
}
}
}
}
+31
View File
@@ -0,0 +1,31 @@
# Redirect old theme demo pages to home
[[redirects]]
from = "/city"
to = "/"
status = 308
[[redirects]]
from = "/fantasy"
to = "/"
status = 308
[[redirects]]
from = "/forest"
to = "/"
status = 308
[[redirects]]
from = "/sea"
to = "/"
status = 308
# Redirect old tags pages to blog (no tag filtering in v10)
[[redirects]]
from = "/tags"
to = "/blog"
status = 307
[[redirects]]
from = "/tags/*"
to = "/blog"
status = 307
+1 -2
View File
@@ -5,7 +5,6 @@
"scripts": {
"dev": " astro dev",
"build": "astro build",
"preview": "echo 'Preview server is not supported on with the Vercel adapter.'",
"astro": "astro",
"test": "vitest run",
"test:watch": "vitest",
@@ -14,10 +13,10 @@
},
"dependencies": {
"@astrojs/mdx": "^4.3.7",
"@astrojs/netlify": "^6.6.4",
"@astrojs/react": "^4.4.0",
"@astrojs/rss": "^4.0.12",
"@astrojs/sitemap": "^3.6.0",
"@astrojs/vercel": "^8.2.9",
"@base-ui-components/react": "1.0.0-beta.4",
"@nanostores/react": "^1.0.0",
"@pagefind/default-ui": "^1.4.0",
+2 -2
View File
@@ -2,9 +2,9 @@ import * as Sentry from '@sentry/astro';
Sentry.init({
dsn: 'https://6bcdfa6b82da6dd4d7753618a9a69c7c@o43841.ingest.us.sentry.io/4510671167160320',
environment: import.meta.env.VERCEL_ENV || 'development',
environment: import.meta.env.CONTEXT || 'development',
enabled: import.meta.env.PROD,
release: import.meta.env.VERCEL_GIT_COMMIT_SHA || import.meta.env.VERCEL_DEPLOYMENT_ID || undefined,
release: import.meta.env.COMMIT_REF || import.meta.env.DEPLOY_ID || undefined,
// Adds request headers and IP for users, for more info visit:
// https://docs.sentry.io/platforms/javascript/guides/astro/configuration/options/#sendDefaultPii
sendDefaultPii: true,
-34
View File
@@ -1,34 +0,0 @@
{
"redirects": [
{
"source": "/city",
"destination": "/",
"statusCode": 308
},
{
"source": "/fantasy",
"destination": "/",
"statusCode": 308
},
{
"source": "/forest",
"destination": "/",
"statusCode": 308
},
{
"source": "/sea",
"destination": "/",
"statusCode": 308
},
{
"source": "/tags",
"destination": "/blog",
"statusCode": 307
},
{
"source": "/tags/:path(.*)",
"destination": "/blog",
"statusCode": 307
}
]
}
+1 -1
View File
@@ -5,7 +5,7 @@
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".vercel/output/**"]
"outputs": ["dist/**", ".netlify/**"]
},
"dev": {
"cache": false,