Javascript heredoc

109

Necesito algo como heredoc en JavaScript. ¿Tienes alguna idea para esto? Necesito la funcionalidad de varios navegadores.

Encontré esto:

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

Creo que funcionará para mí. :)

VeroLom
fuente
6
Duplicado de stackoverflow.com/questions/805107/… que tiene algunas respuestas más detalladas.
Chadwick
4
Tener que agregar "\" me hace fruncir el ceño cada vez. En mi humilde opinión, no tener una sintaxis normal para cadenas de varias líneas es completamente injustificado. Y podrían haberlo agregado en cualquier versión dada, pero no lo hicieron.
Lex
1
Posible duplicado de la creación de cadenas de
varias líneas
Hoy en día, esto se hace con literales de plantilla como se muestra en el posible duplicado (no hay una respuesta actualizada aquí).
brasofilo

Respuestas:

77

Pruebe ES6 String Template , puede hacer algo como

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


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

=> true

Puede utilizar esta gran función hoy con 6to5 o TypeScript

mko
fuente
2
Esto es aplicable si solo desea cadenas de varias líneas. Sin embargo, dado que realmente no puede cambiar el símbolo que encierra su cadena, no es realmente heredoc.
Peeyush Kushwaha
3
Solo como nota, la pregunta original se hizo hace 5 años antes de que ES6 estuviera disponible. Esta es la mejor práctica para avanzar, ya que el rendimiento también es mejor.
Henry Tseng
2
@HenryTseng, ¿estás sugiriendo que las respuestas a esta pregunta deberían adaptarse a las tecnologías antiguas que existían hace 5 años? Si la pregunta aún está abierta, entonces merece aprovechar las nuevas tecnologías tal como se fueron creando con el tiempo. De esa manera, los nuevos usuarios que tengan el mismo problema pueden encontrar información "no arqueológica" aquí.
asiby
1
No, estaba haciendo un comentario sobre por qué esa solución no era más prominente antes. Definitivamente parece ser la forma admitida de avanzar si no hay problemas de compatibilidad.
Henry Tseng
63

No, lamentablemente JavaScript no admite nada como heredoc.

Andrew Hare
fuente
12
Lo sé, pero espero encontrar heredoc hack :)
VeroLom
Algo como los comentarios de la función de análisis (pero no funciona en ie / firefox) =
VeroLom
37

Qué tal esto:

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());

Simplemente reemplace "/ * AQUÍ" y "AQUÍ * /" con la palabra de su elección.

Zv_oDD
fuente
4
¿Todos los navegadores / motores devuelven los comentarios en Function.toString ()? eso es muy inteligente
GCB
Funciona en la consola de Chrome
Omn
4
No funcionará si tiene un comentario final */en su documento aquí.
Narigo
2
Recomendaría usar la respuesta de Nate Ferrero, ya que el suyo es un ejemplo más refinado y optimizado. El mío usa 3 llamadas regEx separadas y es más una prueba de concepto.
Zv_oDD
1
Muy inteligente ... pero no puede garantizar que funcione en el futuro. Depende demasiado de la implementación para ser una buena práctica.
Pierre-Olivier Vares
33

Sobre la base de la respuesta de Zv_oDD, creé una función similar para una reutilización más fácil.

Advertencia: esta es una característica no estándar de muchos intérpretes de JS y probablemente se eliminará en algún momento, pero como estoy creando un script para usar solo en Chrome, ¡lo estoy usando! ¡ Nunca confíe en esto para los sitios web orientados al cliente!

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

Utilizar:

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

Devoluciones:

"A test of horrible
Multi-line strings!"

Notas:

  1. El texto se recorta en ambos extremos, por lo que cualquier espacio en blanco adicional en cualquier extremo está bien.

Ediciones:

2/2/2014 - cambiado para no meterse con el prototipo de Función y usar el nombre heredoc en su lugar.

26/05/2017: espacio en blanco actualizado para reflejar los estándares de codificación modernos.

Nate Ferrero
fuente
1
Usaría hereDoc () como mi nombre de función, pero este código funcionó bien cargando mi volcado de registro de 40k líneas en una variable en la consola de Chrome
Omn
¿Por qué crearía una instancia de una función y accedería a la propiedad obsoleta __ proto __? ¿Por qué no simplemente hacer Function.prototype.str = function () {...}?
John Kurlak
@JohnKurlak ¡eso es aún mejor! No creo que fuera consciente de que eso era posible cuando escribí la respuesta.
Nate Ferrero
2
@NateFerrero - ¡Excelente respuesta, gracias! Agregué una extensión propia como respuesta separada.
Andrew Cheong
En mi Android, nexus 4, con 5.0.1, esto ya no funciona en Chrome. Por alguna razón, está eliminando espacios en blanco y comentarios. No puedo averiguar si se trata de una configuración, pero definitivamente está del lado del cliente. ¿Alguna idea para una solución alternativa?
MLU
19

Dependiendo del tipo de motor JS / JS que esté ejecutando (SpiderMonkey, AS3), simplemente puede escribir XML en línea, en el que puede colocar texto en varias líneas, 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
fuente
13

ES6 Template Strings tiene la función heredoc.

Puede declarar cadenas encerradas con tilde invertido (``) y se pueden expandir a través de varias líneas.

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

