mirror of https://github.com/sveltejs/svelte
commit
d6814d76e4
@ -0,0 +1,155 @@
|
|||||||
|
import deindent from '../../../utils/deindent';
|
||||||
|
import visit from '../visit';
|
||||||
|
import { DomGenerator } from '../index';
|
||||||
|
import Block from '../Block';
|
||||||
|
import isDomNode from './shared/isDomNode';
|
||||||
|
import { Node } from '../../../interfaces';
|
||||||
|
import { State } from '../interfaces';
|
||||||
|
|
||||||
|
export default function visitAwaitBlock(
|
||||||
|
generator: DomGenerator,
|
||||||
|
block: Block,
|
||||||
|
state: State,
|
||||||
|
node: Node,
|
||||||
|
elementStack: Node[],
|
||||||
|
componentStack: Node[]
|
||||||
|
) {
|
||||||
|
const name = node.var;
|
||||||
|
|
||||||
|
const needsAnchor = node.next ? !isDomNode(node.next, generator) : !state.parentNode || !isDomNode(node.parent, generator);
|
||||||
|
const anchor = needsAnchor
|
||||||
|
? block.getUniqueName(`${name}_anchor`)
|
||||||
|
: (node.next && node.next.var) || 'null';
|
||||||
|
|
||||||
|
const params = block.params.join(', ');
|
||||||
|
|
||||||
|
block.contextualise(node.expression);
|
||||||
|
const { snippet } = node.metadata;
|
||||||
|
|
||||||
|
if (needsAnchor) {
|
||||||
|
block.addElement(
|
||||||
|
anchor,
|
||||||
|
`@createComment()`,
|
||||||
|
`@createComment()`,
|
||||||
|
state.parentNode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = block.getUniqueName(`promise`);
|
||||||
|
const resolved = block.getUniqueName(`resolved`);
|
||||||
|
const await_block = block.getUniqueName(`await_block`);
|
||||||
|
const await_block_type = block.getUniqueName(`await_block_type`);
|
||||||
|
const token = block.getUniqueName(`token`);
|
||||||
|
const await_token = block.getUniqueName(`await_token`);
|
||||||
|
const handle_promise = block.getUniqueName(`handle_promise`);
|
||||||
|
const replace_await_block = block.getUniqueName(`replace_await_block`);
|
||||||
|
const old_block = block.getUniqueName(`old_block`);
|
||||||
|
const value = block.getUniqueName(`value`);
|
||||||
|
const error = block.getUniqueName(`error`);
|
||||||
|
const create_pending_block = node.pending._block.name;
|
||||||
|
const create_then_block = node.then._block.name;
|
||||||
|
const create_catch_block = node.catch._block.name;
|
||||||
|
|
||||||
|
block.addVariable(await_block);
|
||||||
|
block.addVariable(await_block_type);
|
||||||
|
block.addVariable(await_token);
|
||||||
|
block.addVariable(promise);
|
||||||
|
block.addVariable(resolved);
|
||||||
|
|
||||||
|
block.builders.init.addBlock(deindent`
|
||||||
|
function ${replace_await_block}(${token}, type, ${value}, ${params}) {
|
||||||
|
if (${token} !== ${await_token}) return;
|
||||||
|
|
||||||
|
var ${old_block} = ${await_block};
|
||||||
|
${await_block} = (${await_block_type} = type)(${params}, ${resolved} = ${value}, #component);
|
||||||
|
|
||||||
|
if (${old_block}) {
|
||||||
|
${old_block}.u();
|
||||||
|
${old_block}.d();
|
||||||
|
${await_block}.c();
|
||||||
|
${await_block}.m(${anchor}.parentNode, ${anchor});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ${handle_promise}(${promise}, ${params}) {
|
||||||
|
var ${token} = ${await_token} = {};
|
||||||
|
|
||||||
|
if (@isPromise(${promise})) {
|
||||||
|
${promise}.then(function(${value}) {
|
||||||
|
${replace_await_block}(${token}, ${create_then_block}, ${value}, ${params});
|
||||||
|
}, function (${error}) {
|
||||||
|
${replace_await_block}(${token}, ${create_catch_block}, ${error}, ${params});
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we previously had a then/catch block, destroy it
|
||||||
|
if (${await_block_type} !== ${create_pending_block}) {
|
||||||
|
${replace_await_block}(${token}, ${create_pending_block}, null, ${params});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
${resolved} = ${promise};
|
||||||
|
if (${await_block_type} !== ${create_then_block}) {
|
||||||
|
${replace_await_block}(${token}, ${create_then_block}, ${resolved}, ${params});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${handle_promise}(${promise} = ${snippet}, ${params});
|
||||||
|
`);
|
||||||
|
|
||||||
|
block.builders.create.addBlock(deindent`
|
||||||
|
${await_block}.c();
|
||||||
|
`);
|
||||||
|
|
||||||
|
block.builders.claim.addBlock(deindent`
|
||||||
|
${await_block}.l(${state.parentNodes});
|
||||||
|
`);
|
||||||
|
|
||||||
|
const targetNode = state.parentNode || '#target';
|
||||||
|
const anchorNode = state.parentNode ? 'null' : 'anchor';
|
||||||
|
|
||||||
|
block.builders.mount.addBlock(deindent`
|
||||||
|
${await_block}.m(${targetNode}, ${anchorNode});
|
||||||
|
`);
|
||||||
|
|
||||||
|
const conditions = [];
|
||||||
|
if (node.metadata.dependencies) {
|
||||||
|
conditions.push(
|
||||||
|
`(${node.metadata.dependencies.map(dep => `'${dep}' in changed`).join(' || ')})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions.push(
|
||||||
|
`${promise} !== (${promise} = ${snippet})`,
|
||||||
|
`${handle_promise}(${promise}, ${params})`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (node.pending._block.hasUpdateMethod) {
|
||||||
|
block.builders.update.addBlock(deindent`
|
||||||
|
if (${conditions.join(' && ')}) {
|
||||||
|
// nothing
|
||||||
|
} else {
|
||||||
|
${await_block}.p(changed, ${params}, ${resolved});
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
block.builders.update.addBlock(deindent`
|
||||||
|
if (${conditions.join(' && ')}) {
|
||||||
|
${await_block}.c();
|
||||||
|
${await_block}.m(${anchor}.parentNode, ${anchor});
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.builders.destroy.addBlock(deindent`
|
||||||
|
${await_token} = null;
|
||||||
|
${await_block}.d();
|
||||||
|
`);
|
||||||
|
|
||||||
|
[node.pending, node.then, node.catch].forEach(status => {
|
||||||
|
status.children.forEach(child => {
|
||||||
|
visit(generator, status._block, status._state, child, elementStack, componentStack);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import visit from '../visit';
|
||||||
|
import { SsrGenerator } from '../index';
|
||||||
|
import Block from '../Block';
|
||||||
|
import { Node } from '../../../interfaces';
|
||||||
|
|
||||||
|
export default function visitAwaitBlock(
|
||||||
|
generator: SsrGenerator,
|
||||||
|
block: Block,
|
||||||
|
node: Node
|
||||||
|
) {
|
||||||
|
block.contextualise(node.expression);
|
||||||
|
const { dependencies, snippet } = node.metadata;
|
||||||
|
|
||||||
|
// TODO should this be the generator's job? It's duplicated between
|
||||||
|
// here and the equivalent DOM compiler visitor
|
||||||
|
const contexts = new Map(block.contexts);
|
||||||
|
contexts.set(node.value, '__value');
|
||||||
|
|
||||||
|
const contextDependencies = new Map(block.contextDependencies);
|
||||||
|
contextDependencies.set(node.value, dependencies);
|
||||||
|
|
||||||
|
const childBlock = block.child({
|
||||||
|
contextDependencies,
|
||||||
|
contexts
|
||||||
|
});
|
||||||
|
|
||||||
|
generator.append('${(function(__value) { if(__isPromise(__value)) return `');
|
||||||
|
|
||||||
|
node.pending.children.forEach((child: Node) => {
|
||||||
|
visit(generator, childBlock, child);
|
||||||
|
});
|
||||||
|
|
||||||
|
generator.append('`; return `');
|
||||||
|
|
||||||
|
node.then.children.forEach((child: Node) => {
|
||||||
|
visit(generator, childBlock, child);
|
||||||
|
});
|
||||||
|
|
||||||
|
generator.append(`\`;}(${snippet})) }`);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{{#await thePromise}}
|
||||||
|
<p>loading...</p>
|
||||||
|
{{then theValue}}
|
||||||
|
<p>the value is {{theValue}}</p>
|
||||||
|
{{catch theError}}
|
||||||
|
<p>oh no! {{theError.message}}</p>
|
||||||
|
{{/await}}
|
@ -0,0 +1,161 @@
|
|||||||
|
{
|
||||||
|
"hash": 1040536517,
|
||||||
|
"html": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 158,
|
||||||
|
"type": "Fragment",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 0,
|
||||||
|
"end": 158,
|
||||||
|
"type": "AwaitBlock",
|
||||||
|
"expression": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 9,
|
||||||
|
"end": 19,
|
||||||
|
"name": "thePromise"
|
||||||
|
},
|
||||||
|
"value": "theValue",
|
||||||
|
"error": "theError",
|
||||||
|
"pending": {
|
||||||
|
"start": 21,
|
||||||
|
"end": 41,
|
||||||
|
"type": "PendingBlock",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 21,
|
||||||
|
"end": 23,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "\n\t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 23,
|
||||||
|
"end": 40,
|
||||||
|
"type": "Element",
|
||||||
|
"name": "p",
|
||||||
|
"attributes": [],
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 26,
|
||||||
|
"end": 36,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "loading..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 40,
|
||||||
|
"end": 41,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"start": 41,
|
||||||
|
"end": 93,
|
||||||
|
"type": "ThenBlock",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 58,
|
||||||
|
"end": 60,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "\n\t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 60,
|
||||||
|
"end": 92,
|
||||||
|
"type": "Element",
|
||||||
|
"name": "p",
|
||||||
|
"attributes": [],
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 63,
|
||||||
|
"end": 76,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "the value is "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 76,
|
||||||
|
"end": 88,
|
||||||
|
"type": "MustacheTag",
|
||||||
|
"expression": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 78,
|
||||||
|
"end": 86,
|
||||||
|
"name": "theValue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 92,
|
||||||
|
"end": 93,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"catch": {
|
||||||
|
"start": 93,
|
||||||
|
"end": 148,
|
||||||
|
"type": "CatchBlock",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 111,
|
||||||
|
"end": 113,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "\n\t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 113,
|
||||||
|
"end": 147,
|
||||||
|
"type": "Element",
|
||||||
|
"name": "p",
|
||||||
|
"attributes": [],
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"start": 116,
|
||||||
|
"end": 123,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "oh no! "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 123,
|
||||||
|
"end": 143,
|
||||||
|
"type": "MustacheTag",
|
||||||
|
"expression": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"start": 125,
|
||||||
|
"end": 141,
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 125,
|
||||||
|
"end": 133,
|
||||||
|
"name": "theError"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 134,
|
||||||
|
"end": 141,
|
||||||
|
"name": "message"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 147,
|
||||||
|
"end": 148,
|
||||||
|
"type": "Text",
|
||||||
|
"data": "\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"css": null,
|
||||||
|
"js": null
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
let fulfil;
|
||||||
|
|
||||||
|
let thePromise = new Promise(f => {
|
||||||
|
fulfil = f;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data: {
|
||||||
|
thePromise
|
||||||
|
},
|
||||||
|
|
||||||
|
html: `
|
||||||
|
<p>loading...</p>
|
||||||
|
<p>loading...</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
test(assert, component, target) {
|
||||||
|
fulfil(42);
|
||||||
|
|
||||||
|
return thePromise
|
||||||
|
.then(() => {
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>the value is 42</p>
|
||||||
|
<p>the value is 42</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
let reject;
|
||||||
|
|
||||||
|
thePromise = new Promise((f, r) => {
|
||||||
|
reject = r;
|
||||||
|
});
|
||||||
|
|
||||||
|
component.set({
|
||||||
|
thePromise
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>loading...</p>
|
||||||
|
<p>loading...</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
reject(new Error('something broke'));
|
||||||
|
|
||||||
|
return thePromise.catch(() => {});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>oh no! something broke</p>
|
||||||
|
<p>oh no! something broke</p>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,15 @@
|
|||||||
|
{{#await thePromise}}
|
||||||
|
<p>loading...</p>
|
||||||
|
{{then theValue}}
|
||||||
|
<p>the value is {{theValue}}</p>
|
||||||
|
{{catch theError}}
|
||||||
|
<p>oh no! {{theError.message}}</p>
|
||||||
|
{{/await}}
|
||||||
|
|
||||||
|
{{#await thePromise}}
|
||||||
|
<p>loading...</p>
|
||||||
|
{{then theValue}}
|
||||||
|
<p>the value is {{theValue}}</p>
|
||||||
|
{{catch theError}}
|
||||||
|
<p>oh no! {{theError.message}}</p>
|
||||||
|
{{/await}}
|
@ -0,0 +1,19 @@
|
|||||||
|
export default {
|
||||||
|
data: {
|
||||||
|
thePromise: 'not actually a promise'
|
||||||
|
},
|
||||||
|
|
||||||
|
html: `
|
||||||
|
<p>the value is not actually a promise</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
test(assert, component, target) {
|
||||||
|
component.set({
|
||||||
|
thePromise: 'still not a promise'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>the value is still not a promise</p>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
{{#await thePromise}}
|
||||||
|
<p>loading...</p>
|
||||||
|
{{then theValue}}
|
||||||
|
<p>the value is {{theValue}}</p>
|
||||||
|
{{catch theError}}
|
||||||
|
<p>oh no! {{theError.message}}</p>
|
||||||
|
{{/await}}
|
@ -0,0 +1,49 @@
|
|||||||
|
let fulfil;
|
||||||
|
|
||||||
|
let thePromise = new Promise(f => {
|
||||||
|
fulfil = f;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data: {
|
||||||
|
thePromise
|
||||||
|
},
|
||||||
|
|
||||||
|
html: `
|
||||||
|
<p>loading...</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
test(assert, component, target) {
|
||||||
|
fulfil(42);
|
||||||
|
|
||||||
|
return thePromise
|
||||||
|
.then(() => {
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>the value is 42</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
let reject;
|
||||||
|
|
||||||
|
thePromise = new Promise((f, r) => {
|
||||||
|
reject = r;
|
||||||
|
});
|
||||||
|
|
||||||
|
component.set({
|
||||||
|
thePromise
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>loading...</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
reject(new Error('something broke'));
|
||||||
|
|
||||||
|
return thePromise.catch(() => {});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
assert.htmlEqual(target.innerHTML, `
|
||||||
|
<p>oh no! something broke</p>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
{{#await thePromise}}
|
||||||
|
<p>loading...</p>
|
||||||
|
{{then theValue}}
|
||||||
|
<p>the value is {{theValue}}</p>
|
||||||
|
{{catch theError}}
|
||||||
|
<p>oh no! {{theError.message}}</p>
|
||||||
|
{{/await}}
|
Loading…
Reference in new issue