├── .gitignore ├── tests ├── results │ ├── css │ │ ├── header.absurd.css │ │ └── index.absurd.css │ ├── component │ │ ├── widget.absurd.html │ │ ├── widget.absurd.css │ │ ├── share.absurd.html │ │ └── share.absurd.css │ ├── html │ │ ├── page2.absurd.html │ │ └── page.absurd.html │ ├── compileComponent.absurd.html │ ├── compileComponent.absurd.css │ ├── compiledCSS.css │ └── compiledHTML.html └── data │ ├── css │ ├── header.css.js │ ├── local.plugin.js │ └── index.css.js │ ├── html │ ├── content.js │ ├── page2.html.js │ └── page.html.js │ ├── global.plugin.js │ └── component │ ├── widget.component.js │ └── share.component.js ├── .jshintrc ├── LICENSE-MIT ├── package.json ├── tasks └── absurd.js ├── Gruntfile.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | tmp 4 | -------------------------------------------------------------------------------- /tests/results/css/header.absurd.css: -------------------------------------------------------------------------------- 1 | .header { 2 | marginTop: 20px; 3 | padding: 0; 4 | } 5 | -------------------------------------------------------------------------------- /tests/results/component/widget.absurd.html: -------------------------------------------------------------------------------- 1 |
2 |

Spread the world.

