1 |
|
2 | # Jade - template engine
|
3 |
|
4 | Jade is a high performance template engine heavily influenced by [Haml](http://haml-lang.com)
|
5 | and implemented with JavaScript for [node](http://nodejs.org).
|
6 |
|
7 | ## Features
|
8 |
|
9 | - client-side support
|
10 | - great readability
|
11 | - flexible indentation
|
12 | - block-expansion
|
13 | - attribute interpolation
|
14 | - code is escaped by default for security
|
15 | - contextual error reporting at compile & run time
|
16 | - executable for compiling jade templates via the command line
|
17 | - html 5 mode (using the _!!! 5_ doctype)
|
18 | - optional memory caching
|
19 | - combine dynamic and static tag classes
|
20 | - parse tree manipulation via _filters_
|
21 | - supports [Express JS](http://expressjs.com) out of the box
|
22 | - transparent iteration over objects, arrays, and even non-enumerables via `- each`
|
23 | - block comments
|
24 | - no tag prefix
|
25 | - AST filters
|
26 | - filters
|
27 | - :sass must have [sass.js](http://github.com/visionmedia/sass.js) installed
|
28 | - :less must have [less.js](http://github.com/cloudhead/less.js) installed
|
29 | - :markdown must have [markdown-js](http://github.com/evilstreak/markdown-js) installed or [node-discount](http://github.com/visionmedia/node-discount)
|
30 | - :cdata
|
31 | - :coffeescript must have [coffee-script](http://jashkenas.github.com/coffee-script/) installed
|
32 | - [Vim Syntax](https://github.com/digitaltoad/vim-jade)
|
33 | - [TextMate Bundle](http://github.com/miksago/jade-tmbundle)
|
34 | - [Screencasts](http://tjholowaychuk.com/post/1004255394/jade-screencast-template-engine-for-nodejs)
|
35 | - [html2jade](https://github.com/donpark/html2jade) converter
|
36 |
|
37 | ## Implementations
|
38 |
|
39 | - [php](http://github.com/everzet/jade.php)
|
40 | - [scala](http://scalate.fusesource.org/versions/snapshot/documentation/scaml-reference.html)
|
41 | - [ruby](http://github.com/stonean/slim)
|
42 |
|
43 | ## Installation
|
44 |
|
45 | via npm:
|
46 |
|
47 | npm install jade
|
48 |
|
49 | ## Browser Support
|
50 |
|
51 | To compile jade to a single file compatible for client-side use simply execute:
|
52 |
|
53 | $ make jade.js
|
54 |
|
55 | Alternatively, if uglifyjs is installed via npm (`npm install uglify-js`) you may execute the following which will create both files.
|
56 |
|
57 | $ make jade.min.js
|
58 |
|
59 | ## Public API
|
60 |
|
61 | ```javascript
|
62 | var jade = require('jade');
|
63 |
|
64 | // Render a string
|
65 | jade.render('string of jade', { options: 'here' });
|
66 |
|
67 | // Render a file
|
68 | jade.renderFile('path/to/some.jade', { options: 'here' }, function(err, html){
|
69 | // options are optional,
|
70 | // the callback can be the second arg
|
71 | });
|
72 |
|
73 | // Compile a function
|
74 | var fn = jade.compile('string of jade', options);
|
75 | fn.call(scope, locals);
|
76 | ```
|
77 |
|
78 | ### Options
|
79 |
|
80 | - `scope` Evaluation scope (`this`)
|
81 | - `self` Use a `self` namespace to hold the locals. _false by default_
|
82 | - `locals` Local variable object
|
83 | - `filename` Used in exceptions, and required by `cache`
|
84 | - `cache` Cache intermediate JavaScript in memory keyed by `filename`
|
85 | - `debug` Outputs tokens and function body generated
|
86 | - `compiler` Compiler to replace jade's default
|
87 |
|
88 | ## Syntax
|
89 |
|
90 | ### Line Endings
|
91 |
|
92 | **CRLF** and **CR** are converted to **LF** before parsing.
|
93 |
|
94 | ### Tags
|
95 |
|
96 | A tag is simply a leading word:
|
97 |
|
98 | html
|
99 |
|
100 | for example is converted to `<html></html>`
|
101 |
|
102 | tags can also have ids:
|
103 |
|
104 | div#container
|
105 |
|
106 | which would render `<div id="container"></div>`
|
107 |
|
108 | how about some classes?
|
109 |
|
110 | div.user-details
|
111 |
|
112 | renders `<div class="user-details"></div>`
|
113 |
|
114 | multiple classes? _and_ an id? sure:
|
115 |
|
116 | div#foo.bar.baz
|
117 |
|
118 | renders `<div id="foo" class="bar baz"></div>`
|
119 |
|
120 | div div div sure is annoying, how about:
|
121 |
|
122 | #foo
|
123 | .bar
|
124 |
|
125 | which is syntactic sugar for what we have already been doing, and outputs:
|
126 |
|
127 | `<div id="foo"></div><div class="bar"></div>`
|
128 |
|
129 | ### Tag Text
|
130 |
|
131 | Simply place some content after the tag:
|
132 |
|
133 | p wahoo!
|
134 |
|
135 | renders `<p>wahoo!</p>`.
|
136 |
|
137 | well cool, but how about large bodies of text:
|
138 |
|
139 | p
|
140 | | foo bar baz
|
141 | | rawr rawr
|
142 | | super cool
|
143 | | go jade go
|
144 |
|
145 | renders `<p>foo bar baz rawr.....</p>`
|
146 |
|
147 | interpolation? yup! both types of text can utilize interpolation,
|
148 | if we passed `{ locals: { name: 'tj', email: 'tj@vision-media.ca' }}` to `render()`
|
149 | we can do the following:
|
150 |
|
151 | #user #{name} <#{email}>
|
152 |
|
153 | outputs `<div id="user">tj <tj@vision-media.ca></div>`
|
154 |
|
155 | Actually want `#{}` for some reason? escape it!
|
156 |
|
157 | p \#{something}
|
158 |
|
159 | now we have `<p>#{something}</p>`
|
160 |
|
161 | We can also utilize the unescaped variant `!{html}`, so the following
|
162 | will result in a literal script tag:
|
163 |
|
164 | - var html = "<script></script>"
|
165 | | !{html}
|
166 |
|
167 | Nested tags that also contain text can optionally use a text block:
|
168 |
|
169 | label
|
170 | | Username:
|
171 | input(name='user[name]')
|
172 |
|
173 | or immediate tag text:
|
174 |
|
175 | label Username:
|
176 | input(name='user[name]')
|
177 |
|
178 | Tags that accept _only_ text such as `script`, `style`, and `textarea` do not
|
179 | need the leading `|` character, for example:
|
180 |
|
181 | html
|
182 | head
|
183 | title Example
|
184 | script
|
185 | if (foo) {
|
186 | bar();
|
187 | } else {
|
188 | baz();
|
189 | }
|
190 |
|
191 | Once again as an alternative, we may use a leading '.' to indicate a text block, for example:
|
192 |
|
193 | p.
|
194 | foo asdf
|
195 | asdf
|
196 | asdfasdfaf
|
197 | asdf
|
198 | asd.
|
199 |
|
200 | outputs:
|
201 |
|
202 | <p>foo asdf
|
203 | asdf
|
204 | asdfasdfaf
|
205 | asdf
|
206 | asd
|
207 | .
|
208 | </p>
|
209 |
|
210 | This however differs from a leading '.' followed by a space, which although is ignored by the Jade parser, tells Jade that this period is a literal:
|
211 |
|
212 | p .
|
213 |
|
214 | outputs:
|
215 |
|
216 | <p>.</p>
|
217 |
|
218 | ### Comments
|
219 |
|
220 | Single line comments currently look the same as JavaScript comments,
|
221 | aka "//" and must be placed on their own line:
|
222 |
|
223 | // just some paragraphs
|
224 | p foo
|
225 | p bar
|
226 |
|
227 | would output
|
228 |
|
229 | <!-- just some paragraphs -->
|
230 | <p>foo</p>
|
231 | <p>bar</p>
|
232 |
|
233 | Jade also supports unbuffered comments, by simply adding a hyphen:
|
234 |
|
235 | //- will not output within markup
|
236 | p foo
|
237 | p bar
|
238 |
|
239 | outputting
|
240 |
|
241 | <p>foo</p>
|
242 | <p>bar</p>
|
243 |
|
244 | ### Block Comments
|
245 |
|
246 | A block comment is legal as well:
|
247 |
|
248 | body
|
249 | //
|
250 | #content
|
251 | h1 Example
|
252 |
|
253 | outputting
|
254 |
|
255 | <body>
|
256 | <!--
|
257 | <div id="content">
|
258 | <h1>Example</h1>
|
259 | </div>
|
260 | -->
|
261 | </body>
|
262 |
|
263 | Jade supports conditional-comments as well, for example:
|
264 |
|
265 | body
|
266 | /if IE
|
267 | a(href='http://www.mozilla.com/en-US/firefox/') Get Firefox
|
268 |
|
269 | outputs:
|
270 |
|
271 | <body>
|
272 | <!--[if IE]>
|
273 | <a href="http://www.mozilla.com/en-US/firefox/">Get Firefox</a>
|
274 | <![endif]-->
|
275 | </body>
|
276 |
|
277 |
|
278 | ### Nesting
|
279 |
|
280 | Jade supports nesting to define the tags in a natural way:
|
281 |
|
282 | ul
|
283 | li.first
|
284 | a(href='#') foo
|
285 | li
|
286 | a(href='#') bar
|
287 | li.last
|
288 | a(href='#') baz
|
289 |
|
290 | ### Block Expansion
|
291 |
|
292 | Block expansion allows you to create terse single-line nested tags,
|
293 | the following example is equivalent to the nesting example above.
|
294 |
|
295 | ul
|
296 | li.first: a(href='#') foo
|
297 | li: a(href='#') bar
|
298 | li.last: a(href='#') baz
|
299 |
|
300 |
|
301 | ### Attributes
|
302 |
|
303 | Jade currently supports '(' and ')' as attribute delimiters.
|
304 |
|
305 | a(href='/login', title='View login page') Login
|
306 |
|
307 | Boolean attributes are also supported:
|
308 |
|
309 | input(type="checkbox", checked)
|
310 |
|
311 | Boolean attributes with code will only output the attribute when `true`:
|
312 |
|
313 | input(type="checkbox", checked= someValue)
|
314 |
|
315 | Multiple lines work too:
|
316 |
|
317 | input(type='checkbox',
|
318 | name='agreement',
|
319 | checked)
|
320 |
|
321 | Multiple lines without the comma work fine:
|
322 |
|
323 | input(type='checkbox'
|
324 | name='agreement'
|
325 | checked)
|
326 |
|
327 | Funky whitespace? fine:
|
328 |
|
329 |
|
330 | input(
|
331 | type='checkbox'
|
332 | name='agreement'
|
333 | checked)
|
334 |
|
335 | Colons work:
|
336 |
|
337 | rss(xmlns:atom="atom")
|
338 |
|
339 | Suppose we have the `user` local `{ id: 12, name: 'tobi' }`
|
340 | and we wish to create an anchor tag with `href` pointing to "/user/12"
|
341 | we could use regular javascript concatenation:
|
342 |
|
343 | a(href='/user/' + user.id)= user.name
|
344 |
|
345 | or we could use jade's interpolation:
|
346 |
|
347 | a(href='/user/#{user.id}')= user.name
|
348 |
|
349 | ### Doctypes
|
350 |
|
351 | To add a doctype simply use `!!!`, or `doctype` followed by an optional value:
|
352 |
|
353 | !!!
|
354 |
|
355 | Will output the _transitional_ doctype, however:
|
356 |
|
357 | !!! 5
|
358 |
|
359 | or
|
360 |
|
361 | !!! html
|
362 |
|
363 | or
|
364 |
|
365 | doctype html
|
366 |
|
367 | doctypes are case-insensitive, so the following are equivalent:
|
368 |
|
369 | doctype Basic
|
370 | doctype basic
|
371 |
|
372 | Will output the _html 5_ doctype. Below are the doctypes
|
373 | defined by default, which can easily be extended:
|
374 |
|
375 | ```javascript
|
376 | var doctypes = exports.doctypes = {
|
377 | '5': '<!DOCTYPE html>',
|
378 | 'xml': '<?xml version="1.0" encoding="utf-8" ?>',
|
379 | 'default': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
|
380 | 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
|
381 | 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
|
382 | 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
|
383 | '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
|
384 | 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
|
385 | 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
|
386 | };
|
387 | ```
|
388 |
|
389 | To alter the default simply change:
|
390 |
|
391 | ```javascript
|
392 | jade.doctypes.default = 'whatever you want';
|
393 | ```
|
394 |
|
395 | ## Filters
|
396 |
|
397 | Filters are prefixed with `:`, for example `:markdown` and
|
398 | pass the following block of text to an arbitrary function for processing. View the _features_
|
399 | at the top of this document for available filters.
|
400 |
|
401 | body
|
402 | :markdown
|
403 | Woah! jade _and_ markdown, very **cool**
|
404 | we can even link to [stuff](http://google.com)
|
405 |
|
406 | Renders:
|
407 |
|
408 | <body><p>Woah! jade <em>and</em> markdown, very <strong>cool</strong> we can even link to <a href="http://google.com">stuff</a></p></body>
|
409 |
|
410 | Filters may also manipulate the parse tree. For example perhaps I want to
|
411 | bake conditionals right into jade, we could do so with a filter named _conditionals_. Typically filters work on text blocks, however by passing a regular block our filter can do anything it wants with the tags nested within it.
|
412 |
|
413 | body
|
414 | conditionals:
|
415 | if role == 'admin'
|
416 | p You are amazing
|
417 | else
|
418 | p Not so amazing
|
419 |
|
420 | Not that we no longer prefix with "-" for these code blocks. Examples of
|
421 | how to manipulate the parse tree can be found at _./examples/conditionals.js_ and _./examples/model.js_, basically we subclass and re-implement visitor methods as needed. There are several interesting use-cases for this functionality above what was shown above such as transparently aggregating / compressing assets to reduce the number of HTTP requests, transparent record error reporting, and more.
|
422 |
|
423 | ## Code
|
424 |
|
425 | Jade currently supports three classifications of executable code. The first
|
426 | is prefixed by `-`, and is not buffered:
|
427 |
|
428 | - var foo = 'bar';
|
429 |
|
430 | This can be used for conditionals, or iteration:
|
431 |
|
432 | - for (var key in obj)
|
433 | p= obj[key]
|
434 |
|
435 | Due to Jade's buffering techniques the following is valid as well:
|
436 |
|
437 | - if (foo)
|
438 | ul
|
439 | li yay
|
440 | li foo
|
441 | li worked
|
442 | - else
|
443 | p oh no! didnt work
|
444 |
|
445 | Hell, even verbose iteration:
|
446 |
|
447 | - if (items.length)
|
448 | ul
|
449 | - items.forEach(function(item){
|
450 | li= item
|
451 | - })
|
452 |
|
453 | Anything you want!
|
454 |
|
455 | Next up we have _escaped_ buffered code, which is used to
|
456 | buffer a return value, which is prefixed by `=`:
|
457 |
|
458 | - var foo = 'bar'
|
459 | = foo
|
460 | h1= foo
|
461 |
|
462 | Which outputs `bar<h1>bar</h1>`. Code buffered by `=` is escaped
|
463 | by default for security, however to output unescaped return values
|
464 | you may use `!=`:
|
465 |
|
466 | p!= aVarContainingMoreHTML
|
467 |
|
468 | The on exception made in terms of allowing "vanilla" JavaScript, is
|
469 | the `- each` token. This takes the form of:
|
470 |
|
471 | - each VAL[, KEY] in OBJ
|
472 |
|
473 | An example iterating over an array:
|
474 |
|
475 | - var items = ["one", "two", "three"]
|
476 | - each item in items
|
477 | li= item
|
478 |
|
479 | outputs:
|
480 |
|
481 | <li>one</li>
|
482 | <li>two</li>
|
483 | <li>three</li>
|
484 |
|
485 | iterating an object's keys and values:
|
486 |
|
487 | - var obj = { foo: 'bar' }
|
488 | - each val, key in obj
|
489 | li #{key}: #{val}
|
490 |
|
491 | would output `<li>foo: bar</li>`
|
492 |
|
493 | You can also nest these!
|
494 |
|
495 | - each user in users
|
496 | - each role in user.roles
|
497 | li= role
|
498 |
|
499 | When a property is undefined, Jade will output an empty string. For example:
|
500 |
|
501 | textarea= user.signature
|
502 |
|
503 | when undefined would normally output "undefined" in your html, however recent
|
504 | versions of Jade will simply render:
|
505 |
|
506 | <textarea></textarea>
|
507 |
|
508 | ## bin/jade
|
509 |
|
510 | Output html to _stdout_:
|
511 |
|
512 | jade examples/*.jade --pipe
|
513 |
|
514 | Generate _examples/*.html_:
|
515 |
|
516 | jade examples/*.jade
|
517 |
|
518 | Pass options:
|
519 |
|
520 | jade examples/layout.jade --options '{ locals: { title: "foo" }}'
|
521 |
|
522 | Usage info:
|
523 |
|
524 | Usage: jade [options] <path ...>
|
525 |
|
526 | Options:
|
527 | -o, --options STR JavaScript options object passed
|
528 | -p, --pipe Output to stdout instead of PATH.html
|
529 | -h, --help Output help information
|
530 |
|
531 | ## License
|
532 |
|
533 | (The MIT License)
|
534 |
|
535 | Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>
|
536 |
|
537 | Permission is hereby granted, free of charge, to any person obtaining
|
538 | a copy of this software and associated documentation files (the
|
539 | 'Software'), to deal in the Software without restriction, including
|
540 | without limitation the rights to use, copy, modify, merge, publish,
|
541 | distribute, sublicense, and/or sell copies of the Software, and to
|
542 | permit persons to whom the Software is furnished to do so, subject to
|
543 | the following conditions:
|
544 |
|
545 | The above copyright notice and this permission notice shall be
|
546 | included in all copies or substantial portions of the Software.
|
547 |
|
548 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
549 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
550 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
551 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
552 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
553 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
554 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|