parser-md-file-embed

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: ^0.1.0@ngx-docs-markdown-kit/parser-md-seo: ^0.1.0@ngx-docs-markdown-kit/parser-md-code-block: ^0.2.0@ngx-docs-markdown-kit/parser-md-code-block-themes: ^0.1.0@ngx-docs-markdown-kit/parser-md-image: ^0.1.0@ngx-docs-markdown-kit/parser-md-card: ^0.2.0@ngx-docs-markdown-kit/parser-md-file-embed: ^0.1.0@ngx-docs-markdown-kit/parser-md-converter: ^0.2.0@ngx-docs-markdown-kit/ui: ^0.1.0rxjs: ~7.8.0tslib: ^2.3.0
Apartados de parser-md-file-embed
- Docs -- Documentación de @ngx-docs-markdown-kit/parser-md-file-embed -- embeber el contenido real de un archivo de public/ dentro de un code-block, siempre sincronizado.
- Parser MD File Embed -- Embebe el contenido real de un archivo de public/ dentro de un code-block, siempre sincronizado, para sitios @ngx-docs-markdown-kit.
Docs de parser-md-file-embed
REGRESAR A APARTADOS DE parser-md-file-embed
Índice Docs de parser-md-file-embed
- Primeros pasos -- Instalación y wiring de la extensión.
- Instalación -- npm install + wiring de la extensión, el componente, y el lector de disco opcional para SSR.
- Uso -- Sintaxis del fence, rangos de líneas, y cómo se detecta el lenguaje para resaltar.
- [El fence ```file-embed](#el-fence-file-embed-de-parser-md-file-embed) -- Embebe el contenido REAL de un archivo de public/ dentro de un code-block -- siempre sincronizado, nunca una copia pegada a mano.
- Rangos de líneas -- Uno o varios fragmentos disjuntos de un mismo archivo, y el sentinel 0 para "desde/hasta donde el archivo tenga texto real".
- Detección de lenguaje -- Cómo se decide con qué lenguaje resaltar un archivo embebido -- la tabla por extensión, y cuándo gana el "lang" explícito.
- Ecosistema -- De qué depende @ngx-docs-markdown-kit/parser-md-file-embed, y por qué nunca reimplementa lo que ya hace parser-md-code-block.
- Relación con el ecosistema -- De qué depende @ngx-docs-markdown-kit/parser-md-file-embed, quién la consume, y por qué nunca depende de highlight.js.
Primeros pasos de parser-md-file-embed
< Índice Docs de parser-md-file-embed
Instalación de parser-md-file-embed
< Primeros pasos de parser-md-file-embed
npm install @ngx-docs-markdown-kit/parser-md-file-embed
Trae @ngx-docs-markdown-kit/parser-md-code-block
(^0.2.0 o superior) como dependencia real (siempre instalada junto con este paquete) -- todo el
resaltado/tema/copiado real se delega en <ndmk-code-block> de ese paquete, este nunca duplica esa
lógica.
Registrar la extensión
import { createParser } from '@ngx-docs-markdown-kit/parser-md';
import { fileEmbedExtension } from '@ngx-docs-markdown-kit/parser-md-file-embed';
const parser = createParser().use(fileEmbedExtension());
Renderizar el segmento
En el template, junto a los demás tipos de segmento:
@case ('file-embed') {
<ndmk-file-embed
[path]="segment.path"
[lang]="segment.lang"
[ranges]="segment.ranges"
[lineNumberLabel]="segment.lineNumberLabel"
/>
}
El lector de disco para SSR/prerender (opcional, recomendado)
FileEmbedComponent pide el archivo real por HttpClient -- funciona sin nada más, pero durante
el prerender de build (antes de que exista un servidor real escuchando) un self-fetch por HTTP
puede colgarse hasta un timeout. FILE_EMBED_DISK_READER evita eso: si está provisto, se usa disco
directo en el servidor; si no, cae solo al HttpClient de siempre.
// app.config.server.ts -- SOLO se importa desde acá, nunca desde main.ts (entrada de navegador),
// así "node:fs" nunca llega al bundle del navegador.
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { FILE_EMBED_DISK_READER } from '@ngx-docs-markdown-kit/parser-md-file-embed';
function readFileEmbedFromDisk(publicPath: string): string {
return readFileSync(resolve(process.cwd(), 'public', publicPath), 'utf-8');
}
const serverConfig: ApplicationConfig = {
providers: [
// ...
{ provide: FILE_EMBED_DISK_READER, useValue: readFileEmbedFromDisk },
],
};
Ver [El fence ```file-embed](#el-fence-file-embed-de-parser-md-file-embed) para la sintaxis completa, y
Rangos de líneas para el detalle de line_ranges.
Uso de parser-md-file-embed
< Índice Docs de parser-md-file-embed
El fence ```file-embed de parser-md-file-embed
Un fence normal (3 backticks) con un lang especial (file-embed), reconocido vía
buildSegment() -- cero cambios al núcleo de parser-md. A diferencia de escribir el código a
mano dentro del fence (lo que hacen ```code-block/```content-card), acá el fence solo trae
un PUNTERO (path, más algunos campos opcionales) -- el contenido real se pide en tiempo de
render, siempre el archivo actual, nunca una copia que se desactualiza cuando el archivo real
cambia.
Por qué solo archivos de public/
Un navegador solo puede pedir lo que el sitio realmente sirve -- nunca puede leer un archivo
arbitrario de tu repositorio, aunque el archivo exista en el mismo proyecto. Angular solo sirve
como asset estático lo que está bajo public/ (mismo motivo por el que ContentService, en
cualquier sitio de create-ngx-docs-site, pide el cuerpo de cada página por HTTP en vez de leerlo
del filesystem del cliente). Por eso path: siempre es relativo a public/ del propio sitio --
path: templates/user-service.cs referencia public/templates/user-service.cs, nunca un archivo
de otra parte del repo.
Ejemplo en vivo
path: templates/user-service.cs
line_ranges: 26-30
Esto embebe las líneas 26 a 30 de user-service.cs (un archivo real de public/templates/ de
este mismo sitio, ver Rangos de líneas para más ejemplos) tal cual están
en disco ahora mismo -- si ese archivo cambia, esta misma página muestra el cambio en el próximo
build, sin tocar este .md.
Referencia completa de campos
Todos son opcionales salvo path -- el ejemplo de arriba ya muestra la sintaxis completa del
fence en sí (```file-embed seguido de estos mismos campos, uno por línea):
path: templates/user-service.cs
lang: csharp
line_ranges: 26-30
line_number_label: true
| Campo | Obligatorio | Qué hace |
|---|---|---|
path |
Sí | Ruta relativa a public/ del sitio. Único campo obligatorio -- sin él, el fence queda sin reclamar. |
lang |
No | Solo se usa si la tabla por extensión (ver Detección de lenguaje) no reconoce el archivo por su nombre. |
line_ranges |
No | Uno o más rangos inicio-fin separados por coma (ver Rangos de líneas). Sin este campo, embebe el archivo completo. |
line_number_label |
No (default true) |
Gutter con los números de línea reales del archivo. line_number_label: false lo apaga. |
Cómo funciona por dentro
Dos piezas con una sola responsabilidad cada una, separadas a propósito:
fileEmbedExtension()(file-embed-extension.ts) parsea el fence conparseKeyValueLines(reusado deparser-md, el mismo parser que usanimage/content-card) y arma un segmento con el puntero SIN RESOLVER -- nunca lee el archivo real. Esto no es una limitación, es deliberado: esta función corre tanto en el servidor (prerender) como en el navegador (navegación cliente-a-cliente entre páginas ya hidratadas), y un navegador no tiene forma de leer un archivo arbitrario -- solo lo que ya está resuelto en el segmento puede viajar de forma segura a ambos lados.FileEmbedComponentes quien de verdad pide el archivo (porHttpClient, con un lector de disco opcional para SSR/prerender -- ver Instalación), corta el/los rango/s pedido/s (ver Rangos de líneas), y arma un<ndmk-code-block>por rango -- nunca reimplementa resaltado, tema, gutter de números, ni botón de copiar: todo eso es 100% trabajo de@ngx-docs-markdown-kit/parser-md-code-block, este paquete ni siquiera depende dehighlight.js. Ver Relación con el ecosistema para el detalle completo de esa frontera de responsabilidad.
El texto nunca se reformatea/reindenta -- se corta byte a byte, tal cual está en el archivo real. La única "inteligencia" de este paquete es leer el puntero y cortar el texto; todo lo visual (color, tema, números de línea, copiar al portapapeles) vive en el componente que reusa.
Rangos de líneas de parser-md-file-embed
line_ranges acepta uno o más pares inicio-fin, separados por coma -- cada par es un rango
ATÓMICO (nunca 2 arrays paralelos que se puedan desincronizar al reordenar/insertar uno).
Un solo rango
path: templates/user-service.cs
line_ranges: 32-57
Varios rangos disjuntos en un mismo embed
Útil para mostrar, por ejemplo, 2 trabajos de un mismo pipeline sin el trabajo intermedio que no aporta al ejemplo:
path: templates/deploy.yml
line_ranges: 8-16, 33-45
Cada rango se renderiza como su propio <ndmk-code-block>, con un separador visual (⋯) entre
ellos -- nunca se concatenan en un solo bloque de texto (cada uno mantiene su propio botón de
copiar, con SOLO el código de ese rango).
Sintaxis
path: templates/archivo.ts
line_ranges: 1-5, 45-63, 95-98
El sentinel 0
Sin line_ranges, el default es 0-0 -- el archivo completo, pero recortado de líneas en blanco
al principio/final (no necesariamente la línea física 1 ni la última línea física). 0 en
cualquiera de los 2 lados de un rango individual significa lo mismo:
0en el lado IZQUIERDO (start) resuelve a la primera línea REAL con texto del archivo.0en el lado DERECHO (end) resuelve a la última línea REAL con texto del archivo.
Esto importa porque muchos archivos reales tienen una línea en blanco al final (la mayoría de editores la agregan solo) -- sin este sentinel, embeber "todo el archivo" mostraría esa línea vacía colgando al final, sin ningún valor real.
path: templates/app-config.json
Ese ejemplo de arriba usa el default (0-0) -- muestra app-config.json completo, sin importar
si termina o no con una línea en blanco.
Un rango desactualizado nunca rompe la página
Si el archivo real encoge (alguien borra líneas) y un rango queda fuera de límites, se recorta al
total real de líneas en vez de fallar -- una documentación con un line_ranges desactualizado
sigue mostrando algo razonable, nunca una página rota. Nada se reformatea nunca: el texto sale
byte a byte, tal cual está en el archivo real -- ver El fence file-embed
para por qué esto es una garantía deliberada, no un detalle de implementación.
Detección de lenguaje de parser-md-file-embed
El lenguaje que se le pasa a <ndmk-code-block> (para el resaltado de sintaxis) se decide en este
orden -- el primero que da una respuesta gana:
- Tabla por extensión/nombre de archivo (interna, ~50 entradas) -- deriva del
path, que es obligatorio y siempre está actualizado. Gana SIEMPRE que tenga una entrada. lang:explícito en el fence -- solo se usa si la tabla no reconoce la extensión.- Auto-detección de
highlight.js-- si ninguno de los 2 anteriores dio un lenguaje, o el lenguaje elegido no está registrado hoy (verparser-md-code-block), cae ahí -- sigue coloreando, solo que adivinando en vez de con la gramática exacta.
Por qué la tabla gana sobre lang: explícito, no al revés
Si lang: explícito ganara siempre, cambiar path: Foo.cs a path: Foo.json sin acordarse de
actualizar lang: csharp dejaría un lenguaje incorrecto pegado, en silencio. Como la tabla se
deriva del campo OBLIGATORIO (path), nunca puede quedar desactualizada -- lang: explícito
existe solo para el caso real donde la tabla no tiene una entrada (una extensión rara, o un
archivo sin extensión que no está en la lista de nombres exactos).
path: templates/app-config.json
line_ranges: 8-16
El ejemplo de arriba nunca declaró lang: -- app-config.json se reconoce solo por su extensión.
Nunca depende de highlight.js
Este paquete no importa highlight.js en ningún momento -- la tabla es solo texto ("cs" -> "csharp", strings planos), nunca una gramática real. Si un lenguaje de la tabla no está
REGISTRADO hoy en parser-md-code-block (ver su propio catálogo,
DEFAULT_CODE_BLOCK_LANGUAGES), el string igual se
le pasa a <ndmk-code-block>, que cae solo a auto-detección -- este paquete nunca necesita saber
cuáles gramáticas están cargadas. Ver Relación con el ecosistema
para el porqué de este límite de responsabilidad.
La tabla completa
| Extensión | Lenguaje | Extensión | Lenguaje | |
|---|---|---|---|---|
.cs |
csharp |
.py |
python |
|
.ts / .tsx |
typescript |
.rb |
ruby |
|
.js / .jsx / .mjs / .cjs |
javascript |
.php |
php |
|
.json |
json |
.pl |
perl |
|
.html / .htm |
xml |
.lua |
lua |
|
.xml / .svg / .vue |
xml |
.java |
java |
|
.css |
css |
.kt / .kts |
kotlin |
|
.scss |
scss |
.scala |
scala |
|
.less |
less |
.go |
go |
|
.sh / .bash / .zsh |
bash |
.rs |
rust |
|
.ps1 |
powershell |
.c / .h |
c |
|
.yml / .yaml |
yaml |
.cpp / .cc / .hpp |
cpp |
|
.toml / .ini / .cfg |
ini |
.fs / .fsx |
fsharp |
|
.sql |
sql |
.vb |
vbnet |
|
.md / .markdown |
markdown |
.dart |
dart |
|
.txt |
plaintext |
.swift |
swift |
|
.graphql / .gql |
graphql |
.m |
objectivec |
|
.diff / .patch |
diff |
.r |
r |
|
.properties |
properties |
.hs |
haskell |
|
.tex |
latex |
.ex / .exs |
elixir |
|
.groovy |
groovy |
.erl |
erlang |
|
.nginx / .conf |
nginx |
.clj |
clojure |
|
Dockerfile (nombre exacto) |
dockerfile |
Makefile (nombre exacto) |
makefile |
Dockerfile/Makefile se reconocen por NOMBRE de archivo exacto (sin extensión), no por
extensión -- el resto de la tabla mapea por extensión, tomando siempre la ÚLTIMA (archivo.conf.template
usa la extensión .template, no .conf -- una limitación conocida del diseño simple de esta
tabla, cubrible con lang: explícito si hace falta).
Ecosistema de parser-md-file-embed
< Índice Docs de parser-md-file-embed
Relación con el ecosistema de parser-md-file-embed
< Ecosistema de parser-md-file-embed
@ngx-docs-markdown-kit/parser-md-file-embed es una extensión de
parser-md -- no funciona sola, necesita registrarse con
.use(fileEmbedExtension()) sobre un parser ya creado con createParser(). Ver
Instalación para el wiring completo.
De qué depende
@ngx-docs-markdown-kit/parser-md(peer) -- el motor que extiende. De ahí saleparseKeyValueLines(reusado para parsear el fence) y el propio mecanismo de extensión (buildSegment).@ngx-docs-markdown-kit/parser-md-code-block(dependencia REAL, no peer,^0.2.0o superior) -- declarada enng-package.jsonbajoallowedNonPeerDependencies.FileEmbedComponentcompone<ndmk-code-block>directo, uno por rango -- resaltado, tema, gutter de números de línea, botón de copiar, TODO eso es de ese paquete.parser-md-code-blockse instala SIEMPRE junto conparser-md-file-embed, sin importar si el sitio expone o no el fence```code-blockpropio de ese paquete.
Nunca depende de highlight.js -- ni directa ni transitivamente en el sentido de necesitar
saber algo de él. La tabla de detección de lenguaje (ver Detección de
lenguaje) es solo texto plano; el resaltado real lo hace
parser-md-code-block por su cuenta, con su propia copia de highlight.js/lib/core.
Por qué una responsabilidad, no dos
Hasta una versión temprana de este paquete, FileEmbedComponent armaba su PROPIO gutter de
números de línea al lado de <ndmk-code-block> (2 elementos por separado, tratando de coincidir
en altura por CSS) -- un comentario multi-línea real (un solo <span> de highlight.js con
varios saltos de línea reales adentro) desalineaba ese gutter cada vez más a medida que aparecían
más líneas largas. La cantidad de líneas SIEMPRE daba bien (por eso no se veía en los tests que
solo comparaban esa cantidad) -- el problema era de layout, no de conteo: 2 elementos
independientes nunca pueden garantizar la misma altura por línea sin duplicar sus constantes de
CSS a mano.
El fix real fue mover la numeración ADENTRO de parser-md-code-block
(lineNumbersVisible/lineNumbersLabelStartLine, ver su propio
CHANGELOG) -- número y
código de una misma línea ahora viven en la MISMA fila, no en 2 layouts separados. Esto dejó a
parser-md-file-embed con una sola responsabilidad real: conseguir el texto del archivo y cortar
el rango pedido. Todo lo visual es 100% de parser-md-code-block, sin excepción.
Quién la usa hoy
create-ngx-docs-site (el CLI generador) la trae por default en la plantilla de cualquier
sitio nuevo -- se puede omitir con el flag --no-file-embed. Ninguna otra librería del ecosistema
depende de parser-md-file-embed.
Alcance conocido
path: solo puede referenciar archivos que YA están dentro de public/ del propio sitio --
nunca otro lugar del repositorio (ver [El fence ```file-embed](#el-fence-file-embed-de-parser-md-file-embed) para el
porqué). Si el archivo real que querés documentar vive en otra carpeta de tu proyecto (código
fuente de una librería, por ejemplo), tenés que copiarlo/mantenerlo también dentro de public/ --
este paquete no trae ningún mecanismo de build que lo copie automáticamente desde otra parte del
repo.
Parser MD File Embed de parser-md-file-embed
REGRESAR A APARTADOS DE parser-md-file-embed
parser-md-file-embed extiende parser-md con un fence ```file-embed que incrusta el
contenido REAL de un archivo de public/ (o uno o varios rangos de líneas) dentro de un
code-block -- siempre sincronizado con el archivo real, nunca una copia pegada a mano que se
desactualiza cada vez que ese archivo cambia.
Por qué esto, en vez de copiar y pegar
Documentar un Dockerfile/docker-compose.yml/config real a mano en un bloque de código es una
copia que se desincroniza la primera vez que alguien toca el archivo original y se olvida de
actualizar la documentación -- un problema real, no hipotético. Con file-embed, la documentación
siempre muestra el archivo tal cual está HOY, sin ningún paso manual.
Qué trae de base
Resaltado de sintaxis real, gutter de números de línea, tema de color, y botón de copiar --
TODO eso reusado de parser-md-code-block, nunca
reimplementado acá (este paquete no depende de highlight.js en ningún momento). Su única
responsabilidad real: conseguir el texto del archivo real y cortar el rango pedido.