diff --git a/src/parse/index.ts b/src/parse/index.ts index c81d2f61b0..91d7c3253d 100644 --- a/src/parse/index.ts +++ b/src/parse/index.ts @@ -112,14 +112,14 @@ export class Parser { throw new ParseError(message, this.template, index, this.filename); } - eat(str: string, required?: boolean) { + eat(str: string, required?: boolean, message?: string) { if (this.match(str)) { this.index += str.length; return true; } if (required) { - this.error(`Expected ${str}`); + this.error(message || `Expected ${str}`); } return false; diff --git a/src/parse/state/mustache.ts b/src/parse/state/mustache.ts index bd1db07460..0a255b1256 100644 --- a/src/parse/state/mustache.ts +++ b/src/parse/state/mustache.ts @@ -6,7 +6,7 @@ import { Parser } from '../index'; import { Node } from '../../interfaces'; function trimWhitespace(block: Node, trimBefore: boolean, trimAfter: boolean) { - if (!block.children) return; // AwaitBlock + if (!block.children || block.children.length === 0) return; // AwaitBlock const firstChild = block.children[0]; const lastChild = block.children[block.children.length - 1]; @@ -74,8 +74,6 @@ export default function mustache(parser: Parser) { } // strip leading/trailing whitespace as necessary - if (block.children && !block.children.length) parser.error(`Empty block`, block.start); - const charBefore = parser.template[block.start - 1]; const charAfter = parser.template[parser.index]; const trimBefore = !charBefore || whitespace.test(charBefore); diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index 8b701cae1d..57e2b1bbb9 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -71,7 +71,7 @@ export default function tag(parser: Parser) { if (parser.eat('!--')) { const data = parser.readUntil(/-->/); - parser.eat('-->'); + parser.eat('-->', true, 'comment was left open, expected -->'); parser.current().children.push({ start, diff --git a/src/validate/html/index.ts b/src/validate/html/index.ts index b2a190696f..9ae50baca1 100644 --- a/src/validate/html/index.ts +++ b/src/validate/html/index.ts @@ -12,6 +12,13 @@ const meta = new Map([ [':Head', validateHead] ]); +function isEmptyBlock(node: Node) { + if (!/Block$/.test(node.type) || !node.children) return false; + if (node.children.length > 1) return false; + const child = node.children[0]; + return !child || (child.type === 'Text' && !/\S/.test(child.data)); +} + export default function validateHtml(validator: Validator, html: Node) { const refs = new Map(); const refCallees: Node[] = []; @@ -58,6 +65,10 @@ export default function validateHtml(validator: Validator, html: Node) { } } + if (validator.options.dev && isEmptyBlock(node)) { + validator.warn('Empty block', node.start); + } + if (node.children) { if (node.type === 'Element') elementStack.push(node); stack.push(node); diff --git a/src/validate/index.ts b/src/validate/index.ts index b57f2b0c2a..da603a437b 100644 --- a/src/validate/index.ts +++ b/src/validate/index.ts @@ -93,7 +93,7 @@ export default function validate( stylesheet: Stylesheet, options: CompileOptions ) { - const { onwarn, onerror, name, filename, store } = options; + const { onwarn, onerror, name, filename, store, dev } = options; try { if (name && !/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(name)) { @@ -114,7 +114,8 @@ export default function validate( onwarn, name, filename, - store + store, + dev }); if (parsed.js) { diff --git a/test/parser/samples/error-comment-unclosed/error.json b/test/parser/samples/error-comment-unclosed/error.json new file mode 100644 index 0000000000..f392e12fa1 --- /dev/null +++ b/test/parser/samples/error-comment-unclosed/error.json @@ -0,0 +1,8 @@ +{ + "message": "comment was left open, expected -->", + "loc": { + "line": 1, + "column": 24 + }, + "pos": 24 +} diff --git a/test/parser/samples/error-comment-unclosed/input.html b/test/parser/samples/error-comment-unclosed/input.html new file mode 100644 index 0000000000..725cf9be60 --- /dev/null +++ b/test/parser/samples/error-comment-unclosed/input.html @@ -0,0 +1 @@ +