Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of github.com:twitter/recess

Conflicts:
	package.json
	test/types/lint.js
  • Loading branch information...
commit 2ec6067080d2e54645791ab233d3dd4dcaf1bf9a 2 parents 6971513 + 8c87127
@fat fat authored
View
10 README.md
@@ -26,6 +26,7 @@ OPTIONS
- --noOverqualifying - doesn't complain about overqualified selectors (ie: `div#foo.bar`)
- --noUnderscores - doesn't complain about using underscores in your class names
- --noUniversalSelectors - doesn't complain about using the universal `*` selector
+- --prefixWhitespace - adds whitespace prefix to line up vender prefixed properties
- --strictPropertyOrder - doesn't looking into your property ordering
- --zeroUnits - doesn't complain if you add units to values of 0
@@ -54,7 +55,13 @@ $ recess ./bootstrap.less --compress > ./bootstrap-production.css
Watch a directory for changes and auto compile a css file from the changes. *experimental*
```CLI
-$ recess input.less:ouput.css --compile --watch watch/this/dir/for/changes
+$ recess input.less:ouput.css --watch watch/this/dir/for/changes
+```
+
+Watch a single file for changes and auto compile a css file from the changes. *experimental*
+
+```CLI
+$ recess input.less:ouput.css --watch
```
PROGRAMMATIC API
@@ -83,6 +90,7 @@ The following options (and defaults) are available in the programatic api:
- noUniversalSelectors: true
- prefixWhitespace: true
- strictPropertyOrder: true
+- stripColors: false
- 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.
View
16 bin/recess
@@ -61,6 +61,9 @@ options.cli = true
// if not watch - run Recess
if (!options.watch) return recess(paths, options)
+// if options watch, but compile isn't set - make it happen
+if (options.watch && !options.compile && !options.compress) options.compile = true;
+
// set CLI to false
options.cli = false
paths = paths[0].split(':')
@@ -74,9 +77,18 @@ writeFile = function () {
})
}
-// create monitor to watch filetree
+// if watch doesn't exist, watch the path
+if (!(fs.existsSync || path.existsSync)(options.watch)) options.watch = path.resolve(paths)
+
+// throw if can't find file to watch
+if (!(fs.existsSync || path.existsSync)(options.watch)) return console.log("can't find file: " + options.watch)
+
+// use fs.watch if watchign single file
+if (path.extname(options.watch)) return fs.watch(options.watch, writeFile)
+
+// create monitor to watch filetree if provided dir
watch.createMonitor(options.watch, function (monitor) {
monitor.on("created", writeFile)
monitor.on("changed", writeFile)
monitor.on("removed", writeFile)
-})
+})
View
46 lib/compile/inline-images.js
@@ -0,0 +1,46 @@
+// ==========================================
+// RECESS
+// COMPILE: replaces image links with base64 image data
+// ==========================================
+// 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')
+ , fs = require('fs')
+ , seperator = (process.platform == 'win32') ? '\\' : '/'
+ , toCSS
+ , path
+
+function compile () {
+ // strip units from 0 values
+ var props = toCSS.apply(this, arguments)
+
+ // do we have a url here?
+ if (/url\(/.test(props)) {
+ var fileName = props.match(/url\((['"]?)(.*)\1\)/)[2]
+ , ext = fileName.match(/[^.]*$/)[0]
+ , mimetype = 'image/' + ext.replace(/jpg/, 'jpeg')
+ , pathParts = path.split(seperator)
+ , filePath = pathParts.slice(0, pathParts.length - 1).join(seperator)
+ , imgBuffer = new Buffer(fs.readFileSync(filePath+seperator+fileName)).toString('base64')
+ , urlData = 'url(data:' + mimetype + ';base64,' + imgBuffer + ')'
+
+ return props.replace(/url\([^\)]*\)/, urlData)
+ }
+
+ return props
+}
+
+module.exports.on = function () {
+ path = this.path
+ toCSS = less.tree.Value.prototype.toCSS
+ less.tree.Value.prototype.toCSS = compile
+}
+
+module.exports.off = function () {
+ less.tree.Value.prototype.toCSS = toCSS
+}
View
2  lib/core.js
@@ -154,7 +154,7 @@ RECESS.prototype = {
Object.keys(this.options).forEach(function (key) {
that.options[key]
&& RECESS.COMPILERS[key]
- && RECESS.COMPILERS[key].on()
+ && RECESS.COMPILERS[key].on.call(that)
})
// iterate over defintions and compress them (join with new lines)
View
1  lib/index.js
@@ -81,6 +81,7 @@ module.exports.DEFAULTS = RECESS.DEFAULTS = {
, stripColors: false
, watch: false
, zeroUnits: true
+, inlineImages: false
}
View
60 lib/lint/inline-images.js
@@ -0,0 +1,60 @@
+// ===================================================
+// RECESS
+// RULE: Linked images should be embeded.
+// ===================================================
+// 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: 'inlineImages'
+ , exp: /^url\((?!data:)/
+ , message: 'Linked images should be embeded.'
+ }
+
+// validation method
+module.exports = function (def, data) {
+
+ // default validation to true
+ var isValid = true
+
+ // return if no selector to validate
+ if (!def.rules) return isValid
+
+ // loop over selectors
+ def.rules.forEach(function (rule) {
+ var extract
+
+ // continue to next rule if no url is 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 $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
+}
View
4 lib/lint/strict-property-order.js
@@ -95,6 +95,7 @@ var _ = require('underscore')
, 'list-style-type'
, 'list-style-position'
, 'list-style-image'
+ , 'pointer-events'
, 'cursor'
, 'background'
, 'background-attachment'
@@ -150,6 +151,7 @@ var _ = require('underscore')
, 'box-pack'
, 'box-shadow'
, 'box-sizing'
+ , 'table-layout'
, 'animation'
, 'animation-delay'
, 'animation-duration'
@@ -157,12 +159,14 @@ var _ = require('underscore')
, 'animation-name'
, 'animation-play-state'
, 'animation-timing-function'
+ , 'animation-fill-mode'
, 'transition'
, 'transition-delay'
, 'transition-duration'
, 'transition-property'
, 'transition-timing-function'
, 'background-clip'
+ , 'backface-visibility'
, 'resize'
, 'appearance'
, 'user-select'
View
2  package.json
@@ -1,6 +1,6 @@
{ "name": "recess"
, "description": "A simple, attractive code quality tool for CSS built on top of LESS"
-, "version": "1.0.5"
+, "version": "1.1.5"
, "author": "Jacob Thornton <jacob@twitter.com> (https://github.com/fat)"
, "keywords": ["css", "lint"]
, "licenses": [ { "type": "Apache-2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0" } ]
View
5 test/compiled/blog.css
@@ -1,10 +1,5 @@
/* Fat's blog styles */
-@font-face {
- font-family: "Mistral";
- src: url("/fonts/Mistral.ttf");
-}
-
html,
body {
overflow: auto;
View
15 test/compiled/inline-images.css
@@ -0,0 +1,15 @@
+.foo {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAIAAADqTdgKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbJJREFUeNrslFtLAlEQx4/X42U1dUU3NaULPkRSVARBEAT17LfsO/QW9CoURQ9aBlvKJpmKl3VtL/1DUVdPKfjQi8Oy7M6c+c2cOTPHkr0sk8XEShaWJWIk9nkWNVt16bPUbtW8XHA9vmW3UWig93GBEQKqZ/Fhb/tk2v/28abaqnXdfsPDWRofolTkuSA0pwcXpo0gyBt1Y7WqKRPxoe+kdvRI0uBCeMt8DBpfev+l9GRCUH8QK2C7zl2J9VGzAQ39OBS/fc1hOmOqRczH3yll2Dpc6P61UMjnksIGuL9V51xYa3R1E0KW5Sh1vis9fCPbDiH5iuiSikibUM80Yug/QtTUTqUqkbGc+yCmIBijL9Lh+MSe/xCBWBiIrmpkY6k5ESgcuzuV3tc8/tgFdTrYCBh2KZ2JOOZXNc3KRsCQERJnUWGiWuMC64wxq7blar3WP1qmP2/3TKQwONT+FKH5f+rq9pNIcnr/yN9hsaHq7EkNrPgT4RBGsNxEbxjDLOCJ8+MDwU3OV+9qTP8BArk1NJ2jrow3cWQ35SmruqIaUkud675AEDwNoi+v339FfAswADLzuoci9jFaAAAAAElFTkSuQmCC);
+}
+
+.bar {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAIAAADqTdgKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbJJREFUeNrslFtLAlEQx4/X42U1dUU3NaULPkRSVARBEAT17LfsO/QW9CoURQ9aBlvKJpmKl3VtL/1DUVdPKfjQi8Oy7M6c+c2cOTPHkr0sk8XEShaWJWIk9nkWNVt16bPUbtW8XHA9vmW3UWig93GBEQKqZ/Fhb/tk2v/28abaqnXdfsPDWRofolTkuSA0pwcXpo0gyBt1Y7WqKRPxoe+kdvRI0uBCeMt8DBpfev+l9GRCUH8QK2C7zl2J9VGzAQ39OBS/fc1hOmOqRczH3yll2Dpc6P61UMjnksIGuL9V51xYa3R1E0KW5Sh1vis9fCPbDiH5iuiSikibUM80Yug/QtTUTqUqkbGc+yCmIBijL9Lh+MSe/xCBWBiIrmpkY6k5ESgcuzuV3tc8/tgFdTrYCBh2KZ2JOOZXNc3KRsCQERJnUWGiWuMC64wxq7blar3WP1qmP2/3TKQwONT+FKH5f+rq9pNIcnr/yN9hsaHq7EkNrPgT4RBGsNxEbxjDLOCJ8+MDwU3OV+9qTP8BArk1NJ2jrow3cWQ35SmruqIaUkud675AEDwNoi+v339FfAswADLzuoci9jFaAAAAAElFTkSuQmCC);
+}
+
+.fat {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAIAAADqTdgKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbJJREFUeNrslFtLAlEQx4/X42U1dUU3NaULPkRSVARBEAT17LfsO/QW9CoURQ9aBlvKJpmKl3VtL/1DUVdPKfjQi8Oy7M6c+c2cOTPHkr0sk8XEShaWJWIk9nkWNVt16bPUbtW8XHA9vmW3UWig93GBEQKqZ/Fhb/tk2v/28abaqnXdfsPDWRofolTkuSA0pwcXpo0gyBt1Y7WqKRPxoe+kdvRI0uBCeMt8DBpfev+l9GRCUH8QK2C7zl2J9VGzAQ39OBS/fc1hOmOqRczH3yll2Dpc6P61UMjnksIGuL9V51xYa3R1E0KW5Sh1vis9fCPbDiH5iuiSikibUM80Yug/QtTUTqUqkbGc+yCmIBijL9Lh+MSe/xCBWBiIrmpkY6k5ESgcuzuV3tc8/tgFdTrYCBh2KZ2JOOZXNc3KRsCQERJnUWGiWuMC64wxq7blar3WP1qmP2/3TKQwONT+FKH5f+rq9pNIcnr/yN9hsaHq7EkNrPgT4RBGsNxEbxjDLOCJ8+MDwU3OV+9qTP8BArk1NJ2jrow3cWQ35SmruqIaUkud675AEDwNoi+v339FfAswADLzuoci9jFaAAAAAElFTkSuQmCC);
+}
+
+.woo {
+ background: #ffffff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAIAAADqTdgKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbJJREFUeNrslFtLAlEQx4/X42U1dUU3NaULPkRSVARBEAT17LfsO/QW9CoURQ9aBlvKJpmKl3VtL/1DUVdPKfjQi8Oy7M6c+c2cOTPHkr0sk8XEShaWJWIk9nkWNVt16bPUbtW8XHA9vmW3UWig93GBEQKqZ/Fhb/tk2v/28abaqnXdfsPDWRofolTkuSA0pwcXpo0gyBt1Y7WqKRPxoe+kdvRI0uBCeMt8DBpfev+l9GRCUH8QK2C7zl2J9VGzAQ39OBS/fc1hOmOqRczH3yll2Dpc6P61UMjnksIGuL9V51xYa3R1E0KW5Sh1vis9fCPbDiH5iuiSikibUM80Yug/QtTUTqUqkbGc+yCmIBijL9Lh+MSe/xCBWBiIrmpkY6k5ESgcuzuV3tc8/tgFdTrYCBh2KZ2JOOZXNc3KRsCQERJnUWGiWuMC64wxq7blar3WP1qmP2/3TKQwONT+FKH5f+rq9pNIcnr/yN9hsaHq7EkNrPgT4RBGsNxEbxjDLOCJ8+MDwU3OV+9qTP8BArk1NJ2jrow3cWQ35SmruqIaUkud675AEDwNoi+v339FfAswADLzuoci9jFaAAAAAElFTkSuQmCC) center center no-repeat;
+}
View
5 test/fixtures/blog.css
@@ -1,10 +1,5 @@
/* Fat's blog styles */
-@font-face {
- font-family: "Mistral";
- src: url("/fonts/Mistral.ttf");
-}
-
html,
body {
overflow: auto;
View
12 test/fixtures/inline-images.css
@@ -0,0 +1,12 @@
+.foo {
+ background-image: url("sprite.png");
+}
+.bar {
+ background: url('sprite.png');
+}
+.fat {
+ background: url(sprite.png);
+}
+.woo {
+ background: #fff url(../sprite.png) center center no-repeat;
+}
View
BIN  test/fixtures/sprite.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  test/sprite.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
6 test/types/compile.js
@@ -4,8 +4,12 @@ var fs = require('fs')
, RECESS = require('../../lib')
fs.readdirSync('test/fixtures').forEach(function (file, index) {
+ // Ignore anything not a less/css file.
+ if (file.indexOf('css') === -1 && file.indexOf('less') === -1) {
+ return
+ }
- RECESS('test/fixtures/' + file, { compile: true }, function (err, fat) {
+ RECESS('test/fixtures/' + file, { compile: true, inlineImages: true }, function (err, fat) {
file = file.replace(/less$/, 'css')
assert.ok(err == null)
assert.ok(fat.output[0] == fs.readFileSync('test/compiled/' + file, 'utf-8'))
View
27 test/types/lint.js
@@ -227,6 +227,33 @@ var assert = require('assert')
Recess.parse()
assert.notEqual(Recess.output[0], '\u001b[31mParse error\u001b[39m: Cannot read property \'red\' of undefined on line 1');
+
+ RECESS.Constructor.prototype.validate = validate
+
+}()
+
+//VALIDATIONS.inlineImage
+!function () {
+
+ var path = 'test/fixtures/inline-images.css'
+ , Recess = new RECESS.Constructor()
+ , validate = RECESS.Constructor.prototype.validate
+ , def
+
+ RECESS.Constructor.prototype.validate = noop
+
+ Recess.data = fs.readFileSync(path, 'utf8')
+
+ Recess.parse()
+
+ def = Recess.definitions[0]
+
+ RECESS.Constructor.RULES.inlineImages(def, Recess.data)
+
+ assert.ok(def.errors)
+ assert.equal(def.errors.length, 1, 'one error found')
+ assert.equal(def.errors[0].type, 'inlineImages')
+
RECESS.Constructor.prototype.validate = validate
}()
Please sign in to comment.
Something went wrong with that request. Please try again.