UNPKG

jscc

Version:

Tiny and powerful preprocessor for conditional comments and replacement of compile-time variables in text files

122 lines 3.8 kB
"use strict"; const skipRegex = require("skip-regex"); const R = require("./regexes"); /** Flag for ES6 TL in the stack */ const ES6_BQ = '`'; /** * Searches the next backtick that signals the end of the ES6 Template Literal * or the sequence "${" that starts a sub-expression, skipping any escaped * character. * * @param buffer Whole code * @param start Starting position of the template * @param stack To save nested ES6 TL positions * @returns The end of the string (-1 if not found). */ const skipES6TL = (buffer, start, stack) => { // Only three characters are of interest to this function const re = /[`$\\]/g; // `start` points to the a backtick inside `code` re.lastIndex = start + 1; while (re.exec(buffer)) { const pos = re.lastIndex; const c = buffer[pos - 1]; if (c === ES6_BQ) { return pos; // found the end of this TL } /* If a sub-expression is found, push a backtick in the stack. When the calling loop finds a closing brace and see the backtick, it will restore the ES6 TL parsing mode. */ if (c === '$' && buffer[pos] === '{') { stack.push(ES6_BQ); return pos + 1; } // This is an escaped char, skip it re.lastIndex = pos + 1; } return buffer.length; // let JS VM handles this error }; /** * Handles closing brackets. It can be a regular bracket or one closing an * ES6 TL expression. * * @param expr Raw expression * @param start Position of this bracket * @param stack Brackets stack */ const skipBracket = (expr, start, stack) => { const ch = stack.pop(); if (ch === '`') { return skipES6TL(expr, start, stack); } // If ch==null then there's an error, returns expr.length to // let JS VM handles this. return ch ? start + 1 : expr.length; }; /** * To find the comment (//), it is necessary to skip strings, es6 tl, * brackets, and regexes */ const RE_EXPR = RegExp(R.S_STRINGS + '|[`/{}]', 'g'); /** * Skip ES6 TL in expressions. * * @param expr Execution context * @param start Start of the ES6 TL */ const extractExpr = function (expr, start) { const stack = []; const re = new RegExp(RE_EXPR); re.lastIndex = start; let mm; // tslint:disable-next-line:no-conditional-assignment while (mm = re.exec(expr)) { switch (mm[0]) { case '{': stack.push('}'); break; case '}': re.lastIndex = skipBracket(expr, mm.index, stack); break; case '`': re.lastIndex = skipES6TL(expr, mm.index, stack); break; case '/': if (expr[mm.index + 1] === '/') { return expr.slice(0, mm.index); } re.lastIndex = skipRegex(expr, mm.index); } } return expr; }; /** * Get an expression, removing surrounding whitespace and the trailing comment, * if necessary. * * @param key Keyword for this expression * @param expr Raw expression */ const getExpr = function (key, expr) { if (expr.indexOf('/') < 0) { return expr.trim(); } /* When an assignment has a regex (ex: `#set _R /\s/`), skipRegex will not recognize it due to invalid syntax. Inserting the missing '=' solves this. */ if (key === 'set') { const mm = R.ASSIGNMENT.exec(expr); const ss = mm && mm[2]; // beware of something like `//#set _V //cmnt` // istanbul ignore else if (ss) { expr = ss.startsWith('//') ? mm[1] : `${mm[1]}=${ss}`; } } return extractExpr(expr, 0).trim(); }; module.exports = getExpr; //# sourceMappingURL=get-expr.js.map