import { sep } from 'node:path'; import { mdsvex } from 'mdsvex'; import remarkBreaks from 'remark-breaks'; import { defineConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import adapter from '@sveltejs/adapter-node'; import { sveltekit } from '@sveltejs/kit/vite'; import type { Plugin } from 'vite'; import type { PreprocessorGroup } from 'svelte/compiler'; import { conExtracto } from './src/lib/content/extracto.ts'; import { ancla } from './src/lib/content/anclas.ts'; /** * Sirve el panel de contenido en /admin durante el desarrollo. * * En produccion lo resuelve el servidor de estaticos, que ya busca el * index.html de una carpeta; el servidor de desarrollo de Vite no lo hace y * /admin daria 404. Sin esto, la direccion del panel seria distinta en cada * entorno, que es justo la clase de detalle que se olvida y hace perder media * hora. */ const panelEnDesarrollo: Plugin = { name: 'panel-en-desarrollo', apply: 'serve', configureServer(servidor) { servidor.middlewares.use((peticion, _respuesta, siguiente) => { const ruta = (peticion.url ?? '').split('?')[0]; if (ruta === '/admin' || ruta === '/admin/') peticion.url = '/admin/index.html'; siguiente(); }); } }; /** * Escribe en el frontmatter de cada cancion los primeros versos de su letra. * * Tiene que correr ANTES que mdsvex: despues, el cuerpo ya es un componente * compilado y el texto ha dejado de ser texto. Con esto, las tarjetas pueden * ensenar unos versos sin que nadie mantenga a mano un campo de resumen. */ const inyectarExtracto: PreprocessorGroup = { name: 'extracto-de-cancion', markup({ content, filename }) { // En Windows los nombres llegan con barras invertidas. if (!filename?.split(sep).join('/').includes('/src/content/canciones/')) return; const conFragmento = conExtracto(content); return conFragmento === content ? undefined : { code: conFragmento }; } }; /** * Pone un `id` a cada encabezado de nivel 2 del Markdown. * * Hace falta para que el indice lateral de los textos largos pueda enlazar a * sus apartados. Se escribe a mano en vez de traer `rehype-slug` porque son * quince lineas, y porque el ancla tiene que salir de la misma funcion que usa * el cargador al construir el indice: con dos implementaciones parecidas, un * dia dejan de coincidir. */ type NodoHast = { type: string; tagName?: string; value?: string; properties?: Record; children?: NodoHast[]; }; function textoDe(nodo: NodoHast): string { if (nodo.type === 'text') return nodo.value ?? ''; return (nodo.children ?? []).map(textoDe).join(''); } function anclasEnEncabezados() { return (arbol: NodoHast) => { const recorrer = (nodo: NodoHast) => { if (nodo.tagName === 'h2') { nodo.properties = { ...nodo.properties, id: ancla(textoDe(nodo)) }; } for (const hijo of nodo.children ?? []) recorrer(hijo); }; recorrer(arbol); }; } /** * mdsvex 0.12 emite `