También puede incluir expresiones dentro de las cadenas de plantillas. Estos se indican mediante el signo de dólar y llaves ( ${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"

De hecho, hay más características como Tagged Temple Strings y Raw Strings en él. Encuentre la documentación en

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

Charlie
fuente
8

Me siento mal por escribir una respuesta separada para simplemente una extensión de la respuesta de @NateFerrero , pero tampoco creo que editar su respuesta sea apropiado, así que vota a @NateFerrero si esta respuesta fue útil para ti.

tl; dr: para aquellos que deseen utilizar comentarios en bloque dentro de su heredoc ...

Principalmente necesitaba heredocs de Javascript para almacenar un bloque de CSS, por ejemplo

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;
    }
*/});

Sin embargo, como puede ver, me gusta comentar mi CSS y, desafortunadamente (como lo sugiere el resaltado de sintaxis), el primero */finaliza el comentario general, rompiendo el heredoc.


Para este propósito específico (CSS), mi solución fue agregar

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

a la cadena dentro de @NateFerrero's heredoc; en forma completa:

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

y utilícelo agregando un espacio entre *y /para los comentarios del bloque "interno", así:

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;
    }
*/});

El replacesimplemente encuentra /* ... * /y elimina el espacio para hacer /* ... */, preservando así la heredoc hasta que fue llamado.


Por supuesto, puede eliminar los comentarios usando

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

También puede admitir //comentarios si los agrega a la cadena:

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

Además, puede hacer algo diferente al espacio único entre *y /, como -:

    /**
     * Nuke rounded corners.
     *-/

si solo actualiza la expresión regular de manera adecuada:

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

¿O tal vez le gustaría una cantidad arbitraria de espacios en blanco en lugar de un solo espacio?

.replace(/(\/\*[\s\S]*?\*)\s+\//g, '$1/')
                          ^^^
Andrew Cheong
fuente
2
¡Frio! Esa fue una limitación conocida con mi método, me gusta :)
Nate Ferrero
8

Puede utilizar CoffeeScript , un lenguaje que se compila en JavaScript. El código se compila uno a uno en el JS equivalente y no hay interpretación en tiempo de ejecución.

Y por supuesto, tiene heredocs :)

Jakob
fuente
24
La respuesta correcta es no. CoffeeScript y EJS se pueden utilizar como sugerencias.
alvincrespo
4
CoffeeScript es la respuesta correcta a la mayoría de los problemas de JS que he encontrado. Si escribe más de una cantidad trivial de JS (y valora su tiempo y energía), se lo debe a sí mismo para usarlo.
Brandon
5
Creo que esta es una buena respuesta, ya que proporciona una manera fácil de eludir la ausencia de heredoc en javascript. Me ahorro mucho tiempo.
Stofke
3
Si solo quiere un fragmento de js pero no quiere tener que escribirlo: coffeescript.org y use el botón "Probar Coffeescript".
jcollum
1
Esta es más una respuesta que la mejor calificada, que es básicamente ... "no". Odio ese tipo de respuestas.
Matt Fletcher
7

ES5 y versiones anteriores

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

ES6 y versiones posteriores

`some random
multi line
text here`

resultado

some random
multi line
text here
Ga1der
fuente
mejor respuesta simple
Benjamin
1
Lo antiguo es un truco increíblemente brutal: /
Shayne
1

Puede usar Sweet.js Macros para agregarlo así, como lo creó Tim Disney en esta publicación

Tenga en cuenta que este enfoque usa comillas invertidas como delimitadores de cadena en su lugar:

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
fuente
1

Como han dicho otros, las cadenas de plantillas de ES6 le brindan la mayor parte de lo que proporcionan los heredocs tradicionales.

Si desea ir un paso más allá y usar una cadena de plantilla etiquetada, theredochay una buena función de utilidad que le permite hacer esto:

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
fuente
0

Si tiene html y jQuery a mano y la cadena es HTML válido, esto puede 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>

Si el texto tiene comentarios "<! - ->" en el medio, también funciona, pero una parte del texto puede ser visible. Aquí está el violín: https://jsfiddle.net/hr6ar152/1/

Max Oriola
fuente
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
}

ejemplo

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));

con caché: - crea una función de plantilla simple y guarda la función: (la 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
fuente
1
Resultados bastante diferentes en FF y Chrome.
Dave Newton
¿Qué es diferente? Acabo de probar en Chrome y FF y obtengo exactamente los mismos resultados. Excepto que en Chrome no hay nuevas líneas en la consola de Chrome si solo escribe el nombre de var. pero la variable es la misma. Es posible imprimir con nuevas líneas con console.log ()
Shimon Doodkin
0

Estoy publicando esta versión porque evita el uso de expresiones regulares para algo tan trivial.

En mi humilde opinión, la expresión regular es una ofuscación que se creó como una broma entre los desarrolladores de perl. el resto de la comunidad los tomó en serio y ahora pagamos el precio, décadas después. no use expresiones regulares, excepto por compatibilidad con versiones anteriores del código heredado. No hay excusa en estos días para escribir código que no sea inmediatamente legible y comprensible para humanos. regex viola este principio en todos los niveles.

También agregué una forma de agregar el resultado a la página actual, no es que se haya 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>

desincronizado
fuente