ic-mops 2.0.1 → 2.1.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.
Files changed (176) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/RELEASE.md +179 -0
  3. package/bundle/cli.tgz +0 -0
  4. package/check-requirements.ts +3 -8
  5. package/cli.ts +79 -11
  6. package/commands/bench/bench-canister.mo +17 -6
  7. package/commands/bench.ts +2 -13
  8. package/commands/build.ts +2 -4
  9. package/commands/check.ts +117 -0
  10. package/commands/format.ts +3 -18
  11. package/commands/lint.ts +92 -0
  12. package/commands/sync.ts +2 -8
  13. package/commands/test/test.ts +7 -19
  14. package/commands/toolchain/index.ts +21 -8
  15. package/commands/toolchain/lintoko.ts +54 -0
  16. package/commands/toolchain/toolchain-utils.ts +2 -0
  17. package/constants.ts +23 -0
  18. package/dist/check-requirements.js +3 -8
  19. package/dist/cli.js +60 -10
  20. package/dist/commands/bench/bench-canister.mo +17 -6
  21. package/dist/commands/bench.js +2 -11
  22. package/dist/commands/build.js +2 -4
  23. package/dist/commands/check.d.ts +6 -0
  24. package/dist/commands/check.js +78 -0
  25. package/dist/commands/format.js +3 -16
  26. package/dist/commands/lint.d.ts +7 -0
  27. package/dist/commands/lint.js +69 -0
  28. package/dist/commands/sync.js +2 -7
  29. package/dist/commands/test/test.js +7 -17
  30. package/dist/commands/toolchain/index.d.ts +2 -2
  31. package/dist/commands/toolchain/index.js +18 -7
  32. package/dist/commands/toolchain/lintoko.d.ts +8 -0
  33. package/dist/commands/toolchain/lintoko.js +36 -0
  34. package/dist/commands/toolchain/toolchain-utils.d.ts +1 -0
  35. package/dist/commands/toolchain/toolchain-utils.js +1 -0
  36. package/dist/constants.d.ts +15 -0
  37. package/dist/constants.js +21 -0
  38. package/dist/environments/nodejs/cli.js +6 -1
  39. package/dist/helpers/autofix-motoko.d.ts +26 -0
  40. package/dist/helpers/autofix-motoko.js +105 -0
  41. package/dist/helpers/get-moc-version.d.ts +2 -0
  42. package/dist/helpers/get-moc-version.js +10 -1
  43. package/dist/mops.js +2 -1
  44. package/dist/package.json +3 -2
  45. package/dist/tests/build-no-dfx.test.d.ts +1 -0
  46. package/dist/tests/build-no-dfx.test.js +9 -0
  47. package/dist/tests/build.test.d.ts +1 -0
  48. package/dist/tests/build.test.js +18 -0
  49. package/dist/tests/check-candid.test.d.ts +1 -0
  50. package/dist/tests/check-candid.test.js +20 -0
  51. package/dist/tests/check-fix.test.d.ts +1 -0
  52. package/dist/tests/check-fix.test.js +73 -0
  53. package/dist/tests/check.test.d.ts +1 -0
  54. package/dist/tests/check.test.js +33 -0
  55. package/dist/tests/cli.test.js +4 -57
  56. package/dist/tests/helpers.d.ts +22 -0
  57. package/dist/tests/helpers.js +43 -0
  58. package/dist/tests/lint.test.d.ts +1 -0
  59. package/dist/tests/lint.test.js +15 -0
  60. package/dist/tests/toolchain.test.d.ts +1 -0
  61. package/dist/tests/toolchain.test.js +11 -0
  62. package/dist/types.d.ts +5 -1
  63. package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  64. package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
  65. package/environments/nodejs/cli.ts +7 -1
  66. package/helpers/autofix-motoko.ts +170 -0
  67. package/helpers/get-moc-version.ts +12 -1
  68. package/mops.ts +2 -1
  69. package/package.json +3 -2
  70. package/tests/__snapshots__/build-no-dfx.test.ts.snap +11 -0
  71. package/tests/__snapshots__/build.test.ts.snap +77 -0
  72. package/tests/__snapshots__/check-candid.test.ts.snap +73 -0
  73. package/tests/__snapshots__/check-fix.test.ts.snap +242 -0
  74. package/tests/__snapshots__/check.test.ts.snap +72 -0
  75. package/tests/__snapshots__/lint.test.ts.snap +78 -0
  76. package/tests/build/no-dfx/mops.toml +5 -0
  77. package/tests/build/no-dfx/src/Main.mo +5 -0
  78. package/tests/build-no-dfx.test.ts +10 -0
  79. package/tests/build.test.ts +24 -0
  80. package/tests/check/error/Error.mo +7 -0
  81. package/tests/check/error/mops.toml +2 -0
  82. package/tests/check/fix/M0223.mo +11 -0
  83. package/tests/check/fix/M0236.mo +11 -0
  84. package/tests/check/fix/M0237.mo +11 -0
  85. package/tests/check/fix/Ok.mo +7 -0
  86. package/tests/check/fix/edit-suggestions.mo +143 -0
  87. package/tests/check/fix/mops.toml +5 -0
  88. package/tests/check/fix/transitive-lib.mo +9 -0
  89. package/tests/check/fix/transitive-main.mo +9 -0
  90. package/tests/check/success/Ok.mo +5 -0
  91. package/tests/check/success/Warning.mo +5 -0
  92. package/tests/check/success/mops.toml +2 -0
  93. package/tests/check-candid.test.ts +22 -0
  94. package/tests/check-fix.test.ts +111 -0
  95. package/tests/check.test.ts +46 -0
  96. package/tests/cli.test.ts +4 -74
  97. package/tests/helpers.ts +58 -0
  98. package/tests/lint/lints/no-bool-switch.toml +9 -0
  99. package/tests/lint/mops.toml +4 -0
  100. package/tests/lint/src/NoBoolSwitch.mo +8 -0
  101. package/tests/lint/src/Ok.mo +5 -0
  102. package/tests/lint.test.ts +17 -0
  103. package/tests/toolchain/mock +2 -0
  104. package/tests/toolchain/mops.toml +2 -0
  105. package/tests/toolchain.test.ts +12 -0
  106. package/types.ts +5 -1
  107. package/wasm/Cargo.lock +101 -54
  108. package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  109. package/wasm/pkg/web/wasm_bg.wasm +0 -0
  110. package/.DS_Store +0 -0
  111. package/bundle/bench/bench-canister.mo +0 -121
  112. package/bundle/bench/user-bench.mo +0 -10
  113. package/bundle/bin/moc-wrapper.sh +0 -40
  114. package/bundle/bin/mops.js +0 -3
  115. package/bundle/cli.js +0 -2144
  116. package/bundle/declarations/bench/bench.did +0 -30
  117. package/bundle/declarations/bench/bench.did.d.ts +0 -33
  118. package/bundle/declarations/bench/bench.did.js +0 -30
  119. package/bundle/declarations/bench/index.d.ts +0 -50
  120. package/bundle/declarations/bench/index.js +0 -40
  121. package/bundle/declarations/main/index.d.ts +0 -50
  122. package/bundle/declarations/main/index.js +0 -40
  123. package/bundle/declarations/main/main.did +0 -428
  124. package/bundle/declarations/main/main.did.d.ts +0 -348
  125. package/bundle/declarations/main/main.did.js +0 -406
  126. package/bundle/declarations/storage/index.d.ts +0 -50
  127. package/bundle/declarations/storage/index.js +0 -30
  128. package/bundle/declarations/storage/storage.did +0 -46
  129. package/bundle/declarations/storage/storage.did.d.ts +0 -40
  130. package/bundle/declarations/storage/storage.did.js +0 -38
  131. package/bundle/package.json +0 -36
  132. package/bundle/templates/README.md +0 -13
  133. package/bundle/templates/licenses/Apache-2.0 +0 -202
  134. package/bundle/templates/licenses/Apache-2.0-NOTICE +0 -13
  135. package/bundle/templates/licenses/MIT +0 -21
  136. package/bundle/templates/mops-publish.yml +0 -17
  137. package/bundle/templates/mops-test.yml +0 -24
  138. package/bundle/templates/src/lib.mo +0 -15
  139. package/bundle/templates/test/lib.test.mo +0 -4
  140. package/bundle/wasm_bg.wasm +0 -0
  141. package/bundle/xhr-sync-worker.js +0 -59
  142. package/dist/wasm/pkg/bundler/package.json +0 -20
  143. package/dist/wasm/pkg/bundler/wasm.d.ts +0 -3
  144. package/dist/wasm/pkg/bundler/wasm.js +0 -5
  145. package/dist/wasm/pkg/bundler/wasm_bg.js +0 -93
  146. package/dist/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  147. package/dist/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
  148. package/tests/__snapshots__/cli.test.ts.snap +0 -202
  149. package/tests/build/success/.dfx/local/canister_ids.json +0 -17
  150. package/tests/build/success/.dfx/local/canisters/bar/bar.did +0 -3
  151. package/tests/build/success/.dfx/local/canisters/bar/bar.most +0 -4
  152. package/tests/build/success/.dfx/local/canisters/bar/bar.wasm +0 -0
  153. package/tests/build/success/.dfx/local/canisters/bar/constructor.did +0 -3
  154. package/tests/build/success/.dfx/local/canisters/bar/index.js +0 -42
  155. package/tests/build/success/.dfx/local/canisters/bar/init_args.txt +0 -1
  156. package/tests/build/success/.dfx/local/canisters/bar/service.did +0 -3
  157. package/tests/build/success/.dfx/local/canisters/bar/service.did.d.ts +0 -7
  158. package/tests/build/success/.dfx/local/canisters/bar/service.did.js +0 -4
  159. package/tests/build/success/.dfx/local/canisters/foo/constructor.did +0 -3
  160. package/tests/build/success/.dfx/local/canisters/foo/foo.did +0 -3
  161. package/tests/build/success/.dfx/local/canisters/foo/foo.most +0 -4
  162. package/tests/build/success/.dfx/local/canisters/foo/foo.wasm +0 -0
  163. package/tests/build/success/.dfx/local/canisters/foo/index.js +0 -42
  164. package/tests/build/success/.dfx/local/canisters/foo/init_args.txt +0 -1
  165. package/tests/build/success/.dfx/local/canisters/foo/service.did +0 -3
  166. package/tests/build/success/.dfx/local/canisters/foo/service.did.d.ts +0 -7
  167. package/tests/build/success/.dfx/local/canisters/foo/service.did.js +0 -4
  168. package/tests/build/success/.dfx/local/lsp/ucwa4-rx777-77774-qaada-cai.did +0 -3
  169. package/tests/build/success/.dfx/local/lsp/ulvla-h7777-77774-qaacq-cai.did +0 -3
  170. package/tests/build/success/.dfx/local/network-id +0 -4
  171. package/wasm/pkg/bundler/package.json +0 -20
  172. package/wasm/pkg/bundler/wasm.d.ts +0 -3
  173. package/wasm/pkg/bundler/wasm.js +0 -5
  174. package/wasm/pkg/bundler/wasm_bg.js +0 -93
  175. package/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  176. package/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
