gen-typescript-from-tolk-dev 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +33 -29
  2. package/dist/abi-types.d.ts +169 -0
  3. package/dist/abi-types.js +8 -0
  4. package/dist/abi.d.ts +54 -0
  5. package/dist/abi.js +13 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +83 -0
  8. package/dist/codegen-ctx.d.ts +66 -0
  9. package/dist/codegen-ctx.js +108 -0
  10. package/dist/emit-field-defs.d.ts +5 -0
  11. package/dist/emit-field-defs.js +61 -0
  12. package/dist/emit-pack-unpack.d.ts +9 -0
  13. package/dist/emit-pack-unpack.js +296 -0
  14. package/dist/emit-stack-rw.d.ts +4 -0
  15. package/dist/emit-stack-rw.js +232 -0
  16. package/dist/emit-ts-types.d.ts +6 -0
  17. package/dist/emit-ts-types.js +74 -0
  18. package/dist/formatting.d.ts +15 -0
  19. package/{src/formatting.ts → dist/formatting.js} +26 -28
  20. package/dist/generate-ts-wrappers.d.ts +7 -0
  21. package/dist/generate-ts-wrappers.js +444 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +11 -0
  24. package/dist/out-template.generated.d.ts +1 -0
  25. package/dist/out-template.generated.js +6 -0
  26. package/dist/types-kernel.d.ts +10 -0
  27. package/dist/types-kernel.js +212 -0
  28. package/dist/unsupported-errors.d.ts +28 -0
  29. package/{src/unsupported-errors.ts → dist/unsupported-errors.js} +33 -39
  30. package/package.json +56 -30
  31. package/bin/generator.js +0 -15
  32. package/src/abi-types.ts +0 -157
  33. package/src/abi.ts +0 -132
  34. package/src/cli-generate-from-abi-json.ts +0 -21
  35. package/src/codegen-ctx.ts +0 -115
  36. package/src/dynamic-ctx.ts +0 -55
  37. package/src/dynamic-debug-print.ts +0 -191
  38. package/src/dynamic-get-methods.ts +0 -454
  39. package/src/dynamic-serialization.ts +0 -430
  40. package/src/dynamic-validation.ts +0 -55
  41. package/src/emit-field-defs.ts +0 -60
  42. package/src/emit-pack-unpack.ts +0 -280
  43. package/src/emit-stack-rw.ts +0 -239
  44. package/src/emit-ts-types.ts +0 -66
  45. package/src/generate-from-abi-json.ts +0 -22
  46. package/src/generate-ts-wrappers.ts +0 -477
  47. package/src/out-template.ts +0 -514
  48. package/src/tolk-to-abi.ts +0 -5
  49. package/src/types-kernel.ts +0 -215
  50. package/tsconfig.json +0 -13
