Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proxy elements - or a marriage of decorators, parser transforms, and partials - covers lightweight, dynamic, and async components - RFC #3106

Closed
wants to merge 3 commits into from
Closed
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
3 changes: 3 additions & 0 deletions src/Ractive.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import interpolators from './Ractive/static/interpolators';
import { svg, win } from './config/environment';
import proto from './Ractive/prototype';
import { extend, extendWith } from './extend/_extend';
import { proxy } from './extend/_proxy';
import parse from './parse/_parse';
import getContext, { getNodeInfo } from './Ractive/static/getContext';
import isInstance from './Ractive/static/isInstance';
Expand Down Expand Up @@ -76,6 +77,7 @@ defineProperties( Ractive, {
joinKeys: { value: joinKeys },
normaliseKeypath: { value: normalise },
parse: { value: parse },
proxy: { value: proxy },
splitKeypath: { value: splitKeypath },
// sharedSet and styleSet are in _extend because circular refs
unescapeKey: { value: unescapeKey },
Expand All @@ -96,6 +98,7 @@ defineProperties( Ractive, {
extensions: { value: [] },
interpolators: { writable: true, value: interpolators },
partials: { writable: true, value: {} },
proxies: { writable: true, value: {} },
transitions: { writable: true, value: {} },

// CSS variables
Expand Down
46 changes: 24 additions & 22 deletions src/Ractive/config/custom/css/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,7 @@ export default {
value: new CSSModel( Child )
});

if ( !options.css ) return;

let css = typeof options.css === 'string' && !hasCurly.test( options.css ) ?
( getElement( options.css ) || options.css ) :
options.css;

const id = options.cssId || uuid();

if ( typeof css === 'object' ) {
css = 'textContent' in css ? css.textContent : css.innerHTML;
} else if ( typeof css === 'function' ) {
Child._css = options.css;
css = evalCSS( Child, css );
}

const def = Child._cssDef = { transform: !options.noCssTransform };

def.styles = def.transform ? transformCss( css, id ) : css;
def.id = proto.cssId = id;
Child._cssIds.push( id );

addCSS( Child._cssDef );
if ( options.css ) initCSS( options, Child, proto );
},

// Called when creating a new component instance
Expand Down Expand Up @@ -90,3 +69,26 @@ export function evalCSS ( component, css ) {
const result = css.call( component, data );
return typeof result === 'string' ? result : '';
}

export function initCSS ( options, target, proto ) {
let css = typeof options.css === 'string' && !hasCurly.test( options.css ) ?
( getElement( options.css ) || options.css ) :
options.css;

const id = options.cssId || uuid();

if ( typeof css === 'object' ) {
css = 'textContent' in css ? css.textContent : css.innerHTML;
} else if ( typeof css === 'function' ) {
target._css = options.css;
css = evalCSS( target, css );
}

const def = target._cssDef = { transform: !options.noCssTransform };

def.styles = def.transform ? transformCss( css, id ) : css;
def.id = proto.cssId = id;
target._cssIds.push( id );

addCSS( target._cssDef );
}
1 change: 0 additions & 1 deletion src/Ractive/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default {
sanitize: false,
stripComments: true,
contextLines: 0,
parserTransforms: [],

// data & binding:
data: {},
Expand Down
1 change: 1 addition & 0 deletions src/Ractive/config/registries.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const registryNames = [
'events',
'interpolators',
'partials',
'proxies',
'transitions'
];

Expand Down
1 change: 0 additions & 1 deletion src/Ractive/config/runtime-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const parseOptions = [
'sanitize',
'stripComments',
'contextLines',
'parserTransforms',
'allowExpressions',
'attributes'
];
Expand Down
1 change: 1 addition & 0 deletions src/Ractive/construct.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const registryNames = [
'events',
'interpolators',
'partials',
'proxies',
'transitions'
];

Expand Down
2 changes: 2 additions & 0 deletions src/config/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const ANCHOR = 11;
export const ATTRIBUTE = 13;
export const CLOSING_TAG = 14;
export const COMPONENT = 15;
export const PROXY = 49;
export const YIELDER = 16;
export const INLINE_PARTIAL = 17;
export const DOCTYPE = 18;
Expand Down Expand Up @@ -52,3 +53,4 @@ export const DECORATOR = 71;
export const TRANSITION = 72;
export const BINDING_FLAG = 73;
export const DELEGATE_FLAG = 74;
export const PROXY_FLAG = 75;
25 changes: 25 additions & 0 deletions src/extend/_proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styleSet from '../Ractive/static/styleSet';
import CSSModel from 'src/model/specials/CSSModel';
import { assign, create, defineProperties, defineProperty } from 'utils/object';

import { initCSS } from 'src/Ractive/config/custom/css/css';

export function proxy ( fn, opts ) {
if ( typeof fn !== 'function' ) throw new Error( `The proxy must be a function` );

assign( fn, opts );

defineProperties( fn, {
extensions: { value: [] },
_cssIds: { value: [] },
cssData: { value: assign( create( this.cssData ), fn.cssData || {} ) },

styleSet: { value: styleSet.bind( fn ) }
});

defineProperty( fn, '_cssModel', { value: new CSSModel( fn ) } );

if ( fn.css ) initCSS( fn, fn, fn );

return fn;
}
1 change: 0 additions & 1 deletion src/model/specials/CSSModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export default class CSSModel extends SharedModel {
if ( this.locked ) return;

const component = this.component;

component.extensions.forEach( e => {
const model = e._cssModel;
model.mark();
Expand Down
58 changes: 0 additions & 58 deletions src/parse/_parse.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { TEMPLATE_VERSION } from 'config/template';
import { ELEMENT } from 'config/types';
import Parser from './Parser';
import readMustache from './converters/readMustache';
import readTriple from './converters/mustache/readTriple';
Expand All @@ -17,7 +16,6 @@ import cleanup from './utils/cleanup';
import insertExpressions from './utils/insertExpressions';
import shared from '../Ractive/shared';
import { create, keys } from 'utils/object';
import { isArray } from 'utils/is';

// See https://github.com/ractivejs/template-spec for information
// about the Ractive template specification
Expand Down Expand Up @@ -75,13 +73,6 @@ const StandardParser = Parser.extend({
this.allowExpressions = options.allowExpressions;

if ( options.attributes ) this.inTag = true;

this.transforms = options.transforms || options.parserTransforms;
if ( this.transforms ) {
this.transforms = this.transforms.concat( shared.defaults.parserTransforms );
} else {
this.transforms = shared.defaults.parserTransforms;
}
},

postProcess ( result ) {
Expand All @@ -96,55 +87,6 @@ const StandardParser = Parser.extend({

cleanup( result[0].t, this.stripComments, this.preserveWhitespace, !this.preserveWhitespace, !this.preserveWhitespace );

const transforms = this.transforms;
if ( transforms.length ) {
const tlen = transforms.length;
const walk = function ( fragment ) {
let len = fragment.length;

for ( let i = 0; i < len; i++ ) {
let node = fragment[i];

if ( node.t === ELEMENT ) {
for ( let j = 0; j < tlen; j++ ) {
const res = transforms[j].call( shared.Ractive, node );
if ( !res ) {
continue;
} else if ( res.remove ) {
fragment.splice( i--, 1 );
len--;
break;
} else if ( res.replace ) {
if ( isArray( res.replace ) ) {
fragment.splice( i--, 1, ...res.replace );
len += res.replace.length - 1;
} else {
fragment[i--] = node = res.replace;
}

break;
}
}

// watch for partials
if ( node.p && !isArray( node.p ) ) {
for ( const k in node.p ) walk( node.p[k] );
}
}

if ( node.f ) walk( node.f );
}
};

// process the root fragment
walk( result[0].t );

// watch for root partials
if ( result[0].p && !isArray( result[0].p ) ) {
for ( const k in result[0].p ) walk( result[0].p[k] );
}
}

if ( this.csp !== false ) {
const expr = {};
insertExpressions( result[0].t, expr );
Expand Down
5 changes: 3 additions & 2 deletions src/parse/converters/element/readAttribute.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ARRAY_LITERAL, ATTRIBUTE, DECORATOR, DELEGATE_FLAG, BINDING_FLAG, INTERPOLATOR, TRANSITION, EVENT } from '../../../config/types';
import { ARRAY_LITERAL, ATTRIBUTE, DECORATOR, DELEGATE_FLAG, BINDING_FLAG, INTERPOLATOR, PROXY_FLAG, TRANSITION, EVENT } from '../../../config/types';
import getLowestIndex from '../utils/getLowestIndex';
import readMustache from '../readMustache';
import { decodeCharacterReferences } from 'src/utils/html';
Expand All @@ -17,7 +17,8 @@ const boundPattern = /^((bind|class)-(([-a-zA-Z0-9_])+))$/;
const directives = {
lazy: { t: BINDING_FLAG, v: 'l' },
twoway: { t: BINDING_FLAG, v: 't' },
'no-delegation': { t: DELEGATE_FLAG }
'no-delegation': { t: DELEGATE_FLAG },
'no-proxy': { t: PROXY_FLAG }
};
const unquotedAttributeValueTextPattern = /^[^\s"'=<>\/`]+/;
const proxyEvent = /^[^\s"'=<>@\[\]()]*/;
Expand Down
5 changes: 3 additions & 2 deletions src/view/items/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { teardown } from 'src/Ractive/prototype/teardown';
import getRactiveContext from 'shared/getRactiveContext';
import { warnIfDebug } from 'utils/log';
import { createDocumentFragment } from 'utils/dom';
import { ANCHOR, ATTRIBUTE, BINDING_FLAG, COMPONENT, DECORATOR, EVENT, TRANSITION, YIELDER } from 'config/types';
import { ANCHOR, ATTRIBUTE, BINDING_FLAG, COMPONENT, DECORATOR, EVENT, PROXY_FLAG, TRANSITION, YIELDER } from 'config/types';
import construct from 'src/Ractive/construct';
import initialise from 'src/Ractive/initialise';
import render from 'src/Ractive/render';
Expand Down Expand Up @@ -96,9 +96,10 @@ export default class Component extends Item {
}) );
break;

case TRANSITION:
case BINDING_FLAG:
case DECORATOR:
case PROXY_FLAG:
case TRANSITION:
break;

default:
Expand Down
4 changes: 3 additions & 1 deletion src/view/items/Element.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ATTRIBUTE, BINDING_FLAG, DECORATOR, DELEGATE_FLAG, EVENT, TRANSITION } from 'config/types';
import { ATTRIBUTE, BINDING_FLAG, DECORATOR, DELEGATE_FLAG, EVENT, PROXY_FLAG, TRANSITION } from 'config/types';
import { win } from 'config/environment';
import { html, svg } from 'config/namespaces';
import { toArray, addToArray, removeFromArray } from 'utils/array';
Expand Down Expand Up @@ -70,6 +70,8 @@ export default class Element extends ContainerItem {
this.delegate = false;
break;

case PROXY_FLAG: break;

default:
( leftovers || ( leftovers = [] ) ).push( template );
break;
Expand Down
2 changes: 1 addition & 1 deletion src/view/items/Partial.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export default class Partial extends MustacheContainer {
}
}

function parsePartial( name, partial, ractive ) {
export function parsePartial( name, partial, ractive ) {
let parsed;

try {
Expand Down
Loading