What's up 3 |
-------------------------------------------------------------------------------- /tests/results/component/widget.absurd.css: -------------------------------------------------------------------------------- 1 | .social-widget { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | .social-widget a { 6 | color: #000; 7 | } 8 | -------------------------------------------------------------------------------- /tests/results/component/share.absurd.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
-------------------------------------------------------------------------------- /tests/data/css/header.css.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.add({ 3 | '.header': { 4 | marginTop: '20px', 5 | padding: 0 6 | } 7 | }) 8 | } -------------------------------------------------------------------------------- /tests/results/html/page2.absurd.html: -------------------------------------------------------------------------------- 1 |
2 |

AbsurdJS is awesome

3 |

Yes, it is ...

4 |

Just anothe page

5 |
-------------------------------------------------------------------------------- /tests/results/component/share.absurd.css: -------------------------------------------------------------------------------- 1 | .share p { 2 | line-height: 20px; 3 | margin: 0; 4 | background: linear-gradient(to bottom, red, yellow, blue); 5 | } 6 | -------------------------------------------------------------------------------- /tests/data/css/local.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.storage("mixin", function(px) { 3 | return { 4 | fontSize: px + 'px' 5 | } 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /tests/results/compileComponent.absurd.html: -------------------------------------------------------------------------------- 1 |

Spread the world.

What's up
-------------------------------------------------------------------------------- /tests/data/html/content.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | return { 3 | '.content': { 4 | p: "Hello World!", 5 | 'a[href="http://krasimir.github.io/absurd/"]': 'Official AbsurdJS site' 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/results/compileComponent.absurd.css: -------------------------------------------------------------------------------- 1 | .share p{line-height: 20px;background: linear-gradient(to bottom,red,yellow,blue);}.share p,.social-widget{margin: 0;}.social-widget{padding: 0;}.social-widget a{color: #000;} -------------------------------------------------------------------------------- /tests/data/html/page2.html.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.add({ 3 | header:{ 4 | h1: "AbsurdJS is awesome", 5 | h2: "Yes, it is ...", 6 | p: "Just anothe page" 7 | } 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /tests/data/global.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.plugin('my-custom-gradient', function(api, colors) { 3 | return { 4 | background: 'linear-gradient(to bottom, ' + colors.join(", ") + ')' 5 | }; 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "node": true 13 | } 14 | -------------------------------------------------------------------------------- /tests/data/html/page.html.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.add({ 3 | body: { 4 | header:{ 5 | h1: "AbsurdJS is awesome", 6 | h2: "Yes, it is ..." 7 | }, 8 | 'section.container': require(__dirname + "/content.js")() 9 | } 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /tests/results/css/index.absurd.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100%; 3 | height: 100%; 4 | margin: 0; 5 | padding: 0; 6 | background: linear-gradient(to bottom, red, yellow, blue); 7 | } 8 | .header { 9 | height: 100%; 10 | backgroundColor: white; 11 | fontSize: 18px; 12 | } 13 | -------------------------------------------------------------------------------- /tests/results/compiledCSS.css: -------------------------------------------------------------------------------- 1 | .header { 2 | margin-top: 20px; 3 | background-color: white; 4 | font-size: 18px; 5 | } 6 | .header, body { 7 | padding: 0; 8 | height: 100%; 9 | } 10 | body { 11 | width: 100%; 12 | margin: 0; 13 | background: linear-gradient(to bottom, red, yellow, blue); 14 | } 15 | -------------------------------------------------------------------------------- /tests/results/compiledHTML.html: -------------------------------------------------------------------------------- 1 |

AbsurdJS is awesome

Yes, it is ...

Hello World!

Official AbsurdJS site

AbsurdJS is awesome

Yes, it is ...

Just anothe page

-------------------------------------------------------------------------------- /tests/data/component/widget.component.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.add({ 3 | css: { 4 | '.social-widget': { 5 | margin: 0, 6 | padding: 0, 7 | a: { 8 | color: '#000' 9 | } 10 | } 11 | }, 12 | html: { 13 | '.social-widget': { 14 | p: "Spread the world.", 15 | a: "What's up" 16 | } 17 | } 18 | }) 19 | } -------------------------------------------------------------------------------- /tests/results/html/page.absurd.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

AbsurdJS is awesome

4 |

Yes, it is ...

5 |
6 |
7 |
8 |

Hello World!

Official AbsurdJS site 9 |
10 |
11 | -------------------------------------------------------------------------------- /tests/data/css/index.css.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.add({ 3 | body: { 4 | width: '100%', 5 | height: '100%', 6 | margin: 0, 7 | padding: 0, 8 | 'my-custom-gradient': ['red','yellow', 'blue'] 9 | }, 10 | '.header': [ 11 | { 12 | height: '100%', 13 | backgroundColor: 'white' 14 | }, 15 | api.storage('mixin')(18) 16 | ] 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /tests/data/component/share.component.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.add({ 3 | css: { 4 | '.share': { 5 | p: { 6 | lineHeight: "20px", 7 | margin: 0, 8 | 'my-custom-gradient': ['red','yellow', 'blue'] 9 | } 10 | } 11 | }, 12 | html: { 13 | '.share': { 14 | ul: [ 15 | { li: "Option A" }, 16 | { li: "Option B" } 17 | ] 18 | } 19 | } 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Krasimir Tsonev 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-absurd", 3 | "description": "Grunt plugin for AbsurdJS", 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/krasimir/grunt-absurd.git", 6 | "author": { 7 | "name": "Krasimir Tsonev", 8 | "email": "krasimir@outset.ws", 9 | "url": "http://krasimirtsonev.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git@github.com:krasimir/grunt-absurd.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/krasimir/grunt-absurd/issues" 17 | }, 18 | "licenses": [ 19 | { 20 | "type": "MIT", 21 | "url": "https://github.com/krasimir/grunt-absurd.git/blob/master/LICENSE-MIT" 22 | } 23 | ], 24 | "main": "Gruntfile.js", 25 | "engines": { 26 | "node": ">= 0.8.0" 27 | }, 28 | "scripts": { 29 | "test": "grunt test" 30 | }, 31 | "devDependencies": { 32 | "grunt-contrib-jshint": "~0.6.0", 33 | "grunt": "~0.4.1", 34 | "grunt-contrib-clean": "^0.5.0" 35 | }, 36 | "peerDependencies": { 37 | "grunt": ">=0.4.0" 38 | }, 39 | "dependencies": { 40 | "absurd": "*", 41 | "chalk": "^0.4.0" 42 | }, 43 | "keywords": [ 44 | "gruntplugin", 45 | "grunt", 46 | "absurd" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /tasks/absurd.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-absurd 3 | * https://github.com/krasimir/grunt-absurd.git 4 | * 5 | * Copyright (c) 2013 Krasimir Tsonev 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | var path = require('path'), 13 | chalk = require('chalk'), 14 | Absurd = require('absurd'), 15 | api; 16 | 17 | grunt.registerMultiTask('absurd', 'Grunt plugin for AbsurdJS', function() { 18 | // Merge task-specific and/or target-specific options with these defaults. 19 | var options = this.options({ 20 | require: [], 21 | morph: null, 22 | combineSelectors: true, 23 | minify: false, 24 | keepCamelCase: false, 25 | extCSS: '.css', 26 | extHTML: '.html' 27 | }); 28 | 29 | // Iterate over all src-dest file pairs 30 | this.files.forEach(function(f) { 31 | // validate src files 32 | var srcFiles = f.src.filter(function(filepath) { 33 | if (!grunt.file.exists(filepath)) { 34 | grunt.log.warn('Source file ' + chalk.cyan(filepath) + ' not found.'); 35 | return false; 36 | } else { 37 | return true; 38 | } 39 | }); 40 | 41 | if (srcFiles.length === 0) { 42 | grunt.log.warn('Destination ' + chalk.cyan(f.dest) + ' not written because src files were empty. '); 43 | return; 44 | } 45 | 46 | api = Absurd(); 47 | 48 | api.morph(options.morph); 49 | 50 | if (options.require.length > 0) { 51 | api.import(grunt.file.expand(options.require).map(function(file){ 52 | return path.resolve(file); 53 | })); 54 | } 55 | 56 | api.import(srcFiles.map(function(file){ 57 | return path.resolve(file); 58 | })); 59 | 60 | api.compile(function(err,A,B) { 61 | if (err) { 62 | grunt.log.error('Absurd:' + err); 63 | return; 64 | } 65 | 66 | if (options.morph === 'component') { 67 | grunt.file.write(path.resolve(f.dest) + options.extCSS, A); 68 | grunt.verbose.writeln('File ' + chalk.cyan(f.dest + options.extCSS) + ' created.'); 69 | grunt.file.write(path.resolve(f.dest) + options.extHTML, B); 70 | grunt.verbose.writeln('File ' + chalk.cyan(f.dest + options.extHTML) + ' created.'); 71 | } else { 72 | grunt.file.write(path.resolve(f.dest), A); 73 | grunt.verbose.writeln('File ' + chalk.cyan(f.dest) + ' created.'); 74 | } 75 | }, options); 76 | }); 77 | }); 78 | }; 79 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | // Project configuration. 3 | grunt.initConfig({ 4 | testSrc: 'tests/data', 5 | testResult: 'tests/results', 6 | 7 | clean: ['<%= testResult %>'], 8 | 9 | absurd: { 10 | buildCSS: { 11 | options: { 12 | require: ['<%= testSrc %>/**/*.plugin.js'], 13 | minify: false, 14 | combineSelectors: false, 15 | keepCamelCase: true 16 | }, 17 | expand: true, 18 | cwd: '<%= testSrc %>', 19 | src: ['**/*.css.js'], 20 | dest: '<%= testResult %>', 21 | ext: '.absurd.css' 22 | }, 23 | compileCSS: { 24 | options: { 25 | require: ['<%= testSrc %>/**/*.plugin.js'], 26 | minify: false 27 | }, 28 | src: ['<%= testSrc %>/**/*.css.js'], 29 | dest: '<%= testResult %>/compiledCSS.css', 30 | }, 31 | buildHTML: { 32 | options: { 33 | minify: false, 34 | morph: 'html' 35 | }, 36 | expand: true, 37 | cwd: '<%= testSrc %>/', 38 | src: ['**/*.html.js'], 39 | dest: '<%= testResult %>', 40 | ext: '.absurd.html' 41 | }, 42 | compileHTML: { 43 | options: { 44 | minify: true, 45 | morph: 'html' 46 | }, 47 | src: ['<%= testSrc %>/**/*.html.js'], 48 | dest: '<%= testResult %>/compiledHTML.html', 49 | }, 50 | buildComponent: { 51 | options: { 52 | require: ['<%= testSrc %>/**/*.plugin.js'], 53 | minify: false, 54 | morph: 'component' 55 | }, 56 | expand: true, 57 | cwd: '<%= testSrc %>/', 58 | src: ['**/*.component.js'], 59 | dest: '<%= testResult %>', 60 | ext: '.absurd' 61 | }, 62 | compileComponent: { 63 | options: { 64 | require: ['<%= testSrc %>/**/*.plugin.js'], 65 | minify: true, 66 | morph: 'component' 67 | }, 68 | src: ['<%= testSrc %>/**/*.component.js'], 69 | dest: '<%= testResult %>/compileComponent.absurd' 70 | } 71 | }, 72 | 73 | jshint: { 74 | options: { 75 | curly: true, 76 | eqeqeq: true, 77 | immed: true, 78 | latedef: true, 79 | newcap: false, 80 | noarg: true, 81 | sub: true, 82 | undef: true, 83 | boss: true, 84 | eqnull: true, 85 | node: true, 86 | globals: {} 87 | }, 88 | 89 | files: ['Gruntfile.js', 'tasks/**/*.js'] 90 | } 91 | }); 92 | 93 | // Load local tasks. 94 | grunt.loadTasks('tasks'); 95 | 96 | grunt.loadNpmTasks('grunt-contrib-jshint'); 97 | grunt.loadNpmTasks('grunt-contrib-clean'); 98 | 99 | // Default task. 100 | grunt.registerTask('default', ['jshint', 'clean', 'absurd']); 101 | }; 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-absurd 2 | 3 | > Grunt plugin for AbsurdJS ([https://github.com/krasimir/absurd](https://github.com/krasimir/absurd)) 4 | 5 | ## Getting Started 6 | This plugin requires Grunt `~0.4.1` 7 | 8 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 9 | 10 | ```shell 11 | npm install grunt-absurd 12 | ``` 13 | 14 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 15 | 16 | ```js 17 | grunt.loadNpmTasks('grunt-absurd'); 18 | ``` 19 | 20 | ## The "absurd" task 21 | 22 | ### Overview 23 | In your project's Gruntfile, add a section named `absurd` to the data object passed into `grunt.initConfig()`. 24 | 25 | ```js 26 | grunt.initConfig({ 27 | absurd: { 28 | task: { 29 | src: 'path to your .js file', 30 | dest: 'path to your .css file', 31 | options: { 32 | minify: true, 33 | } 34 | } 35 | }, 36 | }) 37 | ``` 38 | 39 | ### Options 40 | 41 | #### require 42 | 43 | Type: `Array` 44 | Default: `[]` 45 | 46 | A list of files loaded before process the src files. 47 | 48 | #### extCSS 49 | 50 | Type: `String` 51 | Default: `'.css'` 52 | 53 | The css file extension used in the output of `morph: "component"` 54 | 55 | #### extHTML 56 | 57 | Type: `String` 58 | Default: `'.html'` 59 | 60 | The html file extension used in the output of `morph: "component"` 61 | 62 | #### Other Absurd Options 63 | 64 | ##### morph 65 | 66 | Type: `null | 'html' | 'component'` 67 | Default: `null` 68 | 69 | ##### combineSelectors 70 | 71 | Type: `Boolean` 72 | Default: `true` 73 | 74 | ##### minify 75 | 76 | Type: `Boolean` 77 | Default: `false` 78 | 79 | ##### keepCamelCase 80 | 81 | Type: `Boolean` 82 | Default: `false` 83 | 84 | ### Usage example 85 | 86 | ```js 87 | ... 88 | grunt.initConfig({ 89 | testSrc: 'tests/data', 90 | testResult: 'tests/results', 91 | 92 | absurd: { 93 | buildCSS: { 94 | options: { 95 | require: ['<%= testSrc %>/**/*.plugin.js'], 96 | minify: false, 97 | combineSelectors: false, 98 | keepCamelCase: true 99 | }, 100 | expand: true, 101 | cwd: '<%= testSrc %>', 102 | src: ['**/*.css.js'], 103 | dest: '<%= testResult %>', 104 | ext: '.absurd.css' 105 | }, 106 | compileCSS: { 107 | options: { 108 | require: ['<%= testSrc %>/**/*.plugin.js'], 109 | minify: false 110 | }, 111 | src: ['<%= testSrc %>/**/*.css.js'], 112 | dest: '<%= testResult %>/compiledCSS.css', 113 | }, 114 | buildHTML: { 115 | options: { 116 | minify: false, 117 | morph: 'html' 118 | }, 119 | expand: true, 120 | cwd: '<%= testSrc %>/', 121 | src: ['**/*.html.js'], 122 | dest: '<%= testResult %>', 123 | ext: '.absurd.html' 124 | }, 125 | compileHTML: { 126 | options: { 127 | minify: true, 128 | morph: 'html' 129 | }, 130 | src: ['<%= testSrc %>/**/*.html.js'], 131 | dest: '<%= testResult %>/compiledHTML.html', 132 | }, 133 | buildComponent: { 134 | options: { 135 | require: ['<%= testSrc %>/**/*.plugin.js'], 136 | minify: false, 137 | morph: 'component' 138 | }, 139 | expand: true, 140 | cwd: '<%= testSrc %>/', 141 | src: ['**/*.component.js'], 142 | dest: '<%= testResult %>', 143 | ext: '.absurd' 144 | }, 145 | compileComponent: { 146 | options: { 147 | require: ['<%= testSrc %>/**/*.plugin.js'], 148 | minify: true, 149 | morph: 'component' 150 | }, 151 | src: ['<%= testSrc %>/**/*.component.js'], 152 | dest: '<%= testResult %>/compileComponent.absurd' 153 | } 154 | }, 155 | ... 156 | ``` 157 | 158 | Use [dynamic_mappings](http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically) to create separate output files. use `static_mappings` to create single output file. 159 | --------------------------------------------------------------------------------