Skip to content

Commit

Permalink
Support @compose in CSS files directly
Browse files Browse the repository at this point in the history
  • Loading branch information
spencer516 committed Jan 10, 2017
1 parent fac3c3b commit 402d836
Show file tree
Hide file tree
Showing 18 changed files with 190 additions and 22 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default Component.extend({
```

```css
/* app/styles/app.css-compose */
/* app/styles/app.css */
@composer {
@a-list-component {
composes: 'my-list', 'background-g3', 'font-size-big';
Expand Down Expand Up @@ -88,7 +88,7 @@ In the event that you need to dynamically generate class names, you can directly
utility to convert a key into a list of class names. For example:

```css
/* app/styles/app.css-compose */
/* app/styles/app.css */
@composer {
@myComponent-red {
composes: 'red';
Expand Down
11 changes: 10 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const LOOKUP_OUTPUT = 'css-classes-json.js';
const CSStoJSON = require('./lib/css-plugin/index');
const CleanCSSCompose = require('./lib/css-plugin/clean-composer');
const BabelPlugin = require('./lib/babel-plugin/index');
const BabelTranspiler = require('broccoli-babel-transpiler');
const MergeTrees = require('broccoli-merge-trees');
Expand All @@ -17,6 +18,14 @@ module.exports = {
return;
}

registry.add('css', {
name: 'ember-css-composer-clean-css',
toTree: function(inputTree) {
return new CleanCSSCompose(inputTree);
}
});


registry.add('js', {
name: 'ember-css-composer-babel',
toTree: function(inputTree) {
Expand All @@ -40,7 +49,7 @@ module.exports = {
var addonTree = this._super.treeForAddon.call(this, tree);

let cssComposeTree = new Funnel(this.app.trees.app, {
include: ['**/*.css-compose']
include: ['**/*.css']
});

let configFileTree = CSStoJSON(cssComposeTree, {
Expand Down
33 changes: 33 additions & 0 deletions lib/css-plugin/clean-composer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const postcss = require('postcss');
const Filter = require('broccoli-persistent-filter');

CSSStripCompose.prototype = Object.create(Filter.prototype);
CSSStripCompose.prototype.constructor = CSSStripCompose;
CSSStripCompose.prototype.extensions = ['css'];
CSSStripCompose.prototype.targetExtension = 'css';

function CSSStripCompose(inputTree, options = {}) {
if (!(this instanceof CSSStripCompose)) {
return new CSSStripCompose(inputTree, options);
}

Filter.call(this, inputTree, options);
}

CSSStripCompose.prototype.processString = function(content) {
return removeComposesDecls(content);
};

function removeComposesDecls(content) {
let ast = postcss.parse(content);

ast.walkAtRules((node) => {
if (node.name === 'composer') {
node.remove();
}
});

return ast.toResult().css;
}

module.exports = CSSStripCompose;
17 changes: 17 additions & 0 deletions node-test/fixtures/css-clean-plugin/test-1/input/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@composer {
@test-1 {
composes: 'class-a', 'class-b', 'class-c';
}

@test-2 {
composes: 'class-d','class-e';
}
}

.class-a {
background: red;
}

.class-b {
background: green;
}
7 changes: 7 additions & 0 deletions node-test/fixtures/css-clean-plugin/test-1/output/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.class-a {
background: red;
}

.class-b {
background: green;
}
13 changes: 13 additions & 0 deletions node-test/fixtures/css-clean-plugin/test-2/input/app-2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.class-e {
font-size: 12px;
}

@composer {
@test-2 {
composes: 'class-d','class-e';
}
}

.other-styles {
color: blue;
}
13 changes: 13 additions & 0 deletions node-test/fixtures/css-clean-plugin/test-2/input/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@composer {
@test-1 {
composes: 'class-a', 'class-b', 'class-c';
}
}

.class-a {
background: red;
}

.class-b {
background: green;
}
7 changes: 7 additions & 0 deletions node-test/fixtures/css-clean-plugin/test-2/output/app-2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.class-e {
font-size: 12px;
}

.other-styles {
color: blue;
}
7 changes: 7 additions & 0 deletions node-test/fixtures/css-clean-plugin/test-2/output/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.class-a {
background: red;
}

.class-b {
background: green;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
@test-2 {
composes: 'class-d','class-e';
}
}
}
28 changes: 28 additions & 0 deletions node-test/helpers/clean-compose-css-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const broccoli = require('broccoli');
const plugin = require('../../lib/css-plugin/clean-composer');
const fs = require('fs');
const path = require('path');
const filesPath = 'node-test/fixtures/css-clean-plugin';
const outputFile = 'some/file/tmp.js';

module.exports = {
transform(treePath) {
let tree = plugin(`${filesPath}/${treePath}/input`, { outputFile });
let builder = new broccoli.Builder(tree);

return builder.build().then(() => {
return filesMap(builder.outputPath);
});
},
getExpectedResult(path) {
let fullPath = `${filesPath}/${path}/output`;
return filesMap(fullPath);
}
}

function filesMap(folder) {
return fs.readdirSync(folder).reduce((hash, file) => {
hash[file] = fs.readFileSync(path.join(folder, file), 'utf8');
return hash;
}, {});
}
33 changes: 33 additions & 0 deletions node-test/lib/css-plugin/clean-composer-nodetest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { transform, getExpectedResult } = require('../../helpers/clean-compose-css-file');
const should = require('should');

describe('css plugin', function() {
it('works with a single file', function() {
// Make sure it strips out any @compose declarations.
return transform('test-1').then((result) => {
let expected = getExpectedResult('test-1');
assertKeys(expected, result);
});
});

it('works with multiple files', function() {
// Make sure it strips out any @compose declarations.
return transform('test-2').then((result) => {
let expected = getExpectedResult('test-2');
assertKeys(expected, result);
});
});
});

function assertKeys(expectedHash, resultHash) {
let expectedKeys = Object.keys(expectedHash);
let resultKeys = Object.keys(resultHash);

// Assert the same keys
expectedKeys.should.deepEqual(resultKeys);

// Assert each of the values are the same
expectedKeys.forEach((key) => {
expectedHash[key].should.equal(resultHash[key]);
});
}
2 changes: 1 addition & 1 deletion node-test/lib/css-plugin/index-nodetest.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ describe('css plugin', function() {
result.should.equal(expected);
});
});
});
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"postcss": "^5.2.8"
},
"devDependencies": {
"babel-core": "^5.8.38",
"babel-preset-es2015": "^6.18.0",
"babelify": "^7.3.0",
"broccoli-asset-rev": "^2.4.5",
Expand Down
17 changes: 17 additions & 0 deletions tests/dummy/app/styles/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@composer {
@testing-basic {
composes: 'tb-1', 'tb-2';
}

@testing-basic__open {
composes: 'tb-3', 'tb-4';
}

@testing-basic__closed {
composes: 'tb-5';
}

@testing-basic__inner {
composes: 'tb-6', 'tb-7', 'tb-8';
}
}
17 changes: 0 additions & 17 deletions tests/dummy/app/styles/config.css-compose

This file was deleted.

0 comments on commit 402d836

Please sign in to comment.