Tengo un subconjunto muy pequeño de Markdown junto con algunos html personalizados que me gustaría analizar en los componentes React. Por ejemplo, me gustaría convertir esta siguiente cadena:
hello *asdf* *how* _are_ you !doing! today
En la siguiente matriz:
[ "hello ", <strong>asdf</strong>, " ", <strong>how</strong>, " ", <em>are</em>, " you ", <MyComponent onClick={this.action}>doing</MyComponent>, " today" ]
y luego lo devuelve desde una función de representación React (React representará la matriz correctamente como HTML formateado)
Básicamente, quiero darles a los usuarios la opción de usar un conjunto muy limitado de Markdown para convertir su texto en componentes con estilo (¡y en algunos casos mis propios componentes!)
Es imprudente peligrosamente SetInnerHTML, y no quiero traer una dependencia externa, porque todos son muy pesados y solo necesito una funcionalidad muy básica.
Actualmente estoy haciendo algo como esto, pero es muy frágil y no funciona en todos los casos. Me preguntaba si había una mejor manera:
function matchStrong(result, i) {
let match = result[i].match(/(^|[^\\])\*(.*)\*/);
if (match) { result[i] = <strong key={"ms" + i}>{match[2]}</strong>; }
return match;
}
function matchItalics(result, i) {
let match = result[i].match(/(^|[^\\])_(.*)_/); // Ignores \_asdf_ but not _asdf_
if (match) { result[i] = <em key={"mi" + i}>{match[2]}</em>; }
return match;
}
function matchCode(result, i) {
let match = result[i].match(/(^|[^\\])```\n?([\s\S]+)\n?```/);
if (match) { result[i] = <code key={"mc" + i}>{match[2]}</code>; }
return match;
}
// Very brittle and inefficient
export function convertMarkdownToComponents(message) {
let result = message.match(/(\\?([!*_`+-]{1,3})([\s\S]+?)\2)|\s|([^\\!*_`+-]+)/g);
if (result == null) { return message; }
for (let i = 0; i < result.length; i++) {
if (matchCode(result, i)) { continue; }
if (matchStrong(result, i)) { continue; }
if (matchItalics(result, i)) { continue; }
}
return result;
}
Aquí está mi pregunta anterior que condujo a esta.
font _italic *and bold* then only italic_ and normal
? ¿Cuál sería el resultado esperado? ¿O nunca estará anidado?asdf*
sin que desaparezca)Respuestas:
¿Cómo funciona?
Funciona leyendo un trozo de trozo por trozo, que podría no ser la mejor solución para cadenas realmente largas.
Cada vez que el analizador detecta que se está leyendo un fragmento crítico, es decir
'*'
o cualquier otra etiqueta de reducción, comienza a analizar fragmentos de este elemento hasta que el analizador encuentra su etiqueta de cierre.Funciona en cadenas de varias líneas, consulte el código, por ejemplo.
Advertencias
No ha especificado, o podría haber entendido mal sus necesidades, si existe la necesidad de analizar etiquetas que están en negrita y cursiva , mi solución actual podría no funcionar en este caso.
Sin embargo, si necesita trabajar con las condiciones anteriores, simplemente comente aquí y modificaré el código.
Primera actualización: modifica cómo se tratan las etiquetas de descuento
Las etiquetas ya no están codificadas, sino que son un mapa donde se puede extender fácilmente para satisfacer sus necesidades.
Se corrigieron los errores que mencionaste en los comentarios, gracias por señalar estos problemas = p
Segunda actualización: etiquetas de descuento de longitud múltiple
La forma más fácil de lograr esto: reemplazando caracteres de varias longitudes con un Unicode raramente usado
Aunque el método
parseMarkdown
aún no admite etiquetas de varias longitudes, podemos reemplazar fácilmente esas etiquetas de múltiples longitudes con un simplestring.replace
al enviar nuestrorawMarkdown
accesorio.Para ver un ejemplo de esto en la práctica, mire el
ReactDOM.render
, ubicado al final del código.Incluso si su aplicación hace soportar múltiples idiomas, hay caracteres Unicode no válidos que JavaScript sigue detectando, ej .:
"\uFFFF"
no es un Unicode válida, si no recuerdo mal, pero JS todavía será capaz de compararlo ("\uFFFF" === "\uFFFF" = true
)Puede parecer hack-y al principio, pero, dependiendo de su caso de uso, no veo ningún problema importante al usar esta ruta.
Otra forma de lograr esto
Bueno, podríamos rastrear fácilmente el último
N
(dondeN
fragmentos corresponde a la longitud de la etiqueta de longitud múltiple más larga).Habría que hacer algunos ajustes en la forma en que se
parseMarkdown
comporta el método del bucle interno , es decir, verificar si el fragmento actual es parte de una etiqueta de varias longitudes, si se usa como etiqueta; de lo contrario, en casos como``k
, tendríamos que marcarlo comonotMultiLength
o algo similar e impulsar ese fragmento como contenido.Código
Enlace al código (TypeScript) https://codepen.io/ludanin/pen/GRgNWPv
Enlace al código (vainilla / babel) https://codepen.io/ludanin/pen/eYmBvXw
fuente
This must be *bold*
conThis must be *bo_ld*
. Hace que el HTML resultante tenga un formato incorrectoParece que estás buscando una pequeña solución muy básica. No "super-monstruos" como
react-markdown-it
:)¡Me gustaría recomendarle https://github.com/developit/snarkdown que se ve muy ligero y agradable! Con solo 1 kb y extremadamente simple, puede usarlo y extenderlo si necesita otras características de sintaxis.
Lista de etiquetas admitidas https://github.com/developit/snarkdown/blob/master/src/index.js#L1
Actualizar
Acabo de notar los componentes de reacción, lo perdí al principio. Así que eso es genial para usted. Creo que tomar la biblioteca como ejemplo e implementar sus componentes requeridos personalizados para hacerlo sin configurar HTML peligrosamente. La biblioteca es bastante pequeña y clara. ¡Diviértete con eso! :)
fuente
El resultado:
Resultado de la prueba Regexp
Explicación:
Puede definir sus etiquetas en esta sección:
[*|!|_]
una vez que una de ellas coincida, se capturará como un grupo y se denominará "tag_begin".Y entonces
(?<content>\w+)
captura el contenido envuelto por la etiqueta.La etiqueta final debe ser la misma que la coincidente anteriormente, por lo que aquí se usa
\k<tag_begin>
, y si pasó la prueba, captúrela como un grupo y asígnele un nombre "tag_end", eso es lo que(?<tag_end>\k<tag_begin>))
está diciendo.En el JS has configurado una tabla como esta:
Use esta tabla para reemplazar las etiquetas coincidentes.
Sting.replace tiene una sobrecarga String.replace (regexp, function) que puede tomar grupos capturados como parámetros, usamos estos elementos capturados para buscar la tabla y generar la cadena de reemplazo.
[Actualización]
He actualizado el código, guardé el primero en caso de que alguien más no necesite componentes de reacción, y puede ver que hay poca diferencia entre ellos.
fuente
console.log
salida, verá que la matriz está llena de cadenas, no componentes reales de React: jsfiddle.net/xftswh41puedes hacerlo así:
fuente
A working solution purely using Javascript and ReactJs without dangerouslySetInnerHTML.
Acercarse
Búsqueda de personaje por personaje para los elementos de rebajas. Tan pronto como se encuentre uno, busque la etiqueta final para el mismo y luego conviértalo en html.
Etiquetas admitidas en el fragmento
Entrada y salida del fragmento:
JsFiddle: https://jsfiddle.net/sunil12738/wg7emcz1/58/
Código:
Explicación detallada (con ejemplo):
Supongamos que si la cadena es
How are *you* doing?
Mantener una asignación de símbolos a etiquetas["How are "]
y comienza el bucle interno hasta que encuentres el siguiente *.Now next between * and * needs to be bold
, los convertimos en elementos html por texto y los insertamos directamente en una matriz donde Tag = b del mapa. Si lo hace<Tag>text</Tag>
, reaccione internamente se convierte en texto y presione en matriz. Ahora la matriz es ["cómo están", usted ]. Romper del bucle internoHow are <b>you</b> doing?
Note: <b>you</b> is html and not text
Nota : Anidar también es posible. Necesitamos llamar a la lógica anterior en recursión
Para agregar nuevas etiquetas de apoyo
map
objeto con la clave como carácter y el valor como etiqueta correspondiente¿Es compatible con la anidación? No
¿Es compatible con todos los casos de uso mencionados por OP? si
Espero eso ayude.
fuente
asdf
que se representarán<pre>asdf</pre>
con fondo oscuro, ¿verdad? Déjame saber esto y lo veré. Incluso puedes intentarlo ahora. Un enfoque simple es: en la solución anterior, reemplace el `` '' en el texto con un carácter especial como ^ o ~ y mapeelo en la etiqueta previa. Entonces funcionará bien. Otro enfoque necesita más trabajo<pre>asdf</pre>
. ¡Gracias!pre
También he agregado el soporte de etiquetas. Avíseme si funciona