ic-mops 2.2.1 → 2.3.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.
- package/CHANGELOG.md +13 -0
- package/RELEASE.md +9 -1
- package/cli.ts +25 -2
- package/commands/build.ts +2 -10
- package/commands/check-stable.ts +177 -0
- package/commands/check.ts +53 -6
- package/dist/bin/mops.js +1 -1
- package/dist/cli.js +22 -2
- package/dist/commands/build.js +2 -5
- package/dist/commands/check-stable.d.ts +14 -0
- package/dist/commands/check-stable.js +95 -0
- package/dist/commands/check.js +41 -6
- package/dist/fix-dist.js +5 -0
- package/dist/helpers/resolve-canisters.d.ts +7 -0
- package/dist/helpers/resolve-canisters.js +31 -0
- package/dist/package.json +2 -2
- package/dist/tests/build.test.js +18 -0
- package/dist/tests/check-fix.test.js +17 -0
- package/dist/tests/check-stable.test.d.ts +1 -0
- package/dist/tests/check-stable.test.js +51 -0
- package/dist/tests/check.test.js +54 -1
- package/dist/tests/moc-args.test.js +3 -3
- package/dist/types.d.ts +5 -1
- package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
- package/fix-dist.ts +9 -0
- package/helpers/resolve-canisters.ts +52 -0
- package/package.json +2 -2
- package/tests/README.md +16 -0
- package/tests/__snapshots__/check-fix.test.ts.snap +16 -10
- package/tests/__snapshots__/check-stable.test.ts.snap +29 -0
- package/tests/__snapshots__/check.test.ts.snap +26 -16
- package/tests/build/no-dfx/mops.toml +3 -0
- package/tests/build/no-dfx/src/Main.mo +1 -1
- package/tests/build.test.ts +24 -0
- package/tests/check/canisters/Ok.mo +5 -0
- package/tests/check/canisters/mops.toml +8 -0
- package/tests/check/canisters-error/Error.mo +7 -0
- package/tests/check/canisters-error/mops.toml +8 -0
- package/tests/check/canisters-moc-args/Warning.mo +5 -0
- package/tests/check/canisters-moc-args/mops.toml +8 -0
- package/tests/check/deployed-compatible/main.mo +4 -0
- package/tests/check/deployed-compatible/mops.toml +11 -0
- package/tests/check/deployed-compatible/old.mo +3 -0
- package/tests/check/deployed-compile-error/Error.mo +7 -0
- package/tests/check/deployed-compile-error/mops.toml +11 -0
- package/tests/check/deployed-compile-error/old.mo +3 -0
- package/tests/check/deployed-missing-error/Ok.mo +5 -0
- package/tests/check/deployed-missing-error/mops.toml +11 -0
- package/tests/check/deployed-missing-skip/Ok.mo +5 -0
- package/tests/check/deployed-missing-skip/mops.toml +12 -0
- package/tests/check/error/Error.mo +1 -1
- package/tests/check/error/mops.toml +5 -2
- package/tests/check/fix/M0223.mo +1 -1
- package/tests/check/fix/M0236.mo +1 -1
- package/tests/check/fix/M0237.mo +1 -1
- package/tests/check/fix/Ok.mo +1 -1
- package/tests/check/fix/fix-with-error.mo +9 -0
- package/tests/check/fix/fix-with-warning.mo +8 -0
- package/tests/check/fix/mops.toml +3 -0
- package/tests/check/fix/transitive-main.mo +1 -1
- package/tests/check/moc-args/Warning.mo +1 -1
- package/tests/check/moc-args/mops.toml +4 -1
- package/tests/check/success/Ok.mo +1 -1
- package/tests/check/success/Warning.mo +1 -1
- package/tests/check/success/mops.toml +5 -2
- package/tests/check-fix.test.ts +25 -0
- package/tests/check-stable/compatible/mops.toml +8 -0
- package/tests/check-stable/compatible/new.mo +4 -0
- package/tests/check-stable/compatible/old.mo +3 -0
- package/tests/check-stable/incompatible/mops.toml +8 -0
- package/tests/check-stable/incompatible/new.mo +3 -0
- package/tests/check-stable/incompatible/old.mo +4 -0
- package/tests/check-stable/subdirectory/.old/src/main.mo +3 -0
- package/tests/check-stable/subdirectory/mops.toml +8 -0
- package/tests/check-stable/subdirectory/src/main.mo +4 -0
- package/tests/check-stable.test.ts +56 -0
- package/tests/check.test.ts +63 -1
- package/tests/moc-args.test.ts +3 -3
- package/types.ts +5 -1
- package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/wasm/pkg/web/wasm_bg.wasm +0 -0
- package/.DS_Store +0 -0
- package/bundle/bench/bench-canister.mo +0 -130
- package/bundle/bench/user-bench.mo +0 -10
- package/bundle/bin/moc-wrapper.sh +0 -40
- package/bundle/bin/mops.js +0 -3
- package/bundle/cli.js +0 -1569
- package/bundle/cli.tgz +0 -0
- package/bundle/declarations/bench/bench.did +0 -30
- package/bundle/declarations/bench/bench.did.d.ts +0 -33
- package/bundle/declarations/bench/bench.did.js +0 -30
- package/bundle/declarations/bench/index.d.ts +0 -50
- package/bundle/declarations/bench/index.js +0 -40
- package/bundle/declarations/main/index.d.ts +0 -50
- package/bundle/declarations/main/index.js +0 -40
- package/bundle/declarations/main/main.did +0 -428
- package/bundle/declarations/main/main.did.d.ts +0 -348
- package/bundle/declarations/main/main.did.js +0 -406
- package/bundle/declarations/storage/index.d.ts +0 -50
- package/bundle/declarations/storage/index.js +0 -30
- package/bundle/declarations/storage/storage.did +0 -46
- package/bundle/declarations/storage/storage.did.d.ts +0 -40
- package/bundle/declarations/storage/storage.did.js +0 -38
- package/bundle/default-stylesheet.css +0 -415
- package/bundle/package.json +0 -36
- package/bundle/templates/README.md +0 -13
- package/bundle/templates/licenses/Apache-2.0 +0 -202
- package/bundle/templates/licenses/Apache-2.0-NOTICE +0 -13
- package/bundle/templates/licenses/MIT +0 -21
- package/bundle/templates/mops-publish.yml +0 -17
- package/bundle/templates/mops-test.yml +0 -24
- package/bundle/templates/src/lib.mo +0 -15
- package/bundle/templates/test/lib.test.mo +0 -4
- package/bundle/wasm_bg.wasm +0 -0
- package/bundle/xhr-sync-worker.js +0 -51
- package/dist/wasm/pkg/bundler/package.json +0 -20
- package/dist/wasm/pkg/bundler/wasm.d.ts +0 -3
- package/dist/wasm/pkg/bundler/wasm.js +0 -5
- package/dist/wasm/pkg/bundler/wasm_bg.js +0 -93
- package/dist/wasm/pkg/bundler/wasm_bg.wasm +0 -0
- package/dist/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
- package/tests/build/success/.dfx/local/canister_ids.json +0 -17
- package/tests/build/success/.dfx/local/canisters/bar/bar.did +0 -3
- package/tests/build/success/.dfx/local/canisters/bar/bar.most +0 -4
- package/tests/build/success/.dfx/local/canisters/bar/bar.wasm +0 -0
- package/tests/build/success/.dfx/local/canisters/bar/constructor.did +0 -3
- package/tests/build/success/.dfx/local/canisters/bar/index.js +0 -42
- package/tests/build/success/.dfx/local/canisters/bar/init_args.txt +0 -1
- package/tests/build/success/.dfx/local/canisters/bar/service.did +0 -3
- package/tests/build/success/.dfx/local/canisters/bar/service.did.d.ts +0 -7
- package/tests/build/success/.dfx/local/canisters/bar/service.did.js +0 -4
- package/tests/build/success/.dfx/local/canisters/foo/constructor.did +0 -3
- package/tests/build/success/.dfx/local/canisters/foo/foo.did +0 -3
- package/tests/build/success/.dfx/local/canisters/foo/foo.most +0 -4
- package/tests/build/success/.dfx/local/canisters/foo/foo.wasm +0 -0
- package/tests/build/success/.dfx/local/canisters/foo/index.js +0 -42
- package/tests/build/success/.dfx/local/canisters/foo/init_args.txt +0 -1
- package/tests/build/success/.dfx/local/canisters/foo/service.did +0 -3
- package/tests/build/success/.dfx/local/canisters/foo/service.did.d.ts +0 -7
- package/tests/build/success/.dfx/local/canisters/foo/service.did.js +0 -4
- package/tests/build/success/.dfx/local/lsp/ucwa4-rx777-77774-qaada-cai.did +0 -3
- package/tests/build/success/.dfx/local/lsp/ulvla-h7777-77774-qaacq-cai.did +0 -3
- package/tests/build/success/.dfx/local/network-id +0 -4
- package/wasm/Cargo.lock +0 -1475
- package/wasm/pkg/bundler/package.json +0 -20
- package/wasm/pkg/bundler/wasm.d.ts +0 -3
- package/wasm/pkg/bundler/wasm.js +0 -5
- package/wasm/pkg/bundler/wasm_bg.js +0 -93
- package/wasm/pkg/bundler/wasm_bg.wasm +0 -0
- package/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { cliError } from "../error.js";
|
|
2
|
+
export function resolveCanisterConfigs(config) {
|
|
3
|
+
if (!config.canisters) {
|
|
4
|
+
return {};
|
|
5
|
+
}
|
|
6
|
+
return Object.fromEntries(Object.entries(config.canisters).map(([name, c]) => typeof c === "string" ? [name, { main: c }] : [name, c]));
|
|
7
|
+
}
|
|
8
|
+
export function resolveCanisterEntrypoints(config) {
|
|
9
|
+
const canisters = resolveCanisterConfigs(config);
|
|
10
|
+
return Object.values(canisters)
|
|
11
|
+
.map((c) => c.main)
|
|
12
|
+
.filter((main) => Boolean(main));
|
|
13
|
+
}
|
|
14
|
+
export function resolveSingleCanister(config, canisterName) {
|
|
15
|
+
const canisters = resolveCanisterConfigs(config);
|
|
16
|
+
const names = Object.keys(canisters);
|
|
17
|
+
if (names.length === 0) {
|
|
18
|
+
cliError("No canisters defined in mops.toml [canisters] section");
|
|
19
|
+
}
|
|
20
|
+
if (canisterName) {
|
|
21
|
+
const canister = canisters[canisterName];
|
|
22
|
+
if (!canister) {
|
|
23
|
+
cliError(`Canister '${canisterName}' not found in mops.toml. Available: ${names.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
return { name: canisterName, canister };
|
|
26
|
+
}
|
|
27
|
+
if (names.length > 1) {
|
|
28
|
+
cliError(`Multiple canisters defined in mops.toml. Please specify one: ${names.join(", ")}`);
|
|
29
|
+
}
|
|
30
|
+
return { name: names[0], canister: canisters[names[0]] };
|
|
31
|
+
}
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "bin/mops.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"pic-ic": "0.5.4",
|
|
64
64
|
"pic-js-mops": "0.14.8",
|
|
65
65
|
"prettier": "3.5.3",
|
|
66
|
-
"prettier-plugin-motoko": "0.
|
|
66
|
+
"prettier-plugin-motoko": "0.13.0",
|
|
67
67
|
"promisify-child-process": "4.1.2",
|
|
68
68
|
"prompts": "2.4.2",
|
|
69
69
|
"semver": "7.7.1",
|
package/dist/tests/build.test.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
2
4
|
import path from "path";
|
|
3
5
|
import { cliSnapshot } from "./helpers";
|
|
6
|
+
const distBin = path.resolve(import.meta.dirname, "../dist/bin/mops.js");
|
|
4
7
|
describe("build", () => {
|
|
5
8
|
test("ok", async () => {
|
|
6
9
|
const cwd = path.join(import.meta.dirname, "build/success");
|
|
@@ -15,4 +18,19 @@ describe("build", () => {
|
|
|
15
18
|
expect((await cliSnapshot(["build", "bar"], { cwd }, 1)).stderr).toMatch("Candid compatibility check failed for canister bar");
|
|
16
19
|
expect((await cliSnapshot(["build", "foo", "bar"], { cwd }, 1)).stderr).toMatch("Candid compatibility check failed for canister bar");
|
|
17
20
|
});
|
|
21
|
+
// Regression: bin/mops.js must route through environments/nodejs/cli.js
|
|
22
|
+
// so that setWasmBindings() is called before any command runs.
|
|
23
|
+
// The dev entry point (npm run mops) uses tsx and always worked;
|
|
24
|
+
// this test exercises the compiled dist binary (same path as npm i -g ic-mops).
|
|
25
|
+
const hasDistBin = existsSync(distBin);
|
|
26
|
+
(hasDistBin ? test : test.skip)("wasm bindings initialized via dist entry point", async () => {
|
|
27
|
+
const cwd = path.join(import.meta.dirname, "build/success");
|
|
28
|
+
const result = await execa("node", [distBin, "build", "foo"], {
|
|
29
|
+
cwd,
|
|
30
|
+
stdio: "pipe",
|
|
31
|
+
reject: false,
|
|
32
|
+
});
|
|
33
|
+
expect(result.stderr).not.toContain("Wasm bindings have not been set");
|
|
34
|
+
expect(result.exitCode).toBe(0);
|
|
35
|
+
});
|
|
18
36
|
});
|
|
@@ -86,4 +86,21 @@ describe("check --fix", () => {
|
|
|
86
86
|
expect(result.stdout).toContain("Attempting to fix files");
|
|
87
87
|
expect(result.stdout).toContain("No fixes were needed");
|
|
88
88
|
});
|
|
89
|
+
test("fix with remaining warnings", async () => {
|
|
90
|
+
const runFilePath = copyFixture("fix-with-warning.mo");
|
|
91
|
+
const result = await cli(["check", runFilePath, "--fix", "--", warningFlags], { cwd: fixDir });
|
|
92
|
+
expect(result.exitCode).toBe(0);
|
|
93
|
+
expect(result.stdout).toContain("1 fix applied");
|
|
94
|
+
expect(result.stdout).toMatch(/✓/);
|
|
95
|
+
expect(result.stderr).toMatch(/warning \[M0194\]/);
|
|
96
|
+
expect(result.stderr).toMatch(/unused identifier/);
|
|
97
|
+
});
|
|
98
|
+
test("fix with remaining errors", async () => {
|
|
99
|
+
const runFilePath = copyFixture("fix-with-error.mo");
|
|
100
|
+
const result = await cli(["check", runFilePath, "--fix", "--", warningFlags], { cwd: fixDir });
|
|
101
|
+
expect(result.exitCode).toBe(1);
|
|
102
|
+
expect(result.stdout).toContain("1 fix applied");
|
|
103
|
+
expect(result.stderr).toMatch(/error/i);
|
|
104
|
+
expect(result.stdout).not.toMatch(/✓ run/);
|
|
105
|
+
});
|
|
89
106
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { cli, cliSnapshot } from "./helpers";
|
|
6
|
+
describe("check-stable", () => {
|
|
7
|
+
test("compatible upgrade from .mo file", async () => {
|
|
8
|
+
const cwd = path.join(import.meta.dirname, "check-stable/compatible");
|
|
9
|
+
await cliSnapshot(["check-stable", "old.mo"], { cwd }, 0);
|
|
10
|
+
});
|
|
11
|
+
test("incompatible upgrade from .mo file", async () => {
|
|
12
|
+
const cwd = path.join(import.meta.dirname, "check-stable/incompatible");
|
|
13
|
+
const result = await cliSnapshot(["check-stable", "old.mo"], { cwd }, 1);
|
|
14
|
+
expect(result.stderr).toMatch(/compatibility/i);
|
|
15
|
+
});
|
|
16
|
+
test("compatible upgrade with verbose", async () => {
|
|
17
|
+
const cwd = path.join(import.meta.dirname, "check-stable/compatible");
|
|
18
|
+
const result = await cli(["check-stable", "old.mo", "--verbose"], { cwd });
|
|
19
|
+
expect(result.exitCode).toBe(0);
|
|
20
|
+
expect(result.stdout).toMatch(/Generating stable types for old\.mo/);
|
|
21
|
+
expect(result.stdout).toMatch(/Generating stable types for new\.mo/);
|
|
22
|
+
expect(result.stdout).toMatch(/--stable-compatible/);
|
|
23
|
+
expect(result.stdout).toMatch(/Stable compatibility check passed/);
|
|
24
|
+
});
|
|
25
|
+
test("old file in subdirectory (.old/src/ pattern)", async () => {
|
|
26
|
+
const cwd = path.join(import.meta.dirname, "check-stable/subdirectory");
|
|
27
|
+
const result = await cli(["check-stable", ".old/src/main.mo"], { cwd });
|
|
28
|
+
expect(result.exitCode).toBe(0);
|
|
29
|
+
expect(result.stdout).toMatch(/Stable compatibility check passed/);
|
|
30
|
+
});
|
|
31
|
+
test("compatible upgrade from .most file", async () => {
|
|
32
|
+
const cwd = path.join(import.meta.dirname, "check-stable/compatible");
|
|
33
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), "mops-test-most-"));
|
|
34
|
+
try {
|
|
35
|
+
const mostPath = path.join(tempDir, "old.most");
|
|
36
|
+
await writeFile(mostPath, "actor {\n stable var counter : Nat\n};\n");
|
|
37
|
+
const result = await cli(["check-stable", mostPath], { cwd });
|
|
38
|
+
expect(result.exitCode).toBe(0);
|
|
39
|
+
expect(result.stdout).toMatch(/Stable compatibility check passed/);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
test("errors when old file does not exist", async () => {
|
|
46
|
+
const cwd = path.join(import.meta.dirname, "check-stable/compatible");
|
|
47
|
+
const result = await cli(["check-stable", "nonexistent.mo"], { cwd });
|
|
48
|
+
expect(result.exitCode).toBe(1);
|
|
49
|
+
expect(result.stderr).toMatch(/File not found/);
|
|
50
|
+
});
|
|
51
|
+
});
|
package/dist/tests/check.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "@jest/globals";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { cliSnapshot } from "./helpers";
|
|
3
|
+
import { cli, cliSnapshot } from "./helpers";
|
|
4
4
|
describe("check", () => {
|
|
5
5
|
test("ok", async () => {
|
|
6
6
|
const cwd = path.join(import.meta.dirname, "check/success");
|
|
@@ -34,4 +34,57 @@ describe("check", () => {
|
|
|
34
34
|
const cwd = path.join(import.meta.dirname, "check/moc-args");
|
|
35
35
|
await cliSnapshot(["check", "Warning.mo"], { cwd }, 1);
|
|
36
36
|
});
|
|
37
|
+
test("no args falls back to [canisters] entrypoints", async () => {
|
|
38
|
+
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
39
|
+
await cliSnapshot(["check"], { cwd }, 0);
|
|
40
|
+
});
|
|
41
|
+
test("[moc] args applied when using canister fallback", async () => {
|
|
42
|
+
const cwd = path.join(import.meta.dirname, "check/canisters-moc-args");
|
|
43
|
+
const result = await cli(["check"], { cwd });
|
|
44
|
+
expect(result.exitCode).toBe(1);
|
|
45
|
+
expect(result.stderr).toMatch(/warning \[M0194\]/);
|
|
46
|
+
});
|
|
47
|
+
test("canister entrypoint with errors", async () => {
|
|
48
|
+
const cwd = path.join(import.meta.dirname, "check/canisters-error");
|
|
49
|
+
const result = await cli(["check"], { cwd });
|
|
50
|
+
expect(result.exitCode).toBe(1);
|
|
51
|
+
expect(result.stderr).toMatch(/error/i);
|
|
52
|
+
});
|
|
53
|
+
test("--fix with canister fallback", async () => {
|
|
54
|
+
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
55
|
+
const result = await cli(["check", "--fix"], { cwd });
|
|
56
|
+
expect(result.exitCode).toBe(0);
|
|
57
|
+
});
|
|
58
|
+
test("deployed: runs stable check when deployed file exists", async () => {
|
|
59
|
+
const cwd = path.join(import.meta.dirname, "check/deployed-compatible");
|
|
60
|
+
const result = await cli(["check"], { cwd });
|
|
61
|
+
expect(result.exitCode).toBe(0);
|
|
62
|
+
expect(result.stdout).toMatch(/Stable compatibility check passed/);
|
|
63
|
+
});
|
|
64
|
+
test("deployed: silently skips when file missing and skipIfMissing", async () => {
|
|
65
|
+
const cwd = path.join(import.meta.dirname, "check/deployed-missing-skip");
|
|
66
|
+
const result = await cli(["check"], { cwd });
|
|
67
|
+
expect(result.exitCode).toBe(0);
|
|
68
|
+
expect(result.stdout).not.toMatch(/stable/i);
|
|
69
|
+
});
|
|
70
|
+
test("deployed: errors when file missing without deployedSkipIfFileMissing", async () => {
|
|
71
|
+
const cwd = path.join(import.meta.dirname, "check/deployed-missing-error");
|
|
72
|
+
const result = await cli(["check"], { cwd });
|
|
73
|
+
expect(result.exitCode).toBe(1);
|
|
74
|
+
expect(result.stderr).toMatch(/Deployed file not found/);
|
|
75
|
+
expect(result.stderr).toMatch(/skipIfMissing/);
|
|
76
|
+
});
|
|
77
|
+
test("--fix runs stable check after fixing", async () => {
|
|
78
|
+
const cwd = path.join(import.meta.dirname, "check/deployed-compatible");
|
|
79
|
+
const result = await cli(["check", "--fix"], { cwd });
|
|
80
|
+
expect(result.exitCode).toBe(0);
|
|
81
|
+
expect(result.stdout).toMatch(/Stable compatibility check passed/);
|
|
82
|
+
});
|
|
83
|
+
test("stable check is skipped when type-checking fails", async () => {
|
|
84
|
+
const cwd = path.join(import.meta.dirname, "check/deployed-compile-error");
|
|
85
|
+
const result = await cli(["check"], { cwd });
|
|
86
|
+
expect(result.exitCode).toBe(1);
|
|
87
|
+
expect(result.stderr).toMatch(/error/i);
|
|
88
|
+
expect(result.stdout).not.toMatch(/Stable compatibility/);
|
|
89
|
+
});
|
|
37
90
|
});
|
|
@@ -6,12 +6,12 @@ describe("moc-args", () => {
|
|
|
6
6
|
const cwd = path.join(import.meta.dirname, "check/moc-args");
|
|
7
7
|
const result = await cli(["moc-args"], { cwd });
|
|
8
8
|
expect(result.exitCode).toBe(0);
|
|
9
|
-
expect(result.stdout).toBe("-Werror");
|
|
9
|
+
expect(result.stdout).toBe("--default-persistent-actors\n-Werror");
|
|
10
10
|
});
|
|
11
|
-
test("prints
|
|
11
|
+
test("prints only global args when no extra [moc] args", async () => {
|
|
12
12
|
const cwd = path.join(import.meta.dirname, "check/success");
|
|
13
13
|
const result = await cli(["moc-args"], { cwd });
|
|
14
14
|
expect(result.exitCode).toBe(0);
|
|
15
|
-
expect(result.stdout).toBe("");
|
|
15
|
+
expect(result.stdout).toBe("--default-persistent-actors");
|
|
16
16
|
});
|
|
17
17
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -32,10 +32,14 @@ export type Config = {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
export type CanisterConfig = {
|
|
35
|
-
main
|
|
35
|
+
main?: string;
|
|
36
36
|
args?: string[];
|
|
37
37
|
candid?: string;
|
|
38
38
|
initArg?: string;
|
|
39
|
+
"check-stable"?: {
|
|
40
|
+
path: string;
|
|
41
|
+
skipIfMissing?: boolean;
|
|
42
|
+
};
|
|
39
43
|
};
|
|
40
44
|
export type Dependencies = Record<string, Dependency>;
|
|
41
45
|
export type Dependency = {
|
|
Binary file
|
|
Binary file
|
package/fix-dist.ts
CHANGED
|
@@ -10,3 +10,12 @@ json.bin.mops = "bin/mops.js";
|
|
|
10
10
|
json.bin["ic-mops"] = "bin/mops.js";
|
|
11
11
|
|
|
12
12
|
writeFileSync("dist/package.json", JSON.stringify(json, null, 2));
|
|
13
|
+
|
|
14
|
+
// Route the npm entry point through the Node.js environment wrapper
|
|
15
|
+
// so setWasmBindings() is called before the CLI runs.
|
|
16
|
+
// The source bin/mops.js imports ../cli.js (needed for the single-file bundle),
|
|
17
|
+
// but dist/ has the full directory structure with environments/nodejs/cli.js.
|
|
18
|
+
writeFileSync(
|
|
19
|
+
"dist/bin/mops.js",
|
|
20
|
+
'#!/usr/bin/env node\n\nimport "../environments/nodejs/cli.js";\n',
|
|
21
|
+
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { CanisterConfig, Config } from "../types.js";
|
|
2
|
+
import { cliError } from "../error.js";
|
|
3
|
+
|
|
4
|
+
export function resolveCanisterConfigs(
|
|
5
|
+
config: Config,
|
|
6
|
+
): Record<string, CanisterConfig> {
|
|
7
|
+
if (!config.canisters) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
return Object.fromEntries(
|
|
11
|
+
Object.entries(config.canisters).map(([name, c]) =>
|
|
12
|
+
typeof c === "string" ? [name, { main: c }] : [name, c],
|
|
13
|
+
),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveCanisterEntrypoints(config: Config): string[] {
|
|
18
|
+
const canisters = resolveCanisterConfigs(config);
|
|
19
|
+
return Object.values(canisters)
|
|
20
|
+
.map((c) => c.main)
|
|
21
|
+
.filter((main): main is string => Boolean(main));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveSingleCanister(
|
|
25
|
+
config: Config,
|
|
26
|
+
canisterName?: string,
|
|
27
|
+
): { name: string; canister: CanisterConfig } {
|
|
28
|
+
const canisters = resolveCanisterConfigs(config);
|
|
29
|
+
const names = Object.keys(canisters);
|
|
30
|
+
|
|
31
|
+
if (names.length === 0) {
|
|
32
|
+
cliError("No canisters defined in mops.toml [canisters] section");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (canisterName) {
|
|
36
|
+
const canister = canisters[canisterName];
|
|
37
|
+
if (!canister) {
|
|
38
|
+
cliError(
|
|
39
|
+
`Canister '${canisterName}' not found in mops.toml. Available: ${names.join(", ")}`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return { name: canisterName, canister };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (names.length > 1) {
|
|
46
|
+
cliError(
|
|
47
|
+
`Multiple canisters defined in mops.toml. Please specify one: ${names.join(", ")}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { name: names[0]!, canister: canisters[names[0]!]! };
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "dist/bin/mops.js",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"pic-ic": "0.5.4",
|
|
85
85
|
"pic-js-mops": "0.14.8",
|
|
86
86
|
"prettier": "3.5.3",
|
|
87
|
-
"prettier-plugin-motoko": "0.
|
|
87
|
+
"prettier-plugin-motoko": "0.13.0",
|
|
88
88
|
"promisify-child-process": "4.1.2",
|
|
89
89
|
"prompts": "2.4.2",
|
|
90
90
|
"semver": "7.7.1",
|
package/tests/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# CLI Test Fixtures
|
|
2
|
+
|
|
3
|
+
Each subdirectory under `check/`, `check-stable/`, etc. is a self-contained test fixture with its own `mops.toml`.
|
|
4
|
+
|
|
5
|
+
## Adding a new fixture
|
|
6
|
+
|
|
7
|
+
1. Create a directory with a `mops.toml` and the `.mo` files your test needs.
|
|
8
|
+
2. Only declare `[dependencies]` if your `.mo` files actually import from them. Unused dependencies cause `moc` to receive `--package` flags pointing to directories that may not exist on CI.
|
|
9
|
+
3. If your fixture declares `[dependencies]`, add a `mops install` step for it in `.github/workflows/ci.yml` under the "Pre-cache" step. Test fixtures' `.mops/` directories are gitignored and don't exist on CI unless explicitly installed.
|
|
10
|
+
4. If your fixture uses a `[toolchain]` moc version that isn't already pre-cached in CI, add a download step for it in the same CI pre-cache block.
|
|
11
|
+
|
|
12
|
+
## Why this matters
|
|
13
|
+
|
|
14
|
+
Jest runs test suites in parallel. Without pre-installation:
|
|
15
|
+
- Multiple workers race to download the same `moc` binary, corrupting the cache.
|
|
16
|
+
- `moc` fails when `--package` flags point to missing `.mops/` directories.
|
|
@@ -4,7 +4,7 @@ exports[`check --fix M0223 1`] = `
|
|
|
4
4
|
"// M0223: Redundant type instantiation
|
|
5
5
|
// The type annotation is not needed when it can be inferred
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
actor {
|
|
8
8
|
public func testM0223() : async () {
|
|
9
9
|
func identity<T>(x : T) : T = x;
|
|
10
10
|
let varArray : [var Nat] = [var 1];
|
|
@@ -18,7 +18,8 @@ persistent actor {
|
|
|
18
18
|
exports[`check --fix M0223: fix output 1`] = `
|
|
19
19
|
"Fixed run/M0223.mo (1 fix: M0223)
|
|
20
20
|
|
|
21
|
-
✓ 1 fix applied to 1 file
|
|
21
|
+
✓ 1 fix applied to 1 file
|
|
22
|
+
✓ <TEST_DIR>/check/fix/run/M0223.mo"
|
|
22
23
|
`;
|
|
23
24
|
|
|
24
25
|
exports[`check --fix M0236 1`] = `
|
|
@@ -27,7 +28,7 @@ exports[`check --fix M0236 1`] = `
|
|
|
27
28
|
import List "mo:core/List";
|
|
28
29
|
import Nat "mo:core/Nat";
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
actor {
|
|
31
32
|
public func testM0236() : async () {
|
|
32
33
|
let list = List.fromArray<Nat>([1, 2, 3]);
|
|
33
34
|
list.sortInPlace();
|
|
@@ -39,7 +40,8 @@ persistent actor {
|
|
|
39
40
|
exports[`check --fix M0236: fix output 1`] = `
|
|
40
41
|
"Fixed run/M0236.mo (1 fix: M0236)
|
|
41
42
|
|
|
42
|
-
✓ 1 fix applied to 1 file
|
|
43
|
+
✓ 1 fix applied to 1 file
|
|
44
|
+
✓ <TEST_DIR>/check/fix/run/M0236.mo"
|
|
43
45
|
`;
|
|
44
46
|
|
|
45
47
|
exports[`check --fix M0237 1`] = `
|
|
@@ -48,7 +50,7 @@ exports[`check --fix M0237 1`] = `
|
|
|
48
50
|
import List "mo:core/List";
|
|
49
51
|
import Nat "mo:core/Nat";
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
actor {
|
|
52
54
|
public func testM0237() : async () {
|
|
53
55
|
let list = List.fromArray<Nat>([3, 2, 1]);
|
|
54
56
|
list.sortInPlace();
|
|
@@ -60,7 +62,8 @@ persistent actor {
|
|
|
60
62
|
exports[`check --fix M0237: fix output 1`] = `
|
|
61
63
|
"Fixed run/M0237.mo (1 fix: M0237)
|
|
62
64
|
|
|
63
|
-
✓ 1 fix applied to 1 file
|
|
65
|
+
✓ 1 fix applied to 1 file
|
|
66
|
+
✓ <TEST_DIR>/check/fix/run/M0237.mo"
|
|
64
67
|
`;
|
|
65
68
|
|
|
66
69
|
exports[`check --fix edit-suggestions 1`] = `
|
|
@@ -205,7 +208,8 @@ do {
|
|
|
205
208
|
exports[`check --fix edit-suggestions: fix output 1`] = `
|
|
206
209
|
"Fixed run/edit-suggestions.mo (30 fixes: M0223, M0236, M0237)
|
|
207
210
|
|
|
208
|
-
✓ 30 fixes applied to 1 file
|
|
211
|
+
✓ 30 fixes applied to 1 file
|
|
212
|
+
✓ <TEST_DIR>/check/fix/run/edit-suggestions.mo"
|
|
209
213
|
`;
|
|
210
214
|
|
|
211
215
|
exports[`check --fix overlapping edits 1`] = `
|
|
@@ -224,14 +228,16 @@ do {
|
|
|
224
228
|
exports[`check --fix overlapping edits: fix output 1`] = `
|
|
225
229
|
"Fixed run/overlapping.mo (4 fixes: M0223, M0236)
|
|
226
230
|
|
|
227
|
-
✓ 4 fixes applied to 1 file
|
|
231
|
+
✓ 4 fixes applied to 1 file
|
|
232
|
+
✓ <TEST_DIR>/check/fix/run/overlapping.mo"
|
|
228
233
|
`;
|
|
229
234
|
|
|
230
235
|
exports[`check --fix transitive imports: fix output 1`] = `
|
|
231
236
|
"Fixed run/transitive-lib.mo (1 fix: M0236)
|
|
232
237
|
Fixed run/transitive-main.mo (1 fix: M0223)
|
|
233
238
|
|
|
234
|
-
✓ 2 fixes applied to 2 files
|
|
239
|
+
✓ 2 fixes applied to 2 files
|
|
240
|
+
✓ <TEST_DIR>/check/fix/run/transitive-main.mo"
|
|
235
241
|
`;
|
|
236
242
|
|
|
237
243
|
exports[`check --fix transitive imports: lib file 1`] = `
|
|
@@ -250,7 +256,7 @@ module {
|
|
|
250
256
|
exports[`check --fix transitive imports: main file 1`] = `
|
|
251
257
|
"import Lib "./transitive-lib";
|
|
252
258
|
|
|
253
|
-
|
|
259
|
+
actor {
|
|
254
260
|
public func run() : async () {
|
|
255
261
|
func identity<T>(x : T) : T = x;
|
|
256
262
|
let _ = identity(1);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
|
+
|
|
3
|
+
exports[`check-stable compatible upgrade from .mo file 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"exitCode": 0,
|
|
6
|
+
"stderr": "",
|
|
7
|
+
"stdout": "✓ Stable compatibility check passed for canister 'backend'",
|
|
8
|
+
}
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
exports[`check-stable incompatible upgrade from .mo file 1`] = `
|
|
12
|
+
{
|
|
13
|
+
"exitCode": 1,
|
|
14
|
+
"stderr": "(unknown location): Compatibility error [M0170], the new type of stable variable \`counter\` is not compatible with the previous version.
|
|
15
|
+
The previous type
|
|
16
|
+
var Nat
|
|
17
|
+
is not a subtype of
|
|
18
|
+
var Text
|
|
19
|
+
because the type
|
|
20
|
+
Nat
|
|
21
|
+
is not compatible with type
|
|
22
|
+
Text
|
|
23
|
+
in \`counter\`.
|
|
24
|
+
Write an explicit migration function, please see https://internetcomputer.org/docs/motoko/fundamentals/actors/compatibility#explicit-migration-using-a-migration-function.
|
|
25
|
+
(unknown location): Compatibility error [M0169], the stable variable \`name\` of the previous version cannot be implicitly discarded. The variable can only be dropped by an explicit migration function, please see https://internetcomputer.org/docs/motoko/fundamentals/actors/compatibility#explicit-migration-using-a-migration-function
|
|
26
|
+
✗ Stable compatibility check failed for canister 'backend'",
|
|
27
|
+
"stdout": "",
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
exports[`check [moc] args are passed to moc 1`] = `
|
|
4
4
|
{
|
|
5
5
|
"exitCode": 1,
|
|
6
|
-
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier unused
|
|
6
|
+
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
7
|
+
help: if this is intentional, prefix it with an underscore: \`_unused\`
|
|
7
8
|
✗ Check failed for file Warning.mo (exit code: 1)",
|
|
8
|
-
"stdout": "
|
|
9
|
+
"stdout": "",
|
|
9
10
|
}
|
|
10
11
|
`;
|
|
11
12
|
|
|
@@ -18,7 +19,7 @@ cannot produce expected type
|
|
|
18
19
|
()
|
|
19
20
|
Error.mo:7.1-7.21: type error [M0057], unbound variable thisshouldnotcompile
|
|
20
21
|
✗ Check failed for file Error.mo (exit code: 1)",
|
|
21
|
-
"stdout": "
|
|
22
|
+
"stdout": "",
|
|
22
23
|
}
|
|
23
24
|
`;
|
|
24
25
|
|
|
@@ -27,7 +28,15 @@ exports[`check error 2`] = `
|
|
|
27
28
|
"exitCode": 1,
|
|
28
29
|
"stderr": "Ok.mo: No such file or directory
|
|
29
30
|
✗ Check failed for file Ok.mo (exit code: 1)",
|
|
30
|
-
"stdout": "
|
|
31
|
+
"stdout": "",
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
exports[`check no args falls back to [canisters] entrypoints 1`] = `
|
|
36
|
+
{
|
|
37
|
+
"exitCode": 0,
|
|
38
|
+
"stderr": "",
|
|
39
|
+
"stdout": "✓ Ok.mo",
|
|
31
40
|
}
|
|
32
41
|
`;
|
|
33
42
|
|
|
@@ -35,8 +44,7 @@ exports[`check ok 1`] = `
|
|
|
35
44
|
{
|
|
36
45
|
"exitCode": 0,
|
|
37
46
|
"stderr": "",
|
|
38
|
-
"stdout": "
|
|
39
|
-
✓ Ok.mo",
|
|
47
|
+
"stdout": "✓ Ok.mo",
|
|
40
48
|
}
|
|
41
49
|
`;
|
|
42
50
|
|
|
@@ -44,9 +52,9 @@ exports[`check ok 2`] = `
|
|
|
44
52
|
{
|
|
45
53
|
"exitCode": 0,
|
|
46
54
|
"stderr": "",
|
|
47
|
-
"stdout": "
|
|
55
|
+
"stdout": "check Using --all-libs for richer diagnostics
|
|
48
56
|
check Running moc:
|
|
49
|
-
moc-wrapper ["Ok.mo","--check","--
|
|
57
|
+
<CACHE>moc-wrapper ["Ok.mo","--check","--all-libs","--default-persistent-actors"]
|
|
50
58
|
✓ Ok.mo",
|
|
51
59
|
}
|
|
52
60
|
`;
|
|
@@ -54,19 +62,20 @@ moc-wrapper ["Ok.mo","--check","--package","core",".mops/core@2.0.0/src"]
|
|
|
54
62
|
exports[`check warning 1`] = `
|
|
55
63
|
{
|
|
56
64
|
"exitCode": 0,
|
|
57
|
-
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier unused
|
|
58
|
-
|
|
59
|
-
✓ Warning.mo",
|
|
65
|
+
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
66
|
+
help: if this is intentional, prefix it with an underscore: \`_unused\`",
|
|
67
|
+
"stdout": "✓ Warning.mo",
|
|
60
68
|
}
|
|
61
69
|
`;
|
|
62
70
|
|
|
63
71
|
exports[`check warning verbose 1`] = `
|
|
64
72
|
{
|
|
65
73
|
"exitCode": 0,
|
|
66
|
-
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier unused
|
|
67
|
-
|
|
74
|
+
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
75
|
+
help: if this is intentional, prefix it with an underscore: \`_unused\`",
|
|
76
|
+
"stdout": "check Using --all-libs for richer diagnostics
|
|
68
77
|
check Running moc:
|
|
69
|
-
moc-wrapper ["Warning.mo","--check","--
|
|
78
|
+
<CACHE>moc-wrapper ["Warning.mo","--check","--all-libs","--default-persistent-actors"]
|
|
70
79
|
✓ Warning.mo",
|
|
71
80
|
}
|
|
72
81
|
`;
|
|
@@ -74,8 +83,9 @@ moc-wrapper ["Warning.mo","--check","--package","core",".mops/core@2.0.0/src"]
|
|
|
74
83
|
exports[`check warning with -Werror flag 1`] = `
|
|
75
84
|
{
|
|
76
85
|
"exitCode": 1,
|
|
77
|
-
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier unused
|
|
86
|
+
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
87
|
+
help: if this is intentional, prefix it with an underscore: \`_unused\`
|
|
78
88
|
✗ Check failed for file Warning.mo (exit code: 1)",
|
|
79
|
-
"stdout": "
|
|
89
|
+
"stdout": "",
|
|
80
90
|
}
|
|
81
91
|
`;
|
package/tests/build.test.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
2
4
|
import path from "path";
|
|
3
5
|
import { cliSnapshot } from "./helpers";
|
|
4
6
|
|
|
7
|
+
const distBin = path.resolve(import.meta.dirname, "../dist/bin/mops.js");
|
|
8
|
+
|
|
5
9
|
describe("build", () => {
|
|
6
10
|
test("ok", async () => {
|
|
7
11
|
const cwd = path.join(import.meta.dirname, "build/success");
|
|
@@ -21,4 +25,24 @@ describe("build", () => {
|
|
|
21
25
|
(await cliSnapshot(["build", "foo", "bar"], { cwd }, 1)).stderr,
|
|
22
26
|
).toMatch("Candid compatibility check failed for canister bar");
|
|
23
27
|
});
|
|
28
|
+
|
|
29
|
+
// Regression: bin/mops.js must route through environments/nodejs/cli.js
|
|
30
|
+
// so that setWasmBindings() is called before any command runs.
|
|
31
|
+
// The dev entry point (npm run mops) uses tsx and always worked;
|
|
32
|
+
// this test exercises the compiled dist binary (same path as npm i -g ic-mops).
|
|
33
|
+
const hasDistBin = existsSync(distBin);
|
|
34
|
+
(hasDistBin ? test : test.skip)(
|
|
35
|
+
"wasm bindings initialized via dist entry point",
|
|
36
|
+
async () => {
|
|
37
|
+
const cwd = path.join(import.meta.dirname, "build/success");
|
|
38
|
+
const result = await execa("node", [distBin, "build", "foo"], {
|
|
39
|
+
cwd,
|
|
40
|
+
stdio: "pipe",
|
|
41
|
+
reject: false,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(result.stderr).not.toContain("Wasm bindings have not been set");
|
|
45
|
+
expect(result.exitCode).toBe(0);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
24
48
|
});
|