@taqueria/lib-ligo 0.40.10

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.
package/compile.ts ADDED
@@ -0,0 +1,478 @@
1
+ import {
2
+ execCmd,
3
+ getArch,
4
+ getArtifactsDir,
5
+ sendAsyncErr,
6
+ sendErr,
7
+ sendJsonRes,
8
+ sendRes,
9
+ sendWarn,
10
+ } from '@taqueria/node-sdk';
11
+ import { createReadStream } from 'fs';
12
+ import { access, readFile, writeFile } from 'fs/promises';
13
+ import { basename, extname, join } from 'path';
14
+ import * as readline from 'readline';
15
+ import {
16
+ Common,
17
+ CompileOpts as Opts,
18
+ configure,
19
+ emitExternalError,
20
+ formatLigoError,
21
+ getInputFilenameAbsPath,
22
+ getInputFilenameRelPath,
23
+ UnionOpts,
24
+ } from './common';
25
+
26
+ export type TableRow = { source: string; artifact: string; err?: unknown };
27
+
28
+ export type ExprKind = 'storage' | 'default_storage' | 'parameter';
29
+
30
+ export type Syntax = 'mligo' | 'jsligo' | 'religo' | 'ligo';
31
+
32
+ export type ModuleInfo = {
33
+ moduleName: string;
34
+ sourceName: string;
35
+ sourceFile: string;
36
+ syntax: Syntax;
37
+ type: 'file-main' | 'file-entry' | 'module-main' | 'module-entry';
38
+ };
39
+
40
+ const COMPILE_ERR_MSG: string = 'Not compiled';
41
+
42
+ const isStorageKind = (exprKind: ExprKind): boolean => exprKind === 'storage' || exprKind === 'default_storage';
43
+
44
+ export const isSupportedLigoSyntax = (sourceFile: string) => /\.(mligo|jsligo)$/.test(sourceFile);
45
+
46
+ export const isUnsupportedLigoSyntax = (sourceFile: string) => /\.(ligo|religo)$/.test(sourceFile);
47
+
48
+ export const isLIGOFile = (sourceFile: string) =>
49
+ isSupportedLigoSyntax(sourceFile) || isUnsupportedLigoSyntax(sourceFile);
50
+
51
+ export const isStorageListFile = (sourceFile: string): boolean =>
52
+ /.+\.(storageList|storages)\.(ligo|religo|mligo|jsligo)$/.test(sourceFile);
53
+
54
+ export const isParameterListFile = (sourceFile: string): boolean =>
55
+ /.+\.(parameterList|parameters)\.(ligo|religo|mligo|jsligo)$/.test(sourceFile);
56
+
57
+ const extractExt = (path: string): string => {
58
+ const matchResult = path.match(/\.(ligo|religo|mligo|jsligo)$/);
59
+ return matchResult ? matchResult[0] : '';
60
+ };
61
+
62
+ const removeExt = (path: string): string => {
63
+ const extRegex = new RegExp(extractExt(path));
64
+ return path.replace(extRegex, '');
65
+ };
66
+
67
+ const isOutputFormatJSON = (parsedArgs: Opts): boolean => parsedArgs.json;
68
+
69
+ const getOutputContractFilename = (parsedArgs: Opts, module: ModuleInfo): string => {
70
+ const ext = isOutputFormatJSON(parsedArgs) ? '.json' : '.tz';
71
+ return join(getArtifactsDir(parsedArgs), `${module.moduleName}${ext}`);
72
+ };
73
+
74
+ const getOutputExprFilename = (parsedArgs: Opts, module: ModuleInfo, exprKind: ExprKind, exprName: string): string => {
75
+ const contractName = module.moduleName;
76
+ const ext = isOutputFormatJSON(parsedArgs) ? '.json' : '.tz';
77
+ const outputFile = exprKind === 'default_storage'
78
+ ? `${contractName}.default_storage${ext}`
79
+ : `${contractName}.${exprKind}.${exprName}${ext}`;
80
+ return join(getArtifactsDir(parsedArgs), `${outputFile}`);
81
+ };
82
+
83
+ const getExprNames = (parsedArgs: Opts, sourceFile: string): Promise<string[]> => {
84
+ return new Promise((resolve, reject) => {
85
+ const inputFilePath = getInputFilenameAbsPath(parsedArgs, sourceFile);
86
+ const readInterface = readline.createInterface({
87
+ input: createReadStream(inputFilePath),
88
+ output: process.stdout,
89
+ });
90
+
91
+ const variableNames: string[] = [];
92
+
93
+ readInterface.on('line', function(line) {
94
+ // Skip lines that start with a comment
95
+ if (!line.trim().startsWith('//')) {
96
+ const matches = line.match(/(?<=\s*(let|const)\s+)[a-zA-Z0-9_]+/g);
97
+ if (matches) {
98
+ variableNames.push(...matches);
99
+ }
100
+ }
101
+ });
102
+
103
+ readInterface.on('close', function() {
104
+ resolve(variableNames);
105
+ });
106
+ });
107
+ };
108
+
109
+ // Helper function to get the initial message based on the pair value
110
+ const getInitialMessage = (pair: string, module: ModuleInfo) => {
111
+ const messages = {
112
+ 'mligo-file-main':
113
+ `// When this file was created, the smart contract was defined with a main function that was not within a named module. As such, the examples below are written with that assumption in mind.`,
114
+ 'mligo-file-entry':
115
+ `// When this file was created, the smart contract was defined with an entrypoint using \`@entry\` that was not within a named module. As such, the examples below are written with that assumption in mind.`,
116
+ 'mligo-module-main':
117
+ `// When this file was created, the smart contract was defined with a main function that was within a named module. As such, the examples below are written with that assumption in mind.`,
118
+ 'mligo-module-entry':
119
+ `// When this file was created, the smart contract was defined with an entrypoint using \`@entry\` that was within a named module. As such, the examples below are written with that assumption in mind.`,
120
+ 'jsligo-file-main':
121
+ `// When this file was created, the smart contract was defined with a main function that was not within a namespace. As such, the examples below are written with that assumption in mind.\n`
122
+ + `// NOTE: The "storage" type should be exported from the contract file (${module.sourceFile})`,
123
+ 'jsligo-file-entry':
124
+ `// When this file was created, the smart contract was defined with an entrypoint using \`@entry\` that was not within a namespace. As such, the examples below are written with that assumption in mind.`,
125
+ 'jsligo-module-main':
126
+ `// When this file was created, the smart contract was defined with a main function that was within a namespace. As such, the examples below are written with that assumption in mind.\n`
127
+ + `// NOTE: The "storage" type should be exported from the contract file (${module.sourceFile})`,
128
+ 'jsligo-module-entry':
129
+ `// When this file was created, the smart contract was defined with an entrypoint using \`@entry\` that was within a namespace. As such, the examples below are written with that assumption in mind.`,
130
+ // ... any other combinations
131
+ } as Record<string, string>;
132
+
133
+ return messages[pair] || '// This file was created by Taqueria.';
134
+ };
135
+
136
+ // Helper function to get a common message
137
+ const getCommonMsg = (langType: Syntax, listType: ExprKind) => {
138
+ const varKeyword = langType === 'mligo' ? 'let' : 'const';
139
+ const commonMsgForStorage = `// IMPORTANT: We suggest always explicitly typing your storage values:\n`
140
+ + `// E.g.: \`${varKeyword} storage: int = 10\` or \`${varKeyword} storage: Contract.storage = 10\``;
141
+
142
+ const commonMsgForParameter = `// IMPORTANT: We suggest always explicitly typing your parameter values:\n`
143
+ + `// E.g.: \`${varKeyword} parameter: int = 10\` or \`${varKeyword} parameter: Contract.parameter = 10\``;
144
+
145
+ return listType === 'storage' ? commonMsgForStorage : commonMsgForParameter;
146
+ };
147
+
148
+ // Main function to get the content for storage or parameter
149
+ const getContent = (moduleInfo: ModuleInfo, listType: ExprKind) => {
150
+ const linkToContract = `#import "${moduleInfo.sourceFile}" "Contract"`;
151
+ const pair = `${moduleInfo.syntax}-${moduleInfo.type}`;
152
+ const initialMsg = getInitialMessage(pair, moduleInfo);
153
+ const commonMsg = getCommonMsg(moduleInfo.syntax, listType);
154
+
155
+ return `${linkToContract}\n\n${initialMsg}\n\n${commonMsg}`;
156
+ };
157
+
158
+ // Usage for storage list
159
+ const initContentForStorage = (moduleInfo: ModuleInfo) => getContent(moduleInfo, 'storage');
160
+
161
+ // Usage for parameter list
162
+ const initContentForParameter = (moduleInfo: ModuleInfo) => getContent(moduleInfo, 'parameter');
163
+
164
+ // Inject commonObj to return some functions
165
+ export const inject = (commonObj: Common) => {
166
+ const { getLigoDockerImage } = commonObj;
167
+
168
+ const getListDeclarationsCmd = async (parsedArgs: UnionOpts, sourceFile: string): Promise<string> => {
169
+ const projectDir = process.env.PROJECT_DIR ?? parsedArgs.projectDir;
170
+ if (!projectDir) throw new Error(`No project directory provided`);
171
+ const baseCmd =
172
+ `DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -v \"${projectDir}\":/project -w /project -u $(id -u):$(id -g) ${getLigoDockerImage()} info list-declarations`;
173
+ const inputFile = getInputFilenameRelPath(parsedArgs, sourceFile);
174
+ const flags = '--display-format json';
175
+ const cmd = `${baseCmd} ${inputFile} ${flags}`;
176
+ return cmd;
177
+ };
178
+
179
+ const listContractModules = async (parsedArgs: UnionOpts, sourceFile: string): Promise<ModuleInfo[]> => {
180
+ try {
181
+ await getArch();
182
+ const cmd = await getListDeclarationsCmd(parsedArgs, sourceFile);
183
+ const { stderr, stdout } = await execCmd(cmd);
184
+ if (stderr.length > 0) return Promise.reject(stderr);
185
+
186
+ return JSON.parse(stdout).declarations.reduce(
187
+ (acc: ModuleInfo[], decl: string) => {
188
+ // We need to process delcarations (decl) like so:
189
+ // 1. If the decl is equal to the string "main", then the module type is "file-main" and the name of the module is the sourceFile.
190
+ // 2. If the decl is equal to $main, then the module type is "file-entry" and the name fo the module is the sourceFile.
191
+ // 3. If the decl ends with .main, then the module type is "module-main" and the name of the module is the decl without the .main suffix.
192
+ // 4. If the decl ends with .$main, then the module type is "module-entry" and the name of the module is the decl without the .$main suffix.
193
+ // Otherwise, this is not a declaration we care about.
194
+ const srcFile = removeExt(basename(sourceFile));
195
+ const syntax = extractExt(sourceFile).replace('.', '');
196
+
197
+ if (decl === 'main') {
198
+ return [...acc, { moduleName: srcFile, sourceName: sourceFile, sourceFile, type: 'file-main', syntax }];
199
+ } else if (decl === '$main') {
200
+ return [...acc, { moduleName: srcFile, sourceName: sourceFile, sourceFile, type: 'file-entry', syntax }];
201
+ } else if (decl.endsWith('.main')) {
202
+ const moduleName = decl.replace(/\.main$/, '');
203
+ return [...acc, {
204
+ moduleName,
205
+ sourceName: `${sourceFile}/${moduleName}`,
206
+ sourceFile,
207
+ type: 'module-main',
208
+ syntax,
209
+ }];
210
+ } else if (decl.endsWith('.$main')) {
211
+ const moduleName = decl.replace(/\.\$main$/, '');
212
+ return [...acc, {
213
+ moduleName,
214
+ sourceName: `${sourceFile}/${moduleName}`,
215
+ sourceFile,
216
+ type: 'module-entry',
217
+ syntax,
218
+ }];
219
+ }
220
+ return acc;
221
+ },
222
+ [],
223
+ );
224
+ } catch (err) {
225
+ emitExternalError(err, sourceFile);
226
+ return [];
227
+ }
228
+ };
229
+
230
+ const getCompileContractCmd = async (parsedArgs: Opts, sourceFile: string, module: ModuleInfo): Promise<string> => {
231
+ const projectDir = process.env.PROJECT_DIR ?? parsedArgs.projectDir;
232
+ if (!projectDir) throw new Error(`No project directory provided`);
233
+ const baseCmd =
234
+ `DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -v \"${projectDir}\":/project -w /project -u $(id -u):$(id -g) ${getLigoDockerImage()} compile contract`;
235
+ const inputFile = getInputFilenameRelPath(parsedArgs, sourceFile);
236
+ const outputFile = `-o ${getOutputContractFilename(parsedArgs, module)}`;
237
+ const flags = isOutputFormatJSON(parsedArgs) ? ' --michelson-format json ' : '';
238
+ const moduleFlag = module.type.startsWith('file-') ? '' : `-m ${module.moduleName}`;
239
+ const cmd = `${baseCmd} ${inputFile} ${outputFile} ${flags}${moduleFlag}`;
240
+ return cmd;
241
+ };
242
+
243
+ const compileContract = async (parsedArgs: Opts, sourceFile: string, module: ModuleInfo): Promise<TableRow> => {
244
+ try {
245
+ await getArch();
246
+ const cmd = await getCompileContractCmd(parsedArgs, sourceFile, module);
247
+ const { stderr } = await execCmd(cmd);
248
+ if (stderr.length > 0) sendWarn(stderr);
249
+
250
+ return {
251
+ source: module.sourceName,
252
+ artifact: getOutputContractFilename(parsedArgs, module),
253
+ };
254
+ } catch (err) {
255
+ emitExternalError(err, sourceFile);
256
+ return {
257
+ source: module.sourceName,
258
+ artifact: COMPILE_ERR_MSG,
259
+ };
260
+ }
261
+ };
262
+
263
+ const getCompileExprCmd = (
264
+ parsedArgs: Opts,
265
+ sourceFile: string,
266
+ module: ModuleInfo,
267
+ exprKind: ExprKind,
268
+ exprName: string,
269
+ ): string => {
270
+ const projectDir = process.env.PROJECT_DIR ?? parsedArgs.projectDir;
271
+ if (!projectDir) throw new Error(`No project directory provided`);
272
+ const compilerType = isStorageKind(exprKind) ? 'storage' : 'parameter';
273
+ const baseCmd =
274
+ `DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -v \"${projectDir}\":/project -w /project -u $(id -u):$(id -g) ${getLigoDockerImage()} compile ${compilerType}`;
275
+ const inputFile = getInputFilenameRelPath(parsedArgs, sourceFile);
276
+ const outputFile = `-o ${getOutputExprFilename(parsedArgs, module, exprKind, exprName)}`;
277
+ const flags = isOutputFormatJSON(parsedArgs) ? ' --michelson-format json ' : '';
278
+
279
+ // Parameter and Storage list files are expected to import the smart contract file as the "Contract" module.
280
+ const moduleFlag = (() => {
281
+ switch (module.type) {
282
+ case 'file-main':
283
+ case 'file-entry':
284
+ return '-m Contract';
285
+ default:
286
+ return `-m Contract.${module.moduleName}`;
287
+ }
288
+ })();
289
+
290
+ const cmd = `${baseCmd} ${inputFile} ${exprName} ${outputFile} ${flags} ${moduleFlag}`;
291
+ return cmd;
292
+ };
293
+
294
+ const compileExpr =
295
+ (parsedArgs: Opts, sourceFile: string, module: ModuleInfo, exprKind: ExprKind) =>
296
+ (exprName: string): Promise<TableRow> => {
297
+ return getArch()
298
+ .then(() => getCompileExprCmd(parsedArgs, sourceFile, module, exprKind, exprName))
299
+ .then(execCmd)
300
+ .then(({ stderr }) => {
301
+ if (stderr.length > 0) sendWarn(stderr);
302
+ const artifactName = getOutputExprFilename(parsedArgs, module, exprKind, exprName);
303
+ return {
304
+ source: module.sourceName,
305
+ artifact: artifactName,
306
+ };
307
+ })
308
+ .catch(err => {
309
+ return {
310
+ source: module.sourceName,
311
+ artifact: `${exprName} in ${sourceFile} not compiled`,
312
+ err,
313
+ };
314
+ });
315
+ };
316
+
317
+ const compileExprs = async (
318
+ parsedArgs: Opts,
319
+ sourceFile: string,
320
+ module: ModuleInfo,
321
+ exprKind: ExprKind,
322
+ ): Promise<TableRow[]> => {
323
+ // Get expressions from file
324
+ let exprs = [];
325
+ try {
326
+ exprs = await getExprNames(parsedArgs, sourceFile);
327
+ } catch (err) {
328
+ emitExternalError(err, sourceFile);
329
+ return [{
330
+ source: module.sourceName,
331
+ artifact: `No ${isStorageKind(exprKind) ? 'storage' : 'parameter'} expressions compiled`,
332
+ }];
333
+ }
334
+
335
+ const results = await Promise.all(exprs.map(async (exprName, index) => {
336
+ const compileResult = await compileExpr(
337
+ parsedArgs,
338
+ sourceFile,
339
+ module,
340
+ exprKind === 'storage' && index === 0 ? 'default_storage' : exprKind,
341
+ )(exprName);
342
+ return compileResult;
343
+ }));
344
+
345
+ // Collect errors
346
+ const errors = results.reduce(
347
+ (acc, result) => {
348
+ if (result.err) {
349
+ // If its not an Error object, then just add it to the list
350
+ if (!(result.err instanceof Error)) return [...acc, result.err];
351
+
352
+ // Otherwise, get all ligo errors and ensure that the list is unique
353
+ const ligoErrs = (acc
354
+ .filter(err => err instanceof Error) as Error[])
355
+ .map(err => err.message);
356
+
357
+ const formattedError = formatLigoError(result.err);
358
+
359
+ return (ligoErrs.includes(formattedError.message)) ? acc : [...acc, formattedError];
360
+ }
361
+ return acc;
362
+ },
363
+ [] as unknown[],
364
+ );
365
+
366
+ // Collect table rows
367
+ const retval = results.map(({ source, artifact }) => ({ source, artifact }));
368
+
369
+ if (errors.length) emitExternalError(errors, sourceFile);
370
+
371
+ return retval;
372
+ };
373
+
374
+ const compileContractWithStorageAndParameter = async (
375
+ parsedArgs: Opts,
376
+ sourceFile: string,
377
+ module: ModuleInfo,
378
+ ): Promise<TableRow[]> => {
379
+ const contractCompileResult = await compileContract(parsedArgs, sourceFile, module);
380
+ if (contractCompileResult.artifact === COMPILE_ERR_MSG) return [contractCompileResult];
381
+ debugger;
382
+
383
+ const storageListFile = `${module.moduleName}.storageList${extractExt(sourceFile)}`;
384
+ const storageListFilename = getInputFilenameAbsPath(parsedArgs, storageListFile);
385
+ const storageCompileResult = await access(storageListFilename)
386
+ .then(() => compileExprs(parsedArgs, storageListFile, module, 'storage'))
387
+ .catch(() => {
388
+ sendWarn(
389
+ `Note: storage file associated with "${module.moduleName}" can't be found, so "${storageListFile}" has been created for you. Use this file to define all initial storage values for this contract\n`,
390
+ );
391
+ return writeFile(storageListFilename, initContentForStorage(module), 'utf8');
392
+ });
393
+
394
+ const parameterListFile = `${module.moduleName}.parameterList${extractExt(sourceFile)}`;
395
+ const parameterListFilename = getInputFilenameAbsPath(parsedArgs, parameterListFile);
396
+ const parameterCompileResult = await access(parameterListFilename)
397
+ .then(() => compileExprs(parsedArgs, parameterListFile, module, 'parameter'))
398
+ .catch(() => {
399
+ sendWarn(
400
+ `Note: parameter file associated with "${module.moduleName}" can't be found, so "${parameterListFile}" has been created for you. Use this file to define all parameter values for this contract\n`,
401
+ );
402
+ return writeFile(parameterListFilename, initContentForParameter(module), 'utf8');
403
+ });
404
+
405
+ const storageArtifacts = storageCompileResult ? storageCompileResult.map(res => res.artifact).join('\n') : '';
406
+ const parameterArtifacts = parameterCompileResult ? parameterCompileResult.map(res => res.artifact).join('\n') : '';
407
+
408
+ const combinedArtifact = [
409
+ contractCompileResult.artifact,
410
+ storageArtifacts,
411
+ parameterArtifacts,
412
+ ].filter(Boolean).join('\n');
413
+
414
+ const combinedRow: TableRow = {
415
+ source: module.sourceName,
416
+ artifact: combinedArtifact,
417
+ };
418
+
419
+ return [combinedRow];
420
+ };
421
+
422
+ return {
423
+ getLigoDockerImage,
424
+ getListDeclarationsCmd,
425
+ listContractModules,
426
+ getCompileContractCmd,
427
+ compileContract,
428
+ getCompileExprCmd,
429
+ compileExpr,
430
+ compileExprs,
431
+ compileContractWithStorageAndParameter,
432
+ };
433
+ };
434
+
435
+ export const compile = async (commonObj: Common, parsedArgs: Opts): Promise<void> => {
436
+ const { listContractModules, compileContractWithStorageAndParameter } = inject(commonObj);
437
+
438
+ const sourceFile = parsedArgs.sourceFile;
439
+ if (!isLIGOFile(sourceFile)) {
440
+ sendErr(`${sourceFile} is not a LIGO file`);
441
+ return;
442
+ }
443
+ if (isStorageListFile(sourceFile) || isParameterListFile(sourceFile)) {
444
+ sendErr(`Storage and parameter list files are not meant to be compiled directly`);
445
+ return;
446
+ }
447
+ if (isUnsupportedLigoSyntax(sourceFile)) {
448
+ sendErr(`Unsupported LIGO syntax detected in ${sourceFile}. Note, we only support .jsligo and .mligo files.`);
449
+ return;
450
+ }
451
+
452
+ try {
453
+ const modules = await listContractModules(parsedArgs, sourceFile);
454
+ if (modules.length === 0) {
455
+ return sendJsonRes([
456
+ {
457
+ source: sourceFile,
458
+ artifact: `No contract modules found in "${sourceFile}"`,
459
+ },
460
+ ]);
461
+ }
462
+
463
+ let allCompileResults: TableRow[] = [];
464
+ for (const module of modules) {
465
+ // If we're only to compile a particular module, then we'll skip any that don't match
466
+ if (parsedArgs.module && parsedArgs.module !== module.moduleName) continue;
467
+
468
+ const compileResults = await compileContractWithStorageAndParameter(parsedArgs, sourceFile, module);
469
+ allCompileResults = allCompileResults.concat(compileResults);
470
+ }
471
+
472
+ sendJsonRes(allCompileResults, { footer: `\nCompiled ${allCompileResults.length} contract(s) in "${sourceFile}"` });
473
+ } catch (err) {
474
+ sendErr(`Error processing "${sourceFile}": ${err}`);
475
+ }
476
+ };
477
+
478
+ export default compile;
@@ -0,0 +1,38 @@
1
+ import { sendAsyncErr } from '@taqueria/node-sdk';
2
+ import { RequestArgs } from '@taqueria/node-sdk';
3
+ import { writeFile } from 'fs/promises';
4
+ import { jsligo_template, mligo_template } from './ligo_templates';
5
+
6
+ interface Opts extends RequestArgs.t {
7
+ sourceFileName?: string;
8
+ syntax?: string;
9
+ }
10
+
11
+ const getLigoTemplate = async (contractName: string, syntax: string | undefined): Promise<string> => {
12
+ const matchResult = contractName.match(/\.[^.]+$/);
13
+ const ext = matchResult ? matchResult[0].substring(1) : null;
14
+
15
+ if (syntax === 'mligo') return mligo_template;
16
+ if (syntax === 'jsligo') return jsligo_template;
17
+
18
+ if (syntax === undefined) {
19
+ if (ext === 'mligo') return mligo_template;
20
+ if (ext === 'jsligo') return jsligo_template;
21
+ return sendAsyncErr(
22
+ `Unable to infer LIGO syntax from "${contractName}". Please specify a LIGO syntax via the --syntax option`,
23
+ );
24
+ } else {
25
+ return sendAsyncErr(`"${syntax}" is not a valid syntax. Please specify a valid LIGO syntax`);
26
+ }
27
+ };
28
+
29
+ const createContract = (args: RequestArgs.t) => {
30
+ const unsafeOpts = args as unknown as Opts;
31
+ const contractName = unsafeOpts.sourceFileName as string;
32
+ const syntax = unsafeOpts.syntax;
33
+ const contractsDir = `${args.config.projectDir}/${args.config.contractsDir}`;
34
+ return getLigoTemplate(contractName, syntax)
35
+ .then(ligo_template => writeFile(`${contractsDir}/${contractName}`, ligo_template));
36
+ };
37
+
38
+ export default createContract;
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }