@taqueria/plugin-ligo 0.40.0 → 0.40.4

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/common.ts CHANGED
@@ -38,8 +38,29 @@ export const getInputFilenameAbsPath = (parsedArgs: UnionOpts, sourceFile: strin
38
38
  export const getInputFilenameRelPath = (parsedArgs: UnionOpts, sourceFile: string): string =>
39
39
  join(parsedArgs.config.contractsDir ?? 'contracts', sourceFile);
40
40
 
41
- export const emitExternalError = (err: unknown, sourceFile: string): void => {
41
+ export const formatLigoError = (err: Error): Error => {
42
+ let result = err.message.replace(/Command failed.+?\n/, '');
43
+ if (
44
+ result.includes('An internal error ocurred. Please, contact the developers.')
45
+ && result.includes('Module Contract not found with last Contract.')
46
+ ) {
47
+ result =
48
+ `The contract must be imported with "Contract" as the namespace: #import "path/to/contract.ligo" "Contract"`;
49
+ }
50
+
51
+ err.message = result.replace(
52
+ 'An internal error ocurred. Please, contact the developers.',
53
+ 'The LIGO compiler experienced an internal error. Please contact the LIGO developers.',
54
+ );
55
+
56
+ return err;
57
+ };
58
+
59
+ export const emitExternalError = (errs: unknown[] | unknown, sourceFile: string): void => {
42
60
  sendErr(`\n=== Error messages for ${sourceFile} ===`);
43
- err instanceof Error ? sendErr(err.message.replace(/Command failed.+?\n/, '')) : sendErr(err as any);
44
- sendErr(`\n===`);
61
+ const errors = Array.isArray(errs) ? errs : [errs];
62
+ errors.map(err => {
63
+ err instanceof Error ? sendErr(err.message) : sendErr(err as any);
64
+ });
65
+ sendErr(`===`);
45
66
  };
package/compile.ts CHANGED
@@ -8,26 +8,31 @@ import {
8
8
  sendRes,
9
9
  sendWarn,
10
10
  } from '@taqueria/node-sdk';
11
+ import { createReadStream } from 'fs';
11
12
  import { access, readFile, writeFile } from 'fs/promises';
12
13
  import { basename, extname, join } from 'path';
14
+ import * as readline from 'readline';
13
15
  import {
14
16
  CompileOpts as Opts,
15
17
  emitExternalError,
18
+ formatLigoError,
16
19
  getInputFilenameAbsPath,
17
20
  getInputFilenameRelPath,
18
21
  getLigoDockerImage,
19
22
  UnionOpts,
20
23
  } from './common';
21
24
 
22
- export type TableRow = { source: string; artifact: string };
25
+ export type TableRow = { source: string; artifact: string; err?: unknown };
23
26
 
24
27
  export type ExprKind = 'storage' | 'default_storage' | 'parameter';
25
28
 
29
+ export type Syntax = 'mligo' | 'jsligo' | 'religo' | 'ligo';
30
+
26
31
  export type ModuleInfo = {
27
32
  moduleName: string;
28
33
  sourceName: string;
29
34
  sourceFile: string;
30
- syntax: 'mligo' | 'jsligo' | 'religo' | 'ligo';
35
+ syntax: Syntax;
31
36
  type: 'file-main' | 'file-entry' | 'module-main' | 'module-entry';
32
37
  };
33
38
 
@@ -215,259 +220,152 @@ const compileExpr =
215
220
  };
216
221
  })
217
222
  .catch(err => {
218
- emitExternalError(err, sourceFile);
219
223
  return {
220
224
  source: module.sourceName,
221
- artifact: `${sourceFile} not compiled`,
225
+ artifact: `${exprName} in ${sourceFile} not compiled`,
226
+ err,
222
227
  };
223
228
  });
224
229
  };
225
230
 
