ic-mops 2.0.1 → 2.2.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 (189) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/RELEASE.md +198 -0
  3. package/bundle/cli.tgz +0 -0
  4. package/check-requirements.ts +3 -8
  5. package/cli.ts +94 -11
  6. package/commands/bench/bench-canister.mo +17 -6
  7. package/commands/bench.ts +13 -16
  8. package/commands/build.ts +5 -6
  9. package/commands/check.ts +121 -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 +10 -20
  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/commands/watch/error-checker.ts +8 -2
  18. package/commands/watch/warning-checker.ts +8 -2
  19. package/constants.ts +23 -0
  20. package/dist/check-requirements.js +3 -8
  21. package/dist/cli.js +73 -11
  22. package/dist/commands/bench/bench-canister.mo +17 -6
  23. package/dist/commands/bench.js +7 -15
  24. package/dist/commands/build.js +5 -6
  25. package/dist/commands/check.d.ts +6 -0
  26. package/dist/commands/check.js +82 -0
  27. package/dist/commands/format.js +3 -16
  28. package/dist/commands/lint.d.ts +7 -0
  29. package/dist/commands/lint.js +69 -0
  30. package/dist/commands/sync.js +2 -7
  31. package/dist/commands/test/test.js +10 -18
  32. package/dist/commands/toolchain/index.d.ts +2 -2
  33. package/dist/commands/toolchain/index.js +18 -7
  34. package/dist/commands/toolchain/lintoko.d.ts +8 -0
  35. package/dist/commands/toolchain/lintoko.js +36 -0
  36. package/dist/commands/toolchain/toolchain-utils.d.ts +1 -0
  37. package/dist/commands/toolchain/toolchain-utils.js +1 -0
  38. package/dist/commands/watch/error-checker.js +8 -2
  39. package/dist/commands/watch/warning-checker.js +8 -2
  40. package/dist/constants.d.ts +15 -0
  41. package/dist/constants.js +21 -0
  42. package/dist/environments/nodejs/cli.js +6 -1
  43. package/dist/error.d.ts +1 -1
  44. package/dist/helpers/autofix-motoko.d.ts +26 -0
  45. package/dist/helpers/autofix-motoko.js +150 -0
  46. package/dist/helpers/get-moc-version.d.ts +2 -0
  47. package/dist/helpers/get-moc-version.js +10 -1
  48. package/dist/mops.d.ts +1 -0
  49. package/dist/mops.js +12 -1
  50. package/dist/package.json +3 -2
  51. package/dist/tests/build-no-dfx.test.d.ts +1 -0
  52. package/dist/tests/build-no-dfx.test.js +9 -0
  53. package/dist/tests/build.test.d.ts +1 -0
  54. package/dist/tests/build.test.js +18 -0
  55. package/dist/tests/check-candid.test.d.ts +1 -0
  56. package/dist/tests/check-candid.test.js +20 -0
  57. package/dist/tests/check-fix.test.d.ts +1 -0
  58. package/dist/tests/check-fix.test.js +89 -0
  59. package/dist/tests/check.test.d.ts +1 -0
  60. package/dist/tests/check.test.js +37 -0
  61. package/dist/tests/cli.test.js +4 -57
  62. package/dist/tests/helpers.d.ts +22 -0
  63. package/dist/tests/helpers.js +43 -0
  64. package/dist/tests/lint.test.d.ts +1 -0
  65. package/dist/tests/lint.test.js +15 -0
  66. package/dist/tests/moc-args.test.d.ts +1 -0
  67. package/dist/tests/moc-args.test.js +17 -0
  68. package/dist/tests/toolchain.test.d.ts +1 -0
  69. package/dist/tests/toolchain.test.js +11 -0
  70. package/dist/types.d.ts +8 -1
  71. package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  72. package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
  73. package/environments/nodejs/cli.ts +7 -1
  74. package/error.ts +1 -1
  75. package/helpers/autofix-motoko.ts +240 -0
  76. package/helpers/get-moc-version.ts +12 -1
  77. package/mops.ts +15 -1
  78. package/package.json +3 -2
  79. package/tests/__snapshots__/build-no-dfx.test.ts.snap +11 -0
  80. package/tests/__snapshots__/build.test.ts.snap +77 -0
  81. package/tests/__snapshots__/check-candid.test.ts.snap +73 -0
  82. package/tests/__snapshots__/check-fix.test.ts.snap +261 -0
  83. package/tests/__snapshots__/check.test.ts.snap +81 -0
  84. package/tests/__snapshots__/lint.test.ts.snap +78 -0
  85. package/tests/build/no-dfx/mops.toml +5 -0
  86. package/tests/build/no-dfx/src/Main.mo +5 -0
  87. package/tests/build-no-dfx.test.ts +10 -0
  88. package/tests/build.test.ts +24 -0
  89. package/tests/check/error/Error.mo +7 -0
  90. package/tests/check/error/mops.toml +2 -0
  91. package/tests/check/fix/M0223.mo +11 -0
  92. package/tests/check/fix/M0236.mo +11 -0
  93. package/tests/check/fix/M0237.mo +11 -0
  94. package/tests/check/fix/Ok.mo +7 -0
  95. package/tests/check/fix/edit-suggestions.mo +143 -0
  96. package/tests/check/fix/mops.toml +5 -0
  97. package/tests/check/fix/overlapping.mo +10 -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/moc-args/Warning.mo +5 -0
  101. package/tests/check/moc-args/mops.toml +2 -0
  102. package/tests/check/success/Ok.mo +5 -0
  103. package/tests/check/success/Warning.mo +5 -0
  104. package/tests/check/success/mops.toml +2 -0
  105. package/tests/check-candid.test.ts +22 -0
  106. package/tests/check-fix.test.ts +134 -0
  107. package/tests/check.test.ts +51 -0
  108. package/tests/cli.test.ts +4 -74
  109. package/tests/helpers.ts +58 -0
  110. package/tests/lint/lints/no-bool-switch.toml +9 -0
  111. package/tests/lint/mops.toml +4 -0
  112. package/tests/lint/src/NoBoolSwitch.mo +8 -0
  113. package/tests/lint/src/Ok.mo +5 -0
  114. package/tests/lint.test.ts +17 -0
  115. package/tests/moc-args.test.ts +19 -0
  116. package/tests/toolchain/mock +2 -0
  117. package/tests/toolchain/mops.toml +2 -0
  118. package/tests/toolchain.test.ts +12 -0
  119. package/types.ts +8 -1
  120. package/wasm/Cargo.lock +101 -54
  121. package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
  122. package/wasm/pkg/web/wasm_bg.wasm +0 -0
  123. package/.DS_Store +0 -0
  124. package/bundle/bench/bench-canister.mo +0 -121
  125. package/bundle/bench/user-bench.mo +0 -10
  126. package/bundle/bin/moc-wrapper.sh +0 -40
  127. package/bundle/bin/mops.js +0 -3
  128. package/bundle/cli.js +0 -2144
  129. package/bundle/declarations/bench/bench.did +0 -30
  130. package/bundle/declarations/bench/bench.did.d.ts +0 -33
  131. package/bundle/declarations/bench/bench.did.js +0 -30
  132. package/bundle/declarations/bench/index.d.ts +0 -50
  133. package/bundle/declarations/bench/index.js +0 -40
  134. package/bundle/declarations/main/index.d.ts +0 -50
  135. package/bundle/declarations/main/index.js +0 -40
  136. package/bundle/declarations/main/main.did +0 -428
  137. package/bundle/declarations/main/main.did.d.ts +0 -348
  138. package/bundle/declarations/main/main.did.js +0 -406
  139. package/bundle/declarations/storage/index.d.ts +0 -50
  140. package/bundle/declarations/storage/index.js +0 -30
  141. package/bundle/declarations/storage/storage.did +0 -46
  142. package/bundle/declarations/storage/storage.did.d.ts +0 -40
  143. package/bundle/declarations/storage/storage.did.js +0 -38
  144. package/bundle/package.json +0 -36
  145. package/bundle/templates/README.md +0 -13
  146. package/bundle/templates/licenses/Apache-2.0 +0 -202
  147. package/bundle/templates/licenses/Apache-2.0-NOTICE +0 -13
  148. package/bundle/templates/licenses/MIT +0 -21
  149. package/bundle/templates/mops-publish.yml +0 -17
  150. package/bundle/templates/mops-test.yml +0 -24
  151. package/bundle/templates/src/lib.mo +0 -15
  152. package/bundle/templates/test/lib.test.mo +0 -4
  153. package/bundle/wasm_bg.wasm +0 -0
  154. package/bundle/xhr-sync-worker.js +0 -59
  155. package/dist/wasm/pkg/bundler/package.json +0 -20
  156. package/dist/wasm/pkg/bundler/wasm.d.ts +0 -3
  157. package/dist/wasm/pkg/bundler/wasm.js +0 -5
  158. package/dist/wasm/pkg/bundler/wasm_bg.js +0 -93
  159. package/dist/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  160. package/dist/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
  161. package/tests/__snapshots__/cli.test.ts.snap +0 -202
  162. package/tests/build/success/.dfx/local/canister_ids.json +0 -17
  163. package/tests/build/success/.dfx/local/canisters/bar/bar.did +0 -3
  164. package/tests/build/success/.dfx/local/canisters/bar/bar.most +0 -4
  165. package/tests/build/success/.dfx/local/canisters/bar/bar.wasm +0 -0
  166. package/tests/build/success/.dfx/local/canisters/bar/constructor.did +0 -3
  167. package/tests/build/success/.dfx/local/canisters/bar/index.js +0 -42
  168. package/tests/build/success/.dfx/local/canisters/bar/init_args.txt +0 -1
  169. package/tests/build/success/.dfx/local/canisters/bar/service.did +0 -3
  170. package/tests/build/success/.dfx/local/canisters/bar/service.did.d.ts +0 -7
  171. package/tests/build/success/.dfx/local/canisters/bar/service.did.js +0 -4
  172. package/tests/build/success/.dfx/local/canisters/foo/constructor.did +0 -3
  173. package/tests/build/success/.dfx/local/canisters/foo/foo.did +0 -3
  174. package/tests/build/success/.dfx/local/canisters/foo/foo.most +0 -4
  175. package/tests/build/success/.dfx/local/canisters/foo/foo.wasm +0 -0
  176. package/tests/build/success/.dfx/local/canisters/foo/index.js +0 -42
  177. package/tests/build/success/.dfx/local/canisters/foo/init_args.txt +0 -1
  178. package/tests/build/success/.dfx/local/canisters/foo/service.did +0 -3
  179. package/tests/build/success/.dfx/local/canisters/foo/service.did.d.ts +0 -7
  180. package/tests/build/success/.dfx/local/canisters/foo/service.did.js +0 -4
  181. package/tests/build/success/.dfx/local/lsp/ucwa4-rx777-77774-qaada-cai.did +0 -3
  182. package/tests/build/success/.dfx/local/lsp/ulvla-h7777-77774-qaacq-cai.did +0 -3
  183. package/tests/build/success/.dfx/local/network-id +0 -4
  184. package/wasm/pkg/bundler/package.json +0 -20
  185. package/wasm/pkg/bundler/wasm.d.ts +0 -3
  186. package/wasm/pkg/bundler/wasm.js +0 -5
  187. package/wasm/pkg/bundler/wasm_bg.js +0 -93
  188. package/wasm/pkg/bundler/wasm_bg.wasm +0 -0
  189. package/wasm/pkg/bundler/wasm_bg.wasm.d.ts +0 -8
