ic-mops 2.1.0 → 2.2.1

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 (118) hide show
  1. package/.DS_Store +0 -0
  2. package/CHANGELOG.md +8 -0
  3. package/RELEASE.md +19 -0
  4. package/bundle/bench/bench-canister.mo +130 -0
  5. package/bundle/bench/user-bench.mo +10 -0
  6. package/bundle/bin/moc-wrapper.sh +40 -0
  7. package/bundle/bin/mops.js +3 -0
  8. package/bundle/cli.js +1569 -0
  9. package/bundle/cli.tgz +0 -0
  10. package/bundle/declarations/bench/bench.did +30 -0
  11. package/bundle/declarations/bench/bench.did.d.ts +33 -0
  12. package/bundle/declarations/bench/bench.did.js +30 -0
  13. package/bundle/declarations/bench/index.d.ts +50 -0
  14. package/bundle/declarations/bench/index.js +40 -0
  15. package/bundle/declarations/main/index.d.ts +50 -0
  16. package/bundle/declarations/main/index.js +40 -0
  17. package/bundle/declarations/main/main.did +428 -0
  18. package/bundle/declarations/main/main.did.d.ts +348 -0
  19. package/bundle/declarations/main/main.did.js +406 -0
  20. package/bundle/declarations/storage/index.d.ts +50 -0
  21. package/bundle/declarations/storage/index.js +30 -0
  22. package/bundle/declarations/storage/storage.did +46 -0
  23. package/bundle/declarations/storage/storage.did.d.ts +40 -0
  24. package/bundle/declarations/storage/storage.did.js +38 -0
  25. package/bundle/default-stylesheet.css +415 -0
  26. package/bundle/package.json +36 -0
  27. package/bundle/templates/README.md +13 -0
  28. package/bundle/templates/licenses/Apache-2.0 +202 -0
  29. package/bundle/templates/licenses/Apache-2.0-NOTICE +13 -0
  30. package/bundle/templates/licenses/MIT +21 -0
  31. package/bundle/templates/mops-publish.yml +17 -0
  32. package/bundle/templates/mops-test.yml +24 -0
  33. package/bundle/templates/src/lib.mo +15 -0
  34. package/bundle/templates/test/lib.test.mo +4 -0
  35. package/bundle/wasm_bg.wasm +0 -0
  36. package/bundle/xhr-sync-worker.js +51 -0
  37. package/cli.ts +15 -0
  38. package/commands/bench.ts +11 -3
  39. package/commands/build.ts +3 -2
  40. package/commands/check.ts +4 -0
  41. package/commands/test/test.ts +3 -1
  42. package/commands/toolchain/index.ts +4 -0
  43. package/commands/watch/error-checker.ts +8 -2
  44. package/commands/watch/warning-checker.ts +8 -2
  45. package/dist/cli.js +13 -1
  46. package/dist/commands/bench.js +5 -4
  47. package/dist/commands/build.js +3 -2
  48. package/dist/commands/check.js +4 -0
  49. package/dist/commands/test/test.js +3 -1
  50. package/dist/commands/toolchain/index.js +3 -0
  51. package/dist/commands/watch/error-checker.js +8 -2
  52. package/dist/commands/watch/warning-checker.js +8 -2
  53. package/dist/error.d.ts +1 -1
  54. package/dist/helpers/autofix-motoko.js +84 -39
  55. package/dist/mops.d.ts +1 -0
  56. package/dist/mops.js +10 -4
  57. package/dist/package.json +6 -7
  58. package/dist/tests/check-fix.test.js +16 -0
  59. package/dist/tests/check.test.js +4 -0
  60. package/dist/tests/moc-args.test.d.ts +1 -0
  61. package/dist/tests/moc-args.test.js +17 -0
  62. package/dist/tests/toolchain.test.js +11 -0
  63. package/dist/types.d.ts +3 -0
  64. package/dist/wasm/pkg/bundler/package.json +20 -0
  65. package/dist/wasm/pkg/bundler/wasm.d.ts +3 -0
  66. package/dist/wasm/pkg/bundler/wasm.js +5 -0
  67. package/dist/wasm/pkg/bundler/wasm_bg.js +93 -0
  68. package/dist/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  69. package/dist/wasm/pkg/bundler/wasm_bg.wasm.d.ts +8 -0
  70. package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  71. package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
  72. package/error.ts +1 -1
  73. package/helpers/autofix-motoko.ts +119 -49
  74. package/mops.ts +13 -6
  75. package/package.json +8 -9
  76. package/tests/__snapshots__/check-fix.test.ts.snap +25 -6
  77. package/tests/__snapshots__/check.test.ts.snap +9 -0
  78. package/tests/build/success/.dfx/local/canister_ids.json +17 -0
  79. package/tests/build/success/.dfx/local/canisters/bar/bar.did +3 -0
  80. package/tests/build/success/.dfx/local/canisters/bar/bar.most +4 -0
  81. package/tests/build/success/.dfx/local/canisters/bar/bar.wasm +0 -0
  82. package/tests/build/success/.dfx/local/canisters/bar/constructor.did +3 -0
  83. package/tests/build/success/.dfx/local/canisters/bar/index.js +42 -0
  84. package/tests/build/success/.dfx/local/canisters/bar/init_args.txt +1 -0
  85. package/tests/build/success/.dfx/local/canisters/bar/service.did +3 -0
  86. package/tests/build/success/.dfx/local/canisters/bar/service.did.d.ts +7 -0
  87. package/tests/build/success/.dfx/local/canisters/bar/service.did.js +4 -0
  88. package/tests/build/success/.dfx/local/canisters/foo/constructor.did +3 -0
  89. package/tests/build/success/.dfx/local/canisters/foo/foo.did +3 -0
  90. package/tests/build/success/.dfx/local/canisters/foo/foo.most +4 -0
  91. package/tests/build/success/.dfx/local/canisters/foo/foo.wasm +0 -0
  92. package/tests/build/success/.dfx/local/canisters/foo/index.js +42 -0
  93. package/tests/build/success/.dfx/local/canisters/foo/init_args.txt +1 -0
  94. package/tests/build/success/.dfx/local/canisters/foo/service.did +3 -0
  95. package/tests/build/success/.dfx/local/canisters/foo/service.did.d.ts +7 -0
  96. package/tests/build/success/.dfx/local/canisters/foo/service.did.js +4 -0
  97. package/tests/build/success/.dfx/local/lsp/ucwa4-rx777-77774-qaada-cai.did +3 -0
  98. package/tests/build/success/.dfx/local/lsp/ulvla-h7777-77774-qaacq-cai.did +3 -0
  99. package/tests/build/success/.dfx/local/network-id +4 -0
  100. package/tests/check/fix/overlapping.mo +10 -0
  101. package/tests/check/moc-args/Warning.mo +5 -0
  102. package/tests/check/moc-args/mops.toml +2 -0
  103. package/tests/check-fix.test.ts +23 -0
  104. package/tests/check.test.ts +5 -0
  105. package/tests/moc-args.test.ts +19 -0
  106. package/tests/toolchain-local-subpath/bin/moc +2 -0
  107. package/tests/toolchain-local-subpath/mops.toml +2 -0
  108. package/tests/toolchain.test.ts +13 -0
  109. package/types.ts +3 -0
  110. package/wasm/Cargo.lock +54 -101
  111. package/wasm/pkg/bundler/package.json +20 -0
  112. package/wasm/pkg/bundler/wasm.d.ts +3 -0
  113. package/wasm/pkg/bundler/wasm.js +5 -0
  114. package/wasm/pkg/bundler/wasm_bg.js +93 -0
  115. package/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  116. package/wasm/pkg/bundler/wasm_bg.wasm.d.ts +8 -0
  117. package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  118. package/wasm/pkg/web/wasm_bg.wasm +0 -0
