diff --git a/packages/pug-lexer/index.js b/packages/pug-lexer/index.js index 434e3a048..79c2f244f 100644 --- a/packages/pug-lexer/index.js +++ b/packages/pug-lexer/index.js @@ -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 @@ -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, @@ -1052,7 +1052,7 @@ Lexer.prototype = { return true; } }, - + /** * Attribute Name. */ @@ -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; @@ -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); @@ -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; @@ -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; @@ -1136,15 +1139,15 @@ Lexer.prototype = { this.incrementColumn(1); } } - + if(str[i] === ','){ this.incrementColumn(1); i++; } - + return str.substr(i); }, - + /** * Attribute Value. */ @@ -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; @@ -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] !== ','){ @@ -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; @@ -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])) { @@ -1224,24 +1227,24 @@ 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; @@ -1249,22 +1252,22 @@ Lexer.prototype = { 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; diff --git a/packages/pug/test/anti-cases/Invalid-Attribute-test/__snapshots__/invalid.test.js.snap b/packages/pug/test/anti-cases/Invalid-Attribute-test/__snapshots__/invalid.test.js.snap new file mode 100644 index 000000000..5fe8d75b7 --- /dev/null +++ b/packages/pug/test/anti-cases/Invalid-Attribute-test/__snapshots__/invalid.test.js.snap @@ -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 """, +] +`; diff --git a/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid-character.pug b/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid-character.pug new file mode 100644 index 000000000..43008b602 --- /dev/null +++ b/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid-character.pug @@ -0,0 +1 @@ +p({{bar}}) foo diff --git a/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid.test.js b/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid.test.js new file mode 100644 index 000000000..82ceeef9e --- /dev/null +++ b/packages/pug/test/anti-cases/Invalid-Attribute-test/invalid.test.js @@ -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('')); + expect(warnings).toMatchSnapshot(); +});