ic-mops 1.1.0-pre.0 → 1.1.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 (74) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/moc-wrapper.sh +1 -1
  3. package/bundle/cli.tgz +0 -0
  4. package/check-requirements.ts +1 -1
  5. package/cli.ts +18 -0
  6. package/commands/bench.ts +1 -1
  7. package/commands/install/install-all.ts +2 -2
  8. package/commands/replica.ts +33 -3
  9. package/commands/sources.ts +1 -1
  10. package/commands/sync.ts +4 -19
  11. package/commands/test/mmf1.ts +4 -0
  12. package/commands/test/reporters/silent-reporter.ts +22 -4
  13. package/commands/test/test.ts +74 -10
  14. package/commands/watch/deployer.ts +155 -0
  15. package/commands/watch/error-checker.ts +87 -0
  16. package/commands/watch/generator.ts +99 -0
  17. package/commands/watch/globMoFiles.ts +16 -0
  18. package/commands/watch/parseDfxJson.ts +64 -0
  19. package/commands/watch/tester.ts +81 -0
  20. package/commands/watch/warning-checker.ts +133 -0
  21. package/commands/watch/watch.ts +90 -0
  22. package/declarations/main/main.did +16 -10
  23. package/declarations/main/main.did.d.ts +19 -10
  24. package/declarations/main/main.did.js +25 -11
  25. package/dist/bin/moc-wrapper.sh +1 -1
  26. package/dist/check-requirements.js +1 -1
  27. package/dist/cli.js +16 -0
  28. package/dist/commands/bench.js +1 -1
  29. package/dist/commands/install/install-all.js +2 -2
  30. package/dist/commands/replica.d.ts +2 -2
  31. package/dist/commands/replica.js +26 -3
  32. package/dist/commands/sources.d.ts +1 -1
  33. package/dist/commands/sources.js +1 -1
  34. package/dist/commands/sync.js +3 -18
  35. package/dist/commands/test/mmf1.d.ts +1 -0
  36. package/dist/commands/test/mmf1.js +3 -0
  37. package/dist/commands/test/reporters/silent-reporter.d.ts +6 -1
  38. package/dist/commands/test/reporters/silent-reporter.js +18 -5
  39. package/dist/commands/test/test.d.ts +1 -1
  40. package/dist/commands/test/test.js +62 -10
  41. package/dist/commands/watch/deployer.d.ts +24 -0
  42. package/dist/commands/watch/deployer.js +125 -0
  43. package/dist/commands/watch/error-checker.d.ts +13 -0
  44. package/dist/commands/watch/error-checker.js +76 -0
  45. package/dist/commands/watch/generator.d.ts +21 -0
  46. package/dist/commands/watch/generator.js +79 -0
  47. package/dist/commands/watch/globMoFiles.d.ts +1 -0
  48. package/dist/commands/watch/globMoFiles.js +14 -0
  49. package/dist/commands/watch/parseDfxJson.d.ts +2 -0
  50. package/dist/commands/watch/parseDfxJson.js +22 -0
  51. package/dist/commands/watch/tester.d.ts +19 -0
  52. package/dist/commands/watch/tester.js +63 -0
  53. package/dist/commands/watch/warning-checker.d.ts +20 -0
  54. package/dist/commands/watch/warning-checker.js +111 -0
  55. package/dist/commands/watch/watch.d.ts +7 -0
  56. package/dist/commands/watch/watch.js +79 -0
  57. package/dist/declarations/main/main.did +16 -10
  58. package/dist/declarations/main/main.did.d.ts +19 -10
  59. package/dist/declarations/main/main.did.js +25 -11
  60. package/dist/helpers/get-moc-path.d.ts +1 -1
  61. package/dist/helpers/get-moc-path.js +17 -3
  62. package/dist/helpers/get-moc-version.d.ts +1 -1
  63. package/dist/helpers/get-moc-version.js +17 -5
  64. package/dist/integrity.js +3 -2
  65. package/dist/package.json +3 -2
  66. package/dist/parallel.d.ts +1 -1
  67. package/dist/templates/mops-test.yml +4 -2
  68. package/helpers/get-moc-path.ts +19 -3
  69. package/helpers/get-moc-version.ts +17 -5
  70. package/integrity.ts +3 -2
  71. package/package.json +3 -2
  72. package/parallel.ts +2 -2
  73. package/templates/mops-test.yml +4 -2
  74. package/test +0 -4
