
Multiline strings in JavaScript - mofle
https://github.com/sindresorhus/multiline?unicorns
======
mappum
That's a really smart hack, I didn't realize comments were included in the
output of Function#toString. You could do other cool things, e.g. an
annotation for type checking:

    
    
      function checkType(f) {
        return function(a) {
          var type = f.toString().match(/\/\/(.*)\n/)[1].trim();
          if(type !== typeof(a)) throw new Error('Invalid type');
          return f(a);
        }
      }
      
      var halve = checkType(function(n) {
        // number
        return n / 2;
      });
      
      halve(4); // returns 2
      halve('string'); // emits error

~~~
tlrobinson
_I didn 't realize comments were included in the output of Function#toString_

Probably because sometimes they're not, depending on the JavaScript engine
(though perhaps all of the major modern browsers do)

~~~
mofle
The following browsers return comments:

\- Latest Chrome

\- Firefox >=17

\- Safari >=4

\- Opera >=9

\- Internet Explorer >=6

From:
[https://github.com/sindresorhus/multiline#compatibility](https://github.com/sindresorhus/multiline#compatibility)

~~~
coldtea
Not just "Latest Chrome". Chrome has been doing it for at least 2 years. I
know, because I'm using it with it.

~~~
manojlds
I think Chrome is mentioned as "Latest Chrome" because:

1) There have been so many versions and not easy to know what came when.

2) Most people get updated to Chrome latest automatically without any fuss.

~~~
msujaws
In fact, (2) is not as true as it used to be. This graph,
[http://cdn.arstechnica.net/wp-
content/uploads/2014/07/chrome...](http://cdn.arstechnica.net/wp-
content/uploads/2014/07/chrome-2014-06.png), shows that about 6-7 percent of
worldwide browsers are out-of-date Chrome installs, and the trend is for that
to grow.

From [http://arstechnica.com/information-
technology/2014/07/window...](http://arstechnica.com/information-
technology/2014/07/windows-8-x-internet-explorer-both-flatline-in-june/)

------
adrusi
When I was first really to program I was into hacking javascript to do the
crazy things I wanted it to do. I basically did exactly this, albeit with a
worse interpolation feature[1]. Don't actually do this please. In the browser,
just open up a different <script type="text/plain"> and grab the string from
that tag. On the server you can just use a file. Any string that's actually
long enough for `["foo", "bar"].join("\n")` to be too cumbersome should be in
a separate file, especially if the other way involves a massive hack.

[1]:
[http://zurb.com/forrst/posts/Javascript_native_multi_line_st...](http://zurb.com/forrst/posts/Javascript_native_multi_line_string-
gBE)

~~~
jonpacker
So you would suggest using external text files to store every SQL/Cypher query
that is used in a model-based node app?

~~~
adrusi
Well, if the query is longer than a 3-4 lines then it's really a program in
its own right, and yes, I think it deserves its own file.

------
wldlyinaccurate
Probably worth pointing out that this won't play nice with most minification
utilities unless you don't mind leaving all comments in the output.

I think for this reason I still prefer using Array.prototype.join for
multiline strings:

    
    
        var str = [
            '<!doctype html>',
            '<html>',
                '<body>',
                    '<h1>❤ unicorns</h1>',
                '</body>',
            '</html>',
        ].join('');

~~~
gravity13
You can also

    
    
      var str = ' \
          <!doctype html> \
          <html> \
            <body> \
              <h1>❤ unicorns</h1> \
            </body> \
          </html> \
      ' 
    

I find it's better than string concat because the trailing \ is just one
annoying thing rather than three.

~~~
jaredmcateer
6 months later:

    
    
        var str = ' \
            <!doctype html> \
            <html> \
              <body> \ 
                <h1>❤ unicorns</h1> \
              </body> \
            </html> \
        ' 
    

Help I'm maintaining your code and I get a syntax error

