237 lines
6.4 KiB
TypeScript
237 lines
6.4 KiB
TypeScript
import assert from "assert";
|
|
import { parse } from "../lib/parser";
|
|
import { getReprinter } from "../lib/patcher";
|
|
import { Printer } from "../lib/printer";
|
|
import { fromString } from "../lib/lines";
|
|
import * as types from "ast-types";
|
|
const namedTypes = types.namedTypes;
|
|
import FastPath from "../lib/fast-path";
|
|
import { EOL as eol } from "os";
|
|
const nodeMajorVersion = parseInt(process.versions.node, 10);
|
|
|
|
// Esprima seems unable to handle unnamed top-level functions, so declare
|
|
// test functions with names and then export them later.
|
|
|
|
describe("parser", function () {
|
|
[
|
|
"../parsers/acorn",
|
|
"../parsers/babel",
|
|
"../parsers/esprima",
|
|
"../parsers/flow",
|
|
"../parsers/typescript",
|
|
].forEach(runTestsForParser);
|
|
|
|
it("AlternateParser", function () {
|
|
const b = types.builders;
|
|
const parser = {
|
|
parse: function () {
|
|
const program = b.program([
|
|
b.expressionStatement(b.identifier("surprise")),
|
|
]);
|
|
program.comments = [];
|
|
return program;
|
|
},
|
|
};
|
|
|
|
function check(options?: any) {
|
|
const ast = parse("ignored", options);
|
|
const printer = new Printer();
|
|
|
|
types.namedTypes.File.assert(ast, true);
|
|
assert.strictEqual(printer.printGenerically(ast).code, "surprise;");
|
|
}
|
|
|
|
check({ esprima: parser });
|
|
check({ parser: parser });
|
|
});
|
|
});
|
|
|
|
function runTestsForParser(parserId: string) {
|
|
const parserName = parserId.split("/").pop();
|
|
|
|
if (
|
|
nodeMajorVersion < 6 &&
|
|
(parserName === "babel" ||
|
|
parserName === "flow" ||
|
|
parserName === "typescript")
|
|
) {
|
|
// Babel 7 no longer supports Node 4 or 5.
|
|
return;
|
|
}
|
|
|
|
if (!parserName) {
|
|
return;
|
|
}
|
|
|
|
const parser = require(parserId);
|
|
|
|
it("[" + parserName + "] empty source", function () {
|
|
const printer = new Printer();
|
|
|
|
function check(code: string) {
|
|
const ast = parse(code, { parser });
|
|
assert.strictEqual(printer.print(ast).code, code);
|
|
}
|
|
|
|
check("");
|
|
check("/* block comment */");
|
|
check("// line comment");
|
|
check("\t\t\t");
|
|
check(eol);
|
|
check(eol + eol);
|
|
check(" ");
|
|
});
|
|
|
|
const lineCommentTypes: { [name: string]: string } = {
|
|
acorn: "Line",
|
|
babel: "CommentLine",
|
|
esprima: "Line",
|
|
flow: "CommentLine",
|
|
typescript: "CommentLine",
|
|
};
|
|
|
|
it("[" + parserName + "] parser basics", function testParser(done) {
|
|
const code = testParser + "";
|
|
const ast = parse(code, { parser });
|
|
|
|
namedTypes.File.assert(ast);
|
|
assert.ok(getReprinter(FastPath.from(ast)));
|
|
|
|
const funDecl = ast.program.body[0];
|
|
const funBody = funDecl.body;
|
|
|
|
namedTypes.FunctionDeclaration.assert(funDecl);
|
|
namedTypes.BlockStatement.assert(funBody);
|
|
assert.ok(getReprinter(FastPath.from(funBody)));
|
|
|
|
const lastStatement = funBody.body.pop();
|
|
const doneCall = lastStatement.expression;
|
|
|
|
assert.ok(!getReprinter(FastPath.from(funBody)));
|
|
assert.ok(getReprinter(FastPath.from(ast)));
|
|
|
|
funBody.body.push(lastStatement);
|
|
assert.ok(getReprinter(FastPath.from(funBody)));
|
|
|
|
assert.strictEqual(doneCall.callee.name, "done");
|
|
|
|
assert.strictEqual(lastStatement.comments.length, 2);
|
|
|
|
const firstComment = lastStatement.comments[0];
|
|
|
|
assert.strictEqual(firstComment.type, lineCommentTypes[parserName]);
|
|
|
|
assert.strictEqual(firstComment.leading, true);
|
|
assert.strictEqual(firstComment.trailing, false);
|
|
assert.strictEqual(
|
|
firstComment.value,
|
|
" Make sure done() remains the final statement in this function,",
|
|
);
|
|
|
|
const secondComment = lastStatement.comments[1];
|
|
|
|
assert.strictEqual(secondComment.type, lineCommentTypes[parserName]);
|
|
|
|
assert.strictEqual(secondComment.leading, true);
|
|
assert.strictEqual(secondComment.trailing, false);
|
|
assert.strictEqual(
|
|
secondComment.value,
|
|
" or the above assertions will probably fail.",
|
|
);
|
|
|
|
// Make sure done() remains the final statement in this function,
|
|
// or the above assertions will probably fail.
|
|
done();
|
|
});
|
|
|
|
it("[" + parserName + "] LocationFixer", function () {
|
|
const code = ["function foo() {", " a()", " b()", "}"].join(eol);
|
|
const ast = parse(code, { parser });
|
|
const printer = new Printer();
|
|
|
|
types.visit(ast, {
|
|
visitFunctionDeclaration: function (path) {
|
|
if (namedTypes.BlockStatement.check(path.node.body)) {
|
|
path.node.body.body.reverse();
|
|
}
|
|
this.traverse(path);
|
|
},
|
|
});
|
|
|
|
const altered = code
|
|
.replace("a()", "xxx")
|
|
.replace("b()", "a()")
|
|
.replace("xxx", "b()");
|
|
|
|
assert.strictEqual(altered, printer.print(ast).code);
|
|
});
|
|
|
|
it("[" + parserName + "] TabHandling", function () {
|
|
function check(code: string, tabWidth: number) {
|
|
const lines = fromString(code, { tabWidth: tabWidth });
|
|
assert.strictEqual(lines.length, 1);
|
|
|
|
function checkId(s: any, loc: types.namedTypes.SourceLocation) {
|
|
const sliced = lines.slice(loc.start, loc.end);
|
|
assert.strictEqual(s + "", sliced.toString());
|
|
}
|
|
|
|
types.visit(
|
|
parse(code, {
|
|
tabWidth: tabWidth,
|
|
parser,
|
|
}),
|
|
{
|
|
visitIdentifier(path) {
|
|
const ident = path.node;
|
|
checkId(ident.name, ident.loc!);
|
|
this.traverse(path);
|
|
},
|
|
|
|
visitLiteral(path) {
|
|
const lit = path.node;
|
|
checkId(lit.value, lit.loc!);
|
|
this.traverse(path);
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
for (let tabWidth = 1; tabWidth <= 8; ++tabWidth) {
|
|
check("\t\ti = 10;", tabWidth);
|
|
check("\t\ti \t= 10;", tabWidth);
|
|
check("\t\ti \t=\t 10;", tabWidth);
|
|
check("\t \ti \t=\t 10;", tabWidth);
|
|
check("\t \ti \t=\t 10;\t", tabWidth);
|
|
check("\t \ti \t=\t 10;\t ", tabWidth);
|
|
}
|
|
});
|
|
|
|
it("[" + parserName + "] Only comment followed by space", function () {
|
|
const printer = new Printer();
|
|
|
|
function check(code: string) {
|
|
const ast = parse(code, { parser });
|
|
assert.strictEqual(printer.print(ast).code, code);
|
|
}
|
|
|
|
check("// comment");
|
|
check("// comment ");
|
|
check("// comment\n");
|
|
check("// comment\n\n");
|
|
check(" // comment\n");
|
|
check(" // comment\n ");
|
|
check(" // comment \n ");
|
|
|
|
check("/* comment */");
|
|
check("/* comment */ ");
|
|
check(" /* comment */");
|
|
check("\n/* comment */");
|
|
check("\n/* comment */\n");
|
|
check("\n /* comment */\n ");
|
|
check("/* comment */\n ");
|
|
check("/* com\n\nment */");
|
|
check("/* com\n\nment */ ");
|
|
check(" /* com\n\nment */ ");
|
|
});
|
|
}
|