ic-mops 1.0.1 → 1.1.0

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 (93) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/bundle/cli.tgz +0 -0
  3. package/check-requirements.ts +1 -1
  4. package/cli.ts +29 -5
  5. package/commands/bench.ts +1 -1
  6. package/commands/install/install-all.ts +28 -6
  7. package/commands/install/install-dep.ts +5 -4
  8. package/commands/install/install-deps.ts +3 -2
  9. package/commands/install/install-local-dep.ts +11 -5
  10. package/commands/install/install-mops-dep.ts +8 -5
  11. package/commands/replica.ts +33 -3
  12. package/commands/sources.ts +1 -1
  13. package/commands/sync.ts +4 -19
  14. package/commands/test/mmf1.ts +4 -0
  15. package/commands/test/reporters/silent-reporter.ts +22 -4
  16. package/commands/test/test.ts +74 -10
  17. package/commands/watch/deployer.ts +155 -0
  18. package/commands/watch/error-checker.ts +87 -0
  19. package/commands/watch/generator.ts +99 -0
  20. package/commands/watch/globMoFiles.ts +16 -0
  21. package/commands/watch/parseDfxJson.ts +64 -0
  22. package/commands/watch/tester.ts +81 -0
  23. package/commands/watch/warning-checker.ts +133 -0
  24. package/commands/watch/watch.ts +90 -0
  25. package/declarations/main/main.did +16 -10
  26. package/declarations/main/main.did.d.ts +19 -10
  27. package/declarations/main/main.did.js +25 -11
  28. package/dist/check-requirements.js +1 -1
  29. package/dist/cli.js +26 -5
  30. package/dist/commands/bench.js +1 -1
  31. package/dist/commands/install/install-all.d.ts +2 -1
  32. package/dist/commands/install/install-all.js +24 -6
  33. package/dist/commands/install/install-dep.d.ts +2 -1
  34. package/dist/commands/install/install-dep.js +4 -4
  35. package/dist/commands/install/install-deps.d.ts +2 -1
  36. package/dist/commands/install/install-deps.js +2 -2
  37. package/dist/commands/install/install-local-dep.d.ts +2 -1
  38. package/dist/commands/install/install-local-dep.js +9 -4
  39. package/dist/commands/install/install-mops-dep.d.ts +2 -1
  40. package/dist/commands/install/install-mops-dep.js +7 -5
  41. package/dist/commands/replica.d.ts +2 -2
  42. package/dist/commands/replica.js +26 -3
  43. package/dist/commands/sources.d.ts +1 -1
  44. package/dist/commands/sources.js +1 -1
  45. package/dist/commands/sync.js +3 -18
  46. package/dist/commands/test/mmf1.d.ts +1 -0
  47. package/dist/commands/test/mmf1.js +3 -0
  48. package/dist/commands/test/reporters/silent-reporter.d.ts +6 -1
  49. package/dist/commands/test/reporters/silent-reporter.js +18 -5
  50. package/dist/commands/test/test.d.ts +1 -1
  51. package/dist/commands/test/test.js +62 -10
  52. package/dist/commands/watch/deployer.d.ts +24 -0
  53. package/dist/commands/watch/deployer.js +125 -0
  54. package/dist/commands/watch/error-checker.d.ts +13 -0
  55. package/dist/commands/watch/error-checker.js +76 -0
  56. package/dist/commands/watch/generator.d.ts +21 -0
  57. package/dist/commands/watch/generator.js +79 -0
  58. package/dist/commands/watch/globMoFiles.d.ts +1 -0
  59. package/dist/commands/watch/globMoFiles.js +14 -0
  60. package/dist/commands/watch/parseDfxJson.d.ts +2 -0
  61. package/dist/commands/watch/parseDfxJson.js +22 -0
  62. package/dist/commands/watch/tester.d.ts +19 -0
  63. package/dist/commands/watch/tester.js +63 -0
  64. package/dist/commands/watch/warning-checker.d.ts +20 -0
  65. package/dist/commands/watch/warning-checker.js +111 -0
  66. package/dist/commands/watch/watch.d.ts +7 -0
  67. package/dist/commands/watch/watch.js +79 -0
  68. package/dist/declarations/main/main.did +16 -10
  69. package/dist/declarations/main/main.did.d.ts +19 -10
  70. package/dist/declarations/main/main.did.js +25 -11
  71. package/dist/helpers/get-moc-path.d.ts +1 -1
  72. package/dist/helpers/get-moc-path.js +17 -3
  73. package/dist/helpers/get-moc-version.d.ts +1 -1
  74. package/dist/helpers/get-moc-version.js +17 -5
  75. package/dist/integrity.d.ts +20 -0
  76. package/dist/integrity.js +42 -10
  77. package/dist/mops.d.ts +2 -1
  78. package/dist/mops.js +13 -10
  79. package/dist/package.json +3 -2
  80. package/dist/parallel.d.ts +1 -1
  81. package/dist/resolve-packages.js +7 -0
  82. package/dist/templates/mops-test.yml +4 -2
  83. package/dist/vessel.d.ts +2 -1
  84. package/dist/vessel.js +4 -1
  85. package/helpers/get-moc-path.ts +19 -3
  86. package/helpers/get-moc-version.ts +17 -5
  87. package/integrity.ts +56 -13
  88. package/mops.ts +15 -11
  89. package/package.json +3 -2
  90. package/parallel.ts +2 -2
  91. package/resolve-packages.ts +9 -0
  92. package/templates/mops-test.yml +4 -2
  93. package/vessel.ts +5 -1
