Skip to content
This repository
Browse code

All tests are now passing

  • Loading branch information...
commit 9b03e49d49291ac6ec41371f357e07bf2f89c500 1 parent 5d413c7
authored December 19, 2010

Showing 53 changed files with 507 additions and 185 deletions. Show diff stats Hide diff stats

  1. 38  lib/mu-cli.js
  2. 126  lib/mu.js
  3. 37  lib/mu/parser.js
  4. 142  lib/mu/renderer.js
  5. 85  lib/mu/stream.js
  6. 7  package.json
  7. 6  test.txt
  8. 38  test/bench.js
  9. 1  test/examples/comments.html
  10. 5  test/examples/comments.js
  11. 1  test/examples/comments.txt
  12. 1  test/examples/complex.html
  13. 19  test/examples/complex.js
  14. 1  test/examples/complex.txt
  15. 2  test/examples/deep_partial.html
  16. 3  test/examples/deep_partial.js
  17. 3  test/examples/deep_partial.txt
  18. 6  test/examples/delimiters.html
  19. 6  test/examples/delimiters.js
  20. 6  test/examples/delimiters.txt
  21. 1  test/examples/error_not_found.html
  22. 1  test/examples/error_not_found.js
  23. 0  test/examples/error_not_found.txt
  24. 1  test/examples/escaped.html
  25. 5  test/examples/escaped.js
  26. 1  test/examples/escaped.txt
  27. 3  test/examples/hash_instead_of_array.html
  28. 5  test/examples/hash_instead_of_array.js
  29. 2  test/examples/hash_instead_of_array.txt
  30. 1  test/examples/inner_partial.html
  31. 2  test/examples/inverted.html
  32. 3  test/examples/inverted.js
  33. 2  test/examples/inverted.txt
  34. 2  test/examples/partial.html
  35. 3  test/examples/partial.js
  36. 2  test/examples/partial.txt
  37. 5  test/examples/recursion_with_same_names.html
  38. 8  test/examples/recursion_with_same_names.js
  39. 7  test/examples/recursion_with_same_names.txt
  40. 5  test/examples/reuse_of_enumerables.html
  41. 6  test/examples/reuse_of_enumerables.js
  42. 9  test/examples/reuse_of_enumerables.txt
  43. 5  test/examples/simple.html
  44. 8  test/examples/simple.js
  45. 4  test/examples/simple.txt
  46. 1  test/examples/two_in_a_row.html
  47. 4  test/examples/two_in_a_row.js
  48. 1  test/examples/two_in_a_row.txt
  49. 1  test/examples/unescaped.html
  50. 5  test/examples/unescaped.js
  51. 1  test/examples/unescaped.txt
  52. 47  test/run_examples_test.js
  53. 8  try.js
38  lib/mu-cli.js
... ...
@@ -0,0 +1,38 @@
  1
+#!/usr/bin/env node
  2
+
  3
+var util     = require('util'),
  4
+    mu       = require('./mu'),
  5
+    parser   = require('./mu/parser');
  6
+
  7
+var stdin = process.openStdin();
  8
+
  9
+   
  10
+stdin.on('data', function (template) {
  11
+  template = template.toString('utf8');
  12
+  var parsed = parser.parse(template);
  13
+
  14
+  if (~process.argv.indexOf('--tokens')) {
  15
+    console.log(util.inspect(parsed, false, 20));
  16
+    return;
  17
+  }
  18
+
  19
+  process.argv.forEach(function (arg) {
  20
+    if (arg.indexOf('--view=') === 0) {
  21
+      try {
  22
+        var view = eval('(' + arg.replace('--view=', '') + ')');
  23
+      } catch (e) {
  24
+        console.log('\nData: ' + arg.replace('--view=', ''));
  25
+        throw e;
  26
+      }
  27
+
  28
+      mu.renderText(template, view)
  29
+        .on('data', function (d) {
  30
+          util.print(d);
  31
+        });
  32
+    }
  33
+  });
  34
+
  35
+});
  36
+
  37
+
  38
+
126  lib/mu.js
... ...
@@ -1,9 +1,9 @@
1 1
 var sys      = require('sys'),
2 2
     fs       = require('fs'),
3 3
     path     = require('path'),
  4
+    Stream   = require('stream').Stream,
4 5
     parser   = require('./mu/parser'),
5 6
     renderer = require('./mu/renderer'),
6  
-    Stream   = require('./mu/stream'),
7 7
     errors   = require('./mu/errors');
8 8
 
9 9
 var mu = module.exports = {};
@@ -12,11 +12,22 @@ mu.root = process.cwd();
12 12
 mu.cache = {};
13 13
 
14 14
 mu.fs = function (filename, callback) {
15  
-  fs.readFile(path.join(mu.root, filename), 'utf8', callback);
  15
+  filename = filename.indexOf('/') === 0 ? filename : path.join(mu.root, filename);
  16
+  fs.readFile(filename, 'utf8', callback);
16 17
 }
17 18
 