@@ -0,0 +1,92 @@
1
+ import chalk from "chalk";
2
+ import { execa } from "execa";
3
+ import { globSync } from "glob";
4
+ import path from "node:path";
5
+ import { cliError } from "../error.js";
6
+ import { getRootDir, readConfig } from "../mops.js";
7
+ import { toolchain } from "./toolchain/index.js";
8
+ import { MOTOKO_GLOB_CONFIG } from "../constants.js";
9
+ import { existsSync } from "node:fs";
10
+
11
+ export interface LintOptions {
12
+ verbose: boolean;
13
+ fix: boolean;
14
+ rules?: string[];
15
+ extraArgs: string[];
16
+ }
17
+
18
+ export async function lint(
19
+ filter: string | undefined,
20
+ options: Partial<LintOptions>,
21
+ ): Promise<void> {
22
+ let config = readConfig();
23
+ let rootDir = getRootDir();
24
+ let lintokoBinPath = config.toolchain?.lintoko
25
+ ? await toolchain.bin("lintoko")
26
+ : "lintoko";
27
+
28
+ let globStr = filter ? `**/*${filter}*.mo` : "**/*.mo";
29
+ let filesToLint = globSync(path.join(rootDir, globStr), {
30
+ ...MOTOKO_GLOB_CONFIG,
31
+ cwd: rootDir,
32
+ });
33
+ if (filesToLint.length === 0) {
34
+ cliError(`No files found for filter '${filter}'`);
35
+ }
36
+
37
+ let args: string[] = [];
38
+ if (options.verbose) {
39
+ args.push("--verbose");
40
+ }
41
+ if (options.fix) {
42
+ args.push("--fix");
43
+ }
44
+ const rules =
45
+ options.rules && options.rules.length > 0
46
+ ? options.rules
47
+ : ["lint", "lints"].filter((d) => existsSync(path.join(rootDir, d)));
48
+ rules.forEach((rule) => args.push("--rules", rule));
49
+
50
+ if (config.lint?.args) {
51
+ if (typeof config.lint.args === "string") {
52
+ cliError(
53
+ `[lint] config 'args' should be an array of strings in mops.toml config file`,
54
+ );
55
+ }
56
+ args.push(...config.lint.args);
57
+ }
58
+
59
+ if (options.extraArgs && options.extraArgs.length > 0) {
60
+ args.push(...options.extraArgs);
61
+ }
62
+
63
+ args.push(...filesToLint);
64
+
65
+ try {
66
+ if (options.verbose) {
67
+ console.log(chalk.blue("lint"), chalk.gray("Running lintoko:"));
68
+ console.log(chalk.gray(lintokoBinPath));
69
+ console.log(chalk.gray(JSON.stringify(args)));
70
+ }
71
+
72
+ const result = await execa(lintokoBinPath, args, {
73
+ cwd: rootDir,
74
+ stdio: "inherit",
75
+ reject: false,
76
+ });
77
+
78
+ if (result.exitCode !== 0) {
79
+ cliError(`Lint failed with exit code ${result.exitCode}`);
80
+ }
81
+
82
+ if (options.fix) {
83
+ console.log(chalk.green("✓ Lint fixes applied"));
84
+ } else {
85
+ console.log(chalk.green("✓ Lint succeeded"));
86
+ }
87
+ } catch (err: any) {
88
+ cliError(
89
+ `Error while running lintoko${err?.message ? `\n${err.message}` : ""}`,
90
+ );
91
+ }
92
+ }
package/commands/sync.ts CHANGED
@@ -7,6 +7,7 @@ import { add } from "./add.js";
7
7
  import { remove } from "./remove.js";
