ic-mops 2.5.0 → 2.6.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 +24 -0
- package/api/network.ts +1 -1
- package/bun.lock +1082 -78
- package/bundle/cli.tgz +0 -0
- package/cli.ts +1 -1
- package/commands/add.ts +4 -1
- package/commands/build.ts +7 -12
- package/commands/check.ts +20 -2
- package/commands/docs-coverage.ts +26 -21
- package/commands/install/install-dep.ts +5 -3
- package/commands/lint.ts +107 -10
- package/commands/publish.ts +24 -11
- package/commands/remove.ts +5 -2
- package/commands/self.ts +1 -1
- package/commands/sources.ts +3 -2
- package/commands/sync.ts +13 -16
- package/commands/test/test.ts +3 -3
- package/commands/update.ts +13 -7
- package/commands/watch/error-checker.ts +3 -8
- package/commands/watch/warning-checker.ts +3 -8
- package/dist/api/network.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/commands/add.js +4 -1
- package/dist/commands/build.js +5 -10
- package/dist/commands/check.js +14 -2
- package/dist/commands/docs-coverage.js +22 -22
- package/dist/commands/install/install-dep.js +3 -3
- package/dist/commands/lint.d.ts +3 -0
- package/dist/commands/lint.js +75 -10
- package/dist/commands/publish.js +19 -11
- package/dist/commands/remove.js +5 -2
- package/dist/commands/self.js +1 -1
- package/dist/commands/sources.js +3 -2
- package/dist/commands/sync.js +9 -14
- package/dist/commands/test/test.js +3 -3
- package/dist/commands/update.js +9 -4
- package/dist/commands/watch/error-checker.js +3 -8
- package/dist/commands/watch/warning-checker.js +3 -8
- package/dist/helpers/find-changelog-entry.js +1 -1
- package/dist/integrity.js +9 -3
- package/dist/mops.js +3 -0
- package/dist/package.json +3 -5
- package/dist/release-cli.js +2 -2
- package/dist/resolve-packages.js +4 -4
- package/dist/tests/build.test.js +1 -1
- package/dist/tests/check.test.js +24 -0
- package/dist/tests/helpers.js +8 -1
- package/dist/tests/lint.test.js +28 -2
- package/dist/types.d.ts +2 -0
- package/dist/vessel.d.ts +1 -1
- package/dist/vessel.js +3 -2
- package/helpers/find-changelog-entry.ts +3 -1
- package/integrity.ts +12 -3
- package/mops.ts +7 -0
- package/package.json +3 -5
- package/release-cli.ts +2 -2
- package/resolve-packages.ts +6 -4
- package/tests/build.test.ts +1 -1
- package/tests/check/with-lint-fail/NoBoolSwitch.mo +8 -0
- package/tests/check/with-lint-fail/lints/no-bool-switch.toml +9 -0
- package/tests/check/with-lint-fail/mops.toml +9 -0
- package/tests/check/with-lint-pass/Ok.mo +5 -0
- package/tests/check/with-lint-pass/lints/no-bool-switch.toml +9 -0
- package/tests/check/with-lint-pass/mops.toml +9 -0
- package/tests/check.test.ts +28 -0
- package/tests/helpers.ts +9 -1
- package/tests/lint-config-rules/extra-rules/no-bool-switch.toml +9 -0
- package/tests/lint-config-rules/mops.toml +5 -0
- package/tests/lint-config-rules/src/NoBoolSwitch.mo +8 -0
- package/tests/lint-extends/mops.toml +8 -0
- package/tests/lint-extends/my-pkg/mops.toml +3 -0
- package/tests/lint-extends/my-pkg/rules/no-bool-switch.toml +9 -0
- package/tests/lint-extends/src/NoBoolSwitch.mo +8 -0
- package/tests/lint-extends-all/mops.toml +8 -0
- package/tests/lint-extends-all/src/NoBoolSwitch.mo +8 -0
- package/tests/lint-extends-ignored/mops.toml +8 -0
- package/tests/lint-extends-ignored/src/NoBoolSwitch.mo +8 -0
- package/tests/lint.test.ts +32 -2
- package/types.ts +2 -0
- package/vessel.ts +5 -3
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { getMocPath } from "../../helpers/get-moc-path.js";
|
|
6
6
|
import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
|
|
7
|
-
import {
|
|
7
|
+
import { sourcesArgs } from "../sources.js";
|
|
8
8
|
import { parallel } from "../../parallel.js";
|
|
9
9
|
import { globMoFiles } from "./globMoFiles.js";
|
|
10
10
|
export class WarningChecker {
|
|
@@ -50,7 +50,7 @@ export class WarningChecker {
|
|
|
50
50
|
onProgress();
|
|
51
51
|
let rootDir = getRootDir();
|
|
52
52
|
let mocPath = getMocPath();
|
|
53
|
-
let deps = await
|
|
53
|
+
let deps = (await sourcesArgs({ cwd: rootDir })).flat();
|
|
54
54
|
let globalMocArgs = getGlobalMocArgs(readConfig());
|
|
55
55
|
let paths = globMoFiles(rootDir);
|
|
56
56
|
this.totalFiles = paths.length;
|
|
@@ -59,12 +59,7 @@ export class WarningChecker {
|
|
|
59
59
|
let controller = new AbortController();
|
|
60
60
|
let { signal } = controller;
|
|
61
61
|
this.controllers.set(file, controller);
|
|
62
|
-
let { stderr } = await promisify(execFile)(mocPath, [
|
|
63
|
-
"--check",
|
|
64
|
-
...deps.flatMap((x) => x.split(" ")),
|
|
65
|
-
...globalMocArgs,
|
|
66
|
-
file,
|
|
67
|
-
], { cwd: rootDir, signal }).catch((error) => {
|
|
62
|
+
let { stderr } = await promisify(execFile)(mocPath, ["--check", ...deps, ...globalMocArgs, file], { cwd: rootDir, signal }).catch((error) => {
|
|
68
63
|
if (error.code === "ABORT_ERR") {
|
|
69
64
|
return { stderr: "" };
|
|
70
65
|
}
|
|
@@ -15,7 +15,7 @@ export function findChangelogEntry(changelog, version) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
else if (node.type === "heading" &&
|
|
18
|
-
toMarkdown(node).match(new RegExp(`\\b${version}\\b`))) {
|
|
18
|
+
toMarkdown(node).match(new RegExp(`\\b${version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`))) {
|
|
19
19
|
depth = node.depth;
|
|
20
20
|
found = true;
|
|
21
21
|
}
|
package/dist/integrity.js
CHANGED
|
@@ -50,7 +50,7 @@ export function getLocalFileHash(fileId) {
|
|
|
50
50
|
return bytesToHex(sha256(fileData));
|
|
51
51
|
}
|
|
52
52
|
function getMopsTomlHash() {
|
|
53
|
-
return bytesToHex(sha256(fs.readFileSync(getRootDir()
|
|
53
|
+
return bytesToHex(sha256(fs.readFileSync(path.join(getRootDir(), "mops.toml"))));
|
|
54
54
|
}
|
|
55
55
|
function getMopsTomlDepsHash() {
|
|
56
56
|
let config = readConfig();
|
|
@@ -90,7 +90,13 @@ export function readLockFile() {
|
|
|
90
90
|
let rootDir = getRootDir();
|
|
91
91
|
let lockFile = path.join(rootDir, "mops.lock");
|
|
92
92
|
if (fs.existsSync(lockFile)) {
|
|
93
|
-
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(fs.readFileSync(lockFile).toString());
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
console.error("mops.lock is corrupted. Delete it and run `mops install` to regenerate.");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
94
100
|
}
|
|
95
101
|
return null;
|
|
96
102
|
}
|
|
@@ -208,7 +214,7 @@ export async function checkLockFile(force = false) {
|
|
|
208
214
|
}
|
|
209
215
|
for (let [fileId, lockedHash] of Object.entries(hashes)) {
|
|
210
216
|
// check if file belongs to package
|
|
211
|
-
if (!fileId.startsWith(packageId)) {
|
|
217
|
+
if (!fileId.startsWith(packageId + "/")) {
|
|
212
218
|
console.error("Integrity check failed");
|
|
213
219
|
console.error(`File ${fileId} in lock file does not belong to package ${packageId}`);
|
|
214
220
|
process.exit(1);
|
package/dist/mops.js
CHANGED
|
@@ -131,6 +131,9 @@ export async function getGithubCommit(repo, ref) {
|
|
|
131
131
|
res = await fetch(`https://api.github.com/repos/${repo}/commits/main`);
|
|
132
132
|
json = await res.json();
|
|
133
133
|
}
|
|
134
|
+
if (!res.ok || !json.sha) {
|
|
135
|
+
throw new Error(`Failed to fetch commit for ${repo}#${ref}: ${json.message || `HTTP ${res.status}`}`);
|
|
136
|
+
}
|
|
134
137
|
return json;
|
|
135
138
|
}
|
|
136
139
|
export function getDependencyType(version) {
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "bin/mops.js",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"@noble/hashes": "1.8.0",
|
|
35
35
|
"as-table": "1.0.55",
|
|
36
36
|
"buffer": "6.0.3",
|
|
37
|
-
"cacheable-request": "12.0.1",
|
|
38
37
|
"chalk": "5.4.1",
|
|
39
38
|
"change-case": "5.4.4",
|
|
40
39
|
"chokidar": "3.6.0",
|
|
@@ -56,7 +55,7 @@
|
|
|
56
55
|
"markdown-table": "3.0.4",
|
|
57
56
|
"mdast-util-from-markdown": "2.0.2",
|
|
58
57
|
"mdast-util-to-markdown": "2.1.2",
|
|
59
|
-
"minimatch": "10.
|
|
58
|
+
"minimatch": "10.2.4",
|
|
60
59
|
"ncp": "2.0.0",
|
|
61
60
|
"octokit": "3.1.2",
|
|
62
61
|
"pem-file": "1.0.1",
|
|
@@ -69,7 +68,7 @@
|
|
|
69
68
|
"semver": "7.7.1",
|
|
70
69
|
"stream-to-promise": "3.0.0",
|
|
71
70
|
"string-width": "7.2.0",
|
|
72
|
-
"tar": "7.5.
|
|
71
|
+
"tar": "7.5.11",
|
|
73
72
|
"terminal-size": "4.0.0",
|
|
74
73
|
"vscode-languageserver-textdocument": "1.0.12"
|
|
75
74
|
},
|
|
@@ -78,7 +77,6 @@
|
|
|
78
77
|
"@types/debounce": "1.2.4",
|
|
79
78
|
"@types/decompress": "4.2.7",
|
|
80
79
|
"@types/fs-extra": "11.0.4",
|
|
81
|
-
"@types/glob": "8.1.0",
|
|
82
80
|
"@types/jsdom": "28.0.0",
|
|
83
81
|
"@types/ncp": "2.0.8",
|
|
84
82
|
"@types/node": "24.0.3",
|
package/dist/release-cli.js
CHANGED
|
@@ -29,7 +29,7 @@ fs.cpSync(path.resolve(__dirname, `../cli-releases/versions/${version}.tgz`), pa
|
|
|
29
29
|
fs.writeFileSync(path.resolve(__dirname, `../cli-releases/tags/${tag}`), version);
|
|
30
30
|
console.log(`Release '${version}' created with tag '${tag}'`);
|
|
31
31
|
if (!fs.existsSync(path.resolve(__dirname, "../cli-releases/releases.json"))) {
|
|
32
|
-
fs.writeFileSync(path.resolve(__dirname, "../cli-releases/releases.json"), JSON.stringify({ tags: {}, versions: {} }, null, 2));
|
|
32
|
+
fs.writeFileSync(path.resolve(__dirname, "../cli-releases/releases.json"), JSON.stringify({ tags: {}, versions: {} }, null, 2) + "\n");
|
|
33
33
|
}
|
|
34
34
|
let releases = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../cli-releases/releases.json"), "utf8"));
|
|
35
35
|
releases.tags[tag] = version;
|
|
@@ -41,4 +41,4 @@ releases.versions[version] = {
|
|
|
41
41
|
url: `https://cli.mops.one/versions/${version}.tgz`,
|
|
42
42
|
hash,
|
|
43
43
|
};
|
|
44
|
-
fs.writeFileSync(path.resolve(__dirname, "../cli-releases/releases.json"), JSON.stringify(releases, null, 2));
|
|
44
|
+
fs.writeFileSync(path.resolve(__dirname, "../cli-releases/releases.json"), JSON.stringify(releases, null, 2) + "\n");
|
package/dist/resolve-packages.js
CHANGED
|
@@ -21,8 +21,8 @@ export async function resolvePackages({ conflicts = "ignore", } = {}) {
|
|
|
21
21
|
let packages = {};
|
|
22
22
|
let versions = {};
|
|
23
23
|
let compareVersions = (a = "0.0.0", b = "0.0.0") => {
|
|
24
|
-
let ap = a.split(".").map((x) => parseInt(x));
|
|
25
|
-
let bp = b.split(".").map((x) => parseInt(x));
|
|
24
|
+
let ap = a.split(".").map((x) => parseInt(x) || 0);
|
|
25
|
+
let bp = b.split(".").map((x) => parseInt(x) || 0);
|
|
26
26
|
if (ap[0] - bp[0]) {
|
|
27
27
|
return Math.sign(ap[0] - bp[0]);
|
|
28
28
|
}
|
|
@@ -97,7 +97,7 @@ export async function resolvePackages({ conflicts = "ignore", } = {}) {
|
|
|
97
97
|
}
|
|
98
98
|
else if (version) {
|
|
99
99
|
let cacheDir = getDepCacheName(name, version);
|
|
100
|
-
nestedConfig = readConfig(getDepCacheDir(cacheDir)
|
|
100
|
+
nestedConfig = readConfig(path.join(getDepCacheDir(cacheDir), "mops.toml"));
|
|
101
101
|
}
|
|
102
102
|
// collect nested deps
|
|
103
103
|
if (nestedConfig) {
|
|
@@ -137,7 +137,7 @@ export async function resolvePackages({ conflicts = "ignore", } = {}) {
|
|
|
137
137
|
if (majors.size > 1) {
|
|
138
138
|
console.error(chalk.reset("") +
|
|
139
139
|
chalk.redBright(conflicts === "error" ? "Error!" : "Warning!"), `Conflicting versions of dependency "${dep}"`);
|
|
140
|
-
for (let { version, dependencyOf } of vers.reverse()) {
|
|
140
|
+
for (let { version, dependencyOf } of [...vers].reverse()) {
|
|
141
141
|
console.error(chalk.reset(" ") +
|
|
142
142
|
`${dep} ${chalk.bold.red(version.split(".")[0])}.${version.split(".").slice(1).join(".")} is dependency of ${chalk.bold(dependencyOf)}`);
|
|
143
143
|
}
|
package/dist/tests/build.test.js
CHANGED
|
@@ -11,7 +11,7 @@ function cleanFixture(cwd, ...extras) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
describe("build", () => {
|
|
14
|
-
// Several dfx/pocket-ic builds per test; slow CI
|
|
14
|
+
// Several dfx/pocket-ic builds per test; slow CI can exceed 60s default.
|
|
15
15
|
jest.setTimeout(120_000);
|
|
16
16
|
test("ok", async () => {
|
|
17
17
|
const cwd = path.join(import.meta.dirname, "build/success");
|
package/dist/tests/check.test.js
CHANGED
|
@@ -94,4 +94,28 @@ describe("check", () => {
|
|
|
94
94
|
expect(result.stderr).toMatch(/error/i);
|
|
95
95
|
expect(result.stdout).not.toMatch(/Stable compatibility/);
|
|
96
96
|
});
|
|
97
|
+
test("lint runs after moc check and passes", async () => {
|
|
98
|
+
const cwd = path.join(import.meta.dirname, "check/with-lint-pass");
|
|
99
|
+
const result = await cli(["check"], { cwd });
|
|
100
|
+
expect(result.exitCode).toBe(0);
|
|
101
|
+
expect(result.stdout).toMatch(/✓ Lint succeeded/);
|
|
102
|
+
});
|
|
103
|
+
test("check fails when lint finds errors", async () => {
|
|
104
|
+
const cwd = path.join(import.meta.dirname, "check/with-lint-fail");
|
|
105
|
+
const result = await cli(["check"], { cwd });
|
|
106
|
+
expect(result.exitCode).toBe(1);
|
|
107
|
+
expect(result.stderr).toMatch(/no-bool-switch/);
|
|
108
|
+
});
|
|
109
|
+
test("lint is skipped when lintoko not configured and no rules exist", async () => {
|
|
110
|
+
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
111
|
+
const result = await cli(["check"], { cwd });
|
|
112
|
+
expect(result.exitCode).toBe(0);
|
|
113
|
+
expect(result.stdout).not.toMatch(/Lint/);
|
|
114
|
+
});
|
|
115
|
+
test("--fix flag reaches lint step", async () => {
|
|
116
|
+
const cwd = path.join(import.meta.dirname, "check/with-lint-pass");
|
|
117
|
+
const result = await cli(["check", "--fix"], { cwd });
|
|
118
|
+
expect(result.exitCode).toBe(0);
|
|
119
|
+
expect(result.stdout).toMatch(/✓ Lint fixes applied/);
|
|
120
|
+
});
|
|
97
121
|
});
|
package/dist/tests/helpers.js
CHANGED
|
@@ -2,8 +2,15 @@ import { expect } from "@jest/globals";
|
|
|
2
2
|
import { execa } from "execa";
|
|
3
3
|
import { dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
// When MOPS_TEST_GLOBAL is set, invoke the globally-installed `mops` binary
|
|
6
|
+
// directly rather than the npm script. This exercises the real global-install
|
|
7
|
+
// code path where the binary lives outside the project tree.
|
|
8
|
+
const useGlobalBinary = Boolean(process.env.MOPS_TEST_GLOBAL);
|
|
5
9
|
export const cli = async (args, { cwd } = {}) => {
|
|
6
|
-
|
|
10
|
+
const [cmd, cmdArgs] = useGlobalBinary
|
|
11
|
+
? ["mops", args]
|
|
12
|
+
: ["npm", ["run", "--silent", "mops", "--", ...args]];
|
|
13
|
+
return await execa(cmd, cmdArgs, {
|
|
7
14
|
env: { ...process.env, ...(cwd != null && { MOPS_CWD: cwd }) },
|
|
8
15
|
...(cwd != null && { cwd }),
|
|
9
16
|
stdio: "pipe",
|
package/dist/tests/lint.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { describe, test } from "@jest/globals";
|
|
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("lint", () => {
|
|
5
5
|
test("ok", async () => {
|
|
6
6
|
const cwd = path.join(import.meta.dirname, "lint");
|
|
@@ -12,4 +12,30 @@ describe("lint", () => {
|
|
|
12
12
|
await cliSnapshot(["lint", "NoBoolSwitch", "--verbose"], { cwd }, 1);
|
|
13
13
|
await cliSnapshot(["lint", "DoesNotExist"], { cwd }, 1);
|
|
14
14
|
});
|
|
15
|
+
test("[lint] rules - additional config rules directory is used", async () => {
|
|
16
|
+
const cwd = path.join(import.meta.dirname, "lint-config-rules");
|
|
17
|
+
const result = await cli(["lint"], { cwd });
|
|
18
|
+
expect(result.exitCode).toBe(1);
|
|
19
|
+
expect(result.stderr).toMatch(/no-bool-switch/);
|
|
20
|
+
});
|
|
21
|
+
test("[lint] extends - picks up rules/ from named dependency", async () => {
|
|
22
|
+
const cwd = path.join(import.meta.dirname, "lint-extends");
|
|
23
|
+
const result = await cli(["lint"], { cwd });
|
|
24
|
+
expect(result.exitCode).toBe(1);
|
|
25
|
+
expect(result.stderr).toMatch(/no-bool-switch/);
|
|
26
|
+
});
|
|
27
|
+
test("[lint] extends true - picks up rules/ from all dependencies", async () => {
|
|
28
|
+
const cwd = path.join(import.meta.dirname, "lint-extends-all");
|
|
29
|
+
const result = await cli(["lint"], { cwd });
|
|
30
|
+
expect(result.exitCode).toBe(1);
|
|
31
|
+
expect(result.stderr).toMatch(/no-bool-switch/);
|
|
32
|
+
});
|
|
33
|
+
test("[lint] extends - dep not in extends list is ignored", async () => {
|
|
34
|
+
// my-pkg has rules/ but extends only lists "other-pkg" (which doesn't exist),
|
|
35
|
+
// so no dep rules are loaded and NoBoolSwitch.mo passes with exit 0.
|
|
36
|
+
const cwd = path.join(import.meta.dirname, "lint-extends-ignored");
|
|
37
|
+
const result = await cli(["lint"], { cwd });
|
|
38
|
+
expect(result.exitCode).toBe(0);
|
|
39
|
+
expect(result.stderr).toMatch(/not found in dependencies/);
|
|
40
|
+
});
|
|
15
41
|
});
|
package/dist/types.d.ts
CHANGED
package/dist/vessel.d.ts
CHANGED
package/dist/vessel.js
CHANGED
|
@@ -141,7 +141,7 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
|
|
|
141
141
|
}
|
|
142
142
|
catch (err) {
|
|
143
143
|
deleteSync([cacheDir], { force: true });
|
|
144
|
-
|
|
144
|
+
return false;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
if (verbose) {
|
|
@@ -151,7 +151,7 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
|
|
|
151
151
|
logUpdate.clear();
|
|
152
152
|
}
|
|
153
153
|
if (ignoreTransitive) {
|
|
154
|
-
return;
|
|
154
|
+
return true;
|
|
155
155
|
}
|
|
156
156
|
const config = await readVesselConfig(cacheDir, { silent });
|
|
157
157
|
if (config) {
|
|
@@ -161,4 +161,5 @@ export const installFromGithub = async (name, repo, { verbose = false, dep = fal
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
+
return true;
|
|
164
165
|
};
|
|
@@ -16,7 +16,9 @@ export function findChangelogEntry(changelog: string, version: string): string {
|
|
|
16
16
|
}
|
|
17
17
|
} else if (
|
|
18
18
|
node.type === "heading" &&
|
|
19
|
-
toMarkdown(node).match(
|
|
19
|
+
toMarkdown(node).match(
|
|
20
|
+
new RegExp(`\\b${version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`),
|
|
21
|
+
)
|
|
20
22
|
) {
|
|
21
23
|
depth = node.depth;
|
|
22
24
|
found = true;
|
package/integrity.ts
CHANGED
|
@@ -87,7 +87,9 @@ export function getLocalFileHash(fileId: string): string {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function getMopsTomlHash(): string {
|
|
90
|
-
return bytesToHex(
|
|
90
|
+
return bytesToHex(
|
|
91
|
+
sha256(fs.readFileSync(path.join(getRootDir(), "mops.toml"))),
|
|
92
|
+
);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
function getMopsTomlDepsHash(): string {
|
|
@@ -137,7 +139,14 @@ export function readLockFile(): LockFile | null {
|
|
|
137
139
|
let rootDir = getRootDir();
|
|
138
140
|
let lockFile = path.join(rootDir, "mops.lock");
|
|
139
141
|
if (fs.existsSync(lockFile)) {
|
|
140
|
-
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(fs.readFileSync(lockFile).toString()) as LockFile;
|
|
144
|
+
} catch {
|
|
145
|
+
console.error(
|
|
146
|
+
"mops.lock is corrupted. Delete it and run `mops install` to regenerate.",
|
|
147
|
+
);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
151
|
return null;
|
|
143
152
|
}
|
|
@@ -290,7 +299,7 @@ export async function checkLockFile(force = false) {
|
|
|
290
299
|
|
|
291
300
|
for (let [fileId, lockedHash] of Object.entries(hashes)) {
|
|
292
301
|
// check if file belongs to package
|
|
293
|
-
if (!fileId.startsWith(packageId)) {
|
|
302
|
+
if (!fileId.startsWith(packageId + "/")) {
|
|
294
303
|
console.error("Integrity check failed");
|
|
295
304
|
console.error(
|
|
296
305
|
`File ${fileId} in lock file does not belong to package ${packageId}`,
|
package/mops.ts
CHANGED
|
@@ -152,6 +152,13 @@ export async function getGithubCommit(repo: string, ref: string): Promise<any> {
|
|
|
152
152
|
res = await fetch(`https://api.github.com/repos/${repo}/commits/main`);
|
|
153
153
|
json = await res.json();
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
if (!res.ok || !json.sha) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Failed to fetch commit for ${repo}#${ref}: ${json.message || `HTTP ${res.status}`}`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
155
162
|
return json;
|
|
156
163
|
}
|
|
157
164
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "dist/bin/mops.js",
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"@noble/hashes": "1.8.0",
|
|
56
56
|
"as-table": "1.0.55",
|
|
57
57
|
"buffer": "6.0.3",
|
|
58
|
-
"cacheable-request": "12.0.1",
|
|
59
58
|
"chalk": "5.4.1",
|
|
60
59
|
"change-case": "5.4.4",
|
|
61
60
|
"chokidar": "3.6.0",
|
|
@@ -77,7 +76,7 @@
|
|
|
77
76
|
"markdown-table": "3.0.4",
|
|
78
77
|
"mdast-util-from-markdown": "2.0.2",
|
|
79
78
|
"mdast-util-to-markdown": "2.1.2",
|
|
80
|
-
"minimatch": "10.
|
|
79
|
+
"minimatch": "10.2.4",
|
|
81
80
|
"ncp": "2.0.0",
|
|
82
81
|
"octokit": "3.1.2",
|
|
83
82
|
"pem-file": "1.0.1",
|
|
@@ -90,7 +89,7 @@
|
|
|
90
89
|
"semver": "7.7.1",
|
|
91
90
|
"stream-to-promise": "3.0.0",
|
|
92
91
|
"string-width": "7.2.0",
|
|
93
|
-
"tar": "7.5.
|
|
92
|
+
"tar": "7.5.11",
|
|
94
93
|
"terminal-size": "4.0.0",
|
|
95
94
|
"vscode-languageserver-textdocument": "1.0.12"
|
|
96
95
|
},
|
|
@@ -99,7 +98,6 @@
|
|
|
99
98
|
"@types/debounce": "1.2.4",
|
|
100
99
|
"@types/decompress": "4.2.7",
|
|
101
100
|
"@types/fs-extra": "11.0.4",
|
|
102
|
-
"@types/glob": "8.1.0",
|
|
103
101
|
"@types/jsdom": "28.0.0",
|
|
104
102
|
"@types/ncp": "2.0.8",
|
|
105
103
|
"@types/node": "24.0.3",
|
package/release-cli.ts
CHANGED
|
@@ -79,7 +79,7 @@ type Releases = {
|
|
|
79
79
|
if (!fs.existsSync(path.resolve(__dirname, "../cli-releases/releases.json"))) {
|
|
80
80
|
fs.writeFileSync(
|
|
81
81
|
path.resolve(__dirname, "../cli-releases/releases.json"),
|
|
82
|
-
JSON.stringify({ tags: {}, versions: {} }, null, 2),
|
|
82
|
+
JSON.stringify({ tags: {}, versions: {} }, null, 2) + "\n",
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -102,5 +102,5 @@ releases.versions[version] = {
|
|
|
102
102
|
|
|
103
103
|
fs.writeFileSync(
|
|
104
104
|
path.resolve(__dirname, "../cli-releases/releases.json"),
|
|
105
|
-
JSON.stringify(releases, null, 2),
|
|
105
|
+
JSON.stringify(releases, null, 2) + "\n",
|
|
106
106
|
);
|
package/resolve-packages.ts
CHANGED
|
@@ -41,12 +41,12 @@ export async function resolvePackages({
|
|
|
41
41
|
> = {};
|
|
42
42
|
|
|
43
43
|
let compareVersions = (a: string = "0.0.0", b: string = "0.0.0") => {
|
|
44
|
-
let ap = a.split(".").map((x: string) => parseInt(x)) as [
|
|
44
|
+
let ap = a.split(".").map((x: string) => parseInt(x) || 0) as [
|
|
45
45
|
number,
|
|
46
46
|
number,
|
|
47
47
|
number,
|
|
48
48
|
];
|
|
49
|
-
let bp = b.split(".").map((x: string) => parseInt(x)) as [
|
|
49
|
+
let bp = b.split(".").map((x: string) => parseInt(x) || 0) as [
|
|
50
50
|
number,
|
|
51
51
|
number,
|
|
52
52
|
number,
|
|
@@ -138,7 +138,9 @@ export async function resolvePackages({
|
|
|
138
138
|
}
|
|
139
139
|
} else if (version) {
|
|
140
140
|
let cacheDir = getDepCacheName(name, version);
|
|
141
|
-
nestedConfig = readConfig(
|
|
141
|
+
nestedConfig = readConfig(
|
|
142
|
+
path.join(getDepCacheDir(cacheDir), "mops.toml"),
|
|
143
|
+
);
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
// collect nested deps
|
|
@@ -193,7 +195,7 @@ export async function resolvePackages({
|
|
|
193
195
|
`Conflicting versions of dependency "${dep}"`,
|
|
194
196
|
);
|
|
195
197
|
|
|
196
|
-
for (let { version, dependencyOf } of vers.reverse()) {
|
|
198
|
+
for (let { version, dependencyOf } of [...vers].reverse()) {
|
|
197
199
|
console.error(
|
|
198
200
|
chalk.reset(" ") +
|
|
199
201
|
`${dep} ${chalk.bold.red(version.split(".")[0])}.${version.split(".").slice(1).join(".")} is dependency of ${chalk.bold(dependencyOf)}`,
|
package/tests/build.test.ts
CHANGED
|
@@ -14,7 +14,7 @@ function cleanFixture(cwd: string, ...extras: string[]) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
describe("build", () => {
|
|
17
|
-
// Several dfx/pocket-ic builds per test; slow CI
|
|
17
|
+
// Several dfx/pocket-ic builds per test; slow CI can exceed 60s default.
|
|
18
18
|
jest.setTimeout(120_000);
|
|
19
19
|
|
|
20
20
|
test("ok", async () => {
|
package/tests/check.test.ts
CHANGED
|
@@ -121,4 +121,32 @@ describe("check", () => {
|
|
|
121
121
|
expect(result.stderr).toMatch(/error/i);
|
|
122
122
|
expect(result.stdout).not.toMatch(/Stable compatibility/);
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
test("lint runs after moc check and passes", async () => {
|
|
126
|
+
const cwd = path.join(import.meta.dirname, "check/with-lint-pass");
|
|
127
|
+
const result = await cli(["check"], { cwd });
|
|
128
|
+
expect(result.exitCode).toBe(0);
|
|
129
|
+
expect(result.stdout).toMatch(/✓ Lint succeeded/);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("check fails when lint finds errors", async () => {
|
|
133
|
+
const cwd = path.join(import.meta.dirname, "check/with-lint-fail");
|
|
134
|
+
const result = await cli(["check"], { cwd });
|
|
135
|
+
expect(result.exitCode).toBe(1);
|
|
136
|
+
expect(result.stderr).toMatch(/no-bool-switch/);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("lint is skipped when lintoko not configured and no rules exist", async () => {
|
|
140
|
+
const cwd = path.join(import.meta.dirname, "check/canisters");
|
|
141
|
+
const result = await cli(["check"], { cwd });
|
|
142
|
+
expect(result.exitCode).toBe(0);
|
|
143
|
+
expect(result.stdout).not.toMatch(/Lint/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("--fix flag reaches lint step", async () => {
|
|
147
|
+
const cwd = path.join(import.meta.dirname, "check/with-lint-pass");
|
|
148
|
+
const result = await cli(["check", "--fix"], { cwd });
|
|
149
|
+
expect(result.exitCode).toBe(0);
|
|
150
|
+
expect(result.stdout).toMatch(/✓ Lint fixes applied/);
|
|
151
|
+
});
|
|
124
152
|
});
|
package/tests/helpers.ts
CHANGED
|
@@ -7,8 +7,16 @@ export interface CliOptions {
|
|
|
7
7
|
cwd?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// When MOPS_TEST_GLOBAL is set, invoke the globally-installed `mops` binary
|
|
11
|
+
// directly rather than the npm script. This exercises the real global-install
|
|
12
|
+
// code path where the binary lives outside the project tree.
|
|
13
|
+
const useGlobalBinary = Boolean(process.env.MOPS_TEST_GLOBAL);
|
|
14
|
+
|
|
10
15
|
export const cli = async (args: string[], { cwd }: CliOptions = {}) => {
|
|
11
|
-
|
|
16
|
+
const [cmd, cmdArgs] = useGlobalBinary
|
|
17
|
+
? ["mops", args]
|
|
18
|
+
: ["npm", ["run", "--silent", "mops", "--", ...args]];
|
|
19
|
+
return await execa(cmd, cmdArgs, {
|
|
12
20
|
env: { ...process.env, ...(cwd != null && { MOPS_CWD: cwd }) },
|
|
13
21
|
...(cwd != null && { cwd }),
|
|
14
22
|
stdio: "pipe",
|