<commit_before>import Node from '../Node.js';

export default class TemplateLiteral extends Node {
	transpile ( code, transforms ) {
		if ( transforms.templateString && this.parent.type !== 'TaggedTemplateExpression' ) {
			let ordered = this.expressions.concat( this.quasis )
				.sort( ( a, b ) => a.start - b.start || a.end - b.end )
				.filter( ( node, i ) => {
					// include all expressions
					if ( node.type !== 'TemplateElement' ) return true;

					// include all non-empty strings
					if ( node.value.raw ) return true;

					// exclude all empty strings not at the head
					return !i;
				});

			// special case – we may be able to skip the first element,
			// if it's the empty string, but only if the second and
			// third elements aren't both expressions (since they maybe
			// be numeric, and `1 + 2 + '3' === '33'`)
			if ( ordered.length >= 3 ) {
				const [ first, , third ] = ordered;
				if ( first.type === 'TemplateElement' && first.value.raw === '' && third.type === 'TemplateElement' ) {
					ordered.shift();
				}
			}

			const parenthesise = ( this.quasis.length !== 1 || this.expressions.length !== 0 ) &&
			                     this.parent.type !== 'AssignmentExpression' &&
			                     this.parent.type !== 'AssignmentPattern' &&
			                     this.parent.type !== 'VariableDeclarator' &&
			                     ( this.parent.type !== 'BinaryExpression' || this.parent.operator !== '+' );

			if ( parenthesise ) code.appendRight( this.start, '(' );

			let lastIndex = this.start;
			ordered.forEach( ( node, i ) => {
				let prefix = i === 0 ?
					parenthesise ? '(' : '' :
					' + ';

				if ( node.type === 'TemplateElement' ) {
					const prefix__plus =
								(i === 0
									&& parenthesise
									&& this.parent.type === 'TemplateLiteral'
								) ? ' + '
									: '';
					code.overwrite( lastIndex, node.end, prefix__plus + prefix + JSON.stringify( node.value.cooked ) );
				} else {
					const parenthesise = node.type !== 'Identifier'; // TODO other cases where it's safe

					if ( parenthesise ) prefix += '(';

					code.remove( lastIndex, node.start );
					if ( prefix ) code.prependRight( node.start, prefix );
					if ( parenthesise ) code.appendLeft( node.end, ')' );
				}
				lastIndex = node.end;
			});

			if ( parenthesise ) code.appendLeft( lastIndex, ')' );
			code.remove( lastIndex, this.end );
		}

		super.transpile( code, transforms );
	}
}<commit_msg>transpile template literals depth-first, to handle nesting<commit_after>import Node from '../Node.js';

export default class TemplateLiteral extends Node {
	transpile ( code, transforms ) {
		super.transpile( code, transforms );

		if ( transforms.templateString && this.parent.type !== 'TaggedTemplateExpression' ) {
			let ordered = this.expressions.concat( this.quasis )
				.sort( ( a, b ) => a.start - b.start || a.end - b.end )
				.filter( ( node, i ) => {
					// include all expressions
					if ( node.type !== 'TemplateElement' ) return true;

					// include all non-empty strings
					if ( node.value.raw ) return true;

					// exclude all empty strings not at the head
					return !i;
				});

			// special case – we may be able to skip the first element,
			// if it's the empty string, but only if the second and
			// third elements aren't both expressions (since they maybe
			// be numeric, and `1 + 2 + '3' === '33'`)
			if ( ordered.length >= 3 ) {
				const [ first, , third ] = ordered;
				if ( first.type === 'TemplateElement' && first.value.raw === '' && third.type === 'TemplateElement' ) {
					ordered.shift();
				}
			}

			const parenthesise = ( this.quasis.length !== 1 || this.expressions.length !== 0 ) &&
			                     this.parent.type !== 'TemplateLiteral' &&
			                     this.parent.type !== 'AssignmentExpression' &&
			                     this.parent.type !== 'AssignmentPattern' &&
			                     this.parent.type !== 'VariableDeclarator' &&
			                     ( this.parent.type !== 'BinaryExpression' || this.parent.operator !== '+' );

			if ( parenthesise ) code.appendRight( this.start, '(' );

			let lastIndex = this.start;

			ordered.forEach( ( node, i ) => {
				let prefix = i === 0 ?
					parenthesise ? '(' : '' :
					' + ';

				if ( node.type === 'TemplateElement' ) {
					code.overwrite( lastIndex, node.end, prefix + JSON.stringify( node.value.cooked ) );
				} else {
					const parenthesise = node.type !== 'Identifier'; // TODO other cases where it's safe

					if ( parenthesise ) prefix += '(';

					code.remove( lastIndex, node.start );

					if ( prefix ) code.prependRight( node.start, prefix );
					if ( parenthesise ) code.appendLeft( node.end, ')' );
				}

				lastIndex = node.end;
			});

			if ( parenthesise ) code.appendLeft( lastIndex, ')' );
			code.remove( lastIndex, this.end );
		}
	}
}
{'fraction_non_alphanumeric': 0.15424111727254056, 'fraction_numerical': 0.00390223865270076, 'mean_word_length': 2.335616438356164, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4423783', 'n_tokens_mistral': 1792, 'n_tokens_neox': 1412, 'n_words': 388}