Upgrade <await> tag to use tag parameters and attribute tags. Add migration from old syntax. (#1244)

This commit is contained in:
Dylan Piercey 2019-01-29 15:09:49 -08:00 committed by Dylan Piercey
parent 681ba112cd
commit 24753a7674
No known key found for this signature in database
GPG Key ID: DA54E25D5BF13DBE
43 changed files with 698 additions and 430 deletions

View File

@ -228,8 +228,13 @@ performance](http://www.ebaytechblog.com/2014/12/08/async-fragments-rediscoverin
```marko
$ const searchResultsPromise = searchService.performSearch(keywords);
<await(person from searchResultsPromise)>
<div>Hello ${person.name}!</div>
<await(searchResultsPromise)>
<@then|person|>
Hello ${person.name}!
</@then>
<@catch|err|>
The error was: ${err.message}.
</@catch>
</await>
```

View File

@ -385,9 +385,8 @@ sample template shows how to use macro functions inside expressions:
### `<await>`
The `<await>` tag is used to dynamically load in content from a data provider. The data provider can be a `Promise` or a `callback`. Once the provider returns it's results the children are rendered.
await-example.marko
The `<await>` tag is used to render a template asynchronously with the results of a Promise.
The `<@then>` and `<@catch>` attribute tags can optionally receive the value of the resolved and rejected promise respectively as [tag parameters](./syntax.md#tag-body-parameters). You can also provide a `<@placeholder>` attribute tag which will be displayed while the promise is pending.
```marko
$ var personPromise = new Promise((resolve, reject) => {
@ -398,34 +397,49 @@ $ var personPromise = new Promise((resolve, reject) => {
}, 1000);
});
<await(person from personPromise)>
<div>Hello ${person.name}!</div>
<await(personPromise)>
<@placeholder>
<!-- Displayed while promise is pending -->
Loading...
</@placeholder>
<@then|person|>
<!-- Displayed if promise resolves -->
<div>Hello ${person.name}!</div>
<@then>
<@catch|err|>
<!-- Displayed if promise rejects -->
Caught error: ${err.name}.
</@catch>
</await>
```
Advanced implementation:
Optional Attributes:
- `<await>` tag signature
- Basic usage: `<await(results from dataProvider)>...</await>`
- Optional attributes
- client-reorder `boolean`
- arg `expression`
- arg-\* `string`
- method `string`
- timeout `integer`
- timeout-message `string`
- error-message `string`
- placeholder `string`
- renderTimeout `function`
- renderError `function`
- renderPlaceholder `function`
- name `string`
- scope `expression`
- show-after `string`
- Optional child tags
- `<await-placeholder>Loading...</await-placeholder>`
- `<await-timeout>Request timed out</await-timeout>`
- `<await-error>Request errored</await-error>`
- **timeout** `integer`: An optional timeout that when reached will cause the promise to reject with a `TimeoutError`.
- **name** `string`: Used to improve debugging and also to ensure promise ordering with the `show-after` attribute.
- **show-after** `string`: This attribute will ensure that (with client-reorder) this `await` block will always show after another `await` block with the provided name.
- **client-reorder** `boolean`: If set anything after this promise will be sent out immediately, and reordered using JS in the browser.
> **Pro Tip**: With the `timeout` attribute set you can differentiate `TimeoutError`s from promise rejections by checking the `name` property of the error.
>
> ```marko
> <await(slowPromise) timeout=5000>
> <@then>Done</@then>
> <@catch|err|>
> <if(err.name === "TimeoutError)>
> Took too long to fetch the data!
> </if>
> <else>
> Promise failed with ${err.message}.
> </else>
> </@catch>
> </await>
> ```
## Comments

View File

@ -1104,8 +1104,10 @@ Given a template like this:
**New:**
```marko
<await(foo from data.provider)>
${foo}
<await(data.provider)>
<@then|foo|>
${foo}
</@then>
</await>
```

View File

@ -720,10 +720,10 @@ import fsp from 'fs-promise';
$ var filePath = __dirname + '/hello.txt';
$ var readPromise = fsp.readFile(filePath, {encoding: 'utf8'});
<await(helloText from readPromise)>
<p>
${helloText}
</p>
<await(readPromise)>
<@then|helloText|>
<p>${helloText}</p>
</@then>
</await>
```

View File

@ -29,7 +29,7 @@ if (target === "src") {
if (shouldBuildSrc) {
buildDir("src", "dist", {
babelExclude: ["/taglibs/async/client-reorder-runtime.min.js"],
babelExclude: ["/taglibs/core/async/client-reorder-runtime.min.js"],
babelOptions
});
}

View File

@ -145,7 +145,13 @@ class Normalizer {
}
}
if (elNode.params.length) {
if (
elNode.params.length &&
!(
(elNode.tagName === "@then" || elNode.tagName === "@catch") &&
elNode.parentNode.tagName === "await"
)
) {
context.setFlag("hasTagParams");
context.exampleTagParam = newNode;
}

View File

@ -275,10 +275,6 @@ function registerCoreTaglibs() {
require("../taglibs/svg/marko.json"),
require.resolve("../taglibs/svg/marko.json")
);
registerTaglib(
require("../taglibs/async/marko.json"),
require.resolve("../taglibs/async/marko.json")
);
registerTaglib(
require("../taglibs/cache/marko.json"),
require.resolve("../taglibs/cache/marko.json")

View File

@ -1,30 +0,0 @@
"use strict";
module.exports = function transform(el, context) {
var parentNode = el.parentNode;
if (parentNode.tagName !== "await") {
context.addError(
"The <" + el.tagName + "> should be nested within an <await> tag."
);
return;
}
var targetProp;
if (el.tagName === "await-error") {
targetProp = "renderError";
} else if (el.tagName === "await-timeout") {
targetProp = "renderTimeout";
} else if (el.tagName === "await-placeholder") {
targetProp = "renderPlaceholder";
}
var builder = context.builder;
parentNode.setAttributeValue(
targetProp,
builder.renderBodyFunction(el.body)
);
el.detach();
};

View File

@ -1,108 +0,0 @@
"use strict";
var isObjectEmpty = require("raptor-util/isObjectEmpty");
module.exports = function transform(el, context) {
if (!el.argument) {
context.addError(
"Invalid <await> tag. Argument is missing. Example: <await(user from data.userProvider)>"
);
return;
}
var match = /^([$A-Z_][0-9A-Z_$]*) from (.*)$/i.exec(el.argument);
if (!match) {
context.addError(
"Invalid <await> tag. Argument is malformed. Example: <await(user from data.userProvider)>"
);
return;
}
var varName = match[1];
var dataProviderAttr = match[2];
if (!context.util.isValidJavaScriptIdentifier(varName)) {
context.addError(
"Invalid <await> tag. Argument's variable name should be a valid JavaScript identifier. Example: user, as in <await(user from data.userProvider)>"
);
return;
}
var builder = context.builder;
el.setAttributeValue("_var", builder.literal(varName));
el.setAttributeValue(
"_dataProvider",
builder.parseExpression(dataProviderAttr)
);
el.argument = null;
////////////////////
var attrs = el.getAttributes().concat([]);
var arg = {};
attrs.forEach(attr => {
var attrName = attr.name;
if (attrName.startsWith("arg-")) {
let argName = attrName.substring("arg-".length);
arg[argName] = attr.value;
el.removeAttribute(attrName);
}
});
var name = el.getAttributeValue("name");
if (name == null) {
el.setAttributeValue("_name", builder.literal(dataProviderAttr));
}
if (el.hasAttribute("arg")) {
if (isObjectEmpty(arg)) {
arg = el.getAttributeValue("arg");
} else {
let mergeVar = context.helper("merge");
arg = builder.functionCall(mergeVar, [
builder.literal(arg), // Input props from the attributes take precedence
el.getAttributeValue("arg")
]);
}
} else {
if (isObjectEmpty(arg)) {
arg = null;
} else {
arg = builder.literal(arg);
}
}
if (arg) {
el.setAttributeValue("arg", arg);
}
var timeoutMessage = el.getAttributeValue("timeout-message");
if (timeoutMessage) {
el.removeAttribute("timeout-message");
el.setAttributeValue(
"renderTimeout",
builder.renderBodyFunction([builder.text(timeoutMessage)])
);
}
var errorMessage = el.getAttributeValue("error-message");
if (errorMessage) {
el.removeAttribute("error-message");
el.setAttributeValue(
"renderError",
builder.renderBodyFunction([builder.text(errorMessage)])
);
}
var placeholder = el.getAttributeValue("placeholder");
if (placeholder) {
el.removeAttribute("placeholder");
el.setAttributeValue(
"renderPlaceholder",
builder.renderBodyFunction([builder.text(placeholder)])
);
}
};

View File

@ -1,104 +0,0 @@
{
"<await>": {
"renderer": "./await-tag",
"@_var": "identifier",
"@_dataProvider": "expression",
"@arg": {
"type": "expression",
"preserve-name": true
},
"@arg-*": {
"pattern": true,
"type": "string",
"preserve-name": true
},
"@method": "string",
"@timeout": "integer",
"@timeout-message": "string",
"@error-message": "string",
"@placeholder": "string",
"@renderTimeout": "function",
"@renderError": "function",
"@renderPlaceholder": "function",
"@name": {
"type": "string",
"description": "Name of await",
"autocomplete": [
{
"snippet": "name=\"${1:name}\""
},
{}
]
},
"@_name": "string",
"@client-reorder": {
"type": "boolean",
"description": "Use JavaScript on client to move async fragment into the proper place."
},
"@scope": {
"type": "expression",
"description": "The value of 'this' when invoking the data provider function (N/A with promises)"
},
"@show-after": {
"type": "string"
},
"vars": [
{
"name-from-attribute": "_var"
}
],
"transformer": "./await-tag-transformer",
"autocomplete": [
{
"snippet": "await(${1:varName} from ${2:data.myDataProvider})",
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait><code>"
},
{
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait><code>"
}
]
},
"<await-reorderer>": {
"renderer": "./await-reorderer-tag",
"autocomplete": [
{
"snippet": "await-reorderer",
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait-reorderer><code>"
},
{
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait-reorderer><code>"
}
]
},
"<await-placeholder>": {
"transformer": "./await-nested-tag-transformer",
"autocomplete": [
{
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait-placeholder><code>"
}
]
},
"<await-timeout>": {
"transformer": "./await-nested-tag-transformer",
"autocomplete": [
{
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait-timeout><code>"
}
]
},
"<await-error>": {
"transformer": "./await-nested-tag-transformer",
"autocomplete": [
{
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait-error><code>"
}
]
}
}

View File

@ -1,4 +1,4 @@
var nextTick = require("../../runtime/nextTick");
var nextTick = require("../../../runtime/nextTick");
function AsyncValue() {
/**

View File

@ -1,6 +1,6 @@
{
"browser": {
"./client-reorder.js": "./client-reorder-browser.js",
"./await-reorderer-tag.js": "./noop-render.js"
"./reorderer-renderer.js": "./noop-render.js"
}
}

View File

@ -1,4 +1,5 @@
"use strict";
var complain = "MARKO_DEBUG" && require("complain");
var isClientReorderSupported = require("./client-reorder").isSupported;
var AsyncValue = require("./AsyncValue");
@ -10,10 +11,17 @@ function safeRenderBody(renderBody, targetOut, data) {
}
}
function requestData(provider, args, thisObj, timeout) {
function requestData(provider, timeout) {
var asyncValue = new AsyncValue();
if (typeof provider === "function") {
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
complain(
"Passing a callback function to the <await> tag has been deprecated, please use a promise instead."
);
}
var callback = function(err, data) {
if (err) {
asyncValue.___reject(err);
@ -25,9 +33,9 @@ function requestData(provider, args, thisObj, timeout) {
var value =
provider.length === 1
? // one argument so only provide callback to function call
provider.call(thisObj, callback)
provider(callback)
: // two arguments so provide args and callback to function call
provider.call(thisObj, args, callback);
provider(null, callback);
if (value !== undefined) {
asyncValue.___resolve(value);
@ -46,6 +54,7 @@ function requestData(provider, args, thisObj, timeout) {
timeoutId = null;
var error = new Error("Timed out after " + timeout + "ms");
error.code = "ERR_AWAIT_TIMEDOUT";
error.name = "TimeoutError";
asyncValue.___reject(error);
}, timeout);
@ -62,22 +71,16 @@ function requestData(provider, args, thisObj, timeout) {
const LAST_OPTIONS = { last: true, name: "await:finish" };
module.exports = function awaitTag(input, out) {
var arg = input.arg || {};
arg.out = out;
var clientReorder =
isClientReorderSupported && input.clientReorder === true && !out.isVDOM;
var name = input.name || input._name;
var scope = input.scope || this;
var method = input.method;
var timeout = input.timeout;
var dataProvider = input._dataProvider;
if (method) {
dataProvider = dataProvider[method].bind(dataProvider);
}
var asyncValue = requestData(dataProvider, arg, scope, timeout);
var provider = input._provider;
var asyncValue = requestData(provider, timeout);
var placeholderRenderer = input.placeholder && input.placeholder.renderBody;
var resultRenderer = input.then && input.then.renderBody;
var errorRenderer = input.catch && input.catch.renderBody;
if (asyncValue.___settled) {
// No point in using client-reordering if the data was fetched
@ -91,7 +94,7 @@ module.exports = function awaitTag(input, out) {
var awaitInfo = {
name: name,
clientReorder: clientReorder,
dataProvider: dataProvider
dataProvider: provider
};
if (clientReorder) {
@ -107,9 +110,9 @@ module.exports = function awaitTag(input, out) {
var id = (awaitInfo.id = input.name || clientReorderContext.nextId++);
var placeholderIdAttrValue = "afph" + id;
if (input.renderPlaceholder) {
if (placeholderRenderer) {
out.write('<span id="' + placeholderIdAttrValue + '">');
input.renderPlaceholder(out);
placeholderRenderer(out);
out.write("</span>");
} else {
out.write(
@ -173,26 +176,19 @@ module.exports = function awaitTag(input, out) {
}
if (err) {
if (err.code === "ERR_AWAIT_TIMEDOUT" && input.renderTimeout) {
input.renderTimeout(asyncOut);
} else if (input.renderError) {
// eslint-disable-next-line no-console
console.error(
"Await (" + name + ") failed. Error:",
err.stack || err
);
input.renderError(asyncOut);
if (errorRenderer) {
errorRenderer(asyncOut, err);
} else {
asyncOut.error(err);
}
} else {
var renderBodyFunc = input.renderBody;
if (renderBodyFunc) {
if (resultRenderer) {
var renderBodyErr = safeRenderBody(
renderBodyFunc,
resultRenderer,
asyncOut,
data
);
if (renderBodyErr) {
return renderBody(renderBodyErr);
}

View File

@ -0,0 +1,15 @@
module.exports = function(elNode, context) {
const builder = context.builder;
const provider = elNode.argument;
elNode.argument = undefined;
if (!provider) {
context.addError(
'You must provide a promise argument to the "<await>" tag, eg: "<await(promise)>".'
);
return elNode;
}
elNode.setAttributeValue("_provider", builder.parseExpression(provider));
elNode.setAttributeValue("_name", builder.literal(provider));
};

View File

@ -166,6 +166,45 @@
}
]
},
"<await>": {
"transformer": "./await/transformer",
"renderer": "./await/renderer",
"@_provider": "expression",
"@_name": "string",
"@timeout": "integer",
"@name": {
"type": "string",
"description": "Name of await",
"autocomplete": [
{
"snippet": "name=\"${1:name}\""
},
{}
]
},
"@client-reorder": {
"type": "boolean",
"description": "Use JavaScript on client to move async fragment into the proper place."
},
"@show-after": {
"type": "string"
},
"autocomplete": [
{
"snippet": "await(${1:promise})",
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait><code>"
}
]
},
"<await-reorderer>": {
"renderer": "./await/reorderer-renderer",
"autocomplete": [
{
"snippet": "await-reorderer",
"descriptionMoreURL": "http://markojs.com/docs/marko/async-taglib/#<code>&ltawait-reorderer><code>"
}
]
},
"<*>": {
"@body-only-if": {
"type": "statement",

View File

@ -1,59 +1,49 @@
const commonTagMigrator = require("./all-tags");
const awaitTagMigration = require("./await-tag");
const newTags = {
"async-fragment": "await",
"async-fragments": "await-reorderer",
"async-fragment-placeholder": "await-placeholder",
"async-fragment-timeout": "await-timeout",
"async-fragment-error": "await-error"
};
module.exports = function migrator(oldNode, context) {
const oldTag = oldNode.tagName;
const newTag = newTags[oldTag];
let provider;
let varName;
let argument;
const attributes = oldNode.attributes;
module.exports = function migrator(elNode, context) {
const builder = context.builder;
commonTagMigrator(oldNode, context);
oldNode.setTransformerApplied(commonTagMigrator);
const attributes = elNode.attributes;
context.deprecate(
`The "<${oldTag}>" tag is deprecated. Please use "<${newTag}>" instead. See: https://github.com/marko-js/marko/wiki/Deprecation:-async-fragment`
`The "<async-fragment>" tag is deprecated. Please use "<await>" instead. See: https://github.com/marko-js/marko/wiki/Deprecation:-async-fragment`
);
if (oldTag == "async-fragment" /* new: <await> */) {
if (!attributes || !attributes.length) {
context.addError(
'Invalid <aync-fragment> tag. Argument is missing. Example; <async-fragment data-provider=data.userInfo var="userInfo" />'
);
return oldNode;
}
// need to convert data-provider and var attributes
// to an argument: <await(var from dataProvider)>
varName = oldNode.getAttributeValue("var").value;
provider = oldNode.getAttributeValue("data-provider").toString();
argument = varName + " from " + provider;
if (!context.util.isValidJavaScriptIdentifier(varName)) {
context.addError(
'Invalid <aync-fragment> tag. Argument\'s variable name should be a valid JavaScript identifier. Example: user, as in <async-fragment data-provider=data.userInfo var="userInfo" />'
);
return;
}
oldNode.removeAttribute("var");
oldNode.removeAttribute("data-provider");
if (!attributes || !attributes.length) {
context.addError(
'Invalid <async-fragment> tag. Argument is missing. Example; <async-fragment data-provider=data.userInfo var="userInfo" />'
);
return elNode;
}
if (oldTag == "async-fragments" /* new: <await-reorderer> */) {
// all this tag ever did was handling of client reordering
// we'll remove the attribute as that's all this new tag does
oldNode.removeAttribute("client-reorder");
// need to convert data-provider and var attributes
// to an argument: <await(var from dataProvider)>
const varName = elNode.getAttributeValue("var").value;
const provider = elNode.getAttributeValue("data-provider").toString();
const argument = varName + " from " + provider;
elNode.removeAttribute("var");
elNode.removeAttribute("data-provider");
if (!context.util.isValidJavaScriptIdentifier(varName)) {
context.addError(
'Invalid <async-fragment> tag. Argument\'s variable name should be a valid JavaScript identifier. Example: user, as in <async-fragment data-provider=data.userInfo var="userInfo" />'
);
return;
}
const newNode = builder.htmlElement(newTag, attributes, [], argument);
elNode.forEachChild(child => {
const newTag = newTags[child.tagName];
if (newTag) {
child.tagName = newTag;
child.tagNameExpression = builder.literal(newTag);
}
});
oldNode.replaceWith(newNode);
oldNode.moveChildrenTo(newNode);
elNode.tagName = "await";
elNode.tagNameExpression = builder.literal(elNode.tagName);
elNode.argument = argument;
awaitTagMigration(elNode, context);
};

View File

@ -0,0 +1,17 @@
const commonTagMigrator = require("./all-tags");
module.exports = function migrator(elNode, context) {
const builder = context.builder;
commonTagMigrator(elNode, context);
elNode.setTransformerApplied(commonTagMigrator);
context.deprecate(
`The "<async-fragments>" tag is deprecated. Please use "<await-reorderer>" instead. See: https://github.com/marko-js/marko/wiki/Deprecation:-async-fragment`
);
// all this tag ever did was handling of client reordering
// we'll remove the attribute as that's all this new tag does
elNode.removeAttribute("client-reorder");
elNode.tagName = "await-reorderer";
elNode.tagNameExpression = builder.literal(elNode.tagName);
};

View File

@ -0,0 +1,235 @@
const printJS = require("./util/printJS");
module.exports = function migrator(elNode, context) {
const builder = context.builder;
const argument = elNode.argument;
const match =
argument && /^([$A-Z_][0-9A-Z_$]*) from (.*)$/i.exec(argument);
if (!match) {
return;
}
context.deprecate(
'The "<await(result from promise)>" syntax has been deprecated, please use the modern syntax of "<await(promise)><@then|result|>". See: https://github.com/marko-js/marko/wiki/Deprecation:-async-tag'
);
elNode.argument = undefined;
let provider = builder.expression(match[2]);
const varName = match[1];
const methodExpression = elNode.getAttributeValue("method");
const scopeExpression =
elNode.getAttributeValue("scope") ||
(methodExpression ? provider : null);
const argAssignments = elNode.attributes
.filter(attr => attr.name && attr.name.startsWith("arg-"))
.map(attr => {
elNode.removeAttribute(attr.name);
return [attr.name.slice(4), attr.value];
});
let argExpression = elNode.getAttributeValue("arg");
elNode.removeAttribute("method");
elNode.removeAttribute("scope");
elNode.removeAttribute("arg");
if (methodExpression) {
provider = builder.memberExpression(provider, methodExpression, true);
}
if (argExpression) {
if (argAssignments.length) {
const argIdentifier = builder.identifier("arg");
if (argExpression.type !== "ObjectExpression") {
argExpression = builder.binaryExpression(
argExpression,
"||",
builder.objectExpression([])
);
}
elNode.insertSiblingBefore(
builder.scriptlet({
value: printJS(
builder.vars({
arg: argExpression
}),
context
)
})
);
argAssignments.forEach(assignment => {
elNode.insertSiblingBefore(
builder.scriptlet({
value: printJS(
builder.assignment(
builder.memberExpression(
argIdentifier,
builder.literal(assignment[0]),
true
),
assignment[1],
"="
),
context
)
})
);
});
argExpression = argIdentifier;
}
} else if (argAssignments.length) {
argExpression = builder.objectExpression(
argAssignments.reduce((obj, parts) => {
obj[parts[0]] = parts[1];
return obj;
}, {})
);
}
if (argExpression || scopeExpression) {
const callArgs = [scopeExpression || builder.literal(null)];
if (argExpression) {
callArgs.push(argExpression);
}
provider = builder.functionCall(
builder.memberExpression(provider, builder.identifier("bind")),
callArgs
);
}
elNode.argument = printJS(provider, context);
let placeholderBody;
if (elNode.hasAttribute("placeholder")) {
context.deprecate(
'The "placeholder" attribute on the "<await>" tag is deprecated. Please use the "<@placeholder>" nested tag instead.'
);
placeholderBody = [
builder.text(elNode.getAttributeValue("placeholder"))
];
elNode.removeAttribute("placeholder");
}
let timeoutBody;
if (elNode.hasAttribute("timeout-message")) {
context.deprecate(
'The "timeout-message" attribute on the "<await>" tag is deprecated. Please use the "<@catch|err|>" nested tag instead with a check for "err.name === "TimeoutError".'
);
timeoutBody = [
builder.text(elNode.getAttributeValue("timeout-message"))
];
elNode.removeAttribute("timeout-message");
}
let errorBody;
if (elNode.hasAttribute("error-message")) {
context.deprecate(
'The "error-message" attribute on the "<await>" tag is deprecated. Please use the "<@catch>" nested tag instead.'
);
errorBody = [builder.text(elNode.getAttributeValue("error-message"))];
elNode.removeAttribute("error-message");
}
elNode.forEachChild(childNode => {
if (childNode.type !== "HtmlElement") {
return;
}
switch (childNode.tagName) {
case "await-placeholder":
placeholderBody = childNode.body;
break;
case "await-timeout":
timeoutBody = childNode.body;
break;
case "await-error":
errorBody = childNode.body;
break;
default:
return;
}
childNode.detach();
});
const renderPlaceholderAttr = elNode.getAttributeValue("renderPlaceholder");
const renderTimeoutAttr = elNode.getAttributeValue("renderTimeout");
const renderErrorAttr = elNode.getAttributeValue("renderError");
elNode.removeAttribute("renderPlaceholder");
elNode.removeAttribute("renderTimeout");
elNode.removeAttribute("renderError");
if (renderPlaceholderAttr && !placeholderBody) {
context.deprecate(
'The "renderPlaceholder" attribute on the "<await>" tag is deprecated and will be removed in a future version of Marko.'
);
placeholderBody = [buildDynamicTag(renderPlaceholderAttr, context)];
}
if (renderTimeoutAttr && !timeoutBody) {
context.deprecate(
'The "renderTimeout" attribute on the "<await>" tag is deprecated and will be removed in a future version of Marko.'
);
timeoutBody = [buildDynamicTag(renderTimeoutAttr, context)];
}
if (renderErrorAttr && !errorBody) {
context.deprecate(
'The "renderError" attribute on the "<await>" tag is deprecated and will be removed in a future version of Marko.'
);
errorBody = [buildDynamicTag(renderErrorAttr, context)];
}
elNode._normalizeChildTextNodes(context);
if (elNode.body.length) {
const thenNode = builder.htmlElement("@then");
thenNode.params = [varName];
elNode.moveChildrenTo(thenNode);
elNode.appendChild(thenNode);
}
if (placeholderBody) {
elNode.appendChild(
builder.htmlElement("@placeholder", undefined, placeholderBody)
);
}
if (timeoutBody) {
const originalErrorBody = errorBody;
errorBody = [
builder.htmlElement(
"if",
undefined,
timeoutBody,
'err.name === "TimeoutError"'
)
];
if (originalErrorBody) {
errorBody.push(
builder.htmlElement("else", undefined, originalErrorBody)
);
}
}
if (errorBody) {
const catchNode = builder.htmlElement("@catch", undefined, errorBody);
if (timeoutBody) {
catchNode.params = ["err"];
}
elNode.appendChild(catchNode);
}
};
function buildDynamicTag(expression, context) {
const node = context.builder.htmlElement();
node.rawTagNameExpression = printJS(expression, context);
return node;
}

View File

@ -117,19 +117,16 @@
"deprecated": true
},
"<async-fragments>": {
"migrator": "./async-fragment-tag",
"migrator": "./async-fragments-tag",
"deprecated": true
},
"<async-fragment-placeholder>": {
"migrator": "./async-fragment-tag",
"deprecated": true
},
"<async-fragment-timeout>": {
"migrator": "./async-fragment-tag",
"deprecated": true
},
"<async-fragment-error>": {
"migrator": "./async-fragment-tag",
"deprecated": true
},
"<include>": {
@ -182,6 +179,18 @@
"migrator": "./unless-tag",
"deprecated": true
},
"<await>": {
"migrator": "./await-tag"
},
"<await-placeholder>": {
"deprecated": true
},
"<await-timeout>": {
"deprecated": true
},
"<await-error>": {
"deprecated": true
},
"<script>": {
"@marko-init": "boolean",
"@template-helpers": "boolean"

View File

@ -5,22 +5,28 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
components_helpers = require("marko/src/components/helpers"),
marko_renderer = components_helpers.r,
marko_defineComponent = components_helpers.c,
hasRenderBodyKey = Symbol.for("hasRenderBody"),
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_loadTag = marko_helpers.t,
await_tag = marko_loadTag(require("marko/src/taglibs/async/await-tag"));
await_tag = marko_loadTag(require("marko/src/taglibs/core/await/renderer"));
function render(input, out, __component, component, state) {
var data = input;
await_tag({
_dataProvider: data.userInfo,
_provider: data.userInfo,
_name: "data.userInfo",
renderError: function renderBody(out) {
out.w("something went wrong!");
},
renderBody: function renderBody(out, userInfo) {
out.w("Success!");
}
then: {
renderBody: function renderBody(out, userInfo) {
out.w("Success!");
}
},
catch: {
renderBody: function renderBody(out) {
out.w("something went wrong!");
}
},
[hasRenderBodyKey]: true
}, out, __component, "0");
}
@ -34,6 +40,6 @@ marko_template.Component = marko_defineComponent({}, marko_template._);
marko_template.meta = {
id: "/marko-test$1.0.0/compiler/fixtures-html/async-fragment-error/template.marko",
tags: [
"marko/src/taglibs/async/await-tag"
"marko/src/taglibs/core/await/renderer"
]
};

View File

@ -7,7 +7,7 @@ exports.checkError = function(e) {
expect(e.errors.length).to.equal(1);
var message = e.toString();
expect(message).to.contain("Invalid <aync-fragment> tag");
expect(message).to.contain("Invalid <async-fragment> tag");
expect(message).to.contain("should be a valid JavaScript identifier");
expect(message).to.contain(
'<async-fragment data-provider=data.userInfo var="userInfo" />'

View File

@ -7,7 +7,7 @@ exports.checkError = function(e) {
expect(e.errors.length).to.equal(1);
var message = e.toString();
expect(message).to.contain("Invalid <aync-fragment> tag");
expect(message).to.contain("Invalid <async-fragment> tag");
expect(message).to.contain("Argument is missing");
expect(message).to.contain(
'<async-fragment data-provider=data.userInfo var="userInfo" />'

View File

@ -7,23 +7,29 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
marko_defineComponent = components_helpers.c,
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_escapeXml = marko_helpers.x,
hasRenderBodyKey = Symbol.for("hasRenderBody"),
marko_loadTag = marko_helpers.t,
await_tag = marko_loadTag(require("marko/src/taglibs/async/await-tag"));
await_tag = marko_loadTag(require("marko/src/taglibs/core/await/renderer"));
function render(input, out, __component, component, state) {
var data = input;
await_tag({
_dataProvider: data.userInfo,
_provider: data.userInfo,
_name: "data.userInfo",
renderPlaceholder: function renderBody(out) {
out.w("Loading name...");
},
renderBody: function renderBody(out, userInfo) {
out.w("Hello " +
marko_escapeXml(testData.name) +
"!");
}
then: {
renderBody: function renderBody(out, userInfo) {
out.w("Hello " +
marko_escapeXml(testData.name) +
"!");
}
},
placeholder: {
renderBody: function renderBody(out) {
out.w("Loading name...");
}
},
[hasRenderBodyKey]: true
}, out, __component, "0");
}
@ -37,6 +43,6 @@ marko_template.Component = marko_defineComponent({}, marko_template._);
marko_template.meta = {
id: "/marko-test$1.0.0/compiler/fixtures-html/async-fragment-placeholder/template.marko",
tags: [
"marko/src/taglibs/async/await-tag"
"marko/src/taglibs/core/await/renderer"
]
};

View File

@ -5,9 +5,10 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
components_helpers = require("marko/src/components/helpers"),
marko_renderer = components_helpers.r,
marko_defineComponent = components_helpers.c,
hasRenderBodyKey = Symbol.for("hasRenderBody"),
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_loadTag = marko_helpers.t,
await_tag = marko_loadTag(require("marko/src/taglibs/async/await-tag"));
await_tag = marko_loadTag(require("marko/src/taglibs/core/await/renderer"));
function render(input, out, __component, component, state) {
var data = input;
@ -15,13 +16,21 @@ function render(input, out, __component, component, state) {
await_tag({
timeout: 100,
name: "userInfo4",
_dataProvider: data.userInfo,
renderTimeout: function renderBody(out) {
out.w("Timeout has occurred!");
},
renderBody: function renderBody(out, userInfo) {
out.w("4");
}
_provider: data.userInfo,
_name: "data.userInfo",
then: {
renderBody: function renderBody(out, userInfo) {
out.w("4");
}
},
catch: {
renderBody: function renderBody(out, err) {
if (err.name === "TimeoutError") {
out.w("Timeout has occurred!");
}
}
},
[hasRenderBodyKey]: true
}, out, __component, "0");
}
@ -35,6 +44,6 @@ marko_template.Component = marko_defineComponent({}, marko_template._);
marko_template.meta = {
id: "/marko-test$1.0.0/compiler/fixtures-html/async-fragment-timeout/template.marko",
tags: [
"marko/src/taglibs/async/await-tag"
"marko/src/taglibs/core/await/renderer"
]
};

View File

@ -7,13 +7,13 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
marko_defineComponent = components_helpers.c,
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_loadTag = marko_helpers.t,
await_tag = marko_loadTag(require("marko/src/taglibs/async/await-tag"));
await_tag = marko_loadTag(require("marko/src/taglibs/core/await/renderer"));
function render(input, out, __component, component, state) {
var data = input;
await_tag({
_dataProvider: data.userInfo,
_provider: data.userInfo,
_name: "data.userInfo"
}, out, __component, "0");
}
@ -28,6 +28,6 @@ marko_template.Component = marko_defineComponent({}, marko_template._);
marko_template.meta = {
id: "/marko-test$1.0.0/compiler/fixtures-html/async-fragment/template.marko",
tags: [
"marko/src/taglibs/async/await-tag"
"marko/src/taglibs/core/await/renderer"
]
};

View File

@ -7,7 +7,7 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
marko_defineComponent = components_helpers.c,
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_loadTag = marko_helpers.t,
await_reorderer_tag = marko_loadTag(require("marko/src/taglibs/async/await-reorderer-tag"));
await_reorderer_tag = marko_loadTag(require("marko/src/taglibs/core/await/reorderer-renderer"));
function render(input, out, __component, component, state) {
var data = input;
@ -25,6 +25,6 @@ marko_template.Component = marko_defineComponent({}, marko_template._);
marko_template.meta = {
id: "/marko-test$1.0.0/compiler/fixtures-html/async-fragments/template.marko",
tags: [
"marko/src/taglibs/async/await-reorderer-tag"
"marko/src/taglibs/core/await/reorderer-renderer"
]
};

View File

@ -13,7 +13,7 @@ var marko_template = module.exports = require("marko/src/vdom").t(__filename),
marko_loadTag = marko_helpers.t,
component_globals_tag = marko_loadTag(require("marko/src/components/taglib/component-globals-tag")),
init_components_tag = marko_loadTag(require("marko/src/components/taglib/init-components-tag")),
await_reorderer_tag = marko_loadTag(require("marko/src/taglibs/async/await-reorderer-tag")),
await_reorderer_tag = marko_loadTag(require("marko/src/taglibs/core/await/reorderer-renderer")),
marko_createElement = marko_helpers.e,
marko_const = marko_helpers.const,
marko_const_nextId = marko_const("5b1bc3"),
@ -61,6 +61,6 @@ marko_template.meta = {
tags: [
"marko/src/components/taglib/component-globals-tag",
"marko/src/components/taglib/init-components-tag",
"marko/src/taglibs/async/await-reorderer-tag"
"marko/src/taglibs/core/await/reorderer-renderer"
]
};

View File

@ -1,8 +1,11 @@
<!-- test/migrate/fixtures/async-fragment-tag/template.marko -->
<await(userInfo from data.userInfo)>
Success!
<await-placeholder>loading...</await-placeholder>
<await-timeout>took too long!</await-timeout>
<await-error>something went wrong!</await-error>
<await(data.userInfo)>
<@then|userInfo|>Success!</@then>
<@placeholder>loading...</@placeholder>
<@catch|err|>
<if(err.name === "TimeoutError")>took too long!</if>
<else>something went wrong!</else>
</@catch>
</await>
<await-reorderer/>

View File

@ -10,4 +10,6 @@
<async-fragment-error>
something went wrong!
</async-fragment-error>
</async-fragment>
</async-fragment>
<async-fragments client-reorder=true/>

View File

@ -0,0 +1,90 @@
<!-- test/migrate/fixtures/legacy-await-syntax/template.marko -->
<await(getUserInfo())>
<@then|user|>Hello ${user.name}</@then>
</await>
<await(getUserInfo())>
<@then|user|>Hello ${user.name}</@then>
<@placeholder>
<${renderPlaceholderFn}/>
</@placeholder>
<@catch>
<${renderErrorFn}/>
</@catch>
</await>
<await(getUserInfo())>
<@then|user|>Hello ${user.name}</@then>
<@placeholder>
<${renderPlaceholderFn}/>
</@placeholder>
<@catch|err|>
<if(err.name === "TimeoutError")>
<${renderTimeoutFn}/>
</if>
<else>
<${renderErrorFn}/>
</else>
</@catch>
</await>
<await(getUserInfo())>
<@then|user|>Hello ${user.name}</@then>
<@placeholder>Placeholder Attr</@placeholder>
<@catch|err|>
<if(err.name === "TimeoutError")>Timeout Message Attr</if>
<else>Error Message Attr</else>
</@catch>
</await>
<await(getUserInfo())>
<@then|user|>Hello ${user.name}</@then>
<@placeholder>nested placeholder</@placeholder>
<@catch|err|>
<if(err.name === "TimeoutError")>nested timeout</if>
<else>nested error</else>
</@catch>
</await>
<await(getters["user"].bind(getters))>
<@then|user|>Hello ${user.name}</@then>
</await>
<await(
getters.bind(null, {
hello: "world"
})
)>
<@then|user|>Hello ${user.name}</@then>
</await>
<await(getters.bind(null, something))>
<@then|user|>Hello ${user.name}</@then>
</await>
$ var arg = {
hello: "world"
};
$ arg["x"] = 1;
$ arg["y"] = 2;
<await(getters.bind(null, arg))>
<@then|user|>Hello ${user.name}</@then>
</await>
$ var arg = something || {};
$ arg["x"] = 1;
$ arg["y"] = 2;
<await(getters.bind(null, arg))>
<@then|user|>Hello ${user.name}</@then>
</await>
<await(
getters.bind(null, {
x: 1,
y: 2
})
)>
<@then|user|>Hello ${user.name}</@then>
</await>
<await(getters.bind(window))>
<@then|user|>Hello ${user.name}</@then>
</await>
$ var arg = {
data: true
};
$ arg["x"] = 1;
$ arg["y"] = 2;
<await(getters["user"].bind(window, arg))>
<@then|user|>Hello ${user.name}</@then>
</await>

View File

@ -0,0 +1,54 @@
<await(user from getUserInfo())>
Hello ${user.name}
</await>
<await(user from getUserInfo()) renderPlaceholder=renderPlaceholderFn renderError=renderErrorFn>
Hello ${user.name}
</await>
<await(user from getUserInfo()) renderPlaceholder=renderPlaceholderFn renderError=renderErrorFn renderTimeout=renderTimeoutFn>
Hello ${user.name}
</await>
<await(user from getUserInfo()) renderPlaceholder=renderPlaceholderFn placeholder="Placeholder Attr" renderError=renderErrorFn error-message="Error Message Attr" renderTimeout=renderTimeoutFn timeout-message="Timeout Message Attr">
Hello ${user.name}
</await>
<await(user from getUserInfo()) renderPlaceholder=renderPlaceholderFn placeholder="Placeholder Attr" renderError=renderErrorFn error-message="Error Message Attr" renderTimeout=renderTimeoutFn timeout-message="Timeout Message Attr">
Hello ${user.name}
<await-placeholder>nested placeholder</await-placeholder>
<await-timeout>nested timeout</await-timeout>
<await-error>nested error</await-error>
</await>
<await(user from getters) method="user">
Hello ${user.name}
</await>
<await(user from getters) arg={ hello: "world" }>
Hello ${user.name}
</await>
<await(user from getters) arg=something>
Hello ${user.name}
</await>
<await(user from getters) arg={ hello: "world" } arg-x=1 arg-y=2>
Hello ${user.name}
</await>
<await(user from getters) arg=something arg-x=1 arg-y=2>
Hello ${user.name}
</await>
<await(user from getters) arg-x=1 arg-y=2>
Hello ${user.name}
</await>
<await(user from getters) scope=window>
Hello ${user.name}
</await>
<await(user from getters) method="user" arg={ data: true } arg-x=1 arg-y=2 scope=window>
Hello ${user.name}
</await>

View File

@ -33,7 +33,8 @@
"name": "userInfo1",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -43,7 +44,8 @@
"name": "userInfo1",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
@ -54,7 +56,8 @@
"name": "userInfo2",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -64,7 +67,8 @@
"name": "userInfo2",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
@ -75,7 +79,8 @@
"name": "userInfo3",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -85,7 +90,8 @@
"name": "userInfo3",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
@ -96,7 +102,8 @@
"name": "userInfo4",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -106,7 +113,8 @@
"name": "userInfo4",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}

View File

@ -33,7 +33,8 @@
"name": "userInfo1",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -43,7 +44,8 @@
"name": "userInfo1",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
@ -54,7 +56,8 @@
"name": "userInfo2",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -64,7 +67,8 @@
"name": "userInfo2",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
@ -75,7 +79,8 @@
"name": "userInfo3",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -85,7 +90,8 @@
"name": "userInfo3",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
@ -96,7 +102,8 @@
"name": "userInfo4",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
}
}
},
@ -106,9 +113,10 @@
"name": "userInfo4",
"clientReorder": false,
"error": {
"code": "ERR_AWAIT_TIMEDOUT"
"code": "ERR_AWAIT_TIMEDOUT",
"name": "TimeoutError"
},
"finished": true
}
}
]
]

