diff --git a/lib/lexer.js b/lib/lexer.js index 11c6685..cd286fa 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -144,14 +144,15 @@ module.exports = function Lexer(input, options = {}) { // we're in attribute land until the *close paren* while (char !== ')') { // attempt to get the attribute key + // - it can't contain */* to avoid malformed html (*/* will be skipped) // - if it has a value, it will end at the *=* // - if it's boolean, it will end at the *paren* or *space* (next attr) const key = collectUntil('=|)|\\s/') - - // if we do have a key, add it to our tokens, otherwise move on + + // if we do have a key, add it to our tokens, otherwise if we have a space or invalid '/' char in attr name move on (example : attr = 'foo') if (key.length) { addToken('attributeKey', key) - } else { + } else if (char && (char.match(/\s/) || char.match(/\//)) && !char.match(/\n/)) { next() } @@ -168,17 +169,17 @@ module.exports = function Lexer(input, options = {}) { next() } - // now we grab the value, it ends at a *space*, *close paren*, or + // now we grab the value, it ends at a *space*, *close paren*, *newline* or // a *close quote*. if it's quoted though, we don't match *space*. // this is because you can have div(class='foo bar') let regex = '\\s|)|\'|"' if (quoted) { - regex = quoted + regex = quoted + '|\n' } val += collectUntil(regex) // if there is no next character, we have a hanging open quote - if (!char) { + if (!char || (quoted && char.match(/\n/))) { throw new SugarmlError({ message: `Unclosed attribute quote`, location: { @@ -191,13 +192,17 @@ module.exports = function Lexer(input, options = {}) { } // if there's a close quote, move past it - if (char.match(/['"]/)) next() + if (quoted) next() } // if we did match a value, push it to tokens if (val.length) addToken('attributeValue', val) - if (!char) { + // if we have a *space*, move on to the next attribute + if (char && char.match(/\s/) && !char.match(/\n/)) next() + + //If we run out of chars or char or next char are a new line when attribute list isn't closed, that's a syntax error + if (!char || char.match(/\n/) || (char !== ')' && nextChar() && nextChar().match(/\n/))) { throw new SugarmlError({ message: `Unclosed attribute parentheses`, location: { @@ -209,11 +214,9 @@ module.exports = function Lexer(input, options = {}) { }) } - // if we have a *space*, move on to the next attribute - if (char.match(/\s/)) next() } // done with attributes, move past the *close paren* - if (char === ')') next() + next() } } diff --git a/test/fixtures/unclosed-attribute-paren.sgr b/test/fixtures/unclosed-attribute-paren.sgr index 6c6ae71..2267aca 100644 --- a/test/fixtures/unclosed-attribute-paren.sgr +++ b/test/fixtures/unclosed-attribute-paren.sgr @@ -1 +1,5 @@ -a(b +ul + li( + img(src=foo + li: img(src='bar' + li(src='') diff --git a/test/fixtures/unclosed-attribute-quote.sgr b/test/fixtures/unclosed-attribute-quote.sgr index 9c4aa8c..6ad6a98 100644 --- a/test/fixtures/unclosed-attribute-quote.sgr +++ b/test/fixtures/unclosed-attribute-quote.sgr @@ -1 +1 @@ -a(href='a +a(href='a)