├── .editorconfig ├── .gitignore ├── Readme.md ├── index.js ├── lib ├── compiler.js └── filters.js ├── package.json └── test ├── mocha.opts ├── test-attributes.js ├── test-compiler.js └── test-filters.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Jade-PHP 2 | 3 | Adds the possibility of compiling PHP for Jade 4 | 5 | ## Usage 6 | 7 | var jade = require('jade'); 8 | var jadephp = require('jade-php'); 9 | 10 | jadephp(jade); 11 | 12 | var html = jade.render('string of jade'); 13 | 14 | ## Example 15 | 16 | The following code: 17 | 18 | !!! 19 | html 20 | head 21 | title= $title 22 | 23 | body 24 | ul 25 | - foreach ($this->list as $list): 26 | li!= $list 27 | - endforeach 28 | 29 | Will produce: 30 | 31 | 32 | 33 | 34 | <?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?> 35 | 36 | 37 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = function (jade) { 2 | if (typeof jade === 'undefined') { 3 | jade = require('jade'); 4 | } 5 | require('./lib/filters')(jade); 6 | require('./lib/compiler')(jade); 7 | }; 8 | -------------------------------------------------------------------------------- /lib/compiler.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (jade) { 3 | var isConstant = require('constantinople'); 4 | if (!jade) { 5 | jade = require('jade'); 6 | } 7 | 8 | var characterParser = require('character-parser'); 9 | 10 | function assertExpression(exp) { 11 | // this verifies that a JavaScript expression is valid 12 | // Fix this for php 13 | return true; 14 | } 15 | function assertNestingCorrect(exp) { 16 | //this verifies that code is properly nested, but allows 17 | //invalid JavaScript such as the contents of `attributes` 18 | var res = characterParser(exp) 19 | if (res.isNesting()) { 20 | throw new Error('Nesting must match on expression `' + exp + '`') 21 | } 22 | } 23 | 24 | // Precisa sobrescrever para retirar validação JS 25 | jade.Lexer.prototype.code = function () { 26 | var captures; 27 | if (captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input)) { 28 | this.consume(captures[0].length); 29 | var flags = captures[1]; 30 | captures[1] = captures[2]; 31 | var tok = this.tok('code', captures[1]); 32 | tok.escape = flags.charAt(0) === '='; 33 | tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '='; 34 | // if (tok.buffer) assertExpression(captures[1]) 35 | return tok; 36 | } 37 | }; 38 | 39 | jade.Lexer.prototype.attrs = function() { 40 | if ('(' == this.input.charAt(0)) { 41 | var index = this.bracketExpression().end 42 | , str = this.input.substr(1, index-1) 43 | , tok = this.tok('attrs') 44 | , equals = this.colons ? ':' : '='; 45 | 46 | if (equals === ':') { 47 | console.warn('`:` in jade is deprecated, please use `=`'); 48 | } 49 | 50 | assertNestingCorrect(str); 51 | 52 | var quote = ''; 53 | function interpolate(attr) { 54 | return attr.replace(/(\\)?#\{(.+)/g, function(_, escape, expr){ 55 | if (escape) return _; 56 | try { 57 | var range = characterParser.parseMax(expr); 58 | if (expr[range.end] !== '}') return _.substr(0, 2) + interpolate(_.substr(2)); 59 | assertExpression(range.src) 60 | return quote + " + (" + range.src + ") + " + quote + interpolate(expr.substr(range.end + 1)); 61 | } catch (ex) { 62 | return _.substr(0, 2) + interpolate(_.substr(2)); 63 | } 64 | }); 65 | } 66 | 67 | this.consume(index + 1); 68 | tok.attrs = {}; 69 | tok.escaped = {}; 70 | 71 | var escapedAttr = true 72 | var key = ''; 73 | var val = ''; 74 | var interpolatable = ''; 75 | var state = characterParser.defaultState(); 76 | var loc = 'key'; 77 | function isEndOfAttribute(i) { 78 | if (key.trim() === '') return false; 79 | if (i === str.length) return true; 80 | if (loc === 'key') { 81 | if (str[i] === ' ' || str[i] === '\n') { 82 | for (var x = i; x < str.length; x++) { 83 | if (str[x] != ' ' && str[x] != '\n') { 84 | if (str[x] === '=' || str[x] === '!' || str[x] === ',') return false; 85 | else return true; 86 | } 87 | } 88 | } 89 | return str[i] === ',' 90 | } else if (loc === 'value' && !state.isNesting()) { 91 | try { 92 | Function('', 'return (' + val + ');'); 93 | if (str[i] === ' ' || str[i] === '\n') { 94 | for (var x = i; x < str.length; x++) { 95 | if (str[x] != ' ' && str[x] != '\n') { 96 | if (characterParser.isPunctuator(str[x]) && str[x] != '"' && str[x] != "'") return false; 97 | else return true; 98 | } 99 | } 100 | } 101 | return str[i] === ','; 102 | } catch (ex) { 103 | return false; 104 | } 105 | } 106 | } 107 | for (var i = 0; i <= str.length; i++) { 108 | if (isEndOfAttribute(i)) { 109 | val = val.trim(); 110 | if (val) assertExpression(val) 111 | key = key.trim(); 112 | key = key.replace(/^['"]|['"]$/g, ''); 113 | tok.escaped[key] = escapedAttr; 114 | tok.attrs[key] = '' == val ? true : val; 115 | key = val = ''; 116 | loc = 'key'; 117 | escapedAttr = false; 118 | } else { 119 | switch (loc) { 120 | case 'key-char': 121 | if (str[i] === quote) { 122 | loc = 'key'; 123 | if (i + 1 < str.length && [' ', ',', '!', equals, '\n'].indexOf(str[i + 1]) === -1) 124 | throw new Error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`'); 125 | } else if (loc === 'key-char') { 126 | key += str[i]; 127 | } 128 | break; 129 | case 'key': 130 | if (key === '' && (str[i] === '"' || str[i] === "'")) { 131 | loc = 'key-char'; 132 | quote = str[i]; 133 | } else if (str[i] === '!' || str[i] === equals) { 134 | escapedAttr = str[i] !== '!'; 135 | if (str[i] === '!') i++; 136 | if (str[i] !== equals) throw new Error('Unexpected character ' + str[i] + ' expected `=`'); 137 | loc = 'value'; 138 | state = characterParser.defaultState(); 139 | } else { 140 | key += str[i] 141 | } 142 | break; 143 | case 'value': 144 | state = characterParser.parseChar(str[i], state); 145 | if (state.isString()) { 146 | loc = 'string'; 147 | quote = str[i]; 148 | interpolatable = str[i]; 149 | } else { 150 | val += str[i]; 151 | } 152 | break; 153 | case 'string': 154 | state = characterParser.parseChar(str[i], state); 155 | interpolatable += str[i]; 156 | if (!state.isString()) { 157 | loc = 'value'; 158 | val += interpolate(interpolatable); 159 | } 160 | break; 161 | } 162 | } 163 | } 164 | 165 | if ('/' == this.input.charAt(0)) { 166 | this.consume(1); 167 | tok.selfClosing = true; 168 | } 169 | 170 | return tok; 171 | } 172 | }, 173 | 174 | jade.Compiler.prototype.visitCode = function (code) { 175 | var val = code.val; 176 | 177 | if (code.buffer) { 178 | if (code.escape) { 179 | val = 'htmlspecialchars(' + val + ', ENT_QUOTES, \'UTF-8\')'; 180 | } 181 | 182 | val = 'echo ' + val + ';' 183 | } 184 | 185 | this.buffer(''); 186 | 187 | if (code.block) { 188 | if (!code.buffer) this.buf.push('{'); 189 | this.visit(code.block); 190 | if (!code.buffer) this.buf.push('}'); 191 | } 192 | }; 193 | 194 | jade.Compiler.prototype.attrs = function(attrs){ 195 | var buf = [] 196 | , classes = [] 197 | , escaped = {} 198 | , constant = attrs.every(function (attr) { return isConstant(attr.val) }) 199 | , inherits = false; 200 | 201 | if (this.terse) buf.push('terse: true'); 202 | 203 | attrs.forEach(function(attr){ 204 | if (attr.name == 'attributes') { 205 | return inherits = true; 206 | } 207 | 208 | var val = attr.val; 209 | if (!isConstant(val)) { 210 | if (attr.escaped) { 211 | val = 'htmlspecialchars(' + val + ', ENT_QUOTES, \'UTF-8\')'; 212 | } 213 | 214 | val = '""'; 215 | escaped[attr.name] = false; 216 | } else { 217 | escaped[attr.name] = attr.escaped; 218 | } 219 | 220 | if (attr.name == 'class') { 221 | classes.push('(' + val + ')'); 222 | } else { 223 | var pair = "'" + attr.name + "':(" + val + ')'; 224 | buf.push(pair); 225 | } 226 | }); 227 | 228 | if (classes.length) { 229 | buf.push('"class": [' + classes.join(',') + ']'); 230 | } 231 | 232 | return { 233 | buf: buf.join(', '), 234 | escaped: JSON.stringify(escaped), 235 | inherits: inherits, 236 | constant: constant 237 | }; 238 | }; 239 | 240 | }; 241 | -------------------------------------------------------------------------------- /lib/filters.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (jade) { 3 | if (!jade) { 4 | jade = require('jade'); 5 | } 6 | 7 | jade.filters.php = function (text) { 8 | return ''; 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jade-php", 3 | "version": "0.1.4", 4 | "description": "jade compiler and filter for PHP", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "keywords": [ 10 | "jade", 11 | "filter", 12 | "php" 13 | ], 14 | "author": "Vinicius Wrubleski ", 15 | "license": "MIT", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/viniwrubleski/jade-php.git" 19 | }, 20 | "devDependencies": { 21 | "mocha": "~1.14.0" 22 | }, 23 | "dependencies": { 24 | "constantinople": "~1.0.1", 25 | "character-parser": "~1.2.0", 26 | "jade": "~0.35.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter list 2 | -------------------------------------------------------------------------------- /test/test-attributes.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var jade = require('jade'); 3 | require('../lib/compiler.js')(jade); 4 | 5 | describe('php attribute compiler', function () { 6 | it('should compile attributes', function () { 7 | var html = jade.render('a(href=test(), id!=test2(), data-teste="teste", class=$test, data-class= $obj->errors()).test Test'); 8 | assert.equal(html, 9 | 'Test' 13 | ); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/test-compiler.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var jade = require('jade'); 3 | require('../lib/compiler.js')(jade); 4 | 5 | describe('php compiler', function () { 6 | it('should compile lines', function () { 7 | var html = jade.render('- echo \'teste\';'); 8 | assert.equal(html, ''); 9 | }); 10 | 11 | describe('should replace', function () { 12 | it('escaped values', function () { 13 | var html = jade.render('title= \'teste\''); 14 | assert.equal(html, '<?php echo htmlspecialchars(\'teste\', ENT_QUOTES, \'UTF-8\'); ?>'); 15 | }); 16 | 17 | it('unescaped values', function () { 18 | var html = jade.render('title!= \'teste\''); 19 | assert.equal(html, '<?php echo \'teste\'; ?>'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/test-filters.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var jade = require('jade'); 3 | require('../lib/filters.js')(jade); 4 | 5 | describe('php filter', function () { 6 | it('should apply', function () { 7 | var html = jade.render(':php\n\techo \'test filter!\';'); 8 | assert.equal(html, ''); 9 | }); 10 | 11 | it('can handle multiple lines', function () { 12 | var html = jade.render(':php\n\techo \'test filter!\';\n\techo \'test filter2!\';'); 13 | assert.equal(html, ''); 14 | }); 15 | }); 16 | --------------------------------------------------------------------------------