Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Commit

Permalink
Merge branch 'release/1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
borodean committed Dec 26, 2014
2 parents 941045f + e68b497 commit 742a0a1
Show file tree
Hide file tree
Showing 13 changed files with 330 additions and 179 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules/
npm-debug.log
*.actual.css
*.sublime-*
143 changes: 140 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
PostCSS Assets
==============

An asset manager for CSS.
PostCSS Assets is an asset manager for CSS. It isolates stylesheets from environmental changes, gets image sizes and inlines files.

Table of contents
-----------------

* [Installation](#installation)
* [Usage](#usage)
* [URL resolution](#url-resolution)
* [Load paths](#load-paths)
* [Base path](#base-path)
* [Base URL](#base-url)
* [Relative paths](#relative-paths)
* [Image dimensions](#image-dimensions)
* [Inlining files](#inlining-files)
* [Full list of options](#full-list-of-options)
* [Full list of modifiers](#full-list-of-modifiers)

Installation
------------

```bash
npm install postcss-assets --save-dev
```

Usage
-----
Expand All @@ -14,7 +36,9 @@ gulp.task('assets', function () {
var assets = require('postcss-assets');

return gulp.src('source/*.css')
.pipe(postcss([ assets() ]))
.pipe(postcss([assets({
loadPaths: ['images/']
})]))
.pipe(gulp.dest('build/'));
});
```
Expand All @@ -28,10 +52,123 @@ grunt.initConfig({
postcss: {
options: {
processors: [
assets().postcss
assets({
loadPaths: ['images/']
})
]
},
dist: { src: 'build/*.css' }
},
});
```

URL resolution
--------------

These options isolate stylesheets from environmental changes.

### Load paths

To make PostCSS Assets search for files in specific directories, define load paths:

```js
var options = {
loadPaths: ['fonts/', 'media/patterns/', 'images/']
};
```

Example:

```css
body {
background: url('foobar.jpg');
background: url('icons/baz.png');
}
```

PostCSS Assets would look for the files in load paths, then in the base path. If it succeed, it would resolve a true URL:

```css
body {
background: url('/media/patterns/foobar.jpg');
background: url('/images/icons/baz.png');
}
```

### Base path

If the root directory of your site is not where you execute PostCSS Assets, correct it:

```js
var options = {
basePath: 'source/'
};
```

PostCSS Assets would treat `source` directory as `/` for all URLs and load paths would be relative to it.

### Base URL

If the URL of your base path is not `/`, correct it:

```js
var options = {
baseUrl: 'http://example.com/wp-content/themes/'
};
```

### Relative paths

To make resolved paths relative, define a directory to relate to:

```js
var options = {
relativeTo: 'assets/css'
};
```

Image dimensions
----------------

PostCSS Assets calculates dimensions of PNG, JPEG, GIF, SVG and WebP images:

```css
body {
width: width('images/foobar.png'); /* 320px */
height: height('images/foobar.png'); /* 240px */
background-size: size('images/foobar.png'); /* 320px 240px */
}
```

To correct the dimensions for images with a high density, pass it as a second parameter:

```css
body {
width: width('images/foobar.png', 2); /* 160px */
height: height('images/foobar.png', 2); /* 120px */
background-size: size('images/foobar.png', 2); /* 160px 120px */
}
```

Inlining files
--------------

PostCSS inlines files to a stylesheet in Base64 encoding:

```css
body {
background: inline('images/foobar.png');
}
```

SVG files would be inlined unencoded, because [then they benefit in size](http://css-tricks.com/probably-dont-base64-svg/).

Full list of options
--------------------

| Option | Description | Default |
|:-----------------|:--------------------------------------------------------------------------------|:--------|
| `basePath` | Root directory of the project. | `.` |
| `baseUrl` | URL of the project when running the web server. | `/` |
| `loadPaths` | Specific directories to look for the files. | `[]` |
| `relativeTo` | Directory to relate to when resolving URLs. If `false`, disables relative URLs. | `false` |
101 changes: 52 additions & 49 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,6 @@ var cssesc = require('cssesc');
var mime = require('mime');
var sizeOf = require('image-size');

const AUTO_SIZE = ['background-size', 'border-image-width', 'border-width',
'margin', 'padding'];
const AUTO_WIDTH = ['border-left', 'border-left-width', 'border-right',
'border-right-width', 'left', 'margin-left',
'margin-right', 'max-width', 'min-width', 'padding-left',
'padding-right', 'width'];
const AUTO_HEIGHT = ['border-bottom', 'border-bottom-width', 'border-top',
'border-top-width', 'bottom', 'height', 'margin-bottom',
'margin-top', 'max-height', 'min-height',
'padding-bottom', 'padding-top'];

module.exports = function (options) {

options = options || {};
Expand All @@ -50,6 +39,25 @@ module.exports = function (options) {
options.relativeTo = false;
}

function getImageSize(assetStr, density) {
var assetPath = resolvePath(assetStr.value);
var size;
try {
size = sizeOf(assetPath);
if (typeof density !== 'undefined') {
density = parseFloat(density.value, 10);
console.log(density);
size.width = +(size.width / density).toFixed(4);
size.height = +(size.height / density).toFixed(4);
}
return size;
} catch (exception) {
var err = new Error("Image corrupted: " + assetPath);
err.name = 'ECORRUPT';
throw err;
}
}

function matchPath(assetPath) {
var exception, matchingPath;
var isFound = options.loadPaths.some(function (loadPath) {
Expand Down Expand Up @@ -95,50 +103,45 @@ module.exports = function (options) {
return cssesc(url.format(assetUrl));
}

function shouldBeInline(assetPath) {
if (options.inline && options.inline.maxSize) {
var size = fs.statSync(assetPath).size;
return (size <= parseBytes(options.inline.maxSize));
}
return false;
}

return function (cssTree) {
cssTree.eachDecl(function (decl) {

decl.value = mapFunctions(decl.value, function (before, quote, assetStr, modifier, after) {

try {

var assetPath = resolvePath(assetStr);
var prop = vendor.unprefixed(decl.prop);

if (modifier === 'width' || AUTO_WIDTH.indexOf(prop) !== -1) {
return sizeOf(assetPath).width + 'px';
}

if (modifier === 'height' || AUTO_HEIGHT.indexOf(prop) !== -1) {
return sizeOf(assetPath).height + 'px';
}

if (modifier === 'size' || AUTO_SIZE.indexOf(prop) !== -1) {
var size = sizeOf(assetPath);
try {
decl.value = mapFunctions(decl.value, {
'url': function (assetStr) {
assetStr.value = resolveUrl(assetStr.value);
return 'url(' + assetStr + ')';
},

'inline': function (assetStr) {
assetStr.value = resolveDataUrl(assetStr.value);
return 'url(' + assetStr + ')';
},

'width': function (assetStr, density) {
return getImageSize(assetStr, density).width + 'px';
},

'height': function (assetStr, density) {
return getImageSize(assetStr, density).height + 'px';
},

'size': function (assetStr, density) {
var size = getImageSize(assetStr, density);
return size.width + 'px ' + size.height + 'px';
}

if (shouldBeInline(assetPath)) {
return 'url(' + before + quote + resolveDataUrl(assetStr) + quote + after + ')';
}

return 'url(' + before + quote + resolveUrl(assetStr) + quote + after + ')';

} catch (exception) {
if (exception.name !== 'ENOENT') {
throw exception;
}
});
} catch (exception) {
switch (exception.name) {
case 'ECORRUPT':
console.warn(exception.message);
break;
case 'ENOENT':
console.warn('%s\nLoad paths:\n %s', exception.message, options.loadPaths.join('\n '));
break;
default:
throw exception;
}
});
}
});
};
};
66 changes: 55 additions & 11 deletions lib/mapFunctions.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
const R_FUNC = /url\((\s*)((['"]?).*?\3.*?)(\s*)\)/gi;
const R_PARAMS = /^(['"]?)(.+?)\1(?:\s+(.+))?$/;

module.exports = function mapFunctions(cssValue, callback) {
return cssValue.replace(R_FUNC, function (matches, before, params, quote, after) {
params = params.match(R_PARAMS);
var assetStr = params[2];
var modifier = params[3];
var result = callback(before, quote, assetStr, modifier, after);
return result || matches;
});
var gonzales = require('gonzales');
var list = require('postcss/lib/list');

function CSSString (str) {
this.quotes = str[0];

if (this.quotes === "'" || this.quotes === '"') {
this.value = str.slice(1, -1);
} else {
this.value = str;
this.quotes = '';
}
}

CSSString.prototype.toString = function () {
return this.quotes + this.value + this.quotes;
};

module.exports = function mapFunctions(cssValue, map) {
var ast = gonzales.srcToCSSP(cssValue, 'value');

var traverse = function (node) {
var type = node[0];
var children = node.slice(1);

if (type === 'uri') {
node = gonzales.srcToCSSP(gonzales.csspToSrc(node), 'funktion');
return traverse(node);
}

if (type === 'funktion') {
var name = children[0][1];
var body = children[1].slice(1).map(function (x) {
return gonzales.csspToSrc(traverse(x));;
}).join('');

var process = map[name];

if (typeof process === 'function') {
return ['raw', process.apply(this, list.comma(body).map(function (param) {
return new CSSString(param);
}))];
}
}

return [type].concat(children.map(function (child) {
if (Array.isArray(child)) {
return traverse(child);
}

return child;
}));
};

return gonzales.csspToSrc(traverse(ast));
};
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
{
"name": "postcss-assets",
"version": "0.9.1",
"version": "1.0.0",
"description": "PostCSS plugin to manage assets",
"author": "Vadim Borodean <[email protected]>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/borodean/postcss-assets"
"url": "https://github.com/borodean/postcss-assets"
},
"dependencies": {
"cssesc": "^0.1.0",
"image-size": "^0.3.3",
"gonzales": "^1.0.7",
"image-size": "^0.3.5",
"js-base64": "^2.1.5",
"mime": "^1.2.11",
"postcss": "^2.2.5"
"postcss": "^3.0.7"
},
"devDependencies": {
"tape": "^3.0.0"
"tape": "^3.0.3"
},
"scripts": {
"test": "tape test"
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/alpha/invalid.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 742a0a1

Please sign in to comment.