diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index 4a1578ada5..c470ab548e 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -160,57 +160,47 @@ export default function mustache(parser: Parser) { parser.stack.push(block.else); } - } else if (parser.eat(':then')) { - // TODO DRY out this and the next section - const pending_block = parser.current(); - if (pending_block.type === 'PendingBlock') { - pending_block.end = start; - parser.stack.pop(); - const await_block = parser.current(); + } else if (parser.match(':then') || parser.match(':catch')) { + const block = parser.current(); + const isThen = parser.eat(':then') || !parser.eat(':catch'); - if (!parser.eat('}')) { - parser.require_whitespace(); - await_block.value = parser.read_identifier(); - parser.allow_whitespace(); - parser.eat('}', true); + if (isThen) { + if (block.type !== 'PendingBlock') { + parser.error({ + code: `invalid-then-placement`, + message: 'Cannot have an {:then} block outside an {#await ...} block' + }); } - - const then_block: TemplateNode = { - start, - end: null, - type: 'ThenBlock', - children: [], - skip: false - }; - - await_block.then = then_block; - parser.stack.push(then_block); - } - } else if (parser.eat(':catch')) { - const then_block = parser.current(); - if (then_block.type === 'ThenBlock') { - then_block.end = start; - parser.stack.pop(); - const await_block = parser.current(); - - if (!parser.eat('}')) { - parser.require_whitespace(); - await_block.error = parser.read_identifier(); - parser.allow_whitespace(); - parser.eat('}', true); + } else { + if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { + parser.error({ + code: `invalid-catch-placement`, + message: 'Cannot have an {:catch} block outside an {#await ...} block' + }); } + } - const catch_block: TemplateNode = { - start, - end: null, - type: 'CatchBlock', - children: [], - skip: false - }; + block.end = start; + parser.stack.pop(); + const await_block = parser.current(); - await_block.catch = catch_block; - parser.stack.push(catch_block); + if (!parser.eat('}')) { + parser.require_whitespace(); + await_block[isThen ? 'value': 'error'] = parser.read_identifier(); + parser.allow_whitespace(); + parser.eat('}', true); } + + const new_block: TemplateNode = { + start, + end: null, + type: isThen ? 'ThenBlock': 'CatchBlock', + children: [], + skip: false + }; + + await_block[isThen ? 'then' : 'catch'] = new_block; + parser.stack.push(new_block); } else if (parser.eat('#')) { // {#if foo}, {#each foo} or {#await foo} let type; diff --git a/test/parser/samples/await-catch/input.svelte b/test/parser/samples/await-catch/input.svelte new file mode 100644 index 0000000000..207bacb5b1 --- /dev/null +++ b/test/parser/samples/await-catch/input.svelte @@ -0,0 +1,5 @@ +{#await thePromise} +

loading...

+{:catch theError} +

oh no! {theError.message}

+{/await} \ No newline at end of file diff --git a/test/parser/samples/await-catch/output.json b/test/parser/samples/await-catch/output.json new file mode 100644 index 0000000000..2461f467f2 --- /dev/null +++ b/test/parser/samples/await-catch/output.json @@ -0,0 +1,168 @@ +{ + "html": { + "start": 0, + "end": 99, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 99, + "type": "AwaitBlock", + "expression": { + "type": "Identifier", + "start": 8, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "name": "thePromise" + }, + "value": null, + "error": "theError", + "pending": { + "start": 19, + "end": 39, + "type": "PendingBlock", + "children": [ + { + "start": 19, + "end": 21, + "type": "Text", + "raw": "\n\t", + "data": "\n\t" + }, + { + "start": 21, + "end": 38, + "type": "Element", + "name": "p", + "attributes": [], + "children": [ + { + "start": 24, + "end": 34, + "type": "Text", + "raw": "loading...", + "data": "loading..." + } + ] + }, + { + "start": 38, + "end": 39, + "type": "Text", + "raw": "\n", + "data": "\n" + } + ], + "skip": false + }, + "then": { + "start": null, + "end": null, + "type": "ThenBlock", + "children": [], + "skip": true + }, + "catch": { + "start": 39, + "end": 91, + "type": "CatchBlock", + "children": [ + { + "start": 56, + "end": 58, + "type": "Text", + "raw": "\n\t", + "data": "\n\t" + }, + { + "start": 58, + "end": 90, + "type": "Element", + "name": "p", + "attributes": [], + "children": [ + { + "start": 61, + "end": 68, + "type": "Text", + "raw": "oh no! ", + "data": "oh no! " + }, + { + "start": 68, + "end": 86, + "type": "MustacheTag", + "expression": { + "type": "MemberExpression", + "start": 69, + "end": 85, + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 4, + "column": 28 + } + }, + "object": { + "type": "Identifier", + "start": 69, + "end": 77, + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 4, + "column": 20 + } + }, + "name": "theError" + }, + "property": { + "type": "Identifier", + "start": 78, + "end": 85, + "loc": { + "start": { + "line": 4, + "column": 21 + }, + "end": { + "line": 4, + "column": 28 + } + }, + "name": "message" + }, + "computed": false + } + } + ] + }, + { + "start": 90, + "end": 91, + "type": "Text", + "raw": "\n", + "data": "\n" + } + ], + "skip": false + } + } + ] + } +} \ No newline at end of file diff --git a/test/parser/samples/error-catch-without-await/error.json b/test/parser/samples/error-catch-without-await/error.json new file mode 100644 index 0000000000..86cf64a673 --- /dev/null +++ b/test/parser/samples/error-catch-without-await/error.json @@ -0,0 +1,10 @@ +{ + "code": "invalid-catch-placement", + "message": "Cannot have an {:catch} block outside an {#await ...} block", + "start": { + "line": 1, + "column": 7, + "character": 7 + }, + "pos": 7 +} diff --git a/test/parser/samples/error-catch-without-await/input.svelte b/test/parser/samples/error-catch-without-await/input.svelte new file mode 100644 index 0000000000..ee2fab68e0 --- /dev/null +++ b/test/parser/samples/error-catch-without-await/input.svelte @@ -0,0 +1 @@ +{:catch theValue} diff --git a/test/parser/samples/error-then-without-await/error.json b/test/parser/samples/error-then-without-await/error.json new file mode 100644 index 0000000000..62d9cbc3c9 --- /dev/null +++ b/test/parser/samples/error-then-without-await/error.json @@ -0,0 +1,10 @@ +{ + "code": "invalid-then-placement", + "message": "Cannot have an {:then} block outside an {#await ...} block", + "start": { + "line": 1, + "column": 6, + "character": 6 + }, + "pos": 6 +} diff --git a/test/parser/samples/error-then-without-await/input.svelte b/test/parser/samples/error-then-without-await/input.svelte new file mode 100644 index 0000000000..4b7fb6c677 --- /dev/null +++ b/test/parser/samples/error-then-without-await/input.svelte @@ -0,0 +1 @@ +{:then theValue}