You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
senzapaura_es/scripts/db/comparar-catalogo.mjs

146 lines
4.6 KiB

/**
* Compara el catálogo de la base con el de los archivos, campo a campo.
*
* npm run db:comparar
*
* Es la red de la fase 4. Cambiar de dónde salen los datos de veinte rutas sin
* poder comprobar que salen iguales sería confiar en que las pruebas de
* interfaz noten una diferencia de un año o de un porcentaje de autoría, y no
* la notarían: pintan lo que les den.
*
* Lo que se compara es el objeto entero de cada canción, álbum y estilo, con
* las mismas claves y ordenado igual. Las diferencias se listan una a una con
* su ruta —`dorina.registro.reparto[0].porcentaje`— porque un «no coinciden»
* sin más no sirve para arreglar nada.
*/
import fs from 'node:fs';
import path from 'node:path';
import { load as leerYaml } from 'js-yaml';
import { parsearAlbum, parsearCancion, parsearEstilo } from '$lib/content/schema';
import { extraerLineas } from '$lib/content/extracto';
import { leerCatalogo } from '$lib/server/catalogo';
/*
* Los archivos se leen a mano y no por `$lib/content`, que usa
* `import.meta.glob`: eso lo resuelve Vite al construir y en Node no existe.
* Los analizadores sí son los mismos, que es lo que hace que la comparación
* signifique algo.
*/
const RAIZ = 'src/content';
const SALTO = String.fromCharCode(10);
function partir(texto) {
const lineas = texto.split(new RegExp(String.raw`\r?\n`));
if (lineas[0]?.trim() !== '---') return { datos: {}, cuerpo: texto.trim() };
const cierre = lineas.indexOf('---', 1);
if (cierre === -1) return { datos: {}, cuerpo: texto.trim() };
return {
datos: leerYaml(lineas.slice(1, cierre).join(SALTO)) ?? {},
cuerpo: lineas
.slice(cierre + 1)
.join(SALTO)
.trim()
};
}
function leerCarpeta(carpeta, parser) {
const dir = path.join(RAIZ, carpeta);
if (!fs.existsSync(dir)) return [];
return fs
.readdirSync(dir)
.filter((n) => n.endsWith('.md'))
.sort()
.map((nombre) => {
const ruta = path.join(dir, nombre);
const { datos, cuerpo } = partir(fs.readFileSync(ruta, 'utf8'));
const slug = nombre.replace(/\.md$/, '');
const parseado = parser(datos, slug, ruta);
// El extracto lo inyecta un preprocesador al construir, no el parser.
if (carpeta === 'canciones') parseado.extracto = extraerLineas(cuerpo);
return parseado;
});
}
const deArchivo = leerCarpeta('canciones', parsearCancion);
const albumesArchivo = leerCarpeta('albumes', parsearAlbum);
const estilosArchivo = leerCarpeta('estilos', parsearEstilo);
/** Recorre dos objetos a la vez y devuelve dónde difieren. */
function diferencias(a, b, ruta = '') {
const fuera = [];
if (a === b) return fuera;
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
fuera.push(`${ruta}: archivo ${a.length} elementos, base ${b.length}`);
}
for (let i = 0; i < Math.max(a.length, b.length); i++) {
fuera.push(...diferencias(a[i], b[i], `${ruta}[${i}]`));
}
return fuera;
}
if (a && b && typeof a === 'object' && typeof b === 'object') {
for (const clave of new Set([...Object.keys(a), ...Object.keys(b)])) {
fuera.push(...diferencias(a[clave], b[clave], ruta ? `${ruta}.${clave}` : clave));
}
return fuera;
}
// `undefined` y ausente son lo mismo para este sitio: el tipo los declara
// opcionales y la plantilla los trata igual.
if ((a ?? undefined) === (b ?? undefined)) return fuera;
fuera.push(`${ruta}: archivo ${JSON.stringify(a)} · base ${JSON.stringify(b)}`);
return fuera;
}
const base = await leerCatalogo();
const grupos = [
['canciones', deArchivo, base.canciones],
['álbumes', albumesArchivo, base.albumes],
['estilos', estilosArchivo, base.estilos]
];
let problemas = 0;
for (const [nombre, archivo, deBase] of grupos) {
console.log(`\n${nombre.toUpperCase()} · archivo ${archivo.length} · base ${deBase.length}`);
const porSlug = new Map(deBase.map((x) => [x.slug, x]));
for (const uno of archivo) {
const otro = porSlug.get(uno.slug);
if (!otro) {
console.log(` ✗ ${uno.slug}: no está en la base`);
problemas++;
continue;
}
porSlug.delete(uno.slug);
const fuera = diferencias(uno, otro, uno.slug);
if (fuera.length === 0) continue;
problemas += fuera.length;
console.log(` ✗ ${uno.slug}`);
for (const d of fuera) console.log(` ${d}`);
}
for (const sobrante of porSlug.keys()) {
console.log(` · ${sobrante}: solo en la base (no es un fallo si se añadió después)`);
}
}
console.log(
problemas === 0
? '\nIdénticos. La base puede sustituir a los archivos sin que cambie nada.'
: `\n${problemas} diferencias. Cada una es algo que la web enseñaría distinto.`
);
process.exit(problemas === 0 ? 0 : 1);

Powered by TurnKey Linux.