motoko 2.0.6 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +2 -2
- package/contrib/monaco.js +65 -52
- package/index.js +1 -4
- package/interpreter.js +1 -4
- package/lib/file.d.ts +22 -0
- package/lib/file.d.ts.map +1 -0
- package/lib/file.js +7 -5
- package/lib/file.js.map +1 -0
- package/lib/index.d.ts +69 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +39 -34
- package/lib/index.js.map +1 -0
- package/lib/package.d.ts +10 -0
- package/lib/package.d.ts.map +1 -0
- package/lib/package.js +125 -120
- package/lib/package.js.map +1 -0
- package/lib/versions/interpreter.d.ts +45 -0
- package/lib/versions/interpreter.d.ts.map +1 -0
- package/lib/versions/interpreter.js +9 -0
- package/lib/versions/interpreter.js.map +1 -0
- package/lib/versions/moc.d.ts +45 -0
- package/lib/versions/moc.d.ts.map +1 -0
- package/lib/versions/moc.js +9 -0
- package/lib/versions/moc.js.map +1 -0
- package/package.json +16 -7
- package/src/file.ts +68 -0
- package/src/index.ts +139 -0
- package/src/package.ts +185 -0
- package/src/versions/interpreter.ts +4 -0
- package/src/versions/moc.ts +4 -0
- package/lib/__tests__/index.test.js +0 -40
- package/lib/__tests__/interpreter.test.js +0 -15
package/src/index.ts
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
import { file } from './file';
|
2
|
+
import { loadPackages, PackageInfo } from './package';
|
3
|
+
|
4
|
+
export type Motoko = ReturnType<typeof getMotoko>;
|
5
|
+
|
6
|
+
type Compiler = any; // TODO
|
7
|
+
|
8
|
+
// TODO
|
9
|
+
export type Diagnostic = {
|
10
|
+
code?: string | number | { target: any; value: string | number };
|
11
|
+
message: string;
|
12
|
+
range: {
|
13
|
+
start: { line: number; character: number };
|
14
|
+
end: { line: number; character: number };
|
15
|
+
};
|
16
|
+
severity: string;
|
17
|
+
source?: string;
|
18
|
+
tags?: string[];
|
19
|
+
};
|
20
|
+
|
21
|
+
export type WasmMode = 'ic' | 'wasi';
|
22
|
+
|
23
|
+
export default function getMotoko(compiler: Compiler, version: string) {
|
24
|
+
const debug = require('debug')(version ? `motoko:${version}` : 'motoko');
|
25
|
+
|
26
|
+
const invoke = (key: string, unwrap: boolean, args: any[]) => {
|
27
|
+
if (!compiler) {
|
28
|
+
throw new Error(
|
29
|
+
'Please load a Motoko compiler before running this function',
|
30
|
+
);
|
31
|
+
}
|
32
|
+
if (typeof compiler[key] !== 'function') {
|
33
|
+
throw new Error(`Unknown compiler function: '${key}'`);
|
34
|
+
}
|
35
|
+
let result;
|
36
|
+
try {
|
37
|
+
result = compiler[key](...args);
|
38
|
+
} catch (err) {
|
39
|
+
if (err instanceof Error) {
|
40
|
+
throw err;
|
41
|
+
}
|
42
|
+
throw new Error(
|
43
|
+
`Unable to execute ${key}(${[...args]
|
44
|
+
.map((x) => typeof x)
|
45
|
+
.join(', ')}):\n${JSON.stringify(err)}`,
|
46
|
+
);
|
47
|
+
}
|
48
|
+
if (!unwrap) {
|
49
|
+
return result;
|
50
|
+
}
|
51
|
+
if (!result.code) {
|
52
|
+
throw new Error(
|
53
|
+
result.diagnostics
|
54
|
+
? result.diagnostics
|
55
|
+
.map(({ message }: Diagnostic) => message)
|
56
|
+
.join('; ')
|
57
|
+
: '(no diagnostics)',
|
58
|
+
);
|
59
|
+
}
|
60
|
+
return result.code;
|
61
|
+
};
|
62
|
+
|
63
|
+
const mo = {
|
64
|
+
version,
|
65
|
+
compiler,
|
66
|
+
file(path: string) {
|
67
|
+
return file(mo, path);
|
68
|
+
},
|
69
|
+
// findPackage,
|
70
|
+
async loadPackages(packages: Record<string, string | PackageInfo>) {
|
71
|
+
return loadPackages(mo, packages);
|
72
|
+
},
|
73
|
+
read(path: string): string {
|
74
|
+
return invoke('readFile', false, [path]);
|
75
|
+
},
|
76
|
+
write(path: string, content: string = '') {
|
77
|
+
if (typeof content !== 'string') {
|
78
|
+
throw new Error('Non-string file content');
|
79
|
+
}
|
80
|
+
debug('+file', path);
|
81
|
+
invoke('saveFile', false, [path, content]);
|
82
|
+
},
|
83
|
+
rename(path: string, newPath: string) {
|
84
|
+
invoke('renameFile', false, [path, newPath]);
|
85
|
+
},
|
86
|
+
delete(path: string) {
|
87
|
+
debug('-file', path);
|
88
|
+
invoke('removeFile', false, [path]);
|
89
|
+
},
|
90
|
+
list(directory: string): string[] {
|
91
|
+
return invoke('readDir', false, [directory]);
|
92
|
+
},
|
93
|
+
addPackage(name: string, directory: string) {
|
94
|
+
debug('+package', name, directory);
|
95
|
+
invoke('addPackage', false, [name, directory]);
|
96
|
+
},
|
97
|
+
clearPackages() {
|
98
|
+
debug('-packages');
|
99
|
+
invoke('clearPackage', false, []);
|
100
|
+
},
|
101
|
+
setAliases(aliases: string) {
|
102
|
+
debug('aliases', aliases);
|
103
|
+
invoke('setActorAliases', false, [Object.entries(aliases)]);
|
104
|
+
},
|
105
|
+
setMetadata(values: string) {
|
106
|
+
invoke('setPublicMetadata', false, [values]);
|
107
|
+
},
|
108
|
+
check(path: string): Diagnostic[] {
|
109
|
+
const result = invoke('check', false, [path]);
|
110
|
+
return result.diagnostics;
|
111
|
+
},
|
112
|
+
run(
|
113
|
+
path: string,
|
114
|
+
libPaths?: string[] | undefined,
|
115
|
+
): { stdout: string; stderr: string; result: number | string } {
|
116
|
+
return invoke('run', false, [libPaths || [], path]);
|
117
|
+
},
|
118
|
+
candid(path: string): string {
|
119
|
+
return invoke('candid', true, [path]);
|
120
|
+
},
|
121
|
+
wasm(path: string, mode: WasmMode) {
|
122
|
+
if (!mode) {
|
123
|
+
mode = 'ic';
|
124
|
+
} else if (mode !== 'ic' && mode !== 'wasi') {
|
125
|
+
throw new Error(`Invalid WASM format: ${mode}`);
|
126
|
+
}
|
127
|
+
return invoke('compileWasm', true, [mode, path]);
|
128
|
+
},
|
129
|
+
parseMotoko(content: string): object {
|
130
|
+
return invoke('parseMotoko', true, [content]);
|
131
|
+
},
|
132
|
+
parseCandid(content: string): object {
|
133
|
+
return invoke('parseCandid', true, [content]);
|
134
|
+
},
|
135
|
+
};
|
136
|
+
// @ts-ignore
|
137
|
+
mo.default = mo;
|
138
|
+
return mo;
|
139
|
+
}
|
package/src/package.ts
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
// Derived from: https://github.com/dfinity/motoko-playground/blob/main/src/workers/file.ts
|
2
|
+
|
3
|
+
// @ts-ignore
|
4
|
+
import { default as parse } from 'isomorphic-parse-github-url';
|
5
|
+
import fetch from 'cross-fetch';
|
6
|
+
import { Motoko } from '.';
|
7
|
+
|
8
|
+
export interface PackageInfo {
|
9
|
+
name: string;
|
10
|
+
repo: string;
|
11
|
+
version: string;
|
12
|
+
dir: string;
|
13
|
+
branch?: string | undefined;
|
14
|
+
}
|
15
|
+
|
16
|
+
async function fetchPackage(mo: Motoko, info: PackageInfo) {
|
17
|
+
if (
|
18
|
+
!info.repo.startsWith('https://github.com/') ||
|
19
|
+
!info.repo.endsWith('.git')
|
20
|
+
) {
|
21
|
+
return false;
|
22
|
+
}
|
23
|
+
const repo = {
|
24
|
+
name: info.name,
|
25
|
+
version: info.version,
|
26
|
+
repo: info.repo.slice(0, -4).replace(/^(https:\/\/github.com\/)/, ''),
|
27
|
+
branch: info.version,
|
28
|
+
dir: info.dir || 'src',
|
29
|
+
};
|
30
|
+
const result = await fetchGithub(mo, repo, info.name);
|
31
|
+
if (result) {
|
32
|
+
mo.addPackage(info.name, info.name + '/');
|
33
|
+
}
|
34
|
+
return result ? true : false;
|
35
|
+
}
|
36
|
+
|
37
|
+
async function fetchGithub(mo: Motoko, info: PackageInfo, directory = '') {
|
38
|
+
const possiblyCDN = !(
|
39
|
+
(info.branch.length % 2 === 0 && /^[A-F0-9]+$/i.test(info.branch)) ||
|
40
|
+
info.branch === 'master' ||
|
41
|
+
info.branch === 'main'
|
42
|
+
);
|
43
|
+
if (possiblyCDN) {
|
44
|
+
const result = await fetchFromCDN(mo, info, directory);
|
45
|
+
if (result) {
|
46
|
+
return result;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
return await fetchFromGithub(mo, info, directory);
|
50
|
+
}
|
51
|
+
|
52
|
+
// function saveWorkplaceToMotoko(mo, files) {
|
53
|
+
// for (const [name, code] of Object.entries(files)) {
|
54
|
+
// if (!name.endsWith('mo')) continue;
|
55
|
+
// mo.addFile(name, code);
|
56
|
+
// }
|
57
|
+
// }
|
58
|
+
|
59
|
+
async function fetchFromCDN(mo: Motoko, info: PackageInfo, directory = '') {
|
60
|
+
const meta_url = `https://data.jsdelivr.com/v1/package/gh/${info.repo}@${info.branch}/flat`;
|
61
|
+
const base_url = `https://cdn.jsdelivr.net/gh/${info.repo}@${info.branch}`;
|
62
|
+
const response = await fetch(meta_url);
|
63
|
+
const json = await response.json();
|
64
|
+
if (!json.hasOwnProperty('files')) {
|
65
|
+
throw new Error(json.message || `Could not fetch from CDN: ${info}`);
|
66
|
+
}
|
67
|
+
const promises: Promise<void>[] = [];
|
68
|
+
const files: Record<string, string> = {};
|
69
|
+
for (const f of json.files) {
|
70
|
+
if (f.name.startsWith(`/${info.dir}/`) && /\.mo$/.test(f.name)) {
|
71
|
+
const promise = (async () => {
|
72
|
+
const content = await (await fetch(base_url + f.name)).text();
|
73
|
+
const stripped =
|
74
|
+
directory +
|
75
|
+
f.name.slice(info.dir ? info.dir.length + 1 : 0);
|
76
|
+
mo.write(stripped, content);
|
77
|
+
files[stripped] = content;
|
78
|
+
})();
|
79
|
+
promises.push(promise);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
if (!promises.length) {
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
return Promise.all(promises).then(() => {
|
86
|
+
return files;
|
87
|
+
});
|
88
|
+
}
|
89
|
+
|
90
|
+
async function fetchFromGithub(
|
91
|
+
mo: Motoko,
|
92
|
+
info: PackageInfo,
|
93
|
+
directory: string = '',
|
94
|
+
) {
|
95
|
+
const meta_url = `https://api.github.com/repos/${info.repo}/git/trees/${info.branch}?recursive=1`;
|
96
|
+
const base_url = `https://raw.githubusercontent.com/${info.repo}/${info.branch}/`;
|
97
|
+
const response = await fetch(meta_url);
|
98
|
+
const json = await response.json();
|
99
|
+
if (!json.hasOwnProperty('tree')) {
|
100
|
+
throw new Error(
|
101
|
+
json.message || `Could not fetch from GitHub repository: ${info}`,
|
102
|
+
);
|
103
|
+
}
|
104
|
+
const promises: Promise<void>[] = [];
|
105
|
+
const files: Record<string, string> = {};
|
106
|
+
for (const f of json.tree) {
|
107
|
+
if (
|
108
|
+
f.path.startsWith(info.dir ? `${info.dir}/` : '') &&
|
109
|
+
f.type === 'blob' &&
|
110
|
+
/\.mo$/.test(f.path)
|
111
|
+
) {
|
112
|
+
const promise = (async () => {
|
113
|
+
const content = await (await fetch(base_url + f.path)).text();
|
114
|
+
const stripped =
|
115
|
+
directory +
|
116
|
+
(directory ? '/' : '') +
|
117
|
+
f.path.slice(info.dir ? info.dir.length + 1 : 0);
|
118
|
+
mo.write(stripped, content);
|
119
|
+
files[stripped] = content;
|
120
|
+
})();
|
121
|
+
promises.push(promise);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
if (!promises.length) {
|
125
|
+
return;
|
126
|
+
}
|
127
|
+
return Promise.all(promises).then(() => {
|
128
|
+
return files;
|
129
|
+
});
|
130
|
+
}
|
131
|
+
|
132
|
+
// async function resolve(path) {
|
133
|
+
|
134
|
+
// }
|
135
|
+
|
136
|
+
function parseGithubPackage(
|
137
|
+
path: string | PackageInfo,
|
138
|
+
name: string,
|
139
|
+
): PackageInfo {
|
140
|
+
if (!path) {
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
if (typeof path === 'object') {
|
144
|
+
return path;
|
145
|
+
}
|
146
|
+
|
147
|
+
let result;
|
148
|
+
try {
|
149
|
+
result = parse(path);
|
150
|
+
if (!result) {
|
151
|
+
return;
|
152
|
+
}
|
153
|
+
} catch (err) {
|
154
|
+
console.warn(err);
|
155
|
+
}
|
156
|
+
|
157
|
+
const { name: repoName, filepath, branch, owner } = result;
|
158
|
+
|
159
|
+
return {
|
160
|
+
name: name || repoName,
|
161
|
+
repo: `https://github.com/${owner}/${repoName}.git`,
|
162
|
+
version: branch,
|
163
|
+
dir: filepath,
|
164
|
+
// homepage: ,
|
165
|
+
};
|
166
|
+
}
|
167
|
+
|
168
|
+
export async function loadPackages(
|
169
|
+
mo: Motoko,
|
170
|
+
packages: Record<string, string | PackageInfo>,
|
171
|
+
) {
|
172
|
+
await Promise.all(
|
173
|
+
Object.entries(packages).map(([name, path]) => {
|
174
|
+
const info = parseGithubPackage(path, name);
|
175
|
+
return fetchPackage(mo, info);
|
176
|
+
}),
|
177
|
+
);
|
178
|
+
}
|
179
|
+
|
180
|
+
// export async function findPackage(package) {
|
181
|
+
// if (typeof package === 'string') {
|
182
|
+
// return resolve(package);
|
183
|
+
// }
|
184
|
+
// return package;
|
185
|
+
// },
|
@@ -1,40 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
const mo = require('../..');
|
4
|
-
|
5
|
-
const actor = `
|
6
|
-
actor Main {
|
7
|
-
public func test() : async Nat {
|
8
|
-
123
|
9
|
-
}
|
10
|
-
}
|
11
|
-
`;
|
12
|
-
|
13
|
-
describe('virtual file system I/O', () => {
|
14
|
-
test('write -> read', () => {
|
15
|
-
const path = 'test__write_read__.txt';
|
16
|
-
const text = 'A\nB';
|
17
|
-
mo.write(path, text);
|
18
|
-
expect(mo.read(path)).toStrictEqual(text);
|
19
|
-
});
|
20
|
-
});
|
21
|
-
|
22
|
-
describe('check', () => {
|
23
|
-
test('works for a basic example', () => {
|
24
|
-
const path = 'test__check__.mo';
|
25
|
-
mo.write(path, actor);
|
26
|
-
expect(mo.check(path)).toStrictEqual([]);
|
27
|
-
});
|
28
|
-
});
|
29
|
-
|
30
|
-
describe('run', () => {
|
31
|
-
test('works for a basic example', () => {
|
32
|
-
const path = 'test__run__.mo';
|
33
|
-
mo.write(path, 'let x = 1 + 1; x');
|
34
|
-
expect(mo.run(path)).toStrictEqual({
|
35
|
-
result: 0,
|
36
|
-
stdout: '2 : Nat\n',
|
37
|
-
stderr: '',
|
38
|
-
});
|
39
|
-
});
|
40
|
-
});
|
@@ -1,15 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
const mo = require('../../interpreter');
|
4
|
-
|
5
|
-
describe('run', () => {
|
6
|
-
test('works for a basic example', () => {
|
7
|
-
const path = 'test__run__.mo';
|
8
|
-
mo.write(path, 'let x = 1 + 1; x');
|
9
|
-
expect(mo.run(path)).toStrictEqual({
|
10
|
-
result: 0,
|
11
|
-
stdout: '2 : Nat\n',
|
12
|
-
stderr: '',
|
13
|
-
});
|
14
|
-
});
|
15
|
-
});
|