@@ -6,12 +6,6 @@ import {
6
6
  type TextEdit,
7
7
  } from "vscode-languageserver-textdocument";
8
8
 
9
- interface Fix {
10
- file: string;
11
- code: string;
12
- edit: TextEdit;
13
- }
14
-
15
9
  interface MocSpan {
16
10
  file: string;
17
11
  line_start: number;
@@ -46,35 +40,127 @@ export function parseDiagnostics(stdout: string): MocDiagnostic[] {
46
40
  .filter((d) => d !== null);
47
41
  }
48
42
 
49
- function extractFixes(diagnostics: MocDiagnostic[]): Fix[] {
50
- const fixes: Fix[] = [];
43
+ interface DiagnosticFix {
44
+ code: string;
45
+ edits: TextEdit[];
46
+ }
47
+
48
+ function extractDiagnosticFixes(
49
+ diagnostics: MocDiagnostic[],
50
+ ): Map<string, DiagnosticFix[]> {
51
+ const result = new Map<string, DiagnosticFix[]>();
52
+
51
53
  for (const diag of diagnostics) {
54
+ const editsByFile = new Map<string, TextEdit[]>();
55
+
52
56
  for (const span of diag.spans) {
53
57
  if (
54
58
  span.suggestion_applicability === "MachineApplicable" &&
55
59
  span.suggested_replacement !== null
56
60
  ) {
57
- fixes.push({
58
- file: span.file,
59
- code: diag.code,
60
- edit: {
61
- range: {
62
- start: {
63
- line: span.line_start - 1,
64
- character: span.column_start - 1,
65
- },
66
- end: {
67
- line: span.line_end - 1,
68
- character: span.column_end - 1,
69
- },
61
+ const file = resolve(span.file);
62
+ const edits = editsByFile.get(file) ?? [];
63
+ edits.push({
64
+ range: {
65
+ start: {
66
+ line: span.line_start - 1,
67
+ character: span.column_start - 1,
68
+ },
69
+ end: {
70
+ line: span.line_end - 1,
71
+ character: span.column_end - 1,
70
72
  },
71
- newText: span.suggested_replacement,
72
73
  },
74
+ newText: span.suggested_replacement,
73
75
  });
76
+ editsByFile.set(file, edits);
74
77
  }
75
78
  }
79
+
80
+ for (const [file, edits] of editsByFile) {
81
+ const existing = result.get(file) ?? [];
82
+ existing.push({ code: diag.code, edits });
83
+ result.set(file, existing);
84
+ }
85
+ }
86
+
87
+ return result;
88
+ }
89
+
90
+ type Range = TextEdit["range"];
91
+
92
+ function normalizeRange(range: Range): Range {
93
+ const { start, end } = range;
94
+ if (
95
+ start.line > end.line ||
96
+ (start.line === end.line && start.character > end.character)
97
+ ) {
98
+ return { start: end, end: start };
99
+ }
100
+ return range;
101
+ }
102
+
103
+ interface OffsetEdit {
104
+ start: number;
105
+ end: number;
106
+ newText: string;
107
+ }
108
+
109
+ /**
110
+ * Applies diagnostic fixes to a document, processing each diagnostic as
111
+ * an atomic unit. If any edit from a diagnostic overlaps with an already-accepted
112
+ * edit, the entire diagnostic is skipped (picked up in subsequent iterations).
113
+ * Based on vscode-languageserver-textdocument's TextDocument.applyEdits.
114
+ */
115
+ function applyDiagnosticFixes(
116
+ doc: TextDocument,
117
+ fixes: DiagnosticFix[],
118
+ ): { text: string; appliedCodes: string[] } {
119
+ const acceptedEdits: OffsetEdit[] = [];
120
+ const appliedCodes: string[] = [];
121
+
122
+ for (const fix of fixes) {
123
+ const offsets: OffsetEdit[] = fix.edits.map((e) => {
124
+ const range = normalizeRange(e.range);
125
+ return {
126
+ start: doc.offsetAt(range.start),
127
+ end: doc.offsetAt(range.end),
128
+ newText: e.newText,
129
+ };
130
+ });
131
+
132
+ const overlaps = offsets.some((o) =>
133
+ acceptedEdits.some((a) => o.start < a.end && o.end > a.start),
134
+ );
135
+ if (overlaps) {
136
+ continue;
137
+ }
138
+
139
+ acceptedEdits.push(...offsets);
140
+ appliedCodes.push(fix.code);
141
+ }
142
+
143
+ acceptedEdits.sort((a, b) => a.start - b.start);
144
+
145
+ const text = doc.getText();
146
+ const spans: string[] = [];
147
+ let lastOffset = 0;
148
+
149
+ for (const edit of acceptedEdits) {
150
+ if (edit.start < lastOffset) {
151
+ continue;
152
+ }
153
+ if (edit.start > lastOffset) {
154
+ spans.push(text.substring(lastOffset, edit.start));
155
+ }
156
+ if (edit.newText.length) {
157
+ spans.push(edit.newText);
158
+ }
159
+ lastOffset = edit.end;
76
160
  }
77
- return fixes;
161
+
162
+ spans.push(text.substring(lastOffset));
163
+ return { text: spans.join(""), appliedCodes };
78
164
  }
79
165
 
80
166
  const MAX_FIX_ITERATIONS = 10;
@@ -93,47 +179,33 @@ export async function autofixMotoko(
93
179
  const fixedFilesCodes = new Map<string, string[]>();
94
180
 
95
181
  for (let iteration = 0; iteration < MAX_FIX_ITERATIONS; iteration++) {
96
- const allFixes: Fix[] = [];
182
+ const fixesByFile = new Map<string, DiagnosticFix[]>();
97
183
 
98
184
  for (const file of files) {
99
185
  const result = await execa(
100
186
  mocPath,
101
- [file, "--error-format=json", ...mocArgs],
187
+ [file, ...mocArgs, "--error-format=json"],
102
188
  { stdio: "pipe", reject: false },
103
189
  );
104
190
 
105
191
  const diagnostics = parseDiagnostics(result.stdout);
106
- allFixes.push(...extractFixes(diagnostics));
192
+ for (const [targetFile, fixes] of extractDiagnosticFixes(diagnostics)) {
193
+ const existing = fixesByFile.get(targetFile) ?? [];
194
+ existing.push(...fixes);
195
+ fixesByFile.set(targetFile, existing);
196
+ }
107
197
  }
108
198
 
109
- if (allFixes.length === 0) {
199
+ if (fixesByFile.size === 0) {
110
200
  break;
111
201
  }
112
202
 
113
- const fixesByFile = new Map<string, Fix[]>();
114
- for (const fix of allFixes) {
115
- const normalizedPath = resolve(fix.file);
116
- const existing = fixesByFile.get(normalizedPath) ?? [];
117
- existing.push(fix);
118
- fixesByFile.set(normalizedPath, existing);
119
- }
120
-
121
203
  let progress = false;
122
204
 
123
205
  for (const [file, fixes] of fixesByFile) {
124
206
  const original = await readFile(file, "utf-8");
125
207
  const doc = TextDocument.create(`file://${file}`, "motoko", 0, original);
126
-
127
- let result: string;
128
- try {
129
- result = TextDocument.applyEdits(
130
- doc,
131
- fixes.map((f) => f.edit),
132
- );
133
- } catch (err) {
134
- console.warn(`Warning: could not apply fixes to ${file}: ${err}`);
135
- continue;
136
- }
208
+ const { text: result, appliedCodes } = applyDiagnosticFixes(doc, fixes);
137
209
 
138
210
  if (result === original) {
139
211
  continue;
@@ -143,9 +215,7 @@ export async function autofixMotoko(
143
215
  progress = true;
144
216
 
145
217
  const existing = fixedFilesCodes.get(file) ?? [];
146
- for (const fix of fixes) {
147
- existing.push(fix.code);
148
- }
218
+ existing.push(...appliedCodes);
149
219
  fixedFilesCodes.set(file, existing);
150
220
  }
151
221
 
package/mops.ts CHANGED
@@ -5,9 +5,8 @@ import { Identity } from "@icp-sdk/core/agent";
5
5
  import TOML from "@iarna/toml";
6
6
  import chalk from "chalk";
7
7
  import prompts from "prompts";
8
- import fetch from "node-fetch";
9
-
10
8
  import { decodeFile } from "./pem.js";
9
+ import { cliError } from "./error.js";
11
10
  import { Config, Dependency } from "./types.js";
12
11
  import { mainActor, storageActor } from "./api/actors.js";
13
12
  import { getNetwork } from "./api/network.js";
@@ -15,10 +14,6 @@ import { getHighestVersion } from "./api/getHighestVersion.js";
15
14
  import { getPackageId } from "./helpers/get-package-id.js";
16
15
  import { FILE_PATH_REGEX } from "./constants.js";
17
16
 
18
- if (!globalThis.fetch) {
19
- globalThis.fetch = fetch as any;
20
- }
21
-
22
17
  // (!) make changes in pair with backend
23
18
  export let apiVersion = "1.3";
24
19
 
@@ -204,6 +199,18 @@ export function readConfig(configFile = getClosestConfigFile()): Config {
204
199
  return config;
205
200
  }
206
201
 
202
+ export function getGlobalMocArgs(config: Config): string[] {
203
+ if (!config.moc?.args) {
204
+ return [];
205
+ }
206
+ if (typeof config.moc.args === "string") {
207
+ cliError(
208
+ `[moc] config 'args' should be an array of strings in mops.toml config file`,
209
+ );
210
+ }
211
+ return config.moc.args;
212
+ }
213
+
207
214
  export function writeConfig(
208
215
  config: Config,
209
216
  configFile = getClosestConfigFile(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
@@ -36,8 +36,8 @@
36
36
  "build:wasm:web": "wasm-pack build wasm --target web --out-dir pkg/web && rm wasm/pkg/web/.gitignore",
37
37
  "dist": "npm run build:wasm && tsc && mkdir -p dist/wasm && cp -r wasm/pkg dist/wasm",
38
38
  "bundle": "rm -rf ./bundle && bun build ./environments/web/cli.ts --outdir ./bundle --target node --minify --external @napi-rs/lzma --external fsevents --format esm --define '__dirname=import.meta.dirname' && run-s bundle:fix bundle:copy bundle:package-json bundle:tar",
39
- "bundle:fix": "rexreplace 'new URL\\(\"\\.\\./templates' 'new URL(\"./templates' bundle/cli.js && rexreplace 'resolve\\(\".*?/xhr-sync-worker\\.js\"\\)' 'resolve(\"./xhr-sync-worker.js\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"wasm_bg.wasm\"' bundle/cli.js",
40
- "bundle:copy": "cp -r commands/bench bundle && cp -r bin declarations templates package.json bundle && cp -r node_modules/prettier-plugin-motoko/wasm/pkg/nodejs/wasm_bg.wasm node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js bundle",
39
+ "bundle:fix": "rexreplace 'new URL\\(\"\\.\\./templates' 'new URL(\"./templates' bundle/cli.js && rexreplace 'resolve\\(\".*?/xhr-sync-worker\\.js\"\\)' 'resolve(\"./xhr-sync-worker.js\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"wasm_bg.wasm\"' bundle/cli.js && rexreplace 'resolve\\(\"import.meta.dirname\",\".*?/default-stylesheet\\.css\"\\)' 'resolve(import.meta.dirname,\"./default-stylesheet.css\")' bundle/cli.js",
40
+ "bundle:copy": "cp -r commands/bench bundle && cp -r bin declarations templates package.json bundle && cp -r node_modules/prettier-plugin-motoko/wasm/pkg/nodejs/wasm_bg.wasm node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js node_modules/jsdom/lib/jsdom/browser/default-stylesheet.css bundle",
41
41
  "bundle:package-json": "tsx bundle-package-json.ts",
42
42
  "bundle:tar": "rm -f bundle/cli.tgz && touch -t 200101010101 bundle/cli.tgz && find bundle -exec touch -d '1970-01-01 00:00:00' {} + && tar --sort name --exclude bundle/cli.tgz -cvf - bundle | gzip -n > bundle/cli.tgz",
43
43
  "copy": "cp -r commands/bench dist/commands && cp -r declarations templates package.json bin dist | true",
@@ -69,29 +69,28 @@
69
69
  "filesize": "10.1.6",
70
70
  "fs-extra": "11.2.0",
71
71
  "get-folder-size": "5.0.0",
72
- "glob": "11.0.1",
72
+ "glob": "13.0.6",
73
73
  "globby": "14.0.2",
74
74
  "got": "14.4.6",
75
- "jsdom": "26.1.0",
75
+ "jsdom": "28.1.0",
76
76
  "log-update": "6.1.0",
77
77
  "markdown-table": "3.0.4",
78
78
  "mdast-util-from-markdown": "2.0.2",
79
79
  "mdast-util-to-markdown": "2.1.2",
80
80
  "minimatch": "10.0.1",
81
81
  "ncp": "2.0.0",
82
- "node-fetch": "3.3.2",
83
82
  "octokit": "3.1.2",
84
83
  "pem-file": "1.0.1",
85
84
  "pic-ic": "0.5.4",
86
85
  "pic-js-mops": "0.14.8",
87
86
  "prettier": "3.5.3",
88
- "prettier-plugin-motoko": "0.11.0",
87
+ "prettier-plugin-motoko": "0.12.5",
89
88
  "promisify-child-process": "4.1.2",
90
89
  "prompts": "2.4.2",
91
90
  "semver": "7.7.1",
92
91
  "stream-to-promise": "3.0.0",
93
92
  "string-width": "7.2.0",
94
- "tar": "7.5.6",
93
+ "tar": "7.5.9",
95
94
  "terminal-size": "4.0.0",
96
95
  "vscode-languageserver-textdocument": "1.0.12"
97
96
  },
@@ -101,7 +100,7 @@
101
100
  "@types/decompress": "4.2.7",
102
101
  "@types/fs-extra": "11.0.4",
103
102
  "@types/glob": "8.1.0",
104
- "@types/jsdom": "21.1.7",
103
+ "@types/jsdom": "28.0.0",
105
104
  "@types/ncp": "2.0.8",
106
105
  "@types/node": "24.0.3",
107
106
  "@types/prompts": "2.4.9",
@@ -37,9 +37,9 @@ persistent actor {
37
37
  `;
38
38
 
39
39
  exports[`check --fix M0236: fix output 1`] = `
40
- "Fixed run/M0236.mo (2 fixes: M0236)
40
+ "Fixed run/M0236.mo (1 fix: M0236)
41
41
 
42
- 2 fixes applied to 1 file"
42
+ 1 fix applied to 1 file"
43
43
  `;
44
44
 
45
45
  exports[`check --fix M0237 1`] = `
@@ -203,16 +203,35 @@ do {
203
203
  `;
204
204
 
205
205
  exports[`check --fix edit-suggestions: fix output 1`] = `
206
- "Fixed run/edit-suggestions.mo (41 fixes: M0223, M0236, M0237)
206
+ "Fixed run/edit-suggestions.mo (30 fixes: M0223, M0236, M0237)
207
+
208
+ ✓ 30 fixes applied to 1 file"
209
+ `;
210
+
211
+ exports[`check --fix overlapping edits 1`] = `
212
+ "import Array "mo:core/Array";
213
+
214
+ // Overlapping fixable errors (nested calls produce overlapping M0223 + M0236 edits)
215
+ do {
216
+ let ar = [1, 2, 3];
217
+ let _ = ar.filter(func(x) { x > 0 }).filter(
218
+ func(x) { x > 0 },
219
+ );
220
+ };
221
+ "
222
+ `;
223
+
224
+ exports[`check --fix overlapping edits: fix output 1`] = `
225
+ "Fixed run/overlapping.mo (4 fixes: M0223, M0236)
207
226
 
208
- 41 fixes applied to 1 file"
227
+ 4 fixes applied to 1 file"
209
228
  `;
210
229
 
211
230
  exports[`check --fix transitive imports: fix output 1`] = `
212
- "Fixed run/transitive-lib.mo (2 fixes: M0236)
231
+ "Fixed run/transitive-lib.mo (1 fix: M0236)
213
232
  Fixed run/transitive-main.mo (1 fix: M0223)
214
233
 
215
- 3 fixes applied to 2 files"
234
+ 2 fixes applied to 2 files"
216
235
  `;
217
236
 
218
237
  exports[`check --fix transitive imports: lib file 1`] = `
@@ -1,5 +1,14 @@
1
1
  // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
2
 
3
+ exports[`check [moc] args are passed to moc 1`] = `
4
+ {
5
+ "exitCode": 1,
6
+ "stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier unused (delete or rename to wildcard \`_\` or \`_unused\`)
7
+ ✗ Check failed for file Warning.mo (exit code: 1)",
8
+ "stdout": "moc < 1.3.0: some diagnostic hints may be missing",
9
+ }
10
+ `;
11
+
3
12
  exports[`check error 1`] = `
4
13
  {
5
14
  "exitCode": 1,
@@ -0,0 +1,17 @@
1
+ {
2
+ "__Candid_UI": {
3
+ "local": "ufxgi-4p777-77774-qaadq-cai"
4
+ },
5
+ "a": {
6
+ "local": "uzt4z-lp777-77774-qaabq-cai"
7
+ },
8
+ "b": {
9
+ "local": "umunu-kh777-77774-qaaca-cai"
10
+ },
11
+ "bar": {
12
+ "local": "ulvla-h7777-77774-qaacq-cai"
13
+ },
14
+ "foo": {
15
+ "local": "ucwa4-rx777-77774-qaada-cai"
16
+ }
17
+ }
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,4 @@
1
+ // Version: 1.0.0
2
+ actor {
3
+
4
+ };
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,42 @@
1
+ import { Actor, HttpAgent } from "@dfinity/agent";
2
+
3
+ // Imports and re-exports candid interface
4
+ import { idlFactory } from './bar.did.js';
5
+ export { idlFactory } from './bar.did.js';
6
+ // CANISTER_ID is replaced by webpack based on node environment
7
+ export const canisterId = process.env.CANISTER_ID_BAR;
8
+
9
+ /**
10
+ * @deprecated since dfx 0.11.1
11
+ * Do not import from `.dfx`, instead switch to using `dfx generate` to generate your JS interface.
12
+ * @param {string | import("@dfinity/principal").Principal} canisterId Canister ID of Agent
13
+ * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig} | { agent?: import("@dfinity/agent").Agent; actorOptions?: import("@dfinity/agent").ActorConfig }} [options]
14
+ * @return {import("@dfinity/agent").ActorSubclass<import("./bar.did.js")._SERVICE>}
15
+ */
16
+ export const createActor = (canisterId, options = {}) => {
17
+ console.warn(`Deprecation warning: you are currently importing code from .dfx. Going forward, refactor to use the dfx generate command for JavaScript bindings.
18
+
19
+ See https://internetcomputer.org/docs/current/developer-docs/updates/release-notes/ for migration instructions`);
20
+ const agent = options.agent || new HttpAgent({ ...options.agentOptions });
21
+
22
+ // Fetch root key for certificate validation during development
23
+ if (process.env.DFX_NETWORK !== "ic") {
24
+ agent.fetchRootKey().catch(err => {
25
+ console.warn("Unable to fetch root key. Check to ensure that your local replica is running");
26
+ console.error(err);
27
+ });
28
+ }
29
+
30
+ // Creates an actor with using the candid interface and the HttpAgent
31
+ return Actor.createActor(idlFactory, {
32
+ agent,
33
+ canisterId,
34
+ ...(options ? options.actorOptions : {}),
35
+ });
36
+ };
37
+
38
+ /**
39
+ * A ready-to-use agent for the bar canister
40
+ * @type {import("@dfinity/agent").ActorSubclass<import("./bar.did.js")._SERVICE>}
41
+ */
42
+ export const bar = createActor(canisterId);
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,7 @@
1
+ import type { Principal } from '@dfinity/principal';
2
+ import type { ActorMethod } from '@dfinity/agent';
3
+ import type { IDL } from '@dfinity/candid';
4
+
5
+ export interface _SERVICE { 'call' : ActorMethod<[], string> }
6
+ export declare const idlFactory: IDL.InterfaceFactory;
7
+ export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];
@@ -0,0 +1,4 @@
1
+ export const idlFactory = ({ IDL }) => {
2
+ return IDL.Service({ 'call' : IDL.Func([], [IDL.Text], []) });
3
+ };
4
+ export const init = ({ IDL }) => { return []; };
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,4 @@
1
+ // Version: 1.0.0
2
+ actor {
3
+
4
+ };
@@ -0,0 +1,42 @@
1
+ import { Actor, HttpAgent } from "@dfinity/agent";
2
+
3
+ // Imports and re-exports candid interface
4
+ import { idlFactory } from './foo.did.js';
5
+ export { idlFactory } from './foo.did.js';
6
+ // CANISTER_ID is replaced by webpack based on node environment
7
+ export const canisterId = process.env.CANISTER_ID_FOO;
8
+
9
+ /**
10
+ * @deprecated since dfx 0.11.1
11
+ * Do not import from `.dfx`, instead switch to using `dfx generate` to generate your JS interface.
12
+ * @param {string | import("@dfinity/principal").Principal} canisterId Canister ID of Agent
13
+ * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig} | { agent?: import("@dfinity/agent").Agent; actorOptions?: import("@dfinity/agent").ActorConfig }} [options]
14
+ * @return {import("@dfinity/agent").ActorSubclass<import("./foo.did.js")._SERVICE>}
15
+ */
16
+ export const createActor = (canisterId, options = {}) => {
17
+ console.warn(`Deprecation warning: you are currently importing code from .dfx. Going forward, refactor to use the dfx generate command for JavaScript bindings.
18
+
19
+ See https://internetcomputer.org/docs/current/developer-docs/updates/release-notes/ for migration instructions`);
20
+ const agent = options.agent || new HttpAgent({ ...options.agentOptions });
21
+
22
+ // Fetch root key for certificate validation during development
23
+ if (process.env.DFX_NETWORK !== "ic") {
24
+ agent.fetchRootKey().catch(err => {
25
+ console.warn("Unable to fetch root key. Check to ensure that your local replica is running");
26
+ console.error(err);
27
+ });
28
+ }
29
+
30
+ // Creates an actor with using the candid interface and the HttpAgent
31
+ return Actor.createActor(idlFactory, {
32
+ agent,
33
+ canisterId,
34
+ ...(options ? options.actorOptions : {}),
35
+ });
36
+ };
37
+
38
+ /**
39
+ * A ready-to-use agent for the foo canister
40
+ * @type {import("@dfinity/agent").ActorSubclass<import("./foo.did.js")._SERVICE>}
41
+ */
42
+ export const foo = createActor(canisterId);
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,7 @@
1
+ import type { Principal } from '@dfinity/principal';
2
+ import type { ActorMethod } from '@dfinity/agent';
3
+ import type { IDL } from '@dfinity/candid';
4
+
5
+ export interface _SERVICE { 'call' : ActorMethod<[], string> }
6
+ export declare const idlFactory: IDL.InterfaceFactory;
7
+ export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];
@@ -0,0 +1,4 @@
1
+ export const idlFactory = ({ IDL }) => {
2
+ return IDL.Service({ 'call' : IDL.Func([], [IDL.Text], []) });
3
+ };
4
+ export const init = ({ IDL }) => { return []; };
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,3 @@
1
+ service : {
2
+ call: () -> (text);
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "created": "2025-09-11 02:20:43.69975 +00:00:00",
3
+ "settings_digest": "685af83dea9d217efa2077ec50e8b0ed561d1c292a5454dacdc43d3b5e45306f"
4
+ }
@@ -0,0 +1,10 @@
1
+ import Array "mo:core/Array";
2
+
3
+ // Overlapping fixable errors (nested calls produce overlapping M0223 + M0236 edits)
4
+ do {
5
+ let ar = [1, 2, 3];
6
+ let _ = Array.filter<Nat>(
7
+ Array.filter<Nat>(ar, func(x) { x > 0 }),
8
+ func(x) { x > 0 },
9
+ );
10
+ };
@@ -0,0 +1,5 @@
1
+ persistent actor {
2
+ public func example() : async () {
3
+ let unused = 123;
4
+ };
5
+ };
@@ -0,0 +1,2 @@
1
+ [moc]
2
+ args = ["-Werror"]
@@ -80,6 +80,10 @@ describe("check --fix", () => {
80
80
  });
81
81
  });
82
82
 
83
+ test("overlapping edits", async () => {
84
+ await testCheckFix("overlapping.mo", { M0223: 1, M0236: 2 });
85
+ });
86
+
83
87
  test("transitive imports", async () => {
84
88
  const runMainPath = copyFixture("transitive-main.mo");
85
89
  const runLibPath = copyFixture("transitive-lib.mo");
@@ -100,6 +104,25 @@ describe("check --fix", () => {
100
104
  expect(countCodes(afterResult.stdout)).toEqual({});
101
105
  });
102
106
 
107
+ test("--error-format=human does not break --fix", async () => {
108
+ const runFilePath = copyFixture("M0223.mo");
109
+
110
+ const fixResult = await cli(
111
+ [
112
+ "check",
113
+ runFilePath,
114
+ "--fix",
115
+ "--",
116
+ warningFlags,
117
+ "--error-format=human",
118
+ ],
119
+ { cwd: fixDir },
120
+ );
121
+
122
+ expect(fixResult.stdout).toContain("1 fix applied");
123
+ expect(readFileSync(runFilePath, "utf-8")).not.toContain("<Nat>");
124
+ });
125
+
103
126
  test("verbose", async () => {
104
127
  const result = await cli(["check", "Ok.mo", "--fix", "--verbose"], {
105
128
  cwd: fixDir,