@alephium/web3 0.3.0-rc.7 → 0.3.0-rc.8
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/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/api-alephium.d.ts +1 -1
- package/dist/src/api/api-alephium.js +1 -1
- package/dist/src/contract/contract.d.ts +7 -3
- package/dist/src/contract/contract.js +84 -22
- package/package.json +2 -2
- package/src/api/api-alephium.ts +1 -1
- package/src/contract/contract.ts +108 -22
- package/std/token_interface.ral +9 -0
|
@@ -908,7 +908,7 @@ export declare class HttpClient<SecurityDataType = unknown> {
|
|
|
908
908
|
}
|
|
909
909
|
/**
|
|
910
910
|
* @title Alephium API
|
|
911
|
-
* @version 1.6.
|
|
911
|
+
* @version 1.6.4
|
|
912
912
|
* @baseUrl ../
|
|
913
913
|
*/
|
|
914
914
|
export declare class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
|
|
@@ -27,9 +27,10 @@ declare class SourceInfo {
|
|
|
27
27
|
contractRelativePath: string;
|
|
28
28
|
sourceCode: string;
|
|
29
29
|
sourceCodeHash: string;
|
|
30
|
+
isExternal: boolean;
|
|
30
31
|
getArtifactPath(artifactsRootDir: string): string;
|
|
31
|
-
constructor(type: SourceKind, name: string, sourceCode: string, sourceCodeHash: string, contractRelativePath: string);
|
|
32
|
-
static from(type: SourceKind, name: string, sourceCode: string, contractRelativePath: string): Promise<SourceInfo>;
|
|
32
|
+
constructor(type: SourceKind, name: string, sourceCode: string, sourceCodeHash: string, contractRelativePath: string, isExternal: boolean);
|
|
33
|
+
static from(type: SourceKind, name: string, sourceCode: string, contractRelativePath: string, isExternal: boolean): Promise<SourceInfo>;
|
|
33
34
|
}
|
|
34
35
|
declare class Compiled<T extends Artifact> {
|
|
35
36
|
sourceInfo: SourceInfo;
|
|
@@ -62,6 +63,7 @@ export declare class Project {
|
|
|
62
63
|
readonly contractsRootDir: string;
|
|
63
64
|
readonly artifactsRootDir: string;
|
|
64
65
|
static currentProject: Project;
|
|
66
|
+
static readonly importRegex: RegExp;
|
|
65
67
|
static readonly abstractContractMatcher: TypedMatcher<SourceKind>;
|
|
66
68
|
static readonly contractMatcher: TypedMatcher<SourceKind.Contract>;
|
|
67
69
|
static readonly interfaceMatcher: TypedMatcher<SourceKind.Interface>;
|
|
@@ -76,11 +78,13 @@ export declare class Project {
|
|
|
76
78
|
contractByCodeHash(codeHash: string): Contract;
|
|
77
79
|
private static compile;
|
|
78
80
|
private static loadArtifacts;
|
|
81
|
+
private static getImportSourcePath;
|
|
82
|
+
private static handleImports;
|
|
79
83
|
private static loadSourceFile;
|
|
80
84
|
private static loadSourceFiles;
|
|
81
85
|
static readonly DEFAULT_CONTRACTS_DIR = "contracts";
|
|
82
86
|
static readonly DEFAULT_ARTIFACTS_DIR = "artifacts";
|
|
83
|
-
static build(compilerOptionsPartial?: Partial<CompilerOptions>, contractsRootDir?: string, artifactsRootDir?: string): Promise<void>;
|
|
87
|
+
static build(compilerOptionsPartial?: Partial<CompilerOptions>, projectRootDir?: string, contractsRootDir?: string, artifactsRootDir?: string): Promise<void>;
|
|
84
88
|
}
|
|
85
89
|
export declare abstract class Artifact {
|
|
86
90
|
readonly version: string;
|
|
@@ -76,20 +76,39 @@ class TypedMatcher {
|
|
|
76
76
|
this.type = type;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
+
function removeParentsPrefix(parts) {
|
|
80
|
+
let index = 0;
|
|
81
|
+
for (let i = 0; i < parts.length; i++) {
|
|
82
|
+
if (parts[`${i}`] === '..') {
|
|
83
|
+
index += 1;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return path.join(...parts.slice(index));
|
|
90
|
+
}
|
|
79
91
|
class SourceInfo {
|
|
80
|
-
constructor(type, name, sourceCode, sourceCodeHash, contractRelativePath) {
|
|
92
|
+
constructor(type, name, sourceCode, sourceCodeHash, contractRelativePath, isExternal) {
|
|
81
93
|
this.type = type;
|
|
82
94
|
this.name = name;
|
|
83
95
|
this.sourceCode = sourceCode;
|
|
84
96
|
this.sourceCodeHash = sourceCodeHash;
|
|
85
97
|
this.contractRelativePath = contractRelativePath;
|
|
98
|
+
this.isExternal = isExternal;
|
|
86
99
|
}
|
|
87
100
|
getArtifactPath(artifactsRootDir) {
|
|
101
|
+
if (this.isExternal) {
|
|
102
|
+
const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep));
|
|
103
|
+
const externalPath = path.join('.external', relativePath);
|
|
104
|
+
return path.join(artifactsRootDir, externalPath) + '.json';
|
|
105
|
+
}
|
|
88
106
|
return path.join(artifactsRootDir, this.contractRelativePath) + '.json';
|
|
89
107
|
}
|
|
90
|
-
static async from(type, name, sourceCode, contractRelativePath) {
|
|
108
|
+
static async from(type, name, sourceCode, contractRelativePath, isExternal) {
|
|
91
109
|
const sourceCodeHash = await crypto_1.webcrypto.subtle.digest('SHA-256', buffer_1.Buffer.from(sourceCode));
|
|
92
|
-
|
|
110
|
+
const sourceCodeHashHex = buffer_1.Buffer.from(sourceCodeHash).toString('hex');
|
|
111
|
+
return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal);
|
|
93
112
|
}
|
|
94
113
|
}
|
|
95
114
|
class Compiled {
|
|
@@ -301,49 +320,91 @@ class Project {
|
|
|
301
320
|
return Project.compile(provider, sourceInfos, contractsRootDir, artifactsRootDir, errorOnWarnings, compilerOptions);
|
|
302
321
|
}
|
|
303
322
|
}
|
|
304
|
-
static
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
323
|
+
static getImportSourcePath(projectRootDir, importPath) {
|
|
324
|
+
const parts = importPath.split(path.sep);
|
|
325
|
+
if (parts.length > 1 && parts[0] === 'std') {
|
|
326
|
+
const currentDir = path.dirname(__filename);
|
|
327
|
+
return path.join(...[currentDir, '..', '..', '..', importPath]);
|
|
309
328
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
329
|
+
let moduleDir = projectRootDir;
|
|
330
|
+
while (true) {
|
|
331
|
+
const expectedPath = path.join(...[moduleDir, 'node_modules', importPath]);
|
|
332
|
+
if (fs_1.default.existsSync(expectedPath)) {
|
|
333
|
+
return expectedPath;
|
|
334
|
+
}
|
|
335
|
+
const oldModuleDir = moduleDir;
|
|
336
|
+
moduleDir = path.join(moduleDir, '..');
|
|
337
|
+
if (oldModuleDir === moduleDir) {
|
|
338
|
+
throw new Error(`Specified import file does not exist: ${importPath}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
static async handleImports(projectRootDir, contractRootDir, sourceStr, importsCache) {
|
|
343
|
+
const localImportsCache = [];
|
|
344
|
+
const result = sourceStr.replace(Project.importRegex, (match) => {
|
|
345
|
+
localImportsCache.push(match);
|
|
346
|
+
return '';
|
|
347
|
+
});
|
|
348
|
+
const externalSourceInfos = [];
|
|
349
|
+
for (const myImport of localImportsCache) {
|
|
350
|
+
const originImportPath = myImport.slice(8, -1);
|
|
351
|
+
const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral';
|
|
352
|
+
if (!importsCache.includes(importPath)) {
|
|
353
|
+
importsCache.push(importPath);
|
|
354
|
+
const sourcePath = Project.getImportSourcePath(projectRootDir, importPath);
|
|
355
|
+
const sourceInfos = await Project.loadSourceFile(projectRootDir, contractRootDir, sourcePath, importsCache, true);
|
|
356
|
+
externalSourceInfos.push(...sourceInfos);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return [result, externalSourceInfos];
|
|
360
|
+
}
|
|
361
|
+
static async loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, isExternal) {
|
|
362
|
+
const contractRelativePath = path.relative(contractsRootDir, sourcePath);
|
|
363
|
+
if (!sourcePath.endsWith('.ral')) {
|
|
364
|
+
throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`);
|
|
365
|
+
}
|
|
366
|
+
const sourceBuffer = await fs_2.promises.readFile(sourcePath);
|
|
367
|
+
const [sourceStr, externalSourceInfos] = await Project.handleImports(projectRootDir, contractsRootDir, sourceBuffer.toString(), importsCache);
|
|
368
|
+
if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
|
|
369
|
+
throw new Error(`Invalid import statements, source: ${sourcePath}`);
|
|
370
|
+
}
|
|
371
|
+
const sourceInfos = externalSourceInfos;
|
|
313
372
|
for (const matcher of this.matchers) {
|
|
314
373
|
const results = sourceStr.matchAll(matcher.matcher);
|
|
315
374
|
for (const result of results) {
|
|
316
|
-
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath);
|
|
375
|
+
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal);
|
|
317
376
|
sourceInfos.push(sourceInfo);
|
|
318
377
|
}
|
|
319
378
|
}
|
|
320
379
|
return sourceInfos;
|
|
321
380
|
}
|
|
322
|
-
static async loadSourceFiles(contractsRootDir) {
|
|
323
|
-
const
|
|
381
|
+
static async loadSourceFiles(projectRootDir, contractsRootDir) {
|
|
382
|
+
const importsCache = [];
|
|
383
|
+
const sourceInfos = [];
|
|
384
|
+
const loadDir = async function (dirPath) {
|
|
324
385
|
const dirents = await fs_2.promises.readdir(dirPath, { withFileTypes: true });
|
|
325
386
|
for (const dirent of dirents) {
|
|
326
387
|
if (dirent.isFile()) {
|
|
327
|
-
const
|
|
328
|
-
|
|
388
|
+
const sourcePath = path.join(dirPath, dirent.name);
|
|
389
|
+
const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false);
|
|
390
|
+
sourceInfos.push(...infos);
|
|
329
391
|
}
|
|
330
392
|
else {
|
|
331
393
|
const newPath = path.join(dirPath, dirent.name);
|
|
332
|
-
await loadDir(newPath
|
|
394
|
+
await loadDir(newPath);
|
|
333
395
|
}
|
|
334
396
|
}
|
|
335
397
|
};
|
|
336
|
-
|
|
337
|
-
await loadDir(contractsRootDir, sourceInfos);
|
|
398
|
+
await loadDir(contractsRootDir);
|
|
338
399
|
const contractAndScriptSize = sourceInfos.filter((f) => f.type === SourceKind.Contract || f.type === SourceKind.Script).length;
|
|
339
400
|
if (sourceInfos.length === 0 || contractAndScriptSize === 0) {
|
|
340
401
|
throw new Error('Project have no source files');
|
|
341
402
|
}
|
|
342
403
|
return sourceInfos.sort((a, b) => a.type - b.type);
|
|
343
404
|
}
|
|
344
|
-
static async build(compilerOptionsPartial = {}, contractsRootDir = Project.DEFAULT_CONTRACTS_DIR, artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR) {
|
|
405
|
+
static async build(compilerOptionsPartial = {}, projectRootDir = '.', contractsRootDir = Project.DEFAULT_CONTRACTS_DIR, artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR) {
|
|
345
406
|
const provider = (0, global_1.getCurrentNodeProvider)();
|
|
346
|
-
const sourceFiles = await Project.loadSourceFiles(contractsRootDir);
|
|
407
|
+
const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir);
|
|
347
408
|
const { errorOnWarnings, ...nodeCompilerOptions } = { ...exports.DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial };
|
|
348
409
|
const projectArtifact = await ProjectArtifact.from(artifactsRootDir);
|
|
349
410
|
if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
|
|
@@ -357,9 +418,10 @@ class Project {
|
|
|
357
418
|
}
|
|
358
419
|
}
|
|
359
420
|
exports.Project = Project;
|
|
421
|
+
Project.importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg');
|
|
360
422
|
Project.abstractContractMatcher = new TypedMatcher('^Abstract Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.AbstractContract);
|
|
361
423
|
Project.contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract);
|
|
362
|
-
Project.interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)
|
|
424
|
+
Project.interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface);
|
|
363
425
|
Project.scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script);
|
|
364
426
|
Project.matchers = [
|
|
365
427
|
Project.abstractContractMatcher,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alephium/web3",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.8",
|
|
4
4
|
"description": "A JS/TS library to interact with the Alephium platform",
|
|
5
5
|
"license": "GPL",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"author": "Alephium dev <dev@alephium.org>",
|
|
29
29
|
"config": {
|
|
30
|
-
"alephium_version": "1.6.
|
|
30
|
+
"alephium_version": "1.6.4",
|
|
31
31
|
"explorer_backend_version": "1.12.0-rc2"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
package/src/api/api-alephium.ts
CHANGED
|
@@ -1332,7 +1332,7 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
1332
1332
|
|
|
1333
1333
|
/**
|
|
1334
1334
|
* @title Alephium API
|
|
1335
|
-
* @version 1.6.
|
|
1335
|
+
* @version 1.6.4
|
|
1336
1336
|
* @baseUrl ../
|
|
1337
1337
|
*/
|
|
1338
1338
|
export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
|
package/src/contract/contract.ts
CHANGED
|
@@ -86,14 +86,32 @@ class TypedMatcher<T extends SourceKind> {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function removeParentsPrefix(parts: string[]): string {
|
|
90
|
+
let index = 0
|
|
91
|
+
for (let i = 0; i < parts.length; i++) {
|
|
92
|
+
if (parts[`${i}`] === '..') {
|
|
93
|
+
index += 1
|
|
94
|
+
} else {
|
|
95
|
+
break
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return path.join(...parts.slice(index))
|
|
99
|
+
}
|
|
100
|
+
|
|
89
101
|
class SourceInfo {
|
|
90
102
|
type: SourceKind
|
|
91
103
|
name: string
|
|
92
104
|
contractRelativePath: string
|
|
93
105
|
sourceCode: string
|
|
94
106
|
sourceCodeHash: string
|
|
107
|
+
isExternal: boolean
|
|
95
108
|
|
|
96
109
|
getArtifactPath(artifactsRootDir: string): string {
|
|
110
|
+
if (this.isExternal) {
|
|
111
|
+
const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep))
|
|
112
|
+
const externalPath = path.join('.external', relativePath)
|
|
113
|
+
return path.join(artifactsRootDir, externalPath) + '.json'
|
|
114
|
+
}
|
|
97
115
|
return path.join(artifactsRootDir, this.contractRelativePath) + '.json'
|
|
98
116
|
}
|
|
99
117
|
|
|
@@ -102,23 +120,27 @@ class SourceInfo {
|
|
|
102
120
|
name: string,
|
|
103
121
|
sourceCode: string,
|
|
104
122
|
sourceCodeHash: string,
|
|
105
|
-
contractRelativePath: string
|
|
123
|
+
contractRelativePath: string,
|
|
124
|
+
isExternal: boolean
|
|
106
125
|
) {
|
|
107
126
|
this.type = type
|
|
108
127
|
this.name = name
|
|
109
128
|
this.sourceCode = sourceCode
|
|
110
129
|
this.sourceCodeHash = sourceCodeHash
|
|
111
130
|
this.contractRelativePath = contractRelativePath
|
|
131
|
+
this.isExternal = isExternal
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
static async from(
|
|
115
135
|
type: SourceKind,
|
|
116
136
|
name: string,
|
|
117
137
|
sourceCode: string,
|
|
118
|
-
contractRelativePath: string
|
|
138
|
+
contractRelativePath: string,
|
|
139
|
+
isExternal: boolean
|
|
119
140
|
): Promise<SourceInfo> {
|
|
120
141
|
const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
|
|
121
|
-
|
|
142
|
+
const sourceCodeHashHex = Buffer.from(sourceCodeHash).toString('hex')
|
|
143
|
+
return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal)
|
|
122
144
|
}
|
|
123
145
|
}
|
|
124
146
|
|
|
@@ -223,12 +245,13 @@ export class Project {
|
|
|
223
245
|
|
|
224
246
|
static currentProject: Project
|
|
225
247
|
|
|
248
|
+
static readonly importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg')
|
|
226
249
|
static readonly abstractContractMatcher = new TypedMatcher<SourceKind>(
|
|
227
250
|
'^Abstract Contract ([A-Z][a-zA-Z0-9]*)',
|
|
228
251
|
SourceKind.AbstractContract
|
|
229
252
|
)
|
|
230
253
|
static readonly contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract)
|
|
231
|
-
static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)
|
|
254
|
+
static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface)
|
|
232
255
|
static readonly scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script)
|
|
233
256
|
static readonly matchers = [
|
|
234
257
|
Project.abstractContractMatcher,
|
|
@@ -445,45 +468,107 @@ export class Project {
|
|
|
445
468
|
}
|
|
446
469
|
}
|
|
447
470
|
|
|
471
|
+
private static getImportSourcePath(projectRootDir: string, importPath: string): string {
|
|
472
|
+
const parts = importPath.split(path.sep)
|
|
473
|
+
if (parts.length > 1 && parts[0] === 'std') {
|
|
474
|
+
const currentDir = path.dirname(__filename)
|
|
475
|
+
return path.join(...[currentDir, '..', '..', '..', importPath])
|
|
476
|
+
}
|
|
477
|
+
let moduleDir = projectRootDir
|
|
478
|
+
while (true) {
|
|
479
|
+
const expectedPath = path.join(...[moduleDir, 'node_modules', importPath])
|
|
480
|
+
if (fs.existsSync(expectedPath)) {
|
|
481
|
+
return expectedPath
|
|
482
|
+
}
|
|
483
|
+
const oldModuleDir = moduleDir
|
|
484
|
+
moduleDir = path.join(moduleDir, '..')
|
|
485
|
+
if (oldModuleDir === moduleDir) {
|
|
486
|
+
throw new Error(`Specified import file does not exist: ${importPath}`)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private static async handleImports(
|
|
492
|
+
projectRootDir: string,
|
|
493
|
+
contractRootDir: string,
|
|
494
|
+
sourceStr: string,
|
|
495
|
+
importsCache: string[]
|
|
496
|
+
): Promise<[string, SourceInfo[]]> {
|
|
497
|
+
const localImportsCache: string[] = []
|
|
498
|
+
const result = sourceStr.replace(Project.importRegex, (match) => {
|
|
499
|
+
localImportsCache.push(match)
|
|
500
|
+
return ''
|
|
501
|
+
})
|
|
502
|
+
const externalSourceInfos: SourceInfo[] = []
|
|
503
|
+
for (const myImport of localImportsCache) {
|
|
504
|
+
const originImportPath = myImport.slice(8, -1)
|
|
505
|
+
const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral'
|
|
506
|
+
if (!importsCache.includes(importPath)) {
|
|
507
|
+
importsCache.push(importPath)
|
|
508
|
+
const sourcePath = Project.getImportSourcePath(projectRootDir, importPath)
|
|
509
|
+
const sourceInfos = await Project.loadSourceFile(
|
|
510
|
+
projectRootDir,
|
|
511
|
+
contractRootDir,
|
|
512
|
+
sourcePath,
|
|
513
|
+
importsCache,
|
|
514
|
+
true
|
|
515
|
+
)
|
|
516
|
+
externalSourceInfos.push(...sourceInfos)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return [result, externalSourceInfos]
|
|
520
|
+
}
|
|
521
|
+
|
|
448
522
|
private static async loadSourceFile(
|
|
523
|
+
projectRootDir: string,
|
|
449
524
|
contractsRootDir: string,
|
|
450
|
-
|
|
451
|
-
|
|
525
|
+
sourcePath: string,
|
|
526
|
+
importsCache: string[],
|
|
527
|
+
isExternal: boolean
|
|
452
528
|
): Promise<SourceInfo[]> {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`)
|
|
529
|
+
const contractRelativePath = path.relative(contractsRootDir, sourcePath)
|
|
530
|
+
if (!sourcePath.endsWith('.ral')) {
|
|
531
|
+
throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`)
|
|
457
532
|
}
|
|
458
533
|
|
|
459
|
-
const sourceBuffer = await fsPromises.readFile(
|
|
460
|
-
const sourceStr =
|
|
461
|
-
|
|
534
|
+
const sourceBuffer = await fsPromises.readFile(sourcePath)
|
|
535
|
+
const [sourceStr, externalSourceInfos] = await Project.handleImports(
|
|
536
|
+
projectRootDir,
|
|
537
|
+
contractsRootDir,
|
|
538
|
+
sourceBuffer.toString(),
|
|
539
|
+
importsCache
|
|
540
|
+
)
|
|
541
|
+
if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
|
|
542
|
+
throw new Error(`Invalid import statements, source: ${sourcePath}`)
|
|
543
|
+
}
|
|
544
|
+
const sourceInfos = externalSourceInfos
|
|
462
545
|
for (const matcher of this.matchers) {
|
|
463
546
|
const results = sourceStr.matchAll(matcher.matcher)
|
|
464
547
|
for (const result of results) {
|
|
465
|
-
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath)
|
|
548
|
+
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal)
|
|
466
549
|
sourceInfos.push(sourceInfo)
|
|
467
550
|
}
|
|
468
551
|
}
|
|
469
552
|
return sourceInfos
|
|
470
553
|
}
|
|
471
554
|
|
|
472
|
-
private static async loadSourceFiles(contractsRootDir: string): Promise<SourceInfo[]> {
|
|
473
|
-
const
|
|
555
|
+
private static async loadSourceFiles(projectRootDir: string, contractsRootDir: string): Promise<SourceInfo[]> {
|
|
556
|
+
const importsCache: string[] = []
|
|
557
|
+
const sourceInfos: SourceInfo[] = []
|
|
558
|
+
const loadDir = async function (dirPath: string): Promise<void> {
|
|
474
559
|
const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
|
|
475
560
|
for (const dirent of dirents) {
|
|
476
561
|
if (dirent.isFile()) {
|
|
477
|
-
const
|
|
478
|
-
|
|
562
|
+
const sourcePath = path.join(dirPath, dirent.name)
|
|
563
|
+
const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false)
|
|
564
|
+
sourceInfos.push(...infos)
|
|
479
565
|
} else {
|
|
480
566
|
const newPath = path.join(dirPath, dirent.name)
|
|
481
|
-
await loadDir(newPath
|
|
567
|
+
await loadDir(newPath)
|
|
482
568
|
}
|
|
483
569
|
}
|
|
484
570
|
}
|
|
485
|
-
|
|
486
|
-
await loadDir(contractsRootDir, sourceInfos)
|
|
571
|
+
await loadDir(contractsRootDir)
|
|
487
572
|
const contractAndScriptSize = sourceInfos.filter(
|
|
488
573
|
(f) => f.type === SourceKind.Contract || f.type === SourceKind.Script
|
|
489
574
|
).length
|
|
@@ -498,11 +583,12 @@ export class Project {
|
|
|
498
583
|
|
|
499
584
|
static async build(
|
|
500
585
|
compilerOptionsPartial: Partial<CompilerOptions> = {},
|
|
586
|
+
projectRootDir = '.',
|
|
501
587
|
contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
|
|
502
588
|
artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR
|
|
503
589
|
): Promise<void> {
|
|
504
590
|
const provider = getCurrentNodeProvider()
|
|
505
|
-
const sourceFiles = await Project.loadSourceFiles(contractsRootDir)
|
|
591
|
+
const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
|
|
506
592
|
const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
|
|
507
593
|
const projectArtifact = await ProjectArtifact.from(artifactsRootDir)
|
|
508
594
|
if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
|