UNPKG

cream-and-sugar

Version:

A deliciously functional syntax for JavaScript with native support for JSX

133 lines (124 loc) 3.82 kB
'use strict'; var _utils = require('../utils'); /** * Captures blocks in an interpolation string to be parsed and compiled. * * @param {String} str A string surrounded by backticks. * * @return {Array} Contains objects representing string portions and * code block portions. */ function fixInterp(str) { var acc = []; var piece = ''; var inBlock = false; var curlies = 0; for (var i = 0; i < str.length; i += 1) { var cur = str[i]; var next = str[i + 1]; var prev = str[i - 1]; if (cur === '{' && inBlock) { curlies += 1; piece += cur; } else if (cur === '{' && !inBlock && prev === '$') { inBlock = true; piece += cur; acc.push({ type: 'str', val: piece }); piece = ''; } else if (cur === '}' && inBlock) { if (curlies === 0) { inBlock = false; acc.push({ type: 'block', val: piece }); piece = ''; piece += cur; } else { curlies -= 1; piece += cur; } } else { piece += cur; } } acc.push({ type: 'str', val: piece }); return acc; } /** * Surrounds a string with quotation marks. * * @param {String} str A string * @return {String} */ function chooseQuote(str) { var hasSingle = /'/.test(str); var hasDouble = /"/.test(str); if (hasSingle && hasDouble) { return '"' + str.replace(/"/g, '\\"') + '"'; // Use doubles and escape the doubles. } else if (hasSingle) { return '"' + str + '"'; // Use doubles. } else if (hasDouble) { return "'" + str + "'"; // Use singles. } else { return '"' + str + '"'; // Use doubles. } } /** * Handles compiling values withing interpolation brackets of * backtick strings. * * @param {Array} blocks The result of calling `fixInterp`. * @param {StringNode} origNode The original string node. * * @return {String} The compiled string. */ function compileInterpBlocks(blocks, origNode) { // Old string compilation technique. // Keeping it around in case we want to bring it back. // const out = `${blocks.map(block => { // if (block.type === 'str') { // return block.val; // } else { // const value = parser.parse(block.val).body[0]; // value.loc = origNode.loc; // value.shared = origNode.shared; // return value.compile(true); // } // }).join('')}`; // return out; var out = []; blocks.forEach(function (block, index) { var prev = out[index - 1]; var next = blocks[index + 1]; var isFirst = index === 0; var isLast = index === blocks.length - 1; // For string type blocks, remove all of the "${" stuff and the backticks. if (block.type === 'str') { if (isFirst) block.val = block.val.replace(/^`/, ''); if (isLast) block.val = block.val.replace(/`$/, ''); if (prev) block.val = block.val.replace(/^\}/, ''); if (next) block.val = block.val.replace(/\$\{$/, ''); block.val = block.val.replace(/\n/g, ''); // Choose a quote type. block.val.length && out.push(chooseQuote(block.val)); // For block types, compile the block and surround it with parens. } else { var value = _utils.parser.parse(block.val).body[0]; value.loc = origNode.loc; value.shared = origNode.shared; out.push('(' + value.compile(true) + ')'); } }); return out.join(' + '); } /* * Drop in strings. Make sure to handle compiling values * within interpolation brackets. */ (0, _utils.compile)(_utils.nodes.StringNode, function () { if (this.text[0] === '`') { return '(' + compileInterpBlocks(fixInterp(this.text), this) + ')'; } else { // Allow quoted strings to be captured on multiple lines but don't // compile them that way. return this.text.replace(/\n/g, ''); } });