Permalink
Browse files

Initial commit

Signed-off-by: Chris Aniszczyk <zx@twitter.com>
  • Loading branch information...
0 parents commit 9dddaef144ded7d4bd3c47f9fcd719beed8bff02 @fat-bot fat-bot committed with caniszczyk Apr 17, 2012
Showing with 23,218 additions and 0 deletions.
  1. +176 −0 LICENSE
  2. +116 −0 README.md
  3. +3,979 −0 benchmark/benchmark.less
  4. +47 −0 benchmark/index.js
  5. +50 −0 bin/recess
  6. +98 −0 lib/compile/prefix-whitespace.js
  7. +47 −0 lib/compile/strict-property-order.js
  8. +42 −0 lib/compile/zero-units.js
  9. +272 −0 lib/core.js
  10. +93 −0 lib/index.js
  11. +61 −0 lib/lint/no-IDs.js
  12. +62 −0 lib/lint/no-JS-prefix.js
  13. +59 −0 lib/lint/no-overqualifying.js
  14. +62 −0 lib/lint/no-underscores.js
  15. +62 −0 lib/lint/no-universal-selectors.js
  16. +269 −0 lib/lint/strict-property-order.js
  17. +71 −0 lib/lint/zero-units.js
  18. +47 −0 lib/util.js
  19. +14 −0 makefile
  20. +1 −0 node_modules/.bin/lessc
  21. +1 −0 node_modules/.bin/nopt
  22. +22 −0 node_modules/colors/MIT-LICENSE.txt
  23. +77 −0 node_modules/colors/ReadMe.md
  24. +269 −0 node_modules/colors/colors.js
  25. +74 −0 node_modules/colors/example.html
  26. +65 −0 node_modules/colors/example.js
  27. +28 −0 node_modules/colors/package.json
  28. +65 −0 node_modules/colors/test.js
  29. +2 −0 node_modules/less/.npmignore
  30. +26 −0 node_modules/less/CHANGELOG
  31. +179 −0 node_modules/less/LICENSE
  32. +75 −0 node_modules/less/Makefile
  33. +20 −0 node_modules/less/README.md
  34. +47 −0 node_modules/less/benchmark/less-benchmark.js
  35. +139 −0 node_modules/less/bin/lessc
  36. +2,695 −0 node_modules/less/dist/less-1.1.0.js
  37. +16 −0 node_modules/less/dist/less-1.1.0.min.js
  38. +2,710 −0 node_modules/less/dist/less-1.1.1.js
  39. +16 −0 node_modules/less/dist/less-1.1.1.min.js
  40. +2,712 −0 node_modules/less/dist/less-1.1.2.js
  41. +16 −0 node_modules/less/dist/less-1.1.2.min.js
  42. +2,721 −0 node_modules/less/dist/less-1.1.3.js
  43. +16 −0 node_modules/less/dist/less-1.1.3.min.js
  44. +2,769 −0 node_modules/less/dist/less-1.1.4.js
  45. +16 −0 node_modules/less/dist/less-1.1.4.min.js
  46. +2,805 −0 node_modules/less/dist/less-1.1.5.js
  47. +9 −0 node_modules/less/dist/less-1.1.5.min.js