package/CHANGELOG.md CHANGED
@@ -1,6 +1,24 @@
1
1
  # Mops CLI Changelog
2
2
 
3
3
  ## Next
4
+
5
+ ## 2.2.0
6
+ - Add `[moc]` config section for global `moc` compiler flags (applied to `check`, `build`, `test`, `bench`, `watch`)
7
+ - Add `mops moc-args` command to print global `moc` flags from `[moc]` config section
8
+ - Fix `mops check --fix` crash on overlapping diagnostic edits (e.g., nested function calls)
9
+
10
+ ## 2.1.0
11
+ - Add `mops check --fix` subcommand (for Motoko files) with autofix logic
12
+ - Add `mops check` subcommand for type-checking Motoko files
13
+ - Warn for `dfx` projects instead of requiring `mops toolchain init`
14
+ - Allow specifying toolchain file paths in `mops.toml`
15
+ - Add `mops lint` subcommand and `lintoko` toolchain management
16
+ - Improve bench-canister Bench type to be less restrictive (by @timohanke)
17
+
18
+ ## 2.0.1
19
+ - Patch vulnerability in `tar` dependency
20
+
21
+ # 2.0.0
4
22
  - `mops publish` add support for subheadings in changelog (by @f0i)
5
23
  - `mops toolchain` now downloads `moc.js` in addition to `moc` binary
