From 059781113a654209c4b9f69e4a35d352b7de64f4 Mon Sep 17 00:00:00 2001 From: Andrey Sitnik Date: Sat, 16 May 2015 10:35:46 +0300 Subject: [PATCH] Move code from PostCSS --- .eslintrc | 20 ++++++ .gitignore | 8 +++ .travis.yml | 5 ++ Gemfile | 3 + Gemfile.lock | 10 +++ gulpfile.js | 47 +++++++++++++ package.json | 31 +++++++++ parsers.js | 76 +++++++++++++++++++++ preprocessors.js | 173 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 373 insertions(+) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 parsers.js create mode 100644 preprocessors.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..37c478b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,20 @@ +{ + "rules": { + "no-unused-expressions": [0], + "no-underscore-dangle": [0], + "no-reserved-keys": [2], + "no-multi-spaces": [0], + "no-extra-parens": [2], + "no-unused-vars": [2], + "no-loop-func": [0], + "key-spacing": [0], + "max-len": [2, 80], + "strict": [0], + "indent": [2], + "quotes": [2, "single", "avoid-escape"], + "curly": [0] + }, + "env": { + "node": true + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..323a1e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +*~ + +npm-debug.log +node_modules/ +.bundle/ + +cache/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e1bd776 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +sudo: false +language: node_js +node_js: + - iojs + - "0.12" diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..3331160 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem 'sass' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..04731e6 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,10 @@ +GEM + remote: https://rubygems.org/ + specs: + sass (3.4.13) + +PLATFORMS + ruby + +DEPENDENCIES + sass diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..1a01798 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,47 @@ +var gulp = require('gulp'); +var path = require('path'); +var fs = require('fs-extra'); + +// Lint + +gulp.task('lint', function () { + var eslint = require('gulp-eslint'); + return gulp.src(['*.js']) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failAfterError()); +}); + +// Benchmark + +gulp.task('clean', function (done) { + fs.remove(path.join(__dirname, '/cache'), done); +}); + +gulp.task('bootstrap', function (done) { + var cache = path.join(__dirname, 'cache', 'bootstrap.css'); + if ( fs.existsSync(cache) ) return done(); + + var load = require('load-resources'); + load('github:twbs/bootstrap:dist/css/bootstrap.css', '.css', function (f) { + fs.outputFile(cache, f, done); + }); +}); + +gulp.task('preprocessors', ['bootstrap'], function () { + var bench = require('gulp-bench'); + var summary = require('gulp-bench-summary'); + return gulp.src('./preprocessors.js', { read: false }) + .pipe(bench()) + .pipe(summary('PostCSS')); +}); + +gulp.task('parsers', ['bootstrap'], function () { + var bench = require('gulp-bench'); + var summary = require('gulp-bench-summary'); + return gulp.src('./parsers.js', { read: false }) + .pipe(bench()) + .pipe(summary('PostCSS')); +}); + +gulp.task('default', ['lint']); diff --git a/package.json b/package.json new file mode 100644 index 0000000..a2b0683 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "private": true, + "dependencies": { + "postcss-simple-vars": "0.3.0", + "gulp-bench-summary": "0.1.0", + "load-resources": "0.1.0", + "postcss-nested": "0.3.1", + "postcss-mixins": "0.3.0", + "postcss-calc": "4.0.1", + "gonzales-pe": "3.0.0-27", + "gulp-eslint": "0.12.0", + "gulp-bench": "1.1.0", + "gulp-util": "3.0.4", + "node-sass": "3.1.1", + "through2": "0.6.5", + "stylecow": "6.5.0", + "gonzales": "1.0.7", + "fs-extra": "0.18.3", + "postcss": "4.1.10", + "mensch": "0.3.1", + "stylus": "0.51.1", + "rework": "1.0.1", + "cssom": "0.3.0", + "less": "2.5.0", + "myth": "1.4.0", + "gulp": "3.8.11" + }, + "scripts": { + "test": "gulp" + } +} diff --git a/parsers.js b/parsers.js new file mode 100644 index 0000000..d8437e8 --- /dev/null +++ b/parsers.js @@ -0,0 +1,76 @@ +/* Results on Fedora 21, Intel 5Y70, 8 GB RAM and SSD: + +PostCSS: 37 ms +CSSOM: 38 ms (1.0 times slower) +Mensch: 39 ms (1.0 times slower) +Rework: 67 ms (1.8 times slower) +Stylecow: 120 ms (3.2 times slower) +Gonzales: 173 ms (4.6 times slower) +Gonzales PE: 935 ms (25.0 times slower) +*/ + +var path = require('path'); +var fs = require('fs'); + +var example = path.join(__dirname, 'cache', 'bootstrap.css'); +var css = fs.readFileSync(example).toString(); + +var CSSOM = require('cssom'); +var rework = require('rework'); +var mensch = require('mensch'); +var postcss = require('postcss'); +var stylecow = require('stylecow'); +var gonzales = require('gonzales'); +var gonzalesPe = require('gonzales-pe'); + +module.exports = { + name: 'Bootstrap', + maxTime: 15, + tests: [ + { + name: 'Rework', + fn: function () { + rework(css).toString(); + } + }, + { + name: 'PostCSS', + defer: true, + fn: function (done) { + postcss.parse(css, { from: example }).toResult(); + done.resolve(); + } + }, + { + name: 'CSSOM', + fn: function () { + CSSOM.parse(css).toString(); + } + }, + { + name: 'Mensch', + fn: function () { + mensch.stringify( mensch.parse(css) ); + } + }, + { + name: 'Gonzales', + fn: function () { + gonzales.csspToSrc( gonzales.srcToCSSP(css) ); + } + }, + { + name: 'Gonzales PE', + fn: function () { + gonzalesPe.parse(css).toString(); + } + }, + { + name: 'Stylecow', + fn: function () { + var file = new stylecow.Reader(new stylecow.Tokens(css)); + stylecow.Root.create(file).toString(); + } + } + ] +}; diff --git a/preprocessors.js b/preprocessors.js new file mode 100644 index 0000000..a1b0814 --- /dev/null +++ b/preprocessors.js @@ -0,0 +1,173 @@ +/* Results on io.js 2.0, Fedora 21, Intel 5Y70, 8 GB RAM and SSD: + +PostCSS: 36 ms +Rework: 77 ms (2.1 times slower) +libsass: 136 ms (3.8 times slower) +Less: 160 ms (4.4 times slower) +Stylus: 167 ms (4.6 times slower) +Stylecow: 208 ms (5.7 times slower) +Ruby Sass: 1084 ms (30.1 times slower) +*/ + +var exec = require('child_process').exec; +var path = require('path'); +var fs = require('fs'); + +var example = path.join(__dirname, 'cache', 'bootstrap.css'); +var css = fs.readFileSync(example).toString(); + +css = css.replace(/\s+filter:[^;\}]+;?/g, ''); +css = css.replace('/*# sourceMappingURL=bootstrap.css.map */', ''); + +// PostCSS +var postcss = require('postcss'); +var processor = postcss([ + require('postcss-nested'), + require('postcss-simple-vars'), + require('postcss-calc')(), + require('postcss-mixins') +]); +var pcss = css; +pcss += '$size: 100px;'; +pcss += '@define-mixin icon { width: 16px; height: 16px; }'; +for ( var i = 0; i < 100; i++ ) { + pcss += 'body { h1 { a { color: black; } } }'; + pcss += 'h2 { width: $size; }'; + pcss += 'h1 { width: calc(2 * $size); }'; + pcss += '.search { fill: black; @mixin icon; }'; +} + +// Myth +var myth = require('myth'); +var rcss = css; +rcss += ':root { --size: 100px; }'; +for ( var i = 0; i < 100; i++ ) { + rcss += 'body h1 a { color: black; }'; + rcss += 'h2 { width: var(--size); }'; + rcss += 'h1 { width: calc(2 * var(--size)); }'; + rcss += '.search { fill: black; width: 16px; height: 16px; }'; +} + +// Stylecow +var stylecow = require('stylecow'); +stylecow.loadPlugin('variables').loadPlugin('nested-rules').loadPlugin('calc'); +var cowcss = css; +cowcss += ':root { --size: 100px; }'; +for ( var i = 0; i < 100; i++ ) { + cowcss += 'body { h1 { a { color: black; } } }'; + cowcss += 'h2 { width: var(--size); }'; + cowcss += 'h1 { width: calc(2 * var(--size)); }'; + cowcss += '.search { fill: black; width: 16px; height: 16px; }'; +} + +// Sass +var libsass = require('node-sass'); +var scss = css; +scss += '$size: 100px;'; +scss += '@mixin icon { width: 16px; height: 16px; }'; +for ( var i = 0; i < 100; i++ ) { + scss += 'body { h1 { a { color: black; } } }'; + scss += 'h2 { width: $size; }'; + scss += 'h1 { width: 2 * $size; }'; + scss += '.search { fill: black; @include icon; }'; +} +var scssFile = path.join(__dirname, 'cache', 'bootstrap.scss'); +fs.writeFileSync(scssFile, scss); + +// Stylus +var stylus = require('stylus'); +var styl = css; +styl += 'size = 100px;'; +styl += 'icon() { width: 16px; height: 16px; }'; +for ( var i = 0; i < 100; i++ ) { + styl += 'body { h1 { a { color: black; } } }'; + styl += 'h2 { width: size; }'; + styl += 'h1 { width: 2 * size; }'; + styl += '.search { fill: black; icon(); }'; +} + +// Less +var less = require('less'); +var lcss = css; +lcss += '@size: 100px;'; +lcss += '.icon() { width: 16px; height: 16px; }'; +for ( var i = 0; i < 100; i++ ) { + lcss += 'body { h1 { a { color: black; } } }'; + lcss += 'h2 { width: @size; }'; + lcss += 'h1 { width: 2 * @size; }'; + lcss += '.search { fill: black; .icon() }'; +} + +module.exports = { + name: 'Bootstrap', + maxTime: 15, + tests: [ + { + name: 'libsass', + fn: function () { + libsass.renderSync({ data: scss }); + } + }, + { + name: 'Rework', + defer: true, + fn: function (done) { + myth(rcss, { features: { prefixes: false } }); + done.resolve(); + } + }, + { + name: 'PostCSS', + defer: true, + fn: function (done) { + processor.process(pcss, { map: false }).then(function () { + done.resolve(); + }); + } + }, + { + name: 'Stylecow', + defer: true, + fn: function (done) { + var file = new stylecow.Reader(new stylecow.Tokens(cowcss)); + var ast = stylecow.Root.create(file); + stylecow.run(ast); + ast.toString(); + done.resolve(); + } + }, + { + name: 'Stylus', + defer: true, + fn: function (done) { + stylus.render(styl, { filename: example }, function (err) { + if ( err ) throw err; + done.resolve(); + }); + } + }, + { + name: 'Less', + defer: true, + fn: function (done) { + less.render(lcss, function (err) { + if ( err ) throw err; + done.resolve(); + }); + } + }, + { + name: 'Ruby Sass', + defer: true, + fn: function (done) { + var command = 'sass -C --sourcemap=none ' + scssFile; + var dir = __dirname; + exec('cd ' + dir + '; bundle exec ' + command, + function (error, stdout, stderr) { + if ( error ) throw stderr; + done.resolve(); + }); + } + } + ] +};