@ngx-docs-markdown-kit/parser-md-converter
v0.1.0
Published
Genera README.md multi-flavor (GitHub/npmjs/NuGet) a partir del manifiesto de contenido de un sitio create-ngx-docs-site -- nucleo agnostico de framework (sin dependencias de Angular): marcadores de splice, deteccion de badges, downgrade de las extensione
Maintainers
Readme
@ngx-docs-markdown-kit/parser-md-converter
Nucleo agnostico de framework para generar README.md multi-flavor (GitHub/npmjs/NuGet) a partir
del manifiesto de contenido de un sitio create-ngx-docs-site. No escanea el filesystem por su
cuenta -- el llamador (generate-readme.mjs en cada sitio) le pasa las paginas ya resueltas.
Expone:
markersFor/wrapFullDocument/spliceGranular/spliceFullBlock/extractFullBlock/extractHeaderAndBody-- marcadores<!-- {proyecto}:doc_start -->etc., y las 2 semanticas de insercion (granular para el README del proyecto, bloque completo para el README de la raiz del repo).detectAutoBadges/manualBadges/renderBadgesMarkdown-- badges de version/tecnologia detectados delpackage.json, mas los bien conocidos (stars/issues/docker/deepwiki) activados a mano.listDependencies/renderDependenciesMarkdown-- lista de dependencias reales.downgradeToMarkdown-- baja fences enriquecidos (image/code-block/card-code-block/content-card/cards) a Markdown compatible.generateFlavorDocument/buildLanguageStub-- arma el documento completo de un flavor.writeWithSizeGuard/ReadmeTooLargeError-- escritura con resguardo de tamano (copia.bak, restaura si se excede el limite).spliceProjectReadme/spliceRepoReadme-- aplica el splice sobre los 2 destinos reales.
Documentacion de uso completa (sintaxis de [readme_ignored], flujo de 2 pasos, config de un sitio)
en el propio sitio de docs de create-ngx-docs-site.
parser-md-converter

