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

Anchor-less if and each blocks #512

Merged
merged 8 commits into from
Apr 25, 2017
Merged
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
5 changes: 0 additions & 5 deletions src/generators/dom/Block.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,6 @@ export default class Block {
return this.generator.contextualise( this, expression, context, isEventHandler );
}

createAnchor ( name, parentNode ) {
const renderStatement = `${this.generator.helper( 'createComment' )}()`;
this.addElement( name, renderStatement, parentNode, true );
}

findDependencies ( expression ) {
return this.generator.findDependencies( this.contextDependencies, this.indexes, expression );
}
Expand Down
4 changes: 2 additions & 2 deletions src/generators/dom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ export default function dom ( parsed, source, options ) {

const { computations, hasJs, templateProperties, namespace } = generator.parseJs();

const block = preprocess( generator, parsed.html );

const state = {
namespace,
parentNode: null,
isTopLevel: true
};

const block = preprocess( generator, state, parsed.html );

parsed.html.children.forEach( node => {
visit( generator, block, state, node );
});
Expand Down
147 changes: 115 additions & 32 deletions src/generators/dom/preprocess.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,62 @@
import Block from './Block.js';
import { trimStart, trimEnd } from '../../utils/trim.js';
import { assign } from '../../shared/index.js';

function isElseIf ( node ) {
return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
}

function getChildState ( parent, child ) {
return assign( {}, parent, { name: null, parentNode: null }, child || {} );
}

// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elementsWithoutText = new Set([
'audio',
'datalist',
'dl',
'ol',
'optgroup',
'select',
'ul',
'video'
]);

const preprocessors = {
MustacheTag: ( generator, block, node ) => {
MustacheTag: ( generator, block, state, node ) => {
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );

node._state = getChildState( state, {
name: block.getUniqueName( 'text' )
});
},

RawMustacheTag: ( generator, block, state, node ) => {
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );

const basename = block.getUniqueName( 'raw' );
const name = block.getUniqueName( `${basename}_before` );

node._state = getChildState( state, { basename, name });
},

Text: ( generator, block, state, node ) => {
node._state = getChildState( state );

if ( !/\S/.test( node.data ) ) {
if ( state.namespace ) return;
if ( elementsWithoutText.has( state.parentNodeName ) ) return;
}

node._state.shouldCreate = true;
node._state.name = block.getUniqueName( `text` );
},

IfBlock: ( generator, block, node ) => {
IfBlock: ( generator, block, state, node ) => {
const blocks = [];
let dynamic = false;

Expand All @@ -23,8 +68,10 @@ const preprocessors = {
name: generator.getUniqueName( `create_if_block` )
});

node._state = getChildState( state );

blocks.push( node._block );
preprocessChildren( generator, node._block, node );
preprocessChildren( generator, node._block, node._state, node );

if ( node._block.dependencies.size > 0 ) {
dynamic = true;
Expand All @@ -38,8 +85,10 @@ const preprocessors = {
name: generator.getUniqueName( `create_if_block` )
});

node.else._state = getChildState( state );

blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else );
preprocessChildren( generator, node.else._block, node.else._state, node.else );

if ( node.else._block.dependencies.size > 0 ) {
dynamic = true;
Expand All @@ -57,7 +106,7 @@ const preprocessors = {
generator.blocks.push( ...blocks );
},

EachBlock: ( generator, block, node ) => {
EachBlock: ( generator, block, state, node ) => {
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );

Expand Down Expand Up @@ -97,8 +146,12 @@ const preprocessors = {
params: block.params.concat( listName, context, indexName )
});

node._state = getChildState( state, {
inEachBlock: true
});

generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node );
preprocessChildren( generator, node._block, node._state, node );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;

Expand All @@ -107,13 +160,32 @@ const preprocessors = {
name: generator.getUniqueName( `${node._block.name}_else` )
});

node.else._state = getChildState( state );

