Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: twitter/recess
base: 90e11af44c
...
head fork: twitter/recess
compare: 8f0ce609b2
Checking mergeability… Don't worry, you can still create the pull request.
  • 3 commits
  • 12 files changed
  • 0 commit comments
  • 2 contributors
Commits on Jun 23, 2012
@gcoop gcoop added support to inline images in output using option --inlineImages 71d5b62
Commits on Jun 30, 2012
@gcoop gcoop minor tweaks to inlineImages feature ref pull req #32. added another …
…test for embedding images that include paths.
aa6b815
Commits on Jul 10, 2012
@fat fat Merge pull request #32 from gcoop/master
Added --inlineImages option to embed images.
8f0ce60
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
@@ -149,7 +149,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
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
26 test/types/lint.js
@@ -214,4 +214,30 @@ var assert = require('assert')
}()
+//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
+
+}()
+
console.log("✓ linting".green)

No commit comments for this range

Something went wrong with that request. Please try again.