8
8
  import { checkIntegrity } from "../integrity.js";
9
9
  import { getMocPath } from "../helpers/get-moc-path.js";
10
+ import { MOTOKO_IGNORE_PATTERNS } from "../constants.js";
10
11
 
11
12
  type SyncOptions = {
12
13
  lock?: "update" | "ignore";
@@ -43,13 +44,6 @@ export async function sync({ lock }: SyncOptions = {}) {
43
44
  await checkIntegrity(lock);
44
45
  }
45
46
 
46
- let ignore = [
47
- "**/node_modules/**",
48
- "**/.vessel/**",
49
- "**/.git/**",
50
- "**/.mops/**",
51
- ];
52
-
53
47
  async function getUsedPackages(): Promise<string[]> {
54
48
  let rootDir = getRootDir();
55
49
  let mocPath = getMocPath();
@@ -57,7 +51,7 @@ async function getUsedPackages(): Promise<string[]> {
57
51
  let files = globSync("**/*.mo", {
58
52
  cwd: rootDir,
59
53
  nocase: true,
60
- ignore: ignore,
54
+ ignore: MOTOKO_IGNORE_PATTERNS,
61
55
  });
62
56
 
63
57
  let packages: Set<string> = new Set();
@@ -32,18 +32,7 @@ import { toolchain } from "../toolchain/index.js";
32
32
  import { Replica } from "../replica.js";
33
33
  import { TestMode } from "../../types.js";
34
34
  import { getDfxVersion } from "../../helpers/get-dfx-version.js";
35
-
36
- let ignore = [
37
- "**/node_modules/**",
38
- "**/.mops/**",
39
- "**/.vessel/**",
40
- "**/.git/**",
41
- ];
42
-
43
- let globConfig = {
44
- nocase: true,
45
- ignore: ignore,
46
- };
35
+ import { MOTOKO_GLOB_CONFIG, MOTOKO_IGNORE_PATTERNS } from "../../constants.js";
47
36
 
48
37
  type ReporterName = "verbose" | "files" | "compact" | "silent";
49
38
  type ReplicaName = "dfx" | "pocket-ic" | "dfx-pocket-ic";
@@ -146,7 +135,7 @@ export async function test(filter = "", options: Partial<TestOptions> = {}) {
146
135
  let watcher = chokidar.watch(
147
136
  [path.join(rootDir, "**/*.mo"), path.join(rootDir, "mops.toml")],
148
137
  {
149
- ignored: ignore,
138
+ ignored: MOTOKO_IGNORE_PATTERNS,
150
139
  ignoreInitial: true,
151
140
  },
152
141
  );
@@ -200,15 +189,14 @@ export async function testWithReporter(
200
189
  ): Promise<boolean> {
201
190
  let rootDir = getRootDir();
202
191
  let files: string[] = [];
203
- let libFiles = globSync("**/test?(s)/lib.mo", globConfig);
192
+ let libFiles = globSync("**/test?(s)/lib.mo", MOTOKO_GLOB_CONFIG);
204
193
  if (libFiles[0]) {
205
194
  files = [libFiles[0]];
206
195
  } else {
207
- let globStr = "**/test?(s)/**/*.test.mo";
208
- if (filter) {
209
- globStr = `**/test?(s)/**/*${filter}*.mo`;
210
- }
211
- files = globSync(path.join(rootDir, globStr), globConfig);
196
+ let globStr = filter
197
+ ? `**/test?(s)/**/*${filter}*.mo`
198
+ : "**/test?(s)/**/*.test.mo";
199
+ files = globSync(path.join(rootDir, globStr), MOTOKO_GLOB_CONFIG);
212
200
  }
213
201
  if (!files.length) {
214
202
  if (filter) {
@@ -19,6 +19,8 @@ import { checkRequirements } from "../../check-requirements.js";
19
19
  import * as moc from "./moc.js";
20
20
  import * as pocketIc from "./pocket-ic.js";
21
21
  import * as wasmtime from "./wasmtime.js";
22
+ import * as lintoko from "./lintoko.js";
23
+ import { FILE_PATH_REGEX } from "../../constants.js";
22
24
 
23
25
  function getToolUtils(tool: Tool) {
24
26
  if (tool === "moc") {
@@ -27,13 +29,15 @@ function getToolUtils(tool: Tool) {
27
29
  return pocketIc;
28
30
  } else if (tool === "wasmtime") {
29
31
  return wasmtime;
32
+ } else if (tool === "lintoko") {
33
+ return lintoko;
30
34
  } else {
31
35
  console.error(`Unknown tool '${tool}'`);
32
36
  process.exit(1);
33
37
  }
34
38
  }
35
39
 
36
- async function ensureToolchainInited({ strict = true } = {}) {
40
+ async function checkToolchainInited({ strict = false } = {}): Promise<boolean> {
37
41
  // auto init in CI
38
42
  if (process.env.CI) {
39
43
  await init({ silent: true });
@@ -57,10 +61,12 @@ async function ensureToolchainInited({ strict = true } = {}) {
57
61
  return true;
58
62
  }
59
63
  } catch {}
60
- console.error(
61
- 'Toolchain management is not initialized. Run "mops toolchain init"',
64
+ process.stderr.write(
65
+ `${chalk.yellow(
66
+ 'Toolchain management is not initialized. Run "mops toolchain init" to use with dfx.',
67
+ )}\n`,
62
68
  );
63
- process.exit(1);
69
+ return false;
64
70
  }
65
71
 
66
72
  // update shell config files to set DFX_MOC_PATH to moc-wrapper
@@ -216,6 +222,9 @@ async function installAll({ silent = false, verbose = false } = {}) {
216
222
  verbose,
217
223
  });
218
224
  }
225
+ if (config.toolchain?.lintoko) {
226
+ await download("lintoko", config.toolchain.lintoko, { silent, verbose });
227
+ }
219
228
 
220
229
  if (!silent) {
221
230
  logUpdate.clear();
@@ -265,7 +274,7 @@ async function promptVersion(tool: Tool): Promise<string> {
265
274
  // download binary and set version in mops.toml
266
275
  async function use(tool: Tool, version?: string) {
267
276
  if (tool === "moc") {
268
- await ensureToolchainInited();
277
+ await checkToolchainInited();
269
278
  }
270
279
  if (!version) {
271
280
  version = await promptVersion(tool);
@@ -299,7 +308,7 @@ async function use(tool: Tool, version?: string) {
299
308
  // download latest binary and set version in mops.toml
300
309
  async function update(tool?: Tool) {
301
310
  if (tool === "moc") {
302
- await ensureToolchainInited();
311
+ await checkToolchainInited();
303
312
  }
304
313
 
305
314
  let config = readConfig();
@@ -353,8 +362,12 @@ async function bin(tool: Tool, { fallback = false } = {}): Promise<string> {
353
362
  let version = config.toolchain?.[tool];
354
363
 
355
364
  if (version) {
365
+ if (version.match(FILE_PATH_REGEX)) {
366
+ return version;
367
+ }
368
+
356
369
  if (tool === "moc") {
357
- await ensureToolchainInited();
370
+ await checkToolchainInited();
358
371
  }
359
372
 
360
373
  await download(tool, version, { silent: true });
@@ -385,5 +398,5 @@ export let toolchain = {
385
398
  update,
386
399
  bin,
387
400
  installAll,
388
- ensureToolchainInited,
401
+ checkToolchainInited,
389
402
  };
@@ -0,0 +1,54 @@
1
+ import process from "node:process";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+
5
+ import { globalCacheDir } from "../../mops.js";
6
+ import * as toolchainUtils from "./toolchain-utils.js";
7
+
8
+ let cacheDir = path.join(globalCacheDir, "lintoko");
9
+
10
+ export let repo = "caffeinelabs/lintoko";
11
+
12
+ export let getLatestReleaseTag = async () => {
13
+ return toolchainUtils.getLatestReleaseTag(repo);
14
+ };
15
+
16
+ export let getReleases = async () => {
17
+ return toolchainUtils.getReleases(repo);
18
+ };
19
+
20
+ export let isCached = (version: string) => {
21
+ let dir = path.join(cacheDir, version);
22
+ return fs.existsSync(dir) && fs.existsSync(path.join(dir, "lintoko"));
23
+ };
24
+
25
+ export let download = async (
26
+ version: string,
27
+ { silent = false, verbose = false } = {},
28
+ ) => {
29
+ if (!version) {
30
+ console.error("version is not defined");
31
+ process.exit(1);
32
+ }
33
+ if (isCached(version)) {
34
+ if (verbose) {
35
+ console.log(`lintoko ${version} is already installed`);
36
+ }
37
+ return;
38
+ }
39
+
40
+ let platform =
41
+ process.platform == "darwin" ? "apple-darwin" : "unknown-linux-gnu";
42
+ let arch = process.arch.startsWith("arm") ? "aarch64" : "x86_64";
43
+ let url = `https://github.com/caffeinelabs/lintoko/releases/download/v${version}/lintoko-${arch}-${platform}.tar.xz`;
44
+
45
+ if (verbose && !silent) {
46
+ console.log(`Downloading ${url}`);
47
+ }
48
+
49
+ await toolchainUtils.downloadAndExtract(
50
+ url,
51
+ path.join(cacheDir, version),
52
+ "lintoko",
53
+ );
54
+ };
@@ -12,6 +12,8 @@ import { extract as extractTar } from "tar";
12
12
 
13
13
  import { getRootDir } from "../../mops.js";
14
14
 
15
+ export const TOOLCHAINS = ["moc", "wasmtime", "pocket-ic", "lintoko"];
16
+
15
17
  export let tryDownloadFile = async (url: string): Promise<Buffer | null> => {
16
18
  let res = await fetch(url);
17
19
 
package/constants.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Common ignore patterns for glob operations across Motoko files.
3
+ */
4
+ export const MOTOKO_IGNORE_PATTERNS = [
5
+ "**/node_modules/**",
6
+ "**/.mops/**",
7
+ "**/.vessel/**",
8
+ "**/.git/**",
9
+ "**/dist/**",
10
+ ];
11
+
12
+ /**
13
+ * Common glob configuration for Motoko file operations
14
+ */
15
+ export const MOTOKO_GLOB_CONFIG = {
16
+ nocase: true,
17
+ ignore: MOTOKO_IGNORE_PATTERNS,
18
+ };
19
+
20
+ /**
21
+ * Regex to match a file path for dependency and toolchain versions
22
+ */
23
+ export const FILE_PATH_REGEX = /^(\.?\.)?\//;
@@ -3,18 +3,13 @@ import { SemVer } from "semver";
3
3
  import chalk from "chalk";
4
4
  import { getDependencyType, getRootDir, readConfig } from "./mops.js";
5
5
  import { resolvePackages } from "./resolve-packages.js";
6
- import { getMocVersion } from "./helpers/get-moc-version.js";
6
+ import { getMocSemVer } from "./helpers/get-moc-version.js";
7
7
  import { getPackageId } from "./helpers/get-package-id.js";
8
8
  export async function checkRequirements({ verbose = false } = {}) {
9
- let config = readConfig();
10
- let mocVersion = config.toolchain?.moc;
11
- if (!mocVersion) {
12
- mocVersion = getMocVersion(false);
13
- }
14
- if (!mocVersion) {
9
+ let installedMoc = getMocSemVer();
10
+ if (!installedMoc) {
15
11
  return;
16
12
  }
17
- let installedMoc = new SemVer(mocVersion);
18
13
  let highestRequiredMoc = new SemVer("0.0.0");
19
14
  let highestRequiredMocPkgId = "";
20
15
  let rootDir = getRootDir();
package/dist/cli.js CHANGED
@@ -9,11 +9,13 @@ import { add } from "./commands/add.js";
9
9
  import { bench } from "./commands/bench.js";
10
10
  import { build, DEFAULT_BUILD_OUTPUT_DIR } from "./commands/build.js";
11
11
  import { bump } from "./commands/bump.js";
12
+ import { check } from "./commands/check.js";
12
13
  import { checkCandid } from "./commands/check-candid.js";
13
14
  import { docsCoverage } from "./commands/docs-coverage.js";
14
15
  import { docs } from "./commands/docs.js";
15
16
  import { format } from "./commands/format.js";
16
17
  import { init } from "./commands/init.js";
18
+ import { lint } from "./commands/lint.js";
17
19
  import { installAll } from "./commands/install/install-all.js";
18
20
  import { addMaintainer, printMaintainers, removeMaintainer, } from "./commands/maintainer.js";
19
21
  import { outdated } from "./commands/outdated.js";
@@ -32,6 +34,7 @@ import { getPrincipal, getUserProp, importPem, setUserProp, } from "./commands/u
32
34
  import { watch } from "./commands/watch/watch.js";
33
35
  import { apiVersion, checkApiCompatibility, checkConfigFile, getNetworkFile, setNetwork, version, } from "./mops.js";
34
36
  import { resolvePackages } from "./resolve-packages.js";
37
+ import { TOOLCHAINS } from "./commands/toolchain/toolchain-utils.js";
35
38
  events.setMaxListeners(20);
36
39
  // Change working directory for `npm run mops`
37
40
  let cwd = process.env["MOPS_CWD"];
@@ -43,6 +46,17 @@ if (fs.existsSync(networkFile)) {
43
46
  globalThis.MOPS_NETWORK = fs.readFileSync(networkFile).toString() || "ic";
44
47
  }
45
48
  let program = new Command();
49
+ function parseExtraArgs(variadicArgs) {
50
+ const rawArgs = process.argv.slice(2);
51
+ const dashDashIndex = rawArgs.indexOf("--");
52
+ const extraArgs = dashDashIndex !== -1 ? rawArgs.slice(dashDashIndex + 1) : [];
53
+ const args = variadicArgs
54
+ ? extraArgs.length > 0
55
+ ? variadicArgs.slice(0, variadicArgs.length - extraArgs.length)
56
+ : variadicArgs
57
+ : [];
58
+ return { extraArgs, args };
59
+ }
46
60
  program.name("mops");
47
61
  // --version
48
62
  program.version(`CLI ${version()}\nAPI ${apiVersion}`, "-v --version");
@@ -109,7 +123,7 @@ program
109
123
  return;
110
124
  }
111
125
  if (options.toolchain) {
112
- await toolchain.ensureToolchainInited({ strict: false });
126
+ await toolchain.checkToolchainInited({ strict: false });
113
127
  }
114
128
  let ok = await installAll(options);
115
129
  if (options.toolchain) {
@@ -175,7 +189,7 @@ program
175
189
  installFromLockFile: true,
176
190
  });
177
191
  }
178
- await toolchain.ensureToolchainInited({ strict: false });
192
+ await toolchain.checkToolchainInited({ strict: false });
179
193
  let sourcesArr = await sources(options);
180
194
  console.log(sourcesArr.join("\n"));
181
195
  });
@@ -211,17 +225,37 @@ program
211
225
  .addOption(new Option("--verbose", "Verbose console output"))
212
226
  .addOption(new Option("--output, -o <output>", "Output directory").default(DEFAULT_BUILD_OUTPUT_DIR))
213
227
  .allowUnknownOption(true) // TODO: restrict unknown before "--"
214
- .action(async (canisters, options, command) => {
228
+ .action(async (canisters, options) => {
215
229
  checkConfigFile(true);
216
- const extraArgsIndex = command.args.indexOf("--");
230
+ const { extraArgs, args } = parseExtraArgs(canisters);
217
231
  await installAll({
218
232
  silent: true,
219
233
  lock: "ignore",
220
234
  installFromLockFile: true,
221
235
  });
222
- await build(canisters.length ? canisters : undefined, {
236
+ await build(args.length ? args : undefined, {
223
237
  ...options,
224
- extraArgs: extraArgsIndex !== -1 ? command.args.slice(extraArgsIndex + 1) : [],
238
+ extraArgs,
239
+ });
240
+ });
241
+ // check
242
+ program
243
+ .command("check <files...>")
244
+ .description("Check Motoko entrypoint files for syntax errors and type issues (including transitively imported files)")
245
+ .option("--verbose", "Verbose console output")
246
+ .addOption(new Option("--fix", "Apply autofixes to all files, including transitively imported ones"))
247
+ .allowUnknownOption(true)
248
+ .action(async (files, options) => {
249
+ checkConfigFile(true);
250
+ const { extraArgs, args: fileList } = parseExtraArgs(files);
251
+ await installAll({
252
+ silent: true,
253
+ lock: "ignore",
254
+ installFromLockFile: true,
255
+ });
256
+ await check(fileList, {
257
+ ...options,
258
+ extraArgs,
225
259
  });
226
260
  });
227
261
  // check-candid
@@ -446,7 +480,7 @@ toolchainCommand
446
480
  toolchainCommand
447
481
  .command("use")
448
482
  .description("Install specified tool version and update mops.toml")
449
- .addArgument(new Argument("<tool>").choices(["moc", "wasmtime", "pocket-ic"]))
483
+ .addArgument(new Argument("<tool>").choices(TOOLCHAINS))
450
484
  .addArgument(new Argument("[version]"))
451
485
  .action(async (tool, version) => {
452
486
  if (!checkConfigFile()) {
@@ -457,7 +491,7 @@ toolchainCommand
457
491
  toolchainCommand
458
492
  .command("update")
459
493
  .description("Update specified tool or all tools to the latest version and update mops.toml")
460
- .addArgument(new Argument("[tool]").choices(["moc", "wasmtime", "pocket-ic"]))
494
+ .addArgument(new Argument("[tool]").choices(TOOLCHAINS))
461
495
  .action(async (tool) => {
462
496
  if (!checkConfigFile()) {
463
497
  process.exit(1);
@@ -466,8 +500,8 @@ toolchainCommand
466
500
  });
467
501
  toolchainCommand
468
502
  .command("bin")
469
- .description('Get path to the tool binary\n<tool> can be one of "moc", "wasmtime", "pocket-ic"')
470
- .addArgument(new Argument("<tool>").choices(["moc", "wasmtime", "pocket-ic"]))
503
+ .description(`Get path to the tool binary\n<tool> can be one of ${TOOLCHAINS.map((s) => `"${s}"`).join(", ")}`)
504
+ .addArgument(new Argument("<tool>").choices(TOOLCHAINS))
471
505
  .addOption(new Option("--fallback", "Fallback to the moc that comes with dfx if moc is not specified in the [toolchain] section"))
472
506
  .action(async (tool, options) => {
473
507
  let bin = await toolchain.bin(tool, options);
@@ -516,6 +550,22 @@ program
516
550
  process.exit(1);
517
551
  }
518
552
  });
553
+ // lint
554
+ program
555
+ .command("lint [filter]")
556
+ .description("Lint Motoko code")
557
+ .addOption(new Option("--verbose", "Verbose output"))
558
+ .addOption(new Option("--fix", "Apply fixes"))
559
+ .addOption(new Option("-r, --rules <directory...>", "Directories containing rules (can be used multiple times)"))
560
+ .allowUnknownOption(true)
561
+ .action(async (filter, options) => {
562
+ checkConfigFile(true);
563
+ const { extraArgs } = parseExtraArgs();
564
+ await lint(filter, {
565
+ ...options,
566
+ extraArgs,
567
+ });
568
+ });
519
569
  // docs
520
570
  const docsCommand = new Command("docs").description("Documentation management");
521
571
  docsCommand
@@ -2,14 +2,25 @@ import Nat64 "mo:core/Nat64";
2
2
  import Nat "mo:core/Nat";
3
3
  import Runtime "mo:core/Runtime";
4
4
  import InternetComputer "mo:core/InternetComputer";
5
- import Int64 "mo:core/Int64";
6
5
  import Region "mo:core/Region";
7
6
  import Prim "mo:prim";
8
- import Bench "mo:bench";
9
7
 
10
8
  import UserBench "./user-bench"; // file path will be replaced with the *.bench.mo file path
11
9
 
12
10
  persistent actor class () {
11
+ type BenchSchema = {
12
+ name : Text;
13
+ description : Text;
14
+ rows : [Text];
15
+ cols : [Text];
16
+ };
17
+
18
+ type Bench = {
19
+ getVersion : () -> Nat;
20
+ getSchema : () -> BenchSchema;
21
+ runCell : (Nat, Nat) -> ();
22
+ };
23
+
13
24
  type BenchResult = {
14
25
  instructions : Int;
15
26
  rts_mutator_instructions : Int;
@@ -23,16 +34,16 @@ persistent actor class () {
23
34
  rts_reclaimed : Int;
24
35
  };
25
36
 
26
- transient var benchOpt : ?Bench.Bench = null;
37
+ transient var benchOpt : ?Bench = null;
27
38
 
28
- public func init() : async Bench.BenchSchema {
39
+ public func init() : async BenchSchema {
29
40
  let bench = UserBench.init();
30
41
  benchOpt := ?bench;
31
42
  ignore Region.grow(Region.new(), 1);
32
43
  bench.getSchema();
33
44
  };
34
45
 
35
- public query func getSchema() : async Bench.BenchSchema {
46
+ public query func getSchema() : async BenchSchema {
36
47
  let ?bench = benchOpt else Runtime.trap("bench not initialized");
37
48
  bench.getSchema();
38
49
  };
@@ -41,7 +52,7 @@ persistent actor class () {
41
52
  {
42
53
  instructions = 0;
43
54
  rts_heap_size = Prim.rts_heap_size();
44
- stable_memory_size = Int64.toInt(Int64.fromNat64(Prim.stableMemorySize())) * 65536;
55
+ stable_memory_size = Prim.rts_stable_memory_size() * 65536;
45
56
  rts_stable_memory_size = Prim.rts_stable_memory_size();
46
57
  rts_logical_stable_memory_size = Prim.rts_logical_stable_memory_size();
47
58
  rts_memory_size = Prim.rts_memory_size();
@@ -18,17 +18,8 @@ import { getMocVersion } from "../helpers/get-moc-version.js";
18
18
  import { getDfxVersion } from "../helpers/get-dfx-version.js";
19
19
  import { getMocPath } from "../helpers/get-moc-path.js";
20
20
  import { sources } from "./sources.js";
21
+ import { MOTOKO_GLOB_CONFIG } from "../constants.js";
21
22
  import { BenchReplica } from "./bench-replica.js";
22
- let ignore = [
23
- "**/node_modules/**",
24
- "**/.mops/**",
25
- "**/.vessel/**",
26
- "**/.git/**",
27
- ];
28
- let globConfig = {
29
- nocase: true,
30
- ignore: ignore,
31
- };
32
23
  export async function bench(filter = "", optionsArg = {}) {
33
24
  let config = readConfig();
34
25
  let dfxJson = readDfxJson();
@@ -75,7 +66,7 @@ export async function bench(filter = "", optionsArg = {}) {
75
66
  if (filter) {
76
67
  globStr = `**/bench?(mark)/**/*${filter}*.mo`;
77
68
  }
78
- let files = globSync(path.join(rootDir, globStr), globConfig);
69
+ let files = globSync(path.join(rootDir, globStr), MOTOKO_GLOB_CONFIG);
79
70
  if (!files.length) {
80
71
  if (filter) {
81
72
  options.silent ||
@@ -4,18 +4,18 @@ import { exists } from "fs-extra";
4
4
  import { mkdir, readFile, writeFile } from "node:fs/promises";
5
5
  import { join } from "node:path";
6
6
  import { cliError } from "../error.js";
7
- import { getMocPath } from "../helpers/get-moc-path.js";
8
7
  import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
9
8
  import { getWasmBindings } from "../wasm.js";
10
9
  import { readConfig } from "../mops.js";
11
10
  import { sourcesArgs } from "./sources.js";
11
+ import { toolchain } from "./toolchain/index.js";
12
12
  export const DEFAULT_BUILD_OUTPUT_DIR = ".mops/.build";
13
13
  export async function build(canisterNames, options) {
14
14
  if (canisterNames?.length == 0) {
15
15
  cliError("No canisters specified to build");
16
16
  }
17
17
  let outputDir = options.outputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
18
- let mocPath = getMocPath();
18
+ let mocPath = await toolchain.bin("moc", { fallback: true });
19
19
  let canisters = {};
20
20
  let config = readConfig();
21
21
  if (config.canisters) {
@@ -43,7 +43,6 @@ export async function build(canisterNames, options) {
43
43
  ? Object.fromEntries(Object.entries(canisters).filter(([name]) => canisterNames.includes(name)))
44
44
  : canisters;
45
45
  for (let [canisterName, canister] of Object.entries(filteredCanisters)) {
46
- options.verbose && console.time(`build canister ${canisterName}`);
47
46
  console.log(chalk.blue("build canister"), chalk.bold(canisterName));
48
47
  let motokoPath = canister.main;
49
48
  if (!motokoPath) {
@@ -139,7 +138,6 @@ export async function build(canisterNames, options) {
139
138
  }
140
139
  cliError(`Error while compiling canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`);
141
140
  }
142
- options.verbose && console.timeEnd(`build canister ${canisterName}`);
143
141
  }
144
142
  console.log(chalk.green(`\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length == 1 ? "" : "s"} successfully`));
145
143
  }
@@ -0,0 +1,6 @@
1
+ export interface CheckOptions {
2
+ verbose: boolean;
3
+ fix: boolean;
4
+ extraArgs: string[];
5
+ }
6
+ export declare function check(files: string | string[], options?: Partial<CheckOptions>): Promise<void>;