ic-mops 2.0.0 → 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 (197) 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 +42 -17
  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 +38 -16
  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 +4 -3
  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 +6 -1
  63. package/dist/wasm/pkg/nodejs/wasm.d.ts +3 -0
  64. package/dist/wasm/pkg/nodejs/wasm.js +323 -17
  65. package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  66. package/dist/wasm/pkg/nodejs/wasm_bg.wasm.d.ts +6 -1
  67. package/dist/wasm/pkg/web/wasm.d.ts +10 -1
  68. package/dist/wasm/pkg/web/wasm.js +300 -21
  69. package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
  70. package/dist/wasm/pkg/web/wasm_bg.wasm.d.ts +6 -1
  71. package/dist/wasm.d.ts +6 -1
  72. package/environments/nodejs/cli.ts +7 -1
  73. package/helpers/autofix-motoko.ts +170 -0
  74. package/helpers/get-moc-version.ts +12 -1
  75. package/mops.ts +2 -1
  76. package/package.json +4 -3
  77. package/tests/__snapshots__/build-no-dfx.test.ts.snap +11 -0
  78. package/tests/__snapshots__/build.test.ts.snap +77 -0
  79. package/tests/__snapshots__/check-candid.test.ts.snap +73 -0
  80. package/tests/__snapshots__/check-fix.test.ts.snap +242 -0
  81. package/tests/__snapshots__/check.test.ts.snap +72 -0
  82. package/tests/__snapshots__/lint.test.ts.snap +78 -0
  83. package/tests/build/error/src/Bar.mo +2 -2
  84. package/tests/build/no-dfx/mops.toml +5 -0
  85. package/tests/build/no-dfx/src/Main.mo +5 -0
  86. package/tests/build/success/candid/bar.did +1 -0
  87. package/tests/build/success/mops.toml +8 -3
  88. package/tests/build-no-dfx.test.ts +10 -0
  89. package/tests/build.test.ts +24 -0
  90. package/tests/check/error/Error.mo +7 -0
  91. package/tests/check/error/mops.toml +2 -0
  92. package/tests/check/fix/M0223.mo +11 -0
  93. package/tests/check/fix/M0236.mo +11 -0
  94. package/tests/check/fix/M0237.mo +11 -0
  95. package/tests/check/fix/Ok.mo +7 -0
  96. package/tests/check/fix/edit-suggestions.mo +143 -0
  97. package/tests/check/fix/mops.toml +5 -0
  98. package/tests/check/fix/transitive-lib.mo +9 -0
  99. package/tests/check/fix/transitive-main.mo +9 -0
  100. package/tests/check/success/Ok.mo +5 -0
  101. package/tests/check/success/Warning.mo +5 -0
  102. package/tests/check/success/mops.toml +2 -0
  103. package/tests/check-candid.test.ts +22 -0
  104. package/tests/check-fix.test.ts +111 -0
  105. package/tests/check.test.ts +46 -0
  106. package/tests/cli.test.ts +4 -74
  107. package/tests/helpers.ts +58 -0
  108. package/tests/lint/lints/no-bool-switch.toml +9 -0
  109. package/tests/lint/mops.toml +4 -0
  110. package/tests/lint/src/NoBoolSwitch.mo +8 -0
  111. package/tests/lint/src/Ok.mo +5 -0
  112. package/tests/lint.test.ts +17 -0
  113. package/tests/toolchain/mock +2 -0
  114. package/tests/toolchain/mops.toml +2 -0
  115. package/tests/toolchain.test.ts +12 -0
  116. package/types.ts +6 -1
  117. package/wasm/Cargo.lock +101 -54
  118. package/wasm/Cargo.toml +2 -5
  119. package/wasm/pkg/nodejs/wasm.d.ts +3 -0
  120. package/wasm/pkg/nodejs/wasm.js +323 -17
  121. package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  122. package/wasm/pkg/nodejs/wasm_bg.wasm.d.ts +6 -1
  123. package/wasm/pkg/web/wasm.d.ts +10 -1
  124. package/wasm/pkg/web/wasm.js +300 -21
  125. package/wasm/pkg/web/wasm_bg.wasm +0 -0
  126. package/wasm/pkg/web/wasm_bg.wasm.d.ts +6 -1
  127. package/wasm/src/lib.rs +10 -5
  128. package/wasm/src/utils.rs +15 -0
  129. package/wasm/src/wasm_utils.rs +79 -0
  130. package/wasm.ts +10 -1
  131. package/.DS_Store +0 -0
  132. package/bundle/bench/bench-canister.mo +0 -121
  133. package/bundle/bench/user-bench.mo +0 -10
  134. package/bundle/bin/moc-wrapper.sh +0 -40
  135. package/bundle/bin/mops.js +0 -3
  136. package/bundle/cli.js +0 -2144
  137. package/bundle/declarations/bench/bench.did +0 -30
  138. package/bundle/declarations/bench/bench.did.d.ts +0 -33
  139. package/bundle/declarations/bench/bench.did.js +0 -30
  140. package/bundle/declarations/bench/index.d.ts +0 -50
  141. package/bundle/declarations/bench/index.js +0 -40
  142. package/bundle/declarations/main/index.d.ts +0 -50
  143. package/bundle/declarations/main/index.js +0 -40
  144. package/bundle/declarations/main/main.did +0 -428
  145. package/bundle/declarations/main/main.did.d.ts +0 -348
  146. package/bundle/declarations/main/main.did.js +0 -406
  147. package/bundle/declarations/storage/index.d.ts +0 -50
  148. package/bundle/declarations/storage/index.js +0 -30
  149. package/bundle/declarations/storage/storage.did +0 -46
  150. package/bundle/declarations/storage/storage.did.d.ts +0 -40
  151. package/bundle/declarations/storage/storage.did.js +0 -38
  152. package/bundle/package.json +0 -36
  153. package/bundle/templates/README.md +0 -13
  154. package/bundle/templates/licenses/Apache-2.0 +0 -202
  155. package/bundle/templates/licenses/Apache-2.0-NOTICE +0 -13
  156. package/bundle/templates/licenses/MIT +0 -21
  157. package/bundle/templates/mops-publish.yml +0 -17
  158. package/bundle/templates/mops-test.yml +0 -24
  159. package/bundle/templates/src/lib.mo +0 -15
  160. package/bundle/templates/test/lib.test.mo +0 -4
  161. package/bundle/wasm_bg.wasm +0 -0
  162. package/bundle/xhr-sync-worker.js +0 -59
  163. package/dist/wasm/pkg/bundler/package.json +0 -20
  164. package/dist/wasm/pkg/bundler/wasm.d.ts +0 -3
  165. package/dist/wasm/pkg/bundler/wasm.js +0 -5
  166. package/dist/wasm/pkg/bundler/wasm_bg.js +0 -93
  167. package/dist/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  168. package/dist/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
  169. package/tests/__snapshots__/cli.test.ts.snap +0 -202
  170. package/tests/build/success/.dfx/local/canister_ids.json +0 -17
  171. package/tests/build/success/.dfx/local/canisters/bar/bar.did +0 -3
  172. package/tests/build/success/.dfx/local/canisters/bar/bar.most +0 -4
  173. package/tests/build/success/.dfx/local/canisters/bar/bar.wasm +0 -0
  174. package/tests/build/success/.dfx/local/canisters/bar/constructor.did +0 -3
  175. package/tests/build/success/.dfx/local/canisters/bar/index.js +0 -42
  176. package/tests/build/success/.dfx/local/canisters/bar/init_args.txt +0 -1
  177. package/tests/build/success/.dfx/local/canisters/bar/service.did +0 -3
  178. package/tests/build/success/.dfx/local/canisters/bar/service.did.d.ts +0 -7
  179. package/tests/build/success/.dfx/local/canisters/bar/service.did.js +0 -4
  180. package/tests/build/success/.dfx/local/canisters/foo/constructor.did +0 -3
  181. package/tests/build/success/.dfx/local/canisters/foo/foo.did +0 -3
  182. package/tests/build/success/.dfx/local/canisters/foo/foo.most +0 -4
  183. package/tests/build/success/.dfx/local/canisters/foo/foo.wasm +0 -0
  184. package/tests/build/success/.dfx/local/canisters/foo/index.js +0 -42
  185. package/tests/build/success/.dfx/local/canisters/foo/init_args.txt +0 -1
  186. package/tests/build/success/.dfx/local/canisters/foo/service.did +0 -3
  187. package/tests/build/success/.dfx/local/canisters/foo/service.did.d.ts +0 -7
  188. package/tests/build/success/.dfx/local/canisters/foo/service.did.js +0 -4
  189. package/tests/build/success/.dfx/local/lsp/ucwa4-rx777-77774-qaada-cai.did +0 -3
  190. package/tests/build/success/.dfx/local/lsp/ulvla-h7777-77774-qaacq-cai.did +0 -3
  191. package/tests/build/success/.dfx/local/network-id +0 -4
  192. package/wasm/pkg/bundler/package.json +0 -20
  193. package/wasm/pkg/bundler/wasm.d.ts +0 -3
  194. package/wasm/pkg/bundler/wasm.js +0 -5
  195. package/wasm/pkg/bundler/wasm_bg.js +0 -93
  196. package/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  197. package/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
