URLPattern aporta el enrutamiento a la plataforma web

Un enfoque para estandarizar los casos de uso comunes de coincidencia de patrones.

El enrutamiento es una parte clave de cada aplicación web. En esencia, el enrutamiento implica tomar una URL, aplicarle alguna coincidencia de patrones o alguna otra lógica específica de la app y, luego, por lo general, mostrar contenido web según el resultado. El enrutamiento se puede implementar de varias maneras: a veces, es un código que se ejecuta en un servidor que asigna una ruta de acceso a archivos en el disco o es una lógica en una app de una sola página que espera los cambios en la ubicación actual y crea la parte del DOM correspondiente para mostrar.

Si bien no existe un estándar definitivo, los desarrolladores web se han inclinado hacia una sintaxis común para expresar patrones de enrutamiento de URL que tienen mucho en común con regular expressions, pero con algunas incorporaciones específicas del dominio, como tokens para hacer coincidir segmentos de ruta. Los frameworks populares del servidor, como Express y Ruby on Rails, usan esta sintaxis (o algo muy similar), y los desarrolladores de JavaScript pueden usar módulos como path-to-regexp o regexpparam para agregar esa lógica a su propio código.

URLPattern es un complemento de la plataforma web que se basa en la base creada por estos frameworks. Su objetivo es estandarizar una sintaxis de patrón de enrutamiento, incluida la compatibilidad con comodines, grupos de tokens con nombre, grupos de expresiones regulares y modificadores de grupos. Las instancias de URLPattern creadas con esta sintaxis pueden realizar tareas de enrutamiento comunes, como hacer coincidir URLs completas o una URL pathname, y mostrar información sobre las coincidencias de tokens y grupos.

Otro beneficio de proporcionar la coincidencia de URLs directamente en la plataforma web es que se puede compartir una sintaxis común con otras APIs que también deben coincidir con las URLs.

Compatibilidad con navegadores y polyfills

URLPattern está habilitado de forma predeterminada en Chrome y Edge 95 y versiones posteriores.

La biblioteca urlpattern-polyfill proporciona una forma de usar la interfaz URLPattern en navegadores o entornos como Node que no tienen compatibilidad integrada. Si usas el polyfill, asegúrate de usar la detección de funciones para asegurarte de cargarlo solo si el entorno actual no es compatible. De lo contrario, perderás uno de los beneficios clave de URLPattern: el hecho de que los entornos de compatibilidad no tienen que descargar ni analizar código adicional para usarlo.

if (!(globalThis && 'URLPattern' in globalThis)) {
  // URLPattern is not available, so the polyfill is needed.
}

Compatibilidad de sintaxis

Una filosofía fundamental de URLPattern es evitar la reinvención. Si ya conoces la sintaxis de enrutamiento que se usa en Express o Ruby on Rails, no deberías tener que aprender nada nuevo. Sin embargo, debido a las ligeras divergencias entre las sintaxis en las bibliotecas de enrutamiento populares, se debía elegir algo como la sintaxis base, y los diseñadores de URLPattern decidieron usar la sintaxis de patrones de path-to-regexp (aunque no su superficie de API) como punto de partida.

Esta decisión se tomó después de una consulta exhaustiva con el encargado actual de path-to-regexp.

La mejor manera de familiarizarte con el núcleo de la sintaxis compatible es consultar la documentación de path-to-regexp. Puedes leer la documentación diseñada para publicarse en MDN en su página principal actual en GitHub.

Características adicionales

La sintaxis de URLPattern es un superconjunto de lo que admite path-to-regexp, ya que URLPattern admite una función poco común entre las bibliotecas de enrutamiento: coincidencia de orígenes, incluidos los comodines en los nombres de host. La mayoría de las otras bibliotecas de enrutamiento solo se ocupan de la pathname y, en ocasiones, de la parte de búsqueda o hash de una URL. Nunca tienen que verificar la parte de origen de una URL, ya que solo se usan para el enrutamiento de origen en una app web independiente.

Tener en cuenta los orígenes da lugar a casos de uso adicionales, como enrutar solicitudes de origen cruzado dentro del controlador de eventos fetch de un service worker. Si solo enrutas URLs del mismo origen, puedes ignorar de manera eficaz esta función adicional y usar URLPattern como otras bibliotecas.

Ejemplos

Cómo construir el patrón

Para crear un URLPattern, pasa a su constructor cadenas o un objeto cuyas propiedades contengan información sobre el patrón con el que se debe hacer coincidir.

Pasar un objeto ofrece el control más explícito sobre qué patrón usar para hacer coincidir cada componente de URL. En su forma más detallada, puede verse de la siguiente manera:

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
  search: '*',
  hash: '*',
});

