120 lines
3.4 KiB
JavaScript
120 lines
3.4 KiB
JavaScript
'use strict';
|
|
// adapted from https://github.com/jridgewell/string-dedent
|
|
var getBuiltIn = require('../internals/get-built-in');
|
|
var uncurryThis = require('../internals/function-uncurry-this');
|
|
|
|
var fromCharCode = String.fromCharCode;
|
|
var fromCodePoint = getBuiltIn('String', 'fromCodePoint');
|
|
var charAt = uncurryThis(''.charAt);
|
|
var charCodeAt = uncurryThis(''.charCodeAt);
|
|
var stringIndexOf = uncurryThis(''.indexOf);
|
|
var stringSlice = uncurryThis(''.slice);
|
|
|
|
var ZERO_CODE = 48;
|
|
var NINE_CODE = 57;
|
|
var LOWER_A_CODE = 97;
|
|
var LOWER_F_CODE = 102;
|
|
var UPPER_A_CODE = 65;
|
|
var UPPER_F_CODE = 70;
|
|
|
|
var isDigit = function (str, index) {
|
|
var c = charCodeAt(str, index);
|
|
return c >= ZERO_CODE && c <= NINE_CODE;
|
|
};
|
|
|
|
var parseHex = function (str, index, end) {
|
|
if (end >= str.length) return -1;
|
|
var n = 0;
|
|
for (; index < end; index++) {
|
|
var c = hexToInt(charCodeAt(str, index));
|
|
if (c === -1) return -1;
|
|
n = n * 16 + c;
|
|
}
|
|
return n;
|
|
};
|
|
|
|
var hexToInt = function (c) {
|
|
if (c >= ZERO_CODE && c <= NINE_CODE) return c - ZERO_CODE;
|
|
if (c >= LOWER_A_CODE && c <= LOWER_F_CODE) return c - LOWER_A_CODE + 10;
|
|
if (c >= UPPER_A_CODE && c <= UPPER_F_CODE) return c - UPPER_A_CODE + 10;
|
|
return -1;
|
|
};
|
|
|
|
module.exports = function (raw) {
|
|
var out = '';
|
|
var start = 0;
|
|
// We need to find every backslash escape sequence, and cook the escape into a real char.
|
|
var i = 0;
|
|
var n;
|
|
while ((i = stringIndexOf(raw, '\\', i)) > -1) {
|
|
out += stringSlice(raw, start, i);
|
|
// If the backslash is the last char of the string, then it was an invalid sequence.
|
|
// This can't actually happen in a tagged template literal, but could happen if you manually
|
|
// invoked the tag with an array.
|
|
if (++i === raw.length) return;
|
|
var next = charAt(raw, i++);
|
|
switch (next) {
|
|
// Escaped control codes need to be individually processed.
|
|
case 'b':
|
|
out += '\b';
|
|
break;
|
|
case 't':
|
|
out += '\t';
|
|
break;
|
|
case 'n':
|
|
out += '\n';
|
|
break;
|
|
case 'v':
|
|
out += '\v';
|
|
break;
|
|
case 'f':
|
|
out += '\f';
|
|
break;
|
|
case 'r':
|
|
out += '\r';
|
|
break;
|
|
// Escaped line terminators just skip the char.
|
|
case '\r':
|
|
// Treat `\r\n` as a single terminator.
|
|
if (i < raw.length && charAt(raw, i) === '\n') ++i;
|
|
// break omitted
|
|
case '\n':
|
|
case '\u2028':
|
|
case '\u2029':
|
|
break;
|
|
// `\0` is a null control char, but `\0` followed by another digit is an illegal octal escape.
|
|
case '0':
|
|
if (isDigit(raw, i)) return;
|
|
out += '\0';
|
|
break;
|
|
// Hex escapes must contain 2 hex chars.
|
|
case 'x':
|
|
n = parseHex(raw, i, i + 2);
|
|
if (n === -1) return;
|
|
i += 2;
|
|
out += fromCharCode(n);
|
|
break;
|
|
// Unicode escapes contain either 4 chars, or an unlimited number between `{` and `}`.
|
|
// The hex value must not overflow 0x10FFFF.
|
|
case 'u':
|
|
if (i < raw.length && charAt(raw, i) === '{') {
|
|
var end = stringIndexOf(raw, '}', ++i);
|
|
if (end === -1) return;
|
|
n = parseHex(raw, i, end);
|
|
i = end + 1;
|
|
} else {
|
|
n = parseHex(raw, i, i + 4);
|
|
i += 4;
|
|
}
|
|
if (n === -1 || n > 0x10FFFF) return;
|
|
out += fromCodePoint(n);
|
|
break;
|
|
default:
|
|
if (isDigit(next, 0)) return;
|
|
out += next;
|
|
}
|
|
start = i;
|
|
}
|
|
return out + stringSlice(raw, start);
|
|
};
|