Javascript heredoc

109

Preciso de algo como heredoc em JavaScript. Você tem alguma ideia para isso? Preciso da funcionalidade para vários navegadores.

Eu achei isto:

heredoc = '\
<div>\
    <ul>\
        <li><a href="#zzz">zzz</a></li>\
    </ul>\
</div>';

Acho que vai funcionar para mim. :)

VeroLom
fonte
6
Duplicado de stackoverflow.com/questions/805107/… que tem algumas respostas mais detalhadas.
Chadwick
4
Ter que adicionar "\" me faz franzir a testa todas as vezes. Imho, não ter sintaxe normal para strings de várias linhas é completamente injustificado. E eles poderiam ter adicionado em qualquer versão, mas não o fizeram.
Lex
1
Possível duplicata de Criando strings de
várias linhas
Hoje em dia, isso é feito com literais de modelo, conforme mostrado na possível duplicata (não há resposta atualizada aqui).
brasofilo

Respostas:

77

Tente ES6 String Template , você pode fazer algo como

var hereDoc = `
This
is
a
Multiple
Line
String
`.trim()


hereDoc == 'This\nis\na\nMultiple\nLine\nString'

=> true

Você pode usar este ótimo recurso hoje com 6to5 ou TypeScript

mko
fonte
2
Isso é aplicável se você quiser apenas strings de várias linhas. No entanto, como você não pode realmente alterar o símbolo que envolve sua string, ele não é realmente heredoc.
Peeyush Kushwaha
3
Apenas como observação, a pergunta original foi feita 5 anos atrás, antes que ES6 estivesse disponível. Esta é a prática recomendada para avançar, pois o desempenho também é melhor.
Henry Tseng
2
@HenryTseng, então você está sugerindo que as respostas a esta pergunta devem ser adaptadas às tecnologias antigas que existiam 5 anos atrás? Se a questão ainda estiver em aberto, então ela merece tirar proveito das novas tecnologias conforme são criadas ao longo do tempo. Dessa forma, novos usuários com o mesmo problema podem encontrar informações "não arqueológicas" aqui.
asiby
1
Não, eu estava fazendo um comentário sobre por que essa solução não era mais proeminente anteriormente. Parece definitivamente que é o caminho com suporte para seguir em frente, se não houver problemas de compatibilidade.
Henry Tseng,
63

Não, infelizmente o JavaScript não oferece suporte a nada como heredoc.

Andrew Hare
fonte
12
Eu sei, mas espero encontrar hack do heredoc :)
VeroLom
Algo como os comentários da função de análise (mas não funciona em ie / firefox) =
VeroLom
37

Que tal agora:

function MyHereDoc(){
/*HERE
<div>
   <p>
      This is written in the HEREDOC, notice the multilines :D.
   </p>
   <p>
      HERE
   </p>
   <p>
      And Here
   </p>
</div>
HERE*/
    var here = "HERE";
    var reobj = new RegExp("/\\*"+here+"\\n[\\s\\S]*?\\n"+here+"\\*/", "m");
    str = reobj.exec(MyHereDoc).toString();
    str = str.replace(new RegExp("/\\*"+here+"\\n",'m'),'').toString();
    return str.replace(new RegExp("\\n"+here+"\\*/",'m'),'').toString();
}

//Usage 
document.write(MyHereDoc());

Basta substituir "/ * AQUI" e "AQUI * /" pela palavra de sua escolha.

Zv_oDD
fonte
4
todos os navegadores / mecanismos retornam os comentários em Function.toString ()? isso é muito inteligente
gcb
Funciona no console do Chrome
Omn
4
Não funcionará se você tiver um comentário final */em seu heredoc.
Narigo
2
Eu recomendaria usar a resposta de Nate Ferrero, pois seu exemplo é mais refinado e otimizado. O meu usa 3 chamadas regEx separadas e é mais uma prova de conceito.
Zv_oDD
1
Muito inteligente ... mas você não pode garantir que funcionará no futuro. É muito dependente da implementação para ser uma boa prática.
Pierre-Olivier Vares
33

Com base na resposta de Zv_oDD, criei uma função semelhante para facilitar a reutilização.

Aviso: Este é um recurso não padrão de muitos interpretadores JS e provavelmente será removido em algum momento, mas como estou construindo um script para ser usado apenas no Chrome, estou usando-o! Não sempre contar com isso para websites dirigidos ao cliente!

