lib/index.js
function replaceTokens (templateAst, componentNode) {
	let promise;

	if (componentNode.content) {
		const tokenDefinitions = {};

		componentNode.content.forEach(function (tokenDefinition) {
			if (tokenDefinition.name) {
				let tokenValue = '';

				if (tokenDefinition.content) {
					if (tokenDefinition.content.length === 1 && tokenDefinition.content[0].type === 'text') {
						tokenValue = tokenDefinition.content[0].content;
					}
					else {
						tokenValue = tokenDefinition.content;
					}
				}

				tokenDefinitions[tokenDefinition.name] = tokenValue;
			}
		});

		promise = modifyNodes(templateAst,
			function (node) {
				return node.attrs || (node.type === 'text' && tokenRegExp.test(node.content));
			},
			function (node) {
				if (node.attrs) {
					/**
					 * Take a node and do attribute replacement, e.g.:
					 * <div class="card ${state}" ${required} ${autocomplete} ${unused}></div>
					 * From the following component:
					 * <component>
					 *		<state>active</state>
					 *		<required/>
					 *		<autocomplete>postal-address</autocomplete>
					 * </component>
					 * Produces the output (note that ${unused} has been trimmed):
					 * <div class="card active" required autocomplete="postal-address"></div>
					 */
					Object.keys(node.attrs).forEach(function (attributeName) {
						// This code gets confusing fast:
						// attributeName: this is the name of the attribute in the template node
						// It is a token, e.g. ${pattern} and we are going to remove it from the node

						tokenRegExp.lastIndex = 0;
						const match = tokenRegExp.exec(attributeName);

						if (match) {
							// newAttributeName: this is the inner part of 'attributeName' and is going to be the name
							// of the new attribute we add to the node, e.g. 'pattern'
							const newAttributeName = match[1];
							const newAttributeValue = tokenDefinitions[newAttributeName] ||
								node.attrs[attributeName][0].content;

							if (newAttributeValue || newAttributeName in tokenDefinitions) {
								node.attrs[newAttributeName] = [
									{
										type: 'text',
										content: newAttributeValue,
										// TODO: this is not accurate, but should new nodes have no location?
										location: node.attrs[attributeName].location,
									},
								];
							}

							delete node.attrs[attributeName];
						}
						else {
							tokenRegExp.lastIndex = 0;
							const attributeNode = node.attrs[attributeName][0];
							replaceToken(attributeNode, tokenDefinitions, true);
						}
					});
				}
				else if (node.type === 'text') {
					node = replaceToken(node, tokenDefinitions);
				}

				return node;
			}
		);
	}

	return promise;
}
function replaceTokens (templateAst, componentNode) {
	let promise;

	if (componentNode.content) {
		const tokenDefinitions = {};

		componentNode.content.forEach(function (tokenDefinition) {
			if (tokenDefinition.name) {
				let tokenValue = '';

				if (tokenDefinition.content) {
					if (tokenDefinition.content.length === 1 && tokenDefinition.content[0].type === 'text') {
						tokenValue = tokenDefinition.content[0].content;
					}
					else {
						tokenValue = tokenDefinition.content;
					}
				}

				tokenDefinitions[tokenDefinition.name] = tokenValue;
			}
		});

		promise = modifyNodes(templateAst,
			function (node) {
				return node.attrs || (node.type === 'text' && tokenRegExp.test(node.content));
			},
			function (node) {
				if (node.attrs) {
					/**
					 * Take a node and do attribute replacement, e.g.:
					 * <div class="card ${state}" ${required} ${autocomplete} ${unused}></div>
					 * From the following component:
					 * <component>
					 *		<state>active</state>
					 *		<required/>
					 *		<autocomplete>postal-address</autocomplete>
					 * </component>
					 * Produces the output (note that ${unused} has been trimmed):
					 * <div class="card active" required autocomplete="postal-address"></div>
					 */
					Object.keys(node.attrs).forEach(function (attributeName) {
						// This code gets confusing fast:
						// attributeName: this is the name of the attribute in the template node
						// It is a token, e.g. ${pattern} and we are going to remove it from the node

						tokenRegExp.lastIndex = 0;
						const match = tokenRegExp.exec(attributeName);

						if (match) {
							// newAttributeName: this is the inner part of 'attributeName' and is going to be the name
							// of the new attribute we add to the node, e.g. 'pattern'
							const newAttributeName = match[1];
							const newAttributeValue = tokenDefinitions[newAttributeName] ||
								node.attrs[attributeName][0].content;

							if (newAttributeValue || newAttributeName in tokenDefinitions) {
								node.attrs[newAttributeName] = [
									{
										type: 'text',
										content: newAttributeValue,
										// TODO: this is not accurate, but should new nodes have no location?
										location: node.attrs[attributeName].location,
									},
								];
							}

							delete node.attrs[attributeName];
						}
						else {
							tokenRegExp.lastIndex = 0;
							const attributeNode = node.attrs[attributeName][0];
							replaceToken(attributeNode, tokenDefinitions, true);
						}
					});
				}
				else if (node.type === 'text') {
					node = replaceToken(node, tokenDefinitions);
				}

				return node;
			}
		);
	}

	return promise;
}
function replaceTokens ( templateAst , componentNode ) { let promise ; if ( componentNode . content ) { const tokenDefinitions = { } ; componentNode . content . forEach ( function ( tokenDefinition ) { if ( tokenDefinition . name ) { let tokenValue = '' ; if ( tokenDefinition . content ) { if ( tokenDefinition . content . length === 1 && tokenDefinition . content [ 0 ] . type === 'text' ) { tokenValue = tokenDefinition . content [ 0 ] . content ; } else { tokenValue = tokenDefinition . content ; } } tokenDefinitions [ tokenDefinition . name ] = tokenValue ; } } ) ; promise = modifyNodes ( templateAst , function ( node ) { return node . attrs || ( node . type === 'text' && tokenRegExp . test ( node . content ) ) ; } , function ( node ) { if ( node . attrs ) { /**
					 * Take a node and do attribute replacement, e.g.:
					 * <div class="card ${state}" ${required} ${autocomplete} ${unused}></div>
					 * From the following component:
					 * <component>
					 *		<state>active</state>
					 *		<required/>
					 *		<autocomplete>postal-address</autocomplete>
					 * </component>
					 * Produces the output (note that ${unused} has been trimmed):
					 * <div class="card active" required autocomplete="postal-address"></div>
					 */ Object . keys ( node . attrs ) . forEach ( function ( attributeName ) { // This code gets confusing fast: // attributeName: this is the name of the attribute in the template node // It is a token, e.g. ${pattern} and we are going to remove it from the node tokenRegExp . lastIndex = 0 ; const match = tokenRegExp . exec ( attributeName ) ; if ( match ) { // newAttributeName: this is the inner part of 'attributeName' and is going to be the name // of the new attribute we add to the node, e.g. 'pattern' const newAttributeName = match [ 1 ] ; const newAttributeValue = tokenDefinitions [ newAttributeName ] || node . attrs [ attributeName ] [ 0 ] . content ; if ( newAttributeValue || newAttributeName in tokenDefinitions ) { node . attrs [ newAttributeName ] = [ { type : 'text' , content : newAttributeValue , // TODO: this is not accurate, but should new nodes have no location? location : node . attrs [ attributeName ] . location , } , ] ; } delete node . attrs [ attributeName ] ; } else { tokenRegExp . lastIndex = 0 ; const attributeNode = node . attrs [ attributeName ] [ 0 ] ; replaceToken ( attributeNode , tokenDefinitions , true ) ; } } ) ; } else if ( node . type === 'text' ) { node = replaceToken ( node , tokenDefinitions ) ; } return node ; } ) ; } return promise ; }
Replace tokens in 'componentAst' with content defined in child elements of 'node'
@param {ReshapeAst} templateAst - AST from the component source template
@param {ReshapeAstNode} componentNode - '<component>' node
@returns {Thenable|undefined} If no substitution was performed then returns undefined
Replace tokens in componentAst with content defined in child elements of node