From a31fea139b3c80ef503bf5d35ebb240a58a030a8 Mon Sep 17 00:00:00 2001 From: Li Hau Tan Date: Fri, 18 Oct 2019 17:59:50 +0800 Subject: [PATCH 1/2] then and catch block expect await, optional then block --- src/compiler/parse/state/mustache.ts | 92 +++++----- test/parser/samples/await-catch/input.svelte | 5 + test/parser/samples/await-catch/output.json | 168 ++++++++++++++++++ .../error-catch-without-await/error.json | 10 ++ .../error-catch-without-await/input.svelte | 1 + .../error-then-without-await/error.json | 10 ++ .../error-then-without-await/input.svelte | 1 + 7 files changed, 246 insertions(+), 41 deletions(-) create mode 100644 test/parser/samples/await-catch/input.svelte create mode 100644 test/parser/samples/await-catch/output.json create mode 100644 test/parser/samples/error-catch-without-await/error.json create mode 100644 test/parser/samples/error-catch-without-await/input.svelte create mode 100644 test/parser/samples/error-then-without-await/error.json create mode 100644 test/parser/samples/error-then-without-await/input.svelte diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index 4a1578ada5..d34b6a3b6c 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -163,54 +163,64 @@ export default function mustache(parser: Parser) { } 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(); - - if (!parser.eat('}')) { - parser.require_whitespace(); - await_block.value = parser.read_identifier(); - parser.allow_whitespace(); - parser.eat('}', true); - } + if (pending_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 - }; + pending_block.end = start; + parser.stack.pop(); + const await_block = parser.current(); - await_block.then = then_block; - parser.stack.push(then_block); + if (!parser.eat('}')) { + parser.require_whitespace(); + await_block.value = parser.read_identifier(); + parser.allow_whitespace(); + parser.eat('}', true); } - } 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); - } + 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 block = parser.current(); + 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.error = parser.read_identifier(); + parser.allow_whitespace(); + parser.eat('}', true); } + + const catch_block: TemplateNode = { + start, + end: null, + type: 'CatchBlock', + children: [], + skip: false + }; + + await_block.catch = catch_block; + parser.stack.push(catch_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} From 03d4bf48f9269ef9276bd1ed5f05f067807ffbf4 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 19 Oct 2019 00:32:38 +0800 Subject: [PATCH 2/2] DRY out :then and :catch --- src/compiler/parse/state/mustache.ts | 64 ++++++++++------------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index d34b6a3b6c..c470ab548e 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -160,44 +160,24 @@ 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') { - parser.error({ - code: `invalid-then-placement`, - message: 'Cannot have an {:then} block outside an {#await ...} block' - }); - } - - pending_block.end = start; - parser.stack.pop(); - const await_block = parser.current(); - - if (!parser.eat('}')) { - parser.require_whitespace(); - await_block.value = parser.read_identifier(); - parser.allow_whitespace(); - parser.eat('}', true); - } - - 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')) { + } else if (parser.match(':then') || parser.match(':catch')) { const block = parser.current(); - if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { - parser.error({ - code: `invalid-catch-placement`, - message: 'Cannot have an {:catch} block outside an {#await ...} block' - }); + const isThen = parser.eat(':then') || !parser.eat(':catch'); + + if (isThen) { + if (block.type !== 'PendingBlock') { + parser.error({ + code: `invalid-then-placement`, + message: 'Cannot have an {:then} block outside an {#await ...} block' + }); + } + } else { + if (block.type !== 'ThenBlock' && block.type !== 'PendingBlock') { + parser.error({ + code: `invalid-catch-placement`, + message: 'Cannot have an {:catch} block outside an {#await ...} block' + }); + } } block.end = start; @@ -206,21 +186,21 @@ export default function mustache(parser: Parser) { if (!parser.eat('}')) { parser.require_whitespace(); - await_block.error = parser.read_identifier(); + await_block[isThen ? 'value': 'error'] = parser.read_identifier(); parser.allow_whitespace(); parser.eat('}', true); } - const catch_block: TemplateNode = { + const new_block: TemplateNode = { start, end: null, - type: 'CatchBlock', + type: isThen ? 'ThenBlock': 'CatchBlock', children: [], skip: false }; - await_block.catch = catch_block; - parser.stack.push(catch_block); + 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;