generator.blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else );
preprocessChildren( generator, node.else._block, node.else._state, node.else );
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
}
},

Element: ( generator, block, node ) => {
Element: ( generator, block, state, node ) => {
const isComponent = generator.components.has( node.name ) || node.name === ':Self';

if ( isComponent ) {
node._state = getChildState( state );
} else {
const name = block.getUniqueName( node.name.replace( /[^a-zA-Z0-9_$]/g, '_' ) );

node._state = getChildState( state, {
isTopLevel: false,
name,
parentNode: name,
parentNodeName: node.name,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace,
allUsedContexts: []
});
}

node.attributes.forEach( attribute => {
if ( attribute.type === 'Attribute' && attribute.value !== true ) {
attribute.value.forEach( chunk => {
Expand All @@ -130,8 +202,6 @@ const preprocessors = {
}
});

const isComponent = generator.components.has( node.name ) || node.name === ':Self';

if ( node.children.length ) {
if ( isComponent ) {
const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() );
Expand All @@ -141,21 +211,19 @@ const preprocessors = {
});

generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node );
preprocessChildren( generator, node._block, node._state, node );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
}

else {
preprocessChildren( generator, block, node );
preprocessChildren( generator, block, node._state, node );
}
}
}
};

preprocessors.RawMustacheTag = preprocessors.MustacheTag;

function preprocessChildren ( generator, block, node ) {
function preprocessChildren ( generator, block, state, node, isTopLevel ) {
// glue text nodes together
const cleaned = [];
let lastChild;
Expand All @@ -173,15 +241,43 @@ function preprocessChildren ( generator, block, node ) {
lastChild = child;
});

node.children = cleaned;
if ( isTopLevel ) {
// trim leading and trailing whitespace from the top level
const firstChild = cleaned[0];
if ( firstChild && firstChild.type === 'Text' ) {
firstChild.data = trimStart( firstChild.data );
if ( !firstChild.data ) cleaned.shift();
}

const lastChild = cleaned[ cleaned.length - 1 ];
if ( lastChild && lastChild.type === 'Text' ) {
lastChild.data = trimEnd( lastChild.data );
if ( !lastChild.data ) cleaned.pop();
}
}

lastChild = null;

cleaned.forEach( child => {
const preprocess = preprocessors[ child.type ];
if ( preprocess ) preprocess( generator, block, child );
if ( preprocess ) preprocess( generator, block, state, child );

if ( lastChild ) {
lastChild.next = child;
lastChild.needsAnchor = !child._state.name;
}

lastChild = child;
});

if ( lastChild ) {
lastChild.needsAnchor = !state.parentNode;
}

node.children = cleaned;
}

export default function preprocess ( generator, node ) {
export default function preprocess ( generator, state, node ) {
const block = new Block({
generator,
name: generator.alias( 'create_main_fragment' ),
Expand All @@ -199,21 +295,8 @@ export default function preprocess ( generator, node ) {
});

generator.blocks.push( block );
preprocessChildren( generator, block, node );
preprocessChildren( generator, block, state, node, true );
block.hasUpdateMethod = block.dependencies.size > 0;

// trim leading and trailing whitespace from the top level
const firstChild = node.children[0];
if ( firstChild && firstChild.type === 'Text' ) {
firstChild.data = trimStart( firstChild.data );
if ( !firstChild.data ) node.children.shift();
}

const lastChild = node.children[ node.children.length - 1 ];
if ( lastChild && lastChild.type === 'Text' ) {
lastChild.data = trimEnd( lastChild.data );
if ( !lastChild.data ) node.children.pop();
}

return block;
}
4 changes: 1 addition & 3 deletions src/generators/dom/visitors/Component/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ export default function visitComponent ( generator, block, state, node ) {
const hasChildren = node.children.length > 0;
const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() );

const childState = Object.assign( {}, state, {
parentNode: null
});
const childState = node._state;

const local = {
name,
Expand Down
Loading