@rocket.chat/apps-engine
Version:
The engine code for the Rocket.Chat Apps which manages, runs, translates, coordinates and all of that.
437 lines (419 loc) • 13 kB
text/typescript
// @deno-types="../../../../acorn.d.ts"
import { AnyNode, ClassDeclaration, ExpressionStatement, FunctionDeclaration, VariableDeclaration } from 'acorn';
/**
* Partial AST blocks to support testing.
* `start` and `end` properties are omitted for brevity.
*/
type TestNodeExcerpt<N extends AnyNode = AnyNode> = {
code: string;
node: N;
};
export const FunctionDeclarationFoo: TestNodeExcerpt<FunctionDeclaration> = {
code: 'function foo() {}',
node: {
type: 'FunctionDeclaration',
id: {
type: 'Identifier',
name: 'foo',
},
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [],
},
},
};
export const ConstFooAssignedFunctionExpression: TestNodeExcerpt<VariableDeclaration> = {
code: 'const foo = function() {}',
node: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: 'foo',
},
init: {
type: 'FunctionExpression',
id: null,
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [],
},
},
},
],
},
};
export const AssignmentExpressionOfArrowFunctionToFooIdentifier: TestNodeExcerpt<ExpressionStatement> = {
code: 'foo = () => {}',
node: {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'Identifier',
name: 'foo',
},
right: {
type: 'ArrowFunctionExpression',
id: null,
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [],
},
},
},
},
};
export const AssignmentExpressionOfNamedFunctionToFooMemberExpression: TestNodeExcerpt<ExpressionStatement> = {
code: 'obj.foo = function bar() {}',
node: {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'a',
},
property: {
type: 'Identifier',
name: 'foo',
},
computed: false,
optional: false,
},
right: {
type: 'FunctionExpression',
id: null,
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [],
},
},
},
},
};
export const MethodDefinitionOfFooInClassBar: TestNodeExcerpt<ClassDeclaration> = {
code: 'class Bar { foo() {} }',
node: {
type: 'ClassDeclaration',
id: {
type: 'Identifier',
name: 'Bar',
},
superClass: null,
body: {
type: 'ClassBody',
body: [
{
type: 'MethodDefinition',
key: {
type: 'Identifier',
name: 'foo',
},
value: {
type: 'FunctionExpression',
id: null,
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [],
},
},
kind: 'method',
computed: false,
static: false,
},
],
},
},
};
export const SimpleCallExpressionOfFoo: TestNodeExcerpt<ExpressionStatement> = {
code: 'foo()',
node: {
type: 'ExpressionStatement',
expression: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'foo',
},
arguments: [],
optional: false,
},
},
};
export const SyncFunctionDeclarationWithAsyncCallExpression: TestNodeExcerpt<FunctionDeclaration> = {
// NOTE: this is invalid syntax, it won't be parsed by acorn
// but it can be an intermediary state of the AST after we run
// `wrapWithAwait` on "bar" call expressions, for instance
code: 'function foo() { return () => await bar() }',
node: {
type: 'FunctionDeclaration',
id: {
type: 'Identifier',
name: 'foo',
},
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [
{
type: 'ReturnStatement',
argument: {
type: 'ArrowFunctionExpression',
id: null,
expression: true,
generator: false,
async: false,
params: [],
body: {
type: 'AwaitExpression',
argument: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'bar',
},
arguments: [],
optional: false,
},
},
},
},
],
},
},
};
export const AssignmentOfFooToBar: TestNodeExcerpt<ExpressionStatement> = {
code: 'bar = foo',
node: {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'Identifier',
name: 'bar',
},
right: {
type: 'Identifier',
name: 'foo',
},
},
},
};
export const AssignmentOfFooToBarMemberExpression: TestNodeExcerpt<ExpressionStatement> = {
code: 'obj.bar = foo',
node: {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
computed: false,
optional: false,
object: {
type: 'Identifier',
name: 'obj',
},
property: {
type: 'Identifier',
name: 'bar',
},
},
right: {
type: 'Identifier',
name: 'foo',
},
},
},
};
export const AssignmentOfFooToBarVariableDeclarator: TestNodeExcerpt<VariableDeclaration> = {
code: 'const bar = foo',
node: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: 'bar',
},
init: {
type: 'Identifier',
name: 'foo',
},
},
],
},
};
export const AssignmentOfFooToBarPropertyDefinition: TestNodeExcerpt<ClassDeclaration> = {
code: 'class baz { bar = foo }',
node: {
type: 'ClassDeclaration',
id: {
type: 'Identifier',
name: 'baz',
},
superClass: null,
body: {
type: 'ClassBody',
body: [
{
type: 'PropertyDefinition',
static: false,
computed: false,
key: {
type: 'Identifier',
name: 'bar',
},
value: {
type: 'Identifier',
name: 'foo',
},
},
],
},
},
};
const fixSimpleCallExpressionCode = `
function bar() {
const a = foo();
return a;
}`;
export const FixSimpleCallExpression: TestNodeExcerpt<FunctionDeclaration> = {
code: fixSimpleCallExpressionCode,
node: {
type: 'FunctionDeclaration',
id: {
type: 'Identifier',
name: 'bar',
},
expression: false,
generator: false,
async: false,
params: [],
body: {
type: 'BlockStatement',
body: [
{
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: 'a',
},
init: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'foo',
},
arguments: [],
optional: false,
},
},
],
},
{
type: 'ReturnStatement',
argument: {
type: 'Identifier',
name: 'a',
},
},
],
},
},
};
export const ArrowFunctionDerefCallExpression: TestNodeExcerpt<VariableDeclaration> = {
// NOTE: this call strategy is widely used by bundlers; it's used to sever the `this`
// reference in the method from the object that contains it. This is mostly because
// the bundler wants to ensure that it does not messes up the bindings in the code it
// generates.
//
// This would be similar to doing `foo.call(undefined)`
code: 'const bar = () => (0, e.foo)();',
node: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: 'bar',
},
init: {
type: 'ArrowFunctionExpression',
id: null,
expression: true,
generator: false,
async: false,
params: [],
body: {
type: 'CallExpression',
optional: false,
arguments: [],
callee: {
type: 'SequenceExpression',
expressions: [
{
type: 'Literal',
value: 0,
},
{
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'e',
},
property: {
type: 'Identifier',
name: 'foo',
},
computed: false,
optional: false,
},
],
},
},
},
},
],
},
};