UNPKG

nuxt-github-pages

Version:

Nuxt module to fix trailing slash issues for GitHub Pages deployments

123 lines (120 loc) 4.7 kB
import { promises } from 'node:fs'; import { resolve, join, dirname } from 'node:path'; import { defineNuxtModule, logger } from '@nuxt/kit'; const module = defineNuxtModule({ meta: { name: "nuxt-github-pages", configKey: "githubPages", compatibility: { nuxt: ">=3.0.0" } }, defaults: { enabled: true, outputDirs: ["dist", ".output/public"], verbose: true, canonicalUrls: true, trailingSlash: false, createDuplicates: true }, setup(options, nuxt) { if (!options.enabled) { logger.info("nuxt-github-pages module is disabled"); return; } if (nuxt.options.nitro.preset !== "static" && !nuxt.options._generate) { return; } nuxt.hook("nitro:config", (nitroConfig) => { nitroConfig.hooks = nitroConfig.hooks || {}; const hooks = nitroConfig.hooks; hooks["prerender:done"] = async () => { const possibleDirs = options.outputDirs.map((dir) => resolve(dir)); let publicDir = null; for (const dir of possibleDirs) { try { await promises.access(dir); publicDir = dir; if (options.verbose) { logger.success(`Found output directory: ${dir}`); } break; } catch { } } if (!publicDir) { if (options.verbose) { logger.info("No output directory found, skipping duplicate file creation"); } return; } async function processDirectory(dir) { try { const entries = await promises.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = join(dir, entry.name); if (entry.isDirectory()) { if (entry.name.startsWith("_") || entry.name.startsWith(".")) { continue; } await processDirectory(fullPath); } else if (entry.isFile() && entry.name === "index.html") { const relativePath = fullPath.replace(publicDir + "/", ""); const dirPath = dirname(relativePath); if (dirPath === ".") continue; if (options.canonicalUrls) { try { let htmlContent = await promises.readFile(fullPath, "utf-8"); const canonicalPath = options.trailingSlash ? `/${dirPath}/` : `/${dirPath}`; if (!htmlContent.includes('rel="canonical"')) { const headMatch = htmlContent.match(/<head[^>]*>/); if (headMatch) { const headTag = headMatch[0]; const canonicalTag = ` <link rel="canonical" href="${canonicalPath}">`; htmlContent = htmlContent.replace(headTag, headTag + canonicalTag); await promises.writeFile(fullPath, htmlContent, "utf-8"); if (options.verbose) { logger.info(`Added canonical URL to ${relativePath}: ${canonicalPath}`); } } } if (options.createDuplicates) { const duplicatePath = join(publicDir, `${dirPath}.html`); await promises.writeFile(duplicatePath, htmlContent, "utf-8"); if (options.verbose) { logger.success(`Created duplicate: ${dirPath}.html`); } } } catch (error) { logger.error(`Failed to process ${relativePath}:`, error); } } else if (options.createDuplicates) { const duplicatePath = join(publicDir, `${dirPath}.html`); try { await promises.copyFile(fullPath, duplicatePath); if (options.verbose) { logger.success(`Created duplicate: ${dirPath}.html`); } } catch (error) { logger.error(`Failed to create duplicate for ${relativePath}:`, error); } } } } } catch (error) { logger.error(`Error processing directory ${dir}:`, error); } } if (options.verbose) { logger.info("Creating duplicate HTML files for GitHub Pages..."); } await processDirectory(publicDir); if (options.verbose) { logger.success("Duplicate file creation complete"); } }; }); } }); export { module as default };