View File

@ -1 +1 @@
<html><body><!--FLUSH--><div class="inner"><div class="inner-inner"><!--M^s0-6 s0 6--><div>Hello inner-inner</div><!--M/--></div><!--M^s0-7 s0 7--><div>Hello inner</div><!--M/--></div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-6",0,{"name":"inner-inner"},{"f":1}],["s0-7",0,{"name":"inner"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await-beginAsync/components/hello/index.marko"]})||w.$components})()</script><!--M^s0-8 s0 8--><div>Hello outer</div><!--M/--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-8",0,{"name":"outer"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await-beginAsync/components/hello/index.marko"]})||w.$components})()</script><!--FLUSH--></body></html>
<html><body><!--FLUSH--><div class="inner"><div class="inner-inner"><!--M^s0-7 s0 7--><div>Hello inner-inner</div><!--M/--></div><!--M^s0-8 s0 8--><div>Hello inner</div><!--M/--></div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-7",0,{"name":"inner-inner"},{"f":1}],["s0-8",0,{"name":"inner"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await-beginAsync/components/hello/index.marko"]})||w.$components})()</script><!--M^s0-9 s0 9--><div>Hello outer</div><!--M/--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-9",0,{"name":"outer"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await-beginAsync/components/hello/index.marko"]})||w.$components})()</script><!--FLUSH--></body></html>

