recast/test/ecmascript.ts
2022-12-14 16:29:49 -05:00

243 lines
7.1 KiB
TypeScript

import assert from "assert";
import { parse } from "../lib/parser";
import { Printer } from "../lib/printer";
import * as types from "ast-types";
const n = types.namedTypes;
const b = types.builders;
import { EOL as eol } from "os";
describe("ES6 Compatability", function () {
function convertShorthandMethod() {
const printer = new Printer({ tabWidth: 2 });
const code = [
"var name='test-name';",
"var shorthandObj = {",
" name,",
" func() { return 'value'; }",
"};",
].join(eol);
const ast = parse(code);
n.VariableDeclaration.assert(ast.program.body[1]);
const shorthandObjDec = ast.program.body[1].declarations[0].init;
const methodDecProperty = shorthandObjDec.properties[1];
const newES5MethodProperty = b.property(
methodDecProperty.kind,
methodDecProperty.key,
methodDecProperty.value,
);
const correctMethodProperty = b.property(
methodDecProperty.kind,
methodDecProperty.key,
b.functionExpression(
methodDecProperty.value.id,
methodDecProperty.value.params,
methodDecProperty.value.body,
methodDecProperty.value.generator,
methodDecProperty.value.expression,
),
);
assert.strictEqual(
printer.print(newES5MethodProperty).code,
printer.print(correctMethodProperty).code,
);
}
it(
"correctly converts from a shorthand method to ES5 function",
convertShorthandMethod,
);
function respectDestructuringAssignment() {
const printer = new Printer({ tabWidth: 2 });
const code = "var {a} = {};";
const ast = parse(code);
n.VariableDeclaration.assert(ast.program.body[0]);
assert.strictEqual(printer.print(ast).code, code);
}
it("respects destructuring assignments", respectDestructuringAssignment);
});
describe("import/export syntax", function () {
const printer = new Printer({ tabWidth: 2 });
function check(source: string) {
const ast1 = parse(source);
const ast2 = parse(printer.printGenerically(ast1).code);
types.astNodesAreEquivalent.assert(ast1, ast2);
}
it("should parse and print import statements correctly", function () {
check("import foo from 'foo'");
// default imports
check("import foo from 'foo';");
check("import {default as foo} from 'foo';");
// named imports
check("import {bar} from 'foo';");
check("import {bar, baz} from 'foo';");
check("import {bar as baz} from 'foo';");
check("import {bar as baz, xyz} from 'foo';");
// glob imports
check("import * as foo from 'foo';");
// mixing imports
check("import foo, {baz as xyz} from 'foo';");
check("import foo, * as bar from 'foo';");
// just import
check("import 'foo';");
});
it("should parse and print export statements correctly", function () {
// default exports
check("export default 42;");
check("export default {};");
check("export default [];");
check("export default foo;");
check("export default function () {}");
check("export default class {}");
check("export default function foo () {}");
check("export default class foo {}");
check("export default `hello world`");
// variables exports
check("export var foo = 1;");
check("export var foo = function () {};");
check("export var bar;"); // lazy initialization
check("export let foo = 2;");
check("export let bar;"); // lazy initialization
check("export const foo = 3;");
check("export function foo () {}");
check("export class foo {}");
// named exports
check("export {foo};");
check("export {foo, bar};");
check("export {foo as bar};");
check("export {foo as default};");
check("export {foo as default, bar};");
// exports from
check("export * from 'foo';");
check("export {foo} from 'foo';");
check("export {foo, bar} from 'foo';");
check("export {foo as bar} from 'foo';");
check("export {foo as default} from 'foo';");
check("export {foo as default, bar} from 'foo';");
check("export {default} from 'foo';");
check("export {default as foo} from 'foo';");
});
it("should forbid invalid import/export syntax", function () {
function checkInvalid(source: string, expectedMessage: string) {
try {
parse(source);
throw new Error(
"Parsing should have failed: " + JSON.stringify(source),
);
} catch (err: any) {
assert.strictEqual(err.message, "Line 1: " + expectedMessage);
}
}
// const variables must have an initializer
checkInvalid(
"export const bar;",
"Missing initializer in const declaration",
);
// Unexpected token identifier, invalid named export syntax
checkInvalid("export foo;", "Unexpected identifier");
// Unexpected token (, use a function declaration instead
checkInvalid("export function () {}", "Unexpected token (");
// Unexpected token default
checkInvalid("export function default () {}", "Unexpected token default");
// Missing from after import
checkInvalid("import foo;", "Unexpected token ;");
// Missing from after import
checkInvalid("import { foo, bar };", "Unexpected token ;");
// Invalid module specifier
checkInvalid("import foo from bar;", "Unexpected token");
// Unexpected token default
checkInvalid("import default from 'foo';", "Unexpected token default");
// Unexpected token from
checkInvalid("export default from 'foo';", "Unexpected token from");
// Missing from after export
checkInvalid("export {default};", "Unexpected token ;");
// Missing from after export
checkInvalid("export *;", "Unexpected token ;");
// Missing from after import
checkInvalid("import {default as foo};", "Unexpected token ;");
// Missing as after import *
checkInvalid("import * from 'foo';", "Unexpected token");
// Unexpected token =
checkInvalid("export default = 42;", "Unexpected token =");
// Unexpected token default
checkInvalid(
"import {bar as default} from 'foo';",
"Unexpected token default",
);
// Unexpected token ,
checkInvalid(
"import foo, * as bar, {baz as xyz} from 'foo';",
"Unexpected token ,",
);
// Unexpected token ,
checkInvalid("import {bar}, foo from 'foo';", "Unexpected token ,");
// Unexpected token ,
checkInvalid("import {bar}, * as foo from 'foo';", "Unexpected token ,");
// Unexpected token ,
checkInvalid("import foo, {bar}, foo from 'foo';", "Unexpected token ,");
// Unexpected token ,
checkInvalid("import {bar}, {foo} from 'foo';", "Unexpected token ,");
// Unexpected token ,
checkInvalid(
"import * as bar, {baz as xyz} from 'foo';",
"Unexpected token ,",
);
});
it("should pretty-print template strings with backticks", function () {
const code = [
'var noun = "fool";',
"var s = `I am a ${noun}`;",
"var t = tag`You said: ${s}!`;",
].join(eol);
const ast = parse(code);
assert.strictEqual(
new Printer({
tabWidth: 2,
}).printGenerically(ast).code,
code,
);
});
});