From 730f2528dc3fb4867541e3e74fa403e178604c58 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Fri, 15 May 2015 14:42:28 -0400 Subject: [PATCH] Merge pull request babel/babel-eslint#97 from hzoo/fix-templates fix template type issues --- .../babel-eslint-parser/acorn-to-esprima.js | 54 ++++++++++++++----- .../babel-eslint-parser/test/babel-eslint.js | 37 +++++++++++++ .../test/non-regression.js | 2 - 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/eslint/babel-eslint-parser/acorn-to-esprima.js b/eslint/babel-eslint-parser/acorn-to-esprima.js index b562711bcc..ea6222e3ee 100644 --- a/eslint/babel-eslint-parser/acorn-to-esprima.js +++ b/eslint/babel-eslint-parser/acorn-to-esprima.js @@ -36,7 +36,7 @@ exports.toToken = function (token) { token.type = "JSXIdentifier"; } else if (type.keyword === "null") { token.type = "Null"; - } else if (type.keyword === "false" || token.keyword === "true") { + } else if (type.keyword === "false" || type.keyword === "true") { token.type = "Boolean"; } else if (type.keyword) { token.type = "Keyword"; @@ -48,6 +48,11 @@ exports.toToken = function (token) { token.value = JSON.stringify(token.value); } else if (type === tt.regexp) { token.type = "RegularExpression"; + token.regex = { + pattern: token.value.pattern, + flags: token.value.flags + }; + token.value = String(token.value.value); } return token; @@ -70,20 +75,26 @@ function isCompatTag(tagName) { } function convertTemplateType(tokens) { - var startingToken = 0; - var currentToken = 0; - var numBraces = 0; + var startingToken = 0; + var currentToken = 0; + var numBraces = 0; + var hasTemplateEnded = true; + + function isBackQuote(token) { + return tokens[token].type === tt.backQuote; + } function isTemplateStarter(token) { - return tokens[token].type === tt.backQuote || + return isBackQuote(token) || tokens[token].type === tt.braceR; } function isTemplateEnder(token) { - return tokens[token].type === tt.dollarBraceL || - tokens[token].type === tt.backQuote; + return isBackQuote(token) || + tokens[token].type === tt.dollarBraceL; } + // append the values between start and end function createTemplateValue(start, end) { var value = ""; while (start <= end) { @@ -97,6 +108,7 @@ function convertTemplateType(tokens) { return value; } + // create Template token function replaceWithTemplateType(start, end) { var templateToken = { type: 'Template', @@ -107,10 +119,12 @@ function convertTemplateType(tokens) { end: tokens[end].loc.end } } + + // put new token in place of old tokens tokens.splice(start, end - start + 1, templateToken); } - function checkNumBraces(token) { + function trackNumBraces(token) { if (tokens[token].type === tt.braceL) { numBraces++; } else if (tokens[token].type === tt.braceR) { @@ -119,18 +133,30 @@ function convertTemplateType(tokens) { } while (startingToken < tokens.length) { + // template start: check if ` or } if (isTemplateStarter(startingToken) && numBraces === 0) { currentToken = startingToken + 1; - while (currentToken < tokens.length - 1 && !isTemplateEnder(currentToken)) { - checkNumBraces(currentToken); + + // check if token after template start is "template" + if (currentToken >= tokens.length - 1 || tokens[currentToken].type !== tt.template) { + break; + } + + // template end: find ` or ${ + while (!isTemplateEnder(currentToken)) { + if (currentToken >= tokens.length - 1) { + break; + } currentToken++; } + + hasTemplateEnded = isBackQuote(currentToken); + // template start and end found: create new token replaceWithTemplateType(startingToken, currentToken); - startingToken++; - } else { - checkNumBraces(startingToken); - startingToken++; + } else if (!hasTemplateEnded) { + trackNumBraces(startingToken); } + startingToken++; } } diff --git a/eslint/babel-eslint-parser/test/babel-eslint.js b/eslint/babel-eslint-parser/test/babel-eslint.js index 123745f516..4c0d062d7e 100644 --- a/eslint/babel-eslint-parser/test/babel-eslint.js +++ b/eslint/babel-eslint-parser/test/babel-eslint.js @@ -33,7 +33,12 @@ function assertImplementsAST(target, source, path) { function parseAndAssertSame(code) { var esAST = espree.parse(code, { + env: { + "es6": true, + "node": true + }, ecmaFeatures: { + blockBindings: true, templateStrings: true, modules: true, classes: true, @@ -103,6 +108,22 @@ describe("acorn-to-esprima", function () { it("template with nested function/object", function () { parseAndAssertSame("`outer${{x: {y: 10}}}bar${`nested${function(){return 1;}}endnest`}end`"); }); + + it("template with braces inside and outside of template string #96", function () { + parseAndAssertSame("if (a) { var target = `{}a:${webpackPort}{}}}}`; } else { app.use(); }"); + }); + + it("template also with braces #96", function () { + parseAndAssertSame( + "export default function f1() {" + + "function f2(foo) {" + + "const bar = 3;" + + "return `${foo} ${bar}`;" + + "}" + + "return f2;" + + "}" + ); + }); }); it("simple expression", function () { @@ -203,4 +224,20 @@ describe("acorn-to-esprima", function () { " */" ].join("\n")); }); + + it("null", function () { + parseAndAssertSame("null"); + }); + + it("boolean", function () { + parseAndAssertSame("if (true) {} else if (false) {}"); + }); + + it("regexp", function () { + parseAndAssertSame("/affix-top|affix-bottom|affix|[a-z]/"); + }); + + it("regexp in a template string", function () { + parseAndAssertSame("`${/\\d/.exec(\"1\")[0]}`"); + }); }); diff --git a/eslint/babel-eslint-parser/test/non-regression.js b/eslint/babel-eslint-parser/test/non-regression.js index 1ce55b0577..d16b71a624 100644 --- a/eslint/babel-eslint-parser/test/non-regression.js +++ b/eslint/babel-eslint-parser/test/non-regression.js @@ -147,6 +147,4 @@ describe("verify", function () { [] ); }); - - });