~~~
jasonkostempski
HA! I (don't) see what you did there :)

~~~
msujaws
There is a space following one of the slashes.

------
abrkn
Yep, I could use this:
[http://i.gyazo.com/7826deb9e267f52e1f6b4f1afcfb8c4e.png](http://i.gyazo.com/7826deb9e267f52e1f6b4f1afcfb8c4e.png)

------
martin-adams
Interesting solution. I usually find this type of problem when doing
templating in the client when updating things via Ajax. I either use the DOM
to store the template or end up concatenating for smaller things.

Since to preserve these comments in minification requires conscious effort, I
wonder if there's an alternative that could convert the comments to regular JS
before minification. Obviously you can't just run it in Node as normal doing
it this way.

------
tlrobinson
FYI, CoffeeScript has multiline strings (and a bunch of other nice things!)

~~~
mushishi
Sorry but what does this have to do with anything?

~~~
tlrobinson
If you want niceties like multiline strings, CoffeeScript is a better option
than clever hacks like this.

    
    
        var str = multiline(function(){/*!@preserve
        multi
        line
        string
        */console.log});
    

Really?

~~~
mushishi
Well, I don't get what is the point of suggesting alternative languages that
have multiline strings when we are talking about one specific language.

The topic is about how to do multiline string in Javascript in one way, and
one alternative is not to use multiline strings in Javascript. It's kinda big
jump from the original purpose of the topic to start suggesting alternative
languages... if we are to be this slack, then we can start spamming most
threads with weakly related stuff. For example, you can easily advertise
coffeescript in each and every javascript thread in a similar way you did in
this specific thread but do we really need want that?

~~~
nottombrown
Coffeescript is just javascript with syntactic sugar on top. This library
provides sugar for multiline strings.

~~~
idlewan
To me, this library tastes more like too much salt.

------
PavlovsCat
I thank you from the bottom of my heart. This drove me _crazy_ when playing
around with WebGL and shaders, because for various reasons I didn't want to
put those in separate files.

~~~
waynecochran
Why aren't you just doing this, for example?

    
    
            <script id="fragment" type="x-shader">
    	precision highp float;
    	uniform vec3 glyphColor;
    	void main() {
    	        gl_FragColor = vec4(glyphColor, 1.0);
    	}
    	</script>

~~~
PavlovsCat
Because you can't do that from within javascript, and my HTML was really just
a stub to load all the javascript I didn't want to have to otherwise deal
with.

------
ttty
The first problem I see, is using any build process which will remove all the
comments. Therefore your string will become ''. I wonder how to avoid this.

~~~
mofle
It's mainly intended for use in Node.js, but I've documented how to use it
with a minifier for the front-end:
[https://github.com/sindresorhus/multiline#minification](https://github.com/sindresorhus/multiline#minification)

------
evilpie
>Note that ES6 will have template string which can be multiline, but time...

Firefox Nightly currently supports just enough of template strings to give you
multiline strings.

~~~
Excavator
Since a couple weeks back templating is also implemented¹, just lacking tagged
templates and documentation²³.

1:
[https://bugzilla.mozilla.org/show_bug.cgi?id=688857#c22](https://bugzilla.mozilla.org/show_bug.cgi?id=688857#c22)

2: [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/template_strings)

3: [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/ECMA...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/ECMAScript_6_support_in_Mozilla)

------
mattdesl
Cool hack, but a cleaner solution would be to just use fs.readFileSync if you
really want long strings, and let browserify/brfs to inline it for you in the
browser.

Using this in a library would be pretty unsafe, given how it's browser-
dependent, is prone to issues with minifcation, and could just break with
future versions of your JS engine.

------
cyphunk
seriously? you people be nuts

    
    
        http://stackoverflow.com/a/15558082/2371924
        var myString = function(){/*
          multi-line
          string
        */}.toString().slice(14,-3)
    

take this code snippet, throw a package.json and some yaml in and make another
npm to Show HN.

~~~
coldtea
Yeah, only it does several other things do. Like optionally removing indend
whitespace.

Did you even read TFA?

------
eob
At one point the E4X standard (back in 2007?) enabled a cool multi-line string
hack by promoting XML elements to language entities. You could say:

    
    
        var htmlFragment = "" + (<r><![CDATA[
        l(a
        
        le
        af
        fa
        
        ll
        
        s)
        one
        l
        
        iness
        
        - e.e. cummings
        ;
        ]]></r>);
    

And then `htmlFragment` would contain your multi-line string. (The addition
with the empty string coerces the <r> element to output its `toString()`
value, which happens to be the innerHTML).

This is largely a historical factoid, though. I don't think browsers beyond
Mozilla ended up supporting E4X.

Really clever hack with the function's toString method!

------
jasonkostempski
Cool hack but not supporting multiline strings is a feature as far as I'm
concerned. Everyone seems to agree that HTML shouldn't contain JavaScript, why
should JavaScript contain HTML?

~~~
biscarch
That is actually currently being contested by React and .jsx.

That said, I've seen multiline abused for writing SQL queries as well, which
would have been better off being written in a separate .SQL file and `fs.read`
into a string with a descriptive var name. (Ignoring that massive SQL strings
is a code smell imo).

~~~
siculars
Except if your application generates dynamic queries. And not of the sort
where variables in the WHERE clause are dynamically evaluated. Of the sort
where the entire structure of your SQL is dynamically driven by user
interaction with your application.

~~~
biscarch
Are you saying that large SQL strings isn't a code smell if the application is
dynamically putting them together? That seems like DSL/Query Builder territory
to me. Perhaps you could share an example?

------
andrelaszlo
This snippet does the same thing, mostly:

    
    
        var multiline = function(){/*
        foo
        bar
        baz
        */}.toSource().match(/\/\*([\s\S]*)\*\//)[1].trim()

~~~
mofle
Yeah, pretty much, but in Node we wrap repetitive things in modules, as
they're super cheap. Your snippet is also missing some things like indentation
stripping, error handling and browser support.

------
jgillich
I just use ES6 template strings together with the es6ify transform for
Browserify. Works in all browsers and very likely performs much better than
using Function.prototype.toString.

------
z3t4
If you do multi line strings you could probably do it in a smarter way.
Examples:

appendChild instead of innerHTML .html file instead of html inside a .js file.
.css file instead of css inside a .js file.

It can get very messy if you store "data" inside JS. Especially if you store
markup or styling in the JS.

------
tach4n
Wouldn't something like a Sweet.js macro be better for this? Just have it
output a string with '\n\' at the end of lines, or using Array.join() or
whatever you like.

------
chrishawes
This is really handy for DFP creative templates. Thank you.

------
teddyh
Today I learned that in JavaScript, comments are significant and part of the
code.

(And people complain about Python having significant whitespace?)

~~~
Kiro
Only significant if you want it to be.

------
tfb
Weird... I just _just_ randomly looking at this a few days ago upon a search
for "js multiline" when wondering about the common way to spell multiline
(multi line vs multi-line vs multiline). I was intrigued by his code though,
ultimately in awe of the cleverness and conciseness of it. Very cool hack!
Crazy coincidence to see the very code on HN only a few days later.

------
ricardobeat
Be sure to scroll to the bottom for a real-world example, this is very
brittle.

------
VeejayRampay
In other news, the ubiquitous language of the ubiquitous platform of the
information age doesn't support multiline strings. But I guess if we keep
throwing enough modules and libraries at it, it'll end up being nice to
develop for.

Alternatively wait for ES6 or ES7 to (maybe) solve the problem.

------
robgibbons
This is a very clever hack. Great use of anonymous functions and toString.

------
dsego
Why not a pre-processor?

~~~
mofle
In Node.js you don't want a compile step. In the browser however that would
probably be a better solution indeed.

~~~
alextgordon
Why don't you want a compile step?

~~~
mofle
Because a compile step is annoying when it's not strictly needed. In the
browser you usually have some kind of build system for concatenation and
minifying anyways.

~~~
knubie
With coffeescript, at least, there is no extra compile step from the user's
point of view. You can run `coffee myapp.coffee` in the same way you would run
`node myapp.js`

------
igl
I think i saw 8 libraries before who abused comments like this.

Edit: Yawn.