Sorry, we could not display the entire diff because it was too big.
176 LICENSE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
116 README.md
@@ -0,0 +1,116 @@
+RECESS [![Build Status](https://secure.travis-ci.org/twitter/recess.png)](http://travis-ci.org/twitter/recess)
+======
+
+Developed at Twitter to support our internal styleguide, RECESS is a simple, attractive code quality tool for CSS built on top of LESS.
+
+Incorporate it into your development process as a linter, or integrate it directly into your build system as a compiler, RECESS will keep your source looking clean and super managable.
+
+
+GENERAL USE
+-----------
+
+```CLI
+$ recess [path] [options]
+```
+
+OPTIONS
+-------
+
+- --compile - compiles your code and outputs it to the terminal. Fixes white space and sort order. Can compile css or less.
+- --compress - compress your compiled code.
+- --config - accepts a path, which specifies a json config object
+- --noIDs - doesn't complain about using IDs in your stylesheets
+- --noJSPrefix - doesn't complain about styling `.js-` prefixed classnames
+- --noOverqualifying - doesn't complain about overqualified selectors (ie: `div#foo.bar`)
+- --noUnderscores - doesn't complain about using semicolons in your class names
+- --noUniversalSelectors - doesn't complain about using the universal `*` selector
+- --strictPropertyOrder - doesn't looking into your property ordering
+- --zeroUnits - doesn't complain if you add units to values of 0
+
+
+EXAMPLES
+--------
+
+Lint all css files
+
+```CLI
+$ recess *.css
+```
+
+Lint file, ignore styling of IDs
+
+```CLI
+$ recess ./bootstrap.css --noIds false
+```
+
+Compile and compress .less file, then output it to a new file
+
+```CLI
+$ recess ./bootstrap.less --compress > ./bootstrap-production.css
+```
+
+PROGRAMMATIC API
+----------------
+
+Recess provides a pretty simple programmatic api.
+
+```JS
+var recess = require('recess')
+```
+
+Once you've required recess, just pass it a `path` (or array of paths) and an optional `options` object and an optional `callback`:
+
+```js
+recess(['../fat.css', '../twitter.css'], { compile: true }, callback)
+```
+
+The following options (and defaults) are available in the programatic api:
+
+- compile: false
+- compress: false
+- noIDs: true
+- noJSPrefix: true
+- noOverqualifying: true
+- noUnderscores: true
+- noUniversalSelectors: true
+- prefixWhitespace: true
+- strictPropertyOrder: true
+- zeroUnits: true
+
+The callback is fired when each instance has finished processessing an input. The callback is passed an array of of instances (one for each path). The instances have a bunch of useful things on them like the raw data and an array of output strings.
+
+When compiling, access the compiled source through the output property:
+
+```js
+var recess = require('recess')
+
+recess('./js/fat.css', { compile: true }, function (err, obj) {
+ if (err) throw err
+ console.log(
+ obj // recess instance for fat.css
+ , obj.output // array of loggable content
+ , obj.errors // array of failed lint rules
+ )
+})
+```
+
+INSTALLATION
+------------
+
+To install recess you need both node and npm installed.
+
+```CLI
+$ npm install recess -g
+```
+
+AUTHORS
+------------
+
++ **Jacob Thornton**: https://twitter.com/fat
+
+LICENSE
+------------
+
+Copyright 2012 Twitter, Inc.
+
+Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
3,979 benchmark/benchmark.less
3,979 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
47 benchmark/index.js
@@ -0,0 +1,47 @@
+#!/usr/bin/env node
+var path = require('path')
+ , fs = require('fs')
+ , less = require('less')
+ , recess = require('../lib')
+ , file = path.join(__dirname, 'benchmark.less')
+
+fs.readFile(file, 'utf8', function (e, data) {
+ var css, start, end
+
+ new(less.Parser)({ optimization: 2 }).parse(data, function (err, tree) {
+
+ start = new Date()
+ css = tree.toCSS()
+ end = new Date()
+
+ console.log(
+ " LESS toCSS: "
+ + (end - start)
+ + " ms ("
+ + parseInt(1000 / (end - start) * data.length / 1024)
+ + " KB\/s)"
+ )
+
+ new(less.Parser)({ optimization: 2 }).parse(css, function (err, tree) {
+
+ var play = new recess.Constructor(false)
+
+ play.data = css
+ play.definitions = tree.rules
+ play.path = 'less'
+ play.callback = function () {
+ end = new Date()
+ console.log(
+ "RECESS toCSS: "
+ + (end - start)
+ + " ms ("
+ + parseInt(1000 / (end - start) * data.length / 1024)
+ + " KB\/s)"
+ )
+ }
+ start = new Date()
+ play.compile()
+ })
+
+ })
+})
50 bin/recess
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+var RECESS = require('../lib')
+ , nopt = require('nopt')
+ , path = require('path')
+ , fs = require('fs')
+ , config = '.recessrc'
+ , options
+ , paths
+
+// exit with docs
+if (process.argv.length == 2) return RECESS.docs()
+
+// define expected options
+options = {
+ compile: Boolean
+, compress: Boolean
+, config: path
+, noIDs: Boolean
+, noJSPrefix: Boolean
+, noOverqualifying: Boolean
+, noUnderscores: Boolean
+, noUniversalSelectors: Boolean
+, prefixWhitespace: Boolean
+, strictPropertyOrder: Boolean
+, zeroUnits: Boolean
+}
+
+// parse options
+options = nopt(options, {}, process.argv)
+
+// if help exit
+if (options.help) return RECESS.docs()
+
+// set path from remaining arguments
+paths = options.argv.remain
+
+// clean options object
+delete options.argv
+
+// check for config or default .recessrc
+if (options.config || path.existsSync(config)) {
+ config = JSON.parse(fs.readFileSync(options.config || config))
+ for (i in options) config[i] = options[i]
+ options = config
+}
+
+// set CLI to true
+options.cli = true
+
+RECESS(paths, options)
98 lib/compile/prefix-whitespace.js
@@ -0,0 +1,98 @@
+// ==========================================
+// RECESS
+// COMPILE: whitespace for vendor prefixes
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var less = require('less')
+ , toCSS
+
+ // vendor prfixes
+ , vendorPrefixes = [
+ '-webkit-'
+ , '-khtml-'
+ , '-epub-'
+ , '-moz-'
+ , '-ms-'
+ , '-o-'
+ ]
+ , VENDOR_PREFIX = new RegExp('^(\\s*(?:' + vendorPrefixes.join('|').replace(/[-[\]{}()*+?.,\\^$#\s]/g, "\\$&") + '))')
+
+
+// space defintion
+function space(rules, i) {
+ var rule = rules[i]
+ , j = i - 1
+ , peek = rules[j]
+ , result = ''
+ , ruleRoot
+ , peekRoot
+ , ruleVal
+ , peekVal
+
+ // skip if not peak, rule, or rule.name
+ if (!peek || !rule || !rule.name) return
+
+ // if previous rule is not a css property, try searching up tree for nearest rule
+ while (!peek.name) {
+ peek = rules[j--]
+
+ // if none, then exit
+ if (!peek) return
+ }
+
+ // check to see if name has a vnedor prefix
+ if (VENDOR_PREFIX.test(peek.name)) {
+
+ // strip vendor prefix from rule and prior rule
+ ruleRoot = rule.name.replace(VENDOR_PREFIX, '')
+ peekRoot = peek.name.replace(VENDOR_PREFIX, '')
+
+
+ // if they share the same root calculte the offset in spacing
+ if (ruleRoot === peekRoot) {
+
+ // calculate the rules val
+ ruleVal = rule.name.match(VENDOR_PREFIX)
+ ruleVal = (ruleVal && ruleVal[0].length) || 0
+
+ // calculate the peeks val
+ peekVal = peek.name.match(VENDOR_PREFIX)
+ peekVal = (peekVal && peekVal[0].length) || 0
+
+ // if peek has a value, offset the rule val
+ if (peekVal) {
+ ruleVal = peekVal - ruleVal
+ while (ruleVal--) result += ' '
+ }
+
+ }
+ }
+
+ // prefix the rule with the white space offset
+ rule.name = result + rule.name
+}
+
+function compile (context, env) {
+ // iterate over rules and space each property
+ for (var i = 0; i < this.rules.length; i++) {
+ space(this.rules, i)
+ }
+
+ // apply to base CSS
+ return toCSS.apply(this, arguments)
+}
+
+module.exports.on = function () {
+ toCSS = less.tree.Ruleset.prototype.toCSS
+ less.tree.Ruleset.prototype.toCSS = compile
+}
+
+module.exports.off = function () {
+ less.tree.Ruleset.prototype.toCSS = toCSS
+}
47 lib/compile/strict-property-order.js
@@ -0,0 +1,47 @@
+// ==========================================
+// RECESS
+// COMPILE: automatically sort properties
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var less = require('less')
+ , order = require('../lint/strict-property-order')
+ , toCSS
+
+
+function compile (context, env) {
+ var l
+
+ // test property order
+ order(this, env.data)
+
+ // search errors for sortedRules property
+ if (this.errors) {
+ for (l = this.errors.length; l--;) {
+
+ // if sorted rule found apply it, then exit
+ if (this.errors[l].sortedRules) {
+ this.rules = this.errors[l].sortedRules
+ break
+ }
+
+ }
+ }
+
+ // apply old toCSS method to updated object
+ return toCSS.apply(this, arguments)
+}
+
+module.exports.on = function () {
+ toCSS = less.tree.Ruleset.prototype.toCSS
+ less.tree.Ruleset.prototype.toCSS = compile
+}
+
+module.exports.off = function () {
+ less.tree.Ruleset.prototype.toCSS = toCSS
+}
42 lib/compile/zero-units.js
@@ -0,0 +1,42 @@
+// ==========================================
+// RECESS
+// COMPILE: remove units from 0 values
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var less = require('less')
+ , toCSS
+ , units = [
+ '%'
+ , 'in'
+ , 'cm'
+ , 'mm'
+ , 'em'
+ , 'ex'
+ , 'pt'
+ , 'pc'
+ , 'px'
+ ]
+ , UNITS = new RegExp('\\b0\\s?(' + units.join('|') + ')')
+
+function compile () {
+ // strip units from 0 values
+ var props = toCSS.apply(this, arguments)
+
+ // don't strip chars from hex codes
+ return /#/.test(props) ? props : props.replace(UNITS, '0')
+}
+
+module.exports.on = function () {
+ toCSS = less.tree.Value.prototype.toCSS
+ less.tree.Value.prototype.toCSS = compile
+}
+
+module.exports.off = function () {
+ less.tree.Value.prototype.toCSS = toCSS
+}
272 lib/core.js
@@ -0,0 +1,272 @@
+// ==========================================
+// RECESS
+// CORE: The core class definition
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var _ = require('underscore')
+ , colors = require('colors')
+ , less = require('less')
+ , util = require('./util')
+ , path = require('path')
+ , fs = require('fs')
+
+// core class defintion
+function RECESS(path, options, callback) {
+ this.path = path
+ this.output = []
+ this.errors = []
+ this.options = _.extend({}, RECESS.DEFAULTS, options)
+ path && this.read()
+ this.callback = callback
+}
+
+// instance methods
+RECESS.prototype = {
+
+ constructor: RECESS
+
+, log: function (str, force) {
+
+ // if compiling only write with force flag
+ if (!this.options.compile || force) {
+ this.options.cli ? console.log(str) : this.output.push(str)
+ }
+
+ }
+
+, read: function () {
+ var that = this
+
+ // try to read data from path
+ fs.readFile(this.path, 'utf8', function (err, data) {
+
+ // if err, exit with could not read message
+ if (err) {
+ that.errors.push(err)
+ that.log('Error reading file: '.red + that.path.grey + '\n', true)
+ return that.callback && that.callback()
+ }
+
+ // set instance data
+ that.data = data
+
+ // parse data
+ that.parse()
+
+ })
+ }
+
+, parse: function () {
+ var that = this
+ , options = {
+ paths: [path.dirname(this.path)]
+ , optimization: 0
+ , filename: this.path && this.path.replace(/.*(?=\/)\//, '')
+ }
+
+ // try to parse with less parser
+ try {
+
+ // instantiate new parser with options
+ new less.Parser(options)
+
+ // parse data into tree
+ .parse(this.data, function (err, tree) {
+
+ if (err) {
+ // push to errors array
+ that.errors.push(err)
+
+ // less gave up trying to parse the data ;_;
+ that.log(err.name.red + ": " + err.message + ' of ' + err.filename.yellow + '\n')
+
+ // if extract - then log it
+ err.extract && err.extract.forEach(function (line, index) {
+ that.log(util.padLine(err.line + index) + line)
+ })
+
+ // add extra line for readability after error log
+ that.log(" ")
+
+ // exit with callback if present
+ return that.callback && that.callback()
+ }
+
+ // test to see if file has a less extension
+ if (/less$/.test(that.path) && !that.parsed) {
+
+ // if it's a LESS file, we flatten it
+ that.data = tree.toCSS({})
+
+ // set parse to true so as to not infinitely reparse less files
+ that.parsed = true
+
+ // reparse less file
+ return that.parse()
+ }
+
+ // set definitions to parse tree
+ that.definitions = tree.rules
+
+ // validation defintions
+ that.options.compile ? that.compile() : that.validate()
+ })
+
+ } catch (err) {
+
+ // less exploded trying to parse the file (╯°□°)╯︵ ┻━┻
+ // push to errors array
+ that.errors.push(err)
+
+ // log a message trying to explain why
+ that.log(
+ "Parse errror".red
+ + ": "
+ + err.message
+ + " on line "
+ + util.getLine(err.index, this.data)
+ )
+
+ // exit with callback if present
+ this.callback && this.callback()
+ }
+ }
+
+, compile: function () {
+ var that = this
+ , key
+ , css
+
+ // activate all relevant compilers
+ Object.keys(this.options).forEach(function (key) {
+ that.options[key]
+ && RECESS.COMPILERS[key]
+ && RECESS.COMPILERS[key].on()
+ })
+
+ // iterate over defintions and compress them (join with new lines)
+ css = this.definitions.map(function (def) {
+ return def.toCSS([[]], { data: that.data, compress: that.options.compress })
+ }).join(this.options.compress ? '' : '\n')
+
+ // deactivate all relevant compilers
+ Object.keys(this.options).reverse().forEach(function (key) {
+ that.options[key]
+ && RECESS.COMPILERS[key]
+ && RECESS.COMPILERS[key].off()
+ })
+
+ // cleanup trailing newlines
+ css = css.replace(/[\n\s\r]*$/, '')
+
+ // output css
+ this.log(css, true)
+
+ // callback and exit
+ this.callback && this.callback()
+ }
+
+, validate: function () {
+ var failed
+ , key
+
+ // iterate over instance options
+ for (key in this.options) {
+
+ // if option has a validation, then we test it
+ this.options[key]
+ && RECESS.RULES[key]
+ && !this.test(RECESS.RULES[key])
+ && (failed = true)
+
+ }
+
+ // exit with failed flag to validateStatus
+ this.validateStatus(failed)
+ }
+
+, test: function (validation) {
+ var l = this.definitions.length
+ , i = 0
+ , isValid = true
+ , rule
+ , def
+ , j
+ , k
+
+ // test each definition against a given validation
+ for (; i < l; i++) {
+ def = this.definitions[i]
+ if (!validation(def, this.data)) isValid = false
+ }
+
+ // return valid state
+ return isValid
+ }
+
+, validateStatus: function (failed) {
+ var that = this
+ , fails
+
+ if (failed) {
+
+ // count errors
+ fails = util.countErrors(this.definitions)
+
+ // log file overview
+ this.log('FILE: ' + this.path.cyan)
+ this.log('STATUS: ' + 'Busted'.magenta)
+ this.log('FAILURES: ' + (fails + ' failure' + (fails > 1 ? 's' : '')).magenta + '\n')
+
+ // iterate through each definition
+ this.definitions.forEach(function (def) {
+
+ // if there's an error, log the error and optional err.extract
+ def.errors
+ && def.errors.length
+ && def.errors.forEach(function (err) {
+ that.log(err.message)
+ err.extract && that.log(err.extract + '\n')
+ })
+ })
+
+ } else {
+ // it was a success - let the user know!
+ this.log('FILE: ' + this.path.cyan)
+ this.log('STATUS: ' + 'Perfect!\n'.yellow)
+ }
+
+ // callback and exit
+ this.callback && this.callback()
+ }
+
+}
+
+// import validation rules
+RECESS.RULES = {}
+
+fs.readdirSync(path.join(__dirname, 'lint')).forEach(function (name) {
+ var camelName = name
+ .replace(/(\-[a-z])/gi, function ($1) { return $1.toUpperCase().replace('-', '') })
+ .replace(/\.js$/, '')
+ RECESS.RULES[camelName] = require(path.join(__dirname, 'lint', name))
+})
+
+// import compilers
+RECESS.COMPILERS = {}
+
+fs.readdirSync(path.join(__dirname, 'compile')).forEach(function (name) {
+ var camelName = name
+ .replace(/(\-[a-z])/gi, function ($1) { return $1.toUpperCase().replace('-', '') })
+ .replace(/\.js$/, '')
+ RECESS.COMPILERS[camelName] = require(path.join(__dirname, 'compile', name))
+})
+
+// export class
+module.exports = RECESS
93 lib/index.js
@@ -0,0 +1,93 @@
+// ==========================================
+// RECESS
+// INDEX: The root api definition
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+// require core
+var RECESS = require('./core')
+ , colors = require('colors')
+
+// define main export
+module.exports = function (paths, options, callback) {
+
+ var option, i, instances = []
+
+ // if no options default to empty object
+ options = options || {}
+
+ // if options is a function, set to callback and set options to {}
+ if (typeof options == 'function') (callback = options) && (options = {})
+
+ // if single path, convert to array
+ if (typeof paths == 'string') paths = [paths]
+
+ // there were no paths, show the docs
+ if (!paths || !paths.length) return module.exports.docs()
+
+ // if a compress flag is present, we automatically make compile flag true
+ options.compress && (options.compile = true)
+
+ // if not compiling, let user know which files will be linted
+ if (!options.compile && options.cli) {
+ console.log("\nAnalyzing the following files: " + ((paths + '').replace(/,/g, ', ') + '\n').grey)
+ }
+
+ // for each path, create a new RECESS instance
+ function recess(init, path, err) {
+ if (path = paths.pop()) {
+ return instances.push(new RECESS(path, options, recess))
+ }
+
+ // map/filter for errors
+ err = instances
+ .map(function (i) {
+ return i.errors.length && i.errors
+ })
+ .filter(function (i) {
+ return i
+ })
+
+ // if no error, set explicitly to null
+ err = err.length ? err[0] : null
+
+ //callback
+ callback && callback(err, instances.length > 1 ? instances : instances[0])
+ }
+
+ // start processing paths
+ recess(true)
+}
+
+// default options
+module.exports.DEFAULTS = RECESS.DEFAULTS = {
+ compile: false
+, compress: false
+, config: false
+, noIDs: true
+, noJSPrefix: true
+, noOverqualifying: true
+, noUnderscores: true
+, noUniversalSelectors: true
+, prefixWhitespace: true
+, strictPropertyOrder: true
+, zeroUnits: true
+}
+
+
+// expose RAW RECESS class
+module.exports.Constructor = RECESS
+
+// expose docs
+module.exports.docs = function () {
+ console.log("\nGENERAL USE: " + "$".grey + " recess".cyan + " [path] ".yellow + "[options]\n".grey)
+ console.log("OPTIONS:")
+ for (var option in RECESS.DEFAULTS) console.log(' --' + option)
+ console.log("\nEXAMPLE:\n\n" + " $".grey + " recess".cyan + " ./bootstrap.css ".yellow + "--noIDs false\n".grey)
+ console.log('GENERAL HELP: ' + 'http://git.io/recess\n'.yellow)
+}
61 lib/lint/no-IDs.js
@@ -0,0 +1,61 @@
+// ==========================================
+// RECESS
+// RULE: Id's should not be styled
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var util = require('../util')
+ , RULE = {
+ type: 'noIDs'
+ , exp: /^#/
+ , message: 'Id\'s should not be styled'
+ }
+
+// validation method
+module.exports = function (def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no selectors to validate
+ if (!def.selectors) return isValid
+
+ // loop over selectors
+ def.selectors.forEach(function (selector) {
+
+ // loop over selector entities
+ selector.elements.forEach(function (element) {
+
+ var extract
+
+ // continue to next element if no js- prefix
+ if (!RULE.exp.test(element.value)) return
+
+ // calculate line number for the extract
+ extract = util.getLine(element.index - element.value.length, data)
+ extract = util.padLine(extract)
+
+ // highlight invalid styling of ID
+ extract += element.value.replace(RULE.exp, '#'.magenta)
+
+ // set invalid flag to false
+ isValid = false
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message
+ , extract: extract
+ })
+
+ })
+ })
+
+ // return valid state
+ return isValid
+}
62 lib/lint/no-JS-prefix.js
@@ -0,0 +1,62 @@
+// ==========================================
+// RECESS
+// RULE: .js prefixes should not be styled
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var util = require('../util')
+ , RULE = {
+ type: 'noJSPrefix'
+ , exp: /^\.js\-/
+ , message: '.js prefixes should not be styled'
+ }
+
+// validation method
+module.exports = function (def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no selector to validate
+ if (!def.selectors) return isValid
+
+ // loop over selectors
+ def.selectors.forEach(function (selector) {
+
+ // loop over selector entities
+ selector.elements.forEach(function (element) {
+
+ var extract
+
+ // continue to next element if .js- prefix not styled
+ if (!RULE.exp.test(element.value)) return
+
+ // calculate line number for the extract
+ extract = util.getLine(element.index - element.value.length, data)
+ extract = util.padLine(extract)
+
+ // highlight invalid styling of .js- prefix
+ extract += element.value.replace(RULE.exp, '.js-'.magenta)
+
+ // set invalid flag to false
+ isValid = false
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message
+ , extract: extract
+ })
+
+ })
+
+ })
+
+ // return valid state
+ return isValid
+}
59 lib/lint/no-overqualifying.js
@@ -0,0 +1,59 @@
+// ==========================================================
+// RECESS
+// RULE: Underscores should not be used when naming selectors
+// ==========================================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================================
+
+'use strict'
+
+var util = require('../util')
+ , RULE = {
+ type: 'noOverqualifying'
+ , exp: /\b[\w\-\_]+(?=#|\.)/
+ , message: 'Element selectors should not be overqualified'
+ }
+
+// validation method
+module.exports = function (def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no selector to validate
+ if (!def.selectors) return isValid
+
+ // loop over selectors
+ def.selectors.forEach(function (selector) {
+
+ // evaluate selector to string and trim whitespace
+ var selectorString = selector.toCSS().trim()
+ , extract
+
+ // if selector isn't overqualified continue
+ if (!RULE.exp.test(selectorString)) return
+
+ // calculate line number for the extract
+ extract = util.getLine(selector.elements[0].index - selector.elements[0].value.length, data)
+ extract = util.padLine(extract)
+
+ // highlight selector overqualification
+ extract += selectorString.replace(RULE.exp, function ($1) { return $1.magenta })
+
+ // set invalid flag to false
+ isValid = false
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message
+ , extract: extract
+ })
+
+ })
+
+ // return validation state
+ return isValid
+}
62 lib/lint/no-underscores.js
@@ -0,0 +1,62 @@
+// ==========================================================
+// RECESS
+// RULE: Underscores should not be used when naming selectors
+// ==========================================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================================
+
+'use strict'
+
+var util = require('../util')
+ , RULE = {
+ type: 'noUnderscores'
+ , exp: /_/g
+ , message: 'Underscores should not be used when naming selectors'
+ }
+
+// validation method
+module.exports = function validate(def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no selector to validate
+ if (!def.selectors) return isValid
+
+ // loop over selectors
+ def.selectors.forEach(function (selector) {
+
+ // loop over selector entities
+ selector.elements.forEach(function (element) {
+
+ var extract
+
+ // continue to next element if no underscore
+ if (!RULE.exp.test(element.value)) return
+
+ // calculate line number for the extract
+ extract = util.getLine(element.index - element.value.length, data)
+ extract = util.padLine(extract)
+
+ // highlight invalid underscores
+ extract += element.value.replace(RULE.exp, '_'.magenta)
+
+ // set invalid flag to false
+ isValid = false
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message
+ , extract: extract
+ })
+
+ })
+ })
+
+ // return valid state
+ return isValid
+
+}
62 lib/lint/no-universal-selectors.js
@@ -0,0 +1,62 @@
+// ===========================================
+// RECESS
+// RULE: Universal selectors should be avoided
+// ===========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ===========================================
+
+'use strict'
+
+var util = require('../util')
+ , RULE = {
+ type: 'noUniversalSelectors'
+ , exp: /\*/g
+ , message: 'Universal selectors should be avoided'
+ }
+
+// validation method
+module.exports = function (def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no rules to validate
+ if (!def.selectors) return isValid
+
+ // loop over selectors
+ def.selectors.forEach(function (selector) {
+
+ // loop over selector entities
+ selector.elements.forEach(function (element) {
+
+ var extract
+
+ // continue to next element if no underscore
+ if (!RULE.exp.test(element.value)) return
+
+ // calculate line number for the extract
+ extract = util.getLine(element.index - element.value.length, data)
+ extract = util.padLine(extract)
+
+ // highlight the invalid use of a universal selector
+ extract += selector.toCSS({}).replace(RULE.exp, '*'.magenta)
+
+ // set invalid flag to false
+ isValid = false
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message
+ , extract: extract
+ })
+
+ })
+ })
+
+ // return valid state
+ return isValid
+
+}
269 lib/lint/strict-property-order.js
@@ -0,0 +1,269 @@
+// ==========================================
+// RECESS
+// RULE: Must use correct property ordering
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var _ = require('underscore')
+ , util = require('../util')
+ , RULE = {
+ type: 'strictPropertyOrder'
+ , message: 'Incorrect property order for rule'
+ }
+
+ // vendor prefix order
+ , vendorPrefixes = [
+ '-webkit-'
+ , '-khtml-'
+ , '-epub-'
+ , '-moz-'
+ , '-ms-'
+ , '-o-'
+ ]
+
+ // hack prefix order
+ , hackPrefixes = [
+ '_' // ie7
+ , '*' // ie6
+ ]
+
+ // css property order
+ , order = [
+ 'position'
+ , 'top'
+ , 'right'
+ , 'bottom'
+ , 'left'
+ , 'z-index'
+ , 'display'
+ , 'float'
+ , 'width'
+ , 'height'
+ , 'max-width'
+ , 'max-height'
+ , 'min-width'
+ , 'min-height'
+ , 'padding'
+ , 'padding-top'
+ , 'padding-right'
+ , 'padding-bottom'
+ , 'padding-left'
+ , 'margin'
+ , 'margin-top'
+ , 'margin-right'
+ , 'margin-bottom'
+ , 'margin-left'
+ , 'margin-collapse'
+ , 'margin-top-collapse'
+ , 'margin-right-collapse'
+ , 'margin-bottom-collapse'
+ , 'margin-left-collapse'
+ , 'overflow'
+ , 'overflow-x'
+ , 'overflow-y'
+ , 'clip'
+ , 'clear'
+ , 'font'
+ , 'font-family'
+ , 'font-size'
+ , 'font-smoothing'
+ , 'font-style'
+ , 'font-weight'
+ , 'src'
+ , 'line-height'
+ , 'letter-spacing'
+ , 'word-spacing'
+ , 'color'
+ , 'text-align'
+ , 'text-decoration'
+ , 'text-indent'
+ , 'text-overflow'
+ , 'text-rendering'
+ , 'text-size-adjust'
+ , 'text-shadow'
+ , 'text-transform'
+ , 'word-break'
+ , 'word-wrap'
+ , 'white-space'
+ , 'vertical-align'
+ , 'list-style'
+ , 'cursor'
+ , 'background'
+ , 'background-attachment'
+ , 'background-color'
+ , 'background-image'
+ , 'background-position'
+ , 'background-repeat'
+ , 'background-size'
+ , 'border'
+ , 'border-collapse'
+ , 'border-top'
+ , 'border-right'
+ , 'border-bottom'
+ , 'border-left'
+ , 'border-color'
+ , 'border-top-color'
+ , 'border-right-color'
+ , 'border-bottom-color'
+ , 'border-left-color'
+ , 'border-spacing'
+ , 'border-style'
+ , 'border-top-style'
+ , 'border-right-style'
+ , 'border-bottom-style'
+ , 'border-left-style'
+ , 'border-width'
+ , 'border-top-width'
+ , 'border-right-width'
+ , 'border-bottom-width'
+ , 'border-left-width'
+ , 'border-radius'
+ , 'border-top-right-radius'
+ , 'border-bottom-right-radius'
+ , 'border-bottom-left-radius'
+ , 'border-top-left-radius'
+ , 'border-radius-topright'
+ , 'border-radius-bottomright'
+ , 'border-radius-bottomleft'
+ , 'border-radius-topleft'
+ , 'content'
+ , 'quotes'
+ , 'outline'
+ , 'outline-offset'
+ , 'opacity'
+ , 'filter'
+ , 'visibility'
+ , 'size'
+ , 'zoom'
+ , 'box-shadow'
+ , 'animation'
+ , 'animation-delay'
+ , 'animation-duration'
+ , 'animation-iteration-count'
+ , 'animation-name'
+ , 'animation-play-state'
+ , 'animation-timing-function'
+ , 'transition'
+ , 'transition-delay'
+ , 'transition-duration'
+ , 'transition-property'
+ , 'transition-timing-function'
+ , 'background-clip'
+ , 'box-sizing'
+ , 'resize'
+ , 'appearance'
+ , 'user-select'
+ , 'interpolation-mode'
+ , 'direction'
+ , 'marks'
+ , 'page'
+ , 'set-link-source'
+ , 'unicode-bidi'
+ ]
+
+ // regex tests
+ , HACK_PREFIX = new RegExp('^(' + hackPrefixes.join('|').replace(/[-[\]{}()*+?.,\\^$#\s]/g, "\\$&") + ')')
+ , VENDOR_PREFIX = new RegExp('^(' + vendorPrefixes.join('|').replace(/[-[\]{}()*+?.,\\^$#\s]/g, "\\$&") + ')')
+
+
+// validation method
+module.exports = function (def, data) {
+
+ // // default validation to true
+ var isValid = true
+ , dict = {}
+ , index = 0
+ , cleanRules
+ , sortedRules
+ , firstLine
+ , extract
+ , selector
+
+ // return if no rules to validate
+ if (!def.rules) return isValid
+
+ // recurse over nested rulesets
+ def.rules.forEach(function (rule) {
+ if (rule.selectors) module.exports(rule, data)
+ })
+
+ cleanRules = def.rules.map(function (rule) {
+ return rule.name && rule
+ }).filter(function (item) { return item })
+
+ // sort rules
+ sortedRules = _.sortBy(cleanRules, function (rule) {
+
+ // pad value of each rule position to account for vendor prefixes
+ var padding = (vendorPrefixes.length + 1) * 10
+ , root
+ , val
+
+ // strip vendor prefix and hack prefix from rule name to find root
+ root = rule.name
+ .replace(VENDOR_PREFIX, '')
+ .replace(HACK_PREFIX, '')
+
+ // find value of order of the root css property
+ val = order.indexOf(root)
+
+ // if property is not found, exit with property not found error
+ if (!~val) {
+ return util.throwError(def, {
+ type: 'propertyNotFound'
+ , message: 'Unknown property name: "' + rule.name + '"'
+ })
+ }
+
+ // pad value
+ val = (val * padding) + 10
+
+ // adjust value based on prefix
+ val += VENDOR_PREFIX.exec(rule.name) ? vendorPrefixes.indexOf(RegExp.$1) : (vendorPrefixes.length + 1)
+
+ // adjust value based on css hack
+ val += HACK_PREFIX.exec(rule.name) ? (hackPrefixes.indexOf(RegExp.$1)) : 0
+
+ // return sort value
+ return val
+ })
+
+ // check to see if sortedRules has same order as provided rules
+ isValid = _.isEqual(sortedRules, cleanRules)
+
+ // return if sort is correct
+ if (isValid) return isValid
+
+ // get the line number of the first rule
+ firstLine = util.getLine(def.rules[0].index, data)
+
+ // generate a extract what the correct sorted rules would look like
+ extract = sortedRules.map(function (rule) {
+ if (!rule.name) return
+ return util.padLine(firstLine + index++)
+ + ' ' + rule.name + ': '
+ + (typeof rule.value == 'string' ? rule.value : rule.value.toCSS({}))
+ + ';'
+ }).filter(function (item) { return item }).join('\n')
+
+ // extract selector for error message
+ selector = (' "' + def.selectors.map(function (selector) {
+ return selector.toCSS && selector.toCSS({}).replace(/^\s/, '')
+ }).join(', ') + '"').magenta
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message + selector + '\n\n Correct order below:\n'.grey
+ , extract: extract
+ , sortedRules: sortedRules
+ })
+
+ // return valid state
+ return isValid
+}
71 lib/lint/zero-units.js
@@ -0,0 +1,71 @@
+// ================================================
+// RECESS
+// RULE: No need to specify units when a value is 0
+// ================================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ================================================
+
+'use strict'
+
+var util = require('../util')
+ , units = [
+ '%'
+ , 'in'
+ , 'cm'
+ , 'mm'
+ , 'em'
+ , 'ex'
+ , 'pt'
+ , 'pc'
+ , 'px'
+ ]
+ , RULE = {
+ type: 'zeroUnits'
+ , exp: new RegExp('\\b0\\s?(' + units.join('|') + ')')
+ , message: 'No need to specify units when a value is 0'
+ }
+
+// validation method
+module.exports = function (def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no rules to validate
+ if (!def.rules) return isValid
+
+ // loop over rules
+ def.rules.forEach(function (rule) {
+ var extract
+
+ // continue to next rule if no 0 units are present
+ if ( !(rule.value
+ && rule.value.is == 'value'
+ && RULE.exp.test(rule.value.toCSS({}))) ) return
+
+ // calculate line number for the extract
+ extract = util.getLine(rule.index, data)
+ extract = util.padLine(extract)
+
+ // highlight invalid 0 units
+ extract += rule.toCSS({}).replace(RULE.exp, function ($1) {
+ return 0 + $1.slice(1).magenta
+ })
+
+ // set invalid flag to false
+ isValid = false
+
+ // set error object on defintion token
+ util.throwError(def, {
+ type: RULE.type
+ , message: RULE.message
+ , extract: extract
+ })
+
+ })
+
+ // return valid state
+ return isValid
+}
47 lib/util.js
@@ -0,0 +1,47 @@
+// ==========================================
+// RECESS
+// UTIL: simple output util methods
+// ==========================================
+// Copyright 2012 Twitter, Inc
+// Licensed under the Apache License v2.0
+// http://www.apache.org/licenses/LICENSE-2.0
+// ==========================================
+
+'use strict'
+
+var _ = require('underscore')
+
+module.exports = {
+
+ // set fail output object
+ throwError: function (def, err) {
+ def.errors = def.errors || []
+ err.message = err.message.cyan
+ def.errors.push(err)
+ }
+
+ // set line padding
+, padLine: function (line) {
+ var num = (line + '. ')
+ , space = ''
+ _.times(10 - num.length, function () { space += ' ' })
+ return (space + num).grey
+ }
+
+ // get line number from data
+, getLine: function (index, data) {
+ return (data.slice(0, index).match(/\n/g) || "").length + 1;
+ }
+
+ // error counter
+, countErrors: function (definitions) {
+ var fails = 0
+ definitions.forEach(function (def) {
+ def.errors
+ && def.errors.length
+ && def.errors.forEach(function (err) { fails++ })
+ })
+ return fails
+ }
+
+}
14 makefile
@@ -0,0 +1,14 @@
+#
+# Run all tests
+#
+test:
+ @@ node test
+
+#
+# Run benchmark
+#
+benchmark:
+ @@ node benchmark
+
+
+.PHONY: test benchmark
1 node_modules/.bin/lessc
1 node_modules/.bin/nopt
22 node_modules/colors/MIT-LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2010
+
+Marak Squires
+Alexis Sellier (cloudhead)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
77 node_modules/colors/ReadMe.md
@@ -0,0 +1,77 @@
+# colors.js - get color and style in your node.js console ( and browser ) like what
+
+<img src="http://i.imgur.com/goJdO.png" border = "0"/>
+
+
+## Installation
+
+ npm install colors
+
+## colors and styles!
+
+- bold
+- italic
+- underline
+- inverse
+- yellow
+- cyan
+- white
+- magenta
+- green
+- red
+- grey
+- blue
+- rainbow
+- zebra
+- random
+
+## Usage
+
+``` js
+var colors = require('./colors');
+
+console.log('hello'.green); // outputs green text
+console.log('i like cake and pies'.underline.red) // outputs red underlined text
+console.log('inverse the color'.inverse); // inverses the color
+console.log('OMG Rainbows!'.rainbow); // rainbow (ignores spaces)
+```
+
+# Creating Custom themes
+
+```js
+
+var require('colors');
+
+colors.setTheme({
+ silly: 'rainbow',
+ input: 'grey',
+ verbose: 'cyan',
+ prompt: 'grey',
+ info: 'green',
+ data: 'grey',
+ help: 'cyan',
+ warn: 'yellow',
+ debug: 'blue',
+ error: 'red'
+});
+
+// outputs red text
+console.log("this is an error".error);
+
+// outputs yellow text
+console.log("this is a warning".warn);
+```
+
+
+### Contributors
+
+Marak (Marak Squires)
+Alexis Sellier (cloudhead)
+mmalecki (Maciej Małecki)
+nicoreed (Nico Reed)
+morganrallen (Morgan Allen)
+JustinCampbell (Justin Campbell)
+ded (Dustin Diaz)
+
+
+#### , Marak Squires , Justin Campbell, Dustin Diaz (@ded)
269 node_modules/colors/colors.js
@@ -0,0 +1,269 @@
+/*
+colors.js
+
+Copyright (c) 2010
+
+Marak Squires
+Alexis Sellier (cloudhead)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+var isHeadless = false;
+
+if (typeof module !== 'undefined') {
+ isHeadless = true;
+}
+
+if (!isHeadless) {
+ var exports = {};
+ var module = {};
+ var colors = exports;
+ exports.mode = "browser";
+} else {
+ exports.mode = "console";
+}
+
+//
+// Prototypes the string object to have additional method calls that add terminal colors
+//
+var addProperty = function (color, func) {
+ var allowOverride = ['bold'];
+ exports[color] = function(str) {
+ return func.apply(str);
+ };
+ String.prototype.__defineGetter__(color, func);
+}
+
+//
+// Iterate through all default styles and colors
+//
+
+var x = ['bold', 'underline', 'italic', 'inverse', 'grey', 'black', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
+x.forEach(function (style) {
+
+ // __defineGetter__ at the least works in more browsers
+ // http://robertnyman.com/javascript/javascript-getters-setters.html
+ // Object.defineProperty only works in Chrome
+ addProperty(style, function () {
+ return stylize(this, style);
+ });
+});
+
+function sequencer(map) {
+ return function () {
+ if (!isHeadless) {
+ return this.replace(/( )/, '$1');
+ }
+ var exploded = this.split("");
+ var i = 0;
+ exploded = exploded.map(map);
+ return exploded.join("");
+ }
+}
+
+var rainbowMap = (function () {
+ var rainbowColors = ['red','yellow','green','blue','magenta']; //RoY G BiV
+ return function (letter, i, exploded) {
+ if (letter == " ") {
+ return letter;
+ } else {
+ return stylize(letter, rainbowColors[i++ % rainbowColors.length]);
+ }
+ }
+})();
+
+exports.addSequencer = function (name, map) {
+ addProperty(name, sequencer(map));
+}
+
+exports.addSequencer('rainbow', rainbowMap);
+exports.addSequencer('zebra', function (letter, i, exploded) {
+ return i % 2 === 0 ? letter : letter.inverse;
+});
+
+exports.setTheme = function (theme) {
+ Object.keys(theme).forEach(function(prop){
+ addProperty(prop, function(){
+ return exports[theme[prop]](this);
+ });
+ });
+}
+
+function stylize(str, style) {
+
+ if (exports.mode == 'console') {
+ var styles = {
+ //styles
+ 'bold' : ['\033[1m', '\033[22m'],
+ 'italic' : ['\033[3m', '\033[23m'],
+ 'underline' : ['\033[4m', '\033[24m'],
+ 'inverse' : ['\033[7m', '\033[27m'],
+ //grayscale
+ 'white' : ['\033[37m', '\033[39m'],
+ 'grey' : ['\033[90m', '\033[39m'],
+ 'black' : ['\033[30m', '\033[39m'],
+ //colors
+ 'blue' : ['\033[34m', '\033[39m'],
+ 'cyan' : ['\033[36m', '\033[39m'],
+ 'green' : ['\033[32m', '\033[39m'],
+ 'magenta' : ['\033[35m', '\033[39m'],
+ 'red' : ['\033[31m', '\033[39m'],
+ 'yellow' : ['\033[33m', '\033[39m']
+ };
+ } else if (exports.mode == 'browser') {
+ var styles = {
+ //styles
+ 'bold' : ['<b>', '</b>'],
+ 'italic' : ['<i>', '</i>'],
+ 'underline' : ['<u>', '</u>'],
+ 'inverse' : ['<span style="background-color:black;color:white;">', '</span>'],
+ //grayscale
+ 'white' : ['<span style="color:white;">', '</span>'],
+ 'grey' : ['<span style="color:grey;">', '</span>'],
+ 'black' : ['<span style="color:black;">', '</span>'],
+ //colors
+ 'blue' : ['<span style="color:blue;">', '</span>'],
+ 'cyan' : ['<span style="color:cyan;">', '</span>'],
+ 'green' : ['<span style="color:green;">', '</span>'],
+ 'magenta' : ['<span style="color:magenta;">', '</span>'],
+ 'red' : ['<span style="color:red;">', '</span>'],
+ 'yellow' : ['<span style="color:yellow;">', '</span>']
+ };
+ } else if (exports.mode == 'none') {
+ return str;
+ } else {
+ console.log('unsupported mode, try "browser", "console" or "none"');
+ }
+ return styles[style][0] + str + styles[style][1];
+};
+
+// don't summon zalgo
+addProperty('zalgo', function () {
+ return zalgo(this);
+});
+
+// please no
+function zalgo(text, options) {
+ var soul = {
+ "up" : [
+ '̍','̎','̄','̅',
+ '̿','̑','̆','̐',
+ '͒','͗','͑','̇',
+ '̈','̊','͂','̓',
+ '̈','͊','͋','͌',
+ '̃','̂','̌','͐',
+ '̀','́','̋','̏',
+ '̒','̓','̔','̽',
+ '̉','ͣ','ͤ','ͥ',
+ 'ͦ','ͧ','ͨ','ͩ',
+ 'ͪ','ͫ','ͬ','ͭ',
+ 'ͮ','ͯ','̾','͛',
+ '͆','̚'
+ ],
+ "down" : [
+ '̖','̗','̘','̙',
+ '̜','̝','̞','̟',
+ '̠','̤','̥','̦',
+ '̩','̪','̫','̬',
+ '̭','̮','̯','̰',
+ '̱','̲','̳','̹',
+ '̺','̻','̼','ͅ',
+ '͇','͈','͉','͍',
+ '͎','͓','͔','͕',
+ '͖','͙','͚','̣'
+ ],
+ "mid" : [
+ '̕','̛','̀','́',
+ '͘','̡','̢','̧',
+ '̨','̴','̵','̶',
+ '͜','͝','͞',
+ '͟','͠','͢','̸',
+ '̷','͡',' ҉'
+ ]
+ },
+ all = [].concat(soul.up, soul.down, soul.mid),
+ zalgo = {};
+
+ function randomNumber(range) {
+ r = Math.floor(Math.random()*range);
+ return r;
+ };
+
+ function is_char(character) {
+ var bool = false;
+ all.filter(function(i){
+ bool = (i == character);
+ });
+ return bool;
+ }
+
+ function heComes(text, options){
+ result = '';
+ options = options || {};
+ options["up"] = options["up"] || true;
+ options["mid"] = options["mid"] || true;
+ options["down"] = options["down"] || true;
+ options["size"] = options["size"] || "maxi";
+ var counts;
+ text = text.split('');
+ for(var l in text){
+ if(is_char(l)) { continue; }
+ result = result + text[l];
+
+ counts = {"up" : 0, "down" : 0, "mid" : 0};
+
+ switch(options.size) {
+ case 'mini':
+ counts.up = randomNumber(8);
+ counts.min= randomNumber(2);
+ counts.down = randomNumber(8);
+ break;
+ case 'maxi':
+ counts.up = randomNumber(16) + 3;
+ counts.min = randomNumber(4) + 1;
+ counts.down = randomNumber(64) + 3;
+ break;
+ default:
+ counts.up = randomNumber(8) + 1;
+ counts.mid = randomNumber(6) / 2;
+ counts.down= randomNumber(8) + 1;
+ break;
+ }
+
+ var arr = ["up", "mid", "down"];
+ for(var d in arr){
+ var index = arr[d];
+ for (var i = 0 ; i <= counts[index]; i++)
+ {
+ if(options[index]) {
+ result = result + soul[index][randomNumber(soul[index].length)];
+ }
+ }
+ }
+ }
+ return result;
+ };
+ return heComes(text);
+}
+
+addProperty('stripColors', function() {
+ return ("" + this).replace(/\u001b\[\d+m/g,'');
+});
74 node_modules/colors/example.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html lang="en-us">
+ <head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <title>Colors Example</title>
+ <script src="colors.js"></script>
+ </head>
+ <body>
+ <script>
+
+ var test = colors.red("hopefully colorless output");
+
+ document.write('Rainbows are fun!'.rainbow + '<br/>');
+ document.write('So '.italic + 'are'.underline + ' styles! '.bold + 'inverse'.inverse); // styles not widely supported
+ document.write('Chains are also cool.'.bold.italic.underline.red); // styles not widely supported
+ //document.write('zalgo time!'.zalgo);
+ document.write(test.stripColors);
+ document.write("a".grey + " b".black);
+
+ document.write("Zebras are so fun!".zebra);
+
+ document.write(colors.rainbow('Rainbows are fun!'));
+ document.write(colors.italic('So ') + colors.underline('are') + colors.bold(' styles! ') + colors.inverse('inverse')); // styles not widely supported
+ document.write(colors.bold(colors.italic(colors.underline(colors.red('Chains are also cool.'))))); // styles not widely supported
+ //document.write(colors.zalgo('zalgo time!'));
+ document.write(colors.stripColors(test));
+ document.write(colors.grey("a") + colors.black(" b"));
+
+ colors.addSequencer("america", function(letter, i, exploded) {
+ if(letter === " ") return letter;
+ switch(i%3) {
+ case 0: return letter.red;
+ case 1: return letter.white;
+ case 2: return letter.blue;
+ }
+ });
+
+ colors.addSequencer("random", (function() {
+ var available = ['bold', 'underline', 'italic', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
+
+ return function(letter, i, exploded) {
+ return letter === " " ? letter : letter[available[Math.round(Math.random() * (available.length - 1))]];
+ };
+ })());
+
+ document.write("AMERICA! F--K YEAH!".america);
+ document.write("So apparently I've been to Mars, with all the little green men. But you know, I don't recall.".random);
+
+ //
+ // Custom themes
+ //
+
+ colors.setTheme({
+ silly: 'rainbow',
+ input: 'grey',
+ verbose: 'cyan',
+ prompt: 'grey',
+ info: 'green',
+ data: 'grey',
+ help: 'cyan',
+ warn: 'yellow',
+ debug: 'blue',
+ error: 'red'
+ });
+
+ // outputs red text
+ document.write("this is an error".error);
+
+ // outputs yellow text
+ document.write("this is a warning".warn);
+
+ </script>
+ </body>
+</html>
65 node_modules/colors/example.js
@@ -0,0 +1,65 @@
+var colors = require('./colors');
+
+//colors.mode = "browser";
+
+var test = colors.red("hopefully colorless output");
+console.log('Rainbows are fun!'.rainbow);
+console.log('So '.italic + 'are'.underline + ' styles! '.bold + 'inverse'.inverse); // styles not widely supported
+console.log('Chains are also cool.'.bold.italic.underline.red); // styles not widely supported
+//console.log('zalgo time!'.zalgo);
+console.log(test.stripColors);
+console.log("a".grey + " b".black);
+
+console.log("Zebras are so fun!".zebra);
+
+console.log(colors.rainbow('Rainbows are fun!'));
+console.log(colors.italic('So ') + colors.underline('are') + colors.bold(' styles! ') + colors.inverse('inverse')); // styles not widely supported
+console.log(colors.bold(colors.italic(colors.underline(colors.red('Chains are also cool.'))))); // styles not widely supported
+//console.log(colors.zalgo('zalgo time!'));
+console.log(colors.stripColors(test));
+console.log(colors.grey("a") + colors.black(" b"));
+
+colors.addSequencer("america", function(letter, i, exploded) {
+ if(letter === " ") return letter;
+ switch(i%3) {
+ case 0: return letter.red;
+ case 1: return letter.white;
+ case 2: return letter.blue;
+ }
+});
+
+colors.addSequencer("random", (function() {
+ var available = ['bold', 'underline', 'italic', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
+
+ return function(letter, i, exploded) {
+ return letter === " " ? letter : letter[available[Math.round(Math.random() * (available.length - 1))]];
+ };
+})());
+
+console.log("AMERICA! F--K YEAH!".america);
+console.log("So apparently I've been to Mars, with all the little green men. But you know, I don't recall.".random);
+
+//
+// Custom themes
+//
+
+colors.setTheme({
+ silly: 'rainbow',
+ input: 'grey',
+ verbose: 'cyan',
+ prompt: 'grey',
+ info: 'green',
+ data: 'grey',
+ help: 'cyan',
+ warn: 'yellow',
+ debug: 'blue',
+ error: 'red'
+});
+
+// outputs red text
+console.log("this is an error".error);
+
+// outputs yellow text
+console.log("this is a warning".warn);
+
+
28 node_modules/colors/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "colors",
+ "description": "get colors in your node.js console like what",
+ "version": "0.6.0-1",
+ "author": {
+ "name": "Marak Squires"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/Marak/colors.js.git"
+ },
+ "engines": {
+ "node": ">=0.1.90"
+ },
+ "main": "colors",
+ "_id": "colors@0.6.0-1",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "_engineSupported": true<