Si proporcionas una cadena vacía para una propiedad, solo se hará coincidir si no se configuró la parte correspondiente de la URL. El comodín * coincidirá con cualquier valor para una parte determinada de la URL.

El constructor ofrece varias combinaciones de teclas para facilitar el uso. Omitir por completo search y hash, o cualquier otra propiedad, equivale a configurarlos en el comodín '*'. El ejemplo anterior se podría simplificar a

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
});

Como atajo adicional, toda la información sobre el origen se puede proporcionar en una sola propiedad, baseURL, lo que genera

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

En todos estos ejemplos, se supone que tu caso de uso incluye orígenes coincidentes. Si solo te interesa hacer coincidir las otras partes de la URL, sin incluir el origen (como en muchos casos de enrutamiento "tradicional" de un solo origen), puedes omitir la información del origen por completo y proporcionar alguna combinación de las propiedades pathname, search y hash. Al igual que antes, las propiedades omitidas se tratarán como si se hubieran establecido en el patrón de comodín *.

const p = new URLPattern({pathname: '/foo/:image.jpg'});

Como alternativa a pasar un objeto al constructor, puedes proporcionar una o dos cadenas. Si se proporciona una cadena, esta debe representar un patrón de URL completo, incluida la información del patrón que se usa para hacer coincidir el origen. Si proporcionas dos cadenas, la segunda se usa como baseURL y la primera se considera en relación con esa base.

Ya sea que se proporcione una cadena o dos, el constructor URLPattern analizará el patrón de URL completo, lo dividirá en componentes de URL y asignará cada parte del patrón más grande al componente correspondiente. Esto significa que, en el fondo, cada URLPattern creado con cadenas termina representándose de la misma manera que un URLPattern equivalente creado con un objeto. El constructor de cadenas es solo un atajo para quienes prefieren una interfaz menos detallada.

const p = new URLPattern('https://example.com/foo/:image.jpg?*#*');

Cuando se usan cadenas para crear un URLPattern, hay algunas advertencias que se deben tener en cuenta.

Dejar una propiedad cuando se usa un objeto para construir URLPattern equivale a proporcionar un comodín * para esa propiedad. Cuando se analiza el patrón de cadena de URL completa, si falta un valor en uno de los componentes de la URL, se trata como si la propiedad del componente se hubiera establecido en '', que solo coincidirá cuando ese componente esté vacío.

Cuando usas cadenas, debes incluir de forma explícita los comodines si quieres que se usen en la URLPattern construida.

// p1 and p2 are equivalent.
const p1 = new URLPattern('/foo', location.origin);
const p2 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
  search: '',
  hash: '',
});

// p3 and p4 are equivalent.
const p3 = new URLPattern('/foo?*#*', location.origin);
const p4 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
});