View File

@ -1 +1 @@
<html><head><title><!--FLUSH-->Welcome Frank</title></head><body><!--M^s0-0-5-4 s0 4--><div>Hello</div><!--M/--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-0-5-4",0,{},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await-title/components/hello/index.marko"]})||w.$components})()</script><!--FLUSH--></body></html>
<html><head><title><!--FLUSH-->Welcome Frank</title></head><body><!--M^s0-0-5-5 s0 5--><div>Hello</div><!--M/--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-0-5-5",0,{},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await-title/components/hello/index.marko"]})||w.$components})()</script><!--FLUSH--></body></html>

View File

@ -1 +1 @@
<html><body><!--FLUSH--><div class="inner"><div class="inner-inner"><!--M^s0-6 s0 6--><div>Hello inner-inner</div><!--M/--></div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-6",0,{"name":"inner-inner"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await/components/hello/index.marko"]})||w.$components})()</script><!--M^s0-7 s0 7--><div>Hello inner</div><!--M/--></div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-7",0,{"name":"inner"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await/components/hello/index.marko"]})||w.$components})()</script><!--M^s0-8 s0 8--><div>Hello outer</div><!--M/--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-8",0,{"name":"outer"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await/components/hello/index.marko"]})||w.$components})()</script><!--FLUSH--></body></html>
<html><body><!--FLUSH--><div class="inner"><div class="inner-inner"><!--M^s0-8 s0 8--><div>Hello inner-inner</div><!--M/--></div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-8",0,{"name":"inner-inner"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await/components/hello/index.marko"]})||w.$components})()</script><!--M^s0-9 s0 9--><div>Hello inner</div><!--M/--></div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-9",0,{"name":"inner"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await/components/hello/index.marko"]})||w.$components})()</script><!--M^s0-10 s0 10--><div>Hello outer</div><!--M/--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"r":"M","w":[["s0-10",0,{"name":"outer"},{"f":1}]],"t":["/marko-test$1.0.0/render/fixtures-async/components-await/components/hello/index.marko"]})||w.$components})()</script><!--FLUSH--></body></html>

View File

@ -16,6 +16,10 @@
"layout-use",
"layout-placeholder",
"unless",
"await",
"await-placeholder",
"await-timeout",
"await-error",
"script",
"class",
"else",
@ -32,6 +36,7 @@
"module-code",
"static",
"while",
"await-reorderer",
"html-comment",
"a",
"abbr",
@ -227,11 +232,6 @@
"use",
"view",
"vkern",
"await",
"await-reorderer",
"await-placeholder",
"await-timeout",
"await-error",
"cached-fragment",
"_component",
"component-globals",