ic-mops 2.14.1 → 2.15.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.
- package/CHANGELOG.md +22 -0
- package/bundle/cli.tgz +0 -0
- package/cli.ts +149 -4
- package/commands/bench-replica.ts +2 -1
- package/commands/bench.ts +32 -9
- package/commands/build.ts +30 -77
- package/commands/check-stable.ts +4 -0
- package/commands/check.ts +26 -22
- package/commands/deployed.ts +162 -0
- package/commands/generate.ts +201 -0
- package/commands/lint.ts +6 -3
- package/dist/cli.js +76 -4
- package/dist/commands/bench-replica.js +3 -1
- package/dist/commands/bench.js +21 -4
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.js +26 -52
- package/dist/commands/check-stable.d.ts +2 -0
- package/dist/commands/check-stable.js +2 -2
- package/dist/commands/check.d.ts +2 -0
- package/dist/commands/check.js +20 -21
- package/dist/commands/deployed.d.ts +10 -0
- package/dist/commands/deployed.js +97 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.js +124 -0
- package/dist/commands/lint.d.ts +2 -0
- package/dist/commands/lint.js +1 -1
- package/dist/helpers/autofix-motoko.d.ts +2 -0
- package/dist/helpers/autofix-motoko.js +71 -51
- package/dist/helpers/migrations.d.ts +1 -1
- package/dist/helpers/migrations.js +8 -4
- package/dist/helpers/moc-args.d.ts +25 -0
- package/dist/helpers/moc-args.js +58 -0
- package/dist/helpers/pocket-ic-client.d.ts +2 -2
- package/dist/helpers/pocket-ic-client.js +8 -4
- package/dist/package.json +6 -6
- package/dist/tests/check-fix-utf8.test.d.ts +1 -0
- package/dist/tests/check-fix-utf8.test.js +38 -0
- package/dist/tests/check-fix.test.js +14 -2
- package/dist/tests/check.test.js +21 -6
- package/dist/tests/deployed.test.d.ts +1 -0
- package/dist/tests/deployed.test.js +173 -0
- package/dist/tests/generate.test.d.ts +1 -0
- package/dist/tests/generate.test.js +107 -0
- package/dist/tests/helpers.d.ts +8 -0
- package/dist/tests/helpers.js +28 -3
- package/dist/tests/migrate.test.js +5 -19
- package/dist/types.d.ts +3 -0
- package/helpers/autofix-motoko.ts +96 -66
- package/helpers/migrations.ts +8 -3
- package/helpers/moc-args.ts +123 -0
- package/helpers/pocket-ic-client.ts +11 -5
- package/package.json +6 -6
- package/tests/__snapshots__/check-fix-utf8.test.ts.snap +27 -0
- package/tests/__snapshots__/check.test.ts.snap +4 -17
- package/tests/__snapshots__/deployed.test.ts.snap +10 -0
- package/tests/__snapshots__/generate.test.ts.snap +39 -0
- package/tests/check/fix-utf8/mops.toml +5 -0
- package/tests/check/fix-utf8/multibyte.mo +15 -0
- package/tests/check-fix-utf8.test.ts +51 -0
- package/tests/check-fix.test.ts +26 -2
- package/tests/check.test.ts +24 -11
- package/tests/deployed/basic/main.mo +4 -0
- package/tests/deployed/basic/mops.toml +8 -0
- package/tests/deployed/multi/bar.mo +1 -0
- package/tests/deployed/multi/foo.mo +1 -0
- package/tests/deployed/multi/mops.toml +11 -0
- package/tests/deployed.test.ts +249 -0
- package/tests/generate/basic/candid/bar.did +4 -0
- package/tests/generate/basic/mops.toml +12 -0
- package/tests/generate/basic/src/Bar.mo +5 -0
- package/tests/generate/basic/src/Foo.mo +5 -0
- package/tests/generate/error/mops.toml +8 -0
- package/tests/generate/error/src/Broken.mo +5 -0
- package/tests/generate.test.ts +140 -0
- package/tests/helpers.ts +31 -3
- package/tests/migrate.test.ts +7 -22
- package/types.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Next
|
|
4
4
|
|
|
5
|
+
## 2.15.1
|
|
6
|
+
|
|
7
|
+
- `mops check --fix` no longer aborts when a fixable file is read-only (e.g. a frozen migration chain file deliberately `chmod`'d to remove write access). The autofixer now skips such files with a warning and continues fixing the rest, instead of crashing the whole run on `EACCES`/`EPERM`.
|
|
8
|
+
|
|
9
|
+
- Load the PocketIC client lazily, only when a command actually starts a replica. Commands like `mops check`, `mops build`, and `mops install` no longer pay to load it (and its `@icp-sdk/core` dependency) at startup. This also unblocks running the CLI via `tsx` in local dev, where `pic-js-mops` (shipped as ESM without `"type": "module"`) fails to resolve as a static import.
|
|
10
|
+
|
|
11
|
+
- `mops bench --verbose` is now actually verbose. It prints the benchmark pipeline up front — compiler version, replica + version, GC, profile, and whether the wasm is optimized (`dfx` post-optimizes with `optimize: "cycles"` via ic-wasm on deploy; `pocket-ic` runs the raw `moc` output) — logs the full `moc` build command, and streams the compiler and `dfx` output instead of capturing and discarding it. Notably this surfaces dfx's `WARNING: Failed to optimize the Wasm module`, which dfx prints (and then silently deploys the unoptimized module) when `optimize: "cycles"` fails — e.g. on multi-value modules that the bundled ic-wasm can't process. Previously all of this was hidden even with `--verbose`.
|
|
12
|
+
|
|
13
|
+
## 2.15.0
|
|
14
|
+
|
|
15
|
+
- Fix `mops check --fix` corrupting source on lines containing multi-byte UTF-8 characters (e.g. `Char.toNat32('京')` dropping its trailing `)`). The autofixer was feeding moc's UTF-8 byte columns into LSP's UTF-16 position API, mis-applying every edit past the first non-ASCII byte on the line. When moc emits `byte_start`/`byte_end` (1.10.0 and newer) the fixer now applies edits byte-accurately; older moc still falls back to the line+column path (unchanged behavior, still ASCII-only).
|
|
16
|
+
|
|
17
|
+
- Revert "Speed up `mops check <files...>`" (2.14.1). Passing all files to a single `moc --check` invocation accumulates scope across them: checking `A.mo B.mo` makes `A.mo`'s definitions visible while type-checking `B.mo`, so a file that only compiles because a sibling brings something into scope is wrongly reported as passing. `mops check` again checks each file in its own `moc` invocation so every file is validated in isolation.
|
|
18
|
+
|
|
19
|
+
- Add `--no-check-limit` to `mops check`, `mops check-stable`, and `mops lint` to process the full migration chain for a single run, ignoring the configured `[canisters.<name>.migrations].check-limit`. Handy for `mops check --fix --no-check-limit` to autofix issues in older migrations that the limit normally skips
|
|
20
|
+
|
|
21
|
+
- `--help` now lists every option and the `-- <tool flags>` passthrough for each command: `mops build`, `mops check`, `mops check-stable`, and `mops generate candid` document `-- <moc flags>` (e.g. `mops check -- -Werror`), `mops lint` documents `-- <lintoko flags>`, and the `--verbose` flag of `mops add`/`mops install`/`mops publish` now has a description instead of showing blank
|
|
22
|
+
|
|
23
|
+
- Add `mops deployed` (post-deploy hook) and `mops deployed init` (one-time bootstrap). After a successful deploy, `mops deployed [canisters...]` promotes the built `<build-dir>/<name>.most` into `<deployed-dir>/<name>.most` so `mops check-stable` always compares against the just-deployed version. `mops deployed init` creates an empty-actor `.most` baseline and wires `[canisters.<name>.check-stable].path` to it. Configurable via `[deployed].dir` (default `deployed`) and overridable with `--dir`; the build output dir it reads from defaults to `[build].outputDir` (`.mops/.build`) and is overridable with `--build-dir`.
|
|
24
|
+
|
|
25
|
+
- Add `mops generate candid [canisters...]` to (re)generate the curated `.did` file from current Motoko source. With `[canisters.<name>].candid` set, overwrites that file in place; otherwise writes `<name>.did` next to `main` and sets the field in `mops.toml`. `--output, -o <path>` writes to an arbitrary path (single-canister only) without modifying `mops.toml`. `moc` is invoked with the same packages, `[moc].args`, `[build].args`, per-canister `args`, and migration flags as `mops build`, so the generated interface always satisfies `mops build`'s subtype check.
|
|
26
|
+
|
|
5
27
|
## 2.14.1
|
|
6
28
|
- Speed up `mops check <files...>` (e.g. `mops check src/**/*.mo`) on packages with many files. Previously each file was checked in its own `moc` invocation, so every shared transitive import was re-parsed and re-type-checked once per file. All files are now passed to a single `moc --check` call, which loads and type-checks each import only once — on motoko-core (53 files) this drops a full check from ~27s to ~1.6s. The per-file `✓` confirmations now print only when the whole check passes.
|
|
7
29
|
|
package/bundle/cli.tgz
CHANGED
|
Binary file
|
package/cli.ts
CHANGED
|
@@ -13,9 +13,11 @@ import { bump } from "./commands/bump.js";
|
|
|
13
13
|
import { check } from "./commands/check.js";
|
|
14
14
|
import { checkCandid } from "./commands/check-candid.js";
|
|
15
15
|
import { checkStable } from "./commands/check-stable.js";
|
|
16
|
+
import { deployed, deployedInit } from "./commands/deployed.js";
|
|
16
17
|
import { docsCoverage } from "./commands/docs-coverage.js";
|
|
17
18
|
import { docs } from "./commands/docs.js";
|
|
18
19
|
import { format } from "./commands/format.js";
|
|
20
|
+
import { generateCandid } from "./commands/generate.js";
|
|
19
21
|
import { info } from "./commands/info.js";
|
|
20
22
|
import { init } from "./commands/init.js";
|
|
21
23
|
import { lint } from "./commands/lint.js";
|
|
@@ -97,6 +99,19 @@ function parseExtraArgs(variadicArgs?: string[]): {
|
|
|
97
99
|
return { extraArgs, args };
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
// Shared `--help` section describing the enhanced migration `check-limit`
|
|
103
|
+
// trimming and its override flag, so the limit behaviour is discoverable
|
|
104
|
+
// from `--help`. `withFix` appends the `--fix` hint for commands that support it.
|
|
105
|
+
function enhancedMigrationHelp(withFix: boolean): string {
|
|
106
|
+
const example = withFix ? " (e.g. to --fix older migrations)" : "";
|
|
107
|
+
return (
|
|
108
|
+
"\nEnhanced migration ([canisters.<name>.migrations]):\n" +
|
|
109
|
+
" The canister is checked against its migration chain. [migrations].check-limit\n" +
|
|
110
|
+
" trims it to the last N migrations (older ones are skipped). Pass --no-check-limit\n" +
|
|
111
|
+
` to use the full chain${example}.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
100
115
|
program.name("mops");
|
|
101
116
|
|
|
102
117
|
// --version
|
|
@@ -116,7 +131,7 @@ program
|
|
|
116
131
|
.command("add <pkg>")
|
|
117
132
|
.description("Install the package and save it to mops.toml")
|
|
118
133
|
.option("--dev", "Add to [dev-dependencies] section")
|
|
119
|
-
.option("--verbose")
|
|
134
|
+
.option("--verbose", "Show more information")
|
|
120
135
|
.addOption(
|
|
121
136
|
new Option("--lock <action>", "Lockfile action").choices([
|
|
122
137
|
"update",
|
|
@@ -157,7 +172,7 @@ program
|
|
|
157
172
|
.alias("i")
|
|
158
173
|
.description("Install all dependencies specified in mops.toml")
|
|
159
174
|
.option("--no-toolchain", "Do not install toolchain")
|
|
160
|
-
.option("--verbose")
|
|
175
|
+
.option("--verbose", "Show more information")
|
|
161
176
|
.addOption(
|
|
162
177
|
new Option("--lock <action>", "Lockfile action").choices([
|
|
163
178
|
"check",
|
|
@@ -200,7 +215,7 @@ program
|
|
|
200
215
|
.option("--no-docs", "Do not generate docs")
|
|
201
216
|
.option("--no-test", "Do not run tests")
|
|
202
217
|
.option("--no-bench", "Do not run benchmarks")
|
|
203
|
-
.option("--verbose")
|
|
218
|
+
.option("--verbose", "Show more information")
|
|
204
219
|
.action(async (options) => {
|
|
205
220
|
if (!checkConfigFile()) {
|
|
206
221
|
process.exit(1);
|
|
@@ -313,6 +328,18 @@ program
|
|
|
313
328
|
.description("Build a canister")
|
|
314
329
|
.addOption(new Option("--verbose", "Verbose console output"))
|
|
315
330
|
.addOption(new Option("--output, -o <output>", "Output directory"))
|
|
331
|
+
.addHelpText(
|
|
332
|
+
"after",
|
|
333
|
+
"\nArguments after -- are forwarded directly to moc, e.g.:\n $ mops build -- -Werror",
|
|
334
|
+
)
|
|
335
|
+
.addHelpText(
|
|
336
|
+
"after",
|
|
337
|
+
"\nEnhanced migration ([canisters.<name>.migrations]):\n" +
|
|
338
|
+
" The canister is built against its full migration chain (every migration is\n" +
|
|
339
|
+
" compiled into the wasm). If mops check passes but mops build fails while\n" +
|
|
340
|
+
" [migrations].check-limit is set, re-run with mops check --no-check-limit to\n" +
|
|
341
|
+
" surface the issue (check trims the chain; build compiles all of it).",
|
|
342
|
+
)
|
|
316
343
|
.allowUnknownOption(true) // TODO: restrict unknown before "--"
|
|
317
344
|
.action(async (canisters, options) => {
|
|
318
345
|
checkConfigFile(true);
|
|
@@ -342,6 +369,17 @@ program
|
|
|
342
369
|
"Apply autofixes to all files, including transitively imported ones",
|
|
343
370
|
),
|
|
344
371
|
)
|
|
372
|
+
.addOption(
|
|
373
|
+
new Option(
|
|
374
|
+
"--no-check-limit",
|
|
375
|
+
"Check the full migration chain, ignoring [migrations].check-limit",
|
|
376
|
+
),
|
|
377
|
+
)
|
|
378
|
+
.addHelpText(
|
|
379
|
+
"after",
|
|
380
|
+
"\nArguments after -- are forwarded directly to moc, e.g.:\n $ mops check -- -Werror",
|
|
381
|
+
)
|
|
382
|
+
.addHelpText("after", enhancedMigrationHelp(true))
|
|
345
383
|
.allowUnknownOption(true)
|
|
346
384
|
.action(async (args, options) => {
|
|
347
385
|
checkConfigFile(true);
|
|
@@ -378,6 +416,17 @@ program
|
|
|
378
416
|
"Check stable variable compatibility. With no arguments, checks all canisters with [check-stable] configured. Arguments can be canister names or an old file path followed by an optional canister name",
|
|
379
417
|
)
|
|
380
418
|
.option("--verbose", "Verbose console output")
|
|
419
|
+
.addOption(
|
|
420
|
+
new Option(
|
|
421
|
+
"--no-check-limit",
|
|
422
|
+
"Check the full migration chain, ignoring [migrations].check-limit",
|
|
423
|
+
),
|
|
424
|
+
)
|
|
425
|
+
.addHelpText(
|
|
426
|
+
"after",
|
|
427
|
+
"\nArguments after -- are forwarded directly to moc, e.g.:\n $ mops check-stable -- -Werror",
|
|
428
|
+
)
|
|
429
|
+
.addHelpText("after", enhancedMigrationHelp(false))
|
|
381
430
|
.allowUnknownOption(true)
|
|
382
431
|
.action(async (args, options) => {
|
|
383
432
|
checkConfigFile(true);
|
|
@@ -393,6 +442,47 @@ program
|
|
|
393
442
|
});
|
|
394
443
|
});
|
|
395
444
|
|
|
445
|
+
// deployed
|
|
446
|
+
const deployedCommand = new Command("deployed")
|
|
447
|
+
.description(
|
|
448
|
+
"Post-deploy hook: promote .most stable-types files into the deployed directory so `mops check-stable` compares against the just-deployed version. Pass canister names to scope; with no arguments, all canisters in mops.toml are promoted",
|
|
449
|
+
)
|
|
450
|
+
.argument("[canisters...]")
|
|
451
|
+
.addOption(
|
|
452
|
+
new Option(
|
|
453
|
+
"--build-dir <dir>",
|
|
454
|
+
"Directory to read built .most files from (default: [build].outputDir or .mops/.build)",
|
|
455
|
+
),
|
|
456
|
+
)
|
|
457
|
+
.addOption(
|
|
458
|
+
new Option(
|
|
459
|
+
"--dir <dir>",
|
|
460
|
+
"Destination directory (default: [deployed].dir or deployed)",
|
|
461
|
+
),
|
|
462
|
+
)
|
|
463
|
+
.action(async (canisters: string[], options) => {
|
|
464
|
+
checkConfigFile(true);
|
|
465
|
+
await deployed(canisters.length ? canisters : undefined, options);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
deployedCommand
|
|
469
|
+
.command("init [canisters...]")
|
|
470
|
+
.description(
|
|
471
|
+
"Pre-first-deploy bootstrap: create an empty-actor .most baseline in the deployed directory and wire [canisters.<name>.check-stable].path to it. Idempotent",
|
|
472
|
+
)
|
|
473
|
+
.addOption(
|
|
474
|
+
new Option(
|
|
475
|
+
"--dir <dir>",
|
|
476
|
+
"Destination directory (default: [deployed].dir or deployed)",
|
|
477
|
+
),
|
|
478
|
+
)
|
|
479
|
+
.action(async (canisters: string[], options) => {
|
|
480
|
+
checkConfigFile(true);
|
|
481
|
+
await deployedInit(canisters.length ? canisters : undefined, options);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
program.addCommand(deployedCommand);
|
|
485
|
+
|
|
396
486
|
// test
|
|
397
487
|
program
|
|
398
488
|
.command("test [filter]")
|
|
@@ -453,7 +543,12 @@ program
|
|
|
453
543
|
),
|
|
454
544
|
)
|
|
455
545
|
// .addOption(new Option('--force-gc', 'Force GC'))
|
|
456
|
-
.addOption(
|
|
546
|
+
.addOption(
|
|
547
|
+
new Option(
|
|
548
|
+
"--verbose",
|
|
549
|
+
"Print the benchmark pipeline (compiler, replica, GC, optimization) and stream compiler/replica output, including dfx optimization warnings",
|
|
550
|
+
),
|
|
551
|
+
)
|
|
457
552
|
.action(async (filter, options) => {
|
|
458
553
|
checkConfigFile(true);
|
|
459
554
|
await installAll({
|
|
@@ -769,6 +864,44 @@ migrateCommand
|
|
|
769
864
|
|
|
770
865
|
program.addCommand(migrateCommand);
|
|
771
866
|
|
|
867
|
+
// generate
|
|
868
|
+
const generateCommand = new Command("generate")
|
|
869
|
+
.description("Generate source-derived artifacts (Candid, ...)")
|
|
870
|
+
.showHelpAfterError();
|
|
871
|
+
|
|
872
|
+
generateCommand
|
|
873
|
+
.command("candid [canisters...]")
|
|
874
|
+
.description(
|
|
875
|
+
"(Re)generate the curated `.did` file for one or more canisters from current Motoko source. With no canister names, generates for all canisters in mops.toml. When [canisters.<name>].candid is set, overwrites that file; otherwise writes <name>.did next to `main` and sets the field.",
|
|
876
|
+
)
|
|
877
|
+
.addOption(
|
|
878
|
+
new Option(
|
|
879
|
+
"--output, -o <output>",
|
|
880
|
+
"Write the generated .did to <output> (single-canister only; does not touch mops.toml)",
|
|
881
|
+
),
|
|
882
|
+
)
|
|
883
|
+
.addOption(new Option("--verbose", "Verbose console output"))
|
|
884
|
+
.addHelpText(
|
|
885
|
+
"after",
|
|
886
|
+
"\nArguments after -- are forwarded directly to moc, e.g.:\n $ mops generate candid -- -Werror",
|
|
887
|
+
)
|
|
888
|
+
.allowUnknownOption(true)
|
|
889
|
+
.action(async (canisters, options) => {
|
|
890
|
+
checkConfigFile(true);
|
|
891
|
+
const { extraArgs, args } = parseExtraArgs(canisters);
|
|
892
|
+
await installAll({
|
|
893
|
+
silent: true,
|
|
894
|
+
lock: "ignore",
|
|
895
|
+
installFromLockFile: true,
|
|
896
|
+
});
|
|
897
|
+
await generateCandid(args.length ? args : undefined, {
|
|
898
|
+
...options,
|
|
899
|
+
extraArgs,
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
program.addCommand(generateCommand);
|
|
904
|
+
|
|
772
905
|
// self
|
|
773
906
|
const selfCommand = new Command("self").description("Mops CLI management");
|
|
774
907
|
|
|
@@ -836,6 +969,17 @@ program
|
|
|
836
969
|
"Directories containing rules (can be used multiple times)",
|
|
837
970
|
),
|
|
838
971
|
)
|
|
972
|
+
.addOption(
|
|
973
|
+
new Option(
|
|
974
|
+
"--no-check-limit",
|
|
975
|
+
"Lint the full migration chain, ignoring [migrations].check-limit",
|
|
976
|
+
),
|
|
977
|
+
)
|
|
978
|
+
.addHelpText(
|
|
979
|
+
"after",
|
|
980
|
+
"\nArguments after -- are forwarded directly to lintoko, e.g.:\n $ mops lint -- --severity warning",
|
|
981
|
+
)
|
|
982
|
+
.addHelpText("after", enhancedMigrationHelp(true))
|
|
839
983
|
.allowUnknownOption(true)
|
|
840
984
|
.action(async (filter, options) => {
|
|
841
985
|
checkConfigFile(true);
|
|
@@ -843,6 +987,7 @@ program
|
|
|
843
987
|
await lint(filter, {
|
|
844
988
|
...options,
|
|
845
989
|
extraArgs,
|
|
990
|
+
noCheckLimit: options.checkLimit === false,
|
|
846
991
|
});
|
|
847
992
|
});
|
|
848
993
|
|
|
@@ -74,7 +74,8 @@ export class BenchReplica {
|
|
|
74
74
|
if (this.type === "dfx" || this.type === "dfx-pocket-ic") {
|
|
75
75
|
await execaCommand(
|
|
76
76
|
`dfx deploy ${name} --mode reinstall --yes --identity anonymous`,
|
|
77
|
-
|
|
77
|
+
// `inherit` so dfx output is streamed under --verbose (incl. its `Failed to optimize` warning)
|
|
78
|
+
{ cwd, stdio: this.verbose ? "inherit" : ["pipe", "ignore", "pipe"] },
|
|
78
79
|
);
|
|
79
80
|
let canisterId = execSync(`dfx canister id ${name}`, { cwd })
|
|
80
81
|
.toString()
|
package/commands/bench.ts
CHANGED
|
@@ -101,7 +101,28 @@ export async function bench(
|
|
|
101
101
|
|
|
102
102
|
warnIfDfxReplica(replicaType, optionsArg.replica === "dfx");
|
|
103
103
|
|
|
104
|
-
options.verbose
|
|
104
|
+
if (options.verbose) {
|
|
105
|
+
// `dfx` post-optimizes the wasm on deploy (`optimize: "cycles"`, via ic-wasm);
|
|
106
|
+
// `pocket-ic` runs the raw moc output. This changes instruction counts, so surface it.
|
|
107
|
+
let optimize =
|
|
108
|
+
replicaType === "dfx" || replicaType === "dfx-pocket-ic"
|
|
109
|
+
? 'dfx `optimize: "cycles"` (ic-wasm) on deploy'
|
|
110
|
+
: "none (raw moc output)";
|
|
111
|
+
console.log(chalk.gray("Benchmark pipeline:"));
|
|
112
|
+
console.log(chalk.gray(` compiler: moc ${options.compilerVersion}`));
|
|
113
|
+
console.log(
|
|
114
|
+
chalk.gray(
|
|
115
|
+
` replica: ${replicaType}${options.replicaVersion ? ` ${options.replicaVersion}` : ""}`,
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
console.log(
|
|
119
|
+
chalk.gray(
|
|
120
|
+
` gc: ${options.gc}${options.forceGc ? " (forced)" : ""}`,
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
console.log(chalk.gray(` profile: ${options.profile}`));
|
|
124
|
+
console.log(chalk.gray(` optimize: ${optimize}`));
|
|
125
|
+
}
|
|
105
126
|
|
|
106
127
|
let replica = new BenchReplica(replicaType, options.verbose);
|
|
107
128
|
|
|
@@ -303,14 +324,16 @@ async function deployBenchFile(
|
|
|
303
324
|
// build canister
|
|
304
325
|
let mocPath = getMocPath();
|
|
305
326
|
let mocArgs = getMocArgs(options);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
let buildCmd = `${mocPath} -c --idl canister.mo ${globalMocArgs.join(" ")} ${mocArgs} ${(await sources({ cwd: tempDir })).join(" ")}`;
|
|
328
|
+
if (options.verbose) {
|
|
329
|
+
console.log(chalk.gray(`[${canisterName}] ${buildCmd}`));
|
|
330
|
+
console.time(`build ${canisterName}`);
|
|
331
|
+
}
|
|
332
|
+
await execaCommand(buildCmd, {
|
|
333
|
+
cwd: tempDir,
|
|
334
|
+
// `inherit` so the compiler output (warnings/errors) is streamed under --verbose
|
|
335
|
+
stdio: options.verbose ? "inherit" : ["pipe", "ignore", "pipe"],
|
|
336
|
+
});
|
|
314
337
|
options.verbose && console.timeEnd(`build ${canisterName}`);
|
|
315
338
|
|
|
316
339
|
// deploy canister
|
package/commands/build.ts
CHANGED
|
@@ -9,13 +9,11 @@ import { isCandidCompatible } from "../helpers/is-candid-compatible.js";
|
|
|
9
9
|
import {
|
|
10
10
|
filterCanisters,
|
|
11
11
|
resolveCanisterConfigs,
|
|
12
|
-
validateCanisterArgs,
|
|
13
12
|
} from "../helpers/resolve-canisters.js";
|
|
14
|
-
import {
|
|
15
|
-
import { CanisterConfig, Config } from "../types.js";
|
|
13
|
+
import { BUILD_MANAGED_FLAGS, prepareMocArgs } from "../helpers/moc-args.js";
|
|
16
14
|
import { CustomSection, getWasmBindings } from "../wasm.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
15
|
+
import { readConfig, resolveConfigPath } from "../mops.js";
|
|
16
|
+
import { Config } from "../types.js";
|
|
19
17
|
import { toolchain } from "./toolchain/index.js";
|
|
20
18
|
|
|
21
19
|
export interface BuildOptions {
|
|
@@ -26,6 +24,22 @@ export interface BuildOptions {
|
|
|
26
24
|
|
|
27
25
|
export const DEFAULT_BUILD_OUTPUT_DIR = ".mops/.build";
|
|
28
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the build output directory: CLI override → `[build].outputDir`
|
|
29
|
+
* (project-root-relative, resolved via `resolveConfigPath`) → default.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveBuildOutputDir(
|
|
32
|
+
config: Config,
|
|
33
|
+
override?: string,
|
|
34
|
+
): string {
|
|
35
|
+
if (override) {
|
|
36
|
+
return override;
|
|
37
|
+
}
|
|
38
|
+
return config.build?.outputDir
|
|
39
|
+
? resolveConfigPath(config.build.outputDir)
|
|
40
|
+
: DEFAULT_BUILD_OUTPUT_DIR;
|
|
41
|
+
}
|
|
42
|
+
|
|
29
43
|
export async function build(
|
|
30
44
|
canisterNames: string[] | undefined,
|
|
31
45
|
options: Partial<BuildOptions>,
|
|
@@ -35,11 +49,7 @@ export async function build(
|
|
|
35
49
|
}
|
|
36
50
|
|
|
37
51
|
let config = readConfig();
|
|
38
|
-
let
|
|
39
|
-
? resolveConfigPath(config.build.outputDir)
|
|
40
|
-
: undefined;
|
|
41
|
-
let outputDir =
|
|
42
|
-
options.outputDir ?? configOutputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
|
|
52
|
+
let outputDir = resolveBuildOutputDir(config, options.outputDir);
|
|
43
53
|
let mocPath = await toolchain.bin("moc", { fallback: true });
|
|
44
54
|
let canisters = resolveCanisterConfigs(config);
|
|
45
55
|
if (!Object.keys(canisters).length) {
|
|
@@ -54,11 +64,6 @@ export async function build(
|
|
|
54
64
|
|
|
55
65
|
for (let [canisterName, canister] of Object.entries(filteredCanisters)) {
|
|
56
66
|
console.log(chalk.blue("build canister"), chalk.bold(canisterName));
|
|
57
|
-
let motokoPath = canister.main;
|
|
58
|
-
if (!motokoPath) {
|
|
59
|
-
cliError(`No main file is specified for canister ${canisterName}`);
|
|
60
|
-
}
|
|
61
|
-
motokoPath = resolveConfigPath(motokoPath);
|
|
62
67
|
const wasmPath = join(outputDir, `${canisterName}.wasm`);
|
|
63
68
|
const mostPath = join(outputDir, `${canisterName}.most`);
|
|
64
69
|
|
|
@@ -88,12 +93,13 @@ export async function build(
|
|
|
88
93
|
};
|
|
89
94
|
process.on("exit", exitCleanup);
|
|
90
95
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
"build",
|
|
95
|
-
options.verbose,
|
|
96
|
-
|
|
96
|
+
const prepared = await prepareMocArgs(config, canister, canisterName, {
|
|
97
|
+
mode: "build",
|
|
98
|
+
managedFlags: BUILD_MANAGED_FLAGS,
|
|
99
|
+
commandName: "mops build",
|
|
100
|
+
verbose: options.verbose,
|
|
101
|
+
extraArgs: options.extraArgs,
|
|
102
|
+
});
|
|
97
103
|
try {
|
|
98
104
|
let args = [
|
|
99
105
|
"-c",
|
|
@@ -101,14 +107,9 @@ export async function build(
|
|
|
101
107
|
"--stable-types",
|
|
102
108
|
"-o",
|
|
103
109
|
wasmPath,
|
|
104
|
-
motokoPath,
|
|
105
|
-
...
|
|
106
|
-
...getGlobalMocArgs(config),
|
|
107
|
-
...migration.migrationArgs,
|
|
110
|
+
prepared.motokoPath,
|
|
111
|
+
...prepared.args,
|
|
108
112
|
];
|
|
109
|
-
args.push(
|
|
110
|
-
...collectExtraArgs(config, canister, canisterName, options.extraArgs),
|
|
111
|
-
);
|
|
112
113
|
|
|
113
114
|
const isPublicCandid = true; // always true for now to reduce corner cases
|
|
114
115
|
const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
|
|
@@ -207,7 +208,7 @@ export async function build(
|
|
|
207
208
|
);
|
|
208
209
|
}
|
|
209
210
|
} finally {
|
|
210
|
-
await
|
|
211
|
+
await prepared.cleanup();
|
|
211
212
|
process.removeListener("exit", exitCleanup);
|
|
212
213
|
try {
|
|
213
214
|
await release?.();
|
|
@@ -221,51 +222,3 @@ export async function build(
|
|
|
221
222
|
),
|
|
222
223
|
);
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
-
const managedFlags: Record<string, string> = {
|
|
226
|
-
"-o": "use [build].outputDir in mops.toml or --output flag instead",
|
|
227
|
-
"-c": "this flag is always set by mops build",
|
|
228
|
-
"--idl": "this flag is always set by mops build",
|
|
229
|
-
"--stable-types": "this flag is always set by mops build",
|
|
230
|
-
"--public-metadata": "this flag is managed by mops build",
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
function collectExtraArgs(
|
|
234
|
-
config: Config,
|
|
235
|
-
canister: CanisterConfig,
|
|
236
|
-
canisterName: string,
|
|
237
|
-
extraArgs?: string[],
|
|
238
|
-
): string[] {
|
|
239
|
-
const args: string[] = [];
|
|
240
|
-
|
|
241
|
-
if (config.build?.args) {
|
|
242
|
-
if (typeof config.build.args === "string") {
|
|
243
|
-
cliError(
|
|
244
|
-
`[build] config 'args' should be an array of strings in mops.toml config file`,
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
args.push(...config.build.args);
|
|
248
|
-
}
|
|
249
|
-
if (canister.args) {
|
|
250
|
-
validateCanisterArgs(canister, canisterName, config);
|
|
251
|
-
args.push(...canister.args);
|
|
252
|
-
}
|
|
253
|
-
if (extraArgs) {
|
|
254
|
-
args.push(...extraArgs);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const warned = new Set<string>();
|
|
258
|
-
for (const arg of args) {
|
|
259
|
-
const hint = managedFlags[arg];
|
|
260
|
-
if (hint && !warned.has(arg)) {
|
|
261
|
-
warned.add(arg);
|
|
262
|
-
console.warn(
|
|
263
|
-
chalk.yellow(
|
|
264
|
-
`Warning: '${arg}' in args for canister ${canisterName} may conflict with mops build — ${hint}`,
|
|
265
|
-
),
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return args;
|
|
271
|
-
}
|
package/commands/check-stable.ts
CHANGED
|
@@ -25,6 +25,8 @@ const CHECK_STABLE_PREFIX = ".check-stable-";
|
|
|
25
25
|
export interface CheckStableOptions {
|
|
26
26
|
verbose: boolean;
|
|
27
27
|
extraArgs: string[];
|
|
28
|
+
/** Commander `--no-check-limit`: false ignores [migrations].check-limit. */
|
|
29
|
+
checkLimit: boolean;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export function resolveStablePath(
|
|
@@ -91,6 +93,7 @@ export async function checkStable(
|
|
|
91
93
|
name,
|
|
92
94
|
"check",
|
|
93
95
|
options.verbose,
|
|
96
|
+
options.checkLimit === false,
|
|
94
97
|
);
|
|
95
98
|
try {
|
|
96
99
|
await runStableCheck({
|
|
@@ -132,6 +135,7 @@ export async function checkStable(
|
|
|
132
135
|
name,
|
|
133
136
|
"check",
|
|
134
137
|
options.verbose,
|
|
138
|
+
options.checkLimit === false,
|
|
135
139
|
);
|
|
136
140
|
try {
|
|
137
141
|
await runStableCheck({
|
package/commands/check.ts
CHANGED
|
@@ -35,6 +35,8 @@ export interface CheckOptions {
|
|
|
35
35
|
verbose: boolean;
|
|
36
36
|
fix: boolean;
|
|
37
37
|
extraArgs: string[];
|
|
38
|
+
/** Commander `--no-check-limit`: false ignores [migrations].check-limit. */
|
|
39
|
+
checkLimit: boolean;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
function checkAllLibsSupport(verbose?: boolean): boolean {
|
|
@@ -135,6 +137,7 @@ async function checkImpl(
|
|
|
135
137
|
fix: options.fix,
|
|
136
138
|
rules: lintRules,
|
|
137
139
|
files: lintFiles,
|
|
140
|
+
noCheckLimit: options.checkLimit === false,
|
|
138
141
|
});
|
|
139
142
|
}
|
|
140
143
|
}
|
|
@@ -164,6 +167,7 @@ async function checkCanisters(
|
|
|
164
167
|
canisterName,
|
|
165
168
|
"check",
|
|
166
169
|
options.verbose,
|
|
170
|
+
options.checkLimit === false,
|
|
167
171
|
);
|
|
168
172
|
try {
|
|
169
173
|
const mocArgs = [
|
|
@@ -262,30 +266,30 @@ async function checkFiles(
|
|
|
262
266
|
logAutofixResult(fixResult, options.verbose);
|
|
263
267
|
}
|
|
264
268
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
for (const file of files) {
|
|
270
|
+
try {
|
|
271
|
+
const args = [file, ...mocArgs];
|
|
272
|
+
if (options.verbose) {
|
|
273
|
+
console.log(chalk.blue("check"), chalk.gray("Running moc:"));
|
|
274
|
+
console.log(chalk.gray(mocPath, JSON.stringify(args)));
|
|
275
|
+
}
|
|
272
276
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
});
|
|
277
|
+
const result = await execa(mocPath, args, {
|
|
278
|
+
stdio: "inherit",
|
|
279
|
+
reject: false,
|
|
280
|
+
});
|
|
278
281
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
`Error while checking files${err?.message ? `\n${err.message}` : ""}`,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
282
|
+
if (result.exitCode !== 0) {
|
|
283
|
+
cliError(
|
|
284
|
+
`✗ Check failed for file ${file} (exit code: ${result.exitCode})`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
287
|
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
console.log(chalk.green(`✓ ${file}`));
|
|
289
|
+
} catch (err: any) {
|
|
290
|
+
cliError(
|
|
291
|
+
`Error while checking ${file}${err?.message ? `\n${err.message}` : ""}`,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
290
294
|
}
|
|
291
295
|
}
|