css.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*!
  2. * Stylus - CSS to Stylus conversion
  3. * Copyright (c) Automattic <developer.wordpress.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Convert the given `css` to Stylus source.
  8. *
  9. * @param {String} css
  10. * @return {String}
  11. * @api public
  12. */
  13. module.exports = function(css){
  14. return new Converter(css).stylus();
  15. };
  16. /**
  17. * Initialize a new `Converter` with the given `css`.
  18. *
  19. * @param {String} css
  20. * @api private
  21. */
  22. function Converter(css) {
  23. var parse = require('css-parse');
  24. this.css = css;
  25. this.root = parse(css, { position: false });
  26. this.indents = 0;
  27. }
  28. /**
  29. * Convert to Stylus.
  30. *
  31. * @return {String}
  32. * @api private
  33. */
  34. Converter.prototype.stylus = function(){
  35. return this.visitRules(this.root.stylesheet.rules);
  36. };
  37. /**
  38. * Return indent string.
  39. *
  40. * @return {String}
  41. * @api private
  42. */
  43. Converter.prototype.__defineGetter__('indent', function(){
  44. return Array(this.indents + 1).join(' ');
  45. });
  46. /**
  47. * Visit `node`.
  48. *
  49. * @param {*} node
  50. * @return {String}
  51. * @api private
  52. */
  53. Converter.prototype.visit = function(node){
  54. switch (node.type) {
  55. case 'rule':
  56. case 'comment':
  57. case 'charset':
  58. case 'namespace':
  59. case 'media':
  60. case 'import':
  61. case 'document':
  62. case 'keyframes':
  63. case 'page':
  64. case 'host':
  65. case 'supports':
  66. var name = node.type[0].toUpperCase() + node.type.slice(1);
  67. return this['visit' + name](node);
  68. }
  69. };
  70. /**
  71. * Visit the rules on `node`.
  72. *
  73. * @param {Array} node
  74. * @return {String}
  75. * @api private
  76. */
  77. Converter.prototype.visitRules = function(node){
  78. var buf = '';
  79. for (var i = 0, len = node.length; i < len; ++i) {
  80. buf += this.visit(node[i]);
  81. }
  82. return buf;
  83. };
  84. /**
  85. * Visit Media `node`.
  86. *
  87. * @param {Media} node
  88. * @return {String}
  89. * @api private
  90. */
  91. Converter.prototype.visitMedia = function(node){
  92. var buf = this.indent + '@media ' + node.media;
  93. buf += '\n';
  94. ++this.indents;
  95. buf += this.visitRules(node.rules);
  96. --this.indents;
  97. return buf;
  98. };
  99. /**
  100. * Visit Declaration `node`.
  101. *
  102. * @param {Declaration} node
  103. * @return {String}
  104. * @api private
  105. */
  106. Converter.prototype.visitDeclaration = function(node){
  107. if ('comment' == node.type) {
  108. return this.visitComment(node);
  109. } else {
  110. var buf = this.indent + node.property + ': ' + node.value + '\n';
  111. return buf;
  112. }
  113. };
  114. /**
  115. * Visit Rule `node`.`
  116. *
  117. * @param {Rule} node
  118. * @return {String}
  119. * @api private
  120. */
  121. Converter.prototype.visitRule = function(node){
  122. var buf = this.indent + node.selectors.join(',\n' + this.indent) + '\n';
  123. ++this.indents;
  124. for (var i = 0, len = node.declarations.length; i < len; ++i) {
  125. buf += this.visitDeclaration(node.declarations[i]);
  126. }
  127. --this.indents;
  128. return buf + '\n';
  129. };
  130. /**
  131. * Visit Comment `node`.`
  132. *
  133. * @param {Comment} node
  134. * @return {String}
  135. * @api private
  136. */
  137. Converter.prototype.visitComment = function(node){
  138. var buf = this.indent + '/*' + node.comment + '*/';
  139. return buf + '\n';
  140. };
  141. /**
  142. * Visit Charset `node`.`
  143. *
  144. * @param {Charset} node
  145. * @return {String}
  146. * @api private
  147. */
  148. Converter.prototype.visitCharset = function(node){
  149. var buf = this.indent + '@charset ' + node.charset;
  150. return buf + '\n';
  151. };
  152. /**
  153. * Visit Namespace `node`.`
  154. *
  155. * @param {Namespace} node
  156. * @return {String}
  157. * @api private
  158. */
  159. Converter.prototype.visitNamespace = function(node){
  160. var buf = this.indent + '@namespace ' + node.namespace;
  161. return buf + '\n';
  162. };
  163. /**
  164. * Visit Import `node`.`
  165. *
  166. * @param {Import} node
  167. * @return {String}
  168. * @api private
  169. */
  170. Converter.prototype.visitImport = function(node){
  171. var buf = this.indent + '@import ' + node.import;
  172. return buf + '\n';
  173. };
  174. /**
  175. * Visit Document `node`.`
  176. *
  177. * @param {Document} node
  178. * @return {String}
  179. * @api private
  180. */
  181. Converter.prototype.visitDocument = function(node){
  182. var buf = this.indent + '@' + node.vendor + 'document ' + node.document;
  183. buf += '\n';
  184. ++this.indents;
  185. buf += this.visitRules(node.rules);
  186. --this.indents;
  187. return buf;
  188. };
  189. /**
  190. * Visit Keyframes `node`.`
  191. *
  192. * @param {Keyframes} node
  193. * @return {String}
  194. * @api private
  195. */
  196. Converter.prototype.visitKeyframes = function(node){
  197. var buf = this.indent + '@keyframes ' + node.name;
  198. buf += '\n';
  199. ++this.indents;
  200. for (var i = 0, len = node.keyframes.length; i < len; ++i) {
  201. buf += this.visitKeyframe(node.keyframes[i]);
  202. }
  203. --this.indents;
  204. return buf;
  205. };
  206. /**
  207. * Visit Keyframe `node`.`
  208. *
  209. * @param {Keyframe} node
  210. * @return {String}
  211. * @api private
  212. */
  213. Converter.prototype.visitKeyframe = function(node){
  214. var buf = this.indent + node.values.join(', ');
  215. buf += '\n';
  216. ++this.indents;
  217. for (var i = 0, len = node.declarations.length; i < len; ++i) {
  218. buf += this.visitDeclaration(node.declarations[i]);
  219. }
  220. --this.indents;
  221. return buf;
  222. };
  223. /**
  224. * Visit Page `node`.`
  225. *
  226. * @param {Page} node
  227. * @return {String}
  228. * @api private
  229. */
  230. Converter.prototype.visitPage = function(node){
  231. var buf = this.indent + '@page' + (node.selectors.length ? ' ' + node.selectors.join(', ') : '');
  232. buf += '\n';
  233. ++this.indents;
  234. for (var i = 0, len = node.declarations.length; i < len; ++i) {
  235. buf += this.visitDeclaration(node.declarations[i]);
  236. }
  237. --this.indents;
  238. return buf;
  239. };
  240. /**
  241. * Visit Supports `node`.`
  242. *
  243. * @param {Supports} node
  244. * @return {String}
  245. * @api private
  246. */
  247. Converter.prototype.visitSupports = function(node){
  248. var buf = this.indent + '@supports ' + node.supports;
  249. buf += '\n';
  250. ++this.indents;
  251. buf += this.visitRules(node.rules);
  252. --this.indents;
  253. return buf;
  254. };
  255. /**
  256. * Visit Host `node`.`
  257. *
  258. * @param {Host} node
  259. * @return {String}
  260. * @api private
  261. */
  262. Converter.prototype.visitHost = function(node){
  263. var buf = this.indent + '@host';
  264. buf += '\n';
  265. ++this.indents;
  266. buf += this.visitRules(node.rules);
  267. --this.indents;
  268. return buf;
  269. };