ic-mops 2.15.2 → 2.16.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 CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Next
4
4
 
5
+ ## 2.16.0
6
+
7
+ - `mops bench` now compiles benchmark canisters under **enhanced orthogonal persistence** (moc's default) instead of forcing `--legacy-persistence` — measuring the persistence mode real canisters run. Pass `--legacy-persistence` to opt back into legacy persistence.
8
+ - `mops bench --query` measures each cell in a **query** call instead of an update call. Queries run no GC on the IC, so the instruction counts exclude GC work an update would incur — for benchmarking `query`/read-only workloads realistically. Only for synchronous benchmark runners (no inter-canister `await`).
9
+ - `[requirements].lintoko` declares a minimum lintoko version for package consumers. `mops install` (and `mops add`, `mops toolchain use`) warn when the project's lintoko is below a dependency's requirement, same as `moc` (#597).
10
+
5
11
  ## 2.15.2
6
12
 
7
13
  - `mops check-stable` (and the stable check inside `mops check`) reports when `[canisters.<name>.migrations].check-limit` is set but more migrations are pending than the limit allows. If the compatibility check failed, the check-limit diagnostic replaces the misleading `moc` error; if it passed anyway, a warning is shown. Compares the deployed `.most` baseline against the local chain; use `--no-check-limit` to suppress.
package/bundle/cli.tgz CHANGED
Binary file
@@ -5,47 +5,69 @@ import chalk from "chalk";
5
5
  import { getDependencyType, getRootDir, readConfig } from "./mops.js";
6
6
  import { resolvePackages } from "./resolve-packages.js";
7
7
  import { getMocSemVer } from "./helpers/get-moc-version.js";
8
+ import { getLintokoSemVer } from "./helpers/get-lintoko-version.js";
8
9
  import { getPackageId } from "./helpers/get-package-id.js";
10
+ import type { Requirements } from "./types.js";
11
+
12
+ type ToolRequirement = keyof Requirements;
13
+
14
+ const TOOL_REQUIREMENTS: {
15
+ tool: ToolRequirement;
16
+ getInstalled: () => SemVer | null;
17
+ }[] = [
18
+ { tool: "moc", getInstalled: getMocSemVer },
19
+ { tool: "lintoko", getInstalled: getLintokoSemVer },
20
+ ];
9
21
 
10
22
  export async function checkRequirements({ verbose = false } = {}) {
11
- let installedMoc = getMocSemVer();
12
- if (!installedMoc) {
13
- return;
14
- }
15
- let highestRequiredMoc = new SemVer("0.0.0");
16
- let highestRequiredMocPkgId = "";
17
23
  let rootDir = getRootDir();
18
-
19
24
  let resolvedPackages = await resolvePackages();
20
- for (let [name, version] of Object.entries(resolvedPackages)) {
21
- if (getDependencyType(version) === "mops") {
22
- let pkgId = getPackageId(name, version);
23
- let depConfig = readConfig(
24
- path.join(rootDir, ".mops", pkgId, "mops.toml"),
25
- );
26
- let moc = depConfig.requirements?.moc;
27
-
28
- if (moc) {
29
- let requiredMoc = new SemVer(moc);
30
- if (highestRequiredMoc.compare(requiredMoc) < 0) {
31
- highestRequiredMoc = requiredMoc;
32
- highestRequiredMocPkgId = pkgId;
25
+
26
+ for (let { tool, getInstalled } of TOOL_REQUIREMENTS) {
27
+ let installed = getInstalled();
28
+ if (!installed) {
29
+ continue;
30
+ }
31
+
32
+ let highestRequired = new SemVer("0.0.0");
33
+ let highestRequiredPkgId = "";
34
+
35
+ for (let [name, version] of Object.entries(resolvedPackages)) {
36
+ if (getDependencyType(version) === "mops") {
37
+ let pkgId = getPackageId(name, version);
38
+ let depConfig = readConfig(
39
+ path.join(rootDir, ".mops", pkgId, "mops.toml"),
40
+ );
41
+ let required = depConfig.requirements?.[tool];
42
+
43
+ if (required) {
44
+ let requiredVersion = new SemVer(required);
45
+ if (highestRequired.compare(requiredVersion) < 0) {
46
+ highestRequired = requiredVersion;
47
+ highestRequiredPkgId = pkgId;
48
+ }
49
+ verbose && _check(tool, pkgId, installed, requiredVersion);
33
50
  }
34
- verbose && _check(pkgId, installedMoc, requiredMoc);
35
51
  }
36
52
  }
37
- }
38
53
 
39
- verbose || _check(highestRequiredMocPkgId, installedMoc, highestRequiredMoc);
54
+ verbose || _check(tool, highestRequiredPkgId, installed, highestRequired);
55
+ }
40
56
  }
41
57
 
42
- function _check(pkgId: string, installedMoc: SemVer, requiredMoc: SemVer) {
43
- let comp = installedMoc.compare(requiredMoc);
44
- if (comp < 0) {
45
- console.log(
46
- chalk.yellow(`moc version does not meet the requirements of ${pkgId}`),
47
- );
48
- console.log(chalk.yellow(` Required: >= ${requiredMoc.format()}`));
49
- console.log(chalk.yellow(` Installed: ${installedMoc.format()}`));
58
+ function _check(
59
+ tool: ToolRequirement,
60
+ pkgId: string,
61
+ installed: SemVer,
62
+ required: SemVer,
63
+ ) {
64
+ if (!pkgId || installed.compare(required) >= 0) {
65
+ return;
50
66
  }
67
+
68
+ console.log(
69
+ chalk.yellow(`${tool} version does not meet the requirements of ${pkgId}`),
70
+ );
71
+ console.log(chalk.yellow(` Required: >= ${required.format()}`));
72
+ console.log(chalk.yellow(` Installed: ${installed.format()}`));
51
73
  }
package/cli.ts CHANGED
@@ -558,6 +558,18 @@ program
558
558
  ),
559
559
  )
560
560
  // .addOption(new Option('--force-gc', 'Force GC'))
561
+ .addOption(
562
+ new Option(
563
+ "--query",
564
+ "Measure each cell in a query call (how `query` methods run on the IC: no GC). Only for benchmarks whose runner is synchronous (no inter-canister calls)",
565
+ ),
566
+ )
567
+ .addOption(
568
+ new Option(
569
+ "--legacy-persistence",
570
+ "Compile benchmark canisters under legacy persistence instead of enhanced orthogonal persistence (the default)",
571
+ ),
572
+ )
561
573
  .addOption(
562
574
  new Option(
563
575
  "--verbose",
package/commands/bench.ts CHANGED
@@ -40,6 +40,8 @@ type BenchOptions = {
40
40
  compilerVersion: string;
41
41
  gc: "copying" | "compacting" | "generational" | "incremental";
42
42
  forceGc: boolean;
43
+ query: boolean;
44
+ legacyPersistence: boolean;
43
45
  save: boolean;
44
46
  compare: boolean;
45
47
  verbose: boolean;
@@ -61,6 +63,8 @@ export async function bench(
61
63
  compilerVersion: getMocVersion(true),
62
64
  gc: "copying",
63
65
  forceGc: true,
66
+ query: false,
67
+ legacyPersistence: false,
64
68
  save: false,
65
69
  compare: false,
66
70
  verbose: false,
@@ -120,6 +124,14 @@ export async function bench(
120
124
  ` gc: ${options.gc}${options.forceGc ? " (forced)" : ""}`,
121
125
  ),
122
126
  );
127
+ console.log(
128
+ chalk.gray(` context: ${options.query ? "query" : "update"}`),
129
+ );
130
+ console.log(
131
+ chalk.gray(
132
+ ` persistence: ${options.legacyPersistence ? "legacy" : "enhanced"}`,
133
+ ),
134
+ );
123
135
  console.log(chalk.gray(` profile: ${options.profile}`));
124
136
  console.log(chalk.gray(` optimize: ${optimize}`));
125
137
  }
@@ -270,7 +282,12 @@ function computeDiffAll(
270
282
  function getMocArgs(options: BenchOptions): string {
271
283
  let args = "";
272
284
 
285
+ // Benchmarks compile under enhanced orthogonal persistence (moc's default
286
+ // since 0.15) — the mode real canisters run. Pass `--legacy-persistence`
287
+ // only when the user opts in, and only where moc supports the flag (>= 0.15;
288
+ // legacy is already the default below it).
273
289
  if (
290
+ options.legacyPersistence &&
274
291
  options.compilerVersion &&
275
292
  new SemVer(options.compilerVersion).compare("0.15.0") >= 0
276
293
  ) {
@@ -533,13 +550,14 @@ async function runBenchFile(
533
550
  log();
534
551
  }
535
552
 
536
- // run all cells
553
+ // run all cells. `--query` measures in query context (how `query` methods
554
+ // actually run on the IC: no GC), at the cost of not supporting benches whose
555
+ // runner performs async calls. Otherwise use the update path.
537
556
  for (let [rowIndex, row] of schema.rows.entries()) {
538
557
  for (let [colIndex, col] of schema.cols.entries()) {
539
- let res = await actor.runCellUpdateAwait(
540
- BigInt(rowIndex),
541
- BigInt(colIndex),
542
- );
558
+ let res = options.query
559
+ ? await actor.runCellQuery(BigInt(rowIndex), BigInt(colIndex))
560
+ : await actor.runCellUpdateAwait(BigInt(rowIndex), BigInt(colIndex));
543
561
  results.set(`${row}:${col}`, res);
544
562
 
545
563
  // @ts-ignore
@@ -337,9 +337,7 @@ async function update(tool?: Tool) {
337
337
  config.toolchain[tool] = version;
338
338
  writeConfig(config);
339
339
 
340
- if (tool === "moc") {
341
- await checkRequirements();
342
- }
340
+ await checkRequirements();
343
341
 
344
342
  if (oldVersion === version) {
345
343
  console.log(`Latest ${tool} ${version} is already installed`);
@@ -4,38 +4,45 @@ import chalk from "chalk";
4
4
  import { getDependencyType, getRootDir, readConfig } from "./mops.js";
5
5
  import { resolvePackages } from "./resolve-packages.js";
6
6
  import { getMocSemVer } from "./helpers/get-moc-version.js";
7
+ import { getLintokoSemVer } from "./helpers/get-lintoko-version.js";
7
8
  import { getPackageId } from "./helpers/get-package-id.js";
9
+ const TOOL_REQUIREMENTS = [
10
+ { tool: "moc", getInstalled: getMocSemVer },
11
+ { tool: "lintoko", getInstalled: getLintokoSemVer },
12
+ ];
8
13
  export async function checkRequirements({ verbose = false } = {}) {
9
- let installedMoc = getMocSemVer();
10
- if (!installedMoc) {
11
- return;
12
- }
13
- let highestRequiredMoc = new SemVer("0.0.0");
14
- let highestRequiredMocPkgId = "";
15
14
  let rootDir = getRootDir();
16
15
  let resolvedPackages = await resolvePackages();
17
- for (let [name, version] of Object.entries(resolvedPackages)) {
18
- if (getDependencyType(version) === "mops") {
19
- let pkgId = getPackageId(name, version);
20
- let depConfig = readConfig(path.join(rootDir, ".mops", pkgId, "mops.toml"));
21
- let moc = depConfig.requirements?.moc;
22
- if (moc) {
23
- let requiredMoc = new SemVer(moc);
24
- if (highestRequiredMoc.compare(requiredMoc) < 0) {
25
- highestRequiredMoc = requiredMoc;
26
- highestRequiredMocPkgId = pkgId;
16
+ for (let { tool, getInstalled } of TOOL_REQUIREMENTS) {
17
+ let installed = getInstalled();
18
+ if (!installed) {
19
+ continue;
20
+ }
21
+ let highestRequired = new SemVer("0.0.0");
22
+ let highestRequiredPkgId = "";
23
+ for (let [name, version] of Object.entries(resolvedPackages)) {
24
+ if (getDependencyType(version) === "mops") {
25
+ let pkgId = getPackageId(name, version);
26
+ let depConfig = readConfig(path.join(rootDir, ".mops", pkgId, "mops.toml"));
27
+ let required = depConfig.requirements?.[tool];
28
+ if (required) {
29
+ let requiredVersion = new SemVer(required);
30
+ if (highestRequired.compare(requiredVersion) < 0) {
31
+ highestRequired = requiredVersion;
32
+ highestRequiredPkgId = pkgId;
33
+ }
34
+ verbose && _check(tool, pkgId, installed, requiredVersion);
27
35
  }
28
- verbose && _check(pkgId, installedMoc, requiredMoc);
29
36
  }
30
37
  }
38
+ verbose || _check(tool, highestRequiredPkgId, installed, highestRequired);
31
39
  }
32
- verbose || _check(highestRequiredMocPkgId, installedMoc, highestRequiredMoc);
33
40
  }
34
- function _check(pkgId, installedMoc, requiredMoc) {
35
- let comp = installedMoc.compare(requiredMoc);
36
- if (comp < 0) {
37
- console.log(chalk.yellow(`moc version does not meet the requirements of ${pkgId}`));
38
- console.log(chalk.yellow(` Required: >= ${requiredMoc.format()}`));
39
- console.log(chalk.yellow(` Installed: ${installedMoc.format()}`));
41
+ function _check(tool, pkgId, installed, required) {
42
+ if (!pkgId || installed.compare(required) >= 0) {
43
+ return;
40
44
  }
45
+ console.log(chalk.yellow(`${tool} version does not meet the requirements of ${pkgId}`));
46
+ console.log(chalk.yellow(` Required: >= ${required.format()}`));
47
+ console.log(chalk.yellow(` Installed: ${installed.format()}`));
41
48
  }
package/dist/cli.js CHANGED
@@ -401,6 +401,8 @@ program
401
401
  .addOption(new Option("--save", "Save benchmark results to .bench/<filename>.json"))
402
402
  .addOption(new Option("--compare", "Run benchmark and compare results with .bench/<filename>.json"))
403
403
  // .addOption(new Option('--force-gc', 'Force GC'))
404
+ .addOption(new Option("--query", "Measure each cell in a query call (how `query` methods run on the IC: no GC). Only for benchmarks whose runner is synchronous (no inter-canister calls)"))
405
+ .addOption(new Option("--legacy-persistence", "Compile benchmark canisters under legacy persistence instead of enhanced orthogonal persistence (the default)"))
404
406
  .addOption(new Option("--verbose", "Print the benchmark pipeline (compiler, replica, GC, optimization) and stream compiler/replica output, including dfx optimization warnings"))
405
407
  .action(async (filter, options) => {
406
408
  checkConfigFile(true);
@@ -7,6 +7,8 @@ type BenchOptions = {
7
7
  compilerVersion: string;
8
8
  gc: "copying" | "compacting" | "generational" | "incremental";
9
9
  forceGc: boolean;
10
+ query: boolean;
11
+ legacyPersistence: boolean;
10
12
  save: boolean;
11
13
  compare: boolean;
12
14
  verbose: boolean;
@@ -31,6 +31,8 @@ export async function bench(filter = "", optionsArg = {}) {
31
31
  compilerVersion: getMocVersion(true),
32
32
  gc: "copying",
33
33
  forceGc: true,
34
+ query: false,
35
+ legacyPersistence: false,
34
36
  save: false,
35
37
  compare: false,
36
38
  verbose: false,
@@ -71,6 +73,8 @@ export async function bench(filter = "", optionsArg = {}) {
71
73
  console.log(chalk.gray(` compiler: moc ${options.compilerVersion}`));
72
74
  console.log(chalk.gray(` replica: ${replicaType}${options.replicaVersion ? ` ${options.replicaVersion}` : ""}`));
73
75
  console.log(chalk.gray(` gc: ${options.gc}${options.forceGc ? " (forced)" : ""}`));
76
+ console.log(chalk.gray(` context: ${options.query ? "query" : "update"}`));
77
+ console.log(chalk.gray(` persistence: ${options.legacyPersistence ? "legacy" : "enhanced"}`));
74
78
  console.log(chalk.gray(` profile: ${options.profile}`));
75
79
  console.log(chalk.gray(` optimize: ${optimize}`));
76
80
  }
@@ -188,7 +192,12 @@ function computeDiffAll(currentResults, prevResults, rows, cols) {
188
192
  }
189
193
  function getMocArgs(options) {
190
194
  let args = "";
191
- if (options.compilerVersion &&
195
+ // Benchmarks compile under enhanced orthogonal persistence (moc's default
196
+ // since 0.15) — the mode real canisters run. Pass `--legacy-persistence`
197
+ // only when the user opts in, and only where moc supports the flag (>= 0.15;
198
+ // legacy is already the default below it).
199
+ if (options.legacyPersistence &&
200
+ options.compilerVersion &&
192
201
  new SemVer(options.compilerVersion).compare("0.15.0") >= 0) {
193
202
  args += " --legacy-persistence";
194
203
  }
@@ -386,10 +395,14 @@ async function runBenchFile(file, options, replica) {
386
395
  if (canUpdateLog) {
387
396
  log();
388
397
  }
389
- // run all cells
398
+ // run all cells. `--query` measures in query context (how `query` methods
399
+ // actually run on the IC: no GC), at the cost of not supporting benches whose
400
+ // runner performs async calls. Otherwise use the update path.
390
401
  for (let [rowIndex, row] of schema.rows.entries()) {
391
402
  for (let [colIndex, col] of schema.cols.entries()) {
392
- let res = await actor.runCellUpdateAwait(BigInt(rowIndex), BigInt(colIndex));
403
+ let res = options.query
404
+ ? await actor.runCellQuery(BigInt(rowIndex), BigInt(colIndex))
405
+ : await actor.runCellUpdateAwait(BigInt(rowIndex), BigInt(colIndex));
393
406
  results.set(`${row}:${col}`, res);
394
407
  // @ts-ignore
395
408
  instructionsCells[rowIndex][colIndex] = res.instructions;
@@ -259,9 +259,7 @@ async function update(tool) {
259
259
  let oldVersion = config.toolchain[tool];
260
260
  config.toolchain[tool] = version;
261
261
  writeConfig(config);
262
- if (tool === "moc") {
263
- await checkRequirements();
264
- }
262
+ await checkRequirements();
265
263
  if (oldVersion === version) {
266
264
  console.log(`Latest ${tool} ${version} is already installed`);
267
265
  }
@@ -0,0 +1,3 @@
1
+ import { type SemVer } from "semver";
2
+ export declare function getLintokoSemVer(): SemVer | null;
3
+ export declare function getLintokoVersion(throwOnError?: boolean): string;
@@ -0,0 +1,30 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { parse } from "semver";
3
+ import { FILE_PATH_REGEX } from "../constants.js";
4
+ import { readConfig } from "../mops.js";
5
+ export function getLintokoSemVer() {
6
+ return parse(getLintokoVersion(false));
7
+ }
8
+ export function getLintokoVersion(throwOnError = false) {
9
+ let configVersion = readConfig().toolchain?.lintoko;
10
+ if (!configVersion) {
11
+ return "";
12
+ }
13
+ if (!configVersion.match(FILE_PATH_REGEX)) {
14
+ return configVersion;
15
+ }
16
+ try {
17
+ let match = execFileSync(configVersion, ["--version"])
18
+ .toString()
19
+ .trim()
20
+ .match(/lintoko ([^\s]+)/);
21
+ return match?.[1] || "";
22
+ }
23
+ catch (e) {
24
+ if (throwOnError) {
25
+ console.error(e);
26
+ throw new Error("lintoko not found");
27
+ }
28
+ return "";
29
+ }
30
+ }
package/dist/mops.js CHANGED
@@ -177,9 +177,9 @@ export function readConfig(configFile = getClosestConfigFile()) {
177
177
  processDeps(toml["dev-dependencies"] || {});
178
178
  let config = { ...toml };
179
179
  Object.entries(config.requirements || {}).forEach(([name, value]) => {
180
- if (name === "moc") {
180
+ if (name === "moc" || name === "lintoko") {
181
181
  config.requirements = config.requirements || {};
182
- config.requirements.moc = value;
182
+ config.requirements[name] = value;
183
183
  }
184
184
  });
185
185
  return config;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.15.2",
3
+ "version": "2.16.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "bin/mops.js",
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
2
+ import { cp, mkdir, rm, writeFile } from "node:fs/promises";
3
+ import path from "path";
4
+ import { bytesToHex } from "@noble/hashes/utils";
5
+ import { sha256 } from "@noble/hashes/sha256";
6
+ import { cli } from "./helpers";
7
+ describe("requirements", () => {
8
+ const fixtureDir = path.join(import.meta.dirname, "requirements-lintoko");
9
+ let tempDir;
10
+ beforeEach(async () => {
11
+ tempDir = path.join(import.meta.dirname, `_tmp_requirements_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`);
12
+ await cp(fixtureDir, tempDir, { recursive: true });
13
+ const depDir = path.join(tempDir, ".mops", "my-pkg@0.1.0");
14
+ await mkdir(depDir, { recursive: true });
15
+ await writeFile(path.join(depDir, "mops.toml"), `[package]
16
+ name = "my-pkg"
17
+ version = "0.1.0"
18
+
19
+ [requirements]
20
+ lintoko = "0.10.0"
21
+ `);
22
+ const mopsTomlDepsHash = bytesToHex(sha256(JSON.stringify({ "my-pkg": "0.1.0" })));
23
+ await writeFile(path.join(tempDir, "mops.lock"), JSON.stringify({
24
+ version: 3,
25
+ mopsTomlDepsHash,
26
+ deps: { "my-pkg": "0.1.0" },
27
+ hashes: {
28
+ "my-pkg@0.1.0": {
29
+ "mops.toml": "0000000000000000000000000000000000000000000000000000000000000000",
30
+ },
31
+ },
32
+ }));
33
+ });
34
+ afterEach(async () => {
35
+ await rm(tempDir, { recursive: true, force: true });
36
+ });
37
+ test("lintoko requirement warns when installed version is too old", async () => {
38
+ const result = await cli(["toolchain", "use", "lintoko", "0.7.0"], {
39
+ cwd: tempDir,
40
+ });
41
+ expect(result.exitCode).toBe(0);
42
+ expect(result.stdout).toMatch(/lintoko version does not meet the requirements of my-pkg@0\.1\.0/);
43
+ expect(result.stdout).toMatch(/Required: >= 0\.10\.0/);
44
+ expect(result.stdout).toMatch(/Installed:\s+0\.7\.0/);
45
+ });
46
+ });
package/dist/types.d.ts CHANGED
@@ -71,5 +71,6 @@ export type Toolchain = {
71
71
  export type Tool = "moc" | "wasmtime" | "pocket-ic" | "lintoko";
72
72
  export type Requirements = {
73
73
  moc?: string;
74
+ lintoko?: string;
74
75
  };
75
76
  export type TestMode = "interpreter" | "wasi" | "replica";
@@ -0,0 +1,32 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { type SemVer, parse } from "semver";
3
+ import { FILE_PATH_REGEX } from "../constants.js";
4
+ import { readConfig } from "../mops.js";
5
+
6
+ export function getLintokoSemVer(): SemVer | null {
7
+ return parse(getLintokoVersion(false));
8
+ }
9
+
10
+ export function getLintokoVersion(throwOnError = false): string {
11
+ let configVersion = readConfig().toolchain?.lintoko;
12
+ if (!configVersion) {
13
+ return "";
14
+ }
15
+ if (!configVersion.match(FILE_PATH_REGEX)) {
16
+ return configVersion;
17
+ }
18
+
19
+ try {
20
+ let match = execFileSync(configVersion, ["--version"])
21
+ .toString()
22
+ .trim()
23
+ .match(/lintoko ([^\s]+)/);
24
+ return match?.[1] || "";
25
+ } catch (e) {
26
+ if (throwOnError) {
27
+ console.error(e);
28
+ throw new Error("lintoko not found");
29
+ }
30
+ return "";
31
+ }
32
+ }
package/mops.ts CHANGED
@@ -205,9 +205,9 @@ export function readConfig(configFile = getClosestConfigFile()): Config {
205
205
  let config: Config = { ...toml };
206
206
 
207
207
  Object.entries(config.requirements || {}).forEach(([name, value]) => {
208
- if (name === "moc") {
208
+ if (name === "moc" || name === "lintoko") {
209
209
  config.requirements = config.requirements || {};
210
- config.requirements.moc = value;
210
+ config.requirements[name] = value;
211
211
  }
212
212
  });
213
213
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.15.2",
3
+ "version": "2.16.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
@@ -1,6 +1,6 @@
1
1
  name = "migration-self-contained"
2
2
  description = "Migration files must not import local modules (relative paths). Got: @path"
3
- includes = ["**/migrations/**"]
3
+ includes = ["migrations/*.mo"]
4
4
  query = '''
5
5
  (import (text_literal) @path @error
6
6
  (#match? @path "^\"[^:]*\"$"))
@@ -5,4 +5,4 @@ lintoko = "0.7.0"
5
5
  "src/Main.mo" = ["lint/no-types"]
6
6
  "src/Types.mo" = ["lint/types-only"]
7
7
  "src/Migration.mo" = ["lint/migration-only"]
8
- "migrations/**" = ["lint/migration-self-contained"]
8
+ "migrations/*.mo" = ["lint/migration-self-contained"]
@@ -0,0 +1,5 @@
1
+ [dependencies]
2
+ my-pkg = "0.1.0"
3
+
4
+ [toolchain]
5
+ lintoko = "0.7.0"
@@ -0,0 +1,66 @@
1
+ import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
2
+ import { cp, mkdir, rm, writeFile } from "node:fs/promises";
3
+ import path from "path";
4
+ import { bytesToHex } from "@noble/hashes/utils";
5
+ import { sha256 } from "@noble/hashes/sha256";
6
+ import { cli } from "./helpers";
7
+
8
+ describe("requirements", () => {
9
+ const fixtureDir = path.join(import.meta.dirname, "requirements-lintoko");
10
+ let tempDir: string;
11
+
12
+ beforeEach(async () => {
13
+ tempDir = path.join(
14
+ import.meta.dirname,
15
+ `_tmp_requirements_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
16
+ );
17
+ await cp(fixtureDir, tempDir, { recursive: true });
18
+
19
+ const depDir = path.join(tempDir, ".mops", "my-pkg@0.1.0");
20
+ await mkdir(depDir, { recursive: true });
21
+ await writeFile(
22
+ path.join(depDir, "mops.toml"),
23
+ `[package]
24
+ name = "my-pkg"
25
+ version = "0.1.0"
26
+
27
+ [requirements]
28
+ lintoko = "0.10.0"
29
+ `,
30
+ );
31
+
32
+ const mopsTomlDepsHash = bytesToHex(
33
+ sha256(JSON.stringify({ "my-pkg": "0.1.0" })),
34
+ );
35
+ await writeFile(
36
+ path.join(tempDir, "mops.lock"),
37
+ JSON.stringify({
38
+ version: 3,
39
+ mopsTomlDepsHash,
40
+ deps: { "my-pkg": "0.1.0" },
41
+ hashes: {
42
+ "my-pkg@0.1.0": {
43
+ "mops.toml":
44
+ "0000000000000000000000000000000000000000000000000000000000000000",
45
+ },
46
+ },
47
+ }),
48
+ );
49
+ });
50
+
51
+ afterEach(async () => {
52
+ await rm(tempDir, { recursive: true, force: true });
53
+ });
54
+
55
+ test("lintoko requirement warns when installed version is too old", async () => {
56
+ const result = await cli(["toolchain", "use", "lintoko", "0.7.0"], {
57
+ cwd: tempDir,
58
+ });
59
+ expect(result.exitCode).toBe(0);
60
+ expect(result.stdout).toMatch(
61
+ /lintoko version does not meet the requirements of my-pkg@0\.1\.0/,
62
+ );
63
+ expect(result.stdout).toMatch(/Required: >= 0\.10\.0/);
64
+ expect(result.stdout).toMatch(/Installed:\s+0\.7\.0/);
65
+ });
66
+ });
package/types.ts CHANGED
@@ -78,6 +78,7 @@ export type Tool = "moc" | "wasmtime" | "pocket-ic" | "lintoko";
78
78
 
79
79
  export type Requirements = {
80
80
  moc?: string;
81
+ lintoko?: string;
81
82
  };
82
83
 
83
84
  // export type Format = {