Merge pull request babel/babel-eslint#94 from hzoo/support-templates
support template strings - Fixes babel/babel-eslint#31
This commit is contained in:
parent
527287aa29
commit
d84fc559f2
@ -58,10 +58,82 @@ exports.toAST = function (ast) {
|
||||
traverse(ast, astTransformVisitor);
|
||||
};
|
||||
|
||||
exports.toTokens = function (tokens) {
|
||||
// transform tokens to type "Template"
|
||||
convertTemplateType(tokens);
|
||||
|
||||
return tokens.map(exports.toToken);
|
||||
};
|
||||
|
||||
function isCompatTag(tagName) {
|
||||
return tagName && /^[a-z]|\-/.test(tagName);
|
||||
}
|
||||
|
||||
function convertTemplateType(tokens) {
|
||||
var startingToken = 0;
|
||||
var currentToken = 0;
|
||||
var numBraces = 0;
|
||||
|
||||
function isTemplateStarter(token) {
|
||||
return tokens[token].type === tt.backQuote ||
|
||||
tokens[token].type === tt.braceR;
|
||||
}
|
||||
|
||||
function isTemplateEnder(token) {
|
||||
return tokens[token].type === tt.dollarBraceL ||
|
||||
tokens[token].type === tt.backQuote;
|
||||
}
|
||||
|
||||
function createTemplateValue(start, end) {
|
||||
var value = "";
|
||||
while (start <= end) {
|
||||
if (tokens[start].value) {
|
||||
value += tokens[start].value;
|
||||
} else if (tokens[start].type !== tt.template) {
|
||||
value += tokens[start].type.label;
|
||||
}
|
||||
start++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function replaceWithTemplateType(start, end) {
|
||||
var templateToken = {
|
||||
type: 'Template',
|
||||
value: createTemplateValue(start, end),
|
||||
range: [tokens[start].start, tokens[end].end],
|
||||
loc: {
|
||||
start: tokens[start].loc.start,
|
||||
end: tokens[end].loc.end
|
||||
}
|
||||
}
|
||||
tokens.splice(start, end - start + 1, templateToken);
|
||||
}
|
||||
|
||||
function checkNumBraces(token) {
|
||||
if (tokens[token].type === tt.braceL) {
|
||||
numBraces++;
|
||||
} else if (tokens[token].type === tt.braceR) {
|
||||
numBraces--;
|
||||
}
|
||||
}
|
||||
|
||||
while (startingToken < tokens.length) {
|
||||
if (isTemplateStarter(startingToken) && numBraces === 0) {
|
||||
currentToken = startingToken + 1;
|
||||
while (currentToken < tokens.length - 1 && !isTemplateEnder(currentToken)) {
|
||||
checkNumBraces(currentToken);
|
||||
currentToken++;
|
||||
}
|
||||
replaceWithTemplateType(startingToken, currentToken);
|
||||
startingToken++;
|
||||
} else {
|
||||
checkNumBraces(startingToken);
|
||||
startingToken++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var astTransformVisitor = {
|
||||
noScope: true,
|
||||
exit: function (node, parent) {
|
||||
@ -117,5 +189,24 @@ var astTransformVisitor = {
|
||||
node.delegate = node.all;
|
||||
delete node.all;
|
||||
}
|
||||
|
||||
// template strings
|
||||
|
||||
if (t.isTemplateLiteral(node)) {
|
||||
node.quasis.forEach(function (q) {
|
||||
q.range[0] -= 1;
|
||||
if (q.tail) {
|
||||
q.range[1] += 1;
|
||||
} else {
|
||||
q.range[1] += 2;
|
||||
}
|
||||
q.loc.start.column -= 1;
|
||||
if (q.tail) {
|
||||
q.loc.end.column += 1;
|
||||
} else {
|
||||
q.loc.end.column += 2;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -56,7 +56,7 @@ function monkeypatch() {
|
||||
};
|
||||
}
|
||||
|
||||
exports.attachComments = function(ast, comments, tokens) {
|
||||
exports.attachComments = function (ast, comments, tokens) {
|
||||
estraverse.attachComments(ast, comments, tokens);
|
||||
|
||||
if (comments.length) {
|
||||
@ -82,7 +82,7 @@ exports.attachComments = function(ast, comments, tokens) {
|
||||
node.leadingComments = [];
|
||||
var firstTokenStart = token.range[0];
|
||||
var len = comments.length;
|
||||
for(var i = 0; i < len && comments[i].start < firstTokenStart; i++ ) {
|
||||
for (var i = 0; i < len && comments[i].start < firstTokenStart; i++) {
|
||||
node.leadingComments.push(comments[i]);
|
||||
}
|
||||
}
|
||||
@ -138,7 +138,7 @@ exports.parse = function (code) {
|
||||
tokens.pop();
|
||||
|
||||
// convert tokens
|
||||
ast.tokens = tokens.map(acornToEsprima.toToken);
|
||||
ast.tokens = acornToEsprima.toTokens(tokens);
|
||||
|
||||
// add comments
|
||||
ast.comments = comments;
|
||||
|
||||
@ -20,7 +20,7 @@ function assertImplementsAST(target, source, path) {
|
||||
error("have different types (" + typeA + " !== " + typeB + ")");
|
||||
} else if (typeA === "object") {
|
||||
var keysTarget = Object.keys(target);
|
||||
for(var i in keysTarget) {
|
||||
for (var i in keysTarget) {
|
||||
var key = keysTarget[i];
|
||||
path.push(key);
|
||||
assertImplementsAST(target[key], source[key], path);
|
||||
@ -34,6 +34,7 @@ function assertImplementsAST(target, source, path) {
|
||||
function parseAndAssertSame(code) {
|
||||
var esAST = espree.parse(code, {
|
||||
ecmaFeatures: {
|
||||
templateStrings: true,
|
||||
modules: true,
|
||||
classes: true,
|
||||
jsx: true
|
||||
@ -50,14 +51,60 @@ function parseAndAssertSame(code) {
|
||||
} catch(err) {
|
||||
err.message +=
|
||||
"\nespree:\n" +
|
||||
util.inspect(esAST, {depth: err.depth}) +
|
||||
util.inspect(esAST, {depth: err.depth, colors: true}) +
|
||||
"\nbabel-eslint:\n" +
|
||||
util.inspect(acornAST, {depth: err.depth});
|
||||
util.inspect(acornAST, {depth: err.depth, colors: true});
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
describe("acorn-to-esprima", function () {
|
||||
describe("templates", function () {
|
||||
it("empty template string", function () {
|
||||
parseAndAssertSame("``");
|
||||
});
|
||||
|
||||
it("template string", function () {
|
||||
parseAndAssertSame("`test`");
|
||||
});
|
||||
|
||||
it("template string using $", function () {
|
||||
parseAndAssertSame("`$`");
|
||||
});
|
||||
|
||||
it("template string with expression", function () {
|
||||
parseAndAssertSame("`${a}`");
|
||||
});
|
||||
|
||||
it("template string with multiple expressions", function () {
|
||||
parseAndAssertSame("`${a}${b}${c}`");
|
||||
});
|
||||
|
||||
it("template string with expression and strings", function () {
|
||||
parseAndAssertSame("`a${a}a`");
|
||||
});
|
||||
|
||||
it("template string with binary expression", function () {
|
||||
parseAndAssertSame("`a${a + b}a`");
|
||||
});
|
||||
|
||||
it("tagged template", function () {
|
||||
parseAndAssertSame("jsx`<Button>Click</Button>`");
|
||||
});
|
||||
|
||||
it("tagged template with expression", function () {
|
||||
parseAndAssertSame("jsx`<Button>Hi ${name}</Button>`");
|
||||
});
|
||||
|
||||
it("tagged template with new operator", function () {
|
||||
parseAndAssertSame("new raw`42`");
|
||||
});
|
||||
|
||||
it("template with nested function/object", function () {
|
||||
parseAndAssertSame("`outer${{x: {y: 10}}}bar${`nested${function(){return 1;}}endnest`}end`");
|
||||
});
|
||||
});
|
||||
|
||||
it("simple expression", function () {
|
||||
parseAndAssertSame("a = 1");
|
||||
});
|
||||
|
||||
@ -139,4 +139,14 @@ describe("verify", function () {
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
it("template strings #31", function () {
|
||||
verifyAndAssertMessages(
|
||||
"console.log(`${a}, b`);",
|
||||
{ "comma-spacing": 1 },
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user