6
24
  - New `mops build` subcommand (alternative to `dfx build`)
package/RELEASE.md ADDED
@@ -0,0 +1,198 @@
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
+ ### 10. Commit and push release artifacts
134
+
135
+ Step 8 generates files in `cli-releases/` that must be committed and pushed:
136
+
137
+ ```bash
138
+ git add cli-releases/
139
+ git commit -m "cli-releases: v<version> artifacts"
140
+ ```
141
+
142
+ Since direct pushes to `main` are not allowed, create a branch and PR:
143
+
144
+ ```bash
145
+ git checkout -b <username>/release-X.Y.Z-artifacts
146
+ git push -u origin <username>/release-X.Y.Z-artifacts
147
+ gh pr create --title "cli-releases: vX.Y.Z artifacts" --body "Release artifacts generated by \`npm run release-cli\` for CLI vX.Y.Z."
148
+ ```
149
+
150
+ Merge this PR after approval.
151
+
152
+ ## Verify build
153
+
154
+ Anyone can verify a released version by rebuilding from source:
155
+
156
+ ```bash
157
+ cd cli
158
+ docker build . --build-arg COMMIT_HASH=<commit_hash> --build-arg MOPS_VERSION=<mops_version> -t mops
159
+ docker run --rm --env SHASUM=<build_hash> mops
160
+ ```
161
+
162
+ ## Adding the `mops` identity to dfx
163
+
164
+ Check if the identity already exists:
165
+ ```bash
166
+ dfx identity list
167
+ ```
168
+
169
+ If `mops` appears, verify it's the correct one:
170
+ ```bash
171
+ dfx identity get-principal --identity mops
172
+ ```
173
+
174
+ If it doesn't exist, import it from the PEM file stored in Bitwarden (`Mops identity (canisters and packages)`):
175
+
176
+ 1. Save the PEM key to a temporary file:
177
+ ```bash
178
+ cat > /tmp/mops-identity.pem << 'EOF'
179
+ -----BEGIN EC PRIVATE KEY-----
180
+ <paste key from Bitwarden>
181
+ -----END EC PRIVATE KEY-----
182
+ EOF
183
+ ```
184
+
185
+ 2. Import into dfx:
186
+ ```bash
187
+ dfx identity import mops /tmp/mops-identity.pem
188
+ ```
189
+
190
+ 3. Delete the temporary file:
191
+ ```bash
192
+ rm /tmp/mops-identity.pem
193
+ ```
194
+
195
+ 4. Verify:
196
+ ```bash
197
+ dfx identity get-principal --identity mops
198
+ ```
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,
@@ -44,12 +46,15 @@ import {
44
46
  apiVersion,
45
47
  checkApiCompatibility,
46
48
  checkConfigFile,
49
+ getGlobalMocArgs,
47
50
  getNetworkFile,
51
+ readConfig,
48
52
  setNetwork,
49
53
  version,
50
54
  } from "./mops.js";
