├── .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 |
35 |
36 |
37 |
38 |
39 | list as $list): ?>
40 |
41 |
42 |
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 | '" data-teste="teste" ' +
11 | 'data-class="errors(), ENT_QUOTES, \'UTF-8\'); ?>" ' +
12 | 'class=" test">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, '');
15 | });
16 |
17 | it('unescaped values', function () {
18 | var html = jade.render('title!= \'teste\'');
19 | assert.equal(html, '');
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 |
--------------------------------------------------------------------------------