stylus-source 0.21.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/VERSION +1 -1
  2. data/vendor/History.md +11 -0
  3. data/vendor/Readme.md +4 -0
  4. data/vendor/bin/stylus +9 -32
  5. data/vendor/docs/bifs.md +16 -0
  6. data/vendor/docs/js.md +2 -0
  7. data/vendor/editors/Stylus.tmbundle/Syntaxes/Stylus.tmLanguage +67 -7
  8. data/vendor/lib/functions/index.styl +16 -0
  9. data/vendor/lib/lexer.js +13 -0
  10. data/vendor/lib/nodes/extend.js +41 -0
  11. data/vendor/lib/nodes/group.js +1 -0
  12. data/vendor/lib/nodes/index.js +1 -0
  13. data/vendor/lib/nodes/page.js +1 -1
  14. data/vendor/lib/parser.js +10 -0
  15. data/vendor/lib/renderer.js +13 -0
  16. data/vendor/lib/stylus.js +1 -1
  17. data/vendor/lib/utils.js +66 -0
  18. data/vendor/lib/visitor/compiler.js +65 -61
  19. data/vendor/lib/visitor/evaluator.js +10 -0
  20. data/vendor/lib/visitor/normalizer.js +226 -0
  21. data/vendor/node_modules/cssom/docs/parse.html +15 -1
  22. data/vendor/node_modules/cssom/docs/parse.html_ +268 -0
  23. data/vendor/node_modules/cssom/lib/CSSStyleDeclaration.js +18 -2
  24. data/vendor/node_modules/cssom/lib/parse.js +0 -18
  25. data/vendor/node_modules/cssom/package.json +1 -1
  26. data/vendor/node_modules/cssom/test/CSSStyleDeclaration.test.js +10 -3
  27. data/vendor/package.json +2 -2
  28. data/vendor/test/cases/bifs.keys.css +4 -0
  29. data/vendor/test/cases/bifs.keys.styl +6 -0
  30. data/vendor/test/cases/bifs.values.css +5 -0
  31. data/vendor/test/cases/bifs.values.styl +6 -0
  32. data/vendor/test/cases/css.extend.css +13 -0
  33. data/vendor/test/cases/css.extend.styl +15 -0
  34. data/vendor/test/cases/extend.complex.css +16 -0
  35. data/vendor/test/cases/extend.complex.styl +15 -0
  36. data/vendor/test/cases/extend.css +22 -0
  37. data/vendor/test/cases/extend.multiple-definitions.css +11 -0
  38. data/vendor/test/cases/extend.multiple-definitions.styl +9 -0
  39. data/vendor/test/cases/extend.styl +22 -0
  40. data/vendor/testing/foo.css +3753 -3
  41. data/vendor/testing/foo.styl +7 -0
  42. data/vendor/testing/index.html +5 -50
  43. data/vendor/testing/test.css +19 -4
  44. data/vendor/testing/test.styl +19 -12
  45. metadata +18 -2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.21.2
1
+ 0.22.0
data/vendor/History.md CHANGED
@@ -1,4 +1,15 @@
1
1
 
2
+ 0.22.0 / 2012-01-04
3
+ ==================
4
+
5
+ * Added `@extend`. Closes #149
6
+ * Added more syntax highlighting to TextMate bundle [paulmillr]
7
+ * Added `keys(pairs)` and `values(pairs)` BIFs
8
+ * Added JavaScript object coercion support
9
+ * Added JavaScript -> Stylus node coercion utilities
10
+ * Fixed `.define()`ing of functions
11
+ * Fixed `stylus(1)` repl for 0.6.x
12
+
2
13
  0.21.2 / 2011-12-22
3
14
  ==================
4
15
 
