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 trechos como o Github Gist, pode achar que essa parte é fácil, mas nosso problema é que o software do nosso blog suprime e removeiframe especificamente elementos do conteúdo do post. Acredito que isso seja uma armadilha de segurança devido ao motor estar mal preparado para diferenciar o conteúdo do post e o dos comentários no que diz respeito à edição, e você não gostaria que seus redatores de comentários injetassem iframes!
Infelizmente, a maioria dos bons softwares externos de snippet gostaria de carregar o snippet em umiframe, ou precisaria de JavaScript personalizado para rodar (outro erro para o nosso motor de blog).
Por um tempo, rodamos um pouco de JavaScript no nível do site, que aplicava um certo destaque de sintaxe empre tags formatadas de forma muito precisa, mas esse JavaScript continuava sumindo toda vez que nossa equipe de site fazia revisões importantes no site, fazendo com que todos os artigos que dependiam dele ficassem de repente horríveis. Quando isso sumiu 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 garantir que temos Visual Studio Code e Node.js instalados, pois eles serão os principais pilares nesse fluxo de trabalho. Depois de instalados, precisamos criar um diretório que armazene os arquivos markdown, por exemploc:\Blogging, e abrir o VS Code apontando para essa pasta.
Primeiro, crie um arquivo vaziopackage.json no diretório que contém 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 umgulpfile.js que converta continuamente os arquivos Markdown do nosso diretório em HTML à medida que forem salvos, e para iniciar o processo com umaCtrl + Shift + B via mágica do VS Code.
Primeiro, vamos criar um arquivo Markdown de teste chamadotest.md na pasta e dar um pouco de 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 Prévia Markdown comCTRL-K V e visualizar a pré-visualização junto com o arquivo que está editando:

Agora, podemos criar a configuração gulp que vai configurar o fluxo de trabalho de conversão. Crie um arquivo no diretório chamadogulpfile.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 na criação de um arquivo chamadotest.html 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()
{
}
}
```
AquiTestClass foi alterado para lerTestClass2 eTestMethod foi alterado para lerTestMethod2. Depois de apertar 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 VS Code, podemos até evitar a necessidade de rodar o comando gulp para começar. Tudo o que precisamos fazer é criar umtasks.json arquivo na.vscode subpasta do 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, ao apertarCTRL + SHIFT + B, ele comece a executar o comando gulp em segundo plano. Dessa forma, estamos combinando o editor markdown do VS Code, incluindo seu painel de pré-visualização realmente estiloso, com Node.js, markdown-it e gulp para criar um editor 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 motor de blog mexe com abas embutidas em
pretags (por quê, não faço ideia). - Nosso motor de blog falha em fazer roundtrip de quebras de linha dentro das
pretags, muitas vezes as eliminando, então é mais seguro converter quebras de linha em tags explícitas ou as coisas podem ficar erradas se republicarmos nosso HTML. - Nosso motor de blog gosta de destruir espaços em branco no início das linhas até mesmo dentro
predas tags, então gostaríamos de convertê-los em espaços que não quebrem ( )
Então, primeiro, vamos lidar com a realce dos trechos de código e a limpeza do HTML problemático dentro daspre tags. Vamos usar um pacote de nós chamado highlightjs para formatar os blogs de código bloqueado no Markdown em uma marcação com sintaxe destacada. Então vamos instalar isso primeiro:
npm install highlightjs --save
Depois de instalado, podemos modificar paragulpfile.js que fique 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
requireinstruçã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
E então reiniciar a tarefa comCTRL + SHIFT + B. Neste ponto, seu test.html deve ser o seguinte:
<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 esses problemas, existem alguns pacotes de nós simples de usar para corrigir as coisas. Primeiro, podemos instalar um pacote chamadomarkdown-it-link-target que é um plugin para markdown-it que fará com que links sejam renderizados de forma que eles abram em uma nova aba:
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,test.html agora 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!