package/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Mops CLI Changelog
2
2
 
3
3
  ## Next
4
+
5
+ ## 2.1.0
6
+ - Add `mops check --fix` subcommand (for Motoko files) with autofix logic
7
+ - Add `mops check` subcommand for type-checking Motoko files
8
+ - Warn for `dfx` projects instead of requiring `mops toolchain init`
9
+ - Allow specifying toolchain file paths in `mops.toml`
10
+ - Add `mops lint` subcommand and `lintoko` toolchain management
11
+ - Improve bench-canister Bench type to be less restrictive (by @timohanke)
12
+
13
+ ## 2.0.1
14
+ - Patch vulnerability in `tar` dependency
15
+
16
+ # 2.0.0
4
17
  - `mops publish` add support for subheadings in changelog (by @f0i)
5
18
  - `mops toolchain` now downloads `moc.js` in addition to `moc` binary
6
19
  - New `mops build` subcommand (alternative to `dfx build`)
package/RELEASE.md ADDED
@@ -0,0 +1,179 @@
1
+ # Mops CLI Release
2
+
3
+ ## Prerequisites
4
+
5
+ ### macOS: GNU tar
6
+
7
+ ```bash
8
+ brew install gnu-tar
9
+ ```
10
+
11
+ Add to `~/.zshrc` or `~/.bashrc`:
12
+ ```bash
13
+ export PATH="$HOMEBREW_PREFIX/opt/gnu-tar/libexec/gnubin:$PATH"
14
+ ```
15
+
16
+ ### Docker
17
+
18
+ Docker (or OrbStack) must be installed and running. The CLI build happens inside a Docker container (`zenvoich/mops-builder:1.1.0`) for reproducibility.
19
+
20
+ ### dfx
21
+
22
+ `dfx` must be installed with the `mops` identity configured (see [Adding the `mops` identity to dfx](#adding-the-mops-identity-to-dfx)).
23
+
24
+ ## Release Steps
25
+
26
+ ### 1. Install dependencies
27
+
28
+ ```bash
29
+ cd cli && bun install
30
+ ```
31
+
32
+ ### 2. Update changelog
33
+
34
+ Move items from the `## Next` section in `CHANGELOG.md` into a new version heading:
35
+
36
+ ```markdown
37
+ ## Next
38
+
39
+ ## X.Y.Z
40
+ - Change 1
41
+ - Change 2
42
+ ```
43
+
44
+ The heading must contain the exact version string — `release-cli.ts` parses it to extract release notes.
45
+
46
+ ### 3. Create a release branch and PR
47
+
48
+ Direct pushes to `main` are not allowed. Create a branch and PR:
49
+
50
+ ```bash
51
+ git checkout -b <username>/release-X.Y.Z
52
+ ```
53
+
54
+ ### 4. Bump version
55
+
56
+ Use `--no-git-tag-version` since the version bump will be committed as part of the PR (not directly on `main`):
57
+
58
+ ```bash
59
+ cd cli
60
+ npm version minor --no-git-tag-version # or: patch / major
61
+ ```
62
+
63
+ ### 5. Commit, push, and open PR
64
+
65
+ ```bash
66
+ git add cli/CHANGELOG.md cli/package.json cli/package-lock.json
67
+ git commit -m "release: CLI vX.Y.Z"
68
+ git push -u origin <username>/release-X.Y.Z
69
+ gh pr create --title "release: CLI vX.Y.Z" --body "..."
70
+ ```
71
+
72
+ Wait for CI to pass, then merge the PR.
73
+
74
+ ### 6. Check reproducibility of the build
75
+
76
+ After the PR is merged to `main`, check the SHA256 hash from the latest [build-hash workflow](https://github.com/caffeinelabs/mops/actions/workflows/build-hash.yml) run.
77
+
78
+ **Important**: The CI hash is written to the GitHub Actions **Step Summary** — it is only visible on the **Summary tab** of the workflow run page. It does **not** appear in the downloadable logs (`gh run view --log` will not find it). You must open the run URL in a browser to see it.
79
+
80
+ Build the same version locally:
81
+ ```bash
82
+ cd cli
83
+ MOPS_VERSION=0.0.0 ./build.sh
84
+ ```
85
+
86
+ `build.sh` does the following:
87
+ 1. Reads `COMMIT_HASH` (defaults to `git rev-parse HEAD`) and `MOPS_VERSION`
88
+ 2. Runs `docker build` using `cli/Dockerfile` with those as build args
89
+ 3. The Dockerfile clones the repo at that commit, runs `bun install`, sets the version, and runs `npm run build`
90
+ 4. Prints the SHA256 hash of `cli.tgz`
91
+ 5. Copies `cli.tgz` out of the container into `cli/bundle/cli.tgz`
92
+
93
+ Compare the locally printed hash with the one from the CI Step Summary. If they don't match, do not proceed.
94
+
95
+ **Notes on the local build output:**
96
+ - The "Verification failed" message at the end is **expected** — it happens because no `SHASUM` env var is passed for comparison. The important output is the `Actual shasum: <hash>` line.
97
+
98
+ ### 7. Publish to npm
99
+
100
+ After the PR is merged and the build hash is verified:
101
+
102
+ ```bash
103
+ git checkout main && git pull
104
+ cd cli
105
+ npm publish
106
+ ```
107
+
108
+ ### 8. Prepare on-chain release
109
+
110
+ Run from the **repo root** (not `cli/`), with Docker running:
111
+
112
+ ```bash
113
+ npm run release-cli
114
+ ```
115
+
116
+ This runs `cli/release-cli.ts`, which:
117
+ 1. Calls `./build.sh` — full Docker build with the real version from `cli/package.json`
118
+ 2. Extracts release notes from `CHANGELOG.md` for that version
119
+ 3. Computes SHA256 of `bundle/cli.tgz`
120
+ 4. Copies the tarball to `cli-releases/versions/<version>.tgz`
121
+ 5. Creates tag copies: `latest.tgz` and `<major>.tgz`
122
+ 6. Updates `cli-releases/tags/latest` to the new version
123
+ 7. Updates `cli-releases/releases.json` with metadata (timestamp, size, hash, commit hash, download URL, release notes)
124
+
125
+ ### 9. Deploy the canister
126
+
127
+ ```bash
128
+ dfx deploy --network ic --no-wallet cli --identity mops
129
+ ```
130
+
131
+ This deploys the `cli-releases` canister (serving `cli.mops.one`) to the Internet Computer mainnet.
132
+
133
+ ## Verify build
134
+
135
+ Anyone can verify a released version by rebuilding from source:
136
+
137
+ ```bash
138
+ cd cli
139
+ docker build . --build-arg COMMIT_HASH=<commit_hash> --build-arg MOPS_VERSION=<mops_version> -t mops
140
+ docker run --rm --env SHASUM=<build_hash> mops
141
+ ```
142
+
143
+ ## Adding the `mops` identity to dfx
144
+
145
+ Check if the identity already exists:
146
+ ```bash
147
+ dfx identity list
148
+ ```
149
+
150
+ If `mops` appears, verify it's the correct one:
151
+ ```bash
152
+ dfx identity get-principal --identity mops
153
+ ```
154
+
155
+ If it doesn't exist, import it from the PEM file stored in Bitwarden (`Mops identity (canisters and packages)`):
156
+
157
+ 1. Save the PEM key to a temporary file:
158
+ ```bash
159
+ cat > /tmp/mops-identity.pem << 'EOF'
160
+ -----BEGIN EC PRIVATE KEY-----
161
+ <paste key from Bitwarden>
162
+ -----END EC PRIVATE KEY-----
163
+ EOF
164
+ ```
165
+
166
+ 2. Import into dfx:
167
+ ```bash
168
+ dfx identity import mops /tmp/mops-identity.pem
169
+ ```
170
+
171
+ 3. Delete the temporary file:
172
+ ```bash
173
+ rm /tmp/mops-identity.pem
174
+ ```
175
+
176
+ 4. Verify:
177
+ ```bash
178
+ dfx identity get-principal --identity mops
179
+ ```
package/bundle/cli.tgz CHANGED
Binary file
@@ -4,19 +4,14 @@ import chalk from "chalk";
4
4
 
5
5
  import { getDependencyType, getRootDir, readConfig } from "./mops.js";
6
6
  import { resolvePackages } from "./resolve-packages.js";
7
- import { getMocVersion } from "./helpers/get-moc-version.js";
7
+ import { getMocSemVer } from "./helpers/get-moc-version.js";
8
8
  import { getPackageId } from "./helpers/get-package-id.js";
9
9
 
10
10
  export async function checkRequirements({ verbose = false } = {}) {
11
- let config = readConfig();
12
- let mocVersion = config.toolchain?.moc;
13
- if (!mocVersion) {
14
- mocVersion = getMocVersion(false);
15
- }
16
- if (!mocVersion) {
11
+ let installedMoc = getMocSemVer();
12
+ if (!installedMoc) {
17
13
  return;
18
14
  }
19
- let installedMoc = new SemVer(mocVersion);
20
15
  let highestRequiredMoc = new SemVer("0.0.0");
21
16
  let highestRequiredMocPkgId = "";
22
17
  let rootDir = getRootDir();
package/cli.ts CHANGED
@@ -10,11 +10,13 @@ import { add } from "./commands/add.js";
10
10
  import { bench } from "./commands/bench.js";
11
11
  import { build, DEFAULT_BUILD_OUTPUT_DIR } from "./commands/build.js";
12
12
  import { bump } from "./commands/bump.js";
13
+ import { check } from "./commands/check.js";
13
14
  import { checkCandid } from "./commands/check-candid.js";
14
15
  import { docsCoverage } from "./commands/docs-coverage.js";
15
16
  import { docs } from "./commands/docs.js";
16
17
  import { format } from "./commands/format.js";
17
18
  import { init } from "./commands/init.js";
19
+ import { lint } from "./commands/lint.js";
18
20
  import { installAll } from "./commands/install/install-all.js";
19
21
  import {
20
22
  addMaintainer,
@@ -50,6 +52,7 @@ import {
50
52
  } from "./mops.js";
51
53
  import { resolvePackages } from "./resolve-packages.js";
52
54
  import { Tool } from "./types.js";
55
+ import { TOOLCHAINS } from "./commands/toolchain/toolchain-utils.js";
53
56
 
54
57
  declare global {
55
58
  // eslint-disable-next-line no-var
@@ -73,6 +76,22 @@ if (fs.existsSync(networkFile)) {
73
76
 
74
77
  let program = new Command();
75
78
 
79
+ function parseExtraArgs(variadicArgs?: string[]): {
80
+ extraArgs: string[];
81
+ args: string[];
82
+ } {
83
+ const rawArgs = process.argv.slice(2);
84
+ const dashDashIndex = rawArgs.indexOf("--");
85
+ const extraArgs =
86
+ dashDashIndex !== -1 ? rawArgs.slice(dashDashIndex + 1) : [];
87
+ const args = variadicArgs
88
+ ? extraArgs.length > 0
89
+ ? variadicArgs.slice(0, variadicArgs.length - extraArgs.length)
90
+ : variadicArgs
91
+ : [];
92
+ return { extraArgs, args };
93
+ }
94
+
76
95
  program.name("mops");
77
96
 
78
97
  // --version
@@ -152,7 +171,7 @@ program
152
171
  }
153
172
 
154
173
  if (options.toolchain) {
155
- await toolchain.ensureToolchainInited({ strict: false });
174
+ await toolchain.checkToolchainInited({ strict: false });
156
175
  }
157
176
 
158
177
  let ok = await installAll(options);
@@ -231,7 +250,7 @@ program
231
250
  installFromLockFile: true,
232
251
  });
233
252
  }
234
- await toolchain.ensureToolchainInited({ strict: false });
253
+ await toolchain.checkToolchainInited({ strict: false });
235
254
  let sourcesArr = await sources(options);
236
255
  console.log(sourcesArr.join("\n"));
237
256
  });
@@ -272,18 +291,45 @@ program
272
291
  ),
273
292
  )
