in src/scanner.ts [964:1088]
private scanTemplate(): RawToken {
let cooked = '';
let terminated = false;
const start = this.index;
const head = (this.source[start] === '`');
let tail = false;
let rawOffset = 2;
++this.index;
while (!this.eof()) {
let ch = this.source[this.index++];
if (ch === '`') {
rawOffset = 1;
tail = true;
terminated = true;
break;
} else if (ch === '$') {
if (this.source[this.index] === '{') {
this.curlyStack.push('${');
++this.index;
terminated = true;
break;
}
cooked += ch;
} else if (ch === '\\') {
ch = this.source[this.index++];
if (!Character.isLineTerminator(ch.charCodeAt(0))) {
switch (ch) {
case 'n':
cooked += '\n';
break;
case 'r':
cooked += '\r';
break;
case 't':
cooked += '\t';
break;
case 'u':
if (this.source[this.index] === '{') {
++this.index;
cooked += this.scanUnicodeCodePointEscape();
} else {
const restore = this.index;
const unescapedChar = this.scanHexEscape(ch);
if (unescapedChar !== null) {
cooked += unescapedChar;
} else {
this.index = restore;
cooked += ch;
}
}
break;
case 'x':
const unescaped = this.scanHexEscape(ch);
if (unescaped === null) {
this.throwUnexpectedToken(Messages.InvalidHexEscapeSequence);
}
cooked += unescaped;
break;
case 'b':
cooked += '\b';
break;
case 'f':
cooked += '\f';
break;
case 'v':
cooked += '\v';
break;
default:
if (ch === '0') {
if (Character.isDecimalDigit(this.source.charCodeAt(this.index))) {
// Illegal: \01 \02 and so on
this.throwUnexpectedToken(Messages.TemplateOctalLiteral);
}
cooked += '\0';
} else if (Character.isOctalDigit(ch.charCodeAt(0))) {
// Illegal: \1 \2
this.throwUnexpectedToken(Messages.TemplateOctalLiteral);
} else {
cooked += ch;
}
break;
}
} else {
++this.lineNumber;
if (ch === '\r' && this.source[this.index] === '\n') {
++this.index;
}
this.lineStart = this.index;
}
} else if (Character.isLineTerminator(ch.charCodeAt(0))) {
++this.lineNumber;
if (ch === '\r' && this.source[this.index] === '\n') {
++this.index;
}
this.lineStart = this.index;
cooked += '\n';
} else {
cooked += ch;
}
}
if (!terminated) {
this.throwUnexpectedToken();
}
if (!head) {
this.curlyStack.pop();
}
return {
type: Token.Template,
value: this.source.slice(start + 1, this.index - rawOffset),
cooked: cooked,
head: head,
tail: tail,
lineNumber: this.lineNumber,
lineStart: this.lineStart,
start: start,
end: this.index
};
}