51
55
  import { resolvePackages } from "./resolve-packages.js";
52
56
  import { Tool } from "./types.js";
57
+ import { TOOLCHAINS } from "./commands/toolchain/toolchain-utils.js";
53
58
 
54
59
  declare global {
55
60
  // eslint-disable-next-line no-var
@@ -73,6 +78,22 @@ if (fs.existsSync(networkFile)) {
73
78
 
74
79
  let program = new Command();
75
80
 
81
+ function parseExtraArgs(variadicArgs?: string[]): {
82
+ extraArgs: string[];
83
+ args: string[];
84
+ } {
85
+ const rawArgs = process.argv.slice(2);
86
+ const dashDashIndex = rawArgs.indexOf("--");
87
+ const extraArgs =
88
+ dashDashIndex !== -1 ? rawArgs.slice(dashDashIndex + 1) : [];
89
+ const args = variadicArgs
90
+ ? extraArgs.length > 0
91
+ ? variadicArgs.slice(0, variadicArgs.length - extraArgs.length)
92
+ : variadicArgs
93
+ : [];
94
+ return { extraArgs, args };
95
+ }
96
+
76
97
  program.name("mops");
77
98
 
78
99
  // --version
@@ -152,7 +173,7 @@ program
152
173
  }
153
174
 
154
175
  if (options.toolchain) {
155
- await toolchain.ensureToolchainInited({ strict: false });
176
+ await toolchain.checkToolchainInited({ strict: false });
156
177
  }
157
178
 
158
179
  let ok = await installAll(options);
@@ -231,11 +252,24 @@ program
231
252
  installFromLockFile: true,
232
253
  });
