Skip to content

Commit

Permalink
feat: improve serialization across multiple writes (#1542)
Browse files Browse the repository at this point in the history
* feat: improve serialization across multiple writes
  • Loading branch information
DylanPiercey committed Apr 9, 2020
1 parent 7526081 commit 81c4da7
Show file tree
Hide file tree
Showing 31 changed files with 311 additions and 224 deletions.
15 changes: 5 additions & 10 deletions packages/marko/src/core-tags/components/body-transformer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
"use strict";

module.exports = function transform(el, context) {
let componentGlobalsNode = context.createNodeForEl("component-globals");
el.prependChild(componentGlobalsNode);

let initComponentsNode = context.createNodeForEl("init-components");
el.appendChild(initComponentsNode);

// Make <await-reorderer> optional. Automatically insert it before the
// body tag.
let awaitReorderer = context.createNodeForEl("await-reorderer");
el.appendChild(awaitReorderer);
if (context.outputType === "html") {
el.appendChild(context.createNodeForEl("init-components"));
el.appendChild(context.createNodeForEl("await-reorderer"));
el.appendChild(context.createNodeForEl("_preferred-script-location"));
}
};

This file was deleted.

45 changes: 0 additions & 45 deletions packages/marko/src/core-tags/components/component-globals-tag.js

This file was deleted.

15 changes: 3 additions & 12 deletions packages/marko/src/core-tags/components/init-components-tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,10 @@ function addInitScript(writer) {
writer.script(getInitComponentsCode(out, componentDefs));
}

function forceScriptTagAtThisPoint(out) {
const writer = out.writer;
const htmlSoFar = writer.toString();
writer.clear();
writer.write(htmlSoFar);
}

module.exports = function render(input, out) {
const outGlobal = out.global;
if (outGlobal[INIT_COMPONENTS_KEY] === undefined) {
outGlobal[INIT_COMPONENTS_KEY] = true;
const $global = out.global;
if ($global[INIT_COMPONENTS_KEY] === undefined) {
$global[INIT_COMPONENTS_KEY] = true;

out.on("await:finish", addComponentsFromOut);
out.on("___toString", addInitScript);
Expand All @@ -40,7 +33,6 @@ module.exports = function render(input, out) {
// Generate initialization code for any of the UI components that were
// rendered synchronously
addComponentsFromOut(out);
forceScriptTagAtThisPoint(out);
} else {
// Generate initialization code for any of the UI components that were
// rendered asynchronously, but were outside an `<await>` tag
Expand All @@ -54,7 +46,6 @@ module.exports = function render(input, out) {
}
// Write out all of the component init code from the main out
addComponentsFromOut(rootOut, asyncOut);
forceScriptTagAtThisPoint(asyncOut);
asyncOut.end();
next();
});
Expand Down
9 changes: 4 additions & 5 deletions packages/marko/src/core-tags/components/marko.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,15 @@
"code-generator": "./component-tag.js",
"autocomplete": []
},
"<component-globals>": {
"renderer": "./component-globals-tag.js",
"no-output": true,
"autocomplete": []
},
"<init-components>": {
"renderer": "./init-components-tag.js",
"no-output": true,
"@immediate": "boolean"
},
"<_preferred-script-location>": {
"renderer": "./preferred-script-location-tag.js",
"no-output": true
},
"<_preserve>": {
"renderer": "./preserve-tag.js",
"@cid": "string",
Expand Down
1 change: 0 additions & 1 deletion packages/marko/src/core-tags/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"browser": {
"./component-globals-tag.js": "./component-globals-tag-browser.js",
"./getRequirePath.js": "./getRequirePath-browser.js",
"./init-components-tag.js": "./init-components-tag-browser.js",
"./preserve-tag.js": "./preserve-tag-browser.js"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use strict";

function forceScriptTagAtThisPoint(out) {
const writer = out.writer;

out.global.___isLastFlush = true;
const htmlSoFar = writer.toString();
out.global.___isLastFlush = undefined;

writer.clear();
writer.write(htmlSoFar);
}

module.exports = function render(input, out) {
if (out.isSync() === true) {
forceScriptTagAtThisPoint(out);
} else {
const asyncOut = out.beginAsync({ last: true, timeout: -1 });
out.onLast(function(next) {
forceScriptTagAtThisPoint(asyncOut);
asyncOut.end();
next();
});
}
};
8 changes: 5 additions & 3 deletions packages/marko/src/core-tags/core/await/reorderer-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ module.exports = function(input, out) {

var global = out.global;

out.flush();

// We have already invoked an <await-reorderer. We do not need to do this
// We have already invoked an <await-reorderer>. We do not need to do this
// work again.
if (global.__awaitReordererInvoked) {
return;
}

global.__awaitReordererInvoked = true;

if (out.global.___clientReorderContext) {
out.flush();
}

var asyncOut = out.beginAsync({
last: true,
timeout: -1,
Expand Down
7 changes: 7 additions & 0 deletions packages/marko/src/core-tags/migrate/component-globals-tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function render(elNode, context) {
context.deprecate(
"The <component-globals> tag is deprecated. This functionality has been added to the '<init-components>' tag (which is automatically inserted at the end of the <body> tag), you can safely remove the '<component-globals>' tag."
);

elNode.detach();
};
4 changes: 4 additions & 0 deletions packages/marko/src/core-tags/migrate/marko.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,5 +221,9 @@
"<class>": {
"migrator": "./class-tag",
"open-tag-only": true
},
"<component-globals>": {
"migrator": "./component-globals-tag.js",
"deprecated": true
}
}
133 changes: 95 additions & 38 deletions packages/marko/src/runtime/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

var warp10 = require("warp10");
var safeJSONRegExp = /<\/|\u2028|\u2029/g;
var IGNORE_GLOBAL_TYPES = new Set(["undefined", "function", "symbol"]);
var DEFAULT_RUNTIME_ID = "M";

// var FLAG_WILL_RERENDER_IN_BROWSER = 1;
var FLAG_HAS_RENDER_BODY = 2;
Expand All @@ -26,9 +28,33 @@ function isNotEmpty(obj) {

return false;
}
function safeStringify(data) {
return JSON.stringify(warp10.stringifyPrepare(data)).replace(
safeJSONRegExp,
safeJSONReplacer
);
}

function getSerializedGlobals($global) {
let serializedGlobalsLookup = $global.serializedGlobals;
if (serializedGlobalsLookup) {
let serializedGlobals;
let keys = Object.keys(serializedGlobalsLookup);
for (let i = keys.length; i--; ) {
let key = keys[i];
if (serializedGlobalsLookup[key]) {
let value = $global[key];
if (!IGNORE_GLOBAL_TYPES.has(typeof value)) {
if (serializedGlobals === undefined) {
serializedGlobals = {};
}
serializedGlobals[key] = value;
}
}
}

function safeJSON(json) {
return json.replace(safeJSONRegExp, safeJSONReplacer);
return serializedGlobals;
}
}

function addComponentsFromContext(componentsContext, componentsToHydrate) {
Expand Down Expand Up @@ -139,41 +165,82 @@ function addComponentsFromContext(componentsContext, componentsToHydrate) {
}
}

function getInitComponentsData(componentDefs, runtimeId) {
let len;
if ((len = componentDefs.length) === 0) {
function getInitComponentsData(out, componentDefs) {
const len = componentDefs.length;
const $global = out.global;
const isLast = $global.___isLastFlush;
const didSerializeComponents = $global.___didSerializeComponents;
const prefix = $global.componentIdPrefix || $global.widgetIdPrefix;

if (len === 0) {
if (isLast && didSerializeComponents) {
return { p: prefix, l: 1 };
}

return;
}

const typesLookup = {};
const componentTypes = [];
const TYPE_INDEX = 1;
const typesLookup =
$global.___typesLookup || ($global.___typesLookup = new Map());
let newTypes;

for (let i = 0; i < len; i++) {
const componentDef = componentDefs[i];
const typeName = componentDef[TYPE_INDEX];
let typeIndex = typesLookup[typeName];
let typeIndex = typesLookup.get(typeName);

if (typeIndex === undefined) {
typeIndex = componentTypes.length;
componentTypes.push(typeName);
typesLookup[typeName] = typeIndex;
typeIndex = typesLookup.size;
typesLookup.set(typeName, typeIndex);

if (newTypes) {
newTypes.push(typeName);
} else {
newTypes = [typeName];
}
}

componentDef[TYPE_INDEX] = typeIndex;
}
return { r: runtimeId, w: componentDefs, t: componentTypes };

let serializedGlobals;

if (!didSerializeComponents) {
$global.___didSerializeComponents = true;
serializedGlobals = getSerializedGlobals($global);
}

return {
p: prefix,
l: isLast && 1,
g: serializedGlobals,
w: componentDefs,
t: newTypes
};
}

function getInitComponentsDataFromOut(out) {
var componentsContext = out.___components;
const componentsContext = out.___components;

if (componentsContext === null) {
return;
}

var componentsToHydrate = [];

const $global = out.global;
const runtimeId = $global.runtimeId;
const componentsToHydrate = [];
addComponentsFromContext(componentsContext, componentsToHydrate);

return getInitComponentsData(componentsToHydrate, out.global.runtimeId);
$global.___isLastFlush = true;
const data = getInitComponentsData(out, componentsToHydrate);
$global.___isLastFlush = undefined;

if (runtimeId !== DEFAULT_RUNTIME_ID) {
data.r = runtimeId;
}

return data;
}

function writeInitComponentsCode(out) {
Expand All @@ -184,31 +251,22 @@ exports.___getInitComponentsCode = function getInitComponentsCode(
out,
componentDefs
) {
var runtimeId = out.global.runtimeId;
var initComponentsData;

if (arguments.length === 2) {
initComponentsData = getInitComponentsData(componentDefs, runtimeId);
} else {
initComponentsData = getInitComponentsDataFromOut(out);
}
const initComponentsData =
arguments.length === 2
? getInitComponentsData(out, componentDefs)
: getInitComponentsDataFromOut(out);

if (initComponentsData === undefined) {
return "";
}

var componentGlobalKey =
"$" + (runtimeId === "M" ? "components" : runtimeId + "_components");

return (
componentGlobalKey +
"=(window." +
componentGlobalKey +
"||[]).concat(" +
safeJSON(warp10.stringify(initComponentsData)) +
")||" +
componentGlobalKey
);
const runtimeId = out.global.runtimeId;
const componentGlobalKey =
runtimeId === DEFAULT_RUNTIME_ID ? "MC" : runtimeId + "_C";

return `$${componentGlobalKey}=(window.$${componentGlobalKey}||[]).concat(${safeStringify(
initComponentsData
)})`;
};

exports.___addComponentsFromContext = addComponentsFromContext;
Expand All @@ -222,6 +280,5 @@ exports.writeInitComponentsCode = writeInitComponentsCode;
* @return {Object} An object with information about the rendered components that can be serialized to JSON. The object should be treated as opaque
*/
exports.getRenderedComponents = function(out) {
var initComponentsData = getInitComponentsDataFromOut(out);
return warp10.stringifyPrepare(initComponentsData);
return warp10.stringifyPrepare(getInitComponentsDataFromOut(out));
};

0 comments on commit 81c4da7

Please sign in to comment.