Skip to content

Commit 848a0c1

Browse files
feat: add babeliser (#146)
* feat: add babeliser * chore: add babeliser docs * chore: update lock * chore: format * fix: update babel types --------- Co-authored-by: Oliver Eyton-Williams <[email protected]>
1 parent dc30a10 commit 848a0c1

File tree

8 files changed

+926
-28
lines changed

8 files changed

+926
-28
lines changed

docs/SUMMARY.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
- [Python](./python.md)
99
- [Common Tools](./common-tools.md)
1010
- [Testing in General](./testing-in-general.md)
11-
- [JavaScript]()
11+
- [JavaScript](./javascript.md)
1212
- [Nodejs]()
1313
- [Rust](./rust.md)
1414
- [Contributing](./CONTRIBUTING.md)

docs/javascript.md

+224-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,226 @@
11
# JavaScript
22

3-
WIP
3+
## `Babeliser`
4+
5+
Instantiate a new `Babeliser` with an optional [`options` object](https://babeljs.io/docs/babel-parser#options):
6+
7+
```js
8+
const babelisedCode = new __helpers.Babeliser(code, {
9+
plugins: ["typescript"],
10+
});
11+
```
12+
13+
### `getVariableDeclarations`
14+
15+
```js
16+
const programProvider = program.provider as AnchorProvider;
17+
```
18+
19+
```javascript
20+
const variableDeclaration = babelisedCode
21+
.getVariableDeclarations()
22+
.find((v) => {
23+
return v.declarations?.[0]?.id?.name === "programProvider";
24+
});
25+
assert.exists(
26+
variableDeclaration,
27+
"A variable named `programProvider` should exist"
28+
);
29+
const tAsExpression = variableDeclaration.declarations?.[0]?.init;
30+
const { object, property } = tAsExpression.expression;
31+
assert.equal(
32+
object.name,
33+
"program",
34+
"The `programProvider` variable should be assigned `program.provider`"
35+
);
36+
assert.equal(
37+
property.name,
38+
"provider",
39+
"The `programProvider` variable should be assigned `program.provider`"
40+
);
41+
const tAnnotation = tAsExpression.typeAnnotation;
42+
assert.equal(
43+
tAnnotation.typeName.name,
44+
"AnchorProvider",
45+
"The `programProvider` variable should be assigned `program.provider as AnchorProvider`"
46+
);
47+
```
48+
49+
### `getFunctionDeclarations`
50+
51+
```js
52+
export function uploadFile() {}
53+
```
54+
55+
```js
56+
const functionDeclaration = babelisedCode
57+
.getFunctionDeclarations()
58+
.find((f) => {
59+
return f.id.name === "uploadFile";
60+
});
61+
assert.exists(
62+
functionDeclaration,
63+
"A function named `uploadFile` should exist"
64+
);
65+
66+
const exports = babelisedCode.getType("ExportNamedDeclaration");
67+
const functionIsExported = exports.some((e) => {
68+
return (
69+
e.declaration?.id?.name === "uploadFile" ||
70+
e.specifiers?.find((s) => s.exported.name === "uploadFile")
71+
);
72+
});
73+
assert.isTrue(
74+
functionIsExported,
75+
"The `uploadFile` function should be exported"
76+
);
77+
```
78+
79+
### `generateCode`
80+
81+
This method is useful when wanting to regenerate code from the AST. This can then be _re-babelised_, and compacted to compare with an expected code string.
82+
83+
```js
84+
it("example with generateCode", () => {
85+
const [gamePublicKey, _] = PublicKey.findProgramAddressSync(
86+
[Buffer.from("game"), payer.publicKey.toBuffer(), Buffer.from(gameId)],
87+
program.programId
88+
);
89+
});
90+
```
91+
92+
```js
93+
// Limit scope to `it` CallExpression
94+
const callExpression = babelisedCode.getType("CallExpression").find((c) => {
95+
return c.callee?.name === "it";
96+
});
97+
const blockStatement = callExpression?.arguments?.[1]?.body;
98+
// Take body AST, and generate a compacted string
99+
const actualCodeString = babelisedCode.generateCode(blockStatement, {
100+
compact: true,
101+
});
102+
const expectedCodeString = `const[gamePublicKey,_]=PublicKey.findProgramAddressSync([Buffer.from('game'),payer.publicKey.toBuffer(),Buffer.from(gameId)],program.programId)`;
103+
assert.deepInclude(actualCodeString, expectedCodeString);
104+
```
105+
106+
### `getExpressionStatements`
107+
108+
```js
109+
export async function createAccount() {
110+
transaction.add(tx);
111+
}
112+
```
113+
114+
```js
115+
const expressionStatement = babelisedCode
116+
.getExpressionStatements()
117+
.find((e) => {
118+
return (
119+
e.expression?.callee?.property?.name === "add" &&
120+
e.expression?.callee?.object?.name === "transaction" &&
121+
e.scope?.join() === "global,createAccount"
122+
);
123+
});
124+
const callExpression = expressionStatement?.expression?.arguments?.[0];
125+
assert.equal(
126+
callExpression?.name,
127+
"tx",
128+
"`tx` should be the first argument to `transaction.add`"
129+
);
130+
```
131+
132+
### `getExpressionStatement`
133+
134+
```js
135+
await main();
136+
```
137+
138+
```js
139+
const mainExpressionStatement = babelisedCode.getExpressionStatement("main");
140+
assert.exists(mainExpressionStatement, "You should call `main`");
141+
assert.equal(
142+
mainExpressionStatement?.expression?.type,
143+
"AwaitExpression",
144+
"You should call `main` with `await`"
145+
);
146+
```
147+
148+
### `getImportDeclarations`
149+
150+
```js
151+
import { PublicKey } from "@solana/web3.js";
152+
```
153+
154+
```js
155+
const importDeclaration = babelisedCode.getImportDeclarations().find((i) => {
156+
return i.source.value === "@solana/web3.js";
157+
});
158+
assert.exists(importDeclaration, "You should import from `@solana/web3.js`");
159+
const importSpecifiers = importDeclaration.specifiers.map(
160+
(s) => s.imported.name
161+
);
162+
assert.include(
163+
importSpecifiers,
164+
"PublicKey",
165+
"`PublicKey` should be imported from `@solana/web3.js`"
166+
);
167+
```
168+
169+
### `getType`
170+
171+
```js
172+
const tx = await program.methods.setupGame().rpc();
173+
```
174+
175+
```js
176+
const memberExpression = babelisedCode.getType("MemberExpression").find((m) => {
177+
return (
178+
m.object?.object?.name === "program" &&
179+
m.object?.property?.name === "methods"
180+
);
181+
});
182+
assert.exists(memberExpression, "`program.methods.` should exist");
183+
const { property } = memberExpression;
184+
assert.equal(
185+
property.name,
186+
"setupGame",
187+
"`program.methods.setupGame` should exist"
188+
);
189+
```
190+
191+
### `getLineAndColumnFromIndex`
192+
193+
```ts
194+
const ACCOUNT_SIZE = borsh.serialize(
195+
HelloWorldSchema,
196+
new HelloWorldAccount()
197+
).length;
198+
199+
export async function createAccount() {
200+
const lamports = await connection.getMinimumBalanceForRentExemption(
201+
ACCOUNT_SIZE
202+
);
203+
}
204+
```
205+
206+
```js
207+
const account = babelisedCode.getVariableDeclarations().find((v) => {
208+
return v.declarations?.[0]?.id?.name === "ACCOUNT_SIZE";
209+
});
210+
const createAccount = babelisedCode.getFunctionDeclarations().find((f) => {
211+
return f.id?.name === "createAccount";
212+
});
213+
214+
const { end } = account;
215+
const { start } = createAccount;
216+
217+
const { line: accountLine } = babelisedCode.getLineAndColumnFromIndex(end);
218+
const { line: createAccountLine } =
219+
babelisedCode.getLineAndColumnFromIndex(start);
220+
221+
assert.isBelow(
222+
accountLine,
223+
createAccountLine,
224+
`'ACCOUNT_SIZE' declared on line ${accountLine}, but should be declared before ${createAccountLine}`
225+
);
226+
```

0 commit comments

Comments
 (0)