ic-mops 2.14.0 → 2.15.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 (67) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/bundle/cli.tgz +0 -0
  3. package/cli.ts +143 -3
  4. package/commands/build.ts +30 -77
  5. package/commands/check-stable.ts +4 -0
  6. package/commands/check.ts +4 -0
  7. package/commands/deployed.ts +162 -0
  8. package/commands/generate.ts +201 -0
  9. package/commands/lint.ts +6 -3
  10. package/dist/cli.js +75 -3
  11. package/dist/commands/build.d.ts +6 -0
  12. package/dist/commands/build.js +26 -52
  13. package/dist/commands/check-stable.d.ts +2 -0
  14. package/dist/commands/check-stable.js +2 -2
  15. package/dist/commands/check.d.ts +2 -0
  16. package/dist/commands/check.js +2 -1
  17. package/dist/commands/deployed.d.ts +10 -0
  18. package/dist/commands/deployed.js +97 -0
  19. package/dist/commands/generate.d.ts +6 -0
  20. package/dist/commands/generate.js +124 -0
  21. package/dist/commands/lint.d.ts +2 -0
  22. package/dist/commands/lint.js +1 -1
  23. package/dist/helpers/autofix-motoko.d.ts +2 -0
  24. package/dist/helpers/autofix-motoko.js +44 -42
  25. package/dist/helpers/migrations.d.ts +1 -1
  26. package/dist/helpers/migrations.js +8 -4
  27. package/dist/helpers/moc-args.d.ts +25 -0
  28. package/dist/helpers/moc-args.js +58 -0
  29. package/dist/package.json +6 -6
  30. package/dist/tests/check-fix-utf8.test.d.ts +1 -0
  31. package/dist/tests/check-fix-utf8.test.js +38 -0
  32. package/dist/tests/check.test.js +21 -0
  33. package/dist/tests/deployed.test.d.ts +1 -0
  34. package/dist/tests/deployed.test.js +173 -0
  35. package/dist/tests/generate.test.d.ts +1 -0
  36. package/dist/tests/generate.test.js +107 -0
  37. package/dist/tests/helpers.d.ts +8 -0
  38. package/dist/tests/helpers.js +28 -3
  39. package/dist/tests/migrate.test.js +5 -19
  40. package/dist/types.d.ts +3 -0
  41. package/helpers/autofix-motoko.ts +60 -52
  42. package/helpers/migrations.ts +8 -3
  43. package/helpers/moc-args.ts +123 -0
  44. package/package.json +6 -6
  45. package/tests/__snapshots__/check-fix-utf8.test.ts.snap +27 -0
  46. package/tests/__snapshots__/deployed.test.ts.snap +10 -0
  47. package/tests/__snapshots__/generate.test.ts.snap +39 -0
  48. package/tests/check/fix-utf8/mops.toml +5 -0
  49. package/tests/check/fix-utf8/multibyte.mo +15 -0
  50. package/tests/check-fix-utf8.test.ts +51 -0
  51. package/tests/check.test.ts +24 -0
  52. package/tests/deployed/basic/main.mo +4 -0
  53. package/tests/deployed/basic/mops.toml +8 -0
  54. package/tests/deployed/multi/bar.mo +1 -0
  55. package/tests/deployed/multi/foo.mo +1 -0
  56. package/tests/deployed/multi/mops.toml +11 -0
  57. package/tests/deployed.test.ts +249 -0
  58. package/tests/generate/basic/candid/bar.did +4 -0
  59. package/tests/generate/basic/mops.toml +12 -0
  60. package/tests/generate/basic/src/Bar.mo +5 -0
  61. package/tests/generate/basic/src/Foo.mo +5 -0
  62. package/tests/generate/error/mops.toml +8 -0
  63. package/tests/generate/error/src/Broken.mo +5 -0
  64. package/tests/generate.test.ts +140 -0
  65. package/tests/helpers.ts +31 -3
  66. package/tests/migrate.test.ts +7 -22
  67. package/types.ts +3 -0
