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 { PreprocessorGroup } from 'svelte/compiler'; import { conExtracto } from './src/lib/content/extracto.ts'; import { ancla } from './src/lib/content/anclas.ts'; /** * Dos aplicaciones, un repositorio. * * `APP=panel` compila la administración; sin esa variable, la web pública. Cada * una tiene su carpeta de rutas, sus hooks y su carpeta de salida, así que **el * servidor público no lleva dentro ni una línea del panel**: no es que la * esconda, es que ahí no existe. Cada una se sirve en su dominio y en su * proceso, como los archivos en `m.senzapaura.es`. * * Comparten `src/lib`, que es donde están el esquema, las consultas y el * acceso. Con dos repositorios habría que mantener las migraciones por * duplicado, y el día que se desincronizaran nadie se enteraría hasta que el * panel guardara en una columna que ya no existe. */ const esPanel = process.env.APP === 'panel'; /** * 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 `