Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test cases for spread attributes #52

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
87 changes: 63 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,7 @@ Lexer.prototype = {

var whitespaceRe = /[ \n\t]/;
var quoteRe = /['"]/;
var spreadRe = /^\.\.\./;

var escapedAttr = true
var key = '';
Expand All @@ -981,16 +982,38 @@ Lexer.prototype = {
var lineno = startingLine;
var colno = this.colno;
var loc = 'key';
var incrementColumn = function (i) {
if (str[i] === '\n') {
// Save the line number locally to keep this.lineno at the start of
// the attribute.
lineno++;
self.colno = 1;
// If the key has not been started, update this.lineno
// immediately.
if (!key) self.lineno = lineno;
} else if (str[i] !== undefined) {
self.incrementColumn(1);
}
};
var skipWhitespace = function (i) {
if (whitespaceRe.test(str[i])) {
for (; i < str.length; i++) {
if (!whitespaceRe.test(str[i])) break;
incrementColumn(i);
}
}
return i;
};
var isEndOfAttribute = function (i) {
// if the key is not started, then the attribute cannot be ended
if (key.trim() === '') {
colno = this.colno;
return false;
}
if (key === '') return false;
// if there's nothing more then the attribute must be ended
if (i === str.length) return true;

if (loc === 'key') {
// if the spread attribute has just started the attribute cannot be
// ended
if (loc === 'spread') return false;
else if (loc === 'key') {
if (whitespaceRe.test(str[i])) {
// find the first non-whitespace character
for (var x = i; x < str.length; x++) {
Expand Down Expand Up @@ -1019,7 +1042,8 @@ Lexer.prototype = {
if (!whitespaceRe.test(str[x])) {
// if it is a JavaScript punctuator, then assume that it is
// a part of the value
return !characterParser.isPunctuator(str[x]) || quoteRe.test(str[x]);
// also make exception for spread syntax
return spreadRe.test(str.slice(x)) || !characterParser.isPunctuator(str[x]) || quoteRe.test(str[x]);
}
}
}
Expand All @@ -1044,18 +1068,27 @@ Lexer.prototype = {
this.error('COLON_ATTRIBUTE', '":" is not valid as the start or end of an un-quoted attribute.');
}
key = key.trim();
key = key.replace(/^['"]|['"]$/g, '');

var tok = this.tok('attribute');
tok.name = key;
tok.val = '' == val ? true : val;
tok.col = colno;
var tok;
if (key === '...') {
tok = this.tok('spread-attribute');
tok.val = val;
if (!val) {
return this.error('EMPTY_SPREAD_ATTRIBUTE', 'A spread attribute must have a value.')
}
} else {
key = key.replace(/^['"]|['"]$/g, '');
tok = this.tok('attribute');
tok.name = key;
tok.val = '' == val ? true : val;
}
tok.mustEscape = escapedAttr;
tok.col = colno;
this.tokens.push(tok);

key = val = '';
loc = 'key';
escapedAttr = false;
escapedAttr = true;
this.lineno = lineno;
} else {
switch (loc) {
Expand All @@ -1064,14 +1097,18 @@ Lexer.prototype = {
loc = 'key';
if (i + 1 < str.length && !/[ ,!=\n\t]/.test(str[i + 1]))
this.error('INVALID_KEY_CHARACTER', 'Unexpected character "' + str[i + 1] + '" expected ` `, `\\n`, `\t`, `,`, `!` or `=`');
} else {
key += str[i];
}
key += str[i];
break;
case 'key':
if (key === '') {
i = skipWhitespace(i);
colno = this.colno;
}
if (key === '' && quoteRe.test(str[i])) {
loc = 'key-char';
quote = str[i];
key += str[i];
} else if (str[i] === '!' || str[i] === '=') {
escapedAttr = str[i] !== '!';
if (str[i] === '!') {
Expand All @@ -1084,23 +1121,25 @@ Lexer.prototype = {
} else {
key += str[i]
}
if (key === '...') loc = 'spread';
break;
case 'spread':
if (str[i] === '!') {
escapedAttr = false;
loc = 'value';
break;
} else if (!whitespaceRe.test(str[i])) {
loc = 'value';
}
// no cleaner ways to do so, so has to use a
// fallthrough
case 'value':
state = characterParser.parseChar(str[i], state);
val += str[i];
break;
}
}
if (str[i] === '\n') {
// Save the line number locally to keep this.lineno at the start of
// the attribute.
lineno++;
this.colno = 1;
// If the key has not been started, update this.lineno immediately.
if (!key.trim()) this.lineno = lineno;
} else if (str[i] !== undefined) {
this.incrementColumn(1);
}
incrementColumn(i);
}

// Reset the line numbers based on the line started on
Expand Down
48 changes: 48 additions & 0 deletions test/cases/attrs-spread.expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{"type":"tag","line":1,"col":1,"val":"div"}
{"type":"start-attributes","line":1,"col":4}
{"type":"spread-attribute","line":1,"col":5,"val":"attrs","mustEscape":true}
{"type":"end-attributes","line":1,"col":13}
{"type":"newline","line":2,"col":1}
{"type":"tag","line":2,"col":1,"val":"div"}
{"type":"start-attributes","line":2,"col":4}
{"type":"attribute","line":2,"col":5,"name":"a","val":"'bleh'","mustEscape":true}
{"type":"spread-attribute","line":2,"col":15,"val":"attrs","mustEscape":true}
{"type":"end-attributes","line":2,"col":23}
{"type":"newline","line":3,"col":1}
{"type":"tag","line":3,"col":1,"val":"div"}
{"type":"start-attributes","line":3,"col":4}
{"type":"attribute","line":3,"col":5,"name":"a","val":"'bleh'","mustEscape":true}
{"type":"spread-attribute","line":3,"col":14,"val":"attrs","mustEscape":true}
{"type":"end-attributes","line":3,"col":22}
{"type":"newline","line":4,"col":1}
{"type":"tag","line":4,"col":1,"val":"div"}
{"type":"start-attributes","line":4,"col":4}
{"type":"attribute","line":4,"col":5,"name":"a","val":true,"mustEscape":true}
{"type":"spread-attribute","line":4,"col":7,"val":"attrs","mustEscape":true}
{"type":"end-attributes","line":4,"col":15}
{"type":"newline","line":5,"col":1}
{"type":"tag","line":5,"col":1,"val":"div"}
{"type":"start-attributes","line":5,"col":4}
{"type":"attribute","line":5,"col":5,"name":"a","val":true,"mustEscape":true}
{"type":"spread-attribute","line":5,"col":7,"val":"attrs","mustEscape":true}
{"type":"end-attributes","line":5,"col":16}
{"type":"newline","line":6,"col":1}
{"type":"tag","line":6,"col":1,"val":"div"}
{"type":"start-attributes","line":6,"col":4}
{"type":"spread-attribute","line":6,"col":5,"val":"attrs","mustEscape":false}
{"type":"end-attributes","line":6,"col":14}
{"type":"newline","line":7,"col":1}
{"type":"tag","line":7,"col":1,"val":"div"}
{"type":"start-attributes","line":7,"col":4}
{"type":"spread-attribute","line":7,"col":5,"val":"attrs","mustEscape":false}
{"type":"end-attributes","line":9,"col":7}
{"type":"newline","line":10,"col":1}
{"type":"tag","line":10,"col":1,"val":"div"}
{"type":"start-attributes","line":10,"col":4}
{"type":"attribute","line":10,"col":5,"name":"...attrs","val":true,"mustEscape":true}
{"type":"spread-attribute","line":10,"col":16,"val":"attrs","mustEscape":true}
{"type":"attribute","line":10,"col":26,"name":"...attrs","val":"'val'","mustEscape":true}
{"type":"attribute","line":10,"col":44,"name":"...","val":true,"mustEscape":true}
{"type":"end-attributes","line":10,"col":49}
{"type":"newline","line":11,"col":1}
{"type":"eos","line":11,"col":1}
10 changes: 10 additions & 0 deletions test/cases/attrs-spread.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
div(...attrs)
div(a='bleh', ...attrs)
div(a='bleh' ...attrs)
div(a ...attrs)
div(a ... attrs)
div(...!attrs)
div(...
!
attrs)
div("...attrs" ...attrs, '...attrs' ='val' "...")
28 changes: 14 additions & 14 deletions test/cases/attrs.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
{"type":"tag","line":3,"col":1,"val":"a"}
{"type":"start-attributes","line":3,"col":2}
{"type":"attribute","line":3,"col":3,"name":"foo","val":true,"mustEscape":true}
{"type":"attribute","line":3,"col":8,"name":"bar","val":true,"mustEscape":false}
{"type":"attribute","line":3,"col":13,"name":"baz","val":true,"mustEscape":false}
{"type":"attribute","line":3,"col":8,"name":"bar","val":true,"mustEscape":true}
{"type":"attribute","line":3,"col":13,"name":"baz","val":true,"mustEscape":true}
{"type":"end-attributes","line":3,"col":16}
{"type":"newline","line":4,"col":1}
{"type":"tag","line":4,"col":1,"val":"a"}
Expand All @@ -35,7 +35,7 @@
{"type":"tag","line":7,"col":3,"val":"option"}
{"type":"start-attributes","line":7,"col":9}
{"type":"attribute","line":7,"col":10,"name":"value","val":"'foo'","mustEscape":true}
{"type":"attribute","line":7,"col":23,"name":"selected","val":true,"mustEscape":false}
{"type":"attribute","line":7,"col":23,"name":"selected","val":true,"mustEscape":true}
{"type":"end-attributes","line":7,"col":31}
{"type":"text","line":7,"col":33,"val":"Foo"}
{"type":"newline","line":8,"col":1}
Expand Down Expand Up @@ -72,8 +72,8 @@
{"type":"tag","line":14,"col":1,"val":"a"}
{"type":"start-attributes","line":14,"col":2}
{"type":"attribute","line":14,"col":3,"name":"foo","val":true,"mustEscape":true}
{"type":"attribute","line":14,"col":7,"name":"bar","val":true,"mustEscape":false}
{"type":"attribute","line":14,"col":11,"name":"baz","val":true,"mustEscape":false}
{"type":"attribute","line":14,"col":7,"name":"bar","val":true,"mustEscape":true}
{"type":"attribute","line":14,"col":11,"name":"baz","val":true,"mustEscape":true}
{"type":"end-attributes","line":14,"col":14}
{"type":"newline","line":15,"col":1}
{"type":"tag","line":15,"col":1,"val":"a"}
Expand All @@ -93,7 +93,7 @@
{"type":"tag","line":18,"col":3,"val":"option"}
{"type":"start-attributes","line":18,"col":9}
{"type":"attribute","line":18,"col":10,"name":"value","val":"'foo'","mustEscape":true}
{"type":"attribute","line":18,"col":22,"name":"selected","val":true,"mustEscape":false}
{"type":"attribute","line":18,"col":22,"name":"selected","val":true,"mustEscape":true}
{"type":"end-attributes","line":18,"col":30}
{"type":"text","line":18,"col":32,"val":"Foo"}
{"type":"newline","line":19,"col":1}
Expand Down Expand Up @@ -127,37 +127,37 @@
{"type":"tag","line":25,"col":1,"val":"foo"}
{"type":"start-attributes","line":25,"col":4}
{"type":"attribute","line":25,"col":5,"name":"abc","val":true,"mustEscape":true}
{"type":"attribute","line":26,"col":5,"name":"def","val":true,"mustEscape":false}
{"type":"attribute","line":26,"col":5,"name":"def","val":true,"mustEscape":true}
{"type":"end-attributes","line":26,"col":8}
{"type":"newline","line":27,"col":1}
{"type":"tag","line":27,"col":1,"val":"foo"}
{"type":"start-attributes","line":27,"col":4}
{"type":"attribute","line":27,"col":5,"name":"abc","val":true,"mustEscape":true}
{"type":"attribute","line":28,"col":5,"name":"def","val":true,"mustEscape":false}
{"type":"attribute","line":28,"col":5,"name":"def","val":true,"mustEscape":true}
{"type":"end-attributes","line":28,"col":8}
{"type":"newline","line":29,"col":1}
{"type":"tag","line":29,"col":1,"val":"foo"}
{"type":"start-attributes","line":29,"col":4}
{"type":"attribute","line":29,"col":5,"name":"abc","val":true,"mustEscape":true}
{"type":"attribute","line":30,"col":3,"name":"def","val":true,"mustEscape":false}
{"type":"attribute","line":30,"col":3,"name":"def","val":true,"mustEscape":true}
{"type":"end-attributes","line":30,"col":6}
{"type":"newline","line":31,"col":1}
{"type":"tag","line":31,"col":1,"val":"foo"}
{"type":"start-attributes","line":31,"col":4}
{"type":"attribute","line":31,"col":5,"name":"abc","val":true,"mustEscape":true}
{"type":"attribute","line":32,"col":4,"name":"def","val":true,"mustEscape":false}
{"type":"attribute","line":32,"col":4,"name":"def","val":true,"mustEscape":true}
{"type":"end-attributes","line":32,"col":7}
{"type":"newline","line":33,"col":1}
{"type":"tag","line":33,"col":1,"val":"foo"}
{"type":"start-attributes","line":33,"col":4}
{"type":"attribute","line":33,"col":5,"name":"abc","val":true,"mustEscape":true}
{"type":"attribute","line":34,"col":3,"name":"def","val":true,"mustEscape":false}
{"type":"attribute","line":34,"col":3,"name":"def","val":true,"mustEscape":true}
{"type":"end-attributes","line":34,"col":6}
{"type":"newline","line":35,"col":1}
{"type":"tag","line":35,"col":1,"val":"foo"}
{"type":"start-attributes","line":35,"col":4}
{"type":"attribute","line":35,"col":5,"name":"abc","val":true,"mustEscape":true}
{"type":"attribute","line":36,"col":5,"name":"def","val":true,"mustEscape":false}
{"type":"attribute","line":36,"col":5,"name":"def","val":true,"mustEscape":true}
{"type":"end-attributes","line":36,"col":8}
{"type":"newline","line":38,"col":1}
{"type":"code","line":38,"col":1,"val":"var attrs = {foo: 'bar', bar: '<baz>'}","mustEscape":false,"buffer":false}
Expand All @@ -168,13 +168,13 @@
{"type":"tag","line":42,"col":1,"val":"a"}
{"type":"start-attributes","line":42,"col":2}
{"type":"attribute","line":42,"col":3,"name":"foo","val":"'foo'","mustEscape":true}
{"type":"attribute","line":42,"col":14,"name":"bar","val":"\"bar\"","mustEscape":true}
{"type":"attribute","line":42,"col":13,"name":"bar","val":"\"bar\"","mustEscape":true}
{"type":"end-attributes","line":42,"col":24}
{"type":"newline","line":43,"col":1}
{"type":"tag","line":43,"col":1,"val":"a"}
{"type":"start-attributes","line":43,"col":2}
{"type":"attribute","line":43,"col":3,"name":"foo","val":"'foo'","mustEscape":true}
{"type":"attribute","line":43,"col":14,"name":"bar","val":"'bar'","mustEscape":true}
{"type":"attribute","line":43,"col":13,"name":"bar","val":"'bar'","mustEscape":true}
{"type":"end-attributes","line":43,"col":24}
{"type":"newline","line":44,"col":1}
{"type":"eos","line":44,"col":1}
2 changes: 1 addition & 1 deletion test/cases/html5.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{"type":"tag","line":2,"col":1,"val":"input"}
{"type":"start-attributes","line":2,"col":6}
{"type":"attribute","line":2,"col":7,"name":"type","val":"'checkbox'","mustEscape":true}
{"type":"attribute","line":2,"col":24,"name":"checked","val":true,"mustEscape":false}
{"type":"attribute","line":2,"col":24,"name":"checked","val":true,"mustEscape":true}
{"type":"end-attributes","line":2,"col":31}
{"type":"newline","line":3,"col":1}
{"type":"tag","line":3,"col":1,"val":"input"}
Expand Down
2 changes: 1 addition & 1 deletion test/cases/mixin.attrs.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
{"type":"outdent","line":41,"col":1}
{"type":"call","line":41,"col":1,"val":"work_filmstrip_item","args":"'work'"}
{"type":"start-attributes","line":41,"col":29}
{"type":"attribute","line":41,"col":31,"name":"data-profile","val":"'profile'","mustEscape":true}
{"type":"attribute","line":41,"col":30,"name":"data-profile","val":"'profile'","mustEscape":true}
{"type":"attribute","line":41,"col":56,"name":"data-creator-name","val":"'name'","mustEscape":true}
{"type":"end-attributes","line":41,"col":82}
{"type":"newline","line":43,"col":1}
Expand Down
4 changes: 2 additions & 2 deletions test/cases/source.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
{"type":"tag","line":2,"col":3,"val":"audio"}
{"type":"start-attributes","line":2,"col":8}
{"type":"attribute","line":2,"col":9,"name":"preload","val":"'auto'","mustEscape":true}
{"type":"attribute","line":2,"col":25,"name":"autobuffer","val":true,"mustEscape":false}
{"type":"attribute","line":2,"col":37,"name":"controls","val":true,"mustEscape":false}
{"type":"attribute","line":2,"col":25,"name":"autobuffer","val":true,"mustEscape":true}
{"type":"attribute","line":2,"col":37,"name":"controls","val":true,"mustEscape":true}
{"type":"end-attributes","line":2,"col":45}
{"type":"indent","line":3,"col":1,"val":4}
{"type":"tag","line":3,"col":5,"val":"source"}
Expand Down
2 changes: 1 addition & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fs.readdirSync(dir).forEach(function (testCase) {
if (/\.pug$/.test(testCase)) {
console.dir(testCase);
var expected = fs.readFileSync(dir + testCase.replace(/\.pug$/, '.expected.json'), 'utf8')
.split(/\n/).map(JSON.parse);
.trim().split(/\n/).map(JSON.parse);
var result = lex(fs.readFileSync(dir + testCase, 'utf8'), dir + testCase);
fs.writeFileSync(dir + testCase.replace(/\.pug$/, '.actual.json'),
result.map(JSON.stringify).join('\n'));
Expand Down