Dependencias:
@angular/animations: ^22.1.0@angular/cdk: ^22.1.0@angular/common: ^22.1.0@angular/compiler: ^22.1.0@angular/core: ^22.1.0@angular/forms: ^22.1.0@angular/material: ^22.1.0@angular/platform-browser: ^22.1.0@angular/platform-server: ^22.1.0@angular/router: ^22.1.0@angular/ssr: ^22.1.3@ngx-docs-markdown-kit/parser-md: file:./vendor/parser-md/ngx-docs-markdown-kit-parser-md-0.1.0.tgz@ngx-docs-markdown-kit/parser-md-seo: file:./vendor/parser-md-seo/ngx-docs-markdown-kit-parser-md-seo-0.1.0.tgz@ngx-docs-markdown-kit/parser-md-code-block: file:./vendor/parser-md-code-block/ngx-docs-markdown-kit-parser-md-code-block-0.1.0.tgz@ngx-docs-markdown-kit/parser-md-code-block-themes: file:./vendor/parser-md-code-block-themes/ngx-docs-markdown-kit-parser-md-code-block-themes-0.1.0.tgz@ngx-docs-markdown-kit/parser-md-image: file:./vendor/parser-md-image/ngx-docs-markdown-kit-parser-md-image-0.1.0.tgz@ngx-docs-markdown-kit/parser-md-card: file:./vendor/parser-md-card/ngx-docs-markdown-kit-parser-md-card-0.1.0.tgz@ngx-docs-markdown-kit/parser-md-converter: file:./vendor/parser-md-converter/ngx-docs-markdown-kit-parser-md-converter-0.1.0.tgz@ngx-docs-markdown-kit/ui: file:./vendor/ui/ngx-docs-markdown-kit-ui-0.1.0.tgzrxjs: ~7.8.0tslib: ^2.3.0
Apartados de parser-md-converter
- Docs -- Documentación de @ngx-docs-markdown-kit/parser-md-converter -- genera README.md multi-flavor desde el manifiesto de contenido.
- Parser MD Converter -- Genera README.md multi-flavor (GitHub/npmjs/NuGet) desde el manifiesto de contenido de un sitio @ngx-docs-markdown-kit.
Docs de parser-md-converter
REGRESAR A APARTADOS DE parser-md-converter
Índice Docs de parser-md-converter
- Primeros pasos -- Qué genera, y cómo correrlo sobre un sitio create-ngx-docs-site real.
- Instalación -- Ya viene conectado en cualquier sitio create-ngx-docs-site -- npm run readme.
- Uso -- El flujo de 2 pasos, el resguardo de tamaño, el config, badges y [readme_ignored].
- Cómo funciona -- El flujo de 2 pasos -- generar por flavor, después insertar con marcadores en los README reales.
- CONVERTER_CONFIG -- El config real de un sitio -- projectRoot/repoRoot calculados solos, badges, idiomas.
- Resguardo de tamaño -- Por qué toda escritura falla duro y restaura el original en vez de dejar un README a medio escribir.
- "[readme_ignored]" -- Excluir una página puntual del README generado.
- Ecosistema -- Qué depende realmente de parser-md-converter, y qué reimplementa a propósito en vez de importar.
- Relación con el ecosistema -- Depende de parser-md, reimplementa a propósito los fences de las demás librerías Angular, y comparte el manifiesto con parser-md-seo.
Primeros pasos de parser-md-converter
< Índice Docs de parser-md-converter
Instalación de parser-md-converter
< Primeros pasos de parser-md-converter
npm install @ngx-docs-markdown-kit/parser-md-converterNúcleo agnóstico de framework (sin dependencias de Angular) -- pensado para correr con Node plano, en un script de build, NO dentro del bundle del navegador.
Cualquier sitio generado con create-ngx-docs-site ya lo trae conectado por default (no es una
extensión --no-<id> -- es infraestructura de publicación, siempre presente): el script
frugocorp_modules/parser-md-converter/scripts/generate-readme.mjs y el config
frugocorp_modules/parser-md-converter/config/converter.config.ts ya están en la plantilla.
Generar el README real
npm run readme -- --githubSin flavor explícito, genera los 3 (--github/--npmjs/--nuget). No corre automáticamente
como parte de npm run build/npm start -- es un paso deliberado, se corre a mano cuando querés
publicar (ver Cómo funciona para el motivo).
Reusa el CONTENT_MANIFEST YA GENERADO por parser-md -- nunca vuelve a escanear el filesystem por
su cuenta. Si todavía no corriste npm start/npm run build al menos una vez, no hay manifiesto
que leer.
Fuera de un sitio create-ngx-docs-site, es un paquete instalable como cualquier otro de npm
(dependencia real de @ngx-docs-markdown-kit/parser-md, no un peer) -- ver
Relación con el ecosistema para el detalle de qué asume del resto
del kit y qué no.
Uso de parser-md-converter
< Índice Docs de parser-md-converter
Cómo funciona de parser-md-converter
Paso 1 -- generar un documento por flavor
Por cada flavor pedido (github/npmjs/nuget), arma un documento completo a partir del árbol
real de páginas del manifiesto (mismo árbol grupo/orden que usa la navegación del sitio -- ver
buildContentManifest de
parser-md) y lo guarda en frugocorp_modules/parser-md-converter/generated/README.<flavor>.md
(gitignored -- documento intermedio, nunca el destino final).
Ese documento queda envuelto de punta a punta en marcadores, únicos por proyecto (para que 2 proyectos nunca se pisen si comparten un mismo README, ej. la raíz de un monorepo):
<!-- {proyecto}:doc_start -->
<!-- {proyecto}:doc_header_start -->
{título, logo, badges, dependencias}
<!-- {proyecto}:doc_header_end -->
<!-- {proyecto}:doc_body_start -->
{"Apartados de {título}" + un bloque por cada sección de nivel superior}
<!-- {proyecto}:doc_body_end -->
<!-- {proyecto}:doc_end -->El body está organizado en bloques: cada sección de nivel superior del contenido (profundidad 0
en el manifiesto) es su propio bloque, con su título en ##, un link de vuelta a "Apartados de
{título}", y -- si tiene páginas hijas -- su propio sub-índice recursivo (## Índice {título del
bloque}) antes de las secciones reales. Un bloque sin hijos (ej. una página de licencia suelta) va
directo a su contenido. Los niveles de heading DENTRO de un bloque arrancan en ## para el bloque
Y sus hijos directos (no se anida un nivel extra solo por "estar dentro" del bloque), y de ahí para
adentro bajan uno por nivel real, tope ######.
Páginas cuyo frontmatter declara [readme_ignored] quedan afuera (ver
readme_ignored). Los links internos del sitio (/docs/...) se
resuelven al anchor real dentro del mismo documento si la página de destino está incluida, o se
sacan por completo ([texto], sin URL) si no -- nunca queda un link roto apuntando a una ruta que
no sirve de nada en un README.
Anchors reales de GitHub, no los del sitio
Los links de "Apartados de {título}" y de cada "volver" apuntan a un anchor (#mi-titulo) que tiene
que coincidir EXACTO con el que arma GitHub al renderizar un heading -- distinto del slugify que
usa @ngx-docs-markdown-kit/parser-md para los anchors internos del sitio corriendo. parser-md
saca acentos/letras unicode (pensado para URLs limpias); GitHub NO -- solo baja a minúsculas, saca
puntuación ASCII y cambia espacios por guiones, dejando la letra unicode tal cual ("Cómo funciona"
→ cómo-funciona, no como-funciona). Por eso este paquete trae su propia función
(github-anchor.ts), que replica ese algoritmo real (mismo criterio que usa remark/
github-slugger por dentro) en vez de reusar slugify -- resuelven 2 problemas distintos para 2
audiencias distintas, mezclarlos habría generado anchors que se ven bien en el sitio pero no
funcionan al hacer clic en GitHub. Títulos duplicados dentro del mismo documento se desambiguan con
un sufijo (-1, -2, ...) llevando la cuenta por documento generado.
Paso 2 -- insertar en los README reales, NUNCA sobreescribir por completo
Dos destinos, con 2 semánticas de reemplazo distintas:
- README de la raíz del PROYECTO (el sitio en sí) -- reemplazo GRANULAR: el header y el body
se reemplazan cada uno POR SEPARADO -- todo lo demás (antes del header, entre header y body,
después del body) queda intacto. Tu equipo puede escribir contenido propio en cualquiera de esos
huecos sin que la próxima corrida lo borre. Solo el flavor
githubalimenta este destino y el siguiente (es el que tanto GitHub como npmjs terminan leyendo de un solo archivo real). - README de la raíz del repo git que lo contiene (si es una carpeta DISTINTA a la del
proyecto -- ej. la raíz de un monorepo) -- reemplazo COMPLETO del bloque
doc_start/doc_end, copiando tal cual lo que quedó en el README del proyecto (header + body + lo que tu equipo haya agregado adentro, del paso anterior).
Si el destino todavía no tiene marcadores (primera corrida), el bloque se agrega AL FINAL del archivo, sin asumir ninguna posición "correcta".
Las rutas de imagen del contenido (/images/..., absolutas para el navegador del sitio real) se
reescriben distinto según el destino -- public/images/... en el README del proyecto,
{ruta relativa real}/public/images/... en el de la raíz del repo -- para que funcionen como
archivo real en disco al ver el README en GitHub/npmjs, no solo en el sitio corriendo.
npmjs/nuget quedan como documentos alternativos en generated/, sin destino automático --
listos para copiar a mano donde haga falta (ej. junto a un .csproj real de NuGet).
Toda escritura de este paso (los 3 generated/README.<flavor>.md y los 2 destinos reales) pasa por
el mismo resguardo de tamaño -- ver Resguardo de tamaño.
Paso 3 -- actualizar SOLO la propia entrada del índice del repo
No confundir con el sub-índice interno del Paso 1 (## Índice {título del bloque}, dentro del
propio documento de un proyecto) -- este es un mecanismo distinto, para un problema distinto: un
LISTADO de proyectos, no un índice de páginas dentro de uno.
Cuando el README de la raíz del repo reúne a VARIOS proyectos (ej. un monorepo con un paquete por carpeta), cada uno necesita aparecer como 1 línea en ese listado común, sin que la corrida de un proyecto pise las entradas que ya escribieron los demás. Mecanismo aparte del Paso 2 (que solo mueve el bloque completo de UN proyecto) -- 2 niveles de marcador, anidados:
<!-- indice_auto:start -->
<!-- indice_auto_section_{proyecto-a}:start -->
- [Título A](#título-a) -- descripción de A
<!-- indice_auto_section_{proyecto-a}:end -->
<!-- indice_auto_section_{proyecto-b}:start -->
- [Título B](#título-b) -- descripción de B
<!-- indice_auto_section_{proyecto-b}:end -->
<!-- indice_auto:end -->- El contenedor
indice_auto:start/endnunca se crea solo -- su posición "correcta" (normalmente debajo del logo, antes de cualquier bloque de proyecto) es una decisión de diseño, no algo que este paquete pueda adivinar. Si no existe todavía, la corrida no hace nada -- se bootstrapea a mano 1 sola vez; de ahí en adelante cada corrida solo actualiza lo que ya está. - Dentro del contenedor, cada proyecto tiene su propio par
indice_auto_section_{proyecto}:start/ end: si ya existe, la línea se reemplaza en el lugar (el orden entre proyectos se preserva); si no existe, se agrega al final del contenedor (orden de inserción -- reordenable a mano después, sin que la próxima corrida lo pierda). - La línea sale de
home.md:[Título](#anchor) -- [description]del frontmatter de la páginahomedel proyecto -- mismogithubAnchor()que el Paso 1 (arriba), para que el link funcione clickeado en GitHub. Sin descripción en el frontmatter, la línea queda sin el--final. - Solo corre si el destino existe en disco -- el mismo README de la raíz del repo que alimenta
el Paso 2 (o el propio README del proyecto, si
projectRootYA ES la raíz del repo -- caso de un proyecto que documenta el repo completo, no una carpeta particular de un monorepo).
CONVERTER_CONFIG de parser-md-converter
Vive en frugocorp_modules/parser-md-converter/config/converter.config.ts de cada sitio -- TS
fuente, no JSON en runtime (mismo criterio que el resto del config del ecosistema).
export const CONVERTER_CONFIG = {
title: 'MiProyecto',
logo: '/images/mi-logo-300x300.jpg',
projectRoot: { outputPath: 'README.md', path: '.', maxBytes: null },
repoRoot: { outputPath: 'README.md', path: '../../..', enabled: true, maxBytes: null },
nuget: { maxBytes: 1_000_000 },
badges: { stars: null, issues: null, docker: null, deepwiki: null },
languages: [{ code: 'es', label: 'Español', file: 'README.md' }],
} satisfies ConverterConfig;projectRoot/repoRoot
Calculados automáticamente por scaffoldSite() UNA SOLA VEZ, al crear el sitio -- detección
automática, NO una garantía: revisá/corregí repoRoot.path si tu convención de carpetas es
distinta (ej. moviste el sitio a otro lugar después de crearlo). repoRoot.path es la ruta relativa
real (calculada subiendo desde la carpeta del sitio hasta el .git más cercano) hasta la raíz del
repo que lo contiene -- si no encuentra ninguno, o coincide con la carpeta del propio proyecto, cae
a "." (mismo valor que projectRoot.path), lo que auto-anula el splice del repo-root (ver
Cómo funciona) sin necesitar un if aparte.
maxBytes: null = sin límite -- nuget.maxBytes sí trae un default real (1_000_000, ~1MB, límite
real de NuGet) porque ese flavor SIEMPRE necesita uno.
badges
Bien conocidos por su nombre oficial -- null = no aparece (nunca un badge roto/vacío). El equipo
pone el link real cuando quiere activar uno:
| Clave | Valor esperado | Genera |
| --- | --- | --- |
| stars | URL del repo de GitHub | Badge de estrellas (shields.io), link a /stargazers. |
| issues | URL del repo de GitHub | Badge de issues abiertos, link a /issues. |
| docker | URL de Docker Hub | Badge fijo "docker-pull", link directo. |
| deepwiki | URL de deepwiki | Badge fijo "deepwiki-view", link directo. |
Además de estos, el header SIEMPRE detecta automáticamente badges de versión/tecnología desde el
package.json real del proyecto (versión, engines.node, link a npmjs) -- sin config.
languages
Hoy solo un idioma por default (es). Si agregás un segundo, la corrida crea (una vez, si no
existe) un stub README.<idioma>.md con un placeholder "🚧 En construcción" + link de vuelta al
primero -- nunca traducción automática.
Resguardo de tamaño de parser-md-converter
Cada archivo que este paquete escribe -- los 3 generated/README.<flavor>.md intermedios, y los 2
destinos reales del paso 2 (README del proyecto, README de la raíz del repo) -- pasa por la misma
función de escritura protegida, no un writeFileSync directo.
Qué hace
- Si el destino ya existe, lo copia primero a
<destino>.bak. - Escribe el contenido nuevo en el destino real.
- Mide el archivo ya en disco (no el string en memoria -- el tamaño real que va a pesar en GitHub/npmjs/NuGet).
- Si no hay límite configurado (
maxBytes: null), listo -- borra el.baky termina. - Si hay límite y el archivo mide menos, también listo -- borra el
.bak. - Si hay límite y el archivo lo supera, borra lo que acaba de escribir, restaura el
.bak(o borra el destino directamente si no existía antes) y corta la ejecución lanzandoReadmeTooLargeError.
Por qué falla duro en vez de avisar y seguir
Un README real fuera de límite no es un detalle cosmético -- en NuGet, un paquete con
PackageReadmeFile que supera el límite del feed directamente no publica (o el feed lo rechaza
según el caso). Preferible cortar la corrida con un error claro que diga cuántos bytes se pasó y de
cuánto, que dejar un archivo roto a mitad de escritura o -- peor -- un README que "funciona" en
local pero rompe recién en el momento de publicar. Restaurar el .bak automáticamente evita
además el peor escenario posible: perder el contenido bueno que ya estaba en el destino porque la
corrida nueva falló a mitad de camino.
Dónde aplica un límite real hoy
Solo nuget.maxBytes trae un default real (1_000_000, ~1 MB) en CONVERTER_CONFIG -- ver
CONVERTER_CONFIG -- porque ese flavor siempre corre contra un
límite real del ecosistema NuGet. projectRoot/repoRoot quedan en maxBytes: null por default
(GitHub y npmjs no imponen un límite estricto comparable) -- el equipo puede poner un número ahí
igual si quiere protegerse de un README que creció demasiado.
"[readme_ignored]" de parser-md-converter
Una página que declara este campo en su frontmatter queda AFUERA del README generado -- sigue apareciendo normal en el sitio (navegación, sitemap, todo lo demás), solo se excluye de este mecanismo puntual:
---
[slug]: notas-internas
[title]: Notas internas del equipo
[readme_ignored]: no aplica a un lector externo del paquete
---El valor es libre -- un motivo, para quien lea el .md fuente más adelante y se pregunte por qué
no aparece en el README. Ese texto NUNCA viaja al README -- solo importa que la clave exista.
No requiere ningún cambio en parser-md/content-manifest.ts -- el frontmatter completo de cada
página ya viaja genérico en GeneratedPage.meta, este paquete solo lee
page.meta['readme_ignored'] al armar la lista de páginas a incluir.
Ecosistema de parser-md-converter
< Índice Docs de parser-md-converter
Relación con el ecosistema de parser-md-converter
< Ecosistema de parser-md-converter
@ngx-docs-markdown-kit/parser-md-converter es, a propósito, el único paquete del kit pensado para
correr fuera del navegador -- Node plano, en un script de build, nunca dentro del bundle de
Angular. Eso condiciona toda su relación con el resto del ecosistema: depende de una sola librería
en serio, y del resto solo conoce una convención de texto, nunca el código.
Lo único de lo que depende de verdad
@ngx-docs-markdown-kit/parser-md es una dependencia real en su
package.json (no un peer, no algo opcional) -- reusa directo su parseKeyValueLines() para leer
los campos @campo[<Etiqueta>] [lang] de los fences enriquecidos (card-code-block,
content-card) al bajarlos a Markdown plano. Nada más del kit se importa en código.
Por qué REIMPLEMENTA en vez de importar el resto
Este paquete reconoce los mismos nombres de fence que declaran
@ngx-docs-markdown-kit/parser-md-code-block
(code-block), @ngx-docs-markdown-kit/parser-md-image
(image) y @ngx-docs-markdown-kit/parser-md-card
(content-card, cards, card-code-block) -- pero no depende de NINGUNO de esos 3 paquetes. Los
nombres están hardcodeados en fence-downgraders.ts, a propósito duplicados en vez de importados:
esas 3 librerías son extensiones de Angular (.use() sobre MarkdownExtensionRegistry, con
componentes standalone reales) -- traerlas como dependencia habría arrastrado Angular entero a un
paquete que necesita poder correr en cualquier script de Node, sin bundler, sin zone.js, sin
@angular/core instalado siquiera. El acoplamiento real es más barato de mantener (una constante de
string por fence, ver Cómo funciona) que la alternativa de depender de 3
librerías de UI para leer 5 nombres.
El manifiesto que comparte con parser-md-seo
generate-readme.mjs (el script que corre en cada sitio, no este paquete en sí) lee el mismo
CONTENT_MANIFEST que también consume
generate-seo-files.mjs de
@ngx-docs-markdown-kit/parser-md-seo -- ambos scripts
parten del mismo árbol de páginas ya escaneado por parser-md, cada uno lo proyecta a un formato de
salida distinto (sitemap/BreadcrumbList/JSON-LD uno, README multi-flavor el otro). Ninguno de los
2 vuelve a tocar el filesystem de contenido por su cuenta ni conoce al otro -- convergen en el mismo
dato, generado una sola vez.
Quién lo consume
create-ngx-docs-site es el único consumidor real
hoy: lo instala por default en la plantilla de cualquier sitio nuevo (no es una extensión
--no-<id> opcional -- es infraestructura de publicación, siempre presente), junto con el script
generate-readme.mjs y el config converter.config.ts que lo invocan. Nada le impide correr fuera
de ese CLI -- el núcleo (generateFlavorDocument(), spliceProjectReadme(), etc.) solo pide un
array de ConverterPage[] ya resuelto, sin asumir de dónde salió.
Parser MD Converter de parser-md-converter
REGRESAR A APARTADOS DE parser-md-converter
parser-md-converter genera un README.md multi-flavor (GitHub/npmjs/NuGet) a partir del mismo
manifiesto de contenido que ya arma parser-md -- nucleo agnostico de framework, sin ninguna
dependencia de Angular, para poder correr en cualquier pipeline de build o publish.
Por que un README generado y no escrito a mano
Un README a mano se desincroniza del contenido real apenas el sitio crece -- este paquete lo resuelve al reves: splice granular por marcadores unicos (nunca colisionan entre proyectos que comparten un mismo README raiz de monorepo), deteccion de badges, y downgrade automatico de las extensiones de Markdown de este ecosistema (fences enriquecidos) al formato mas simple que cada flavor de destino soporta.
Que trae de base
Indice en "bloques" de 2 niveles (cada raiz del arbol de contenido es un bloque con su propio sub-indice, sin recursion global), y resguardo de tamano para no generar un README inmanejable en sitios con mucho contenido.
