El conocimiento es el nuevo dinero.
Aprender es la nueva manera en la que inviertes
Acceso Cursos

Documentar en Astro

¿Cómo puedes tener varias versiones de la misma página dentro de tu documentación? En el post de hoy estaremos hablando acerca de como añadir soporte multiversión a tu documentación basada en Astro

· 6 min de lectura
Documentar en Astro

Astro es muy popular ahora mismo, y con razón si me preguntas. El framework permite a los desarrolladores crear sitios web estáticos con gran flexibilidad y potencia.

La capacidad de integrar múltiples bibliotecas de renderizado por sí sola ayuda con la adopción cuando se está migrando desde otro lugar.

Y mientras que la creación de un sitio de documentación es probablemente trivial en este punto, quería cubrir un problema que se presentó para resolver recientemente: la adición de soporte multi-versión para tu sitio de documentación.

Así que vamos a empezar.

El problema


Construir un sitio de documentación no es más que construir un sitio estático con muchas páginas.

De nuevo, trivial si usas Astro.

Sin embargo, ¿qué pasa si esas páginas cambian cuando cambia la versión de tu producto?

Hay dos problemas principales que resolver cuando eso ocurre:

  • El más fácil: añadir la versión al proceso de enrutamiento. Es necesario poder pasar de una versión a otra y, dado que Astro utiliza un enrutamiento basado en rutas de archivos, tendrás que crear carpetas para cada versión.
  • La difícil: manejar todos los enlaces relativos para adaptarlos a la versión actual y mantener la coherencia de la navegación interna (el mismo enlace debe redirigir a diferentes versiones, en función de la versión seleccionada en ese momento).

Aunque no parezcan gran cosa, resolver estas cuestiones no es trivial. Echemos un vistazo a cómo se podrían implementar ambas características.

Resolver el problema


Tenemos dos problemas que abordar, así que vayamos uno por uno.

Añadir la versión a su proceso de enrutamiento


La parte fácil es asegurarse de que pasamos de rutas como /docs/getting-started a /docs/1.1.0/getting-started .

De esta forma, podemos seguir haciendo referencia al archivo getting-started.mdx que usaremos para el contenido de esa página, y para llegar a él, tendremos que ponerlo dentro de una carpeta llamada 1.1.0 .

Eso parece simple, todo lo que tenemos que hacer es cambiar nuestra estructura de carpetas, por lo que va desde:

/
 |_ docs
     |_ getting-started.mdx

A algo como esto:

/
 |_ docs
     |_ v1.0.0
     |  |_ getting-started.mdx
     |
     |_ v2.0.0
        |_ getting-started.mdx

Con ese cambio, ahora estamos dejando que Astro sepa dónde puede encontrar nuestro contenido, y lo hará. Si ejecutas npm run build ahora, construirá todo correctamente, pero bajo la nueva carpeta.

Pero no hemos terminado, esto es sólo la mitad del paso 1, todavía nos falta una gran parte de nuestro soporte de versiones: el cambiador de versiones.

Necesitamos añadir una forma para que el usuario cambie de versión, así que veamos ese código:

---
const VERSIONS = [
     {title: "2.6.0", url: "/v2.6.0"},
     {title: "2.7.0", url: "/v2.7.0"},
     {title: "3.0.0", url: "/v3.0.0"},
     {title: "3.1.1", url: ""}
 ]
---
<div class="container">
    <select id="version-selector">
        {VERSIONS.map( v => {
            return <option value={ v.url } >v{v.title}</option>
        })}
    </select>
</div>

<script is:inline>
   let currentVersion = ""
if(typeof window != "undefined") {
    currentVersion = window.location.pathname.split("/")[1]
}
if(currentVersion && currentVersion.indexOf("v") != 0) currentVersion = "";
function updateLocation(evt) {
    let selectedVersion = evt.target.value
    let versionRegExp = /v[0-9]+.[0-9]+.[0-9]+/
    let pathname = window.location.pathname.replace(/\/$/, "") //remove the trailing "/" if there is any
    let pathParts = pathname.split("/")
    if(pathname.match(versionRegExp)) {
        pathParts[1] = selectedVersion.replace("/", "")
    } else {
        pathParts = [pathParts[0], selectedVersion.replace("/", ""), ...pathParts.slice(2)]
    }
    //when we're visiting the root of the default version, we ignore the "index" part
    //but if we're inside a specific version coming from the default version, we need to add it
    if(pathParts[pathParts.length - 1] == 'index' && selectedVersion == "") {
        pathParts[pathParts.length - 1] = ""
    } else {
        if(pathParts[pathParts.length - 1] == '' && pathname === "") {
            pathParts[pathParts.length - 1] = "/index"
        }   
    }
    let newPath = `${window.location.protocol}//${window.location.host}${pathParts.join("/")}`
    window.location.href = newPath
}
function selectCurrentVersion(dropDown) {
    if(typeof currentVersion != "undefined") {
        dropDown.querySelector("option[value*='" + currentVersion + "']").setAttribute("selected", true)
    } else {
        dropDown.querySelector("option[value='']").setAttribute("selected", true)
    }
}
    const dropDown = document.getElementById("version-selector")
    selectCurrentVersion(dropDown)
    dropDown.addEventListener("change", updateLocation)