226
- const getExprNames = (parsedArgs: Opts, sourceFile: string): Promise<string[]> =>
227
- readFile(getInputFilenameAbsPath(parsedArgs, sourceFile), 'utf8')
228
- .then(data => data.match(/(?<=\n\s*(let|const)\s+)[a-zA-Z0-9_]+/g) ?? []);
231
+ const getExprNames = (parsedArgs: Opts, sourceFile: string): Promise<string[]> => {
232
+ return new Promise((resolve, reject) => {
233
+ const inputFilePath = getInputFilenameAbsPath(parsedArgs, sourceFile);
234
+ const readInterface = readline.createInterface({
235
+ input: createReadStream(inputFilePath),
236
+ output: process.stdout,
237
+ });
238
+
239
+ const variableNames: string[] = [];
240
+
241
+ readInterface.on('line', function(line) {
242
+ // Skip lines that start with a comment
243
+ if (!line.trim().startsWith('//')) {
244
+ const matches = line.match(/(?<=\s*(let|const)\s+)[a-zA-Z0-9_]+/g);
245
+ if (matches) {
246
+ variableNames.push(...matches);
247
+ }
248
+ }
249
+ });
229
250
 
230
- const compileExprs = (
251
+ readInterface.on('close', function() {
252
+ resolve(variableNames);
253
+ });
254
+ });
255
+ };
256
+
257
+ const compileExprs = async (
231
258
  parsedArgs: Opts,
232
259
  sourceFile: string,
233
260
  module: ModuleInfo,
234
261
  exprKind: ExprKind,
235
- ): Promise<TableRow[]> =>
236
- getExprNames(parsedArgs, sourceFile)
237
- .then(exprNames => {
238
- if (exprNames.length === 0) return [];
239
- const firstExprName = exprNames.slice(0, 1)[0];
240
- const restExprNames = exprNames.slice(1, exprNames.length);
241
- const firstExprKind = isStorageKind(exprKind) ? 'default_storage' : 'parameter';
242
- const restExprKind = isStorageKind(exprKind) ? 'storage' : 'parameter';
243
- const firstExprResult = compileExpr(parsedArgs, sourceFile, module, firstExprKind)(firstExprName);
244
- const restExprResults = restExprNames.map(compileExpr(parsedArgs, sourceFile, module, restExprKind));
245
- return Promise.all([firstExprResult].concat(restExprResults));
246
- })
247
- .catch(err => {
248
- emitExternalError(err, sourceFile);
249
- return [{
250
- source: module.sourceName,
251
- artifact: `No ${isStorageKind(exprKind) ? 'storage' : 'parameter'} expressions compiled`,
252
- }];
253
- })
254
- .then(results =>
255
- results.length > 0 ? results : [{
256
- source: module.sourceName,
257
- artifact: `No ${isStorageKind(exprKind) ? 'storage' : 'parameter'} expressions found`,
258
- }]
259
- );
262
+ ): Promise<TableRow[]> => {
263
+ // Get expressions from file
264
+ let exprs = [];
265
+ try {
266
+ exprs = await getExprNames(parsedArgs, sourceFile);
267
+ } catch (err) {
268
+ emitExternalError(err, sourceFile);
269
+ return [{
270
+ source: module.sourceName,
271
+ artifact: `No ${isStorageKind(exprKind) ? 'storage' : 'parameter'} expressions compiled`,
272
+ }];
273
+ }
260
274
 
261
- const initContentForStorage = (module: ModuleInfo): string => {
262
- const linkToContract = `#import "${module.sourceFile}" "Contract"\n\n`;
263
- const instruction =
264
- '// Define your initial storage values as a list of LIGO variable definitions, the first of which will be considered the default value to be used for origination later on\n';
265
-
266
- const syntax = (() => {
267
- const pair = [module.syntax, module.type].join('-');
268
- switch (pair) {
269
- case 'mligo-file-main':
270
- return [
271
- '// 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.',
272
- '',
273
- '// If your storage is a simple value, you can define it directly',
274
- '// E.g. let storage = 10',
275
- '',
276
- '// For added type-safety, you can reference the type of your storage from the contract',
277
- '// E.g. let storage : Contract.storage = 10',
278
- ];
279
- break;
280
- case 'mligo-file-entry':
281
- return [
282
- '// 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.',
283
- '',
284
- '// If your storage is a simple value, you can define it directly',
285
- '// E.g. let storage = 10',
286
- '',
287
- '// For added type-safety, you can reference the type of your storage from the contract',
288
- '// E.g. let storage : Contract.storage = 10',
289
- ];
290
- break;
291
- case 'mligo-module-main':
292
- return [
293
- '// 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.',
294
- '',
295
- '// If your storage is a simple value, you can define it directly',
296
- '// E.g. let storage = 10',
297
- '',
298
- '// For added type-safety, you can reference the type of your storage from the contract',
299
- `// E.g. let storage : Contract.${module.moduleName}.storage = 10`,
300
- ];
301
- break;
302
- case 'mligo-module-entry':
303
- return [
304
- '// 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.',
305
- '',
306
- '// If your storage is a simple value, you can define it directly',
307
- '// E.g. let storage = 10',
308
- '',
309
- '// For added type-safety, you can reference the type of your storage from the contract',
310
- `// E.g. let storage : Contract.${module.moduleName}.storage = 10`,
311
- ];
312
- break;
313
- case 'jsligo-file-main':
314
- return [
315
- '// 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.',
316
- `// NOTE: The "storage" type should be exported from the contract file (${module.sourceFile})`,
317
- '',
318
- '// If your storage is a simple value, you can define it directly',
319
- '// E.g. const storage = 10',
320
- '',
321
- '// For added type-safety, you can reference the type of your storage from the contract. This assumes that you have exported your `storage` type from the contract file.',
322
- '// E.g. const storage : Contract.storage = 10',
323
- ];
324
- break;
325
- case 'jsligo-file-entry':
326
- return [
327
- '// 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.',
328
- '',
329
- '// If your storage is a simple value, you can define it directly',
330
- '// E.g. const storage = 10',
331
- '',
332
- '// For added type-safety, you can reference the type of your storage from the contract. This assumes that you have exported your `storage` type from the contract file.',
333
- '// E.g. const storage : Contract.storage = 10',
334
- ];
335
- break;
336
- case 'jsligo-module-main':
337
- return [
338
- '// 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.',
339
- `// NOTE: The "storage" type should be exported from the contract file (${module.sourceFile})`,
340
- '',
341
- '// If your storage is a simple value, you can define it directly',
342
- '// E.g. const storage = 10',
343
- '',
344
- '// For added type-safety, you can reference the type of your storage from the contract. This assumes that you have exported your `storage` type from the contract file.',
345
- `// E.g. const storage : Contract.${module.moduleName}.storage = 10`,
346
- ];
347
- break;
348
- case 'jsligo-module-entry':
349
- return [
350
- '// 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.',
351
- '',
352
- '// If your storage is a simple value, you can define it directly',
353
- '// E.g. const storage = 10',
354
- '',
355
- '// For added type-safety, you can reference the type of your storage from the contract. This assumes that you have exported your `storage` type from the contract file.',
356
- `// E.g. const storage : Contract.${module.moduleName}.storage = 10`,
357
- ];
358
- break;
359
- default:
360
- return [];
361
- }
362
- })();
363
- return linkToContract + instruction + syntax.join('\n');
275
+ const results = await Promise.all(exprs.map(async (exprName, index) => {
276
+ const compileResult = await compileExpr(
277
+ parsedArgs,
278
+ sourceFile,
279
+ module,
280
+ exprKind === 'storage' && index === 0 ? 'default_storage' : exprKind,
281
+ )(exprName);
282
+ return compileResult;
283
+ }));
284
+
285
+ // Collect errors
286
+ const errors = results.reduce(
287
+ (acc, result) => {
288
+ if (result.err) {
289
+ // If its not an Error object, then just add it to the list
290
+ if (!(result.err instanceof Error)) return [...acc, result.err];
291
+
292
+ // Otherwise, get all ligo errors and ensure that the list is unique
293
+ const ligoErrs = (acc
294
+ .filter(err => err instanceof Error) as Error[])
295
+ .map(err => err.message);
296
+
297
+ const formattedError = formatLigoError(result.err);
298
+
299
+ return (ligoErrs.includes(formattedError.message)) ? acc : [...acc, formattedError];
300
+ }
301
+ return acc;
302
+ },
303
+ [] as unknown[],
304
+ );
305
+
306
+ // Collect table rows
307
+ const retval = results.map(({ source, artifact }) => ({ source, artifact }));
308
+
309
+ if (errors.length) emitExternalError(errors, sourceFile);
310
+
311
+ return retval;
364
312
  };
365
313
 
366
- const initContentForParameter = (module: ModuleInfo): string => {
367
- const linkToContract = `#import "${module.sourceFile}" "Contract"\n\n`;
368
- const instruction = '// Define your parameter values as a list of LIGO variable definitions\n';
369
-
370
- const syntax = (() => {
371
- const pair = [module.syntax, module.type].join('-');
372
- switch (pair) {
373
- case 'mligo-file-main':
374
- return [
375
- '// 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.',
376
- '',
377
- '// If your parameter is a simple value, you can define it directly',
378
- '// E.g. let default_parameter = 10',
379
- '',
380
- '// For added type-safety, you can reference the type of your parameter from the contract',
381
- '// E.g. let default_parameter : Contract.parameter = 10',
382
- ];
383
- break;
384
- case 'mligo-file-entry':
385
- return [
386
- '// 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.',
387
- '',
388
- '// If your parameter is a simple value, you can define it directly',
389
- '// E.g. let default_parameter = 10',
390
- '',
391
- '// For added type-safety, you can reference the type of your parameter from the contract',
392
- '// E.g. let default_parameter : parameter_of Contract = 10',
393
- ];
394
- break;
395
- case 'mligo-module-main':
396
- return [
397
- '// 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.',
398
- '',
399
- '// If your parameter is a simple value, you can define it directly',
400
- '// E.g. let default_parameter = 10',
401
- '',
402
- '// For added type-safety, you can reference the type of your parameter from the contract',
403
- `// E.g. let default_parameter : Contract.${module.moduleName}.parameter = 10`,
404
- ];
405
- break;
406
- case 'mligo-module-entry':
407
- return [
408
- '// 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.',
409
- '',
410
- '// If your parameter is a simple value, you can define it directly',
411
- '// E.g. let default_parameter = 10',
412
- '',
413
- '// For added type-safety, you can reference the type of your parameter from the contract',
414
- `// E.g. let default_parameter : parameter_of Contract.${module.moduleName} = 10`,
415
- ];
416
- break;
417
- case 'jsligo-file-main':
418
- return [
419
- '// 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.',
420
- `// NOTE: The "parameter" type should be exported from the contract file (${module.sourceFile})`,
421
- '',
422
- '// If your parameter is a simple value, you can define it directly',
423
- '// E.g. const default_parameter = 10',
424
- '',
425
- '// For added type-safety, you can reference the type of your parameter from the contract',
426
- '// E.g. const default_parameter : Contract.parameter = 10',
427
- ];
428
- break;
429
- case 'jsligo-file-entry':
430
- return [
431
- '// 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.',
432
- '',
433
- '// If your parameter is a simple value, you can define it directly',
434
- '// E.g. const default_parameter = 10',
435
- '',
436
- '// For added type-safety, you can reference the type of your parameter from the contract',
437
- '// E.g. const default_parameter : parameter_of Contract = 10',
438
- ];
439
- break;
440
- case 'jsligo-module-main':
441
- return [
442
- '// 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.',
443
- `// NOTE: The "parameter" type should be exported from the contract file (${module.sourceFile})`,
444
- '',
445
- '// If your parameter is a simple value, you can define it directly',
446
- '// E.g. const default_parameter = 10',
447
- '',
448
- '// For added type-safety, you can reference the type of your parameter from the contract',
449
- `// E.g. const default_parameter : Contract.${module.moduleName}.parameter = 10`,
450
- ];
451
- break;
452
- case 'jsligo-module-entry':
453
- return [
454
- '// 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.',
455
- '',
456
- '// If your parameter is a simple value, you can define it directly',
457
- '// E.g. const default_parameter = 10',
458
- '',
459
- '// For added type-safety, you can reference the type of your parameter from the contract',
460
- `// E.g. const default_parameter : parameter_of Contract.${module.moduleName} = 10`,
461
- ];
462
- break;
463
- default:
464
- return [];
465
- }
466
- })();
314
+ // Helper function to get the initial message based on the pair value
315
+ const getInitialMessage = (pair: string, module: ModuleInfo) => {
316
+ const messages = {
317
+ 'mligo-file-main':
318
+ `// 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.`,
319
+ 'mligo-file-entry':
320
+ `// 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.`,
321
+ 'mligo-module-main':
322
+ `// 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.`,
323
+ 'mligo-module-entry':
324
+ `// 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.`,
325
+ 'jsligo-file-main':
326
+ `// 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`
327
+ + `// NOTE: The "storage" type should be exported from the contract file (${module.sourceFile})`,
328
+ 'jsligo-file-entry':
329
+ `// 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.`,
330
+ 'jsligo-module-main':
331
+ `// 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`
332
+ + `// NOTE: The "storage" type should be exported from the contract file (${module.sourceFile})`,
333
+ 'jsligo-module-entry':
334
+ `// 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.`,
335
+ // ... any other combinations
336
+ } as Record<string, string>;
337
+
338
+ return messages[pair] || '// This file was created by Taqueria.';
339
+ };
467
340
 
468
- return linkToContract + instruction + syntax.join('\n');
341
+ // Helper function to get a common message
342
+ const getCommonMsg = (langType: Syntax, listType: ExprKind) => {
343
+ const varKeyword = langType === 'mligo' ? 'let' : 'const';
344
+ const commonMsgForStorage = `// IMPORTANT: We suggest always explicitly typing your storage values:\n`
345
+ + `// E.g.: \`${varKeyword} storage: int = 10\` or \`${varKeyword} storage: Contract.storage = 10\``;
346
+
347
+ const commonMsgForParameter = `// IMPORTANT: We suggest always explicitly typing your parameter values:\n`
348
+ + `// E.g.: \`${varKeyword} parameter: int = 10\` or \`${varKeyword} parameter: Contract.parameter = 10\``;
349
+
350
+ return listType === 'storage' ? commonMsgForStorage : commonMsgForParameter;
469
351
  };
470
352
 
353
+ // Main function to get the content for storage or parameter
354
+ const getContent = (moduleInfo: ModuleInfo, listType: ExprKind) => {
355
+ const linkToContract = `#import "${moduleInfo.sourceFile}" "Contract"`;
356
+ const pair = `${moduleInfo.syntax}-${moduleInfo.type}`;
357
+ const initialMsg = getInitialMessage(pair, moduleInfo);
358
+ const commonMsg = getCommonMsg(moduleInfo.syntax, listType);
359
+
360
+ return `${linkToContract}\n\n${initialMsg}\n\n${commonMsg}`;
361
+ };
362
+
363
+ // Usage for storage list
364
+ const initContentForStorage = (moduleInfo: ModuleInfo) => getContent(moduleInfo, 'storage');
365
+
366
+ // Usage for parameter list
367
+ const initContentForParameter = (moduleInfo: ModuleInfo) => getContent(moduleInfo, 'parameter');
368
+
471
369
  export const compileContractWithStorageAndParameter = async (
472
370
  parsedArgs: Opts,
473
371
  sourceFile: string,
@@ -475,6 +373,7 @@ export const compileContractWithStorageAndParameter = async (
475
373
  ): Promise<TableRow[]> => {
476
374
  const contractCompileResult = await compileContract(parsedArgs, sourceFile, module);
477
375
  if (contractCompileResult.artifact === COMPILE_ERR_MSG) return [contractCompileResult];
376
+ debugger;
478
377
 
479
378
  const storageListFile = `${module.moduleName}.storageList${extractExt(sourceFile)}`;
480
379
  const storageListFilename = getInputFilenameAbsPath(parsedArgs, storageListFile);