ic-mops 2.3.2 → 2.5.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.
@@ -15,9 +15,12 @@ export async function build(canisterNames, options) {
15
15
  if (canisterNames?.length == 0) {
16
16
  cliError("No canisters specified to build");
17
17
  }
18
- let outputDir = options.outputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
19
- let mocPath = await toolchain.bin("moc", { fallback: true });
20
18
  let config = readConfig();
19
+ let configOutputDir = config.build?.outputDir
20
+ ? resolveConfigPath(config.build.outputDir)
21
+ : undefined;
22
+ let outputDir = options.outputDir ?? configOutputDir ?? DEFAULT_BUILD_OUTPUT_DIR;
23
+ let mocPath = await toolchain.bin("moc", { fallback: true });
21
24
  let canisters = resolveCanisterConfigs(config);
22
25
  if (!Object.keys(canisters).length) {
23
26
  cliError(`No Motoko canisters found in mops.toml configuration`);
@@ -56,19 +59,7 @@ export async function build(canisterNames, options) {
56
59
  ...(await sourcesArgs()).flat(),
57
60
  ...getGlobalMocArgs(config),
58
61
  ];
59
- if (config.build?.args) {
60
- if (typeof config.build.args === "string") {
61
- cliError(`[build] config 'args' should be an array of strings in mops.toml config file`);
62
- }
63
- args.push(...config.build.args);
64
- }
65
- if (canister.args) {
66
- if (typeof canister.args === "string") {
67
- cliError(`Canister config 'args' should be an array of strings for canister ${canisterName}`);
68
- }
69
- args.push(...canister.args);
70
- }
71
- args.push(...(options.extraArgs ?? []));
62
+ args.push(...collectExtraArgs(config, canister, canisterName, options.extraArgs));
72
63
  const isPublicCandid = true; // always true for now to reduce corner cases
73
64
  const candidVisibility = isPublicCandid ? "icp:public" : "icp:private";
74
65
  if (isPublicCandid) {
@@ -142,3 +133,36 @@ export async function build(canisterNames, options) {
142
133
  }
143
134
  console.log(chalk.green(`\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length == 1 ? "" : "s"} successfully`));
144
135
  }
136
+ const managedFlags = {
137
+ "-o": "use [build].outputDir in mops.toml or --output flag instead",
138
+ "-c": "this flag is always set by mops build",
139
+ "--idl": "this flag is always set by mops build",
140
+ "--public-metadata": "this flag is managed by mops build",
141
+ };
142
+ function collectExtraArgs(config, canister, canisterName, extraArgs) {
143
+ const args = [];
144
+ if (config.build?.args) {
145
+ if (typeof config.build.args === "string") {
146
+ cliError(`[build] config 'args' should be an array of strings in mops.toml config file`);
147
+ }
148
+ args.push(...config.build.args);
149
+ }
150
+ if (canister.args) {
151
+ if (typeof canister.args === "string") {
152
+ cliError(`Canister config 'args' should be an array of strings for canister ${canisterName}`);
153
+ }
154
+ args.push(...canister.args);
155
+ }
156
+ if (extraArgs) {
157
+ args.push(...extraArgs);
158
+ }
159
+ const warned = new Set();
160
+ for (const arg of args) {
161
+ const hint = managedFlags[arg];
162
+ if (hint && !warned.has(arg)) {
163
+ warned.add(arg);
164
+ console.warn(chalk.yellow(`Warning: '${arg}' in args for canister ${canisterName} may conflict with mops build — ${hint}`));
165
+ }
166
+ }
167
+ return args;
168
+ }
@@ -1,8 +1,7 @@
1
1
  import { ChildProcessWithoutNullStreams } from "node:child_process";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { IDL } from "@icp-sdk/core/candid";
4
- import { PocketIc, PocketIcServer } from "pic-ic";
5
- import { PocketIc as PocketIcMops, PocketIcServer as PocketIcServerMops } from "pic-js-mops";
4
+ import { type AnyPocketIcServer, type AnyPocketIc } from "../helpers/pocket-ic-client.js";
6
5
  type StartOptions = {
7
6
  type?: "dfx" | "pocket-ic" | "dfx-pocket-ic";
8
7
  dir?: string;
@@ -18,8 +17,8 @@ export declare class Replica {
18
17
  actor: any;
19
18
  stream: PassThrough;
20
19
  }>;
21
- pocketIcServer?: PocketIcServer | PocketIcServerMops;
22
- pocketIc?: PocketIc | PocketIcMops;
20
+ pocketIcServer?: AnyPocketIcServer;
21
+ pocketIc?: AnyPocketIc;
23
22
  dfxProcess?: ChildProcessWithoutNullStreams;
24
23
  dir: string;
25
24
  ttl: number;
@@ -5,10 +5,8 @@ import fs from "node:fs";
5
5
  import { PassThrough } from "node:stream";
6
6
  import { spawn as spawnAsync } from "promisify-child-process";
7
7
  import { Actor, HttpAgent } from "@icp-sdk/core/agent";
8
- import { PocketIc, PocketIcServer } from "pic-ic";
9
- import { PocketIc as PocketIcMops, PocketIcServer as PocketIcServerMops, } from "pic-js-mops";
10
8
  import chalk from "chalk";
11
- import { readConfig } from "../mops.js";
9
+ import { startPocketIc, } from "../helpers/pocket-ic-client.js";
12
10
  import { toolchain } from "./toolchain/index.js";
13
11
  import { getDfxVersion } from "../helpers/get-dfx-version.js";
14
12
  export class Replica {
@@ -74,32 +72,14 @@ export class Replica {
74
72
  }
75
73
  else {
76
74
  let pocketIcBin = await toolchain.bin("pocket-ic");
77
- let config = readConfig();
78
- if (config.toolchain?.["pocket-ic"] !== "4.0.0" &&
79
- !config.toolchain?.["pocket-ic"]?.startsWith("9.")) {
80
- console.error("Current Mops CLI only supports pocket-ic 9.x.x and 4.0.0");
81
- process.exit(1);
82
- }
83
- // pocket-ic 9.x.x
84
- if (config.toolchain?.["pocket-ic"]?.startsWith("9.")) {
85
- this.pocketIcServer = await PocketIcServerMops.start({
86
- showRuntimeLogs: false,
87
- showCanisterLogs: false,
88
- binPath: pocketIcBin,
89
- ttl: this.ttl,
90
- });
91
- this.pocketIc = await PocketIcMops.create(this.pocketIcServer.getUrl());
92
- }
93
- // pocket-ic 4.0.0
94
- else {
95
- this.pocketIcServer = await PocketIcServer.start({
96
- showRuntimeLogs: false,
97
- showCanisterLogs: false,
98
- binPath: pocketIcBin,
99
- ttl: this.ttl,
100
- });
101
- this.pocketIc = await PocketIc.create(this.pocketIcServer.getUrl());
102
- }
75
+ let pic = await startPocketIc({
76
+ binPath: pocketIcBin,
77
+ showRuntimeLogs: false,
78
+ showCanisterLogs: false,
79
+ ttl: this.ttl,
80
+ });
81
+ this.pocketIcServer = pic.server;
82
+ this.pocketIc = pic.client;
103
83
  // process canister logs
104
84
  this._attachCanisterLogHandler(this.pocketIcServer.serverProcess);
105
85
  }
@@ -1,4 +1,13 @@
1
1
  import * as wasm from "../../wasm/pkg/web/wasm.js";
2
2
  import { setWasmBindings } from "../../wasm.js";
3
+ import { readFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+ // Web wasm-pack target requires explicit initialization (unlike the nodejs
6
+ // target which auto-inits on require). Load the CLI's own WASM binary and
7
+ // call initSync before exposing the bindings.
8
+ // In the bundle __dirname is defined as import.meta.dirname and the binary
9
+ // sits next to cli.js; bundle:fix rewrites the path accordingly.
10
+ const wasmBytes = readFileSync(resolve(__dirname, "../../wasm/pkg/web/wasm_bg.wasm"));
11
+ wasm.initSync({ module: wasmBytes });
3
12
  setWasmBindings(wasm);
4
13
  export * from "../../cli.js";
@@ -0,0 +1,9 @@
1
+ import { PocketIc, PocketIcServer } from "pic-ic";
2
+ import { PocketIc as PocketIcModern, PocketIcServer as PocketIcServerModern, type StartServerOptions } from "pic-js-mops";
3
+ export type AnyPocketIcServer = PocketIcServer | PocketIcServerModern;
4
+ export type AnyPocketIc = PocketIc | PocketIcModern;
5
+ export type AnySetupCanister = PocketIc["setupCanister"] & PocketIcModern["setupCanister"];
6
+ export declare function startPocketIc(options: StartServerOptions): Promise<{
7
+ server: AnyPocketIcServer;
8
+ client: AnyPocketIc;
9
+ }>;
@@ -0,0 +1,18 @@
1
+ import semver from "semver";
2
+ import { PocketIc, PocketIcServer } from "pic-ic";
3
+ import { PocketIc as PocketIcModern, PocketIcServer as PocketIcServerModern, } from "pic-js-mops";
4
+ import { readConfig } from "../mops.js";
5
+ function isLegacy() {
6
+ let version = readConfig().toolchain?.["pocket-ic"];
7
+ return !!version && !!semver.valid(version) && semver.lt(version, "9.0.0");
8
+ }
9
+ export async function startPocketIc(options) {
10
+ if (isLegacy()) {
11
+ let server = await PocketIcServer.start(options);
12
+ let client = await PocketIc.create(server.getUrl());
13
+ return { server, client };
14
+ }
15
+ let server = await PocketIcServerModern.start(options);
16
+ let client = await PocketIcModern.create(server.getUrl());
17
+ return { server, client };
18
+ }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.3.2",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "bin/mops.js",
package/dist/parallel.js CHANGED
@@ -1,8 +1,12 @@
1
1
  export async function parallel(threads, items, fn) {
2
- return new Promise((resolve) => {
2
+ return new Promise((resolve, reject) => {
3
3
  let busyThreads = 0;
4
+ let failed = false;
4
5
  items = items.slice();
5
6
  let loop = () => {
7
+ if (failed) {
8
+ return;
9
+ }
6
10
  if (!items.length) {
7
11
  if (busyThreads === 0) {
8
12
  resolve();
@@ -16,6 +20,10 @@ export async function parallel(threads, items, fn) {
16
20
  fn(items.shift()).then(() => {
17
21
  busyThreads--;
18
22
  loop();
23
+ }, (err) => {
24
+ busyThreads--;
25
+ failed = true;
26
+ reject(err);
19
27
  });
20
28
  loop();
21
29
  };
@@ -8,8 +8,12 @@ import { sha256 } from "@noble/hashes/sha256";
8
8
  import { bytesToHex } from "@noble/hashes/utils";
9
9
  import { findChangelogEntry } from "./helpers/find-changelog-entry.js";
10
10
  let __dirname = new URL(".", import.meta.url).pathname;
11
- // build using Docker
12
- execSync("./build.sh", { stdio: "inherit", cwd: __dirname });
11
+ if (!process.env.SKIP_BUILD) {
12
+ execSync("./build.sh", { stdio: "inherit", cwd: __dirname });
13
+ }
14
+ else if (!fs.existsSync(path.resolve(__dirname, "bundle/cli.tgz"))) {
15
+ throw new Error("SKIP_BUILD is set but bundle/cli.tgz does not exist. Run build.sh first.");
16
+ }
13
17
  let commitHash = process.env.COMMIT_HASH || execSync("git rev-parse HEAD").toString().trim();
14
18
  let version = JSON.parse(fs.readFileSync(path.resolve(__dirname, "package.json"), "utf8")).version;
15
19
  let major = semver.parse(version)?.major;
@@ -1,22 +1,86 @@
1
- import { describe, expect, test } from "@jest/globals";
1
+ import { describe, expect, jest, test } from "@jest/globals";
2
2
  import { execa } from "execa";
3
- import { existsSync } from "node:fs";
3
+ import { existsSync, rmSync } from "node:fs";
4
4
  import path from "path";
5
- import { cliSnapshot } from "./helpers";
5
+ import { cli, cliSnapshot } from "./helpers";
6
6
  const distBin = path.resolve(import.meta.dirname, "../dist/bin/mops.js");
7
+ function cleanFixture(cwd, ...extras) {
8
+ rmSync(path.join(cwd, ".mops"), { recursive: true, force: true });
9
+ for (const p of extras) {
10
+ rmSync(p, { recursive: true, force: true });
11
+ }
12
+ }
7
13
  describe("build", () => {
14
+ // Several dfx/pocket-ic builds per test; slow CI (e.g. node 20 matrix) can exceed 60s default.
15
+ jest.setTimeout(120_000);
8
16
  test("ok", async () => {
9
17
  const cwd = path.join(import.meta.dirname, "build/success");
10
- await cliSnapshot(["build", "--verbose"], { cwd }, 0);
11
- await cliSnapshot(["build", "foo"], { cwd }, 0);
12
- await cliSnapshot(["build", "bar"], { cwd }, 0);
13
- await cliSnapshot(["build", "foo", "bar"], { cwd }, 0);
18
+ try {
19
+ await cliSnapshot(["build", "--verbose"], { cwd }, 0);
20
+ await cliSnapshot(["build", "foo"], { cwd }, 0);
21
+ await cliSnapshot(["build", "bar"], { cwd }, 0);
22
+ await cliSnapshot(["build", "foo", "bar"], { cwd }, 0);
23
+ }
24
+ finally {
25
+ cleanFixture(cwd);
26
+ }
14
27
  });
15
28
  test("error", async () => {
16
29
  const cwd = path.join(import.meta.dirname, "build/error");
17
- await cliSnapshot(["build", "foo", "--verbose"], { cwd }, 0);
18
- expect((await cliSnapshot(["build", "bar"], { cwd }, 1)).stderr).toMatch("Candid compatibility check failed for canister bar");
19
- expect((await cliSnapshot(["build", "foo", "bar"], { cwd }, 1)).stderr).toMatch("Candid compatibility check failed for canister bar");
30
+ try {
31
+ await cliSnapshot(["build", "foo", "--verbose"], { cwd }, 0);
32
+ expect((await cliSnapshot(["build", "bar"], { cwd }, 1)).stderr).toMatch("Candid compatibility check failed for canister bar");
33
+ expect((await cliSnapshot(["build", "foo", "bar"], { cwd }, 1)).stderr).toMatch("Candid compatibility check failed for canister bar");
34
+ }
35
+ finally {
36
+ cleanFixture(cwd);
37
+ }
38
+ });
39
+ // [build].outputDir in mops.toml should control where build output goes
40
+ test("custom output path via config outputDir", async () => {
41
+ const cwd = path.join(import.meta.dirname, "build/custom-output");
42
+ const customOut = path.join(cwd, "custom-out");
43
+ const customWasm = path.join(customOut, "main.wasm");
44
+ const customDid = path.join(customOut, "main.did");
45
+ const defaultDid = path.join(cwd, ".mops/.build/main.did");
46
+ try {
47
+ const result = await cli(["build"], { cwd });
48
+ expect(result.exitCode).toBe(0);
49
+ expect(existsSync(customWasm)).toBe(true);
50
+ expect(existsSync(customDid)).toBe(true);
51
+ expect(existsSync(defaultDid)).toBe(false);
52
+ }
53
+ finally {
54
+ cleanFixture(cwd, customOut);
55
+ }
56
+ });
57
+ // Regression: --output CLI option was silently ignored due to
58
+ // Commander storing it as options.output while build() read options.outputDir
59
+ test("--output CLI option", async () => {
60
+ const cwd = path.join(import.meta.dirname, "build/success");
61
+ const outputDir = path.join(cwd, "cli-output-test");
62
+ try {
63
+ const result = await cli(["build", "foo", "--output", outputDir], {
64
+ cwd,
65
+ });
66
+ expect(result.exitCode).toBe(0);
67
+ expect(existsSync(path.join(outputDir, "foo.wasm"))).toBe(true);
68
+ expect(existsSync(path.join(outputDir, "foo.did"))).toBe(true);
69
+ }
70
+ finally {
71
+ cleanFixture(cwd, outputDir);
72
+ }
73
+ });
74
+ test("warns when args contain managed flags", async () => {
75
+ const cwd = path.join(import.meta.dirname, "build/success");
76
+ const artifact = path.join(cwd, "x");
77
+ const artifactDid = path.join(cwd, "x.did");
78
+ try {
79
+ await cliSnapshot(["build", "foo", "--", "-o", "x", "-c", "--idl"], { cwd }, 1);
80
+ }
81
+ finally {
82
+ cleanFixture(cwd, artifact, artifactDid);
83
+ }
20
84
  });
21
85
  // Regression: bin/mops.js must route through environments/nodejs/cli.js
22
86
  // so that setWasmBindings() is called before any command runs.
@@ -25,12 +89,17 @@ describe("build", () => {
25
89
  const hasDistBin = existsSync(distBin);
26
90
  (hasDistBin ? test : test.skip)("wasm bindings initialized via dist entry point", async () => {
27
91
  const cwd = path.join(import.meta.dirname, "build/success");
28
- const result = await execa("node", [distBin, "build", "foo"], {
29
- cwd,
30
- stdio: "pipe",
31
- reject: false,
32
- });
33
- expect(result.stderr).not.toContain("Wasm bindings have not been set");
34
- expect(result.exitCode).toBe(0);
92
+ try {
93
+ const result = await execa("node", [distBin, "build", "foo"], {
94
+ cwd,
95
+ stdio: "pipe",
96
+ reject: false,
97
+ });
98
+ expect(result.stderr).not.toContain("Wasm bindings have not been set");
99
+ expect(result.exitCode).toBe(0);
100
+ }
101
+ finally {
102
+ cleanFixture(cwd);
103
+ }
35
104
  });
36
105
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { describe, test, expect } from "@jest/globals";
2
+ import path from "path";
3
+ import { cli } from "./helpers";
4
+ describe("pocket-ic", () => {
5
+ test("runs tests with pocket-ic 12.0.0", async () => {
6
+ const cwd = path.join(import.meta.dirname, "pocket-ic");
7
+ const result = await cli(["test", "--reporter", "verbose", "--replica", "pocket-ic"], { cwd });
8
+ expect(result.stderr).not.toContain("is not supported");
9
+ expect(result.stderr).not.toContain("only supports pocket-ic 9.x.x and 4.0.0");
10
+ expect(result.exitCode).toBe(0);
11
+ });
12
+ });
@@ -1,5 +1,17 @@
1
1
  import * as wasm from "../../wasm/pkg/web/wasm.js";
2
2
  import { setWasmBindings } from "../../wasm.js";
3
+ import { readFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+
6
+ // Web wasm-pack target requires explicit initialization (unlike the nodejs
7
+ // target which auto-inits on require). Load the CLI's own WASM binary and
8
+ // call initSync before exposing the bindings.
9
+ // In the bundle __dirname is defined as import.meta.dirname and the binary
10
+ // sits next to cli.js; bundle:fix rewrites the path accordingly.
11
+ const wasmBytes = readFileSync(
12
+ resolve(__dirname, "../../wasm/pkg/web/wasm_bg.wasm"),
13
+ );
14
+ wasm.initSync({ module: wasmBytes });
3
15
 
4
16
  setWasmBindings(wasm);
5
17
 
@@ -0,0 +1,32 @@
1
+ import semver from "semver";
2
+ import { PocketIc, PocketIcServer } from "pic-ic";
3
+ import {
4
+ PocketIc as PocketIcModern,
5
+ PocketIcServer as PocketIcServerModern,
6
+ type StartServerOptions,
7
+ } from "pic-js-mops";
8
+ import { readConfig } from "../mops.js";
9
+
10
+ export type AnyPocketIcServer = PocketIcServer | PocketIcServerModern;
11
+ export type AnyPocketIc = PocketIc | PocketIcModern;
12
+ export type AnySetupCanister = PocketIc["setupCanister"] &
13
+ PocketIcModern["setupCanister"];
14
+
15
+ function isLegacy(): boolean {
16
+ let version = readConfig().toolchain?.["pocket-ic"];
17
+ return !!version && !!semver.valid(version) && semver.lt(version, "9.0.0");
18
+ }
19
+
20
+ export async function startPocketIc(
21
+ options: StartServerOptions,
22
+ ): Promise<{ server: AnyPocketIcServer; client: AnyPocketIc }> {
23
+ if (isLegacy()) {
24
+ let server = await PocketIcServer.start(options);
25
+ let client = await PocketIc.create(server.getUrl());
26
+ return { server, client };
27
+ }
28
+
29
+ let server = await PocketIcServerModern.start(options);
30
+ let client = await PocketIcModern.create(server.getUrl());
31
+ return { server, client };
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "2.3.2",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/bin/mops.js",
@@ -36,8 +36,8 @@
36
36
  "build:wasm:web": "wasm-pack build wasm --target web --out-dir pkg/web && rm wasm/pkg/web/.gitignore",
37
37
  "dist": "npm run build:wasm && tsc && mkdir -p dist/wasm && cp -r wasm/pkg dist/wasm",
38
38
  "bundle": "rm -rf ./bundle && bun build ./environments/web/cli.ts --outdir ./bundle --target node --minify --external @napi-rs/lzma --external fsevents --format esm --define '__dirname=import.meta.dirname' && run-s bundle:fix bundle:copy bundle:package-json bundle:tar",
39
- "bundle:fix": "rexreplace 'new URL\\(\"\\.\\./templates' 'new URL(\"./templates' bundle/cli.js && rexreplace 'resolve\\(\".*?/xhr-sync-worker\\.js\"\\)' 'resolve(\"./xhr-sync-worker.js\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"wasm_bg.wasm\"' bundle/cli.js && rexreplace 'resolve\\(\"import.meta.dirname\",\".*?/default-stylesheet\\.css\"\\)' 'resolve(import.meta.dirname,\"./default-stylesheet.css\")' bundle/cli.js",
40
- "bundle:copy": "cp -r commands/bench bundle && cp -r bin declarations templates package.json bundle && cp -r node_modules/prettier-plugin-motoko/wasm/pkg/nodejs/wasm_bg.wasm node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js node_modules/jsdom/lib/jsdom/browser/default-stylesheet.css bundle",
39
+ "bundle:fix": "rexreplace 'new URL\\(\"\\.\\./templates' 'new URL(\"./templates' bundle/cli.js && rexreplace 'resolve\\(\".*?/xhr-sync-worker\\.js\"\\)' 'resolve(\"./xhr-sync-worker.js\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"wasm_bg.wasm\"' bundle/cli.js && rexreplace 'resolve\\(\"import.meta.dirname\",\".*?/default-stylesheet\\.css\"\\)' 'resolve(import.meta.dirname,\"./default-stylesheet.css\")' bundle/cli.js && rexreplace '\"import.meta.dirname\",\"../../wasm/pkg/web/wasm_bg.wasm\"' 'import.meta.dirname || new URL(\".\", import.meta.url).pathname,\"mops_wasm_bg.wasm\"' bundle/cli.js",
40
+ "bundle:copy": "cp -r commands/bench bundle && cp -r bin declarations templates package.json bundle && cp wasm/pkg/web/wasm_bg.wasm bundle/mops_wasm_bg.wasm && cp -r node_modules/prettier-plugin-motoko/wasm/pkg/nodejs/wasm_bg.wasm node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js node_modules/jsdom/lib/jsdom/browser/default-stylesheet.css bundle",
41
41
  "bundle:package-json": "tsx bundle-package-json.ts",
42
42
  "bundle:tar": "rm -f bundle/cli.tgz && touch -t 200101010101 bundle/cli.tgz && find bundle -exec touch -d '1970-01-01 00:00:00' {} + && tar --sort name --exclude bundle/cli.tgz -cvf - bundle | gzip -n > bundle/cli.tgz",
43
43
  "copy": "cp -r commands/bench dist/commands && cp -r declarations templates package.json bin dist | true",
package/parallel.ts CHANGED
@@ -3,11 +3,15 @@ export async function parallel<T>(
3
3
  items: T[],
4
4
  fn: (item: T) => Promise<void>,
5
5
  ) {
6
- return new Promise<void>((resolve) => {
6
+ return new Promise<void>((resolve, reject) => {
7
7
  let busyThreads = 0;
8
+ let failed = false;
8
9
  items = items.slice();
9
10
 
10
11
  let loop = () => {
12
+ if (failed) {
13
+ return;
14
+ }
11
15
  if (!items.length) {
12
16
  if (busyThreads === 0) {
13
17
  resolve();
@@ -18,10 +22,17 @@ export async function parallel<T>(
18
22
  return;
19
23
  }
20
24
  busyThreads++;
21
- fn(items.shift() as T).then(() => {
22
- busyThreads--;
23
- loop();
24
- });
25
+ fn(items.shift() as T).then(
26
+ () => {
27
+ busyThreads--;
28
+ loop();
29
+ },
30
+ (err) => {
31
+ busyThreads--;
32
+ failed = true;
33
+ reject(err);
34
+ },
35
+ );
25
36
  loop();
26
37
  };
27
38
  loop();
package/release-cli.ts CHANGED
@@ -11,8 +11,13 @@ import { findChangelogEntry } from "./helpers/find-changelog-entry.js";
11
11
 
12
12
  let __dirname = new URL(".", import.meta.url).pathname;
13
13
 
14
- // build using Docker
15
- execSync("./build.sh", { stdio: "inherit", cwd: __dirname });
14
+ if (!process.env.SKIP_BUILD) {
15
+ execSync("./build.sh", { stdio: "inherit", cwd: __dirname });
16
+ } else if (!fs.existsSync(path.resolve(__dirname, "bundle/cli.tgz"))) {
17
+ throw new Error(
18
+ "SKIP_BUILD is set but bundle/cli.tgz does not exist. Run build.sh first.",
19
+ );
20
+ }
16
21
 
17
22
  let commitHash =
18
23
  process.env.COMMIT_HASH || execSync("git rev-parse HEAD").toString().trim();
@@ -75,3 +75,15 @@ build canister bar
75
75
  ✓ Built 2 canisters successfully",
76
76
  }
77
77
  `;
78
+
79
+ exports[`build warns when args contain managed flags 1`] = `
80
+ {
81
+ "exitCode": 1,
82
+ "stderr": "Warning: '-o' in args for canister foo may conflict with mops build — use [build].outputDir in mops.toml or --output flag instead
83
+ Warning: '-c' in args for canister foo may conflict with mops build — this flag is always set by mops build
84
+ Warning: '--idl' in args for canister foo may conflict with mops build — this flag is always set by mops build
85
+ Error while compiling canister foo
86
+ ENOENT: no such file or directory, open '.mops/.build/foo.did'",
87
+ "stdout": "build canister foo",
88
+ }
89
+ `;
@@ -0,0 +1,8 @@
1
+ {
2
+ "canisters": {
3
+ "main": {
4
+ "type": "motoko",
5
+ "main": "src/Main.mo"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ [dependencies]
2
+ core = "1.0.0"
3
+
4
+ [canisters.main]
5
+ main = "src/Main.mo"
6
+
7
+ [build]
8
+ outputDir = "custom-out"
@@ -0,0 +1,5 @@
1
+ persistent actor {
2
+ public func greet(name : Text) : async Text {
3
+ "Hello, " # name # "!";
4
+ };
5
+ };