@@ -59,13 +59,22 @@ export async function test(filter = '', options = {}) {
59
59
  process.exit(0);
60
60
  });
61
61
  }
62
+ else {
63
+ process.exit(0);
64
+ }
62
65
  });
63
66
  // todo: run only changed for *.test.mo?
64
67
  // todo: run all for *.mo?
68
+ let curRun = Promise.resolve(true);
69
+ let controller = new AbortController();
65
70
  let run = debounce(async () => {
71
+ controller.abort();
72
+ await curRun;
66
73
  console.clear();
67
74
  process.stdout.write('\x1Bc');
68
- await runAll(options.reporter, filter, options.mode, replicaType, true);
75
+ controller = new AbortController();
76
+ curRun = runAll(options.reporter, filter, options.mode, replicaType, true, controller.signal);
77
+ await curRun;
69
78
  console.log('-'.repeat(50));
70
79
  console.log('Waiting for file changes...');
71
80
  console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
@@ -91,11 +100,11 @@ export async function test(filter = '', options = {}) {
91
100
  }
92
101
  let mocPath = '';
93
102
  let wasmtimePath = '';
94
- async function runAll(reporterName, filter = '', mode = 'interpreter', replicaType, watch = false) {
95
- let done = await testWithReporter(reporterName, filter, mode, replicaType, watch);
103
+ async function runAll(reporterName, filter = '', mode = 'interpreter', replicaType, watch = false, signal) {
104
+ let done = await testWithReporter(reporterName, filter, mode, replicaType, watch, signal);
96
105
  return done;
97
106
  }
98
- export async function testWithReporter(reporterName, filter = '', defaultMode = 'interpreter', replicaType, watch = false) {
107
+ export async function testWithReporter(reporterName, filter = '', defaultMode = 'interpreter', replicaType, watch = false, signal) {
99
108
  let rootDir = getRootDir();
100
109
  let files = [];
101
110
  let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
@@ -149,6 +158,9 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
149
158
  replica.dir = testTempDir;
150
159
  fs.mkdirSync(testTempDir, { recursive: true });
151
160
  await parallel(os.cpus().length, files, async (file) => {
161
+ if (signal?.aborted) {
162
+ return;
163
+ }
152
164
  let mmf = new MMF1('store', absToRel(file));
153
165
  // mode overrides
154
166
  let lines = fs.readFileSync(file, 'utf8').split('\n');
@@ -175,14 +187,26 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
175
187
  let mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
176
188
  // interpret
177
189
  if (mode === 'interpreter') {
178
- let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
190
+ let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs], { signal });
191
+ proc.addListener('error', (error) => {
192
+ if (error?.code === 'ABORT_ERR') {
193
+ return;
194
+ }
195
+ throw error;
196
+ });
179
197
  pipeMMF(proc, mmf).then(resolve);
180
198
  }
181
199
  // build and run wasm
182
200
  else if (mode === 'wasi') {
183
201
  let wasmFile = `${path.join(testTempDir, path.parse(file).name)}.wasm`;
184
202
  // build
185
- let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs]);
203
+ let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs], { signal });
204
+ buildProc.addListener('error', (error) => {
205
+ if (error?.code === 'ABORT_ERR') {
206
+ return;
207
+ }
208
+ throw error;
209
+ });
186
210
  pipeMMF(buildProc, mmf).then(async () => {
187
211
  if (mmf.failed > 0) {
188
212
  return;
@@ -204,7 +228,13 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
204
228
  console.error(chalk.red('Minimum wasmtime version is 14.0.0. Please update wasmtime to the latest version'));
205
229
  process.exit(1);
206
230
  }
207
- let proc = spawn(wasmtimePath, wasmtimeArgs);
231
+ let proc = spawn(wasmtimePath, wasmtimeArgs, { signal });
232
+ proc.addListener('error', (error) => {
233
+ if (error?.code === 'ABORT_ERR') {
234
+ return;
235
+ }
236
+ throw error;
237
+ });
208
238
  await pipeMMF(proc, mmf);
209
239
  }).finally(() => {
210
240
  fs.rmSync(wasmFile, { force: true });
@@ -215,18 +245,30 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
215
245
  // mmf.strategy = 'print'; // because we run replica tests one-by-one
216
246
  let wasmFile = `${path.join(testTempDir, path.parse(file).name)}.wasm`;
217
247
  // build
218
- let buildProc = spawn(mocPath, [`-o=${wasmFile}`, ...mocArgs]);
248
+ let buildProc = spawn(mocPath, [`-o=${wasmFile}`, ...mocArgs], { signal });
249
+ buildProc.addListener('error', (error) => {
250
+ if (error?.code === 'ABORT_ERR') {
251
+ return;
252
+ }
253
+ throw error;
254
+ });
219
255
  pipeMMF(buildProc, mmf).then(async () => {
220
256
  if (mmf.failed > 0) {
221
257
  return;
222
258
  }
223
259
  await startReplicaOnce(replica, replicaType);
260
+ if (signal?.aborted) {
261
+ return;
262
+ }
224
263
  let canisterName = path.parse(file).name;
225
264
  let idlFactory = ({ IDL }) => {
226
265
  return IDL.Service({ 'runTests': IDL.Func([], [], []) });
227
266
  };
228
- let { stream } = await replica.deploy(canisterName, wasmFile, idlFactory);
229
- pipeStdoutToMMF(stream, mmf);
267
+ let canister = await replica.deploy(canisterName, wasmFile, idlFactory, undefined, signal);
268
+ if (signal?.aborted || !canister) {
269
+ return;
270
+ }
271
+ pipeStdoutToMMF(canister.stream, mmf);
230
272
  let actor = await replica.getActor(canisterName);
231
273
  try {
232
274
  if (globalThis.mopsReplicaTestRunning) {
@@ -239,6 +281,9 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
239
281
  }, Math.random() * 1000 | 0);
240
282
  });
241
283
  }
284
+ if (signal?.aborted) {
285
+ return;
286
+ }
242
287
  globalThis.mopsReplicaTestRunning = true;
243
288
  await actor.runTests();
244
289
  globalThis.mopsReplicaTestRunning = false;
@@ -250,10 +295,14 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
250
295
  stderrStream.write(e.message);
251
296
  }
252
297
  }).finally(async () => {
298
+ globalThis.mopsReplicaTestRunning = false;
253
299
  fs.rmSync(wasmFile, { force: true });
254
300
  }).then(resolve);