@@ -0,0 +1,5 @@
1
+ persistent actor {
2
+ public func call() : async NotAType {
3
+ 42;
4
+ };
5
+ };
@@ -0,0 +1,140 @@
1
+ import { afterEach, describe, expect, jest, test } from "@jest/globals";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { cp, rm } from "node:fs/promises";
4
+ import path from "path";
5
+ import { cli, cliSnapshot } from "./helpers";
6
+
7
+ const fixturesDir = path.join(import.meta.dirname, "generate");
8
+
9
+ describe("generate candid", () => {
10
+ jest.setTimeout(120_000);
11
+
12
+ const tempDirs: string[] = [];
13
+
14
+ async function makeTempFixture(fixture: string): Promise<string> {
15
+ const src = path.join(fixturesDir, fixture);
16
+ const dest = path.join(fixturesDir, `_tmp_${fixture}_${Date.now()}`);
17
+ await cp(src, dest, { recursive: true });
18
+ tempDirs.push(dest);
19
+ return dest;
20
+ }
21
+
22
+ afterEach(async () => {
23
+ for (const dir of tempDirs) {
24
+ await rm(dir, { recursive: true, force: true });
25
+ }
26
+ tempDirs.length = 0;
27
+ });
28
+
29
+ test("default path: writes <main-dir>/<name>.did and sets [canisters.<name>].candid", async () => {
30
+ const cwd = await makeTempFixture("basic");
31
+ const tomlBefore = readFileSync(path.join(cwd, "mops.toml"), "utf-8");
32
+ expect(tomlBefore).not.toMatch(/"src\/foo\.did"/);
33
+
34
+ await cliSnapshot(["generate", "candid", "foo"], { cwd }, 0);
35
+
36
+ const did = readFileSync(path.join(cwd, "src/foo.did"), "utf-8");
37
+ expect(did).toMatchSnapshot("src/foo.did");
38
+
39
+ const tomlAfter = readFileSync(path.join(cwd, "mops.toml"), "utf-8");
40
+ expect(tomlAfter).toMatch(/candid\s*=\s*"src\/foo\.did"/);
41
+ });
42
+
43
+ test("configured path: overwrites in place and does not touch mops.toml", async () => {
44
+ const cwd = await makeTempFixture("basic");
45
+ const tomlBefore = readFileSync(path.join(cwd, "mops.toml"), "utf-8");
46
+ const didBefore = readFileSync(path.join(cwd, "candid/bar.did"), "utf-8");
47
+ expect(didBefore).toMatch(/Hand-curated/);
48
+
49
+ await cliSnapshot(["generate", "candid", "bar"], { cwd }, 0);
50
+
51
+ const didAfter = readFileSync(path.join(cwd, "candid/bar.did"), "utf-8");
52
+ expect(didAfter).not.toMatch(/Hand-curated/);
53
+ expect(didAfter).toMatch(/greet/);
54
+ expect(readFileSync(path.join(cwd, "mops.toml"), "utf-8")).toBe(tomlBefore);
55
+ });
56
+
57
+ test("no canister names: processes all canisters", async () => {
58
+ const cwd = await makeTempFixture("basic");
59
+ await cliSnapshot(["generate", "candid"], { cwd }, 0);
60
+
61
+ expect(existsSync(path.join(cwd, "src/foo.did"))).toBe(true);
62
+ const barDid = readFileSync(path.join(cwd, "candid/bar.did"), "utf-8");
63
+ expect(barDid).toMatch(/greet/);
64
+ expect(barDid).not.toMatch(/Hand-curated/);
65
+ });
66
+
67
+ test("--output writes to the given path and does not touch mops.toml", async () => {
68
+ const cwd = await makeTempFixture("basic");
69
+ const outPath = path.join(cwd, "out/custom.did");
70
+ const tomlBefore = readFileSync(path.join(cwd, "mops.toml"), "utf-8");
71
+
72
+ const result = await cli(["generate", "candid", "foo", "-o", outPath], {
73
+ cwd,
74
+ });
75
+ expect(result.exitCode).toBe(0);
76
+ expect(existsSync(outPath)).toBe(true);
77
+ expect(readFileSync(outPath, "utf-8")).toMatch(/call/);
78
+ expect(existsSync(path.join(cwd, "src/foo.did"))).toBe(false);
79
+ expect(readFileSync(path.join(cwd, "mops.toml"), "utf-8")).toBe(tomlBefore);
80
+ });
81
+
82
+ test("--output rejected with multiple canisters", async () => {
83
+ const cwd = await makeTempFixture("basic");
84
+ const result = await cli(
85
+ ["generate", "candid", "foo", "bar", "-o", "x.did"],
86
+ { cwd },
87
+ );
88
+ expect(result.exitCode).toBe(1);
89
+ expect(result.stderr).toMatch(/single canister/i);
90
+ });
91
+
92
+ test("unknown canister name errors", async () => {
93
+ const cwd = await makeTempFixture("basic");
94
+ const result = await cli(["generate", "candid", "nope"], { cwd });
95
+ expect(result.exitCode).toBe(1);
96
+ expect(result.stderr).toMatch(/not found in mops\.toml/i);
97
+ });
98
+
99
+ test("moc failure: leaves destination and mops.toml untouched", async () => {
100
+ const cwd = await makeTempFixture("error");
101
+ const tomlBefore = readFileSync(path.join(cwd, "mops.toml"), "utf-8");
102
+
103
+ const result = await cli(["generate", "candid", "broken"], { cwd });
104
+ expect(result.exitCode).toBe(1);
105
+ expect(result.stderr).toMatch(/Failed to generate Candid/);
106
+ expect(existsSync(path.join(cwd, "src/broken.did"))).toBe(false);
107
+ expect(readFileSync(path.join(cwd, "mops.toml"), "utf-8")).toBe(tomlBefore);
108
+ });
109
+
110
+ test("rejects destination inside .mops/", async () => {
111
+ const cwd = await makeTempFixture("basic");
112
+ const result = await cli(
113
+ ["generate", "candid", "foo", "-o", ".mops/foo.did"],
114
+ { cwd },
115
+ );
116
+ expect(result.exitCode).toBe(1);
117
+ expect(result.stderr).toMatch(/\.mops\//);
118
+ expect(result.stderr).toMatch(/Refusing/i);
119
+ });
120
+
121
+ test("creates file when [canisters.<name>].candid points to a missing path", async () => {
122
+ const cwd = await makeTempFixture("basic");
123
+ // bar's candid path points to candid/bar.did — delete it first
124
+ await rm(path.join(cwd, "candid/bar.did"));
125
+
126
+ const result = await cli(["generate", "candid", "bar"], { cwd });
127
+ expect(result.exitCode).toBe(0);
128
+ expect(existsSync(path.join(cwd, "candid/bar.did"))).toBe(true);
129
+ });
130
+
131
+ test("--output collision with another canister's candid prints a warning", async () => {
132
+ const cwd = await makeTempFixture("basic");
133
+ const result = await cli(
134
+ ["generate", "candid", "foo", "-o", "candid/bar.did"],
135
+ { cwd },
136
+ );
137
+ expect(result.exitCode).toBe(0);
138
+ expect(result.stderr).toMatch(/collides with \[canisters\.bar\]\.candid/);
139
+ });
140
+ });
package/tests/helpers.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { expect } from "@jest/globals";
1
+ import { afterEach, expect } from "@jest/globals";
2
2
  import { execa } from "execa";
3
- import { dirname } from "path";
4
- import { fileURLToPath } from "url";
3
+ import { cp, rm } from "node:fs/promises";
4
+ import path, { dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
 
6
7
  export interface CliOptions {
7
8
  cwd?: string;
@@ -69,3 +70,30 @@ export const cliSnapshot = async (
69
70
  }).toMatchSnapshot();
70
71
  return result;
71
72
  };
73
+
74
+ /**
75
+ * Set up `afterEach` cleanup and return a `makeTempFixture(name)` function
76
+ * that copies a fixture subtree into a unique scratch dir under `fixturesDir`.
77
+ *
78
+ * Use in tests that mutate fixture files (toml edits, baseline file creation, etc.)
79
+ * so they don't pollute the source-controlled fixture.
80
+ */
81
+ export function useTempFixtures(fixturesDir: string) {
82
+ const tempDirs: string[] = [];
83
+
84
+ afterEach(async () => {
85
+ for (const dir of tempDirs) {
86
+ await rm(dir, { recursive: true, force: true });
87
+ }
88
+ tempDirs.length = 0;
89
+ });
90
+
91
+ return async (fixture: string): Promise<string> => {
92
+ const src = path.join(fixturesDir, fixture);
93
+ const suffix = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
94
+ const dest = path.join(fixturesDir, `_tmp_${fixture}_${suffix}`);
95
+ await cp(src, dest, { recursive: true });
96
+ tempDirs.push(dest);
97
+ return dest;
98
+ };
99
+ }
@@ -1,24 +1,16 @@
1
- import { describe, expect, test, afterEach } from "@jest/globals";
1
+ import { describe, expect, test } from "@jest/globals";
2
2
  import { readdirSync, readFileSync } from "node:fs";
3
- import { cp, rm, writeFile } from "node:fs/promises";
4
- import path from "path";
5
- import { cli, cliSnapshot, normalizePaths } from "./helpers";
3
+ import { rm, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { cli, cliSnapshot, normalizePaths, useTempFixtures } from "./helpers";
6
6
 
7
7
  const normalizeTimestamp = (text: string) =>
8
8
  text.replace(/\d{8}_\d{6}/g, "<TIMESTAMP>");
9
9
 
10
- const fixturesDir = path.join(import.meta.dirname, "migrate");
11
-
12
10
  describe("migrate", () => {
13
- const tempDirs: string[] = [];
14
-
15
- async function makeTempFixture(fixture: string): Promise<string> {
16
- const src = path.join(fixturesDir, fixture);
17
- const dest = path.join(fixturesDir, `_tmp_${fixture}_${Date.now()}`);
18
- await cp(src, dest, { recursive: true });
19
- tempDirs.push(dest);
20
- return dest;
21
- }
11
+ const makeTempFixture = useTempFixtures(
12
+ path.join(import.meta.dirname, "migrate"),
13
+ );
22
14
 
23
15
  async function patchMigrations(cwd: string, extra: string): Promise<void> {
24
16
  const tomlPath = path.join(cwd, "mops.toml");
@@ -32,13 +24,6 @@ describe("migrate", () => {
32
24
  );
33
25
  }
34
26
 
35
- afterEach(async () => {
36
- for (const dir of tempDirs) {
37
- await rm(dir, { recursive: true, force: true });
38
- }
39
- tempDirs.length = 0;
40
- });
41
-
42
27
  describe("migrate new", () => {
43
28
  test("creates a migration file with timestamp and template", async () => {
44
29
  const cwd = await makeTempFixture("basic");
package/types.ts CHANGED
@@ -27,6 +27,9 @@ export type Config = {
27
27
  outputDir?: string;
28
28
  args?: string[];
29
29
  };
30
+ deployed?: {
31
+ dir?: string;
32
+ };
30
33
  lint?: {
31
34
  args?: string[];
32
35
  rules?: string[];