18  
-mu.compile = function(filename, callback) {
19  
-  var parsed;
  19
+/**
  20
+ * Compiles a file. The result will be cached as the filename and can be
  21
+ * rendered via that name.
  22
+ *
  23
+ * @param {String} filename The name of the file to compile. If the filename
  24
+ *        starts with a '/', the file is assumed to be absolute, else it is
  25
+ *        relative to mu.root.
  26
+ * @param {Function(err, Parsed)} callback The function to call when the file has been compiled.
  27
+ */
  28
+mu.compile = function(filename, callback, unique) {
  29
+  var parsed,
  30
+      unique = unique || {};
20 31
   
21 32
   mu.fs(filename, function (err, contents) {
22 33
     if (err) {
@@ -24,54 +35,86 @@ mu.compile = function(filename, callback) {
24 35
     }
25 36
     
26 37
     parsed = parser.parse(contents);
27  
-    mu.cache[filename] = parsed;
28  
-    callback(undefined, parsed);
  38
+    mu.cache[filename] = [parsed, unique];
  39
+
  40
+    var i = 0;
  41
+    (function next(err) {
  42
+      if (err) {
  43
+        return callback(err);
  44
+      }
  45
+
  46
+      if (i < parsed.partials.length) {
  47
+        mu.compile(parsed.partials[i], next, unique);
  48
+        i++;
  49
+        
  50
+      } else {
  51
+        callback(undefined, parsed);
  52
+      }
  53
+    }());
29 54
   });
30 55
 }
31 56
 
  57
+/**
  58
+ * Compiles a string into the parsed form. If `name` is provided the text
  59
+ * will be cached by that name. Alternatively you can pass the return
  60
+ * into mu.render.
  61
+ *
  62
+ * @param {String} name (Optional) The name to cache the parsed form as.
  63
+ * @param {String} template The template to parse.
  64
+ * @param {Function(err, Parsed)} callback (Optional) An optional callback that
  65
+ *        will be called when the text is parsed. This is only to unify the
  66
+ *        API with mu.compile.
  67
+ *
  68
+ * @returns {Parsed} The parsed template unless `callback` is provided.
  69
+ */
32 70
 mu.compileText = function (name, template, callback) {
33 71
   var parsed;
34 72
   
35  
-  callback = callback || function() { /* noop */ };
36  
-  
37 73
   if (typeof template === 'undefined') {
38 74
     template = name;
39 75
     name = undefined;
40 76
   }
41 77
   
42  
-  parsed = parser.parse(template);
43  
-  if (name) {
44  
-    mu.cache[name] = parsed;
  78
+  try {
  79
+    parsed = parser.parse(template);
  80
+    
  81
+    if (name) {
  82
+      mu.cache[name] = [parsed, {}];
  83
+    }
  84
+
  85
+    if (callback) callback(undefined, parsed); else return parsed;
  86
+    
  87
+  } catch (err) {
  88
+    if (callback) callback(err); else throw err;
45 89
   }
46  
-  
47  
-  callback(undefined, parsed); // this is to unify the API
48  
-  return parsed;
49 90
 }
50 91
 
51  
-mu.render = function (filename, view) {
52  
-  var stream;
  92
+/**
  93
+ * Renders the previously parsed filename or the parsed object.
  94
+ *
  95
+ * @param {String|Parsed} filenameOrParsed Filename or parsed object to render.
  96
+ * @param {Object} view The data to use when renderings.
  97
+ *
  98
+ * @returns {Stream} The render stream.
  99
+ * @throws {Error(template_not_in_cache)} If filename was not found in cache.
  100
+ */
  101
+mu.render = function (filenameOrParsed, view) {
  102
+  var stream,
  103
+      parsed = typeof filenameOrParsed === 'object' ?
  104
+                  filenameOrParsed :
  105
+                  mu.cache[filenameOrParsed];
53 106
   
54  
-  if (mu.cache[filename]) {
55  
-    stream = new Stream();
56  
-    process.nextTick(function () {
57  
-      renderer.render(mu.cache[filename].tokens, view, mu.cache, stream, function () {
58  
-        stream.emit('end');
59  
-      });
60  
-    });
61  
-    return stream;
  107
+  if (parsed) {
  108
+    return beginRender(parsed[0].tokens, view, mu.cache);
62 109
   } else {
63 110
     throw new Error('template_not_in_cache'); //errors.templateNotInCache(filename)));
64 111
   }
65 112
 }
66 113
 
67  
-mu.renderText = function (template, view, partials, callback) {
68  
-  var name, parsed, tokens;
69  
-  
70  
-  if (typeof callback === 'undefined') {
71  
-    callback = partials;
72  
-    partials = {};
73  
-  }
  114
+mu.renderText = function (template, view, partials) {
  115
+  var name, parsed, tokens, stream;
74 116
   
  117
+  partials = partials || {};
75 118
   partials = shallowCopy(partials);
76 119
   partials.__proto__ = mu.cache;
77 120
   
@@ -84,12 +127,31 @@ mu.renderText = function (template, view, partials, callback) {
84 127
   parsed = parser.parse(template);
85 128
   tokens = parsed.tokens;
86 129
   
87  
-  renderer.render(tokens, view, partials, callback);
  130
+  return beginRender(tokens, view, partials);
88 131
 }
89 132
 
90 133
 
91 134
 /// Private API
92 135
 
  136
+var BUFFER_LENGTH = 1024 * 8;
  137
+
  138
+function beginRender(tokens, view, partials) {
  139
+  var stream = new Stream();
  140
+  var count = 0;
  141
+  
  142
+  process.nextTick(function () {
  143
+    try {
  144
+      renderer.render(tokens, view, partials, stream, function () {
  145
+        stream.emit('end');
  146
+      });
  147
+    } catch (err) {
  148
+      stream.emit('error', err);
  149
+    }
  150
+  });
  151
+  
  152
+  return stream;
  153
+}
  154
+
93 155
 function shallowCopy(obj) {
94 156
   var o = {};
95 157
   
37  lib/mu/parser.js
... ...
@@ -1,5 +1,8 @@
1 1
 var sys    = require('sys'),
2  
-    Buffer = require('buffer').Buffer;
  2
+    Buffer = require('buffer').Buffer,
  3
+    
  4
+    newline = '__MU_NEWLINE__',
  5
+    newlineRegExp = new RegExp(newline, 'g');
3 6
 
4 7
 exports.parse = function (template, options) {
5 8
   var parser = new Parser(template, options);
@@ -7,7 +10,7 @@ exports.parse = function (template, options) {
7 10
 }
8 11
 
9 12
 function Parser(template, options) {
10  
-  this.template = template;
  13
+  this.template = template.replace(/\n/g, newline);
11 14
   this.options  = options || {};
12 15
   
13 16
   this.sections = [];
@@ -15,6 +18,7 @@ function Parser(template, options) {
15 18
   this.partials = [];
16 19
   this.buffer   = this.template;
17 20
   this.state    = 'static'; // 'static' or 'tag'
  21
+  this.currentLine = '';
18 22
   
19 23
   this.setTag(['{{', '}}']);
20 24
 }
@@ -31,6 +35,13 @@ Parser.prototype = {
31 35
     
32 36
     return {partials: this.partials, tokens: this.tokens};
33 37
   },
  38
+
  39
+  appendMultiContent: function (content) {
  40
+    for (var i = 0, len = this.sections.length; i < len; i++) {
  41
+      var multi = this.sections[i][1];
  42
+      multi = multi[multi.length - 1][3] += content;
  43
+    }
  44
+  },
34 45
   
35 46
   setTag: function (tags) {
36 47
     this.otag = tags[0] || '{{';
@@ -44,14 +55,19 @@ Parser.prototype = {
44 55
       index = this.buffer.length;
45 56
     }
46 57
     
47  
-    var content = this.buffer.substring(0, index);
  58
+    var content = this.buffer.substring(0, index).replace(newlineRegExp, '\n');
48 59
         buffer  = new Buffer(Buffer.byteLength(content));
49 60
     
50 61
     if (content !== '') {
51 62
       buffer.write(content, 'utf8', 0);
  63
+      this.appendMultiContent(content);
52 64
       this.tokens.push(['static', content, buffer]);
53 65
     }
54  
-    
  66
+   
  67
+    var line = this.currentLine + content;
  68
+
  69
+    this.currentLine = line.substring(line.lastIndexOf('\n') + 1, line.length);
  70
+    // console.log('line:', this.buffer.lastIndexOf(newline) + newline.length, index, '>', this.currentLine, '/end');
55 71
     this.buffer = this.buffer.substring(index + this.otag.length);
56 72
     this.state  = 'tag';
57 73
   },
@@ -81,22 +97,27 @@ Parser.prototype = {
81 97
     
82 98
     var sigil     = match[1],
83 99
         content   = match[2].trim(),
84  
-        remainder = match[3];
  100
+        remainder = match[3],
  101
+        tagText   = this.otag + this.buffer.substring(0, this.buffer.length - remainder.length);
  102
+
85 103
     
86 104
     switch (sigil) {
87 105
     case undefined:
88 106
       this.tokens.push(['mustache', 'etag', content]);
  107
+    this.appendMultiContent(tagText);
89 108
       break;
90 109
       
91 110
     case '>':
92 111
     case '<':
93 112
       this.tokens.push(['mustache', 'partial', content]);
94 113
       this.partials.push(content);
  114
+    this.appendMultiContent(tagText);
95 115
       break;
96 116
       
97 117
     case '{':
98 118
     case '&':
99 119
       this.tokens.push(['mustache', 'utag', content]);
  120
+    this.appendMultiContent(tagText);
100 121
       break;
101 122
     
102 123
     case '!':
@@ -106,14 +127,16 @@ Parser.prototype = {
106 127
     case '=':
107 128
       sys.puts("Changing tag: " + content)
108 129
       this.setTag(content.split(' '));
  130
+    this.appendMultiContent(tagText);
109 131
       break;
110 132
     
111 133
     case '#':
112 134
     case '^':
  135
+    this.appendMultiContent(tagText);
113 136
       var type = sigil === '#' ? 'section' : 'inverted_section';
114 137
           block = ['multi'];
115 138
       
116  
-      this.tokens.push(['mustache', type, content, block]);
  139
+      this.tokens.push(['mustache', type, content, '', block]);
117 140
       this.sections.push([content, this.tokens]);
118 141
       this.tokens = block;
119 142
       break;
@@ -129,12 +152,12 @@ Parser.prototype = {
129 152
       } else if (name !== content) {
130 153
         throw new Error("Unclosed section " + name);
131 154
       }
  155
+    this.appendMultiContent(tagText);
132 156
       break;
133 157
     }
134 158
     
135 159
     this.buffer = remainder;
136 160
     this.state  = 'static';
137  
-    
138 161
   }
139 162
 }
140 163
 
142  lib/mu/renderer.js
... ...
@@ -1,69 +1,83 @@
  1
+var BUFFER_LENGTH = 1024 * 8;
  2
+
1 3
 var parser = require('./parser'),
2  
-    sys    = require('sys'),
3 4
     baseProto = ({}).__proto__;
4 5
 
5 6
 exports.render = render;
6 7
 
7 8
 function render(tokens, context, partials, stream, callback) {
8 9
   if (tokens[0] !== 'multi') {
9  
-    throw new Error('WTF did you give me?');
  10
+    throw new Error('Mu - WTF did you give me? I expected mustache tokens.');
10 11
   }
  12
+
  13
+  //if (!Array.isArray(context)) {
  14
+  //  context = [context];
  15
+  //}
11 16
   
12 17
   var i = 1;
13 18
   
14 19
   function next() {
  20
+    try {
15 21
     
16  
-    if (stream.paused) {
17  
-      stream.on('resume', function () {
18  
-        process.nextTick(next);
19  
-      });
20  
-      return;
21  
-    }
22  
-    
23  
-    var token = tokens[i++];
  22
+      if (stream.paused) {
  23
+        stream.on('drain', function () {
  24
+          process.nextTick(next);
  25
+        });
  26
+        return;
  27
+      }
24 28
     
25  
-    if (!token) {
26  
-      return callback ? callback() : true;
27  
-    }
  29
+      var token = tokens[i++];
28 30
     
29  
-    switch (token[0]) {
30  
-    case 'static':
31  
-      stream.write(token[2]);
32  
-      return next();
  31
+      if (!token) {
  32
+        return callback ? callback() : true;
  33
+      }
33 34
     
34  
-    case 'mustache':    
35  
-      switch (token[1]) {
36  
-      case 'utag': // Unescaped Tag
37  
-        stream.write(s(normalize(context, token[2])));
  35
+      switch (token[0]) {
  36
+      case 'static':
  37
+        stream.emit('data', token[2]);
38 38
         return next();
39  
-        
40  
-      case 'etag': // Escaped Tag
41  
-        stream.write(escape(s(normalize(context, token[2]))));
42  
-        return next();
43  
-      
44  
-      case 'section':
45  
-        if (normalize(context, token[2])) {
46  
-          return section(context, token[2], token[3], partials, stream, next);
47  
-        } else {
  39
+    
  40
+      case 'mustache':    
  41
+        switch (token[1]) {
  42
+        case 'utag': // Unescaped Tag
  43
+          stream.emit('data', s(normalize(context, token[2])));
48 44
           return next();
49  
-        }
50 45
         
51  
-      case 'inverted_section':
52  
-        if (!normalize(context, token[2])) {
53  
-          return section(context, token[2], token[3], partials, stream, next);
54  
-        } else {
  46
+        case 'etag': // Escaped Tag
  47
+          stream.emit('data', escape(s(normalize(context, token[2]))));
55 48
           return next();
56  
-        }
  49
+      
  50
+        case 'section':
  51
+          var res = normalize(context, token[2], token[3]);
  52
+          if (res) {
  53
+            return section(context, token[2], res, token[4], partials, stream, next);
  54
+          } else {
  55
+            return next();
  56
+          }
57 57
         
58  
-      case 'partial':
59  
-        var partial = partials[token[2]];
60  
-        if (partial) {
61  
-          return render(partial.tokens, context, partials, stream, next);
62  
-        } else {
63  
-          return next();
  58
+        case 'inverted_section':
  59
+          var res = normalize(context, token[2], token[3]);
  60
+          if (!res) {
  61
+            return section(context, token[2], !res, token[4], partials, stream, next);
  62
+          } else {
  63
+            return next();
  64
+          }
  65
+        
  66
+        case 'partial':
  67
+          var partial = partials[token[2]];
  68
+          // console.log(require('util').inspect(partials));
  69
+          if (partial) {
  70
+            return render(partial[0].tokens, context, partials, stream, next);
  71
+          } else {
  72
+            return next();
  73
+          }
64 74
         }
  75
+    
65 76
       }
66 77
     
  78
+    } catch (err) {
  79
+      stream.emit('error', err);
  80
+      next();
67 81
     }
68 82
   }
69 83
   
@@ -78,40 +92,37 @@ function escape(string) {
78 92
   return string.replace(/[&<>"]/g, escapeReplace);
79 93
 }
80 94
 
81  
-function normalize(view, name) {
82  
-  var val = view[name];
  95
+function normalize(context, name, body) {
  96
+  var val = context[name];
83 97
   
84 98
   if (typeof(val) === 'function') {
85  
-    val = view[name]();
  99
+    val = context[name](body);
86 100
   }
87 101
   
88 102
   return val;
89 103
 }
90 104
 
91  
-function section(view, name, tokens, partials, stream, callback) {
92  
-  var val = normalize(view, name);
  105
+function section(view, name, val, tokens, partials, stream, callback) {
  106
+  // var val = normalize(view, name, body);
93 107
   
94 108
   if (typeof val === 'boolean') {
95  
-    return val ? render(tokens, val, partials, stream, callback) : callback();
  109
+    return val ? render(tokens, view, partials, stream, callback) : callback();
96 110
   }
97 111
   
98 112
   if (val instanceof Array) {
99 113
     var i = 0;
100 114
     
101 115
     (function next() {
102  
-      // if (stream.paused) {
103  
-      //   stream.on('resume', function () {
104  
-      //     next();
105  
-      //   });
106  
-      //   return;
107  
-      // }
108  
-      
109 116
       var item = val[i++];
110 117
       
111 118
       if (item) {
  119
+        //view.push(item);
112 120
         var proto = insertProto(item, view);
113  
-        render(tokens, item, partials, stream, next);
114  
-        proto.__proto__ = baseProto;
  121
+        render(tokens, item, partials, stream, function () {
  122
+          proto.__proto__ = baseProto;
  123
+          next();
  124
+        });
  125
+        //view.pop();
115 126
       } else {
116 127
         callback();
117 128
       }
@@ -122,9 +133,11 @@ function section(view, name, tokens, partials, stream, callback) {
122 133
   }
123 134
   
124 135
   if (typeof val === 'object') {
  136
+    //view.push(val);
125 137
     var proto = insertProto(val, view);
126 138
     render(tokens, val, partials, stream, callback);
127 139
     proto.__proto__ = baseProto;
  140
+    //view.pop();
128 141
     return;
129 142
   }
130 143
   
@@ -135,6 +148,18 @@ function section(view, name, tokens, partials, stream, callback) {
135 148
 //
136 149
 //
137 150
 //
  151
+function findInContext(context, key) {
  152
+  var i = context.length;
  153
+  while (i--) {
  154
+    if (context[i][key]) {
  155
+      return context[i][key];
  156
+    }
  157
+  }
  158
+
  159
+  return undefined;
  160
+}
  161
+
  162
+
138 163
 function insertProto(obj, newProto, replaceProto) {
139 164
   replaceProto = replaceProto || baseProto;
140 165
   var proto = obj.__proto__;
@@ -142,11 +167,12 @@ function insertProto(obj, newProto, replaceProto) {
142 167
     obj = proto;
143 168
     proto = proto.__proto__;
144 169
   }
145  
-  
146 170
   obj.__proto__ = newProto;
147 171
   return obj;
148 172
 }
149 173
 
  174
+
  175
+
150 176
 //
151 177
 //
152 178
 //
85  lib/mu/stream.js
... ...
@@ -1,85 +0,0 @@
1  
-var EventEmitter = require('events').EventEmitter;
2  
-
3  
-module.exports = Stream;
4  
-
5  
-function Stream() {
6  
-  //this.events = {data: [], end: [], resume: [], error: []};
7  
-  
8  
-  this.paused = false;
9  
-  this.closed = false;
10  
-  this.buffered = [];
11  
-  this.emitter = new EventEmitter();
12  
-}
13  
-
14  
-Stream.prototype = {
15  
-  addListener: function (eventName, listener) {
16  
-    this.emitter.on(eventName, listener);
17  
-    return this;
18  
-  },
19  
-  
20  
-  removeListener: function (eventName, listener) {
21  
-    this.emitter.removeListener(eventName, listener);
22  
-    return this;
23  
-  },
24  
-  
25  
-  removeAllListeners: function (eventName) {
26  
-    this.emitter.removeAllListeners(eventName);
27  
-    return this;
28  
-  },
29  
-  
30  
-  emit: function (eventName, data) {
31  
-    this.emitter.emit(eventName, data);
32  
-  },
33  
-  
34  
-  write: function (data) {
35  
-    if (this.closed) return false;
36  
-    
37  
-    if (this.paused) {
38  
-      this.buffered.push(data);
39  
-      return false;
40  
-    } else {
41  
-      this.emit('data', data);
42  
-      return true;
43  
-    }
44  
-  },
45  
-  
46  
-  pause: function () {
47  
-    this.paused = true;
48  
-  },
49  
-  
50  
-  resume: function () {
51  
-    var buffer
52  
-    
53  
-    this.paused = false;
54  
-    while (!this.paused && this.buffered.length) {
55  
-      buffer = this.buffered.shift();
56  
-      this.emit('data', buffer);
57  
-    }
58  
-    
59  
-    if (!this.paused) {
60  
-      this.emit('resume');
61  
-      this.removeAllListeners('resume');
62  
-      
63  
-      if (!this.paused) {
64  
-        if (this.closed) {
65  
-          this.end();
66  
-        }
67  
-
68  
-        return true;
69  
-      }
70  
-    }
71  
-    
72  
-    return false;
73  
-  },
74  
-  
75  
-  end: function () {
76  
-    this.closed = true;
77  
-    
78  
-    if (this.buffered.length === 0) {
79  
-      this.emit('end');
80  
-    }
81  
-  }
82  
-};
83  
-
84  
-Stream.prototype.on = Stream.prototype.addListener;
85  
-
7  package.json
... ...
@@ -0,0 +1,7 @@
  1
+{ "name": "mu"
  2
+, "description": "A Mustache template engine for Node.js"
  3
+, "keywords": ["template", "mustache"]
  4
+, "version" : "0.5.0"
  5
+, "homepage": "http://github.com/raycmorgan/mu"
  6
+, "author" : "RayMorgan <ray@simple-apps.com>"
  7
+}
6  test.txt
... ...
@@ -0,0 +1,6 @@
  1
+Hello World
  2
+{{#person}}
  3
+\/\\tHow are you, {{#person}}{{name}}{{/person}}?
  4
+{{/person}}
  5
+Goodbye.
  6
+
38  test/bench.js
... ...
@@ -0,0 +1,38 @@
  1
+var fs     = require('fs'),
  2
+    path   = require('path'),
  3
+    mu     = require('../lib/mu'),
  4
+    pump   = require('util').pump;
  5
+
  6
+mu.root = path.join(__dirname, 'examples');
  7
+
  8
+var js   = fs.readFileSync(path.join(mu.root, 'complex.js')).toString(),
  9
+    text = fs.readFileSync(path.join(mu.root, 'complex.txt')).toString();
  10
+
  11
+js = eval('(' + js + ')');
  12
+
  13
+var RUNS = parseInt(process.argv[2] || "1000000");
  14
+
  15
+mu.compile('complex.html', function (err, compiled) {
  16
+  if (err) {
  17
+    throw err;
  18
+  }
  19
+  
  20
+  //var buffer = '';
  21
+  //mu.render('complex.html', js)
  22
+  //  .on('data', function (c) { buffer += c.toString(); })
  23
+  //  .on('end', function () { console.log(buffer); });
  24
+  
  25
+  pump(mu.render('complex.html', js), process.stdout);
  26
+
  27
+  var i = 0, d = new Date();
  28
+  
  29
+  (function go() {
  30
+    if (i++ < RUNS) {
  31
+      mu.render('complex.html', js).on('end', function () { go(); });
  32
+    }
  33
+  }())
  34
+  
  35
+  process.addListener('exit', function () {
  36
+    require('util').debug("Time taken: " + ((new Date() - d) / 1000) + "secs");
  37
+  });
  38
+});
1  test/examples/comments.html
... ...
@@ -0,0 +1 @@
  1
+<h1>{{title}}{{! just something interesting... or not... }}</h1>
5  test/examples/comments.js
... ...
@@ -0,0 +1,5 @@
  1
+{
  2
+  title: function() {
  3
+    return "A Comedy of Errors";
  4
+  }
  5
+}
1  test/examples/comments.txt
... ...
@@ -0,0 +1 @@
  1
+<h1>A Comedy of Errors</h1>
1  test/examples/complex.html
... ...
@@ -0,0 +1 @@
  1
+<h1>{{header}}</h1>{{#list}}<ul>{{#item}}{{#current}}<li><strong>{{name}}</strong></li>{{/current}}{{#link}}<li><a href="{{url}}">{{name}}</a></li>{{/link}}{{/item}}</ul>{{/list}}{{#empty}}<p>The list is empty.</p>{{/empty}}
19  test/examples/complex.js
... ...
@@ -0,0 +1,19 @@
  1
+{
  2
+  header: function() {
  3
+    return "Colors";
  4
+  },
  5
+  item: [
  6
+      {name: "red", current: true, url: "#Red"},
  7
+      {name: "green", current: false, url: "#Green"},
  8
+      {name: "blue", current: false, url: "#Blue"}
  9
+  ],
  10
+  link: function() {
  11
+    return this["current"] !== true;
  12
+  },
  13
+  list: function() {
  14
+    return this.item.length !== 0;
  15
+  },
  16
+  empty: function() {
  17
+    return this.item.length === 0;
  18
+  }
  19
+}
1  test/examples/complex.txt
... ...
@@ -0,0 +1 @@
  1
+<h1>Colors</h1><ul><li><strong>red</strong></li><li><a href="#Green">green</a></li><li><a href="#Blue">blue</a></li></ul>
2  test/examples/deep_partial.html
... ...
@@ -0,0 +1,2 @@
  1
+<h1>First: {{title}}</h1>
  2
+{{>partial.html}}
3  test/examples/deep_partial.js
... ...
@@ -0,0 +1,3 @@
  1
+{
  2
+  title: "Welcome"
  3
+}
3  test/examples/deep_partial.txt
... ...
@@ -0,0 +1,3 @@
  1
+<h1>First: Welcome</h1>
  2
+<h1>Welcome</h1>
  3
+<p class="again">Again, Welcome!</p>
6  test/examples/delimiters.html
... ...
@@ -0,0 +1,6 @@
  1
+{{=<% %>=}}* <% first %>
  2
+* <% second %>
  3
+<%=| |=%>
  4
+* | third |
  5
+|={{ }}=|
  6
+* {{ fourth }}
6  test/examples/delimiters.js
... ...
@@ -0,0 +1,6 @@
  1
+{
  2
+  first: "It worked the first time.",
  3
+  second: "And it worked the second time.",
  4
+  third: "Then, surprisingly, it worked the third time.",
  5
+  fourth: "Fourth time also fine!."
  6
+}
6  test/examples/delimiters.txt
... ...
@@ -0,0 +1,6 @@
  1
+* It worked the first time.
  2
+* And it worked the second time.
  3
+
  4
+* Then, surprisingly, it worked the third time.
  5
+
  6
+* Fourth time also fine!.
1  test/examples/error_not_found.html
... ...
@@ -0,0 +1 @@
  1
+{{foo}}
1  test/examples/error_not_found.js
... ...
@@ -0,0 +1 @@
  1
+{bar: 2}
0  test/examples/error_not_found.txt
No changes.
1  test/examples/escaped.html
... ...
@@ -0,0 +1 @@
  1
+<h1>{{title}}</h1>
5  test/examples/escaped.js
... ...
@@ -0,0 +1,5 @@
  1
+{
  2
+  title: function() {
  3
+    return "Bear > Shark";
  4
+  }
  5
+}
1  test/examples/escaped.txt
... ...
@@ -0,0 +1 @@
  1
+<h1>Bear &gt; Shark</h1>
3  test/examples/hash_instead_of_array.html
... ...
@@ -0,0 +1,3 @@
  1
+{{#person}}
  2
+  Name: {{name}}
  3
+{{/person}}
5  test/examples/hash_instead_of_array.js
... ...
@@ -0,0 +1,5 @@
  1
+{
  2
+  person: {
  3
+    name: "Chris"
  4
+  }
  5
+}
2  test/examples/hash_instead_of_array.txt
... ...
@@ -0,0 +1,2 @@
  1
+
  2
+  Name: Chris
1  test/examples/inner_partial.html
... ...
@@ -0,0 +1 @@
  1
+<p class="again">Again, {{title}}!</p>
2  test/examples/inverted.html
... ...
@@ -0,0 +1,2 @@
  1
+{{#admin}}Admin.{{/admin}}
  2
+{{^admin}}Not Admin.{{/admin}}
3  test/examples/inverted.js
... ...
@@ -0,0 +1,3 @@
  1
+{
  2
+  admin: false
  3
+}
2  test/examples/inverted.txt
... ...
@@ -0,0 +1,2 @@
  1
+
  2
+Not Admin.
2  test/examples/partial.html
... ...
@@ -0,0 +1,2 @@
  1
+<h1>{{title}}</h1>
  2
+{{>inner_partial.html}}
3  test/examples/partial.js
... ...
@@ -0,0 +1,3 @@
  1
+{
  2
+  title: "Welcome"
  3
+}
2  test/examples/partial.txt
... ...
@@ -0,0 +1,2 @@
  1
+<h1>Welcome</h1>
  2
+<p class="again">Again, Welcome!</p>
5  test/examples/recursion_with_same_names.html
... ...
@@ -0,0 +1,5 @@
  1
+{{ name }}
  2
+{{ description }}
  3
+{{#terms}}
  4
+  {{name}}
  5
+  {{index}}{{/terms}}
8  test/examples/recursion_with_same_names.js
... ...
@@ -0,0 +1,8 @@
  1
+{
  2
+  name: 'name',
  3
+  description: 'desc',
  4
+  terms: [
  5
+    {name: 't1', index: 0},
  6
+    {name: 't2', index: 1},
  7
+  ]
  8
+}
7  test/examples/recursion_with_same_names.txt
... ...
@@ -0,0 +1,7 @@
  1
+name
  2
+desc
  3
+
  4
+  t1
  5
+  0
  6
+  t2
  7
+  1
5  test/examples/reuse_of_enumerables.html
... ...
@@ -0,0 +1,5 @@
  1
+{{#terms}}
  2
+  {{name}}
  3
+  {{index}}{{/terms}}{{#terms}}
  4
+  {{name}}
  5
+  {{index}}{{/terms}}
6  test/examples/reuse_of_enumerables.js
... ...
@@ -0,0 +1,6 @@
  1
+{
  2
+  terms: [
  3
+    {name: 't1', index: 0},
  4
+    {name: 't2', index: 1},
  5
+  ]
  6
+}
9  test/examples/reuse_of_enumerables.txt
... ...
@@ -0,0 +1,9 @@
  1
+
  2
+  t1
  3
+  0
  4
+  t2
  5
+  1
  6
+  t1
  7
+  0
  8
+  t2
  9
+  1
5  test/examples/simple.html
... ...
@@ -0,0 +1,5 @@
  1
+Hello {{name}}
  2
+You have just won ${{value}}!
  3
+{{#in_ca}}
  4
+Well, ${{ taxed_value }}, after taxes.
  5
+{{/in_ca}}
8  test/examples/simple.js
... ...
@@ -0,0 +1,8 @@
  1
+{
  2
+  name: "Chris",
  3
+  value: 10000,
  4
+  taxed_value: function () {
  5
+    return this.value - (this.value * 0.4);
  6
+  },
  7
+  in_ca: true
  8
+}
4  test/examples/simple.txt
... ...
@@ -0,0 +1,4 @@
  1
+Hello Chris
  2
+You have just won $10000!
  3
+
  4
+Well, $6000, after taxes.
1  test/examples/two_in_a_row.html
... ...
@@ -0,0 +1 @@
  1
+{{greeting}}, {{name}}!
4  test/examples/two_in_a_row.js
... ...
@@ -0,0 +1,4 @@
  1
+{
  2
+  name: "Joe",
  3
+  greeting: "Welcome"
  4
+}
1  test/examples/two_in_a_row.txt
... ...
@@ -0,0 +1 @@
  1
+Welcome, Joe!
1  test/examples/unescaped.html
... ...
@@ -0,0 +1 @@
  1
+<h1>{{{title}}}</h1>
5  test/examples/unescaped.js
... ...
@@ -0,0 +1,5 @@
  1
+{
  2
+  title: function() {
  3
+    return "Bear > Shark";
  4
+  }
  5
+}
1  test/examples/unescaped.txt
... ...
@@ -0,0 +1 @@
  1
+<h1>Bear > Shark</h1>
47  test/run_examples_test.js
... ...
@@ -0,0 +1,47 @@
  1
+var assert = require('assert'),
  2
+    fs     = require('fs'),
  3
+    path   = require('path'),
  4
+    mu     = require('../lib/mu');
  5
+
  6
+mu.root = path.join(__dirname, 'examples');
  7
+
  8
+[
  9
+ 'comments',
  10
+ 'complex',
  11
+ 'deep_partial',
  12
+  // 'delimiters',
  13
+  'error_not_found',
  14
+  'escaped',
  15
+  'hash_instead_of_array',
  16
+  'inverted',
  17
+  'partial',
  18
+  'recursion_with_same_names',
  19
+  'reuse_of_enumerables',
  20
+  'simple',
  21
+  'two_in_a_row',
  22
+  'unescaped',
  23
+].forEach(function (name) {
  24
+  
  25
+  var js   = fs.readFileSync(path.join(mu.root, name + '.js')).toString(),
  26
+      text = fs.readFileSync(path.join(mu.root, name + '.txt')).toString();
  27
+
  28
+  js = eval('(' + js + ')');
  29
+  
  30
+  mu.compile(name + '.html', function (err, parsed) {
  31
+    if (err) {
  32
+      throw err;
  33
+    }
  34
+    
  35
+    var buffer = '';
  36
+    
  37
+    mu.render(name + '.html', js)
  38
+      .on('data', function (c) { buffer += c.toString(); })
  39
+      .on('end', function () {
  40
+        assert.equal(buffer, text);
  41
+        console.log(name + ' passed');
  42
+      })
  43
+      //.on('error', function (error) {
  44
+      //  console.log('Error: ' + error);
  45
+      //});
  46
+  });
  47
+});
8  try.js
@@ -45,14 +45,16 @@ mu.compileText('bar.html', template);
45 45
 //     .on('error', function (err) {});
46 46
 // });
47 47
 
  48
+var numErrors = 0;
  49
+
48 50
 var stream = mu.render('bar.html', view)
49 51
   .on('data', function (data) { 
50 52
     sys.print(data);
51 53
     //stream.pause();
52  
-    //setTimeout(function () { stream.resume(); }, 1000);
  54
+    //setTimeout(function () { stream.resume(); }, 50);
53 55
   })
54  
-  .on('end',  function () { sys.print('\nDONE\n'); })
55  
-  .on('error', function (err) {});
  56
+  .on('end',  function () { sys.print('\nDONE, Errors: ' + numErrors + '\n'); })
  57
+  .on('error', function (err) { numErrors++; });
56 58
 
57 59
 
58 60
 

0 notes on commit 9b03e49

Please sign in to comment.
Something went wrong with that request. Please try again.