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.
- package/CHANGELOG.md +9 -0
- package/bundle/cli.tgz +0 -0
- package/check-requirements.ts +1 -1
- package/cli.ts +29 -5
- package/commands/bench.ts +1 -1
- package/commands/install/install-all.ts +28 -6
- package/commands/install/install-dep.ts +5 -4
- package/commands/install/install-deps.ts +3 -2
- package/commands/install/install-local-dep.ts +11 -5
- package/commands/install/install-mops-dep.ts +8 -5
- package/commands/replica.ts +33 -3
- package/commands/sources.ts +1 -1
- package/commands/sync.ts +4 -19
- package/commands/test/mmf1.ts +4 -0
- package/commands/test/reporters/silent-reporter.ts +22 -4
- package/commands/test/test.ts +74 -10
- package/commands/watch/deployer.ts +155 -0
- package/commands/watch/error-checker.ts +87 -0
- package/commands/watch/generator.ts +99 -0
- package/commands/watch/globMoFiles.ts +16 -0
- package/commands/watch/parseDfxJson.ts +64 -0
- package/commands/watch/tester.ts +81 -0
- package/commands/watch/warning-checker.ts +133 -0
- package/commands/watch/watch.ts +90 -0
- package/declarations/main/main.did +16 -10
- package/declarations/main/main.did.d.ts +19 -10
- package/declarations/main/main.did.js +25 -11
- package/dist/check-requirements.js +1 -1
- package/dist/cli.js +26 -5
- package/dist/commands/bench.js +1 -1
- package/dist/commands/install/install-all.d.ts +2 -1
- package/dist/commands/install/install-all.js +24 -6
- package/dist/commands/install/install-dep.d.ts +2 -1
- package/dist/commands/install/install-dep.js +4 -4
- package/dist/commands/install/install-deps.d.ts +2 -1
- package/dist/commands/install/install-deps.js +2 -2
- package/dist/commands/install/install-local-dep.d.ts +2 -1
- package/dist/commands/install/install-local-dep.js +9 -4
- package/dist/commands/install/install-mops-dep.d.ts +2 -1
- package/dist/commands/install/install-mops-dep.js +7 -5
- package/dist/commands/replica.d.ts +2 -2
- package/dist/commands/replica.js +26 -3
- package/dist/commands/sources.d.ts +1 -1
- package/dist/commands/sources.js +1 -1
- package/dist/commands/sync.js +3 -18
- package/dist/commands/test/mmf1.d.ts +1 -0
- package/dist/commands/test/mmf1.js +3 -0
- package/dist/commands/test/reporters/silent-reporter.d.ts +6 -1
- package/dist/commands/test/reporters/silent-reporter.js +18 -5
- package/dist/commands/test/test.d.ts +1 -1
- package/dist/commands/test/test.js +62 -10
- package/dist/commands/watch/deployer.d.ts +24 -0
- package/dist/commands/watch/deployer.js +125 -0
- package/dist/commands/watch/error-checker.d.ts +13 -0
- package/dist/commands/watch/error-checker.js +76 -0
- package/dist/commands/watch/generator.d.ts +21 -0
- package/dist/commands/watch/generator.js +79 -0
- package/dist/commands/watch/globMoFiles.d.ts +1 -0
- package/dist/commands/watch/globMoFiles.js +14 -0
- package/dist/commands/watch/parseDfxJson.d.ts +2 -0
- package/dist/commands/watch/parseDfxJson.js +22 -0
- package/dist/commands/watch/tester.d.ts +19 -0
- package/dist/commands/watch/tester.js +63 -0
- package/dist/commands/watch/warning-checker.d.ts +20 -0
- package/dist/commands/watch/warning-checker.js +111 -0
- package/dist/commands/watch/watch.d.ts +7 -0
- package/dist/commands/watch/watch.js +79 -0
- package/dist/declarations/main/main.did +16 -10
- package/dist/declarations/main/main.did.d.ts +19 -10
- package/dist/declarations/main/main.did.js +25 -11
- package/dist/helpers/get-moc-path.d.ts +1 -1
- package/dist/helpers/get-moc-path.js +17 -3
- package/dist/helpers/get-moc-version.d.ts +1 -1
- package/dist/helpers/get-moc-version.js +17 -5
- package/dist/integrity.d.ts +20 -0
- package/dist/integrity.js +42 -10
- package/dist/mops.d.ts +2 -1
- package/dist/mops.js +13 -10
- package/dist/package.json +3 -2
- package/dist/parallel.d.ts +1 -1
- package/dist/resolve-packages.js +7 -0
- package/dist/templates/mops-test.yml +4 -2
- package/dist/vessel.d.ts +2 -1
- package/dist/vessel.js +4 -1
- package/helpers/get-moc-path.ts +19 -3
- package/helpers/get-moc-version.ts +17 -5
- package/integrity.ts +56 -13
- package/mops.ts +15 -11
- package/package.json +3 -2
- package/parallel.ts +2 -2
- package/resolve-packages.ts +9 -0
- package/templates/mops-test.yml +4 -2
- package/vessel.ts +5 -1
|
@@ -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,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
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
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 WarningChecker {
|
|
11
|
+
constructor({ verbose, canisters, errorChecker }) {
|
|
12
|
+
this.verbose = false;
|
|
13
|
+
this.canisters = {};
|
|
14
|
+
this.status = 'pending';
|
|
15
|
+
this.warnings = [];
|
|
16
|
+
this.aborted = false;
|
|
17
|
+
this.controllers = new Map();
|
|
18
|
+
this.verbose = verbose;
|
|
19
|
+
this.canisters = canisters;
|
|
20
|
+
this.errorChecker = errorChecker;
|
|
21
|
+
}
|
|
22
|
+
reset() {
|
|
23
|
+
this.status = 'pending';
|
|
24
|
+
this.warnings = [];
|
|
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
|
+
this.status = 'running';
|
|
44
|
+
onProgress();
|
|
45
|
+
let rootDir = getRootDir();
|
|
46
|
+
let mocPath = getMocPath();
|
|
47
|
+
let deps = await sources({ cwd: rootDir });
|
|
48
|
+
let paths = [...Object.values(this.canisters)];
|
|
49
|
+
if (!paths.length) {
|
|
50
|
+
paths = globMoFiles(rootDir);
|
|
51
|
+
}
|
|
52
|
+
this.currentRun = parallel(os.cpus().length, paths, async (file) => {
|
|
53
|
+
let controller = new AbortController();
|
|
54
|
+
let { signal } = controller;
|
|
55
|
+
this.controllers.set(file, controller);
|
|
56
|
+
let { stderr } = await promisify(execFile)(mocPath, ['--check', ...deps.flatMap(x => x.split(' ')), file], { cwd: rootDir, signal }).catch((error) => {
|
|
57
|
+
if (error.code === 'ABORT_ERR') {
|
|
58
|
+
return { stderr: '' };
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
});
|
|
62
|
+
if (stderr) {
|
|
63
|
+
stderr.split('\n').forEach((line) => {
|
|
64
|
+
// ignore deps warnings
|
|
65
|
+
if (line.startsWith('.mops/')) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
else if (line.includes(': warning [')) {
|
|
69
|
+
// better formatting
|
|
70
|
+
let str = line
|
|
71
|
+
.replace(': warning [', `: ${chalk.yellow('warning')} [`)
|
|
72
|
+
.replace(/unused field (\w+)/, `unused field ${chalk.bold('$1')}`)
|
|
73
|
+
.replace(/unused identifier (\w+)/, `unused identifier ${chalk.bold('$1')}`)
|
|
74
|
+
.trim();
|
|
75
|
+
this.warnings.push(str);
|
|
76
|
+
}
|
|
77
|
+
else if (line.startsWith(' ') && this.warnings.length) {
|
|
78
|
+
this.warnings[this.warnings.length - 1] += '\n ' + line;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// console.log('UNKNOWN WARNING', line);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
onProgress();
|
|
85
|
+
}
|
|
86
|
+
this.controllers.delete(file);
|
|
87
|
+
});
|
|
88
|
+
await this.currentRun;
|
|
89
|
+
if (!this.aborted) {
|
|
90
|
+
this.status = this.warnings.length ? 'error' : 'success';
|
|
91
|
+
}
|
|
92
|
+
onProgress();
|
|
93
|
+
}
|
|
94
|
+
getOutput() {
|
|
95
|
+
if (this.status === 'pending') {
|
|
96
|
+
return `Warnings: ${chalk.gray('(pending)')}`;
|
|
97
|
+
}
|
|
98
|
+
if (this.status === 'running') {
|
|
99
|
+
return `Warnings: ${chalk.gray('(running)')}`;
|
|
100
|
+
}
|
|
101
|
+
if (this.status === 'syntax-error') {
|
|
102
|
+
return `Warnings: ${chalk.gray('(errors)')}`;
|
|
103
|
+
}
|
|
104
|
+
let output = '';
|
|
105
|
+
output += `Warnings: ${chalk.bold[this.warnings.length ? 'yellowBright' : 'green'](this.warnings.length)}`;
|
|
106
|
+
if (this.verbose && this.warnings.length) {
|
|
107
|
+
output += `\n ${this.warnings.join('\n ')}`;
|
|
108
|
+
}
|
|
109
|
+
return output;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import debounce from 'debounce';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { ErrorChecker } from './error-checker.js';
|
|
6
|
+
import { WarningChecker } from './warning-checker.js';
|
|
7
|
+
import { getMotokoCanisters, getMotokoCanistersWithDeclarations } from './parseDfxJson.js';
|
|
8
|
+
import { getRootDir } from '../../mops.js';
|
|
9
|
+
import { Tester } from './tester.js';
|
|
10
|
+
import { Generator } from './generator.js';
|
|
11
|
+
import { Deployer } from './deployer.js';
|
|
12
|
+
let ignore = [
|
|
13
|
+
'**/node_modules/**',
|
|
14
|
+
'**/.mops/**',
|
|
15
|
+
'**/.vessel/**',
|
|
16
|
+
'**/.git/**',
|
|
17
|
+
];
|
|
18
|
+
export async function watch(options) {
|
|
19
|
+
let hasOptions = Object.values(options).includes(true);
|
|
20
|
+
if (!hasOptions) {
|
|
21
|
+
options = {
|
|
22
|
+
error: true,
|
|
23
|
+
warning: true,
|
|
24
|
+
test: true,
|
|
25
|
+
generate: true,
|
|
26
|
+
deploy: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
options.error = true;
|
|
30
|
+
let rootDir = getRootDir();
|
|
31
|
+
let canisters = getMotokoCanisters();
|
|
32
|
+
let canistersWithDeclarations = getMotokoCanistersWithDeclarations();
|
|
33
|
+
let errorChecker = new ErrorChecker({ verbose: true, canisters: canisters });
|
|
34
|
+
let warningChecker = new WarningChecker({ errorChecker, verbose: true, canisters: canisters });
|
|
35
|
+
let tester = new Tester({ errorChecker, verbose: true });
|
|
36
|
+
let generator = new Generator({ errorChecker, verbose: true, canisters: canistersWithDeclarations });
|
|
37
|
+
let deployer = new Deployer({ errorChecker, generator, verbose: true, canisters: canisters });
|
|
38
|
+
let watcher = chokidar.watch([
|
|
39
|
+
path.join(rootDir, '**/*.mo'),
|
|
40
|
+
path.join(rootDir, 'mops.toml'),
|
|
41
|
+
], {
|
|
42
|
+
ignored: ignore,
|
|
43
|
+
ignoreInitial: true,
|
|
44
|
+
});
|
|
45
|
+
let run = debounce(async () => {
|
|
46
|
+
errorChecker.reset();
|
|
47
|
+
warningChecker.reset();
|
|
48
|
+
tester.reset();
|
|
49
|
+
generator.reset();
|
|
50
|
+
deployer.reset();
|
|
51
|
+
options.error && await errorChecker.run(print);
|
|
52
|
+
options.warning && warningChecker.run(print);
|
|
53
|
+
options.test && tester.run(print);
|
|
54
|
+
options.generate && await generator.run(print);
|
|
55
|
+
options.deploy && deployer.run(print);
|
|
56
|
+
}, 200);
|
|
57
|
+
let print = () => {
|
|
58
|
+
console.clear();
|
|
59
|
+
process.stdout.write('\x1Bc');
|
|
60
|
+
options.error && console.log(errorChecker.getOutput());
|
|
61
|
+
options.warning && console.log(warningChecker.getOutput());
|
|
62
|
+
options.test && console.log(tester.getOutput());
|
|
63
|
+
options.generate && console.log(generator.getOutput());
|
|
64
|
+
options.deploy && console.log(deployer.getOutput());
|
|
65
|
+
let statuses = [];
|
|
66
|
+
options.error && statuses.push(errorChecker.status);
|
|
67
|
+
options.warning && statuses.push(warningChecker.status);
|
|
68
|
+
options.test && statuses.push(tester.status);
|
|
69
|
+
options.generate && statuses.push(generator.status);
|
|
70
|
+
options.deploy && statuses.push(deployer.status);
|
|
71
|
+
if (statuses.every(status => status !== 'pending' && status !== 'running')) {
|
|
72
|
+
console.log(chalk.dim('-'.repeat(50)));
|
|
73
|
+
console.log(chalk.dim('Waiting for file changes...'));
|
|
74
|
+
console.log(chalk.dim(`Press ${chalk.bold('Ctrl+C')} to exit.`));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
watcher.on('all', run);
|
|
78
|
+
run();
|
|
79
|
+
}
|
|
@@ -91,13 +91,13 @@ type Result_6 =
|
|
|
91
91
|
err: Err;
|
|
92
92
|
ok: vec record {
|
|
93
93
|
PackageName;
|
|
94
|
-
|
|
94
|
+
PackageVersion__1;
|
|
95
95
|
};
|
|
96
96
|
};
|
|
97
97
|
type Result_5 =
|
|
98
98
|
variant {
|
|
99
99
|
err: Err;
|
|
100
|
-
ok:
|
|
100
|
+
ok: PackageVersion__1;
|
|
101
101
|
};
|
|
102
102
|
type Result_4 =
|
|
103
103
|
variant {
|
|
@@ -147,6 +147,7 @@ type Request =
|
|
|
147
147
|
};
|
|
148
148
|
type PublishingId = text;
|
|
149
149
|
type PageCount = nat;
|
|
150
|
+
type PackageVersion__1 = text;
|
|
150
151
|
type PackageVersion = text;
|
|
151
152
|
type PackageSummary__1 =
|
|
152
153
|
record {
|
|
@@ -155,6 +156,7 @@ type PackageSummary__1 =
|
|
|
155
156
|
downloadsInLast30Days: nat;
|
|
156
157
|
downloadsInLast7Days: nat;
|
|
157
158
|
downloadsTotal: nat;
|
|
159
|
+
highestVersion: PackageVersion;
|
|
158
160
|
owner: principal;
|
|
159
161
|
ownerInfo: User;
|
|
160
162
|
publication: PackagePublication;
|
|
@@ -168,6 +170,7 @@ type PackageSummaryWithChanges__1 =
|
|
|
168
170
|
downloadsInLast30Days: nat;
|
|
169
171
|
downloadsInLast7Days: nat;
|
|
170
172
|
downloadsTotal: nat;
|
|
173
|
+
highestVersion: PackageVersion;
|
|
171
174
|
owner: principal;
|
|
172
175
|
ownerInfo: User;
|
|
173
176
|
publication: PackagePublication;
|
|
@@ -181,6 +184,7 @@ type PackageSummaryWithChanges =
|
|
|
181
184
|
downloadsInLast30Days: nat;
|
|
182
185
|
downloadsInLast7Days: nat;
|
|
183
186
|
downloadsTotal: nat;
|
|
187
|
+
highestVersion: PackageVersion;
|
|
184
188
|
owner: principal;
|
|
185
189
|
ownerInfo: User;
|
|
186
190
|
publication: PackagePublication;
|
|
@@ -193,6 +197,7 @@ type PackageSummary =
|
|
|
193
197
|
downloadsInLast30Days: nat;
|
|
194
198
|
downloadsInLast7Days: nat;
|
|
195
199
|
downloadsTotal: nat;
|
|
200
|
+
highestVersion: PackageVersion;
|
|
196
201
|
owner: principal;
|
|
197
202
|
ownerInfo: User;
|
|
198
203
|
publication: PackagePublication;
|
|
@@ -237,6 +242,7 @@ type PackageDetails =
|
|
|
237
242
|
downloadsInLast7Days: nat;
|
|
238
243
|
downloadsTotal: nat;
|
|
239
244
|
fileStats: PackageFileStatsPublic;
|
|
245
|
+
highestVersion: PackageVersion;
|
|
240
246
|
owner: principal;
|
|
241
247
|
ownerInfo: User;
|
|
242
248
|
publication: PackagePublication;
|
|
@@ -303,13 +309,13 @@ type Main =
|
|
|
303
309
|
getDefaultPackages: (text) ->
|
|
304
310
|
(vec record {
|
|
305
311
|
PackageName;
|
|
306
|
-
|
|
312
|
+
PackageVersion__1;
|
|
307
313
|
}) query;
|
|
308
314
|
getDownloadTrendByPackageId: (PackageId) ->
|
|
309
315
|
(vec DownloadsSnapshot__1) query;
|
|
310
316
|
getDownloadTrendByPackageName: (PackageName) ->
|
|
311
317
|
(vec DownloadsSnapshot__1) query;
|
|
312
|
-
getFileHashes: (PackageName,
|
|
318
|
+
getFileHashes: (PackageName, PackageVersion__1) -> (Result_8);
|
|
313
319
|
getFileHashesByPackageIds: (vec PackageId) ->
|
|
314
320
|
(vec record {
|
|
315
321
|
PackageId;
|
|
@@ -318,19 +324,19 @@ type Main =
|
|
|
318
324
|
blob;
|
|
319
325
|
};
|
|
320
326
|
});
|
|
321
|
-
getFileHashesQuery: (PackageName,
|
|
322
|
-
getFileIds: (PackageName,
|
|
327
|
+
getFileHashesQuery: (PackageName, PackageVersion__1) -> (Result_8) query;
|
|
328
|
+
getFileIds: (PackageName, PackageVersion__1) -> (Result_7) query;
|
|
323
329
|
getHighestSemverBatch:
|
|
324
330
|
(vec record {
|
|
325
331
|
PackageName;
|
|
326
|
-
|
|
332
|
+
PackageVersion__1;
|
|
327
333
|
SemverPart;
|
|
328
334
|
}) -> (Result_6) query;
|
|
329
335
|
getHighestVersion: (PackageName) -> (Result_5) query;
|
|
330
336
|
getMostDownloadedPackages: () -> (vec PackageSummary) query;
|
|
331
337
|
getMostDownloadedPackagesIn7Days: () -> (vec PackageSummary) query;
|
|
332
338
|
getNewPackages: () -> (vec PackageSummary) query;
|
|
333
|
-
getPackageDetails: (PackageName,
|
|
339
|
+
getPackageDetails: (PackageName, PackageVersion__1) -> (Result_4) query;
|
|
334
340
|
getPackagesByCategory: () -> (vec record {
|
|
335
341
|
text;
|
|
336
342
|
vec PackageSummary;
|
|
@@ -344,10 +350,10 @@ type Main =
|
|
|
344
350
|
getTotalPackages: () -> (nat) query;
|
|
345
351
|
getUser: (principal) -> (opt User__1) query;
|
|
346
352
|
http_request: (Request) -> (Response) query;
|
|
347
|
-
notifyInstall: (PackageName,
|
|
353
|
+
notifyInstall: (PackageName, PackageVersion__1) -> () oneway;
|
|
348
354
|
notifyInstalls: (vec record {
|
|
349
355
|
PackageName;
|
|
350
|
-
|
|
356
|
+
PackageVersion__1;
|
|
351
357
|
}) -> () oneway;
|
|
352
358
|
restore: (nat) -> ();
|
|
353
359
|
search: (Text, opt nat, opt nat) -> (vec PackageSummary, PageCount) query;
|