#!/usr/bin/env tsx /** * Simple link checker for the blog * Tests: All internal links work, no broken references */ import fs from 'fs'; import path from 'path'; console.log('šŸ”— Checking links and references...\n'); let passed = 0; let failed = 0; function test(name: string, fn: () => void): void { try { fn(); console.log(`āœ… ${name}`); passed++; } catch (error) { console.log(`āŒ ${name}`); if (error instanceof Error) { console.log(` Error: ${error.message}`); } failed++; } } // Test 1: Check that all referenced blog posts exist test('All blog post files exist', () => { const blogDir = path.join(process.cwd(), 'src/pages/blog'); const files = fs.readdirSync(blogDir); // Get all .astro files const astroFiles = files.filter(f => f.endsWith('.astro')); if (astroFiles.length === 0) { throw new Error('No blog post files found'); } // Check blogPosts.ts references exist const blogPostsPath = path.join(process.cwd(), 'src/data/blogPosts.ts'); const content = fs.readFileSync(blogPostsPath, 'utf8'); // Extract slug references const slugMatches = content.match(/slug:\s*['"]([^'"]+)['"]/g); if (!slugMatches) { throw new Error('No slugs found in blogPosts.ts'); } const slugs = slugMatches.map(m => m.match(/['"]([^'"]+)['"]/)?.[1]); for (const slug of slugs) { const expectedFile = `${slug}.astro`; if (!astroFiles.includes(expectedFile)) { throw new Error(`Blog post file missing: ${expectedFile} (referenced in blogPosts.ts)`); } } }); // Test 2: Check that tag pages reference valid tags test('Tag references are valid', () => { const blogPostsPath = path.join(process.cwd(), 'src/data/blogPosts.ts'); const content = fs.readFileSync(blogPostsPath, 'utf8'); // Extract tags const tagsMatch = content.match(/tags:\s*\[([^\]]+)\]/); if (!tagsMatch) { throw new Error('No tags found in blogPosts.ts'); } const tagsContent = tagsMatch[1]; const tags = tagsContent.match(/['"]([^'"]+)['"]/g)?.map(t => t.replace(/['"]/g, '')); if (!tags || tags.length === 0) { throw new Error('No tags extracted'); } // Check that tag page exists const tagPagePath = path.join(process.cwd(), 'src/pages/tags/[tag].astro'); if (!fs.existsSync(tagPagePath)) { throw new Error('Tag page template missing'); } }); // Test 3: Check component imports test('All component imports are valid', () => { const components = [ 'src/components/MediumCard.tsx', 'src/components/SearchBar.tsx', 'src/components/ArticleBlockquote.tsx', 'src/components/ArticleHeading.tsx', 'src/components/ArticleList.tsx', 'src/components/ArticleParagraph.tsx' ]; for (const component of components) { const componentPath = path.join(process.cwd(), component); if (!fs.existsSync(componentPath)) { throw new Error(`Component missing: ${component}`); } // Check for export const content = fs.readFileSync(componentPath, 'utf8'); if (!content.includes('export')) { throw new Error(`Component has no exports: ${component}`); } } }); // Test 4: Check page structure test('All required pages exist', () => { const requiredPages = [ 'src/pages/index.astro', 'src/pages/about.astro', 'src/pages/blog.astro', 'src/pages/tags/[tag].astro' ]; for (const page of requiredPages) { const pagePath = path.join(process.cwd(), page); if (!fs.existsSync(pagePath)) { throw new Error(`Required page missing: ${page}`); } } }); // Test 5: Check layout structure test('Layout files are valid', () => { const layouts = [ 'src/layouts/BaseLayout.astro', 'src/layouts/BlogLayout.astro' ]; for (const layout of layouts) { const layoutPath = path.join(process.cwd(), layout); if (!fs.existsSync(layoutPath)) { throw new Error(`Layout missing: ${layout}`); } // Check for basic structure const content = fs.readFileSync(layoutPath, 'utf8'); if (!content.includes(' { const baseLayoutPath = path.join(process.cwd(), 'src/layouts/BaseLayout.astro'); const content = fs.readFileSync(baseLayoutPath, 'utf8'); if (!content.includes('global.css')) { throw new Error('BaseLayout does not import global.css'); } const globalCssPath = path.join(process.cwd(), 'src/styles/global.css'); if (!fs.existsSync(globalCssPath)) { throw new Error('Global CSS file missing'); } }); // Summary console.log('\n' + '='.repeat(50)); console.log(`Tests passed: ${passed}`); console.log(`Tests failed: ${failed}`); console.log('='.repeat(50)); if (failed === 0) { console.log('\nšŸŽ‰ All link checks passed! Your blog structure is solid.'); console.log('\nYour blog is ready to use!'); process.exit(0); } else { console.log('\nāŒ Some checks failed. Please fix the errors above.'); process.exit(1); }