ic-mops 2.8.1 → 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.
- package/CHANGELOG.md +10 -0
- package/bundle/cli.tgz +0 -0
- package/cli.ts +21 -11
- package/commands/build.ts +134 -114
- package/commands/check-stable.ts +110 -17
- package/commands/check.ts +183 -102
- package/commands/info.ts +103 -0
- package/commands/lint.ts +132 -42
- package/commands/toolchain/moc.ts +5 -5
- package/dist/cli.js +19 -10
- package/dist/commands/build.js +105 -82
- package/dist/commands/check-stable.d.ts +7 -1
- package/dist/commands/check-stable.js +83 -19
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +127 -78
- package/dist/commands/info.d.ts +4 -0
- package/dist/commands/info.js +75 -0
- package/dist/commands/lint.js +84 -37
- package/dist/commands/toolchain/moc.js +5 -5
- package/dist/helpers/resolve-canisters.d.ts +3 -1
- package/dist/helpers/resolve-canisters.js +20 -5
- package/dist/package.json +3 -1
- package/dist/templates/mops-publish.yml +1 -1
- package/dist/templates/mops-test.yml +1 -1
- package/dist/tests/build.test.js +17 -0
- package/dist/tests/check-stable.test.js +18 -0
- package/dist/tests/check.test.js +23 -5
- package/dist/tests/lint.test.js +33 -0
- package/dist/types.d.ts +1 -0
- package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
- package/helpers/resolve-canisters.ts +36 -5
- package/package.json +3 -1
- package/templates/mops-publish.yml +1 -1
- package/templates/mops-test.yml +1 -1
- package/tests/__snapshots__/check.test.ts.snap +2 -2
- package/tests/__snapshots__/lint.test.ts.snap +163 -5
- package/tests/build.test.ts +17 -0
- package/tests/check/canisters-canister-args/Warning.mo +5 -0
- package/tests/check/canisters-canister-args/mops.toml +9 -0
- package/tests/check-stable/canister-args/migrations/20250101_000000_Init.mo +8 -0
- package/tests/check-stable/canister-args/migrations/20250201_000000_AddField.mo +9 -0
- package/tests/check-stable/canister-args/mops.toml +9 -0
- package/tests/check-stable/canister-args/old.most +8 -0
- package/tests/check-stable/canister-args/src/main.mo +11 -0
- package/tests/check-stable.test.ts +21 -0
- package/tests/check.test.ts +26 -5
- package/tests/lint-extra/mops.toml +5 -0
- package/tests/lint-extra/src/Ok.mo +5 -0
- package/tests/lint-extra/src/restricted/B.mo +8 -0
- package/tests/lint-extra/src/restricted/Restricted.mo +8 -0
- package/tests/lint-extra-edge-cases/mops.toml +8 -0
- package/tests/lint-extra-edge-cases/src/Clean.mo +5 -0
- package/tests/lint-extra-example-rules/lint/migration-only/migration-only.toml +9 -0
- package/tests/lint-extra-example-rules/lint/no-types/no-types.toml +5 -0
- package/tests/lint-extra-example-rules/lint/types-only/types-only.toml +6 -0
- package/tests/lint-extra-example-rules/mops.toml +7 -0
- package/tests/lint-extra-example-rules/src/Main.mo +10 -0
- package/tests/lint-extra-example-rules/src/Migration.mo +9 -0
- package/tests/lint-extra-example-rules/src/Types.mo +10 -0
- package/tests/lint-extra-with-base/mops.toml +8 -0
- package/tests/lint-extra-with-base/src/BadBase.mo +8 -0
- package/tests/lint-extra-with-base/src/Ok.mo +5 -0
- package/tests/lint-extra-with-base/src/Restricted.mo +5 -0
- package/tests/lint-extra-with-cli-rules/empty-rules/.gitkeep +0 -0
- package/tests/lint-extra-with-cli-rules/mops.toml +5 -0
- package/tests/lint-extra-with-cli-rules/rules-b/no-bool-switch-2.toml +9 -0
- package/tests/lint-extra-with-cli-rules/src/Ok.mo +5 -0
- package/tests/lint-extra-with-cli-rules/src/Restricted.mo +8 -0
- package/tests/lint.test.ts +42 -0
- package/types.ts +1 -0
- package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/wasm/pkg/web/wasm_bg.wasm +0 -0
package/dist/tests/check.test.js
CHANGED
|
@@ -34,30 +34,48 @@ 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
|
|
37
|
+
test("no args checks all canisters", async () => {
|
|
38
38
|
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
39
39
|
await cliSnapshot(["check"], { cwd }, 0);
|
|
40
40
|
});
|
|
41
|
-
test("canister
|
|
41
|
+
test("canister name filters to specific canister", async () => {
|
|
42
|
+
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
43
|
+
const result = await cli(["check", "backend"], { cwd });
|
|
44
|
+
expect(result.exitCode).toBe(0);
|
|
45
|
+
expect(result.stdout).toMatch(/✓ backend/);
|
|
46
|
+
});
|
|
47
|
+
test("canister resolved relative to config root when run from subdirectory", async () => {
|
|
42
48
|
const fixtureRoot = path.join(import.meta.dirname, "check/canisters-subdir");
|
|
43
49
|
const subdir = path.join(fixtureRoot, "src/backend");
|
|
44
50
|
const result = await cli(["check"], { cwd: subdir });
|
|
45
51
|
expect(result.exitCode).toBe(0);
|
|
46
52
|
expect(result.stdout).toMatch(/✓/);
|
|
47
53
|
});
|
|
48
|
-
test("[moc] args applied
|
|
54
|
+
test("[moc] args applied to canister check", async () => {
|
|
49
55
|
const cwd = path.join(import.meta.dirname, "check/canisters-moc-args");
|
|
50
56
|
const result = await cli(["check"], { cwd });
|
|
51
57
|
expect(result.exitCode).toBe(1);
|
|
52
58
|
expect(result.stderr).toMatch(/warning \[M0194\]/);
|
|
53
59
|
});
|
|
54
|
-
test("
|
|
60
|
+
test("[canisters.X].args applied to canister check", async () => {
|
|
61
|
+
const cwd = path.join(import.meta.dirname, "check/canisters-canister-args");
|
|
62
|
+
const result = await cli(["check"], { cwd });
|
|
63
|
+
expect(result.exitCode).toBe(1);
|
|
64
|
+
expect(result.stderr).toMatch(/warning \[M0194\]/);
|
|
65
|
+
});
|
|
66
|
+
test("canister with errors", async () => {
|
|
55
67
|
const cwd = path.join(import.meta.dirname, "check/canisters-error");
|
|
56
68
|
const result = await cli(["check"], { cwd });
|
|
57
69
|
expect(result.exitCode).toBe(1);
|
|
58
70
|
expect(result.stderr).toMatch(/error/i);
|
|
59
71
|
});
|
|
60
|
-
test("
|
|
72
|
+
test("invalid canister name errors", async () => {
|
|
73
|
+
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
74
|
+
const result = await cli(["check", "nonexistent"], { cwd });
|
|
75
|
+
expect(result.exitCode).toBe(1);
|
|
76
|
+
expect(result.stderr).toMatch(/not found in mops\.toml/);
|
|
77
|
+
});
|
|
78
|
+
test("--fix with canister", async () => {
|
|
61
79
|
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
62
80
|
const result = await cli(["check", "--fix"], { cwd });
|
|
63
81
|
expect(result.exitCode).toBe(0);
|
package/dist/tests/lint.test.js
CHANGED
|
@@ -38,4 +38,37 @@ describe("lint", () => {
|
|
|
38
38
|
expect(result.exitCode).toBe(0);
|
|
39
39
|
expect(result.stderr).toMatch(/not found in dependencies/);
|
|
40
40
|
});
|
|
41
|
+
describe("[lint.extra]", () => {
|
|
42
|
+
test("extra rules on glob-matched files", async () => {
|
|
43
|
+
// src/restricted/*.mo has violations, Ok.mo does not.
|
|
44
|
+
// Extra rules apply only to the glob match → fails on restricted/ files.
|
|
45
|
+
// Filter "Ok" narrows scope so extra is skipped → passes.
|
|
46
|
+
const cwd = path.join(import.meta.dirname, "lint-extra");
|
|
47
|
+
await cliSnapshot(["lint"], { cwd }, 1);
|
|
48
|
+
await cliSnapshot(["lint", "Ok"], { cwd }, 0);
|
|
49
|
+
});
|
|
50
|
+
test("edge cases: pass, empty value, no-match, missing dir", async () => {
|
|
51
|
+
// Single fixture with 4 entries processed in order:
|
|
52
|
+
// 1. Clean.mo + valid rules → passes
|
|
53
|
+
// 2. empty array → warns and skips
|
|
54
|
+
// 3. non-matching glob → warns and skips
|
|
55
|
+
// 4. missing rule dir → errors
|
|
56
|
+
const cwd = path.join(import.meta.dirname, "lint-extra-edge-cases");
|
|
57
|
+
await cliSnapshot(["lint"], { cwd }, 1);
|
|
58
|
+
});
|
|
59
|
+
test("base rules still run alongside extra rules", async () => {
|
|
60
|
+
const cwd = path.join(import.meta.dirname, "lint-extra-with-base");
|
|
61
|
+
await cliSnapshot(["lint"], { cwd }, 1);
|
|
62
|
+
});
|
|
63
|
+
test("--rules CLI flag does not affect extra runs, multi-rules", async () => {
|
|
64
|
+
// --rules overrides base with an empty dir (no base violations).
|
|
65
|
+
// Extra runs independently with two rule dirs → Restricted.mo fails.
|
|
66
|
+
const cwd = path.join(import.meta.dirname, "lint-extra-with-cli-rules");
|
|
67
|
+
await cliSnapshot(["lint", "--rules", "empty-rules", "--verbose"], { cwd }, 1);
|
|
68
|
+
});
|
|
69
|
+
test("example rules: no-types, types-only, migration-only", async () => {
|
|
70
|
+
const cwd = path.join(import.meta.dirname, "lint-extra-example-rules");
|
|
71
|
+
await cliSnapshot(["lint"], { cwd }, 1);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
41
74
|
});
|
package/dist/types.d.ts
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -14,11 +14,22 @@ export function resolveCanisterConfigs(
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "dist/bin/mops.js",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"prettier-plugin-motoko": "0.13.0",
|
|
87
87
|
"promisify-child-process": "4.1.2",
|
|
88
88
|
"prompts": "2.4.2",
|
|
89
|
+
"proper-lockfile": "4.1.2",
|
|
89
90
|
"semver": "7.7.1",
|
|
90
91
|
"stream-to-promise": "3.0.0",
|
|
91
92
|
"string-width": "7.2.0",
|
|
@@ -102,6 +103,7 @@
|
|
|
102
103
|
"@types/ncp": "2.0.8",
|
|
103
104
|
"@types/node": "24.0.3",
|
|
104
105
|
"@types/prompts": "2.4.9",
|
|
106
|
+
"@types/proper-lockfile": "4.1.4",
|
|
105
107
|
"@types/semver": "7.5.8",
|
|
106
108
|
"@types/stream-to-promise": "2.2.4",
|
|
107
109
|
"@types/tar": "6.1.13",
|
|
@@ -10,7 +10,7 @@ jobs:
|
|
|
10
10
|
|
|
11
11
|
steps:
|
|
12
12
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
|
13
|
-
- uses:
|
|
13
|
+
- uses: caffeinelabs/setup-mops@v1
|
|
14
14
|
with:
|
|
15
15
|
# Make sure you set the MOPS_IDENTITY_PEM secret in your repository settings https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository
|
|
16
16
|
identity-pem: ${{ secrets.MOPS_IDENTITY_PEM }}
|
package/templates/mops-test.yml
CHANGED
|
@@ -32,11 +32,11 @@ exports[`check error 2`] = `
|
|
|
32
32
|
}
|
|
33
33
|
`;
|
|
34
34
|
|
|
35
|
-
exports[`check no args
|
|
35
|
+
exports[`check no args checks all canisters 1`] = `
|
|
36
36
|
{
|
|
37
37
|
"exitCode": 0,
|
|
38
38
|
"stderr": "",
|
|
39
|
-
"stdout": "✓
|
|
39
|
+
"stdout": "✓ backend",
|
|
40
40
|
}
|
|
41
41
|
`;
|
|
42
42
|
|
|
@@ -1,5 +1,163 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
2
|
|
|
3
|
+
exports[`lint [lint.extra] --rules CLI flag does not affect extra runs, multi-rules 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"exitCode": 1,
|
|
6
|
+
"stderr": " × [ERROR]: no-bool-switch
|
|
7
|
+
╭─[<TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo:3:5]
|
|
8
|
+
2 │ public func boolSwitch(b : Bool) : Bool {
|
|
9
|
+
3 │ ╭─▶ switch (b) {
|
|
10
|
+
4 │ │ case false { false };
|
|
11
|
+
5 │ │ case true { true };
|
|
12
|
+
6 │ ├─▶ };
|
|
13
|
+
· ╰──── Don't switch on boolean values, use if instead
|
|
14
|
+
7 │ };
|
|
15
|
+
╰────
|
|
16
|
+
|
|
17
|
+
× [ERROR]: no-bool-switch-2
|
|
18
|
+
╭─[<TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo:3:5]
|
|
19
|
+
2 │ public func boolSwitch(b : Bool) : Bool {
|
|
20
|
+
3 │ ╭─▶ switch (b) {
|
|
21
|
+
4 │ │ case false { false };
|
|
22
|
+
5 │ │ case true { true };
|
|
23
|
+
6 │ ├─▶ };
|
|
24
|
+
· ╰──── Duplicate detection: don't switch on boolean values
|
|
25
|
+
7 │ };
|
|
26
|
+
╰────
|
|
27
|
+
|
|
28
|
+
Error: Found 2 errors
|
|
29
|
+
Lint failed",
|
|
30
|
+
"stdout": "lint Running lintoko (base):
|
|
31
|
+
<CACHE>/lintoko/0.7.0/lintoko
|
|
32
|
+
["--verbose","--rules","empty-rules","<TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo","<TEST_DIR>/lint-extra-with-cli-rules/src/Ok.mo"]
|
|
33
|
+
DEBUG file input: <TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo
|
|
34
|
+
DEBUG file input: <TEST_DIR>/lint-extra-with-cli-rules/src/Ok.mo
|
|
35
|
+
DEBUG Loading rules from: empty-rules
|
|
36
|
+
DEBUG Linting file: <TEST_DIR>/lint-extra-with-cli-rules/src/Ok.mo
|
|
37
|
+
DEBUG Linting file: <TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo
|
|
38
|
+
lint Running lintoko (extra: src/Restricted.mo):
|
|
39
|
+
<CACHE>/lintoko/0.7.0/lintoko
|
|
40
|
+
["--verbose","--rules","../lint/lints","--rules","rules-b","<TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo"]
|
|
41
|
+
DEBUG file input: <TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo
|
|
42
|
+
DEBUG Loading rules from: ../lint/lints
|
|
43
|
+
DEBUG Parsing extra rule at: ../lint/lints/no-bool-switch.toml
|
|
44
|
+
DEBUG Loading rules from: rules-b
|
|
45
|
+
DEBUG Parsing extra rule at: rules-b/no-bool-switch-2.toml
|
|
46
|
+
DEBUG Linting file: <TEST_DIR>/lint-extra-with-cli-rules/src/Restricted.mo",
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
exports[`lint [lint.extra] base rules still run alongside extra rules 1`] = `
|
|
51
|
+
{
|
|
52
|
+
"exitCode": 1,
|
|
53
|
+
"stderr": " × [ERROR]: no-bool-switch
|
|
54
|
+
╭─[<TEST_DIR>/lint-extra-with-base/src/BadBase.mo:3:5]
|
|
55
|
+
2 │ public func boolSwitch(b : Bool) : Bool {
|
|
56
|
+
3 │ ╭─▶ switch (b) {
|
|
57
|
+
4 │ │ case false { false };
|
|
58
|
+
5 │ │ case true { true };
|
|
59
|
+
6 │ ├─▶ };
|
|
60
|
+
· ╰──── Don't switch on boolean values, use if instead
|
|
61
|
+
7 │ };
|
|
62
|
+
╰────
|
|
63
|
+
|
|
64
|
+
Error: Found 1 errors
|
|
65
|
+
Lint failed",
|
|
66
|
+
"stdout": "",
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
exports[`lint [lint.extra] edge cases: pass, empty value, no-match, missing dir 1`] = `
|
|
71
|
+
{
|
|
72
|
+
"exitCode": 1,
|
|
73
|
+
"stderr": "[lint.extra] skipping 'src/also-missing/*.mo': value must be a non-empty array of rule directories
|
|
74
|
+
[lint.extra] no files matched glob 'src/nonexistent/*.mo', skipping
|
|
75
|
+
[lint.extra] rule directory 'nonexistent-rules' not found (referenced by glob 'src/*.mo')",
|
|
76
|
+
"stdout": "",
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
exports[`lint [lint.extra] example rules: no-types, types-only, migration-only 1`] = `
|
|
81
|
+
{
|
|
82
|
+
"exitCode": 1,
|
|
83
|
+
"stderr": " × [ERROR]: no-types
|
|
84
|
+
╭─[<TEST_DIR>/lint-extra-example-rules/src/Main.mo:6:10]
|
|
85
|
+
5 │
|
|
86
|
+
6 │ ╭─▶ public type User = {
|
|
87
|
+
7 │ │ name : Text;
|
|
88
|
+
8 │ │ age : Nat;
|
|
89
|
+
9 │ ├─▶ };
|
|
90
|
+
· ╰──── File must not contain type declarations. Move types to a separate Types module.
|
|
91
|
+
10 │ };
|
|
92
|
+
╰────
|
|
93
|
+
|
|
94
|
+
Error: Found 1 errors
|
|
95
|
+
× [ERROR]: types-only
|
|
96
|
+
╭─[<TEST_DIR>/lint-extra-example-rules/src/Types.mo:7:10]
|
|
97
|
+
6 │
|
|
98
|
+
7 │ ╭─▶ public func helper() : Text {
|
|
99
|
+
8 │ │ "oops";
|
|
100
|
+
9 │ ├─▶ };
|
|
101
|
+
· ╰──── File must contain only type declarations. No functions, classes, or variable bindings.
|
|
102
|
+
10 │ };
|
|
103
|
+
╰────
|
|
104
|
+
|
|
105
|
+
Error: Found 1 errors
|
|
106
|
+
× [ERROR]: migration-only
|
|
107
|
+
╭─[<TEST_DIR>/lint-extra-example-rules/src/Migration.mo:6:10]
|
|
108
|
+
5 │
|
|
109
|
+
6 │ ╭─▶ public func notAllowed(old : {}) : {} {
|
|
110
|
+
7 │ │ {};
|
|
111
|
+
8 │ ├─▶ };
|
|
112
|
+
· ╰──── Only migration() may be a public function.
|
|
113
|
+
9 │ };
|
|
114
|
+
╰────
|
|
115
|
+
|
|
116
|
+
Error: Found 1 errors
|
|
117
|
+
Lint failed",
|
|
118
|
+
"stdout": "",
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
exports[`lint [lint.extra] extra rules on glob-matched files 1`] = `
|
|
123
|
+
{
|
|
124
|
+
"exitCode": 1,
|
|
125
|
+
"stderr": " × [ERROR]: no-bool-switch
|
|
126
|
+
╭─[<TEST_DIR>/lint-extra/src/restricted/B.mo:3:5]
|
|
127
|
+
2 │ public func anotherBoolSwitch(b : Bool) : Bool {
|
|
128
|
+
3 │ ╭─▶ switch (b) {
|
|
129
|
+
4 │ │ case true { false };
|
|
130
|
+
5 │ │ case false { true };
|
|
131
|
+
6 │ ├─▶ };
|
|
132
|
+
· ╰──── Don't switch on boolean values, use if instead
|
|
133
|
+
7 │ };
|
|
134
|
+
╰────
|
|
135
|
+
|
|
136
|
+
× [ERROR]: no-bool-switch
|
|
137
|
+
╭─[<TEST_DIR>/lint-extra/src/restricted/Restricted.mo:3:5]
|
|
138
|
+
2 │ public func boolSwitch(b : Bool) : Bool {
|
|
139
|
+
3 │ ╭─▶ switch (b) {
|
|
140
|
+
4 │ │ case false { false };
|
|
141
|
+
5 │ │ case true { true };
|
|
142
|
+
6 │ ├─▶ };
|
|
143
|
+
· ╰──── Don't switch on boolean values, use if instead
|
|
144
|
+
7 │ };
|
|
145
|
+
╰────
|
|
146
|
+
|
|
147
|
+
Error: Found 2 errors
|
|
148
|
+
Lint failed",
|
|
149
|
+
"stdout": "",
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
exports[`lint [lint.extra] extra rules on glob-matched files 2`] = `
|
|
154
|
+
{
|
|
155
|
+
"exitCode": 0,
|
|
156
|
+
"stderr": "[lint.extra] no files matched glob 'src/restricted/*.mo', skipping",
|
|
157
|
+
"stdout": "✓ Lint succeeded",
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
|
|
3
161
|
exports[`lint error 1`] = `
|
|
4
162
|
{
|
|
5
163
|
"exitCode": 1,
|
|
@@ -15,8 +173,8 @@ exports[`lint error 1`] = `
|
|
|
15
173
|
╰────
|
|
16
174
|
|
|
17
175
|
Error: Found 1 errors
|
|
18
|
-
Lint failed
|
|
19
|
-
"stdout": "lint Running lintoko:
|
|
176
|
+
Lint failed",
|
|
177
|
+
"stdout": "lint Running lintoko (base):
|
|
20
178
|
<CACHE>/lintoko/0.7.0/lintoko
|
|
21
179
|
["--verbose","--rules","lints","<TEST_DIR>/lint/src/Ok.mo","<TEST_DIR>/lint/src/NoBoolSwitch.mo"]
|
|
22
180
|
DEBUG file input: <TEST_DIR>/lint/src/Ok.mo
|
|
@@ -43,8 +201,8 @@ exports[`lint error 2`] = `
|
|
|
43
201
|
╰────
|
|
44
202
|
|
|
45
203
|
Error: Found 1 errors
|
|
46
|
-
Lint failed
|
|
47
|
-
"stdout": "lint Running lintoko:
|
|
204
|
+
Lint failed",
|
|
205
|
+
"stdout": "lint Running lintoko (base):
|
|
48
206
|
<CACHE>/lintoko/0.7.0/lintoko
|
|
49
207
|
["--verbose","--rules","lints","<TEST_DIR>/lint/src/NoBoolSwitch.mo"]
|
|
50
208
|
DEBUG file input: <TEST_DIR>/lint/src/NoBoolSwitch.mo
|
|
@@ -66,7 +224,7 @@ exports[`lint ok 1`] = `
|
|
|
66
224
|
{
|
|
67
225
|
"exitCode": 0,
|
|
68
226
|
"stderr": "",
|
|
69
|
-
"stdout": "lint Running lintoko:
|
|
227
|
+
"stdout": "lint Running lintoko (base):
|
|
70
228
|
<CACHE>/lintoko/0.7.0/lintoko
|
|
71
229
|
["--verbose","--rules","lints","<TEST_DIR>/lint/src/Ok.mo"]
|
|
72
230
|
DEBUG file input: <TEST_DIR>/lint/src/Ok.mo
|
package/tests/build.test.ts
CHANGED
|
@@ -100,6 +100,23 @@ describe("build", () => {
|
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
test("parallel builds of the same canister both succeed", async () => {
|
|
104
|
+
const cwd = path.join(import.meta.dirname, "build/success");
|
|
105
|
+
try {
|
|
106
|
+
const [a, b] = await Promise.all([
|
|
107
|
+
cli(["build", "foo"], { cwd }),
|
|
108
|
+
cli(["build", "foo"], { cwd }),
|
|
109
|
+
]);
|
|
110
|
+
expect(a.exitCode).toBe(0);
|
|
111
|
+
expect(b.exitCode).toBe(0);
|
|
112
|
+
expect(existsSync(path.join(cwd, ".mops/.build/foo.wasm"))).toBe(true);
|
|
113
|
+
expect(existsSync(path.join(cwd, ".mops/.build/foo.did"))).toBe(true);
|
|
114
|
+
expect(existsSync(path.join(cwd, ".mops/.build/foo.most"))).toBe(true);
|
|
115
|
+
} finally {
|
|
116
|
+
cleanFixture(cwd);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
103
120
|
// Regression: bin/mops.js must route through environments/nodejs/cli.js
|
|
104
121
|
// so that setWasmBindings() is called before any command runs.
|
|
105
122
|
// The dev entry point (npm run mops) uses tsx and always worked;
|
|
@@ -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 });
|
package/tests/check.test.ts
CHANGED
|
@@ -49,12 +49,19 @@ describe("check", () => {
|
|
|
49
49
|
await cliSnapshot(["check", "Warning.mo"], { cwd }, 1);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
test("no args
|
|
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
|
|
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
|
|
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("
|
|
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("
|
|
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);
|