Usando o VS Code e o Node para escrever HTML com estilo
Neste artigo, explicarei como você pode usar o poder do Visual Studio Code e do Node.js para criar uma ferramenta de edição personalizada para superar plataformas de blog teimosas e apenas escrever Markdown.
As especificidades deste artigo são adaptadas ao problema que estávamos tendo, mas a estratégia envolvida aqui pode ser aplicada a um grande número de tarefas em que você gostaria de usar o Markdown para criar algum conteúdo para um sistema legado ou criar outros fluxos de trabalho de editor personalizados ainda mais interessantes. Por exemplo, digamos que você queira criar algum HTML para inclusão em um e-mail, você está fortemente limitado quanto à marcação que pode usar.

O problema
Infragistics fornece fóruns e blogs há muito tempo e, como costuma acontecer quando você precisa manter uma grande biblioteca de conteúdo acessível, está executando um software de blog bastante antigo que funciona, mas não é necessariamente muito agradável de se trabalhar. Os principais vetores para colocar conteúdo no sistema incluem:
- Trabalhar com um editor WYSIWYG baseado na web com bugs que não suporta muito bem a inserção de trechos de código e faz viagens incorretas ao conteúdo existente, fazendo com que o conteúdo mude de maneiras imprevisíveis se você editá-lo novamente.
- Usando um botão no referido editor WYSIWYG baseado na web com bugs para injetar código HTML literal, que coage o HTML injetado em formas às vezes com bugs, presumivelmente para evitar padrões inseguros, mas, geralmente, resulta em coisas que não parecem nada como o autor pretendia.
- Trabalhando com magias negras e lutando contra o obscuro logon único em sistemas para conectar o Windows Live Writer (agora Open Live Writer) à interface do serviço Web e injetar conteúdo de lá, apenas para descobrir que o mecanismo do blog ainda está mutilando o HTML enviado de maneiras indesejáveis.
Além da dificuldade de realmente colocar o conteúdo no sistema, há o fato de que Infragistics conteúdo do blog é pesado no uso de trechos de código. Idealmente, eles devem ter uma boa aparência e realce de sintaxe.
Se você usa um serviço de armazenamento de snippets como o Github Gist, pode pensar que essa parte é fácil, mas nosso problema é que nosso software Blog suprime e remove iframe
especificamente elementos do conteúdo da postagem. Acredito que este seja um conceito de segurança devido ao mecanismo estar inadequadamente preparado para delinear entre o conteúdo da postagem e o conteúdo do comentário no que diz respeito à edição, e você não gostaria que seus redatores de comentários injetassem iframes!
Infelizmente, a maioria dos softwares de snippets externos decentes gostaria de carregar o snippet em um iframe
ou precisa de JavaScript personalizado para ser executado (outro não-não no que diz respeito ao nosso mecanismo de blog).
Por um tempo, estávamos executando um pouco de JavaScript no nível do site, que aplicava algum realce de sintaxe a pre
tags formatadas de maneira muito precisa, mas esse JavaScript continuava desaparecendo toda vez que nossa equipe do site fazia algumas revisões importantes no site, fazendo com que todos os artigos que dependiam dele parecessem repentinamente atrozes. Quando isso desapareceu pela última vez, comecei a preparar essa solução.
Finalmente, trabalhar em um editor WYSIWYG em um portal de blog proprietário não oferece muito em maneiras de revisar e iterar o conteúdo antes de ir ao ar. Idealmente, alguém deve ser capaz de pedir a revisão de uma postagem de blog de colegas ou editores de texto antes de publicar. A aproximação mais próxima que tivemos disso foi postar um rascunho de postagem não listado e solicitar feedback. No entanto, não havia como anotar e comentar o conteúdo embutido, como você pode dizer, um arquivo markdown armazenado no git com alterações feitas usando um fluxo de trabalho de solicitação de pull.
Agora, felizmente, tenho certeza de que estamos atualizando nosso software de blog em breve, para um mecanismo muito mais recente, então esse é um problema de curto prazo que estou abordando (espero), mas essa solução de curto prazo não foi tão difícil de criar, pode continuar a ser relevante após a atualização e oferece uma ótima estratégia para criar fluxos de trabalho de edição personalizados para contornar problemas semelhantes aos nossos. Muitos de nossos engenheiros blogariam menos ou não blogariam devido às barreiras à criação e iteração do conteúdo, reduzir essas barreiras incentiva mais conteúdo.
O Plano
Ok, vamos resumir os problemas a serem superados:
- Fazer qualquer coisa além de injetar HTML literal no sistema atual é muito difícil, e mesmo isso precisa ser feito com cuidado, pois há muitos padrões HTML que o sistema ignorará, destruirá ou deturpará.
- Trabalhar com o editor WYSIWYG do mecanismo de blog ou mesmo com o Open Live Writer é uma grande dor, especialmente ao trabalhar com trechos de código ou iterar no conteúdo.
- Precisamos de trechos de código que tenham uma boa aparência e que não explodam quando algum JavasScript externo desaparece.
- Precisamos de alguma maneira de ter um fluxo de trabalho de revisão para o conteúdo.
E aqui está um plano para resolver os problemas:
- O VS Code é gratuito e tem uma ótima visualização de Markdown lado a lado ao editar um arquivo markdown.
- Node.js e gulp nos permitem iniciar uma tarefa em segundo plano para converter continuamente nossos arquivos Markdown em HTML, sempre que eles são salvos.
- Como podemos executar JavaScript arbitrário como parte dos pipelines gulp, devemos ser capazes de realizar um trabalho adicional para manipular o HTML produzido para que o software de blog o aceite normalmente.
- Se a experiência de edição for apenas iterar em arquivos Markdown no VS Code, devemos ser capazes de armazenar esses arquivos Markdown no git e usar um fluxo de trabalho de revisão de solicitação de pull padrão para criar um processo de revisão para nossas postagens.
A solução
OK, primeiro precisamos ter certeza de que temos o Visual Studio Code e Node.js instalados, pois eles serão os principais cavalos de batalha neste fluxo de trabalho. Depois de instalados, precisamos criar um diretório que hospedará os arquivos markdown, por exemplo c:\Blogging
, e abrir o VS Code apontando para essa pasta.
Primeiro crie um arquivo vazio package.json
no diretório que contenha apenas:
{ }
Agora precisaremos executar um monte de comandos no console no contexto da pasta, então abra o terminal integrado via:
View => Integrated Terminal
Em seguida, precisamos instalar globalmente o markdown-it, que é o pacote Node.js que usaremos para converter o Markdown para HTML:
npm install -g markdown-it
Em seguida, precisamos instalar o gulp global e localmente, e algumas extensões gulp, que ajudarão a gerenciar o fluxo de trabalho de conversão dos arquivos markdown para HTML:
npm install -g gulp npm install gulp gulp-markdown-it --save
Isso deve ser suficiente para nos permitir escrever um gulpfile.js
que converterá continuamente os arquivos Markdown em nosso diretório em HTML à medida que são salvos e iniciar o processo com um Ctrl + Shift + B
pouco de mágica do VS Code.
Primeiro, criaremos um arquivo Markdown de teste chamado test.md
na pasta e forneceremos a ele algum conteúdo:
## Introduction This is an introduction. This is another paragraph. ```cs //this is a gated code block public class TestClass { public void TestMethod() { } } ```
Você pode abrir a Visualização de Markdown com CTRL-K V
e visualizar a visualização ao lado do arquivo que está editando:

Agora, podemos criar a configuração gulp que configurará o fluxo de trabalho de conversão. Crie um arquivo no diretório chamado gulpfile.js
e preencha-o com este conteúdo:
var gulp = require('gulp'); var markdown = require('gulp-markdown-it'); var fs = require('fs'); gulp.task('markdown', function() { return gulp.src(['**/*.md', '!node_modules/**']) .pipe(markdown({ options: { html: true } })) .pipe(gulp.dest(function(f) { return f.base; })); }); gulp.task('default', ['markdown'], function() { gulp.watch(['**/*.md', '!node_modules/**'], ['markdown']); });
Com esse arquivo salvo, devemos ser capazes de executar o gulp e ver os resultados, portanto, a partir do terminal integrado, execute:
gulp

Isso resulta em um arquivo chamado test.html
sendo criado em nosso diretório com este conteúdo:
<h2>Introduction</h2> <p>This is an introduction.</p> <p>This is another paragraph.</p> <pre><code class="language-cs">//this is a gated code block public class TestClass { public void TestMethod() { } } </code></pre>
Da maneira como o configuramos, o gulp continuará a observar quaisquer alterações nos arquivos Markdown neste diretório (ou subdiretórios) e, se algum deles for alterado, ele os alimentará por meio do markdown-it para produzir novo conteúdo HTML em um arquivo html com o mesmo nome do arquivo Markdown. Se fizermos uma alteração no arquivo Markdown:
## Introduction This is an introduction. This is another paragraph. ```cs //this is a gated code block public class TestClass2 { public void TestMethod2() { } } ```
Aqui TestClass
foi alterado para ler TestClass2
e TestMethod
foi alterado para ler TestMethod2
. Depois de clicar em salvar e esperar um momento, test.html
agora contém:
<h2>Introduction</h2> <p>This is an introduction.</p> <p>This is another paragraph.</p> <pre><code class="language-cs">//this is a gated code block public class TestClass2 { public void TestMethod2() { } } </code></pre>
Isso é legal, mas como estamos usando o VS Code, podemos até evitar a necessidade de executar o comando gulp para começar. Tudo o.vscode
que precisamos fazer é criar um tasks.json
arquivo na subpasta no diretório do projeto e fornecer este conteúdo:
{ "version": "2.0.0", "tasks": [ { "type": "gulp", "task": "default", "problemMatcher": [], "group": { "kind": "build", "isDefault": true } } ] }
Isso faz com que, quando você pressionar CTRL + SHIFT + B
, ele comece a executar o comando gulp em segundo plano. Dessa forma, estamos combinando o editor de markdown no VS Code, incluindo seu painel de visualização realmente elegante, com Node.js, markdown-it e gulp para criar um editor de HTML incrível.
Domando o mecanismo de blog teimoso
Agora, se você não está lidando com alguns mecanismos de blog teimosos, como nós, o acima pode ser tudo o que você precisa. Mas mesmo assim, você pode achar o resto interessante e útil. Aqui estão os problemas com nosso mecanismo de blog que precisamos resolver:
- Preferimos renderizar trechos de código para html de estilo estático, dependendo de nenhuma folha de estilo JS ou CSS externa.
- Nosso mecanismo de blog mexe com guias embutidas em
pre
tags (por que, eu não tenho ideia). - Nosso mecanismo de blog falha em arredondar quebras de linha dentro
pre
de tags, muitas vezes descartando-as, então é mais seguro converter quebras de linha em tags de quebra explícitas ou as coisas ficarão confusas se republicarmos nosso HTML. - Nosso mecanismo de blog gosta de destruir espaços em branco no início das linhas, mesmo dentro
pre
de tags, então gostaríamos de convertê-los em espaços inquebráveis ( 
)
Então, primeiro, vamos lidar com o destaque dos snippets de código e limpar o HTML problemático dentro das pre
tags. Usaremos um pacote de nó chamado highlightjs para formatar os blogs de código fechado no Markdown em alguma marcação realçada de sintaxe. Então, vamos instalá-lo primeiro:
npm install highlightjs --save
Uma vez instalado, podemos modificar o gulpfile.js
para ficar assim:
var gulp = require('gulp'); var markdown = require('gulp-markdown-it'); //new var hljs = require('highlightjs/highlight.pack.js'); var fs = require('fs'); //new hljs.configure({ tabReplace: ' ' }); gulp.task('markdown', function() { return gulp.src(['**/*.md', '!node_modules/**']) .pipe(markdown({ options: { html: true, //new highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { var output = hljs.highlight(lang, str).value; output = output.replace(/(?:\r\n|\r|\n)/g, '<br />'); output = output.replace(/^\s+/gm, function(m){ return m.replace(/\s/g, ' ');}); output = output.replace(/(\<br\s+\/\>)(\s+)/gm, function(m){ return m.replace(/\>(\s+)/g, function (n) { return n.replace(/\s/g, ' '); } ); }); return '<pre class="hljs"><code>' + output + '</code></pre>'; } catch (e) {} } return ''; } } })) .pipe(gulp.dest(function(f) { return f.base; })); }); gulp.task('default', ['markdown'], function() { gulp.watch(['**/*.md', '!node_modules/**'], ['markdown']); });
No acima, nós:
- Adicione uma
require
instrução para que possamos usar a biblioteca highlightjs. - Configure a biblioteca highlightjs para usar 4 espaços não separáveis em vez do caractere de tabulação.
- Use o gancho de destaque ao executar markdown-it para especificar como o realce de sintaxe deve ser executado para blocos de código protegidos. Nesse caso, invocamos highlightjs para fazer o realce.
- Além de transformar os blocos de código cercados, realizamos uma série de substituições de expressões regulares no conteúdo de saída para remover padrões de caracteres que se mostram problemáticos para o mecanismo de blog e substituí-los por sequências equivalentes mais seguras de caracteres.
Você deve reiniciar a tarefa gulp neste ponto:
F1 => Terminate Running Task
Em seguida, reinicie a tarefa com CTRL + SHIFT + B
. Neste ponto, seu test.html deve ser lido assim:
<h2>Introduction</h2> <p>This is an introduction.</p> <p>This is another paragraph.</p> <pre><code class="language-cs"><pre class="hljs"><code><span class="hljs-comment">//this is a gated code block</span><br /><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestClass2</span><br />{<br /> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">TestMethod2</span>(<span class="hljs-params"></span>)<br /> </span>{<br /> <br /> }<br />}<br /></code></pre></code></pre>
É muito feio com todas as quebras de linha removidas da tag pre, mas agora deve funcionar mais bem com o mecanismo de blog.
Temos duas questões finais que gostaríamos de abordar antes de podermos encerrar isso. Primeiro, gostaríamos que todos os links fossem abertos automaticamente em uma nova guia, de acordo com as solicitações de nosso site/equipes de marketing. Em segundo lugar, o HTML resultante atual espera que uma folha de estilo CSS seja carregada para que o HTML de saída seja realmente colorido para os snippets de código. Se fôssemos olhar para a saída em um navegador, ficaria assim:

Como mencionei antes, não podemos injetar nenhum CSS por postagem em nossos artigos de blog, então preferimos que toda a coloração da sintaxe seja incorporada diretamente no HTML embutido.
Felizmente, para ambos os problemas, existem alguns pacotes de nós simples de usar para consertar as coisas. Primeiro, podemos instalar um pacote chamado markdown-it-link-target
que é um plugin para markdown-it que fará com que os links sejam renderizados de forma que sejam abertos em uma nova guia:
npm install markdown-it-link-target --save
Em seguida, instalaremos um plugin para gulp que invocará um pacote de nó chamado juice:
npm install gulp-juice --save
juice é uma biblioteca elegante que pegará uma folha de estilo CSS e um pouco de HTML e, em seguida, assará os estilos CSS no HTML embutido para que o CSS externo não seja mais necessário. Para conectar essas peças, precisamos atualizar novamente nosso gulpfile e reiniciar a tarefa:
var gulp = require('gulp'); var markdown = require('gulp-markdown-it'); var hljs = require('highlightjs/highlight.pack.js'); //new var juice = require('gulp-juice'); var fs = require('fs'); //new var codeCss = fs.readFileSync("./node_modules/highlightjs/styles/atom-one-dark.css", "utf-8"); hljs.configure({ tabReplace: ' ' }); gulp.task('markdown', function() { return gulp.src(['**/*.md', '!node_modules/**']) .pipe(markdown({ //new plugins: ["markdown-it-link-target"], options: { html: true, highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { var output = hljs.highlight(lang, str).value; output = output.replace(/(?:\r\n|\r|\n)/g, '<br />'); output = output.replace(/^\s+/gm, function(m){ return m.replace(/\s/g, ' ');}); output = output.replace(/(\<br\s+\/\>)(\s+)/gm, function(m){ return m.replace(/\>(\s+)/g, function (n) { return n.replace(/\s/g, ' '); } ); }); return '<pre class="hljs"><code>' + output + '</code></pre>'; } catch (e) {} } return ''; } } })) //new .pipe(juice({ extraCss: codeCss })) .pipe(gulp.dest(function(f) { return f.base; })); }); gulp.task('default', ['markdown'], function() { gulp.watch(['**/*.md', '!node_modules/**'], ['markdown']); });
Com essas mudanças,agora test.html
deve ficar assim:
<h2>Introduction</h2> <p>This is an introduction.</p> <p>This is another paragraph.</p> <pre><code class="language-cs"><pre class="hljs" style="background: #282c34; color: #abb2bf; display: block; overflow-x: auto; padding: 0.5em;"><code><span class="hljs-comment" style="color: #5c6370; font-style: italic;">//this is a gated code block</span><br><span class="hljs-keyword" style="color: #c678dd;">public</span> <span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #61aeee;">TestClass2</span><br>{<br> <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">public</span> <span class="hljs-keyword" style="color: #c678dd;">void</span> <span class="hljs-title" style="color: #61aeee;">TestMethod2</span>(<span class="hljs-params"></span>)<br> </span>{<br> <br> }<br>}<br></code></pre></code></pre>
No anterior, esta linha:
var codeCss = fs.readFileSync("./node_modules/highlightjs/styles/atom-one-dark.css", "utf-8");
carrega um dos arquivos css que acompanham o highlightjs e o incorpora com o HTML.
Agora, isso pode simplesmente ser colado como HTML bruto em praticamente qualquer coisa que suporte HTML, incluindo mecanismos de blog teimosos!
Se fôssemos carregá-lo no navegador neste momento, ficaria assim:

Espero que você tenha achado isso interessante e / ou útil!