También debes tener en cuenta que el análisis de un patrón de cadena en sus componentes es potencialmente ambiguo. Hay caracteres, como :, que se encuentran en las URLs, pero que también tienen un significado especial en la sintaxis de coincidencia de patrones. Para evitar esta ambigüedad, el constructor URLPattern supone que cualquiera de esos caracteres especiales forma parte de un patrón, no de la URL. Si deseas que un carácter ambiguo se interprete como parte de la URL, asegúrate de escaparlo con un \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` cuando se proporcione como una cadena.

Cómo usar el patrón

Después de construir un URLPattern, tienes dos opciones para usarlo. Los métodos test() y exec() toman la misma entrada y usan el mismo algoritmo para verificar si hay una coincidencia, y solo difieren en su valor que se muestra. test() muestra true cuando hay una coincidencia para la entrada dada y false en caso contrario. exec() muestra información detallada sobre la coincidencia junto con los grupos de captura, o null si no hay coincidencia. En los siguientes ejemplos, se muestra el uso de exec(), pero puedes intercambiar test() por cualquiera de ellos si solo deseas que se muestre un valor booleano simple.

Una forma de usar los métodos test() y exec() es pasar cadenas. Al igual que lo que admite el constructor, si se proporciona una sola cadena, esta debe ser una URL completa, incluido el origen. Si se proporcionan dos strings, la segunda se trata como un valor baseURL y la primera se evalúa como relacionada con esa base.

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

const result = p.exec('https://example.com/foo/cat.jpg');
// result will contain info about the successful match.
// const result = p.exec('/foo/cat.jpg', 'https://example.com')
// is equivalent, using the baseURL syntax.

const noMatchResult = p.exec('https://example.com/bar');
// noMatchResult will be null.

Como alternativa, puedes pasar el mismo tipo de objeto que admite el constructor, con propiedades que se establecen solo en las partes de la URL que te interesan para que coincidan.

const p = new URLPattern({pathname: '/foo/:image.jpg'});

const result = p.exec({pathname: '/foo/:image.jpg'});
// result will contain info about the successful match.

Cuando usas exec() en un URLPattern que contiene comodines o tokens, el valor que se muestra te brindará información sobre cuáles eran los valores correspondientes en la URL de entrada. Esto puede ahorrarte la molestia de tener que analizar esos valores por tu cuenta.

const p = new URLPattern({
  hostname: ':subdomain.example.com',
  pathname: '/*/:image.jpg'
});

const result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'

Grupos anónimos y con nombre

Cuando pasas una cadena de URL a exec(), se muestra un valor que indica qué partes coincidieron con todos los grupos del patrón.

El valor que se muestra tiene propiedades que corresponden a los componentes de URLPattern, como pathname. Por lo tanto, si se definió un grupo como parte de la porción pathname de URLPattern, las coincidencias se pueden encontrar en el pathname.groups del valor que se muestra. Las coincidencias se representan de manera diferente según si el patrón correspondiente era un grupo anónimo o con nombre.

Puedes usar índices de array para acceder a los valores de una coincidencia de patrón anónima. Si hay varios patrones anónimos, el índice 0 representará el valor coincidente para el que se encuentra más a la izquierda, con 1 y otros índices para los patrones posteriores.

Cuando se usan grupos con nombre en un patrón, las coincidencias se exponen como propiedades cuyos nombres corresponden al nombre de cada grupo.

Compatibilidad y normalización con Unicode

URLPattern admite caracteres Unicode de diferentes maneras.

  • Los grupos nombrados, como :café, pueden contener caracteres Unicode. Las reglas que se usan para los identificadores de JavaScript válidos se aplican a los grupos nombrados.

  • El texto dentro de un patrón se codificará automáticamente de acuerdo con las mismas reglas que se usan para la codificación URL de ese componente en particular. Los caracteres Unicode dentro de pathname se codificarán en porcentaje, por lo que un patrón pathname como /café se normalizará a /caf%C3%A9 automáticamente. Los caracteres Unicode en hostname se codifican automáticamente con Punycode, en lugar de codificación porcentual.

  • Los grupos de expresiones regulares solo deben contener caracteres ASCII. La sintaxis de expresión regular dificulta y no segura la codificación automática de caracteres Unicode en estos grupos. Si deseas hacer coincidir un carácter Unicode en un grupo de expresiones regulares, debes codificarlo manualmente, como (caf%C3%A9) para que coincida con café.

Además de codificar caracteres Unicode, URLPattern también realiza la normalización de URLs. Por ejemplo, /foo/./bar en el componente pathname se contrae a la /foo/bar equivalente.

Si tienes dudas sobre cómo se normalizó un patrón de entrada determinado, inspecciona la instancia URLPattern construida con las DevTools de tu navegador.

Revisión general

En la demostración de Glitch incorporada a continuación, se ilustra un caso de uso principal de URLPattern dentro de fetch event handler de un trabajador del servicio, que asigna patrones específicos a funciones asíncronas que podrían generar una respuesta a las solicitudes de red. Los conceptos de este ejemplo también se pueden aplicar a otras situaciones de enrutamiento, ya sea del servidor o del cliente.

Comentarios y planes futuros

Si bien la funcionalidad básica de URLPattern llegó a Chrome y Edge, se planean más incorporaciones. Algunos aspectos de URLPattern aún están en desarrollo y hay una serie de preguntas abiertas sobre comportamientos específicos que aún se pueden definir mejor. Te recomendamos que pruebes URLPattern y envíes tus comentarios a través de un problema de GitHub.

Compatibilidad con plantillas

La biblioteca path-to-regexp proporciona un compile() function que invierte de manera eficaz el comportamiento de enrutamiento. compile() toma un patrón y valores para los marcadores de posición de tokens y muestra una cadena para una ruta de URL con esos valores reemplazados.

Esperamos agregar esto a URLPattern en el futuro, pero no está dentro del alcance de la versión inicial.

Habilitación de funciones futuras de la plataforma web

Si suponemos que URLPattern se convierte en una parte establecida de la plataforma web, otras funciones que podrían beneficiarse del enrutamiento o la coincidencia de patrones pueden basarse en ella como una primitiva.

Se están realizando discusiones sobre el uso de URLPattern para las funciones propuestas, como la coincidencia de patrones de alcance del trabajador de servicio, los PWA como controladores de archivos y la precarga especulativa.

Agradecimientos

Consulta el documento explicativo original para obtener una lista completa de los reconocimientos.