@@ -1,477 +0,0 @@
1
- import fs from 'fs';
2
- import type { Ty, ABIStruct, ABIAlias, ABIEnum } from './abi-types';
3
- import type { ContractABI, ABIGetMethod } from './abi';
4
- import { CodegenCtx, RUNTIME } from './codegen-ctx';
5
- import { OutBuf, safeJsIdent, safeFieldDecl, safeFieldRead } from './formatting';
6
- import { emitTsType, emitUnionLabelAndValue } from './emit-ts-types';
7
- import { emitLoadExpr, emitStoreStatement, emitMakeCellFromExpr, emitCallToCreateMethodExpr } from './emit-pack-unpack';
8
- import { emitStackReadExpr, emitStackWriteItems } from './emit-stack-rw';
9
- import { emitFieldDefault, emitFieldDefaultInComment, isDefaultValueSupported } from './emit-field-defs';
10
- import { CantGeneratePackUnpack, CantGenerateWrappersAtAll } from './unsupported-errors';
11
- import { calcWidthOnStack, createLabelsForUnion, instantiateGenerics, renderTy } from './types-kernel'
12
-
13
- // Output a struct as a doc comment.
14
- function renderStructDocComment(s: ABIStruct): string {
15
- const genericTs = s.typeParams ? `<${s.typeParams.join(', ')}>` : '';
16
- return [
17
- '/**',
18
- ` > struct${s.prefix ? ` (${s.prefix.prefixStr})` : ''} ${s.name}${genericTs} {`,
19
- ...s.fields.map(f => ` > ${f.name}: ${renderTy(f.ty)}`),
20
- ' > }',
21
- ' */'
22
- ].join('\n')
23
- }
24
-
25
- // Output an alias as a doc comment.
26
- function renderAliasDocComment(a: ABIAlias): string {
27
- const genericTs = a.typeParams ? `<${a.typeParams.join(', ')}>` : '';
28
- return [
29
- '/**',
30
- ` > type ${a.name}${genericTs} = ${renderTy(a.targetTy)}`,
31
- ' */'
32
- ].join('\n');
33
- }
34
-
35
- // Output an enum as a doc comment.
36
- function renderEnumDocComment(e: ABIEnum): string {
37
- return [
38
- '/**',
39
- ` > enum ${e.name} { ${e.members.length} variants }`,
40
- ' */'
41
- ].join('\n');
42
- }
43
-
44
- // If a struct can't be serialized, then its `fromSlice`/`store` is just `throw` with description.
45
- // Since Tolk code contains many structs (some are used for contract getters, for example),
46
- // not all of them are intended for serialization, it's expected.
47
- function renderThrowCantPackUnpack(structName: string, caughtEx: any, isPack: boolean): string {
48
- if (!(caughtEx instanceof CantGeneratePackUnpack))
49
- throw caughtEx
50
-
51
- const action = isPack ? `pack '${structName}' to cell` : `unpack '${structName}' from cell`;
52
- return `throw new Error(\`Can't ${action}, because ${caughtEx.message}\`);`;
53
- }
54
-
55
- // All-in-one: a struct with autogen serializers.
56
- // Tolk `struct X` -> TS `interface X` { $: 'X'; ...fields } + `const X` with serializers.
57
- function tolkStructToTypeScript(ctx: CodegenCtx, s: ABIStruct, buf: OutBuf): string {
58
- const ps = s.typeParams;
59
- const genericTs = ps ? `<${ps.join(', ')}>` : '';
60
- const structName = safeJsIdent(s.name);
61
- const structTy: Ty = { kind: 'StructRef', structName: s.name, typeArgs: s.typeParams?.map(nameT => ({kind: 'genericT', nameT})) };
62
-
63
- const loadCallbackParams = ps ? ', ' + ps.map(p => `loadFn_${p}: LoadCallback<${p}>`).join(', ') : '';
64
- const storeCallbackParams = ps ? ', ' + ps.map(p => `storeFn_${p}: StoreCallback<${p}>`).join(', ') : '';
65
-
66
- buf.push(renderStructDocComment(s));
67
- buf.push(`export interface ${structName}${genericTs} {`);
68
- buf.push(`readonly $: '${s.name}'`);
69
- for (const f of s.fields) {
70
- let comment = f.defaultValue ? ` /* = ${emitFieldDefaultInComment(f.defaultValue)} */` : '';
71
- buf.push(`${safeFieldDecl(f.name)}: ${emitTsType(ctx, f.ty)}${comment}`);
72
- }
73
- buf.push('}');
74
- buf.emptyLine();
75
- buf.push(`export const ${structName} = {`);
76
- if (s.prefix) {
77
- buf.push(`PREFIX: ${s.prefix.prefixStr},`);
78
- buf.emptyLine();
79
- }
80
- let argsParam = s.fields.length ? 'args: ' + structParameterNoLabel(ctx, s.name) : '';
81
- buf.push(`create${genericTs}(${argsParam}): ${structName}${genericTs} {`);
82
- buf.push(`return {`);
83
- buf.push(`$: '${s.name}',`);
84
- for (let f of s.fields.filter(f => f.defaultValue && isDefaultValueSupported(f.ty))) {
85
- let needAsAny = f.ty.kind === 'genericT';
86
- buf.push(`${safeFieldDecl(f.name)}: ${emitFieldDefault(ctx, f.defaultValue!)}${needAsAny ? ' as any' : ''},`);
87
- }
88
- if (s.fields.length > 0) {
89
- buf.push('...args');
90
- }
91
- buf.push('}');
92
- buf.push(`},`);
93
- buf.push(`fromSlice${genericTs}(s: c.Slice${loadCallbackParams}): ${structName}${genericTs} {`);
94
- if (s.customPackUnpack?.unpackFromSlice) {
95
- buf.push(`return invokeCustomUnpackFromSlice<${structName}>('${s.name}', s);`);
96
- } else try {
97
- const fieldsLoads = s.fields.map(f =>
98
- `${safeFieldDecl(f.name)}: ${emitLoadExpr(ctx, `${structName}${safeFieldRead(f.name)}`, f.ty)},`
99
- )
100
- if (s.prefix && s.prefix.prefixLen === 32) {
101
- buf.push(`${RUNTIME.loadAndCheckPrefix32}(s, ${s.prefix.prefixStr}, '${s.name}');`);
102
- } else if (s.prefix) {
103
- ctx.has_non32Prefixes = true;
104
- buf.push(`${RUNTIME.loadAndCheckPrefix}(s, ${s.prefix.prefixStr}, ${s.prefix.prefixLen}, '${s.name}');`);
105
- }
106
- buf.push(`return {`);
107
- buf.push(`$: '${s.name}',`);
108
- fieldsLoads.forEach(l => buf.push(l));
109
- buf.push('}');
110
- } catch (ex) {
111
- buf.push(renderThrowCantPackUnpack(structName, ex, false));
112
- }
113
- buf.push(`},`);
114
- buf.push(`store${genericTs}(self: ${structName}${genericTs}, b: c.Builder${storeCallbackParams}): void {`);
115
- if (s.customPackUnpack?.packToBuilder) {
116
- buf.push(`invokeCustomPackToBuilder<${structName}>('${s.name}', self, b);`);
117
- } else try {
118
- const fieldsStores = s.fields.map(f =>
119
- emitStoreStatement(ctx, `self${safeFieldRead(f.name)}`, f.ty)
120
- );
121
- if (s.prefix) {
122
- buf.push(`b.storeUint(${s.prefix.prefixStr}, ${s.prefix.prefixLen});`);
123
- }
124
- fieldsStores.forEach(l => buf.push(l));
125
- } catch (ex) {
126
- buf.push(renderThrowCantPackUnpack(structName, ex, true))
127
- }
128
- buf.push(`},`);
129
- buf.push(`toCell${genericTs}(self: ${structName}${genericTs}${storeCallbackParams}): c.Cell {`);
130
- buf.push(`return ${emitMakeCellFromExpr(ctx, 'self', structTy, true)};`);
131
- buf.push('}');
132
- buf.push('}');
133
- return buf.toString();
134
- }
135
-
136
- // All-in-one: an alias with autogen serializers.
137
- // Tolk `type X = Y` -> TS `type X = Y` (interchangeable with its underlying type, like in Tolk).
138
- function tolkAliasToTypeScript(ctx: CodegenCtx, a: ABIAlias, buf: OutBuf): string {
139
- const ps = a.typeParams;
140
- const genericTs = ps ? `<${ps.join(', ')}>` : '';
141
- const aliasName = safeJsIdent(a.name);
142
- const aliasTy: Ty = { kind: 'AliasRef', aliasName: a.name, typeArgs: a.typeParams?.map(nameT => ({kind: 'genericT', nameT})) };
143
-
144
- const loadCallbackParams = ps ? ', ' + ps.map(p => `loadFn_${p}: LoadCallback<${p}>`).join(', ') : '';
145
- const storeCallbackParams = ps ? ', ' + ps.map(p => `storeFn_${p}: StoreCallback<${p}>`).join(', ') : '';
146
-
147
- ctx.has_customPackUnpack ||= a.customPackUnpack !== undefined;
148
-
149
- buf.push(renderAliasDocComment(a));
150
- if (a.targetTy.kind === 'union') {
151
- buf.push(`export type ${aliasName}${genericTs} =`).indent();
152
- for (const v of createLabelsForUnion(ctx.symbols, a.targetTy.variants)) {
153
- buf.push('| ' + emitUnionLabelAndValue(v, emitTsType(ctx, v.variantTy)));
154
- }
155
- buf.outdent();
156
- } else {
157
- buf.push(`export type ${aliasName}${genericTs} = ${emitTsType(ctx, a.targetTy)}`);
158
- }
159
- buf.emptyLine();
160
- buf.push(`export const ${aliasName} = {`);
161
- buf.push(`fromSlice${genericTs}(s: c.Slice${loadCallbackParams}): ${aliasName}${genericTs} {`);
162
- if (a.customPackUnpack?.unpackFromSlice) {
163
- buf.push(`return invokeCustomUnpackFromSlice<${aliasName}>('${a.name}', s);`);
164
- } else try {
165
- buf.push(`return ${emitLoadExpr(ctx, aliasName, a.targetTy)};`);
166
- } catch (ex) {
167
- buf.push(renderThrowCantPackUnpack(aliasName, ex, false));
168
- }
169
- buf.push(`},`);
170
- buf.push(`store${genericTs}(self: ${aliasName}${genericTs}, b: c.Builder${storeCallbackParams}): void {`);
171
- if (a.customPackUnpack?.packToBuilder) {
172
- buf.push(`invokeCustomPackToBuilder<${aliasName}>('${a.name}', self, b);`);
173
- } else try {
174
- buf.push(emitStoreStatement(ctx, 'self', a.targetTy));
175
- } catch (ex) {
176
- buf.push(renderThrowCantPackUnpack(aliasName, ex, true));
177
- }
178
- buf.push(`},`);
179
- buf.push(`toCell${genericTs}(self: ${aliasName}${genericTs}${storeCallbackParams}): c.Cell {`);
180
- buf.push(`return ${emitMakeCellFromExpr(ctx, 'self', aliasTy, true)};`);
181
- buf.push('}');
182
- buf.push('}');
183
- return buf.toString();
184
- }
185
-
186
- // All-in-one: an enum with autogen serializers.
187
- // Tolk `enum X` -> TS `type X = bigint` + `const X` with members and serializers.
188
- function tolkEnumToTypeScript(ctx: CodegenCtx, e: ABIEnum, buf: OutBuf): string {
189
- const enumName = safeJsIdent(e.name);
190
- const enumTy: Ty = { kind: 'EnumRef', enumName: e.name };
191
-
192
- buf.push(renderEnumDocComment(e));
193
- buf.push(`export type ${enumName} = bigint`);
194
- buf.emptyLine();
195
- buf.push(`export const ${enumName} = {`);
196
- for (const v of e.members) {
197
- let memberName = v.name;
198
- if (memberName === 'toCell' || memberName === 'store' || memberName === 'fromSlice') {
199
- memberName += '_';
200
- }
201
- buf.push(`${safeFieldDecl(memberName)}: ${v.value}n,`);
202
- }
203
- buf.emptyLine();
204
- buf.push(`fromSlice(s: c.Slice): ${enumName} {`);
205
- if (e.customPackUnpack?.unpackFromSlice) {
206
- buf.push(`return invokeCustomUnpackFromSlice<${enumName}>('${e.name}', s);`);
207
- } else {
208
- buf.push(`return ${emitLoadExpr(ctx, e.name, e.encodedAs)};`);
209
- }
210
- buf.push(`},`);
211
- buf.push(`store(self: ${enumName}, b: c.Builder): void {`);
212
- if (e.customPackUnpack?.packToBuilder) {
213
- buf.push(`return invokeCustomPackToBuilder<${enumName}>('${e.name}', self, b);`);
214
- } else {
215
- buf.push(emitStoreStatement(ctx, 'self', e.encodedAs));
216
- }
217
- buf.push(`},`);
218
- buf.push(`toCell(self: ${enumName}): c.Cell {`);
219
- buf.push(`return ${emitMakeCellFromExpr(ctx, 'self', enumTy, true)};`);
220
- buf.push('}');
221
- buf.push('}');
222
- return buf.toString();
223
- }
224
-
225
- // Convert "get_wallet_data" to "getWalletData".
226
- function snakeCaseToCamelCase(snakeString: string): string {
227
- return snakeString.replace(/_([a-zA-Z])/g, (_, letter: string) => letter.toUpperCase());
228
- }
229
-
230
- // For sendXXX contract methods, we generate not `(msg: XXX)` but `(body: { ...fields })`,
231
- // so that on a caller side, one can use `contract.sendXXX({ queryId: 0n, ... })`
232
- // instead of `contract.sendXXX(XXX.create({ queryId: 0n, ... }))` (although the latter is also acceptable).
233
- // Note that if a field has a default value, it's declared as optional (`f?: T`, not `f: T`).
234
- function structParameterNoLabel(ctx: CodegenCtx, structName: string, typeArgs?: Ty[]): string {
235
- const structRef = ctx.symbols.getStruct(structName);
236
- let declBuf = new OutBuf();
237
- declBuf.push(`{`);
238
- for (let f of structRef.fields) {
239
- let comment = f.defaultValue ? ` /* = ${emitFieldDefaultInComment(f.defaultValue)} */` : '';
240
- let fTy = typeArgs ? instantiateGenerics(f.ty, structRef.typeParams, typeArgs) : f.ty;
241
- declBuf.push(`${safeFieldDecl(f.name)}${f.defaultValue && isDefaultValueSupported(fTy) ? '?' : ''}: ${emitTsType(ctx, fTy)}${comment}`);
242
- }
243
- declBuf.push(`}`);
244
- return declBuf.toString();
245
- }
246
-
247
- // Generate `static fromStorage()` method for a storage of type {ty}.
248
- // Most likely, it's a struct, then a caller can just use `MyContract.fromStorage({ fields })`.
249
- function generateFromStorageMethod(ctx: CodegenCtx, ty: Ty | undefined, contractName: string): string {
250
- if (ty == null || ty.kind === 'nullLiteral') { // means "no storage, don't emit a method"
251
- return '';
252
- }
253
-
254
- let storageParamT = emitTsType(ctx, ty);
255
- let bodyArg = 'emptyStorage';
256
- if (ty.kind === 'StructRef') {
257
- storageParamT = structParameterNoLabel(ctx, ty.structName, ty.typeArgs);
258
- bodyArg = emitCallToCreateMethodExpr(ctx, 'emptyStorage', ty);
259
- }
260
-
261
- let buf = new OutBuf();
262
- buf.push(`static fromStorage(emptyStorage: ${storageParamT}, deployedOptions?: DeployedAddrOptions) {`);
263
- buf.push(`const initialState = {`);
264
- buf.push(`code: deployedOptions?.overrideContractCode ?? ${contractName}.CodeCell,`);
265
- buf.push(`data: ${emitMakeCellFromExpr(ctx, bodyArg, ty)},`);
266
- buf.push(`};`);
267
- buf.push(`const address = calculateDeployedAddress(initialState.code, initialState.data, deployedOptions ?? {});`);
268
- buf.push(`return new ${contractName}(address, initialState);`);
269
- buf.push(`}`);
270
- return buf.toString();
271
- }
272
-
273
- // Generate a `createCellOfXXX` static method for a contract class.
274
- // It's essentially a wrapper for `XXX.toCell(XXX.create(body))`, but quite handy for the end user,
275
- // to make a cell for TON Connect. In practice, `toCell` is needed almost for messages only.
276
- function generateCreateCellOfMessageMethod(ctx: CodegenCtx, ty: Ty): string {
277
- let functionName = snakeCaseToCamelCase(safeJsIdent(`createCellOf_${renderTy(ty)}`));
278
-
279
- let bodyParamT = emitTsType(ctx, ty);
280
- let bodyArg = 'body';
281
- if (ty.kind === 'StructRef') {
282
- bodyParamT = structParameterNoLabel(ctx, ty.structName, ty.typeArgs);
283
- bodyArg = emitCallToCreateMethodExpr(ctx, 'body', ty);
284
- }
285
-
286
- let buf = new OutBuf();
287
- buf.push(`static ${functionName}(body: ${bodyParamT}) {`);
288
- buf.push(`return ${emitMakeCellFromExpr(ctx, bodyArg, ty)};`);
289
- buf.push(`}`);
290
- return buf.toString();
291
- }
292
-
293
- // Generate a `sendXXX` method for a message of type {ty}.
294
- // Most likely, it's a struct (an instantiated one also possible), but it can also be an alias for example.
295
- function generateSendMethod(ctx: CodegenCtx, ty: Ty): string {
296
- let functionName = snakeCaseToCamelCase(safeJsIdent(`send_${renderTy(ty)}`));
297
-
298
- let bodyParamT = emitTsType(ctx, ty);
299
- let bodyArg = 'body';
300
- if (ty.kind === 'StructRef') {
301
- bodyParamT = structParameterNoLabel(ctx, ty.structName, ty.typeArgs);
302
- bodyArg = emitCallToCreateMethodExpr(ctx, 'body', ty);
303
- }
304
-
305
- let buf = new OutBuf();
306
- buf.push(`async ${functionName}(provider: ContractProvider, via: Sender, msgValue: coins, body: ${bodyParamT}, extraOptions?: ${RUNTIME.ExtraSendOptions}) {`);
307
- buf.push(`return provider.internal(via, {`);
308
- buf.push(`value: msgValue,`);
309
- buf.push(`body: ${emitMakeCellFromExpr(ctx, bodyArg, ty)},`);
310
- buf.push(`...extraOptions`);
311
- buf.push(`});`);
312
- buf.push(`}`);
313
- return buf.toString();
314
- }
315
-
316
- // Generate a `getXXX` method: a contract getter that works off-chain, via the stack.
317
- function generateGetMethod(ctx: CodegenCtx, getM: ABIGetMethod): string {
318
- let functionName = snakeCaseToCamelCase(safeJsIdent('get_' + getM.name));
319
- functionName = functionName.replace(/^getGet/, 'get');
320
-
321
- let nParams = getM.parameters.length;
322
- let paramNames = getM.parameters.map(p => {
323
- let collision = Object.values(RUNTIME).some(n => p.name === n)
324
- || p.name === 'c' // import @ton/core
325
- || p.name === 'r' // StackReader in body
326
- || p.name === 'provider' // implicit parameter
327
- return safeJsIdent(collision ? p.name + '_' : p.name)
328
- });
329
-
330
- let buf = new OutBuf();
331
- let paramsStr = nParams ? `, ${getM.parameters.map((p, i) =>
332
- `${paramNames[i]}: ${emitTsType(ctx, p.ty)}${p.defaultValue ? ` = ${emitFieldDefault(ctx, p.defaultValue)}` : ''}`
333
- ).join(', ')}` : ''
334
- if (getM.returnTy.kind === 'tensor') {
335
- buf.push(`async ${functionName}(provider: ContractProvider${paramsStr}): Promise<[`);
336
- for (let item of getM.returnTy.items) {
337
- buf.push(emitTsType(ctx, item) + ',')
338
- }
339
- buf.push(']> {')
340
- } else {
341
- buf.push(`async ${functionName}(provider: ContractProvider${paramsStr}): Promise<${emitTsType(ctx, getM.returnTy)}> {`);
342
- }
343
- let stackW = calcWidthOnStack(ctx.symbols, getM.returnTy);
344
- if (nParams) {
345
- buf.push(`const r = ${RUNTIME.StackReader}.fromGetMethod(${stackW}, await provider.get('${getM.name}', [`);
346
- let stackWrites = getM.parameters.flatMap((p, i) => emitStackWriteItems(ctx, paramNames[i], p.ty))
347
- for (let w of stackWrites) {
348
- buf.push(w + ',');
349
- }
350
- buf.push(`]));`);
351
- } else {
352
- buf.push(`const r = ${RUNTIME.StackReader}.fromGetMethod(${stackW}, await provider.get('${getM.name}', []));`);
353
- }
354
- buf.push(`return ${emitStackReadExpr(ctx, 'result', getM.returnTy)};`);
355
- buf.push(`}`);
356
- return buf.toString();
357
- }
358
-
359
- // Render a template of a TypeScript wrapper.
360
- function renderTsTemplate(tplStr: string, content: {
361
- contractClassName: string
362
- flags: Record<string, boolean> // {{if:flag}} ... {{/if:flag}}
363
- slots: Record<string, string> // {{slot}}
364
- }): string {
365
- // {{if:...}} blocks: keep blank lines before it if true
366
- const IF_BLOCK_RE = /(^(?:\s*\n)*)\s*\/\/\s*{{if:(\w+)}}[^\n]*\n([\s\S]+?)^\s*\/\/\s*{{\/if:\2}}[^\n]*/gm;
367
- tplStr = tplStr.replace(IF_BLOCK_RE, (_m, leadingBlank: string, name: string, inner: string) =>
368
- content.flags[name] ? leadingBlank + inner : ''
369
- );
370
-
371
- // {{slot}}: keep indentation, remove a commented marker
372
- const SLOT_RE = /(^ *)(?:\/\/\s*)?{{(\w+)}}[^\n]*$/gm;
373
- tplStr = tplStr.replace(SLOT_RE, (_m, indent: string, name: string) =>
374
- content.slots[name].split('\n').map(l => l.length ? indent + l : '').join('\n')
375
- );
376
-
377
- // {{slot}} inlined (without a preceding comment)
378
- tplStr = tplStr.replace(/{{(\w+)}}/g, (_m, name: string) => content.slots[name]);
379
-
380
- return tplStr
381
- .replace(/\n\s*\n(\s*})/g, '\n$1') // remove an empty line before closing brace
382
- .replace(/\n\s*\n(?:\s*\n)+/g, '\n\n') // merge 2+ blank lines
383
- .replace(/CONTRACT_CLASS_NAME/g, content.contractClassName);
384
- }
385
-
386
- /**
387
- * The main function: accepts top-level declarations from Tolk sources (converted to ABI)
388
- * and returns the full TypeScript wrapper with all serializers, send, and get methods.
389
- * @throws CantGenerateWrappersAtAll
390
- */
391
- export function generateTypeScriptFileForContract(contract: ContractABI): string {
392
- let ctx = new CodegenCtx(contract.declarations);
393
-
394
- const declarationsBuf = new OutBuf();
395
- for (const n of contract.declarations) {
396
- try {
397
- if (n.kind === 'Struct') tolkStructToTypeScript(ctx, n, declarationsBuf);
398
- else if (n.kind === 'Alias') tolkAliasToTypeScript(ctx, n, declarationsBuf);
399
- else if (n.kind === 'Enum') tolkEnumToTypeScript(ctx, n, declarationsBuf);
400
- declarationsBuf.emptyLine();
401
- } catch (ex: any) {
402
- throw new CantGenerateWrappersAtAll(`Error while generating ${n.kind.toLowerCase()} '${n.name}'`, ex);
403
- }
404
- }
405
- let packUnpackSerializers = declarationsBuf.toString();
406
-
407
- let createCellsMethodsStr = contract.incomingMessages.map(msg =>
408
- generateCreateCellOfMessageMethod(ctx, msg.bodyTy)
409
- );
410
-
411
- let sendMethodsStr = contract.incomingMessages.map(msg => {
412
- try {
413
- return generateSendMethod(ctx, msg.bodyTy);
414
- } catch (ex: any) {
415
- throw new CantGenerateWrappersAtAll(`Error while generating send method '${renderTy(msg.bodyTy)}'`, ex);
416
- }
417
- });
418
-
419
- let getMethodsStr = contract.getMethods.map(getM => {
420
- try {
421
- return generateGetMethod(ctx, getM);
422
- } catch (ex: any) {
423
- throw new CantGenerateWrappersAtAll(`Error while generating get method '${getM.name}'`, ex);
424
- }
425
- });
426
-
427
- let intNAliasesStr = ctx.sortOccurred(ctx.intNOccurred).map(intN => `type ${intN} = bigint`);
428
- let uintNAliasesStr = ctx.sortOccurred(ctx.uintNOccurred).map(uintN => `type ${uintN} = bigint`);
429
- let varIntNAliasesStr = ctx.sortOccurred(ctx.varIntNOccurred).map(varIntN => `type ${varIntN} = bigint`);
430
- let sliceAliasesStr = ctx.sortOccurred(ctx.bitsNOccurred).map(bitsN => `type ${bitsN} = c.Slice`);
431
-
432
- let errorCodesStr = contract.thrownErrors.filter(t => t.name).map(t =>
433
- `'${t.name}': ${t.errCode},`
434
- );
435
-
436
- return renderTsTemplate(fs.readFileSync(__dirname + '/out-template.ts', 'utf-8'), {
437
- contractClassName: contract.contractName,
438
- flags: {
439
- 'has_sendDeploy': true,
440
- 'has_bitsN': ctx.bitsNOccurred.size > 0,
441
- 'has_varIntN': ctx.varIntNOccurred.size > 0,
442
- 'has_remaining': ctx.has_RemainingBitsAndRefs,
443
- 'has_customPackUnpack': ctx.has_customPackUnpack,
444
- 'has_customDictV': ctx.has_customDictV,
445
- 'has_implicitUnionPrefix': ctx.has_implicitUnionPrefix,
446
- 'has_non32Prefixes': ctx.has_non32Prefixes,
447
- 'has_addressAny': ctx.has_addressAny,
448
- 'has_arrayOf': ctx.has_arrayOf,
449
- 'has_lispListOf': ctx.has_lispListOf,
450
- 'stackReadsUnknown': ctx.stackReadsUnknown,
451
- 'stackReadsArrayOf': ctx.stackReadsArrayOf,
452
- 'stackReadsLispListOf': ctx.stackReadsLispListOf,
453
- 'stackReadsSnakeString': ctx.stackReadsSnakeString,
454
- 'stackReadsTuple': ctx.stackReadsTuple,
455
- 'stackReadsMapKV': ctx.stackReadsMapKV,
456
- 'stackReadsBuilder': ctx.stackReadsBuilder,
457
- 'stackReadsNullable': ctx.stackReadsNullable,
458
- 'stackReadsWideNullable': ctx.stackReadsWideNullable,
459
- 'stackReadsUnionType': ctx.stackReadsUnionType,
460
- 'stackReadsCellRef': ctx.stackReadsCellRef,
461
- 'stackReadsNullLiteral': ctx.stackReadsNullLiteral,
462
- },
463
- slots: {
464
- 'intNAliases': intNAliasesStr.join('\n'),
465
- 'uintNAliases': uintNAliasesStr.join('\n'),
466
- 'varIntNAliases': varIntNAliasesStr.join('\n'),
467
- 'sliceAliases': sliceAliasesStr.join('\n'),
468
- 'packUnpackSerializers': packUnpackSerializers,
469
- 'codeBoc64': contract.codeBoc64,
470
- 'errorCodes': errorCodesStr.join('\n'),
471
- 'fromStorageMethod': generateFromStorageMethod(ctx, contract.storage.storageAtDeploymentTy ?? contract.storage.storageTy, contract.contractName),
472
- 'createCellsMethods': createCellsMethodsStr.join('\n\n'),
473
- 'sendMethods': sendMethodsStr.join('\n\n'),
474
- 'getMethods': getMethodsStr.join('\n\n'),
475
- }
476
- });
477
- }