@@ -0,0 +1,155 @@
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
+
6
+ import {ErrorChecker} from './error-checker.js';
7
+ import {Generator} from './generator.js';
8
+ import {parallel} from '../../parallel.js';
9
+ import {getRootDir} from '../../mops.js';
10
+
11
+ export class Deployer {
12
+ verbose = false;
13
+ canisters : Record<string, string> = {};
14
+ status : 'pending' | 'running' | 'syntax-error' | 'dfx-error' | 'error' | 'success' = 'pending';
15
+ errorChecker : ErrorChecker;
16
+ generator : Generator;
17
+ success = 0;
18
+ errors : string[] = [];
19
+ aborted = false;
20
+ controllers = new Map<string, AbortController>();
21
+ currentRun : Promise<any> | undefined;
22
+
23
+ constructor({verbose, canisters, errorChecker, generator} : {verbose : boolean, canisters : Record<string, string>, errorChecker : ErrorChecker, generator : Generator}) {
24
+ this.verbose = verbose;
25
+ this.canisters = canisters;
26
+ this.errorChecker = errorChecker;
27
+ this.generator = generator;
28
+ }
29
+
30
+ reset() {
31
+ this.status = 'pending';
32
+ this.success = 0;
33
+ this.errors = [];
34
+ }
35
+
36
+ async abortCurrent() {
37
+ this.aborted = true;
38
+ for (let controller of this.controllers.values()) {
39
+ controller.abort();
40
+ }
41
+ this.controllers.clear();
42
+ await this.currentRun;
43
+ this.reset();
44
+ this.aborted = false;
45
+ }
46
+
47
+ async run(onProgress : () => void) {
48
+ await this.abortCurrent();
49
+
50
+ if (this.errorChecker.status === 'error') {
51
+ this.status = 'syntax-error';
52
+ onProgress();
53
+ return;
54
+ }
55
+
56
+ if (Object.keys(this.canisters).length === 0) {
57
+ this.status = 'success';
58
+ onProgress();
59
+ return;
60
+ }
61
+
62
+ let rootDir = getRootDir();
63
+
64
+ try {
65
+ execSync('dfx ping', {cwd: rootDir});
66
+ }
67
+ catch (error) {
68
+ this.status = 'dfx-error';
69
+ onProgress();
70
+ return;
71
+ }
72
+
73
+ this.status = 'running';
74
+ onProgress();
75
+
76
+ // create canisters (sequentially to avoid DFX errors)
77
+ let resolve : (() => void) | undefined;
78
+ this.currentRun = new Promise<void>((res) => resolve = res);
79
+ for (let canister of Object.keys(this.canisters)) {
80
+ let controller = new AbortController();
81
+ let {signal} = controller;
82
+ this.controllers.set(canister, controller);
83
+
84
+ await promisify(execFile)('dfx', ['canister', 'create', canister], {cwd: rootDir, signal}).catch((error) => {
85
+ if (error.code === 'ABORT_ERR') {
86
+ return {stderr: ''};
87
+ }
88
+ throw error;
89
+ });
90
+
91
+ this.controllers.delete(canister);
92
+ }
93
+
94
+ resolve?.();
95
+
96
+ if (this.aborted) {
97
+ return;
98
+ }
99
+
100
+ this.currentRun = parallel(os.cpus().length, [...Object.keys(this.canisters)], async (canister) => {
101
+ let controller = new AbortController();
102
+ let {signal} = controller;
103
+ this.controllers.set(canister, controller);
104
+
105
+ // build
106
+ if (this.generator.status !== 'success' || !this.generator.canisters[canister]) {
107
+ await promisify(execFile)('dfx', ['build', canister], {cwd: rootDir, signal}).catch((error) => {
108
+ if (error.code === 'ABORT_ERR') {
109
+ return {stderr: ''};
110
+ }
111
+ throw error;
112
+ });
113
+ }
114
+
115
+ // install
116
+ await promisify(execFile)('dfx', ['canister', 'install', '--mode=auto', canister], {cwd: rootDir, signal}).catch((error) => {
117
+ if (error.code === 'ABORT_ERR') {
118
+ return {stderr: ''};
119
+ }
120
+ throw error;
121
+ });
122
+
123
+ this.success += 1;
124
+ this.controllers.delete(canister);
125
+ onProgress();
126
+ });
127
+
128
+ await this.currentRun;
129
+
130
+ if (!this.aborted) {
131
+ this.status = 'success';
132
+ }
133
+ onProgress();
134
+ }
135
+
136
+ getOutput() : string {
137
+ let get = (v : number) => v.toString();
138
+ let count = (this.status === 'running' ? get : chalk.bold[this.errors.length > 0 ? 'redBright' : 'green'])(this.errors.length || this.success);
139
+
140
+ if (this.status === 'pending') {
141
+ return `Deploy: ${chalk.gray('(pending)')}`;
142
+ }
143
+ if (this.status === 'running') {
144
+ return `Deploy: ${count}/${Object.keys(this.canisters).length} ${chalk.gray('(running)')}`;
145
+ }
146
+ if (this.status === 'syntax-error') {
147
+ return `Deploy: ${chalk.gray('(errors)')}`;
148
+ }
149
+ if (this.status === 'dfx-error') {
150
+ return `Deploy: ${chalk.gray('(dfx not running)')}`;
151
+ }
152
+
153
+ return `Deploy: ${count}`;
154
+ }
155
+ }
@@ -0,0 +1,87 @@
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
+
6
+ import {getMocPath} from '../../helpers/get-moc-path.js';
7
+ import {getRootDir} from '../../mops.js';
8
+ import {sources} from '../sources.js';
9
+ import {parallel} from '../../parallel.js';
10
+ import {globMoFiles} from './globMoFiles.js';
11
+
12
+ export class ErrorChecker {
13
+ verbose = false;
14
+ canisters : Record<string, string> = {};
15
+ status : 'pending' | 'running' | 'error' | 'success' = 'pending';
16
+ errors : string[] = [];
17
+
18
+ constructor({verbose, canisters} : {verbose : boolean, canisters : Record<string, string>}) {
19
+ this.verbose = verbose;
20
+ this.canisters = canisters;
21
+ }
22
+
23
+ reset() {
24
+ this.status = 'pending';
25
+ this.errors = [];
26
+ }
27
+
28
+ async run(onProgress : () => void) {
29
+ this.reset();
30
+
31
+ this.status = 'running';
32
+ onProgress();
33
+
34
+ let rootDir = getRootDir();
35
+ let mocPath = getMocPath();
36
+ let deps = await sources({cwd: rootDir});
37
+
38
+ let paths = [...Object.values(this.canisters)];
39
+ if (!paths.length) {
40
+ paths = globMoFiles(rootDir);
41
+ }
42
+
43
+ await parallel(os.cpus().length, paths, async (file) => {
44
+ try {
45
+ await promisify(execFile)(mocPath, ['--check', ...deps.flatMap(x => x.split(' ')), file], {cwd: rootDir});
46
+ }
47
+ catch (error : any) {
48
+ error.message.split('\n').forEach((line : string) => {
49
+ if (line.match(/: \w+ error \[/)) {
50
+ // better formatting
51
+ let str = line
52
+ .replace(/: (\w+ error) \[/, (_, m1) => `: ${chalk.red(m1)} [`)
53
+ .replace(/unbound type (\w+)/, `unbound type ${chalk.bold('$1')}`)
54
+ .replace(/unbound variable (\w+)/, `unbound variable ${chalk.bold('$1')}`)
55
+ .trim();
56
+ this.errors.push(str);
57
+ }
58
+ else if (line.startsWith(' ') && this.errors.length) {
59
+ this.errors[this.errors.length - 1] += '\n ' + line;
60
+ }
61
+ else {
62
+ // console.log('UNKNOWN ERROR', line);
63
+ }
64
+ });
65
+ onProgress();
66
+ }
67
+ });
68
+
69
+ this.status = this.errors.length ? 'error' : 'success';
70
+ onProgress();
71
+ }
72
+
73
+ getOutput() : string {
74
+ if (this.status === 'pending') {
75
+ return `Errors: ${chalk.gray('(pending)')}`;
76
+ }
77
+ if (this.status === 'running') {
78
+ return `Errors: ${chalk.gray('(running)')}`;
79
+ }
80
+ let output = '';
81
+ output += `Errors: ${chalk.bold[this.errors.length ? 'redBright' : 'green'](this.errors.length)}`;
82
+ if (this.verbose && this.errors.length) {
83
+ output += `\n ${this.errors.join('\n ')}`;
84
+ }
85
+ return output;
86
+ }
87
+ }
@@ -0,0 +1,99 @@
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
+
6
+ import {ErrorChecker} from './error-checker.js';
7
+ import {parallel} from '../../parallel.js';
8
+ import {getRootDir} from '../../mops.js';
9
+
10
+ export class Generator {
11
+ verbose = false;
12
+ canisters : Record<string, string> = {};
13
+ status : 'pending' | 'running' | 'syntax-error' | 'error' | 'success' = 'pending';
14
+ errorChecker : ErrorChecker;
15
+ success = 0;
16
+ errors : string[] = [];
17
+ aborted = false;
18
+ controllers = new Map<string, AbortController>();
19
+ currentRun : Promise<any> | undefined;
20
+
21
+ constructor({verbose, canisters, errorChecker} : {verbose : boolean, canisters : Record<string, string>, errorChecker : ErrorChecker}) {
22
+ this.verbose = verbose;
23
+ this.canisters = canisters;
24
+ this.errorChecker = errorChecker;
25
+ }
26
+
27
+ reset() {
28
+ this.status = 'pending';
29
+ this.success = 0;
30
+ this.errors = [];
31
+ }
32
+
33
+ async abortCurrent() {
34
+ this.aborted = true;
35
+ for (let controller of this.controllers.values()) {
36
+ controller.abort();
37
+ }
38
+ this.controllers.clear();
39
+ await this.currentRun;
40
+ this.reset();
41
+ this.aborted = false;
42
+ }
43
+
44
+ async run(onProgress : () => void) {
45
+ await this.abortCurrent();
46
+
47
+ if (this.errorChecker.status === 'error') {
48
+ this.status = 'syntax-error';
49
+ onProgress();
50
+ return;
51
+ }
52
+
53
+ this.status = 'running';
54
+ onProgress();
55
+
56
+ let rootDir = getRootDir();
57
+
58
+ this.currentRun = parallel(os.cpus().length, [...Object.keys(this.canisters)], async (canister) => {
59
+ let controller = new AbortController();
60
+ let {signal} = controller;
61
+ this.controllers.set(canister, controller);
62
+
63
+ await promisify(execFile)('dfx', ['generate', canister], {cwd: rootDir, signal}).catch((error) => {
64
+ if (error.code === 'ABORT_ERR') {
65
+ return {stderr: ''};
66
+ }
67
+ throw error;
68
+ });
69
+
70
+ this.success += 1;
71
+ this.controllers.delete(canister);
72
+ onProgress();
73
+ });
74
+
75
+ await this.currentRun;
76
+
77
+ if (!this.aborted) {
78
+ this.status = 'success';
79
+ }
80
+ onProgress();
81
+ }
82
+
83
+ getOutput() : string {
84
+ let get = (v : number) => v.toString();
85
+ let count = (this.status === 'running' ? get : chalk.bold[this.errors.length > 0 ? 'redBright' : 'green'])(this.errors.length || this.success);
86
+
87
+ if (this.status === 'pending') {
88
+ return `Generate: ${chalk.gray('(pending)')}`;
89
+ }
90
+ if (this.status === 'running') {
91
+ return `Generate: ${count}/${Object.keys(this.canisters).length} ${chalk.gray('(running)')}`;
92
+ }
93
+ if (this.status === 'syntax-error') {
94
+ return `Generate: ${chalk.gray('(errors)')}`;
95
+ }
96
+
97
+ return `Generate: ${count}`;
98
+ }
99
+ }
@@ -0,0 +1,16 @@
1
+ import {globSync} from 'glob';
2
+
3
+ let globConfig = {
4
+ nocase: true,
5
+ ignore: [
6
+ '**/node_modules/**',
7
+ '**/.mops/**',
8
+ '**/.vessel/**',
9
+ '**/.git/**',
10
+ ],
11
+ };
12
+
13
+ export function globMoFiles(rootDir : string) {
14
+ // return globSync('{src,test?(s)}/**/*.mo', {cwd: rootDir, ...globConfig});
15
+ return globSync('src/**/*.mo', {cwd: rootDir, ...globConfig});
16
+ }
@@ -0,0 +1,64 @@
1
+ import {existsSync, readFileSync} from 'node:fs';
2
+ import path from 'node:path';
3
+ import {getRootDir} from '../../mops.js';
4
+
5
+ type DfxConfig = {
6
+ $schema : string;
7
+ version : number;
8
+ canisters : {
9
+ [key : string] : {
10
+ type : 'motoko' | 'assets';
11
+ main ?: string;
12
+ specified_id ?: string;
13
+ declarations ?: {
14
+ output : string;
15
+ node_compatibility : boolean;
16
+ };
17
+ build ?: string[];
18
+ frontend ?: {
19
+ entrypoint : string;
20
+ };
21
+ source ?: string[];
22
+ remote ?: {
23
+ id : {
24
+ ic : string;
25
+ staging : string;
26
+ };
27
+ };
28
+ };
29
+ };
30
+ defaults : {
31
+ build : {
32
+ packtool : string;
33
+ };
34
+ };
35
+ dfx : string;
36
+ networks : {
37
+ [key : string] : {
38
+ type : string;
39
+ providers : string[];
40
+ };
41
+ };
42
+ };
43
+
44
+ function readDfxJson() : DfxConfig | Record<string, never> {
45
+ let dfxJsonPath = path.resolve(getRootDir(), 'dfx.json');
46
+ if (!existsSync(dfxJsonPath)) {
47
+ return {};
48
+ }
49
+ return JSON.parse(readFileSync(dfxJsonPath, 'utf8')) as DfxConfig;
50
+ }
51
+
52
+ export function getMotokoCanisters() : Record<string, string> {
53
+ let dfxJson = readDfxJson();
54
+ return Object.fromEntries(Object.entries(dfxJson.canisters)
55
+ .filter(([_, canister]) => canister.type === 'motoko')
56
+ .map(([name, canister]) => [name, canister.main ?? '']));
57
+ }
58
+
59
+ export function getMotokoCanistersWithDeclarations() : Record<string, string> {
60
+ let dfxJson = readDfxJson();
61
+ return Object.fromEntries(Object.entries(dfxJson.canisters)
62
+ .filter(([_, canister]) => canister.type === 'motoko' && canister.declarations)
63
+ .map(([name, canister]) => [name, canister.main ?? '']));
64
+ }
@@ -0,0 +1,81 @@
1
+ import chalk from 'chalk';
2
+ import {readConfig} from '../../mops.js';
3
+ import {ErrorChecker} from './error-checker.js';
4
+ import {testWithReporter} from '../test/test.js';
5
+ import {SilentReporter} from '../test/reporters/silent-reporter.js';
6
+
7
+ export class Tester {
8
+ verbose = false;
9
+ status : 'pending' | 'running' | 'syntax-error' | 'error' | 'success' = 'pending';
10
+ errorChecker : ErrorChecker;
11
+ reporter = new SilentReporter(false);
12
+ aborted = false;
13
+ controller = new AbortController();
14
+ currentRun : Promise<any> | undefined;
15
+
16
+ constructor({verbose, errorChecker} : {verbose : boolean, errorChecker : ErrorChecker}) {
17
+ this.verbose = verbose;
18
+ this.errorChecker = errorChecker;
19
+ }
20
+
21
+ reset() {
22
+ this.status = 'pending';
23
+ }
24
+
25
+ async abortCurrent() {
26
+ this.aborted = true;
27
+ this.controller.abort();
28
+ await this.currentRun;
29
+ this.reset();
30
+ this.aborted = false;
31
+ }
32
+
33
+ async run(onProgress : () => void) {
34
+ await this.abortCurrent();
35
+
36
+ if (this.errorChecker.status === 'error') {
37
+ this.status = 'syntax-error';
38
+ onProgress();
39
+ return;
40
+ }
41
+
42
+ this.status = 'running';
43
+ onProgress();
44
+
45
+ this.reporter = new SilentReporter(false, onProgress);
46
+ this.controller = new AbortController();
47
+
48
+ let config = readConfig();
49
+
50
+ this.currentRun = testWithReporter(this.reporter, '', 'interpreter', config.toolchain?.['pocket-ic'] ? 'pocket-ic' : 'dfx', true, this.controller.signal);
51
+ await this.currentRun;
52
+
53
+ if (!this.aborted) {
54
+ this.status = this.reporter.failed > 0 ? 'error' : 'success';
55
+ }
56
+
57
+ onProgress();
58
+ }
59
+
60
+ getOutput() : string {
61
+ let get = (v : number) => v.toString();
62
+ let count = (this.status === 'running' ? get : chalk.bold[this.reporter.failed > 0 ? 'redBright' : 'green'])(this.reporter.failedFiles || this.reporter.passedFiles);
63
+
64
+ if (this.status === 'pending') {
65
+ return `Tests: ${chalk.gray('(pending)')}`;
66
+ }
67
+ if (this.status === 'running') {
68
+ return `Tests: ${count}/${this.reporter.total} ${chalk.gray('(running)')}`;
69
+ }
70
+ if (this.status === 'syntax-error') {
71
+ return `Tests: ${chalk.gray('(errors)')}`;
72
+ }
73
+
74
+ let output = '';
75
+ output += `Tests: ${count}`;
76
+ if (this.reporter.errorOutput) {
77
+ output += '\n' + this.reporter.errorOutput;
78
+ }
79
+ return output;
80
+ }
81
+ }
@@ -0,0 +1,133 @@
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
+
6
+ import {getMocPath} from '../../helpers/get-moc-path.js';
7
+ import {getRootDir} from '../../mops.js';
8
+ import {sources} from '../sources.js';
9
+ import {ErrorChecker} from './error-checker.js';
10
+ import {parallel} from '../../parallel.js';
11
+ import {globMoFiles} from './globMoFiles.js';
12
+
13
+ export class WarningChecker {
14
+ verbose = false;
15
+ canisters : Record<string, string> = {};
16
+ status : 'pending' | 'running' | 'syntax-error' | 'error' | 'success' = 'pending';
17
+ warnings : string[] = [];
18
+ errorChecker : ErrorChecker;
19
+ aborted = false;
20
+ controllers = new Map<string, AbortController>();
21
+ currentRun : Promise<any> | undefined;
22
+
23
+ constructor({verbose, canisters, errorChecker} : {verbose : boolean, canisters : Record<string, string>, errorChecker : ErrorChecker}) {
24
+ this.verbose = verbose;
25
+ this.canisters = canisters;
26
+ this.errorChecker = errorChecker;
27
+ }
28
+
29
+ reset() {
30
+ this.status = 'pending';
31
+ this.warnings = [];
32
+ }
33
+
34
+ async abortCurrent() {
35
+ this.aborted = true;
36
+ for (let controller of this.controllers.values()) {
37
+ controller.abort();
38
+ }
39
+ this.controllers.clear();
40
+ await this.currentRun;
41
+ this.reset();
42
+ this.aborted = false;
43
+ }
44
+
45
+ async run(onProgress : () => void) {
46
+ await this.abortCurrent();
47
+
48
+ if (this.errorChecker.status === 'error') {
49
+ this.status = 'syntax-error';
50
+ onProgress();
51
+ return;
52
+ }
53
+
54
+ this.status = 'running';
55
+ onProgress();
56
+
57
+ let rootDir = getRootDir();
58
+ let mocPath = getMocPath();
59
+ let deps = await sources({cwd: rootDir});
60
+
61
+ let paths = [...Object.values(this.canisters)];
62
+ if (!paths.length) {
63
+ paths = globMoFiles(rootDir);
64
+ }
65
+
66
+ this.currentRun = parallel(os.cpus().length, paths, async (file) => {
67
+ let controller = new AbortController();
68
+ let {signal} = controller;
69
+ this.controllers.set(file, controller);
70
+
71
+ let {stderr} = await promisify(execFile)(mocPath, ['--check', ...deps.flatMap(x => x.split(' ')), file], {cwd: rootDir, signal}).catch((error) => {
72
+ if (error.code === 'ABORT_ERR') {
73
+ return {stderr: ''};
74
+ }
75
+ throw error;
76
+ });
77
+
78
+ if (stderr) {
79
+ stderr.split('\n').forEach((line) => {
80
+ // ignore deps warnings
81
+ if (line.startsWith('.mops/')) {
82
+ return;
83
+ }
84
+ else if (line.includes(': warning [')) {
85
+ // better formatting
86
+ let str = line
87
+ .replace(': warning [', `: ${chalk.yellow('warning')} [`)
88
+ .replace(/unused field (\w+)/, `unused field ${chalk.bold('$1')}`)
89
+ .replace(/unused identifier (\w+)/, `unused identifier ${chalk.bold('$1')}`)
90
+ .trim();
91
+ this.warnings.push(str);
92
+ }
93
+ else if (line.startsWith(' ') && this.warnings.length) {
94
+ this.warnings[this.warnings.length - 1] += '\n ' + line;
95
+ }
96
+ else {
97
+ // console.log('UNKNOWN WARNING', line);
98
+ }
99
+ });
100
+ onProgress();
101
+ }
102
+
103
+ this.controllers.delete(file);
104
+ });
105
+
106
+ await this.currentRun;
107
+
108
+ if (!this.aborted) {
109
+ this.status = this.warnings.length ? 'error' : 'success';
110
+ }
111
+
112
+ onProgress();
113
+ }
114
+
115
+ getOutput() : string {
116
+ if (this.status === 'pending') {
117
+ return `Warnings: ${chalk.gray('(pending)')}`;
118
+ }
119
+ if (this.status === 'running') {
120
+ return `Warnings: ${chalk.gray('(running)')}`;
121
+ }
122
+ if (this.status === 'syntax-error') {
123
+ return `Warnings: ${chalk.gray('(errors)')}`;
124
+ }
125
+
126
+ let output = '';
127
+ output += `Warnings: ${chalk.bold[this.warnings.length ? 'yellowBright' : 'green'](this.warnings.length)}`;
128
+ if (this.verbose && this.warnings.length) {
129
+ output += `\n ${this.warnings.join('\n ')}`;
130
+ }
131
+ return output;
132
+ }
133
+ }