jscc
Version:
Tiny and powerful preprocessor for conditional comments and replacement of compile-time variables in text files
122 lines • 3.8 kB
JavaScript
;
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