1/*@internal*/ 2namespace ts { 3 export enum ProcessLevel { 4 LiftRestriction, 5 All 6 } 7 8 export function processTaggedTemplateExpression( 9 context: TransformationContext, 10 node: TaggedTemplateExpression, 11 visitor: Visitor, 12 currentSourceFile: SourceFile, 13 recordTaggedTemplateString: (temp: Identifier) => void, 14 level: ProcessLevel) { 15 16 // Visit the tag expression 17 const tag = visitNode(node.tag, visitor, isExpression); 18 19 // Build up the template arguments and the raw and cooked strings for the template. 20 // We start out with 'undefined' for the first argument and revisit later 21 // to avoid walking over the template string twice and shifting all our arguments over after the fact. 22 const templateArguments: Expression[] = [undefined!]; 23 const cookedStrings: Expression[] = []; 24 const rawStrings: Expression[] = []; 25 const template = node.template; 26 27 if (level === ProcessLevel.LiftRestriction && !hasInvalidEscape(template)) { 28 return visitEachChild(node, visitor, context); 29 } 30 31 if (isNoSubstitutionTemplateLiteral(template)) { 32 cookedStrings.push(createTemplateCooked(template)); 33 rawStrings.push(getRawLiteral(template, currentSourceFile)); 34 } 35 else { 36 cookedStrings.push(createTemplateCooked(template.head)); 37 rawStrings.push(getRawLiteral(template.head, currentSourceFile)); 38 for (const templateSpan of template.templateSpans) { 39 cookedStrings.push(createTemplateCooked(templateSpan.literal)); 40 rawStrings.push(getRawLiteral(templateSpan.literal, currentSourceFile)); 41 templateArguments.push(visitNode(templateSpan.expression, visitor, isExpression)); 42 } 43 } 44 45 const helperCall = context.getEmitHelperFactory().createTemplateObjectHelper( 46 factory.createArrayLiteralExpression(cookedStrings), 47 factory.createArrayLiteralExpression(rawStrings)); 48 49 // Create a variable to cache the template object if we're in a module. 50 // Do not do this in the global scope, as any variable we currently generate could conflict with 51 // variables from outside of the current compilation. In the future, we can revisit this behavior. 52 if (isExternalModule(currentSourceFile)) { 53 const tempVar = factory.createUniqueName("templateObject"); 54 recordTaggedTemplateString(tempVar); 55 templateArguments[0] = factory.createLogicalOr( 56 tempVar, 57 factory.createAssignment( 58 tempVar, 59 helperCall) 60 ); 61 } 62 else { 63 templateArguments[0] = helperCall; 64 } 65 66 return factory.createCallExpression(tag, /*typeArguments*/ undefined, templateArguments); 67 } 68 69 function createTemplateCooked(template: TemplateHead | TemplateMiddle | TemplateTail | NoSubstitutionTemplateLiteral) { 70 return template.templateFlags ? factory.createVoidZero() : factory.createStringLiteral(template.text); 71 } 72 73 /** 74 * Creates an ES5 compatible literal from an ES6 template literal. 75 * 76 * @param node The ES6 template literal. 77 */ 78 function getRawLiteral(node: TemplateLiteralLikeNode, currentSourceFile: SourceFile) { 79 // Find original source text, since we need to emit the raw strings of the tagged template. 80 // The raw strings contain the (escaped) strings of what the user wrote. 81 // Examples: `\n` is converted to "\\n", a template string with a newline to "\n". 82 let text = node.rawText; 83 if (text === undefined) { 84 text = getSourceTextOfNodeFromSourceFile(currentSourceFile, node); 85 86 // text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"), 87 // thus we need to remove those characters. 88 // First template piece starts with "`", others with "}" 89 // Last template piece ends with "`", others with "${" 90 const isLast = node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail; 91 text = text.substring(1, text.length - (isLast ? 1 : 2)); 92 } 93 94 // Newline normalization: 95 // ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's 96 // <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV. 97 text = text.replace(/\r\n?/g, "\n"); 98 return setTextRange(factory.createStringLiteral(text), node); 99 } 100} 101