Files
v10/site/integrations/check-v8-urls.ts
T

348 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { AstroIntegration } from 'astro';
import { existsSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
// Video.js v8 URLs to check for migration status
const V8_URLS = [
'https://videojs.org/city',
'https://videojs.org/fantasy',
'https://videojs.org/forest',
'https://videojs.org/sea',
'https://videojs.org/getting-started/',
'https://videojs.org/blog/videojs-8-and-vhs-3/',
'https://videojs.org/blog/announcing-the-new-videojs-com/',
'https://videojs.org/blog/inband-captions-support-with-vhs/',
'https://videojs.org/blog/video-js-7-4/',
'https://videojs.org/blog/bugpost-disconnects-and-reconnects/',
'https://videojs.org/blog/introducing-video-js-http-streaming-vhs/',
'https://videojs.org/blog/video-js-7-3-responsive-layout-fill-mode-createLogger/',
'https://videojs.org/blog/video-js-7-1-and-6-11-autoplay-and-fullscreen-changes/',
'https://videojs.org/blog/video-js-7-is-here/',
'https://videojs.org/blog/autoplay-best-practices-with-video-js/',
'https://videojs.org/blog/videojs-contrib-ads-6/',
'https://videojs.org/blog/video-js-7-roadmap/',
'https://videojs.org/blog/videojs-vr-now-under-the-video-js-org/',
'https://videojs.org/blog/video-js-6-7-1-released/',
'https://videojs.org/blog/video-js-6-5-0-release/',
'https://videojs.org/blog/video-js-6-4-0-release/',
'https://videojs.org/blog/video-js-6-0-release/',
'https://videojs.org/blog/feature-spotlight-middleware/',
'https://videojs.org/blog/video-js-removes-flash-from-core-player/',
'https://videojs.org/blog/feature-spotlight-accessibility/',
'https://videojs.org/blog/feature-spotlight-advanced-plugins/',
'https://videojs.org/blog/video-js-6-0-0-rc-0-the-first-rc/',
'https://videojs.org/blog/introducing-thumbcoil/',
'https://videojs.org/blog/video-js-5-12-0-and-5-11-5-releases/',
'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/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/',
'https://videojs.org/blog/video-js-4-8-0-released-prost/',
'https://videojs.org/blog/video-js-v4-7-0-built-mostly-by-new-contributors-also-google-chooses-video-js/',
'https://videojs.org/blog/dash-everywhere-ish-hack-project/',
'https://videojs.org/blog/video-js-version-4-6-0-released-it-s-been-a-productive-month/',
'https://videojs.org/blog/video-js-version-4-5-0-released-nothing-to-see-here-move-along/',
'https://videojs.org/blog/video-js-version-4-4-0-released-now-supporting-requirejs-and-browserify/',
'https://videojs.org/blog/video-js-version-4-3-0-released-w-shiny-new-api-docs/',
'https://videojs.org/blog/the-guardian-uses-video-js-in-feature-article/',
'https://videojs.org/blog/4-2-2-patch-release/',
'https://videojs.org/blog/running-video-js-unit-tests-in-real-browsers-with-karma/',
'https://videojs.org/blog/unauthorized-modification-of-video-js-cdn-files/',
'https://videojs.org/blog/video-js-4-2-0-released-rtmp-css-designer-and-stability/',
'https://videojs.org/blog/hiding-and-showing-video-player-controls/',
'https://videojs.org/blog/new-player-skin-designer-for-video-js/',
'https://videojs.org/blog/video-js-4-1-0-released/',
'https://videojs.org/blog/video-js-4-0-now-available/',
'https://videojs.org/blog/repo-moved/',
'https://videojs.org/blog/site-and-support-updates/',
'https://videojs.org/blog/brightcove-acquires-zencoder/',
'https://videojs.org/blog/version-3-2-update/',
'https://videojs.org/blog/version-3-1-update/',
'https://videojs.org/blog/video-js-version-3-0/',
'https://videojs.org/blog/lynda-com-html5-video-tutorial-released/',
'https://videojs.org/blog/how-are-you-using-video-js/',
'https://videojs.org/blog/new-mpeg-la-webm-vp8-patent-pool/',
'https://videojs.org/blog/apple-adds-airplay-to-mobile-safari/',
'https://videojs.org/blog/google-is-dropping-h-264-from-chrome/',
'https://videojs.org/blog/over-50-of-web-users-now-support-html5-video/',
'https://videojs.org/blog/html5-video-google-maps-mashup/',
'https://videojs.org/blog/2-0-2-release-subtitle-optimization-safari-on-leopard-fs-fix/',
'https://videojs.org/blog/2-0-0-release-behaviors-fallback-apis-and-more/',
'https://videojs.org/blog/make-sites-serve-you-html5-video-in-safari/',
'https://videojs.org/blog/1-1-5-release-subtitles-using-track-android-fix-more/',
'https://videojs.org/blog/version-1-1-4-release-css-loading-spinner-more/',
'https://videojs.org/blog/version-1-1-3-release/',
'https://videojs.org/blog/facebook-adds-html5-video/',
'https://videojs.org/blog/ipad-iphone-video-poster-fix-bonus-javascript-placement-fix/',
'https://videojs.org/blog/videojs-is-ie9-compatible/',
'https://videojs.org/blog/new-videojs-site-now-with-more-html5/',
'https://videojs.org/blog',
'https://videojs.org/blog/2',
'https://videojs.org/blog/3',
'https://videojs.org/blog/4',
'https://videojs.org/blog/5',
'https://videojs.org/blog/6',
'https://videojs.org/blog/7',
'https://videojs.org/tags/news',
'https://videojs.org/tags/vhs',
'https://videojs.org/tags/videojs-http-streaming',
'https://videojs.org/tags/playback',
'https://videojs.org/tags/captions',
'https://videojs.org/tags/cea-608',
'https://videojs.org/tags/608',
'https://videojs.org/tags/a11y',
'https://videojs.org/tags/responsive',
'https://videojs.org/tags/languages',
'https://videojs.org/tags/live-ui',
'https://videojs.org/tags/focus-visible',
'https://videojs.org/tags/http-streaming',
'https://videojs.org/tags/bugs',
'https://videojs.org/tags/log',
'https://videojs.org/tags/fill',
'https://videojs.org/tags/fluid',
'https://videojs.org/tags/playerresize',
'https://videojs.org/tags/autoplay',
'https://videojs.org/tags/fullscreen',
'https://videojs.org/tags/IE8',
'https://videojs.org/tags/IE',
'https://videojs.org/tags/google%20analytics',
'https://videojs.org/tags/VHS',
'https://videojs.org/tags/HLS',
'https://videojs.org/tags/DASH',
'https://videojs.org/tags/webpack',
'https://videojs.org/tags/rollup',
'https://videojs.org/tags/vr',
'https://videojs.org/tags/360',
'https://videojs.org/tags/plugins',
'https://videojs.org/tags/middleware',
'https://videojs.org/tags/video.js%206.0',
'https://videojs.org/tags/accessibility',
'https://videojs.org/tags/flash',
'https://videojs.org/tags/html5',
'https://videojs.org/tags/thumbcoil',
'https://videojs.org/tags/mse',
'https://videojs.org/tags/hls',
'https://videojs.org/tags/releases',
'https://videojs.org/tags/version',
'https://videojs.org/tags/babel',
'https://videojs.org/tags/html5%20video',
'https://videojs.org/tags/videojs%205',
'https://videojs.org/tags/release',
'https://videojs.org/tags/videojs',
'https://videojs.org/tags/gallery',
'https://videojs.org/tags/code',
'https://videojs.org/tags/stats',
'https://videojs.org/guides/angular/',
'https://videojs.org/guides/audio-tracks/',
'https://videojs.org/guides/components/',
'https://videojs.org/guides/debugging/',
'https://videojs.org/guides/embeds/',
'https://videojs.org/guides/event-target/',
'https://videojs.org/guides/faqs/',
'https://videojs.org/guides/hooks/',
'https://videojs.org/guides/languages/',
'https://videojs.org/guides/layout/',
'https://videojs.org/guides/live/',
'https://videojs.org/guides/middleware/',
'https://videojs.org/guides/modal-dialog/',
'https://videojs.org/guides/picture-in-picture/',
'https://videojs.org/guides/tech/',
'https://videojs.org/guides/player-workflows/',
'https://videojs.org/guides/react/',
'https://videojs.org/guides/skins/',
'https://videojs.org/guides/spatial-navigation/',
'https://videojs.org/guides/text-tracks/',
'https://videojs.org/guides/troubleshooting/',
'https://videojs.org/guides/video-tracks/',
'https://videojs.org/guides/videojs-7-to-8/',
'https://videojs.org/guides/options/',
'https://videojs.org/guides/plugins/',
'https://videojs.org/guides/setup/',
'https://videojs.org/guides/vue/',
'https://videojs.org/guides/webpack/',
'https://videojs.org/guides',
'https://videojs.org/advanced/',
'https://videojs.org/html5-video-support/',
'https://videojs.org/',
'https://videojs.org/plugins/',
'https://videojs.org/privacy/',
];
interface V8UrlStatus {
url: string;
pathname: string;
status: 'migrated' | 'redirected' | 'needs migration';
}
interface VercelRedirect {
source: string;
destination: string;
statusCode?: number;
permanent?: boolean;
}
/**
* 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
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);
}
return false;
}
export default function checkV8Urls(): AstroIntegration {
return {
name: 'check-v8-urls',
hooks: {
'astro:build:done': async ({ dir, logger }) => {
// 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');
// Read vercel.json redirects if it exists
let vercelJsonRedirects: VercelRedirect[] = [];
if (existsSync(vercelJsonPath)) {
try {
const vercelJson = JSON.parse(readFileSync(vercelJsonPath, 'utf-8'));
vercelJsonRedirects = vercelJson.redirects || [];
} catch {
// Ignore JSON parse errors
}
}
// Check each URL
const results: V8UrlStatus[] = [];
for (const url of V8_URLS) {
// Extract pathname from URL
const urlObj = new URL(url);
const pathname = decodeURIComponent(urlObj.pathname);
// Check for HTML files
const htmlPath1 = join(buildDir, `${pathname}.html`);
const htmlPath2 = join(buildDir, pathname, 'index.html');
let status: V8UrlStatus['status'] = 'needs migration';
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;
if (isRedirect) {
return matchesVercelPattern(pathname, redirect.source);
}
return false;
});
if (hasVercelJsonRedirect) {
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
}
}
}
}
results.push({
url,
pathname,
status,
});
}
// Calculate statistics
const total = results.length;
const migrated = results.filter(r => r.status === 'migrated').length;
const redirected = results.filter(r => r.status === 'redirected').length;
const needsMigration = results.filter(r => r.status === 'needs migration').length;
const migratedPercent = ((migrated / total) * 100).toFixed(1);
const redirectedPercent = ((redirected / total) * 100).toFixed(1);
const needsMigrationPercent = ((needsMigration / total) * 100).toFixed(1);
// Log results
logger.info('');
logger.info('V8 URL Migration Status');
logger.info('========================');
logger.info(`✅ Migrated: ${migrated}/${total} (${migratedPercent}%)`);
logger.info(`🔀 Redirected: ${redirected}/${total} (${redirectedPercent}%)`);
logger.info(`⚠️ Needs migration: ${needsMigration}/${total} (${needsMigrationPercent}%)`);
logger.info('');
// Log table
logger.info('Status | URL');
logger.info('------ | ---');
const statusEmoji: Record<V8UrlStatus['status'], string> = {
migrated: '✅',
redirected: '🔀',
'needs migration': '⚠️ ',
};
for (const result of results) {
logger.info(`${statusEmoji[result.status]} | ${result.pathname}`);
}
logger.info('');
},
},
};
}