274
293
  .allowUnknownOption(true) // TODO: restrict unknown before "--"
275
- .action(async (canisters, options, command) => {
294
+ .action(async (canisters, options) => {
295
+ checkConfigFile(true);
296
+ const { extraArgs, args } = parseExtraArgs(canisters);
297
+ await installAll({
298
+ silent: true,
299
+ lock: "ignore",
300
+ installFromLockFile: true,
301
+ });
302
+ await build(args.length ? args : undefined, {
303
+ ...options,
304
+ extraArgs,
305
+ });
306
+ });
307
+
308
+ // check
309
+ program
310
+ .command("check <files...>")
311
+ .description(
312
+ "Check Motoko entrypoint files for syntax errors and type issues (including transitively imported files)",
313
+ )
314
+ .option("--verbose", "Verbose console output")
315
+ .addOption(
316
+ new Option(
317
+ "--fix",
318
+ "Apply autofixes to all files, including transitively imported ones",
319
+ ),
320
+ )
321
+ .allowUnknownOption(true)
322
+ .action(async (files, options) => {
276
323
  checkConfigFile(true);
277
- const extraArgsIndex = command.args.indexOf("--");
324
+ const { extraArgs, args: fileList } = parseExtraArgs(files);
278
325
  await installAll({
279
326
  silent: true,
280
327
  lock: "ignore",
281
328
  installFromLockFile: true,
282
329
  });
283
- await build(canisters.length ? canisters : undefined, {
330
+ await check(fileList, {
284
331
  ...options,
285
- extraArgs:
286
- extraArgsIndex !== -1 ? command.args.slice(extraArgsIndex + 1) : [],
332
+ extraArgs,
287
333
  });
288
334
  });
289
335
 
@@ -575,7 +621,7 @@ toolchainCommand
575
621
  toolchainCommand
576
622
  .command("use")
577
623
  .description("Install specified tool version and update mops.toml")
578
- .addArgument(new Argument("<tool>").choices(["moc", "wasmtime", "pocket-ic"]))
624
+ .addArgument(new Argument("<tool>").choices(TOOLCHAINS))
579
625
  .addArgument(new Argument("[version]"))
580
626
  .action(async (tool, version) => {
581
627
  if (!checkConfigFile()) {
@@ -589,7 +635,7 @@ toolchainCommand
589
635
  .description(
590
636
  "Update specified tool or all tools to the latest version and update mops.toml",
591
637
  )
592
- .addArgument(new Argument("[tool]").choices(["moc", "wasmtime", "pocket-ic"]))
638
+ .addArgument(new Argument("[tool]").choices(TOOLCHAINS))
593
639
  .action(async (tool?: Tool) => {
594
640
  if (!checkConfigFile()) {
595
641
  process.exit(1);
@@ -600,9 +646,9 @@ toolchainCommand
600
646
  toolchainCommand
601
647
  .command("bin")
602
648
  .description(
603
- 'Get path to the tool binary\n<tool> can be one of "moc", "wasmtime", "pocket-ic"',
649
+ `Get path to the tool binary\n<tool> can be one of ${TOOLCHAINS.map((s) => `"${s}"`).join(", ")}`,
604
650
  )
605
- .addArgument(new Argument("<tool>").choices(["moc", "wasmtime", "pocket-ic"]))
651
+ .addArgument(new Argument("<tool>").choices(TOOLCHAINS))
606
652
  .addOption(
607
653
  new Option(
608
654
  "--fallback",
@@ -671,6 +717,28 @@ program
671
717
  }
672
718
  });
673
719
 
720
+ // lint
721
+ program
722
+ .command("lint [filter]")
723
+ .description("Lint Motoko code")
724
+ .addOption(new Option("--verbose", "Verbose output"))
725
+ .addOption(new Option("--fix", "Apply fixes"))
726
+ .addOption(
727
+ new Option(
728
+ "-r, --rules <directory...>",
729
+ "Directories containing rules (can be used multiple times)",
730
+ ),
731
+ )
732
+ .allowUnknownOption(true)
733
+ .action(async (filter, options) => {
734
+ checkConfigFile(true);
735
+ const { extraArgs } = parseExtraArgs();
736
+ await lint(filter, {
737
+ ...options,
738
+ extraArgs,
739
+ });
740
+ });
741
+
674
742
  // docs
675
743
  const docsCommand = new Command("docs").description("Documentation management");
676
744
 
@@ -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();
package/commands/bench.ts CHANGED
@@ -19,6 +19,7 @@ import { getMocVersion } from "../helpers/get-moc-version.js";
19
19
  import { getDfxVersion } from "../helpers/get-dfx-version.js";
20
20
  import { getMocPath } from "../helpers/get-moc-path.js";
21
21
  import { sources } from "./sources.js";
22
+ import { MOTOKO_GLOB_CONFIG } from "../constants.js";
22
23
 
23
24
  import { Benchmark, Benchmarks } from "../declarations/main/main.did.js";
24
25
  import { BenchResult, _SERVICE } from "../declarations/bench/bench.did.js";
@@ -26,18 +27,6 @@ import { BenchReplica } from "./bench-replica.js";
26
27
 
27
28
  type ReplicaName = "dfx" | "pocket-ic" | "dfx-pocket-ic";
28
29
 
29
- let ignore = [
30
- "**/node_modules/**",
31
- "**/.mops/**",
32
- "**/.vessel/**",
33
- "**/.git/**",
34
- ];
35
-
36
- let globConfig = {
37
- nocase: true,
38
- ignore: ignore,
39
- };
40
-
41
30
  type BenchOptions = {
42
31
  replica: ReplicaName;
43
32
  replicaVersion: string;
@@ -113,7 +102,7 @@ export async function bench(
113
102
  if (filter) {
114
103
  globStr = `**/bench?(mark)/**/*${filter}*.mo`;
115
104
  }
116
- let files = globSync(path.join(rootDir, globStr), globConfig);
105
+ let files = globSync(path.join(rootDir, globStr), MOTOKO_GLOB_CONFIG);
117
106
  if (!files.length) {
118
107
  if (filter) {
119
108
  options.silent ||
package/commands/build.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import chalk from "chalk";
2
2
  import { execa } from "execa";
3
3
  import { exists } from "fs-extra";
4
- import { mkdir } from "node:fs/promises";
4
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
5
5
  import { join } from "node:path";
6
- import { getMocPath } from "../helpers/get-moc-path.js";
6
+ import { cliError } from "../error.js";
7
+ import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
8
+ import { CustomSection, getWasmBindings } from "../wasm.js";
7
9
  import { readConfig } from "../mops.js";
8
10
  import { CanisterConfig } from "../types.js";
9
11
  import { sourcesArgs } from "./sources.js";
10
- import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
11
- import { cliError } from "../error.js";
12
+ import { toolchain } from "./toolchain/index.js";
12
13
 
13
14
  export interface BuildOptions {
14
15
  outputDir: string;
@@ -27,7 +28,7 @@ export async function build(
27
28
  }
28
29
 
29
30
  let outputDir = options.outputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
30
- let mocPath = getMocPath();
31
+ let mocPath = await toolchain.bin("moc", { fallback: true });
31
32
  let canisters: Record<string, CanisterConfig> = {};
32
33
  let config = readConfig();
33
34
  if (config.canisters) {
@@ -69,17 +70,17 @@ export async function build(
69
70
  : canisters;
70
71
 
71
72
  for (let [canisterName, canister] of Object.entries(filteredCanisters)) {
72
- options.verbose && console.time(`build canister ${canisterName}`);
73
73
  console.log(chalk.blue("build canister"), chalk.bold(canisterName));
74
74
  let motokoPath = canister.main;
75
75
  if (!motokoPath) {
76
76
  cliError(`No main file is specified for canister ${canisterName}`);
77
77
  }
78
+ const wasmPath = join(outputDir, `${canisterName}.wasm`);
78
79
  let args = [
79
80
  "-c",
80
81
  "--idl",
81
82
  "-o",
82
- join(outputDir, `${canisterName}.wasm`),
83
+ wasmPath,
83
84
  motokoPath,
84
85
  ...(options.extraArgs ?? []),
85
86
  ...(await sourcesArgs()).flat(),
@@ -100,6 +101,12 @@ export async function build(
100
101
  }
101
102
  args.push(...canister.args);
102
103
  }
104
+ const isPublicCandid = true; // always true for now to reduce corner cases
105
+ const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
106
+ if (isPublicCandid) {
107
+ args.push("--public-metadata", "candid:service");
108
+ args.push("--public-metadata", "candid:args");
109
+ }
103
110
  try {
104
111
  if (options.verbose) {
105
112
  console.log(chalk.gray(mocPath, JSON.stringify(args)));
@@ -128,8 +135,8 @@ export async function build(
128
135
  console.log(result.stdout);
129
136
  }
130
137
 
138
+ const generatedDidPath = join(outputDir, `${canisterName}.did`);
131
139
  if (canister.candid) {
132
- const generatedDidPath = join(outputDir, `${canisterName}.did`);
133
140
  const originalCandidPath = canister.candid;
134
141
 
135
142
  try {
@@ -146,27 +153,45 @@ export async function build(
146
153
 
147
154
  if (options.verbose) {
148
155
  console.log(
149
- chalk.green(
156
+ chalk.gray(
150
157
  `Candid compatibility check passed for canister ${canisterName}`,
151
158
  ),
152
159
  );
153
160
  }
154
- } catch (candidError: any) {
161
+ } catch (err: any) {
155
162
  cliError(
156
- `Error during Candid compatibility check for canister ${canisterName}`,
157
- candidError,
163
+ `Error during Candid compatibility check for canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
158
164
  );
159
165
  }
160
166
  }
161
- } catch (cliError: any) {
162
- if (cliError.message?.includes("Build failed for canister")) {
163
- throw cliError;
167
+
168
+ options.verbose &&
169
+ console.log(chalk.gray(`Adding metadata to ${wasmPath}`));
170
+ const candidPath = canister.candid ?? generatedDidPath;
171
+ const candidText = await readFile(candidPath, "utf-8");
172
+ const customSections: CustomSection[] = [
173
+ { name: `${candidVisibility} candid:service`, data: candidText },
174
+ ];
175
+ if (canister.initArg) {
176
+ customSections.push({
177
+ name: `${candidVisibility} candid:args`,
178
+ data: canister.initArg,
179
+ });
180
+ }
181
+ const wasmBytes = await readFile(wasmPath);
182
+ const newWasm = getWasmBindings().add_custom_sections(
183
+ wasmBytes,
184
+ customSections,
185
+ );
186
+ await writeFile(wasmPath, newWasm);
187
+ } catch (err: any) {
188
+ if (err.message?.includes("Build failed for canister")) {
189
+ throw err;
164
190
  }
165
191
  cliError(
166
- `Error while compiling canister ${canisterName}${cliError?.message ? `\n${cliError.message}` : ""}`,
192
+ `Error while compiling canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
167
193
  );
168
194
  }
169
- options.verbose && console.timeEnd(`build canister ${canisterName}`);
170
195
  }
171
196
 
172
197
  console.log(