data/vendor/Readme.md CHANGED
@@ -110,6 +110,10 @@ form input {
110
110
  - [Connect](/LearnBoost/stylus/blob/master/docs/middleware.md)
111
111
  - [Ruby On Rails](https://github.com/lucasmazza/stylus_rails)
112
112
 
113
+ ### CMS Support
114
+
115
+ - [DocPad](https://github.com/bevry/docpad)
116
+
113
117
  ### Screencasts
114
118
 
115
119
  - [Stylus Intro](http://screenr.com/bNY)
data/vendor/bin/stylus CHANGED
@@ -353,7 +353,7 @@ function repl() {
353
353
  , parser = new stylus.Parser('', options)
354
354
  , evaluator = new stylus.Evaluator(parser.parse(), options)
355
355
  , rl = require('readline')
356
- , repl = rl.createInterface(process.stdin, process.stdout, true)
356
+ , repl = rl.createInterface(process.stdin, process.stdout, autocomplete)
357
357
  , global = evaluator.global.scope;
358
358
 
359
359
  // expose BIFs
@@ -364,56 +364,33 @@ function repl() {
364
364
  repl.prompt();
365
365
 
366
366
  // HACK: flat-list auto-complete
367
- repl._tabComplete = function(){
368
- var out = this.output
369
- , line = this.line
367
+ function autocomplete(line){
368
+ var out = process.stdout
370
369
  , keys = Object.keys(global.locals)
371
370
  , len = keys.length
372
371
  , words = line.split(/\s+/)
373
372
  , word = words.pop()
374
373
  , names = []
375
374
  , name
376
- , obj
375
+ , node
377
376
  , key;
378
377
 
379
378
  // find words that match
380
379
  for (var i = 0; i < len; ++i) {
381
380
  key = keys[i];
382
381
  if (0 == key.indexOf(word)) {
383
- names.push(key);
384
- }
385
- }
386
-
387
- // several candidates
388
- if (names.length > 1) {
389
- out.write('\r\n\r\n\033[90m');
390
- names.forEach(function(name){
391
- var node = global.lookup(name);
382
+ node = global.lookup(key);
392
383
  switch (node.nodeName) {
393
384
  case 'function':
394
- out.write(' - ' + node + '\r\n');
385
+ names.push(node.toString());
395
386
  break;
396
387
  default:
397
- out.write(' - ' + name + '\r\n');
388
+ names.push(key);
398
389
  }
399
- });
400
- out.write('\r\n\033[0m');
401
- this._refreshLine();
402
- // single candidate
403
- } else if (names.length) {
404
- name = names.pop();
405
- obj = global.lookup(name);
406
- name = name.replace(word, '');
407
- switch (obj.nodeName) {
408
- case 'function':
409
- this._insertString(name + '()');
410
- this.cursor--;
411
- break;
412
- default:
413
- this._insertString(name);
414
390
  }
415
- this._refreshLine();
416
391
  }
392
+
393
+ return [names, line];
417
394
  };
418
395
 
419
396
  repl.on('line', function(line){
data/vendor/docs/bifs.md CHANGED
@@ -104,6 +104,22 @@ Return the lightness of the given `color`.
104
104
 
105
105
  Aliased as `prepend()`
106
106
 
107
+ ### keys(pairs)
108
+
109
+ Return keys in the given `pairs`:
110
+
111
+ pairs = (one 1) (two 2) (three 3)
112
+ keys(pairs)
113
+ // => one two three
114
+
115
+ ### values(pairs)
116
+
117
+ Return values in the given `pairs`:
118
+
119
+ pairs = (one 1) (two 2) (three 3)
120
+ values(pairs)
121
+ // => 1 2 3
122
+
107
123
  ### typeof(node)
108
124
 
109
125
  Return type of `node` as a string.
data/vendor/docs/js.md CHANGED
@@ -63,6 +63,8 @@ Defer importing of the given `path` until evaluation is performed. The example b
63
63
  .define('number', 15.5)
64
64
  .define('some-bool', true)
65
65
  .define('list', [1,2,3])
66
+ .define('list', [1,2,[3,4,[5,6]]])
67
+ .define('list', { foo: 'bar', bar: 'baz' })
66
68
  .define('families', ['Helvetica Neue', 'Helvetica', 'sans-serif'])
67
69
 
68
70
  ### .define(name, fn)
@@ -71,13 +71,33 @@
71
71
  <key>name</key>
72
72
  <string>entity.other.attribute-name.pseudo-class.stylus</string>
73
73
  </dict>
74
+ <dict>
75
+ <key>match</key>
76
+ <string>\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|eventsource|fieldset|figure|figcaption|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\b</string>
77
+ <key>name</key>
78
+ <string>entity.name.tag.stylus</string>
79
+ </dict>
80
+ <dict>
81
+ <key>captures</key>
82
+ <dict>
83
+ <key>1</key>
84
+ <dict>
85
+ <key>name</key>
86
+ <string>punctuation.definition.constant.stylus</string>
87
+ </dict>
88
+ </dict>
89
+ <key>match</key>
90
+ <string>(#)([0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b</string>
91
+ <key>name</key>
92
+ <string>constant.other.color.rgb-value.stylus</string>
93
+ </dict>
74
94
  <dict>
75
95
  <key>captures</key>
76
96
  <dict>
77
97
  <key>1</key>
78
98
  <dict>
79
99
  <key>name</key>
80
- <string>punctuation.definition.entity.css</string>
100
+ <string>punctuation.definition.entity.stylus</string>
81
101
  </dict>
82
102
  </dict>
83
103
  <key>match</key>
@@ -91,6 +111,12 @@
91
111
  <key>name</key>
92
112
  <string>keyword.control.stylus</string>
93
113
  </dict>
114
+ <dict>
115
+ <key>match</key>
116
+ <string>((?:!|~|\+|-|(?:\*)?\*|\/|%|(?:\.)\.\.|&lt;|&gt;|(?:=|:|\?|\+|-|\*|\/|%|&lt;|&gt;)?=|!=)|(?:in|is(?:nt)?|not)\b)</string>
117
+ <key>name</key>
118
+ <string>keyword.operator.stylus</string>
119
+ </dict>
94
120
  <dict>
95
121
  <key>begin</key>
96
122
  <string>"</string>
@@ -123,12 +149,6 @@
123
149
  <key>name</key>
124
150
  <string>comment.block.js</string>
125
151
  </dict>
126
- <dict>
127
- <key>match</key>
128
- <string>(?:\b(\d+))|(#[a-fA-F0-9]+)</string>
129
- <key>name</key>
130
- <string>constant.numeric.stylus</string>
131
- </dict>
132
152
  <dict>
133
153
  <key>captures</key>
134
154
  <dict>
@@ -157,6 +177,46 @@
157
177
  <key>name</key>
158
178
  <string>meta.function.stylus</string>
159
179
  </dict>
180
+ <dict>
181
+ <key>comment</key>
182
+ <string>http://www.w3.org/TR/CSS21/syndata.html#value-def-color</string>
183
+ <key>match</key>
184
+ <string>\b(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)\b</string>
185
+ <key>name</key>
186
+ <string>support.constant.color.w3c-standard-color-name.stylus</string>
187
+ </dict>
188
+ <dict>
189
+ <key>match</key>
190
+ <string>(\b(?i:arial|century|comic|courier|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace)\b)</string>
191
+ <key>name</key>
192
+ <string>support.constant.font-name.stylus</string>
193
+ </dict>
194
+ <dict>
195
+ <key>captures</key>
196
+ <dict>
197
+ <key>1</key>
198
+ <dict>
199
+ <key>name</key>
200
+ <string>keyword.other.unit.stylus</string>
201
+ </dict>
202
+ </dict>
203
+ <key>match</key>
204
+ <string>(?x)
205
+ (?:-|\+)?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+))
206
+ ((?:px|pt|ch|cm|mm|in|r?em|ex|pc|deg|g?rad|dpi|dpcm|s)\b|%)?</string>
207
+ <key>name</key>
208
+ <string>constant.numeric.stylus</string>
209
+ </dict>
210
+ <dict>
211
+ <key>match</key> <string>\b(azimuth|background-attachment|background-color|background-image|background-position|background-repeat|background|box-shadow|border-radius|border-bottom-color|border-bottom-style|border-bottom-width|border-bottom|border-collapse|border-color|border-left-color|border-left-style|border-left-width|border-left|border-right-color|border-right-style|border-right-width|border-right|border-spacing|border-style|border-top-color|border-top-style|border-top-width|border-top|border-width|border|bottom|caption-side|clear|clip|color|content|counter-increment|counter-reset|cue-after|cue-before|cue|cursor|direction|display|elevation|empty-cells|float|font-family|font-size-adjust|font-size|font-stretch|font-style|font-variant|font-weight|font|height|image-rendering|left|letter-spacing|line-height|list-style-image|list-style-position|list-style-type|list-style|margin-bottom|margin-left|margin-right|margin-top|marker-offset|margin|marks|max-height|max-width|min-height|min-width|-moz-border-radius|opacity|orphans|outline-color|outline-style|outline-width|outline|overflow(-[xy])?|padding-bottom|padding-left|padding-right|padding-top|padding|page-break-after|page-break-before|page-break-inside|page|pause-after|pause-before|pause|pitch-range|pitch|play-during|pointer-events|position|quotes|resize|richness|right|size|speak-header|speak-numeral|speak-punctuation|speech-rate|speak|src|stress|table-layout|text-(align|decoration|indent|rendering|shadow|transform)|top|transition|transform|unicode-bidi|vertical-align|visibility|voice-family|volume|white-space|widows|width|word-(spacing|wrap)|zoom|z-index)\b</string>
212
+ <key>name</key>
213
+ <string>support.type.property-name.stylus</string>
214
+ </dict>
215
+ <dict>
216
+ <key>match</key> <string>\b(absolute|all(-scroll)?|always|armenian|auto|avoid|baseline|below|bidi-override|block|bold|bolder|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|col-resize|collapse|crosshair|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|fixed|geometricPrecision|georgian|groove|hand|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|inactive|inherit|inline-block|inline|inset|inside|inter-ideograph|inter-word|italic|justify|katakana-iroha|katakana|keep-all|left|lighter|line-edge|line-through|line|list-item|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|medium|middle|move|n-resize|ne-resize|newspaper|no-drop|no-repeat|nw-resize|none|normal|not-allowed|nowrap|oblique|optimize(Legibility|Quality|Speed)|outset|outside|overline|pointer|pre(-(wrap|line))?|progress|relative|repeat-x|repeat-y|repeat|right|ridge|row-resize|rtl|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|sub|super|sw-resize|table-footer-group|table-header-group|tb-rl|text-bottom|text-top|text|thick|thin|top|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|vertical(-(ideographic|text))?|visible(Painted|Fill|Stroke)?|w-resize|wait|whitespace|zero|smaller|larger|((xx?-)?(small|large))|painted|fill|stroke)\b</string>
217
+ <key>name</key>
218
+ <string>support.constant.property-value.stylus</string>
219
+ </dict>
160
220
  </array>
161
221
  <key>scopeName</key>
162
222
  <string>source.stylus</string>
@@ -128,6 +128,22 @@ spin(color, amount)
128
128
  last(expr)
129
129
  expr[length(expr) - 1]
130
130
 
131
+ // return keys in the given pairs
132
+
133
+ keys(pairs)
134
+ ret = ()
135
+ for pair in pairs
136
+ push(ret, pair[0]);
137
+ ret
138
+
139
+ // return values in the given pairs
140
+
141
+ values(pairs)
142
+ ret = ()
143
+ for pair in pairs
144
+ push(ret, pair[1]);
145
+ ret
146
+
131
147
  // join values with the given delimiter
132
148
 
133
149
  join(delim, vals...)
data/vendor/lib/lexer.js CHANGED
@@ -150,6 +150,7 @@ Lexer.prototype = {
150
150
  || this.urlchars()
151
151
  || this.atrule()
152
152
  || this.scope()
153
+ || this.extends()
153
154
  || this.media()
154
155
  || this.comment()
155
156
  || this.newline()
@@ -424,6 +425,18 @@ Lexer.prototype = {
424
425
  }
425
426
  },
426
427
 
428
+ /**
429
+ * '@extends' ([^{\n]+)
430
+ */
431
+
432
+ extends: function() {
433
+ var captures;
434
+ if (captures = /^@extends?[ \t]*([^\/{\n;]+)/.exec(this.str)) {
435
+ this.skip(captures);
436
+ return new Token('extend', captures[1].trim());
437
+ }
438
+ },
439
+
427
440
  /**
428
441
  * '@media' ([^{\n]+)
429
442
  */
@@ -0,0 +1,41 @@
1
+
2
+ /*!
3
+ * Stylus - Extend
4
+ * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
5
+ * MIT Licensed
6
+ */
7
+
8
+ /**
9
+ * Module dependencies.
10
+ */
11
+
12
+ var Node = require('./node');
13
+
14
+ /**
15
+ * Initialize a new `Extend` with the given `selector`.
16
+ *
17
+ * @param {Selector} selector
18
+ * @api public
19
+ */
20
+
21
+ var Extend = module.exports = function Extend(selector){
22
+ Node.call(this);
23
+ this.selector = selector;
24
+ };
25
+
26
+ /**
27
+ * Inherit from `Node.prototype`.
28
+ */
29
+
30
+ Extend.prototype.__proto__ = Node.prototype;
31
+
32
+ /**
33
+ * Return `@extend selector`.
34
+ *
35
+ * @return {String}
36
+ * @api public
37
+ */
38
+
39
+ Extend.prototype.toString = function(){
40
+ return '@extend ' + this.selector;
41
+ };
@@ -20,6 +20,7 @@ var Node = require('./node');
20
20
  var Group = module.exports = function Group(){
21
21
  Node.call(this);
22
22
  this.nodes = [];
23
+ this.extends = [];
23
24
  };
24
25
 
25
26
  /**
@@ -37,6 +37,7 @@ exports.Comment = require('./comment');
37
37
  exports.Keyframes = require('./keyframes');
38
38
  exports.Charset = require('./charset');
39
39
  exports.Import = require('./import');
40
+ exports.Extend = require('./extend');
40
41
  exports.Function = require('./function');
41
42
  exports.Property = require('./property');
42
43
  exports.Selector = require('./selector');
@@ -32,7 +32,7 @@ var Page = module.exports = function Page(selector, block){
32
32
  Page.prototype.__proto__ = Node.prototype;
33
33
 
34
34
  /**
35
- * Return `@oage name`.
35
+ * Return `@page name`.
36
36
  *
37
37
  * @return {String}
38
38
  * @api public
data/vendor/lib/parser.js CHANGED
@@ -515,6 +515,7 @@ Parser.prototype = {
515
515
  case 'literal':
516
516
  case 'charset':
517
517
  case 'import':
518
+ case 'extend':
518
519
  case 'media':
519
520
  case 'page':
520
521
  case 'ident':
@@ -692,6 +693,15 @@ Parser.prototype = {
692
693
  return nodes.null;
693
694
  },
694
695
 
696
+ /**
697
+ * extend
698
+ */
699
+
700
+ extend: function(){
701
+ var val = this.expect('extend').val;
702
+ return new nodes.Extend(val);
703
+ },
704
+
695
705
  /**
696
706
  * media
697
707
  */
@@ -12,6 +12,7 @@
12
12
  var Parser = require('./parser')
13
13
  , Compiler = require('./visitor/compiler')
14
14
  , Evaluator = require('./visitor/evaluator')
15
+ , Normalizer = require('./visitor/normalizer')
15
16
  , utils = require('./utils')
16
17
  , nodes = require('./nodes')
17
18
  , path = require('path')
@@ -47,12 +48,22 @@ Renderer.prototype.render = function(fn){
47
48
  var parser = this.parser = new Parser(this.str, this.options);
48
49
  try {
49
50
  nodes.filename = this.options.filename;
51
+ // parse
50
52
  var ast = parser.parse();
53
+
54
+ // evaluate
51
55
  this.evaluator = new Evaluator(ast, this.options);
52
56
  ast = this.evaluator.evaluate();
57
+
58
+ // normalize
59
+ var normalizer = new Normalizer(ast, this.options);
60
+ ast = normalizer.normalize();
61
+
62
+ // compile
53
63
  var compiler = new Compiler(ast, this.options)
54
64
  , css = compiler.compile()
55
65
  , js = compiler.js;
66
+
56
67
  fn(null, css, js);
57
68
  } catch (err) {
58
69
  var options = {};
@@ -130,6 +141,8 @@ Renderer.prototype.use = function(fn){
130
141
  */
131
142
 
132
143
  Renderer.prototype.define = function(name, fn, raw){
144
+ fn = utils.coerce(fn);
145
+
133
146
  if (fn.nodeName) {
134
147
  this.options.globals[name] = fn;
135
148
  return this;
data/vendor/lib/stylus.js CHANGED
@@ -24,7 +24,7 @@ exports = module.exports = render;
24
24
  * Library version.
25
25
  */
26
26
 
27
- exports.version = '0.21.2';
27
+ exports.version = '0.22.0';
28
28
 
29
29
  /**
30
30
  * Expose nodes.
data/vendor/lib/utils.js CHANGED
@@ -208,6 +208,72 @@ exports.unwrap = function(expr){
208
208
  return exports.unwrap(expr.nodes[0]);
209
209
  };
210
210
 
211
+ /**
212
+ * Coerce JavaScript values to their Stylus equivalents.
213
+ *
214
+ * @param {Mixed} val
215
+ * @return {Node}
216
+ * @api public
217
+ */
218
+
219
+ exports.coerce = function(val){
220
+ switch (typeof val) {
221
+ case 'function':
222
+ return val;
223
+ case 'string':
224
+ return new nodes.String(val);
225
+ case 'boolean':
226
+ return new nodes.Boolean(val);
227
+ case 'number':
228
+ return new nodes.Unit(val);
229
+ default:
230
+ if (null == val) return nodes.null;
231
+ if (Array.isArray(val)) return exports.coerceArray(val);
232
+ if (val.nodeName) return val;
233
+ return exports.coerceObject(val);
234
+ }
235
+ };
236
+
237
+ /**
238
+ * Coerce a javascript `Array` to a Stylus `Expression`.
239
+ *
240
+ * @param {Array} val
241
+ * @return {Expression}
242
+ * @api private
243
+ */
244
+
245
+ exports.coerceArray = function(val){
246
+ var expr = new nodes.Expression;
247
+ val.forEach(function(val){
248
+ expr.push(exports.coerce(val));
249
+ });
250
+ return expr;
251
+ };
252
+
253
+ /**
254
+ * Coerce a javascript object to a Stylus `Expression`.
255
+ *
256
+ * For example `{ foo: 'bar', bar: 'baz' }` would become
257
+ * the expression `(foo 'bar') (bar 'baz')`.
258
+ *
259
+ * @param {Object} obj
260
+ * @return {Expression}
261
+ * @api public
262
+ */
263
+
264
+ exports.coerceObject = function(obj){
265
+ var expr = new nodes.Expression
266
+ , val;
267
+
268
+ for (var key in obj) {
269
+ val = exports.coerce(obj[key]);
270
+ key = new nodes.Ident(key);
271
+ expr.push(exports.coerceArray([key, val]));
272
+ }
273
+
274
+ return expr;
275
+ };
276
+
211
277
  /**
212
278
  * Return param names for `fn`.
213
279
  *
@@ -34,7 +34,7 @@ var Compiler = module.exports = function Compiler(root, options) {
34
34
  this.spaces = options['indent spaces'] || 2;
35
35
  this.indents = 1;
36
36
  Visitor.call(this, root);
37
- this.tree = [];
37
+ this.stack = [];
38
38
  this.js = '';
39
39
  };
40
40
 
@@ -75,18 +75,9 @@ Compiler.prototype.visitRoot = function(block){
75
75
  this.buf = '';
76
76
  for (var i = 0, len = block.nodes.length; i < len; ++i) {
77
77
  var node = block.nodes[i];
78
- switch (node.nodeName) {
79
- case 'null':
80
- case 'expression':
81
- case 'function':
82
- case 'jsliteral':
83
- case 'unit':
84
- continue;
85
- default:
86
- if (this.linenos || this.firebug) this.debugInfo(node);
87
- var ret = this.visit(node);
88
- if (ret) this.buf += ret + '\n';
89
- }
78
+ if (this.linenos || this.firebug) this.debugInfo(node);
79
+ var ret = this.visit(node);
80
+ if (ret) this.buf += ret + '\n';
90
81
  }
91
82
  return this.buf;
92
83
  };
@@ -310,61 +301,19 @@ Compiler.prototype.visitUnit = function(unit){
310
301
  */
311
302
 
312
303
  Compiler.prototype.visitGroup = function(group){
313
- var self = this
314
- , tree = this.tree
315
- , prev = tree[tree.length - 1]
316
- , curr = [];
317
-
318
- // Construct an array of arrays
319
- // representing the selector hierarchy
320
- group.nodes.forEach(function(node){
321
- curr.push(node.parent
322
- ? node
323
- : node.val);
324
- });
304
+ var stack = this.stack;
325
305
 
326
- tree.push(curr);
306
+ stack.push(group.nodes);
327
307
 
328
- // Reverse recurse the
329
- // hierarchy array to build
330
- // up the selector combinations.
331
- // When we reach root, we have our
332
- // selector string built
333
- var selectors = []
334
- , buf = [];
335
- function join(arr, i) {
336
- if (i) {
337
- arr[i].forEach(function(str){
338
- buf.unshift(str);
339
- join(arr, i - 1);
340
- buf.shift();
341
- });
342
- } else {
343
- arr[0].forEach(function(selector){
344
- var str = selector.trim();
345
- if (buf.length) {
346
- for (var i = 0, len = buf.length; i < len; ++i) {
347
- if (~buf[i].indexOf('&')) {
348
- str = buf[i].replace(/&/g, str).trim();
349
- } else {
350
- str += ' ' + buf[i].trim();
351
- }
352
- }
353
- }
354
- selectors.push(self.indent + str.trimRight());
355
- });
356
- }
357
- }
358
-
359
- // Join selectors
308
+ // selectors
360
309
  if (group.block.hasProperties) {
361
- join(tree, tree.length - 1);
310
+ var selectors = this.compileSelectors(stack);
362
311
  this.buf += (this.selector = selectors.join(this.compress ? ',' : ',\n'));
363
312
  }
364
313
 
365
- // Output blocks
314
+ // output block
366
315
  this.visit(group.block);
367
- tree.pop();
316
+ stack.pop();
368
317
  };
369
318
 
370
319
  /**
@@ -450,6 +399,61 @@ Compiler.prototype.visitProperty = function(prop){
450
399
  : ';');
451
400
  };
452
401
 
402
+ /**
403
+ * Compile selector strings in `arr` from the bottom-up
404
+ * to produce the selector combinations. For example
405
+ * the following Stylus:
406
+ *
407
+ * ul
408
+ * li
409
+ * p
410
+ * a
411
+ * color: red
412
+ *
413
+ * Would return:
414
+ *
415
+ * [ 'ul li a', 'ul p a' ]
416
+ *
417
+ * @param {Array} arr
418
+ * @return {Array}
419
+ * @api private
420
+ */
421
+
422
+ Compiler.prototype.compileSelectors = function(arr){
423
+ var stack = this.stack
424
+ , self = this
425
+ , selectors = []
426
+ , buf = [];
427
+
428
+ function compile(arr, i) {
429
+ if (i) {
430
+ arr[i].forEach(function(selector){
431
+ buf.unshift(selector.val);
432
+ compile(arr, i - 1);
433
+ buf.shift();
434
+ });
435
+ } else {
436
+ arr[0].forEach(function(selector){
437
+ var str = selector.val.trim();
438
+ if (buf.length) {
439
+ for (var i = 0, len = buf.length; i < len; ++i) {
440
+ if (~buf[i].indexOf('&')) {
441
+ str = buf[i].replace(/&/g, str).trim();
442
+ } else {
443
+ str += ' ' + buf[i].trim();
444
+ }
445
+ }
446
+ }
447
+ selectors.push(self.indent + str.trimRight());
448
+ });
449
+ }
450
+ }
451
+
452
+ compile(arr, arr.length - 1);
453
+
454
+ return selectors;
455
+ };
456
+
453
457
  /**
454
458
  * Debug info.
455
459
  */