markdown.it el motor CommonMark utilizado por Discourse tiene una amplia gama de plugins
Anclas de encabezado, listas de definición, flechas inteligentes y la lista continúa.
Primero una advertencia
CommonMark está destinado a ser… Común. Cuanto más te alejes de la especificación, menos Común será tu Markdown. Puede dificultar la portabilidad a otras soluciones y, si no se tiene cuidado, causar inconsistencias internas en el análisis. Antes de volver a empaquetar algo, asegúrate de responder a la pregunta “¿realmente quiero volver a empaquetar esto?”.
Acabo de terminar de volver a empaquetar Discourse Footnote y tengo algunas lecciones que compartir sobre cómo hacerlo bien.
Pasos para los perezosos
Si eres perezoso y solo quieres empezar, la forma más fácil es simplemente bifurcar las notas al pie y cambiar archivos y nombres de variables. Fui bastante cuidadoso para asegurarme de que sigue las mejores prácticas, por lo que deberías tener un ejemplo sólido.
Movimientos iniciales, un reprocesamiento mínimo
Por lo que puedo ver, la mayoría de los plugins de markdown.it se envían como archivos js “vainilla”. En muchos casos, los plugins se envían simplemente como un único archivo js, como este: markdown-it-mark.js.
Lo ideal es dejar el original intacto, lo que significa que puedes simplemente copiar una versión actualizada del archivo a tu plugin sin necesidad de modificar el plugin existente.
El primer problema con el que te encontrarás es que tienes que enseñarle a tu plugin a cargar este JavaScript en el servidor, ya que el motor Markdown también se ejecuta en el servidor. Para hacerlo, simplemente puedes copiar el archivo tal cual a assets/javascripts/vendor/original-plugin.js y luego, en tu archivo plugin.rb, agregarías:
# esto le enseña a nuestro motor markdown a cargar tu archivo js vainilla
register_asset "javascripts/vendor/original-plugin.js", :vendored_pretty_text
Una vez que tengas incluido el cuerpo real del plugin, debes enseñarle a nuestra canalización cómo cargarlo e inicializarlo:
Crea un archivo llamado assets/javascripts/lib/discourse-markdown/your-extension.js
Este archivo se cargará automáticamente porque termina en .js Y está en el directorio discourse-markdown.
Un ejemplo simple puede ser:
export function setup(helper) {
// esto te permite cargar tu extensión solo si una configuración del sitio está habilitada
helper.registerOptions((opts, siteSettings) => {
opts.features["your-extension"] = !!siteSettings.enable_my_plugin;
});
// incluye en la lista blanca cualquier atributo que necesites soportar,
// de lo contrario, nuestro sanitizador los eliminará
helper.whiteList(["div.amazingness"]);
// también puedes hacer cosas sofisticadas como esta
helper.whiteList({
custom(tag, name, value) {
if ((tag === "a" || tag === "li") && name === "id") {
return !!value.match(/^fn(ref)?\\d+$/);
}
},
});
// finalmente, esta es la magia que usarías para registrar la extensión en
// nuestra canalización. whateverGlobal es el nombre del global que expone el plugin
// toma una única variable (md) que luego se usa para modificar la canalización
helper.registerPlugin(window.whateverGlobal);
}
Siempre haz pruebas
El bin/rake autospec de Discourse es consciente de los plugins ![]()
Esto significa que cuando agregas el archivo spec/pretty_text_spec.rb, cada vez que lo guardas, se ejecutará el archivo de prueba del plugin.
Lo uso extensivamente porque hace el trabajo mucho más rápido.
Supongamos que agregaste un plugin que cambia cada número en una publicación por un círculo 8, puedes llamarlo discourse-magic-8-ball.
Así es como estructuraría las pruebas:
require "rails_helper"
describe PrettyText do
it "puede ser deshabilitado" do
SiteSetting.enable_magic_8_ball = false
markdown = <<-MD
1 cosa
MD
html = <<-HTML
<p>1 cosa</p>
HTML
cooked = PrettyText.cook markdown.strip
expect(cooked).to eq(html.strip)
end
it "soporta magic 8 ball" do
markdown = <<-MD
1 cosa
MD
html = <<-HTML
<p>8 círculo cosa</p>
HTML
cooked = PrettyText.cook markdown.strip
expect(cooked).to eq(html.strip)
end
end
Puede que necesites “decorar publicaciones”
En algunos casos, los plugins funcionan mejor cuando agregan características “dinámicas” adicionales a tus publicaciones. Ejemplos de esto son el plugin poll o el plugin footnotes que agrega un “…” que muestra dinámicamente una información sobre herramientas.
si necesitas “decorar” publicaciones, agrega assets/javascripts/api-initializers/your-initializer.js
import { apiInitializer } from "discourse/lib/api";
export default apiInitializer((api) => {
const siteSettings = api.container.lookup("service:site-settings");
if (!siteSettings.enable_magic_8_ball) {
return;
}
api.decorateCookedElement((elem) => {
// tu magia increíble va aquí
});
});
Puede que necesites “procesar posteriormente” las publicaciones
En algunos casos, es posible que debas “procesar posteriormente” las publicaciones, el motor de renderizado markdown, por diseño, no es consciente de cierta información como, por ejemplo, post_id. En algunos casos, es posible que desees acceso del lado del servidor a la publicación y al HTML “cocido”, esto puede permitirte hacer cosas como activar trabajos en segundo plano, sincronizar campos personalizados o “corregir” el HTML autogenerado.
Para las notas al pie, necesitaba un id distinto para cada nota al pie, lo que significaba que necesitaba acceso a post_id, por lo que me vi obligado a realizar cambios en el HTML en el procesador de publicaciones (que se ejecuta en sidekiq)
Para conectarte, agregarías lo siguiente a tu archivo plugin.rb:
DiscourseEvent.on(:before_post_process_cooked) do |doc, post|
doc.css("a.always-bing").each do |a|
# esto siempre debería ir a bing
a["href"] = "https://bing.com"
end
end
Puede que necesites algo de CSS personalizado
Si deseas enviar CSS personalizado, asegúrate de registrar el archivo en plugin.rb
Agrega tu css a assets/stylesheets/magic.scss y luego ejecuta
register_asset "stylesheets/magic.scss"
Recuerda que “recargamos automáticamente” los cambios para que puedas modificar el CSS de tu plugin y ver los cambios sobre la marcha en desarrollo.
¡Buena suerte con tus aventuras de reprocesamiento! ![]()
Este documento está controlado por versiones: sugiere cambios en github.