Skip to content
This repository was archived by the owner on Apr 4, 2019. It is now read-only.

[WIP] Rehydration #404

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/dom-helper/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,47 @@ prototype.parseHTML = function(html, contextualElement) {
return fragment;
};

const SHOW_TEXT = 4;

prototype.preserveTextNodes = function(root) {
let iterator = this.document.createNodeIterator(root, SHOW_TEXT, function(node) {
return node.nodeValue === '' || (node.previousSibling && node.previousSibling.nodeType === 3);
});

let node;

/*jshint boss:true*/
while (node = iterator.nextNode()) {
let element = this.createElement('script');

if (node.nodeValue === '') {
this.setAttribute(element, 'data-hbs', 'boundary');
node.parentNode.replaceChild(element, node);
} else {
this.setAttribute(element, 'data-hbs', 'separator');
node.parentNode.insertBefore(element, node);
}
}
};

prototype.restoreTextNodes = function(root) {
let elements = root.querySelectorAll('script[data-hbs]');

for (let i=0, l=elements.length; i<l; i++) {
let element = elements[i];

switch (this.getAttribute(element, 'data-hbs')) {
case 'boundary':
element.parentNode.replaceChild(this.createTextNode(''), element);
break;

case 'separator':
element.parentNode.removeChild(element);
break;
}
}
};

var parsingNode;

// Used to determine whether a URL needs to be sanitized.
Expand Down
3 changes: 2 additions & 1 deletion packages/htmlbars-compiler/lib/template-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import FragmentJavaScriptCompiler from './fragment-javascript-compiler';
import HydrationOpcodeCompiler from './hydration-opcode-compiler';
import HydrationJavaScriptCompiler from './hydration-javascript-compiler';
import TemplateVisitor from "./template-visitor";
import { processOpcodes } from "./utils";
import { processOpcodes, generateId } from "./utils";
import { repeat } from "../htmlbars-util/quoting";
import { map } from "../htmlbars-util/array-utils";

Expand Down Expand Up @@ -110,6 +110,7 @@ TemplateCompiler.prototype.endProgram = function(program, programDepth) {
indent+' return {\n' +
this.buildMeta(indent+' ', program) +
indent+' isEmpty: ' + (program.body.length ? 'false' : 'true') + ',\n' +
indent+' id: "' + generateId() + '",\n' +
indent+' arity: ' + blockParams.length + ',\n' +
indent+' cachedFragment: null,\n' +
indent+' hasRendered: false,\n' +
Expand Down
30 changes: 30 additions & 0 deletions packages/htmlbars-compiler/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/*globals window:false*/
/*globals Uint8Array:false*/

export function processOpcodes(compiler, opcodes) {
for (var i=0, l=opcodes.length; i<l; i++) {
var method = opcodes[i][0];
Expand All @@ -9,3 +12,30 @@ export function processOpcodes(compiler, opcodes) {
}
}
}

let lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var generateId;

if (typeof window !== 'undefined' && window.crypto) {
generateId = function() {
let buf = new Uint8Array(12);
window.crypto.getRandomValues(buf);

buf = buf.map(i => i % 64);
return [].slice.call(buf).map(i => lookup.charAt(i)).join('');
};
} else {
generateId = function() {
let buf = [];

for (let i=0; i<12; i++) {
buf.push(lookup.charAt(Math.floor(Math.random() * 64)));
}

return buf.join('');
};
}

// generateId() returns a unique 12-character string consisting of random
// base64 characters.
export { generateId };
12 changes: 12 additions & 0 deletions packages/htmlbars-compiler/tests/compile-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,15 @@ test('options are not required for `compile`', function () {

ok(template.meta, 'meta is present in template, even if empty');
});

test('templates get unique ids', function() {
var template1 = compile('{{#if foo}}hello{{/if}}');

ok(typeof template1.raw.id === 'string', 'the top-level template has an id');
ok(typeof template1.raw.templates[0].id === 'string', 'nested templates have ids');

var template2 = compile('Another template');
ok(typeof template2.raw.id === 'string', 'the top-level template has an id');

notEqual(template1.raw.id, template2.raw.id, 'different templates should have different ids');
});
Loading