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

Fix regarding issue 2967 #3037

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 45 additions & 42 deletions packages/pug-lexer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ Lexer.prototype = {

tok: function(type, val){
var res = {
type: type,
type: type,
loc: {
start: {
line: this.lineno,
line: this.lineno,
column: this.colno
},
filename: this.filename
Expand All @@ -122,15 +122,15 @@ Lexer.prototype = {

return res;
},

/**
* Set the token's `loc.end` value.
*
*
* @param {Object} tok
* @returns {Object}
* @api private
*/

tokEnd: function(tok){
tok.loc.end = {
line: this.lineno,
Expand Down Expand Up @@ -1052,7 +1052,7 @@ Lexer.prototype = {
return true;
}
},

/**
* Attribute Name.
*/
Expand All @@ -1061,7 +1061,7 @@ Lexer.prototype = {
var quoteRe = /['"]/;
var key = '';
var i;

// consume all whitespace before the key
for(i = 0; i < str.length; i++){
if(!this.whitespaceRe.test(str[i])) break;
Expand All @@ -1071,23 +1071,26 @@ Lexer.prototype = {
this.incrementColumn(1);
}
}

if(i === str.length){
return '';
}

var tok = this.tok('attribute');

// quote?
if(quoteRe.test(str[i])){
quote = str[i];
this.incrementColumn(1);
i++;
}else if(characterParser.isPunctuator(str[i]) && str[i] !== ':'){
console.warn(this.filename + ', line ' + tok.loc.start.line + ', attribute name starts with invalid character "' + str[i] +
'", to use special characters in attribute names, wrap the attribute name in double quotes ""')
}

// start looping through the key
for (; i < str.length; i++) {

if(quote){
if (str[i] === quote) {
this.incrementColumn(1);
Expand All @@ -1099,20 +1102,20 @@ Lexer.prototype = {
break;
}
}

key += str[i];

if (str[i] === '\n') {
this.incrementLine(1);
} else {
this.incrementColumn(1);
}
}

tok.name = key;

var valueResponse = this.attributeValue(str.substr(i));

if (valueResponse.val) {
tok.val = valueResponse.val;
tok.mustEscape = valueResponse.mustEscape;
Expand All @@ -1121,11 +1124,11 @@ Lexer.prototype = {
tok.val = true;
tok.mustEscape = true;
}

str = valueResponse.remainingSource;

this.tokens.push(this.tokEnd(tok));

for(i = 0; i < str.length; i++){
if(!this.whitespaceRe.test(str[i])) {
break;
Expand All @@ -1136,15 +1139,15 @@ Lexer.prototype = {
this.incrementColumn(1);
}
}

if(str[i] === ','){
this.incrementColumn(1);
i++;
}

return str.substr(i);
},

/**
* Attribute Value.
*/
Expand All @@ -1156,7 +1159,7 @@ Lexer.prototype = {
var state = characterParser.defaultState();
var col = this.colno;
var line = this.lineno;

// consume all whitespace before the equals sign
for(i = 0; i < str.length; i++){
if(!this.whitespaceRe.test(str[i])) break;
Expand All @@ -1167,18 +1170,18 @@ Lexer.prototype = {
col++;
}
}

if(i === str.length){
return { remainingSource: str };
}

if(str[i] === '!'){
escapeAttr = false;
col++;
i++;
if (str[i] !== '=') this.error('INVALID_KEY_CHARACTER', 'Unexpected character ' + str[i] + ' expected `=`');
}

if(str[i] !== '='){
// check for anti-pattern `div("foo"bar)`
if (i === 0 && str && !this.whitespaceRe.test(str[0]) && str[0] !== ','){
Expand All @@ -1187,11 +1190,11 @@ Lexer.prototype = {
return { remainingSource: str };
}
}

this.lineno = line;
this.colno = col + 1;
i++;

// consume all whitespace before the value
for(; i < str.length; i++){
if(!this.whitespaceRe.test(str[i])) break;
Expand All @@ -1201,18 +1204,18 @@ Lexer.prototype = {
this.incrementColumn(1);
}
}

line = this.lineno;
col = this.colno;

// start looping through the value
for (; i < str.length; i++) {
// if the character is in a string or in parentheses/brackets/braces
if (!(state.isNesting() || state.isString())){

if (this.whitespaceRe.test(str[i])) {
done = false;

// find the first non-whitespace character
for (x = i; x < str.length; x++) {
if (!this.whitespaceRe.test(str[x])) {
Expand All @@ -1224,47 +1227,47 @@ Lexer.prototype = {
break;
}
}

// if everything else is whitespace, return now so last attribute
// does not include trailing whitespace
if(done || x === str.length){
break;
}
}

// if there's no whitespace and the character is not ',', the
// attribute did not end.
if(str[i] === ',' && this.assertExpression(val, true)){
break;
}
}

state = characterParser.parseChar(str[i], state);
val += str[i];

if (str[i] === '\n') {
line++;
col = 1;
} else {
col++;
}
}

this.assertExpression(val);

this.lineno = line;
this.colno = col;

return { val: val, mustEscape: escapeAttr, remainingSource: str.substr(i) };
},

/**
* Attributes.
*/

attrs: function() {
var tok;

if ('(' == this.input.charAt(0)) {
tok = this.tok('start-attributes');
var index = this.bracketExpression().end;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exports[`test invalid attribute name should log warning 1`] = `
Array [
"/home/travis/build/pugjs/pug/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid-character.pug, line 1, attribute name starts with invalid character "{", to use special characters in attribute names, wrap the attribute name in double quotes """,
]
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
p({{bar}}) foo
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const pug = require('../../../');

test('invalid attribute name should log warning', () => {
const oldWarn = console.warn;
const warnings = [];
console.warn = warnings.push.bind(warnings);
pug.compileFile(
__dirname + '/invalid-character.pug'
);
console.warn = oldWarn;
warnings.map(warning => warning.replace(/\\/g, '/').split(process.cwd().replace(/\\/g, '/')).join('<cwd>'));
expect(warnings).toMatchSnapshot();
});