</script>

Este es un componente sencillo que puedes añadir donde quieras. Yo personalmente lo añadiría en la cabecera, ya que es donde la mayoría de los sitios de documentación lo añaden. Pero puedes hacer lo que quieras con él.

Como puedes ver en el código, el componente dibujará un desplegable de versiones basado en las versiones listadas dentro del array VERSIONS. De ahí obtendrá la etiqueta a mostrar, y la ruta.

Fíjate en que la última versión no tiene ruta, de esta forma puedes mantener tu última versión en la raíz bajo el directorio docs y sólo utilizará carpetas de versiones cuando se especifique una versión.

Entonces el selectCurrentVersion se ejecuta como parte del código JavaScript, esta función toma el número de versión de la URL y encuentra la versión correcta option del elemento a seleccionar.

Por último, con la onChange del desplegable, llamamos al evento updateLocation. Esta función se encarga de sobrescribir la versión actual con la seleccionada dentro de la ruta actual. Luego simplemente redirige al usuario a la nueva URL.

Pero espera un segundo, ¿significa esto ahora que cada vez que lanzas una nueva versión, tienes que revisar todos los enlaces de tu documentación y actualizar los enlaces internos para evitar que desplacen al usuario entre versiones?

Eso sería un golpe bajo, sobre todo si tu documentación es bastante grande, así que vamos a intentar hacer algo al respecto.

Manejo de todos los enlaces relativos


Tenemos que encontrar una manera de evitar mantener la versión real de la documentación dentro de las rutas hard-coded que añadimos a los docs.

Esto se debe a que si lo hacemos así, cada vez que publiquemos una nueva versión y dupliquemos la última versión de los documentos, tendremos que ejecutar un proceso de búsqueda y reemplazo.

Y si nos olvidamos de hacerlo, o si por alguna razón, nuestro patrón de búsqueda no capta todas las URLs, entonces publicaremos un sitio de documentación defectuoso.

Tenemos que encontrar una manera de asegurarnos de que escribimos nuestra documentación pensando siempre en la versión actual, y enlazando a otras secciones de la misma como si no hubiera versiones alternativas de la documentación.

La solución, fue añadir un script para arreglar las URLs directamente en el navegador:

<script >
    import { getVersionFromURL } from "~/util";

    //code to update the links inside the documents to make sure they link to the right
    //version 
	let links = document.getElementsByTagName("a")

	let version = getVersionFromURL(window.location.href)
	const host = import.meta.env.PUBLIC_SITE_URL ? import.meta.env.PUBLIC_SITE_URL : ""
	let urlParts = ["docs"]

	if(version != "") { //if we're not on the default version (the latest)
		version = "v" + version
		urlParts.push(version)
	}
	for(let l of links){
		if(l.href.indexOf(host) != -1) {
			let uri = l.href.replace(host, "")
			uri = uri.replace(`/docs`, "").replace(/\/v[0-9]+\.[0-9]+\.[0-9]+\//, "")
			urlParts.push(uri)
			l.setAttribute("href", urlParts.join("/").replace("//", "/"))
			urlParts.pop()
		}
	
	}
</script>

Pongo ese script en la parte inferior de mi archivo de diseño principal. De esta forma, el script se ejecuta cuando se carga el contenido.

Ese código se ejecutará a través de todos los enlaces, y buscará los que redirigen a un lugar dentro del dominio del sitio (idealmente, algo así como docs.sudominio.com) y sólo para esos enlaces, los recreará con la versión actual en ellos.

En otras palabras, tomará /docs/getting-started y lo transformará dinámicamente en /docs/v1.0.0/getting-started sin que tengas que hacer nada.

De este modo, todos los enlaces relativos (los que se utilizan para la navegación) se fijan automáticamente para la versión seleccionada en ese momento.

Y eso es todo, Astro permite una gran flexibilidad a la hora de crear componentes y añadir JavaScript a un sitio que de otro modo sería estático.

Estos ejemplos muestran dos casos de uso diferentes, uno donde el componente creado tiene una parte dinámica, por lo que a pesar de que el desplegable se renderiza en el servidor, todavía tenemos que añadir código JS extra para que funcione.

Y por otro lado, todos los enlaces renderizados en el servidor necesitan ser actualizados dinámicamente (en realidad, sólo los de navegación interna), así que añadimos un script a la página que hace precisamente eso. Y no necesitamos trabajar con ninguna otra librería de interfaz de usuario.

¿Te habías encontrado antes con este tipo de problema? ¿Cómo lo resolviste?

Gracias por llegar hasta el final de este blog quiero recordarte que todo esto es gratis y posible gracias a que tu compartes. Un fuerte abrazo y recuerda que el conocimiento es poder.

Invertir en conocimientos produce siempre los mejores beneficios. (Benjamín Franklin)

Fuente

Plataforma de cursos gratis sobre programación