233
254
  }
234
- await toolchain.ensureToolchainInited({ strict: false });
255
+ await toolchain.checkToolchainInited({ strict: false });
235
256
  let sourcesArr = await sources(options);
236
257
  console.log(sourcesArr.join("\n"));
237
258
  });
238
259
 
260
+ // moc-args
261
+ program
262
+ .command("moc-args")
263
+ .description("Print global moc compiler flags from [moc] config section")
264
+ .action(async () => {
265
+ checkConfigFile(true);
266
+ let config = readConfig();
267
+ let args = getGlobalMocArgs(config);
268
+ if (args.length) {
269
+ console.log(args.join("\n"));
270
+ }
271
+ });
272
+
239
273
  // search
240
274
  program
241
275
  .command("search <text>")
@@ -272,18 +306,45 @@ program
272
306
  ),
273
307
  )
274
308
  .allowUnknownOption(true) // TODO: restrict unknown before "--"
275
- .action(async (canisters, options, command) => {
309
+ .action(async (canisters, options) => {
310
+ checkConfigFile(true);
311
+ const { extraArgs, args } = parseExtraArgs(canisters);
312
+ await installAll({
313
+ silent: true,
314
+ lock: "ignore",
315
+ installFromLockFile: true,
316
+ });
317
+ await build(args.length ? args : undefined, {
318
+ ...options,
319
+ extraArgs,
320
+ });
321
+ });
322
+
323
+ // check
324
+ program
325
+ .command("check <files...>")
326
+ .description(
327
+ "Check Motoko entrypoint files for syntax errors and type issues (including transitively imported files)",
328
+ )
329
+ .option("--verbose", "Verbose console output")
330
+ .addOption(
331
+ new Option(
332
+ "--fix",
333
+ "Apply autofixes to all files, including transitively imported ones",
334
+ ),
335
+ )
336
+ .allowUnknownOption(true)
337
+ .action(async (files, options) => {
276
338
  checkConfigFile(true);
277
- const extraArgsIndex = command.args.indexOf("--");
339
+ const { extraArgs, args: fileList } = parseExtraArgs(files);
278
340
  await installAll({
279
341
  silent: true,
280
342
  lock: "ignore",
281
343
  installFromLockFile: true,
282
344
  });
283
- await build(canisters.length ? canisters : undefined, {
345
+ await check(fileList, {
284
346
  ...options,
285
- extraArgs:
286
- extraArgsIndex !== -1 ? command.args.slice(extraArgsIndex + 1) : [],
347
+ extraArgs,
287
348
  });
288
349
  });
289
350
 
@@ -575,7 +636,7 @@ toolchainCommand
575
636
  toolchainCommand
576
637
  .command("use")
577
638
  .description("Install specified tool version and update mops.toml")
578
- .addArgument(new Argument("<tool>").choices(["moc", "wasmtime", "pocket-ic"]))
639
+ .addArgument(new Argument("<tool>").choices(TOOLCHAINS))
579
640
  .addArgument(new Argument("[version]"))
580
641
  .action(async (tool, version) => {
581
642
  if (!checkConfigFile()) {
@@ -589,7 +650,7 @@ toolchainCommand
589
650
  .description(
590
651
  "Update specified tool or all tools to the latest version and update mops.toml",
591
652
  )
592
- .addArgument(new Argument("[tool]").choices(["moc", "wasmtime", "pocket-ic"]))
653
+ .addArgument(new Argument("[tool]").choices(TOOLCHAINS))
593
654
  .action(async (tool?: Tool) => {
594
655
  if (!checkConfigFile()) {
595
656
  process.exit(1);
@@ -600,9 +661,9 @@ toolchainCommand
600
661
  toolchainCommand
601
662
  .command("bin")
602
663
  .description(
603
- 'Get path to the tool binary\n<tool> can be one of "moc", "wasmtime", "pocket-ic"',
664
+ `Get path to the tool binary\n<tool> can be one of ${TOOLCHAINS.map((s) => `"${s}"`).join(", ")}`,
604
665
  )
605
- .addArgument(new Argument("<tool>").choices(["moc", "wasmtime", "pocket-ic"]))
666
+ .addArgument(new Argument("<tool>").choices(TOOLCHAINS))
606
667
  .addOption(
607
668
  new Option(
608
669
  "--fallback",
@@ -671,6 +732,28 @@ program
671
732
  }
672
733
  });
673
734
 
735
+ // lint
736
+ program
737
+ .command("lint [filter]")
738
+ .description("Lint Motoko code")
739
+ .addOption(new Option("--verbose", "Verbose output"))
740
+ .addOption(new Option("--fix", "Apply fixes"))
741
+ .addOption(
742
+ new Option(
743
+ "-r, --rules <directory...>",
744
+ "Directories containing rules (can be used multiple times)",
745
+ ),
746
+ )
747
+ .allowUnknownOption(true)
748
+ .action(async (filter, options) => {
749
+ checkConfigFile(true);
750
+ const { extraArgs } = parseExtraArgs();
751
+ await lint(filter, {
752
+ ...options,
753
+ extraArgs,
754
+ });
755
+ });
756
+
674
757
  // docs
675
758
  const docsCommand = new Command("docs").description("Documentation management");
676
759
 
@@ -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
@@ -12,13 +12,19 @@ import { filesize } from "filesize";
12
12
  import terminalSize from "terminal-size";
13
13
  import { SemVer } from "semver";
14
14
 
15
- import { getRootDir, readConfig, readDfxJson } from "../mops.js";
15
+ import {
16
+ getGlobalMocArgs,
17
+ getRootDir,
18
+ readConfig,
19
+ readDfxJson,
20
+ } from "../mops.js";
16
21
  import { parallel } from "../parallel.js";
17
22
  import { absToRel } from "./test/utils.js";
18
23
  import { getMocVersion } from "../helpers/get-moc-version.js";
19
24
  import { getDfxVersion } from "../helpers/get-dfx-version.js";
20
25
  import { getMocPath } from "../helpers/get-moc-path.js";
21
26
  import { sources } from "./sources.js";
27
+ import { MOTOKO_GLOB_CONFIG } from "../constants.js";
22
28
 
23
29
  import { Benchmark, Benchmarks } from "../declarations/main/main.did.js";
24
30
  import { BenchResult, _SERVICE } from "../declarations/bench/bench.did.js";
@@ -26,18 +32,6 @@ import { BenchReplica } from "./bench-replica.js";
26
32
 
27
33
  type ReplicaName = "dfx" | "pocket-ic" | "dfx-pocket-ic";
28
34
 
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
35
  type BenchOptions = {
42
36
  replica: ReplicaName;
43
37
  replicaVersion: string;
@@ -113,7 +107,7 @@ export async function bench(
113
107
  if (filter) {
114
108
  globStr = `**/bench?(mark)/**/*${filter}*.mo`;
115
109
  }
116
- let files = globSync(path.join(rootDir, globStr), globConfig);
110
+ let files = globSync(path.join(rootDir, globStr), MOTOKO_GLOB_CONFIG);
117
111
  if (!files.length) {
118
112
  if (filter) {
119
113
  options.silent ||
@@ -149,13 +143,15 @@ export async function bench(
149
143
 
150
144
  await replica.start({ silent: options.silent });
151
145
 
146
+ let globalMocArgs = getGlobalMocArgs(config);
147
+
152
148
  if (!process.env.CI && !options.silent) {
153
149
  console.log("Deploying canisters...");
154
150
  }
155
151
 
156
152
  await parallel(os.cpus().length, files, async (file: string) => {
157
153
  try {
158
- await deployBenchFile(file, options, replica);
154
+ await deployBenchFile(file, options, replica, globalMocArgs);
159
155
  } catch (err) {
160
156
  console.error("Unexpected error. Stopping replica...");
161
157
  await replica.stop();
@@ -278,6 +274,7 @@ async function deployBenchFile(
278
274
  file: string,
279
275
  options: BenchOptions,
280
276
  replica: BenchReplica,
277
+ globalMocArgs: string[],
281
278
  ): Promise<void> {
282
279
  let rootDir = getRootDir();
283
280
  let tempDir = path.join(rootDir, ".mops/.bench/", path.parse(file).name);
@@ -305,7 +302,7 @@ async function deployBenchFile(
305
302
  let mocArgs = getMocArgs(options);
306
303
  options.verbose && console.time(`build ${canisterName}`);
307
304
  await execaCommand(
308
- `${mocPath} -c --idl canister.mo ${mocArgs} ${(await sources({ cwd: tempDir })).join(" ")}`,
305
+ `${mocPath} -c --idl canister.mo ${globalMocArgs.join(" ")} ${mocArgs} ${(await sources({ cwd: tempDir })).join(" ")}`,
309
306
  {
310
307
  cwd: tempDir,
311
308
  stdio: options.verbose ? "pipe" : ["pipe", "ignore", "pipe"],
package/commands/build.ts CHANGED
@@ -4,12 +4,12 @@ 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 { CustomSection, getWasmBindings } from "../wasm.js";
10
- import { readConfig } from "../mops.js";
9
+ import { getGlobalMocArgs, readConfig } from "../mops.js";
11
10
  import { CanisterConfig } from "../types.js";
12
11
  import { sourcesArgs } from "./sources.js";
12
+ import { toolchain } from "./toolchain/index.js";
13
13
 
14
14
  export interface BuildOptions {
15
15
  outputDir: string;
@@ -28,7 +28,7 @@ export async function build(
28
28
  }
29
29
 
30
30
  let outputDir = options.outputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
31
- let mocPath = getMocPath();
31
+ let mocPath = await toolchain.bin("moc", { fallback: true });
32
32
  let canisters: Record<string, CanisterConfig> = {};
33
33
  let config = readConfig();
34
34
  if (config.canisters) {
@@ -70,7 +70,6 @@ export async function build(
70
70
  : canisters;
71
71
 
72
72
  for (let [canisterName, canister] of Object.entries(filteredCanisters)) {
73
- options.verbose && console.time(`build canister ${canisterName}`);
74
73
  console.log(chalk.blue("build canister"), chalk.bold(canisterName));
75
74
  let motokoPath = canister.main;
76
75
  if (!motokoPath) {
@@ -83,8 +82,8 @@ export async function build(
83
82
  "-o",
84
83
  wasmPath,
85
84
  motokoPath,
86
- ...(options.extraArgs ?? []),
87
85
  ...(await sourcesArgs()).flat(),
86
+ ...getGlobalMocArgs(config),
88
87
  ];
89
88
  if (config.build?.args) {
90
89
  if (typeof config.build.args === "string") {
@@ -102,6 +101,7 @@ export async function build(
102
101
  }
103
102
  args.push(...canister.args);
104
103
  }
104
+ args.push(...(options.extraArgs ?? []));
105
105
  const isPublicCandid = true; // always true for now to reduce corner cases
106
106
  const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
107
107
  if (isPublicCandid) {
@@ -193,7 +193,6 @@ export async function build(
193
193
  `Error while compiling canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
194
194
  );
195
195
  }
196
- options.verbose && console.timeEnd(`build canister ${canisterName}`);
197
196
  }
198
197
 
199
198
  console.log(