sinuous
Version:
🧬 Small, fast, reactive render engine
222 lines (201 loc) • 5.57 kB
JavaScript
const MODE_SLASH = 0;
const MODE_TEXT = 1;
const MODE_WHITESPACE = 2;
const MODE_TAGNAME = 3;
const MODE_COMMENT = 4;
const MODE_PROP_SET = 5;
const MODE_PROP_APPEND = 6;
const TAG_SET = 1;
const CHILD_APPEND = 0;
const CHILD_RECURSE = 2;
const PROPS_ASSIGN = 3;
const PROP_SET = MODE_PROP_SET;
const PROP_APPEND = MODE_PROP_APPEND;
const evaluate = (h, built, fields, args) => {
for (let i = 1; i < built.length; i++) {
const field = built[i];
const value = typeof field === 'number' ? fields[field] : field;
const type = built[++i];
if (type === TAG_SET) {
args[0] = value;
}
else if (type === PROPS_ASSIGN) {
args[1] = Object.assign(args[1] || {}, value);
}
else if (type === PROP_SET) {
(args[1] = args[1] || {})[built[++i]] = value;
}
else if (type === PROP_APPEND) {
args[1][built[++i]] += (value + '');
}
else if (type) {
// code === CHILD_RECURSE
args.push(h.apply(null, evaluate(h, value, fields, ['', null])));
}
else {
// code === CHILD_APPEND
args.push(value);
}
}
return args;
};
const build = function(statics) {
let mode = MODE_TEXT;
let buffer = '';
let quote = '';
let current = [0];
let char, propName;
const commit = field => {
if (mode === MODE_TEXT && (field || (buffer = buffer.replace(/^\s*\n\s*|\s*\n\s*$/g,'')))) {
{
current.push(field || buffer, CHILD_APPEND);
}
}
else if (mode === MODE_TAGNAME && (field || buffer)) {
{
current.push(field || buffer, TAG_SET);
}
mode = MODE_WHITESPACE;
}
else if (mode === MODE_WHITESPACE && buffer === '...' && field) {
{
current.push(field, PROPS_ASSIGN);
}
}
else if (mode === MODE_WHITESPACE && buffer && !field) {
{
current.push(true, PROP_SET, buffer);
}
}
else if (mode >= MODE_PROP_SET) {
{
if (buffer || (!field && mode === MODE_PROP_SET)) {
current.push(buffer, mode, propName);
mode = MODE_PROP_APPEND;
}
if (field) {
current.push(field, mode, propName);
mode = MODE_PROP_APPEND;
}
}
}
buffer = '';
};
for (let i=0; i<statics.length; i++) {
if (i) {
if (mode === MODE_TEXT) {
commit();
}
commit(i);
}
for (let j=0; j<statics[i].length;j++) {
char = statics[i][j];
if (mode === MODE_TEXT) {
if (char === '<') {
// commit buffer
commit();
{
current = [current];
}
mode = MODE_TAGNAME;
}
else {
buffer += char;
}
}
else if (mode === MODE_COMMENT) {
// Ignore everything until the last three characters are '-', '-' and '>'
if (buffer === '--' && char === '>') {
mode = MODE_TEXT;
buffer = '';
}
else {
buffer = char + buffer[0];
}
}
else if (quote) {
if (char === quote) {
quote = '';
}
else {
buffer += char;
}
}
else if (char === '"' || char === "'") {
quote = char;
}
else if (char === '>') {
commit();
mode = MODE_TEXT;
}
else if (!mode) ;
else if (char === '=') {
mode = MODE_PROP_SET;
propName = buffer;
buffer = '';
}
else if (char === '/' && (mode < MODE_PROP_SET || statics[i][j+1] === '>')) {
commit();
if (mode === MODE_TAGNAME) {
current = current[0];
}
mode = current;
{
(current = current[0]).push(mode, CHILD_RECURSE);
}
mode = MODE_SLASH;
}
else if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
// <a disabled>
commit();
mode = MODE_WHITESPACE;
}
else {
buffer += char;
}
if (mode === MODE_TAGNAME && buffer === '!--') {
mode = MODE_COMMENT;
current = current[0];
}
}
}
commit();
return current;
};
/**
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const getCacheMap = (statics) => {
let tpl = CACHE.get(statics);
if (!tpl) {
CACHE.set(statics, tpl = build(statics));
}
return tpl;
};
const getCacheKeyed = (statics) => {
let key = '';
for (let i = 0; i < statics.length; i++) {
key += statics[i].length + '-' + statics[i];
}
return CACHE[key] || (CACHE[key] = build(statics));
};
const USE_MAP = typeof Map === 'function';
const CACHE = USE_MAP ? new Map() : {};
const getCache = USE_MAP ? getCacheMap : getCacheKeyed;
const cached = function(statics) {
const children = evaluate(this, getCache(statics), arguments, []);
const result = children.length > 1 ? children : children[0];
return Array.isArray(result) ? this(result)
: result instanceof Node ? result : this([result]);
};
var index = cached;
export default index;