// Multiline Function String - Nate Ferrero - Public Domain
function heredoc(fn) {
  return fn.toString().match(/\/\*\s*([\s\S]*?)\s*\*\//m)[1];
};

Usar:

var txt = heredoc(function () {/*
A test of horrible
Multi-line strings!
*/});

Retorna:

"A test of horrible
Multi-line strings!"

Notas:

  1. O texto é cortado em ambas as extremidades, portanto, qualquer espaço em branco extra em qualquer uma das extremidades está OK.

Editar% s:

02/02/2014 - alterado para não mexer no protótipo de Função de forma alguma e usar o nome heredoc.

26/05/2017 - espaço em branco atualizado para refletir os padrões de codificação modernos.

Nate Ferrero
fonte
1
Eu usaria hereDoc () como meu nome de função, mas este código funcionou bem carregando meu despejo de log de linha de 40k em uma variável no console do Chrome
Omn
Por que você criaria uma instância de uma função e acessaria a propriedade obsoleta __ proto __? Por que não apenas Function.prototype.str = function () {...}?
John Kurlak
@JohnKurlak isso é ainda melhor! Acho que não sabia que isso era possível quando escrevi a resposta.
Nate Ferrero
2
@NateFerrero - Resposta incrível, obrigado! Adicionada uma extensão minha como uma resposta separada.
Andrew Cheong
No meu Android, nexus 4, rodando 5.0.1, isso não funciona mais no Chrome. Por algum motivo, ele está excluindo espaços em branco e comentários. Não consigo descobrir se isso é uma configuração, mas é definitivamente do lado do cliente. Alguma ideia para uma solução alternativa?
MLU
19

Dependendo de qual tipo de mecanismo JS / JS você está executando (SpiderMonkey, AS3), você pode simplesmente escrever XML embutido, no qual você pode colocar texto em várias linhas, como heredoc:

var xml = <xml>
    Here 
    is 
    some 
    multiline 
    text!
</xml>

console.log(xml.toXMLString())
console.log(xml.toString()) // just gets the content
Dave Stewart
fonte
13

ES6 Template Strings tem recurso heredoc.

Você pode declarar strings delimitadas por back-tick (``) e podem ser expandidas por várias linhas.

var str = `This is my template string...
and is working across lines`;

Você também pode incluir expressões dentro de Template Strings. Eles são indicados pelo sinal de dólar e chaves ( ${expression}).

var js = "Java Script";
var des = `Template strings can now be used in ${js} with lot of additional features`;

console.log(des); //"Template strings can now be used in Java Script with lot of additional features"

Na verdade, há mais recursos, como Tagged Temple Strings e Raw Strings. Encontre a documentação em

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings

Charlie
fonte
8

Sinto-me mal por escrever uma resposta separada meramente como uma extensão da resposta de @NateFerrero , mas também não acho que editar a resposta dele seja apropriado, portanto, vote positivamente em @NateFerrero se esta resposta foi útil para você.

tl; dr — Para aqueles que desejam usar comentários em bloco dentro de seu heredoc ...

Eu precisava principalmente de heredocs Javascript para armazenar um bloco de CSS, por exemplo

var css = heredoc(function() {/*
    /**
     * Nuke rounded corners.
     */
    body div {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
        border-bottom-left-radius: 0 !important;
    }
*/});

Como você pode ver, no entanto, gosto de comentar meu CSS e, infelizmente (conforme sugerido pelo realce de sintaxe), o primeiro */finaliza o comentário geral, quebrando o heredoc.


Para este propósito específico (CSS), minha solução alternativa foi adicionar

.replace(/(\/\*[\s\S]*?\*) \//g, '$1/')

à corrente dentro de @NateFerrero's heredoc; na forma completa:

function heredoc (f) {
    return f.toString().match(/\/\*\s*([\s\S]*?)\s*\*\//m)[1].replace(/(\/\*[\s\S]*?\*) \//g, '$1/');
};

e use-o adicionando um espaço entre *e /para comentários de bloco "internos", como:

var css = heredoc(function() {/*
    /**
     * Nuke rounded corners.
     * /
    body div {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
        border-bottom-left-radius: 0 !important;
    }
*/});

O replacesimplesmente localiza /* ... * /e remove o espaço para fazer /* ... */, preservando assim o heredoc até ser chamado.


Você pode, é claro, remover todos os comentários usando

.replace(/\/\*[\s\S]*?\* \//g, '')

Você também pode oferecer suporte a //comentários se adicioná-los à rede:

.replace(/^\s*\/\/.*$/mg, '')

Além disso, você pode fazer algo diferente do único espaço entre *e /, como -:

    /**
     * Nuke rounded corners.
     *-/

se você apenas atualizar o regex apropriadamente:

.replace(/(\/\*[\s\S]*?\*)-\//g, '$1/')
                          ^

Ou talvez você queira uma quantidade arbitrária de espaços em branco em vez de um único espaço?

.replace(/(\/\*[\s\S]*?\*)\s+\//g, '$1/')
                          ^^^
Andrew Cheong
fonte
2
Legal! Essa era uma limitação conhecida do meu método, eu gosto :)
Nate Ferrero
8

Você pode usar CoffeeScript , uma linguagem que compila em JavaScript. O código compila um a um no JS equivalente e não há interpretação no tempo de execução.

E, claro, tem heredocos :)

Jakob
fonte
24
A resposta correta é não. CoffeeScript e EJS podem ser usados ​​como sugestões.
alvincrespo
4
CoffeeScript é a resposta correta para a maioria dos problemas de JS que encontrei. Se você escreve mais do que uma quantidade trivial de JS (e você valoriza seu tempo e energia), você deve a si mesmo usá-lo.
Brandon
5
Acho que esta é uma boa resposta, pois fornece uma maneira fácil de contornar a ausência de heredoc no javascript. Me economizou muito tempo.
Stofke de
3
Se você quer apenas um pedaço de js, mas não quer realmente escrevê-lo: coffeescript.org e use o botão "Experimentar o Coffeescript".
jcollum
1
Esta é mais uma resposta do que a mais bem avaliada, que é basicamente ... "não". Eu odeio esse tipo de resposta.
Matt Fletcher
7

ES5 e versões anteriores

(function(){/**
some random
multi line
text here
**/}).toString().slice(15,-5);

ES6 e versões posteriores

`some random
multi line
text here`

resultado

some random
multi line
text here
Ga1der
fonte
melhor simplesmente responder
Benjamin
1
O estilo antigo é um hack incrivelmente brutal: /
Shayne
1

Você pode usar as macros Sweet.js para adicioná-lo assim, conforme criado por Tim Disney neste post

Observe que essa abordagem usa crases como delimitadores de string em vez disso:

let str = macro {
    case {_ $template } => {
        var temp = #{$template}[0];
        var tempString = temp.token.value.raw;
        letstx $newTemp = [makeValue(tempString, #{here})];
        return #{$newTemp}
    }
}

str `foo bar baz`
Brad Parks
fonte
1

Como já foi dito, as strings de template ES6 oferecem muito do que os heredocs tradicionais fornecem.

Se você quiser dar um passo adiante e usar uma string de modelo com tag, theredocé uma boa função de utilitário que permite fazer isso:

if (yourCodeIsIndented) {
  console.log(theredoc`
    Theredoc will strip the
    same amount of indentation
    from each line.

      You can still indent
      further if you want.

    It will also chop off the
    whitespace-only first and
    last lines.
  `)
}
Neall
fonte
0

Se você tiver algum html e jQuery em mãos e a string for HTML válido, isso pode ser útil:

<div id="heredoc"><!--heredoc content
with multiple lines, even 'quotes' or "double quotes",
beware not to leave any tag open--></div>
<script>
var str = (function() {
   var div = jQuery('#heredoc');
   var str = div.html();
   str = str.replace(/^<\!--/, "").toString();
   str = str.replace(/-->$/, "").toString();
   return str;
})();
</script>

Se o texto tiver comentários "<! - ->" entre eles, também funcionará, mas uma parte do texto pode estar visível. Aqui está o violino: https://jsfiddle.net/hr6ar152/1/

Max Oriola
fonte
0
// js heredoc - http://stackoverflow.com/a/32915549/466363
// a function with comment with eval-able string, use it just like regular string

function extractFuncCommentString(func,comments) {
  var matches = func.toString().match(/function\s*\(\)\s*\{\s*\/\*\!?\s*([\s\S]+?)\s*\*\/\s*\}/);
  if (!matches) return undefined;
  var str=matches[1];

   // i have made few flavors of comment removal add yours if you need something special, copy replacement lines from examples below, mix them
  if(comments===1 )
  {
   // keep comments, in order to keep comments  you need to convert /**/ to / * * / to be able to put them inside /**/ like /*    / * * /    */
   return (
    str
   .replace(/\/\s\*([\s\S]*?)\*\s\//g,"/*$1*/") //       change   / * text * /  to   /* text */ 
   )
  }
  else if(comments===2)
  {
   // keep comments and replace singleline comment to multiline comment
   return (
    str
   .replace(/\/\s\*([\s\S]*?)\*\s\//g,"/*$1*/") //       change   / * text * /  to   /* text */ 
   .replace(/\/\/(.*)/g,"/*$1*/")          //           change   //abc to  /*abc*/
   )
  }
  else if(comments===3)
  {
   // remove comments
   return (
      str
      .replace(/\/\s\*([\s\S]*?)\*\s\//g,"") //       match / * abc * /
      .replace(/\/\/(.*)/g,"")             // match //abc
     )
  }
  else if(comments===4)
  {
   // remove comments and trim and replace new lines with escape codes
   return (
      str
      .replace(/\/\s\*([\s\S]*?)\*\s\//g,"") //       match / * abc * /
      .replace(/\/\/(.*)/g,"")             // match //abc
      .trim() // after removing comments trim and:
      .replace(/\n/g,'\\n').replace(/\r/g,'\\r') // replace new lines with escape codes. allows further eval() of the string, you put in the comment function: a quoted text but with new lines
     )
  }
  else if(comments===5)
  {
   // keep comments comments and replace strings, might not suit when there are spaces or comments before and after quotes 
   // no comments allowed before quotes of the string
   return (
      str
      .replace(/\/\s\*([\s\S]*?)\*\s\//g,"/*$1*/") //       change   / * text * /  to   /* text */
      .replace(/\/\/(.*)/g,"/*$1*/")          //           change   //abc to  /*abc*/
      .trim() // trim space around quotes to not escape it and:
      .replace(/\n/g,'\\n').replace(/\r/g,'\\r') // replace new lines with escape codes. allows further eval() of the string, you put in the comment function: a quoted text but with new lines
     )
  }
  else 
  return str
}

exemplo

var week=true,b=123;
var q = eval(extractFuncCommentString(function(){/*!

// this is a comment     


'select 

/ * this
is a multiline 
comment * /

 a
,b  // this is a comment  
,c
from `table`
where b='+b+' and monthweek="'+(week?'w':'m')+'" 
//+' where  a=124
order by a asc
'
*/},4));

com cache: - crie uma função de modelo simples e salve a função: (a segunda vez funciona rápido)

var myfunction_sql1;
function myfunction(week,a){


    if(!myfunction_sql1) eval('myfunction_sql1=function(week,a){return ('+extractFuncCommentString(function(){/*!
'select 

/ * this
is a multiline 
comment * /

 a
,b  // this is a comment  
,c
from `table`
where b='+b+' and monthweek="'+(week?'w':'m')+'" 
//+' where  a=124
order by a asc

'*/},4)+')}');
    q=myfunction_sql1(week,a);
    console.log(q)
}
myfunction(true,1234)
Shimon Doodkin
fonte
1
Resultados bastante diferentes em FF e Chrome.
Dave Newton
o que é diferente? Acabei de testar no Chrome e FF e obtenho exatamente os mesmos resultados. Exceto que no Chrome não há novas linhas no console do Chrome se você apenas digitar o nome da var. mas a variável é a mesma. É possível imprimir com novas linhas com console.log ()
Shimon Doodkin
0

Estou postando esta versão porque evita o uso de regex para algo tão trivial.

IMHO regex é uma ofuscação que foi criada como uma piada entre os desenvolvedores de perl. o resto da comunidade os levou a sério e agora pagamos o preço, décadas depois. não use regex, exceto para compatibilidade com versões anteriores com código legado. não há desculpa hoje em dia para escrever código que não seja imediatamente legível e compreensível por humanos. regex viola este princípio em todos os níveis.

Também adicionei uma maneira de adicionar o resultado à página atual, não que isso tenha sido solicitado.

function pretty_css () {
/*
    pre { color: blue; }

*/
}
function css_src (css_fn) {
   var css = css_fn.toString();
   css = css.substr(css.indexOf("/*")+2);
   return css.substr(0,css.lastIndexOf("*/")).trim();
}

function addCss(rule) {
  let css = document.createElement('style');
  css.type = 'text/css';
  if (css.styleSheet) css.styleSheet.cssText = rule; // Support for IE
  else css.appendChild(document.createTextNode(rule)); // Support for the rest
  document.getElementsByTagName("head")[0].appendChild(css);
}

addCss(css_src(pretty_css));

document.querySelector("pre").innerHTML=css_src(pretty_css);
<pre></pre>

não sincronizado
fonte