ic-mops 2.9.0 → 2.10.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.
@@ -14,11 +14,22 @@ export function resolveCanisterConfigs(
14
14
  );
15
15
  }
16
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));
17
+ export function filterCanisters(
18
+ canisters: Record<string, CanisterConfig>,
19
+ names?: string[],
20
+ ): Record<string, CanisterConfig> {
21
+ if (!names) {
22
+ return canisters;
23
+ }
24
+ const invalidNames = names.filter((name) => !(name in canisters));
25
+ if (invalidNames.length) {
26
+ cliError(
27
+ `Canister(s) not found in mops.toml: ${invalidNames.join(", ")}. Available: ${Object.keys(canisters).join(", ")}`,
28
+ );
29
+ }
30
+ return Object.fromEntries(
31
+ Object.entries(canisters).filter(([name]) => names.includes(name)),
32
+ );
22
33
  }
23
34
 
24
35
  export function resolveSingleCanister(
@@ -50,3 +61,23 @@ export function resolveSingleCanister(
50
61
 
51
62
  return { name: names[0]!, canister: canisters[names[0]!]! };
52
63
  }
64
+
65
+ export function looksLikeFile(arg: string): boolean {
66
+ return (
67
+ arg.endsWith(".mo") ||
68
+ arg.endsWith(".most") ||
69
+ arg.includes("/") ||
70
+ arg.includes("\\")
71
+ );
72
+ }
73
+
74
+ export function validateCanisterArgs(
75
+ canister: CanisterConfig,
76
+ canisterName: string,
77
+ ): void {
78
+ if (canister.args && typeof canister.args === "string") {
79
+ cliError(
80
+ `Canister config 'args' should be an array of strings for canister ${canisterName}`,
81
+ );
82
+ }
83
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
@@ -32,11 +32,11 @@ exports[`check error 2`] = `
32
32
  }
33
33
  `;
34
34
 
35
- exports[`check no args falls back to [canisters] entrypoints 1`] = `
35
+ exports[`check no args checks all canisters 1`] = `
36
36
  {
37
37
  "exitCode": 0,
38
38
  "stderr": "",
39
- "stdout": "✓ Ok.mo",
39
+ "stdout": "✓ backend",
40
40
  }
41
41
  `;
42
42
 
@@ -0,0 +1,5 @@
1
+ actor {
2
+ public func example() : async () {
3
+ let unused = 123;
4
+ };
5
+ };
@@ -0,0 +1,9 @@
1
+ [toolchain]
2
+ moc = "1.3.0"
3
+
4
+ [moc]
5
+ args = ["--default-persistent-actors"]
6
+
7
+ [canisters.backend]
8
+ main = "Warning.mo"
9
+ args = ["-Werror"]
@@ -0,0 +1,8 @@
1
+ module {
2
+ public func migration(_ : {}) : { a : Nat; b : Text } {
3
+ {
4
+ a = 42;
5
+ b = "hello";
6
+ };
7
+ };
8
+ };
@@ -0,0 +1,9 @@
1
+ module {
2
+ public func migration(old : { a : Nat; b : Text }) : {
3
+ a : Nat;
4
+ b : Text;
5
+ c : Bool;
6
+ } {
7
+ { old with c = true };
8
+ };
9
+ };
@@ -0,0 +1,9 @@
1
+ [toolchain]
2
+ moc = "1.5.0"
3
+
4
+ [moc]
5
+ args = ["--default-persistent-actors"]
6
+
7
+ [canisters.backend]
8
+ main = "src/main.mo"
9
+ args = ["--enhanced-migration=migrations"]
@@ -0,0 +1,8 @@
1
+ // Version: 4.0.0
2
+ {
3
+ "20250101_000000_Init" : {} -> {a : Nat; b : Text}
4
+ }
5
+ actor {
6
+ stable a : Nat;
7
+ stable b : Text
8
+ };
@@ -0,0 +1,11 @@
1
+ import Prim "mo:prim";
2
+
3
+ actor {
4
+ let a : Nat;
5
+ let b : Text;
6
+ let c : Bool;
7
+
8
+ public func check() : async () {
9
+ Prim.debugPrint(debug_show { a; b; c });
10
+ };
11
+ };
@@ -59,6 +59,27 @@ describe("check-stable", () => {
59
59
  expect(existsSync(path.join(cwd, "new.wasm"))).toBe(false);
60
60
  });
61
61
 
62
+ test("[canisters.X].args are passed to moc (enhanced migration)", async () => {
63
+ const cwd = path.join(import.meta.dirname, "check-stable/canister-args");
64
+ const result = await cli(["check-stable", "old.most"], { cwd });
65
+ expect(result.exitCode).toBe(0);
66
+ expect(result.stdout).toMatch(/Stable compatibility check passed/);
67
+ });
68
+
69
+ test("no args checks all canisters with [check-stable] config", async () => {
70
+ const cwd = path.join(import.meta.dirname, "check/deployed-compatible");
71
+ const result = await cli(["check-stable"], { cwd });
72
+ expect(result.exitCode).toBe(0);
73
+ expect(result.stdout).toMatch(/Stable compatibility check passed/);
74
+ });
75
+
76
+ test("canister name filters to specific canister", async () => {
77
+ const cwd = path.join(import.meta.dirname, "check/deployed-compatible");
78
+ const result = await cli(["check-stable", "backend"], { cwd });
79
+ expect(result.exitCode).toBe(0);
80
+ expect(result.stdout).toMatch(/Stable compatibility check passed/);
81
+ });
82
+
62
83
  test("errors when old file does not exist", async () => {
63
84
  const cwd = path.join(import.meta.dirname, "check-stable/compatible");
64
85
  const result = await cli(["check-stable", "nonexistent.mo"], { cwd });
@@ -49,12 +49,19 @@ describe("check", () => {
49
49
  await cliSnapshot(["check", "Warning.mo"], { cwd }, 1);
50
50
  });
51
51
 
52
- test("no args falls back to [canisters] entrypoints", async () => {
52
+ test("no args checks all canisters", async () => {
53
53
  const cwd = path.join(import.meta.dirname, "check/canisters");
54
54
  await cliSnapshot(["check"], { cwd }, 0);
55
55
  });
56
56
 
57
- test("canister entrypoint resolved relative to config root when run from subdirectory", async () => {
57
+ test("canister name filters to specific canister", async () => {
58
+ const cwd = path.join(import.meta.dirname, "check/canisters");
59
+ const result = await cli(["check", "backend"], { cwd });
60
+ expect(result.exitCode).toBe(0);
61
+ expect(result.stdout).toMatch(/✓ backend/);
62
+ });
63
+
64
+ test("canister resolved relative to config root when run from subdirectory", async () => {
58
65
  const fixtureRoot = path.join(
59
66
  import.meta.dirname,
60
67
  "check/canisters-subdir",
@@ -65,21 +72,35 @@ describe("check", () => {
65
72
  expect(result.stdout).toMatch(/✓/);
66
73
  });
67
74
 
68
- test("[moc] args applied when using canister fallback", async () => {
75
+ test("[moc] args applied to canister check", async () => {
69
76
  const cwd = path.join(import.meta.dirname, "check/canisters-moc-args");
70
77
  const result = await cli(["check"], { cwd });
71
78
  expect(result.exitCode).toBe(1);
72
79
  expect(result.stderr).toMatch(/warning \[M0194\]/);
73
80
  });
74
81
 
75
- test("canister entrypoint with errors", async () => {
82
+ test("[canisters.X].args applied to canister check", async () => {
83
+ const cwd = path.join(import.meta.dirname, "check/canisters-canister-args");
84
+ const result = await cli(["check"], { cwd });
85
+ expect(result.exitCode).toBe(1);
86
+ expect(result.stderr).toMatch(/warning \[M0194\]/);
87
+ });
88
+
89
+ test("canister with errors", async () => {
76
90
  const cwd = path.join(import.meta.dirname, "check/canisters-error");
77
91
  const result = await cli(["check"], { cwd });
78
92
  expect(result.exitCode).toBe(1);
79
93
  expect(result.stderr).toMatch(/error/i);
80
94
  });
81
95
 
82
- test("--fix with canister fallback", async () => {
96
+ test("invalid canister name errors", async () => {
97
+ const cwd = path.join(import.meta.dirname, "check/canisters");
98
+ const result = await cli(["check", "nonexistent"], { cwd });
99
+ expect(result.exitCode).toBe(1);
100
+ expect(result.stderr).toMatch(/not found in mops\.toml/);
101
+ });
102
+
103
+ test("--fix with canister", async () => {
83
104
  const cwd = path.join(import.meta.dirname, "check/canisters");
84
105
  const result = await cli(["check", "--fix"], { cwd });
85
106
  expect(result.exitCode).toBe(0);