255
301
  }
256
302
  });
303
+ if (signal?.aborted) {
304
+ return;
305
+ }
257
306
  reporter.addRun(file, mmf, promise, mode);
258
307
  await promise;
259
308
  });
@@ -261,6 +310,9 @@ export async function testWithReporter(reporterName, filter = '', defaultMode =
261
310
  await replica.stop();
262
311
  fs.rmSync(testTempDir, { recursive: true, force: true });
263
312
  }
313
+ if (signal?.aborted) {
314
+ return false;
315
+ }
264
316
  return reporter.done();
265
317
  }
266
318
  function pipeStdoutToMMF(stdout, mmf) {
@@ -0,0 +1,24 @@
1
+ import { ErrorChecker } from './error-checker.js';
2
+ import { Generator } from './generator.js';
3
+ export declare class Deployer {
4
+ verbose: boolean;
5
+ canisters: Record<string, string>;
6
+ status: 'pending' | 'running' | 'syntax-error' | 'dfx-error' | 'error' | 'success';
7
+ errorChecker: ErrorChecker;
8
+ generator: Generator;
9
+ success: number;
10
+ errors: string[];
11
+ aborted: boolean;
12
+ controllers: Map<string, AbortController>;
13
+ currentRun: Promise<any> | undefined;
14
+ constructor({ verbose, canisters, errorChecker, generator }: {
15
+ verbose: boolean;
16
+ canisters: Record<string, string>;
17
+ errorChecker: ErrorChecker;
18
+ generator: Generator;
19
+ });
20
+ reset(): void;
21
+ abortCurrent(): Promise<void>;
22
+ run(onProgress: () => void): Promise<void>;
23
+ getOutput(): string;
24
+ }
@@ -0,0 +1,125 @@
1
+ import chalk from 'chalk';
2
+ import os from 'node:os';
3
+ import { promisify } from 'node:util';
4
+ import { execFile, execSync } from 'node:child_process';
5
+ import { parallel } from '../../parallel.js';
6
+ import { getRootDir } from '../../mops.js';
7
+ export class Deployer {
8
+ constructor({ verbose, canisters, errorChecker, generator }) {
9
+ this.verbose = false;
10
+ this.canisters = {};
11
+ this.status = 'pending';
12
+ this.success = 0;
13
+ this.errors = [];
14
+ this.aborted = false;
15
+ this.controllers = new Map();
16
+ this.verbose = verbose;
17
+ this.canisters = canisters;
18
+ this.errorChecker = errorChecker;
19
+ this.generator = generator;
20
+ }
21
+ reset() {
22
+ this.status = 'pending';
23
+ this.success = 0;
24
+ this.errors = [];
25
+ }
26
+ async abortCurrent() {
27
+ this.aborted = true;
28
+ for (let controller of this.controllers.values()) {
29
+ controller.abort();
30
+ }
31
+ this.controllers.clear();
32
+ await this.currentRun;
33
+ this.reset();
34
+ this.aborted = false;
35
+ }
36
+ async run(onProgress) {
37
+ await this.abortCurrent();
38
+ if (this.errorChecker.status === 'error') {
39
+ this.status = 'syntax-error';
40
+ onProgress();
41
+ return;
42
+ }
43
+ if (Object.keys(this.canisters).length === 0) {
44
+ this.status = 'success';
45
+ onProgress();
46
+ return;
47
+ }
48
+ let rootDir = getRootDir();
49
+ try {
50
+ execSync('dfx ping', { cwd: rootDir });
51
+ }
52
+ catch (error) {
53
+ this.status = 'dfx-error';
54
+ onProgress();
55
+ return;
56
+ }
57
+ this.status = 'running';
58
+ onProgress();
59
+ // create canisters (sequentially to avoid DFX errors)
60
+ let resolve;
61
+ this.currentRun = new Promise((res) => resolve = res);
62
+ for (let canister of Object.keys(this.canisters)) {
63
+ let controller = new AbortController();
64
+ let { signal } = controller;
65
+ this.controllers.set(canister, controller);
66
+ await promisify(execFile)('dfx', ['canister', 'create', canister], { cwd: rootDir, signal }).catch((error) => {
67
+ if (error.code === 'ABORT_ERR') {
68
+ return { stderr: '' };
69
+ }
70
+ throw error;
71
+ });
72
+ this.controllers.delete(canister);
73
+ }
74
+ resolve?.();
75
+ if (this.aborted) {
76
+ return;
77
+ }
78
+ this.currentRun = parallel(os.cpus().length, [...Object.keys(this.canisters)], async (canister) => {
79
+ let controller = new AbortController();
80
+ let { signal } = controller;
81
+ this.controllers.set(canister, controller);
82
+ // build
83
+ if (this.generator.status !== 'success' || !this.generator.canisters[canister]) {
84
+ await promisify(execFile)('dfx', ['build', canister], { cwd: rootDir, signal }).catch((error) => {
85
+ if (error.code === 'ABORT_ERR') {
86
+ return { stderr: '' };
87
+ }
88
+ throw error;
89
+ });
90
+ }
91
+ // install
92
+ await promisify(execFile)('dfx', ['canister', 'install', '--mode=auto', '--yes', canister], { cwd: rootDir, signal }).catch((error) => {
93
+ if (error.code === 'ABORT_ERR') {
94
+ return { stderr: '' };
95
+ }
96
+ throw error;
97
+ });
98
+ this.success += 1;
99
+ this.controllers.delete(canister);
100
+ onProgress();
101
+ });
102
+ await this.currentRun;
103
+ if (!this.aborted) {
104
+ this.status = 'success';
105
+ }
106
+ onProgress();
107
+ }
108
+ getOutput() {
109
+ let get = (v) => v.toString();
110
+ let count = (this.status === 'running' ? get : chalk.bold[this.errors.length > 0 ? 'redBright' : 'green'])(this.errors.length || this.success);
111
+ if (this.status === 'pending') {
112
+ return `Deploy: ${chalk.gray('(pending)')}`;
113
+ }
114
+ if (this.status === 'running') {
115
+ return `Deploy: ${count}/${Object.keys(this.canisters).length} ${chalk.gray('(running)')}`;
116
+ }
117
+ if (this.status === 'syntax-error') {
118
+ return `Deploy: ${chalk.gray('(errors)')}`;
119
+ }
120
+ if (this.status === 'dfx-error') {
121
+ return `Deploy: ${chalk.gray('(dfx not running)')}`;
122
+ }
123
+ return `Deploy: ${count}`;
124
+ }
125
+ }
@@ -0,0 +1,13 @@
1
+ export declare class ErrorChecker {
2
+ verbose: boolean;
3
+ canisters: Record<string, string>;
4
+ status: 'pending' | 'running' | 'error' | 'success';
5
+ errors: string[];
6
+ constructor({ verbose, canisters }: {
7
+ verbose: boolean;
8
+ canisters: Record<string, string>;
9
+ });
10
+ reset(): void;
11
+ run(onProgress: () => void): Promise<void>;
12
+ getOutput(): string;
13
+ }
@@ -0,0 +1,76 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import os from 'node:os';
4
+ import chalk from 'chalk';
5
+ import { getMocPath } from '../../helpers/get-moc-path.js';
6
+ import { getRootDir } from '../../mops.js';
7
+ import { sources } from '../sources.js';
8
+ import { parallel } from '../../parallel.js';
9
+ import { globMoFiles } from './globMoFiles.js';
10
+ export class ErrorChecker {
11
+ constructor({ verbose, canisters }) {
12
+ this.verbose = false;
13
+ this.canisters = {};
14
+ this.status = 'pending';
15
+ this.errors = [];
16
+ this.verbose = verbose;
17
+ this.canisters = canisters;
18
+ }
19
+ reset() {
20
+ this.status = 'pending';
21
+ this.errors = [];
22
+ }
23
+ async run(onProgress) {
24
+ this.reset();
25
+ this.status = 'running';
26
+ onProgress();
27
+ let rootDir = getRootDir();
28
+ let mocPath = getMocPath();
29
+ let deps = await sources({ cwd: rootDir });
30
+ let paths = [...Object.values(this.canisters)];
31
+ if (!paths.length) {
32
+ paths = globMoFiles(rootDir);
33
+ }
34
+ await parallel(os.cpus().length, paths, async (file) => {
35
+ try {
36
+ await promisify(execFile)(mocPath, ['--check', ...deps.flatMap(x => x.split(' ')), file], { cwd: rootDir });
37
+ }
38
+ catch (error) {
39
+ error.message.split('\n').forEach((line) => {
40
+ if (line.match(/: \w+ error \[/)) {
41
+ // better formatting
42
+ let str = line
43
+ .replace(/: (\w+ error) \[/, (_, m1) => `: ${chalk.red(m1)} [`)
44
+ .replace(/unbound type (\w+)/, `unbound type ${chalk.bold('$1')}`)
45
+ .replace(/unbound variable (\w+)/, `unbound variable ${chalk.bold('$1')}`)
46
+ .trim();
47
+ this.errors.push(str);
48
+ }
49
+ else if (line.startsWith(' ') && this.errors.length) {
50
+ this.errors[this.errors.length - 1] += '\n ' + line;
51
+ }
52
+ else {
53
+ // console.log('UNKNOWN ERROR', line);
54
+ }
55
+ });
56
+ onProgress();
57
+ }
58
+ });
59
+ this.status = this.errors.length ? 'error' : 'success';
60
+ onProgress();
61
+ }
62
+ getOutput() {
63
+ if (this.status === 'pending') {
64
+ return `Errors: ${chalk.gray('(pending)')}`;
65
+ }
66
+ if (this.status === 'running') {
67
+ return `Errors: ${chalk.gray('(running)')}`;
68
+ }
69
+ let output = '';
70
+ output += `Errors: ${chalk.bold[this.errors.length ? 'redBright' : 'green'](this.errors.length)}`;
71
+ if (this.verbose && this.errors.length) {
72
+ output += `\n ${this.errors.join('\n ')}`;
73
+ }
74
+ return output;
75
+ }
76
+ }
@@ -0,0 +1,21 @@
1
+ import { ErrorChecker } from './error-checker.js';
2
+ export declare class Generator {
3
+ verbose: boolean;
4
+ canisters: Record<string, string>;
5
+ status: 'pending' | 'running' | 'syntax-error' | 'error' | 'success';
6
+ errorChecker: ErrorChecker;
7
+ success: number;
8
+ errors: string[];
9
+ aborted: boolean;
10
+ controllers: Map<string, AbortController>;
11
+ currentRun: Promise<any> | undefined;
12
+ constructor({ verbose, canisters, errorChecker }: {
13
+ verbose: boolean;
14
+ canisters: Record<string, string>;
15
+ errorChecker: ErrorChecker;
16
+ });
17
+ reset(): void;
18
+ abortCurrent(): Promise<void>;
19
+ run(onProgress: () => void): Promise<void>;
20
+ getOutput(): string;
21
+ }
@@ -0,0 +1,79 @@
1
+ import os from 'node:os';
2
+ import { promisify } from 'node:util';
3
+ import { execFile } from 'node:child_process';
4
+ import chalk from 'chalk';
5
+ import { parallel } from '../../parallel.js';
6
+ import { getRootDir } from '../../mops.js';
7
+ export class Generator {
8
+ constructor({ verbose, canisters, errorChecker }) {
9
+ this.verbose = false;
10
+ this.canisters = {};
11
+ this.status = 'pending';
12
+ this.success = 0;
13
+ this.errors = [];
14
+ this.aborted = false;
15
+ this.controllers = new Map();
16
+ this.verbose = verbose;
17
+ this.canisters = canisters;
18
+ this.errorChecker = errorChecker;
19
+ }
20
+ reset() {
21
+ this.status = 'pending';
22
+ this.success = 0;
23
+ this.errors = [];
24
+ }
25
+ async abortCurrent() {
26
+ this.aborted = true;
27
+ for (let controller of this.controllers.values()) {
28
+ controller.abort();
29
+ }
30
+ this.controllers.clear();
31
+ await this.currentRun;
32
+ this.reset();
33
+ this.aborted = false;
34
+ }
35
+ async run(onProgress) {
36
+ await this.abortCurrent();
37
+ if (this.errorChecker.status === 'error') {
38
+ this.status = 'syntax-error';
39
+ onProgress();
40
+ return;
41
+ }
42
+ this.status = 'running';
43
+ onProgress();
44
+ let rootDir = getRootDir();
45
+ this.currentRun = parallel(os.cpus().length, [...Object.keys(this.canisters)], async (canister) => {
46
+ let controller = new AbortController();
47
+ let { signal } = controller;
48
+ this.controllers.set(canister, controller);
49
+ await promisify(execFile)('dfx', ['generate', canister], { cwd: rootDir, signal }).catch((error) => {
50
+ if (error.code === 'ABORT_ERR') {
51
+ return { stderr: '' };
52
+ }
53
+ throw error;
54
+ });
55
+ this.success += 1;
56
+ this.controllers.delete(canister);
57
+ onProgress();
58
+ });
59
+ await this.currentRun;
60
+ if (!this.aborted) {
61
+ this.status = 'success';
62
+ }
63
+ onProgress();
64
+ }
65
+ getOutput() {
66
+ let get = (v) => v.toString();
67
+ let count = (this.status === 'running' ? get : chalk.bold[this.errors.length > 0 ? 'redBright' : 'green'])(this.errors.length || this.success);
68
+ if (this.status === 'pending') {
69
+ return `Generate: ${chalk.gray('(pending)')}`;
70
+ }
71
+ if (this.status === 'running') {
72
+ return `Generate: ${count}/${Object.keys(this.canisters).length} ${chalk.gray('(running)')}`;
73
+ }
74
+ if (this.status === 'syntax-error') {
75
+ return `Generate: ${chalk.gray('(errors)')}`;
76
+ }
77
+ return `Generate: ${count}`;
78
+ }
79
+ }
@@ -0,0 +1 @@
1
+ export declare function globMoFiles(rootDir: string): string[];
@@ -0,0 +1,14 @@
1
+ import { globSync } from 'glob';
2
+ let globConfig = {
3
+ nocase: true,
4
+ ignore: [
5
+ '**/node_modules/**',
6
+ '**/.mops/**',
7
+ '**/.vessel/**',
8
+ '**/.git/**',
9
+ ],
10
+ };
11
+ export function globMoFiles(rootDir) {
12
+ // return globSync('{src,test?(s)}/**/*.mo', {cwd: rootDir, ...globConfig});
13
+ return globSync('src/**/*.mo', { cwd: rootDir, ...globConfig });
14
+ }
@@ -0,0 +1,2 @@
1
+ export declare function getMotokoCanisters(): Record<string, string>;
2
+ export declare function getMotokoCanistersWithDeclarations(): Record<string, string>;
@@ -0,0 +1,22 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { getRootDir } from '../../mops.js';
4
+ function readDfxJson() {
5
+ let dfxJsonPath = path.resolve(getRootDir(), 'dfx.json');
6
+ if (!existsSync(dfxJsonPath)) {
7
+ return {};
8
+ }
9
+ return JSON.parse(readFileSync(dfxJsonPath, 'utf8'));
10
+ }
11
+ export function getMotokoCanisters() {
12
+ let dfxJson = readDfxJson();
13
+ return Object.fromEntries(Object.entries(dfxJson.canisters)
14
+ .filter(([_, canister]) => canister.type === 'motoko')
15
+ .map(([name, canister]) => [name, canister.main ?? '']));
16
+ }
17
+ export function getMotokoCanistersWithDeclarations() {
18
+ let dfxJson = readDfxJson();
19
+ return Object.fromEntries(Object.entries(dfxJson.canisters)
20
+ .filter(([_, canister]) => canister.type === 'motoko' && canister.declarations)
21
+ .map(([name, canister]) => [name, canister.main ?? '']));
22
+ }
@@ -0,0 +1,19 @@
1
+ import { ErrorChecker } from './error-checker.js';
2
+ import { SilentReporter } from '../test/reporters/silent-reporter.js';
3
+ export declare class Tester {
4
+ verbose: boolean;
5
+ status: 'pending' | 'running' | 'syntax-error' | 'error' | 'success';
6
+ errorChecker: ErrorChecker;
7
+ reporter: SilentReporter;
8
+ aborted: boolean;
9
+ controller: AbortController;
10
+ currentRun: Promise<any> | undefined;
11
+ constructor({ verbose, errorChecker }: {
12
+ verbose: boolean;
13
+ errorChecker: ErrorChecker;
14
+ });
15
+ reset(): void;
16
+ abortCurrent(): Promise<void>;
17
+ run(onProgress: () => void): Promise<void>;
18
+ getOutput(): string;
19
+ }
@@ -0,0 +1,63 @@
1
+ import chalk from 'chalk';
2
+ import { readConfig } from '../../mops.js';
3
+ import { testWithReporter } from '../test/test.js';
4
+ import { SilentReporter } from '../test/reporters/silent-reporter.js';
5
+ export class Tester {
6
+ constructor({ verbose, errorChecker }) {
7
+ this.verbose = false;
8
+ this.status = 'pending';
9
+ this.reporter = new SilentReporter(false);
10
+ this.aborted = false;
11
+ this.controller = new AbortController();
12
+ this.verbose = verbose;
13
+ this.errorChecker = errorChecker;
14
+ }
15
+ reset() {
16
+ this.status = 'pending';
17
+ }
18
+ async abortCurrent() {
19
+ this.aborted = true;
20
+ this.controller.abort();
21
+ await this.currentRun;
22
+ this.reset();
23
+ this.aborted = false;
24
+ }
25
+ async run(onProgress) {
26
+ await this.abortCurrent();
27
+ if (this.errorChecker.status === 'error') {
28
+ this.status = 'syntax-error';
29
+ onProgress();
30
+ return;
31
+ }
32
+ this.status = 'running';
33
+ onProgress();
34
+ this.reporter = new SilentReporter(false, onProgress);
35
+ this.controller = new AbortController();
36
+ let config = readConfig();
37
+ this.currentRun = testWithReporter(this.reporter, '', 'interpreter', config.toolchain?.['pocket-ic'] ? 'pocket-ic' : 'dfx', true, this.controller.signal);
38
+ await this.currentRun;
39
+ if (!this.aborted) {
40
+ this.status = this.reporter.failed > 0 ? 'error' : 'success';
41
+ }
42
+ onProgress();
43
+ }
44
+ getOutput() {
45
+ let get = (v) => v.toString();
46
+ let count = (this.status === 'running' ? get : chalk.bold[this.reporter.failed > 0 ? 'redBright' : 'green'])(this.reporter.failedFiles || this.reporter.passedFiles);
47
+ if (this.status === 'pending') {
48
+ return `Tests: ${chalk.gray('(pending)')}`;
49
+ }
50
+ if (this.status === 'running') {
51
+ return `Tests: ${count}/${this.reporter.total} ${chalk.gray('(running)')}`;
52
+ }
53
+ if (this.status === 'syntax-error') {
54
+ return `Tests: ${chalk.gray('(errors)')}`;
55
+ }
56
+ let output = '';
57
+ output += `Tests: ${count}`;
58
+ if (this.reporter.errorOutput) {
59
+ output += '\n' + this.reporter.errorOutput;
60
+ }
61
+ return output;
62
+ }
63
+ }
@@ -0,0 +1,20 @@
1
+ import { ErrorChecker } from './error-checker.js';
2
+ export declare class WarningChecker {
3
+ verbose: boolean;
4
+ canisters: Record<string, string>;
5
+ status: 'pending' | 'running' | 'syntax-error' | 'error' | 'success';
6
+ warnings: string[];
7
+ errorChecker: ErrorChecker;
8
+ aborted: boolean;
9
+ controllers: Map<string, AbortController>;
10
+ currentRun: Promise<any> | undefined;
11
+ constructor({ verbose, canisters, errorChecker }: {
12
+ verbose: boolean;
13
+ canisters: Record<string, string>;
14
+ errorChecker: ErrorChecker;
15
+ });
16
+ reset(): void;
17
+ abortCurrent(): Promise<void>;
18
+ run(onProgress: () => void): Promise<void>;
19
+ getOutput(): string;
20
+ }