UNPKG

@itznotabug/routex

Version:

A client side redirection plugin for Vitepress.

84 lines (81 loc) 3.64 kB
import path from 'path'; import { promises as fs } from 'fs'; import { Logger } from './logger.js'; import { Template } from './template.js'; import { sanitizePath } from './utils.js'; /** * Handles generation of redirect files and scripts */ export class RedirectGenerator { rules; options; vitepressConfig; constructor(rules, options, vitepressConfig) { this.rules = rules; this.options = options; this.vitepressConfig = vitepressConfig; } injectDevelopmentClientScript(html) { const redirectsJson = JSON.stringify(this.rules, null, 2); const scriptContent = ` function performRedirect() { const redirects = ${redirectsJson}; const destination = redirects[location.pathname]; if (destination) { try { location.replace(destination); } catch (error) { console.warn('Redirect failed, using fallback:', error); location.href = destination; } } } performRedirect(); `.trim(); const script = `<script>\n ${scriptContent.split('\n').join('\n ')}\n </script>`; return html.replace('<head>', `<head>\n ${script}`); } injectProductionClientScript(html, destination) { // quick, small and efficient const scriptContent = `var d=${JSON.stringify(destination)};try{location.replace(d)}catch(e){location.href=d}`; const script = `<script>${scriptContent}</script>`; return html.replace('<head>', `<head>\n ${script}`); } async generateRedirectPage(source, destination, templateManager) { const delay = this.options.redirectDelay || 0; const template = await templateManager.resolveTemplate(); const metaTags = this.createMetaTags(destination, delay); const processedTemplate = templateManager.processTemplate(template, source, destination, metaTags, delay); if (delay === 0) { // faster redirect if delay is 0. return this.injectProductionClientScript(processedTemplate, destination); } return processedTemplate; } async generateAllRedirectFiles(outDir) { const redirectCount = Object.keys(this.rules).length; if (redirectCount === 0) return; Logger.log(`Generating ${redirectCount} redirect page${redirectCount > 1 ? 's' : ''}...`, true); const templateManager = new Template(this.options, this.vitepressConfig); await Promise.all(Object.entries(this.rules).map(async ([from, to]) => { const filePath = path.join(outDir, sanitizePath(from), 'index.html'); const html = await this.generateRedirectPage(from, to, templateManager); await fs.mkdir(path.dirname(filePath), { recursive: true }); await fs.writeFile(filePath, html, 'utf8'); })); Logger.log(`✓ Generated ${redirectCount} redirect page${redirectCount > 1 ? 's' : ''}`); } createMetaTags(destination, delay) { const tags = []; // Meta refresh for SEO, crawlers, and non-JS fallback tags.push(`<meta http-equiv="refresh" content="${delay}; url=${destination}">`); if (this.options.addNoIndexMeta) { tags.push(`<meta name="robots" content="noindex, nofollow">`); } if (this.options.addCanonical) { tags.push(`<link rel="canonical" href="${destination}">`); } return tags.join('\n '); } }