ic-mops 2.5.0 → 2.6.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 (80) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/api/network.ts +1 -1
  3. package/bun.lock +1082 -78
  4. package/bundle/cli.tgz +0 -0
  5. package/cli.ts +1 -1
  6. package/commands/add.ts +4 -1
  7. package/commands/build.ts +7 -12
  8. package/commands/check.ts +20 -2
  9. package/commands/docs-coverage.ts +26 -21
  10. package/commands/install/install-dep.ts +5 -3
  11. package/commands/lint.ts +107 -10
  12. package/commands/publish.ts +24 -11
  13. package/commands/remove.ts +5 -2
  14. package/commands/self.ts +1 -1
  15. package/commands/sources.ts +3 -2
  16. package/commands/sync.ts +13 -16
  17. package/commands/test/test.ts +3 -3
  18. package/commands/update.ts +13 -7
  19. package/commands/watch/error-checker.ts +3 -8
  20. package/commands/watch/warning-checker.ts +3 -8
  21. package/dist/api/network.js +1 -1
  22. package/dist/cli.js +1 -1
  23. package/dist/commands/add.js +4 -1
  24. package/dist/commands/build.js +5 -10
  25. package/dist/commands/check.js +14 -2
  26. package/dist/commands/docs-coverage.js +22 -22
  27. package/dist/commands/install/install-dep.js +3 -3
  28. package/dist/commands/lint.d.ts +3 -0
  29. package/dist/commands/lint.js +75 -10
  30. package/dist/commands/publish.js +19 -11
  31. package/dist/commands/remove.js +5 -2
  32. package/dist/commands/self.js +1 -1
  33. package/dist/commands/sources.js +3 -2
  34. package/dist/commands/sync.js +9 -14
  35. package/dist/commands/test/test.js +3 -3
  36. package/dist/commands/update.js +9 -4
  37. package/dist/commands/watch/error-checker.js +3 -8
  38. package/dist/commands/watch/warning-checker.js +3 -8
  39. package/dist/helpers/find-changelog-entry.js +1 -1
  40. package/dist/integrity.js +9 -3
  41. package/dist/mops.js +3 -0
  42. package/dist/package.json +3 -5
  43. package/dist/release-cli.js +2 -2
  44. package/dist/resolve-packages.js +4 -4
  45. package/dist/tests/build.test.js +1 -1
  46. package/dist/tests/check.test.js +24 -0
  47. package/dist/tests/helpers.js +8 -1
  48. package/dist/tests/lint.test.js +28 -2
  49. package/dist/types.d.ts +2 -0
  50. package/dist/vessel.d.ts +1 -1
  51. package/dist/vessel.js +3 -2
  52. package/helpers/find-changelog-entry.ts +3 -1
  53. package/integrity.ts +12 -3
  54. package/mops.ts +7 -0
  55. package/package.json +3 -5
  56. package/release-cli.ts +2 -2
  57. package/resolve-packages.ts +6 -4
  58. package/tests/build.test.ts +1 -1
  59. package/tests/check/with-lint-fail/NoBoolSwitch.mo +8 -0
  60. package/tests/check/with-lint-fail/lints/no-bool-switch.toml +9 -0
  61. package/tests/check/with-lint-fail/mops.toml +9 -0
  62. package/tests/check/with-lint-pass/Ok.mo +5 -0
  63. package/tests/check/with-lint-pass/lints/no-bool-switch.toml +9 -0
  64. package/tests/check/with-lint-pass/mops.toml +9 -0
  65. package/tests/check.test.ts +28 -0
  66. package/tests/helpers.ts +9 -1
  67. package/tests/lint-config-rules/extra-rules/no-bool-switch.toml +9 -0
  68. package/tests/lint-config-rules/mops.toml +5 -0
  69. package/tests/lint-config-rules/src/NoBoolSwitch.mo +8 -0
  70. package/tests/lint-extends/mops.toml +8 -0
  71. package/tests/lint-extends/my-pkg/mops.toml +3 -0
  72. package/tests/lint-extends/my-pkg/rules/no-bool-switch.toml +9 -0
  73. package/tests/lint-extends/src/NoBoolSwitch.mo +8 -0
  74. package/tests/lint-extends-all/mops.toml +8 -0
  75. package/tests/lint-extends-all/src/NoBoolSwitch.mo +8 -0
  76. package/tests/lint-extends-ignored/mops.toml +8 -0
  77. package/tests/lint-extends-ignored/src/NoBoolSwitch.mo +8 -0
  78. package/tests/lint.test.ts +32 -2
  79. package/types.ts +2 -0
  80. package/vessel.ts +5 -3
package/bundle/cli.tgz CHANGED
Binary file
package/cli.ts CHANGED
@@ -322,7 +322,7 @@ program
322
322
  program
323
323
  .command("check [files...]")
324
324
  .description(
325
- "Check Motoko files for syntax errors and type issues. If no files are specified, checks all canister entrypoints from mops.toml. Also runs stable compatibility checks for canisters with [check-stable] configured",
325
+ "Check Motoko files for syntax errors and type issues. If no files are specified, checks all canister entrypoints from mops.toml. Also runs stable compatibility checks for canisters with [check-stable] configured, and runs linting if lintoko is configured in [toolchain] and rule directories are present",
326
326
  )
327
327
  .option("--verbose", "Verbose console output")
328
328
  .addOption(
package/commands/add.ts CHANGED
@@ -101,9 +101,12 @@ export async function add(
101
101
  }
102
102
 
103
103
  if (pkgDetails.repo) {
104
- await installFromGithub(pkgDetails.name, pkgDetails.repo, {
104
+ let res = await installFromGithub(pkgDetails.name, pkgDetails.repo, {
105
105
  verbose: verbose,
106
106
  });
107
+ if (!res) {
108
+ process.exit(1);
109
+ }
107
110
  } else if (!pkgDetails.path) {
108
111
  let res = await installMopsDep(pkgDetails.name, pkgDetails.version, {
109
112
  verbose: verbose,
package/commands/build.ts CHANGED
@@ -24,7 +24,7 @@ export async function build(
24
24
  canisterNames: string[] | undefined,
25
25
  options: Partial<BuildOptions>,
26
26
  ): Promise<void> {
27
- if (canisterNames?.length == 0) {
27
+ if (canisterNames?.length === 0) {
28
28
  cliError("No canisters specified to build");
29
29
  }
30
30
 
@@ -41,16 +41,11 @@ export async function build(
41
41
  }
42
42
 
43
43
  if (canisterNames) {
44
- canisterNames = canisterNames.filter((name) => name in canisters);
45
- if (canisterNames.length === 0) {
46
- throw new Error("No valid canister names specified");
47
- }
48
- for (let name of canisterNames) {
49
- if (!(name in canisters)) {
50
- cliError(
51
- `Motoko canister '${name}' not found in mops.toml configuration`,
52
- );
53
- }
44
+ let invalidNames = canisterNames.filter((name) => !(name in canisters));
45
+ if (invalidNames.length) {
46
+ cliError(
47
+ `Motoko canister(s) not found in mops.toml configuration: ${invalidNames.join(", ")}`,
48
+ );
54
49
  }
55
50
  }
56
51
 
@@ -184,7 +179,7 @@ export async function build(
184
179
 
185
180
  console.log(
186
181
  chalk.green(
187
- `\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length == 1 ? "" : "s"} successfully`,
182
+ `\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length === 1 ? "" : "s"} successfully`,
188
183
  ),
189
184
  );
190
185
  }
package/commands/check.ts CHANGED
@@ -3,7 +3,12 @@ import { existsSync } from "node:fs";
3
3
  import chalk from "chalk";
4
4
  import { execa } from "execa";
5
5
  import { cliError } from "../error.js";
6
- import { getGlobalMocArgs, readConfig, resolveConfigPath } from "../mops.js";
6
+ import {
7
+ getGlobalMocArgs,
8
+ getRootDir,
9
+ readConfig,
10
+ resolveConfigPath,
11
+ } from "../mops.js";
7
12
  import { autofixMotoko } from "../helpers/autofix-motoko.js";
8
13
  import { getMocSemVer } from "../helpers/get-moc-version.js";
9
14
  import {
@@ -13,6 +18,7 @@ import {
13
18
  import { runStableCheck } from "./check-stable.js";
14
19
  import { sourcesArgs } from "./sources.js";
15
20
  import { toolchain } from "./toolchain/index.js";
21
+ import { collectLintRules, lint } from "./lint.js";
16
22
 
17
23
  const MOC_ALL_LIBS_MIN_VERSION = "1.3.0";
18
24
 
@@ -31,7 +37,8 @@ export async function check(
31
37
  files: string | string[],
32
38
  options: Partial<CheckOptions> = {},
33
39
  ): Promise<void> {
34
- let fileList = Array.isArray(files) ? files : files ? [files] : [];
40
+ const explicitFiles = Array.isArray(files) ? files : files ? [files] : [];
41
+ let fileList = [...explicitFiles];
35
42
 
36
43
  const config = readConfig();
37
44
 
@@ -166,4 +173,15 @@ export async function check(
166
173
  options: { verbose: options.verbose, extraArgs: options.extraArgs },
167
174
  });
168
175
  }
176
+
177
+ if (config.toolchain?.lintoko) {
178
+ const rootDir = getRootDir();
179
+ const lintRules = await collectLintRules(config, rootDir);
180
+ await lint(undefined, {
181
+ verbose: options.verbose,
182
+ fix: options.fix,
183
+ rules: lintRules,
184
+ files: explicitFiles.length > 0 ? explicitFiles : undefined,
185
+ });
186
+ }
169
187
  }
@@ -1,7 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import chalk from "chalk";
3
3
  import { globSync } from "glob";
4
- import { JSDOM } from "jsdom";
5
4
  import { docs } from "./docs.js";
6
5
 
7
6
  export type DocsCoverageReporter =
@@ -30,12 +29,12 @@ export async function docsCoverage(options: Partial<DocsCoverageOptions> = {}) {
30
29
  await docs({
31
30
  source,
32
31
  output: docsDir,
33
- format: "html",
32
+ format: "adoc",
34
33
  silent: true,
35
34
  });
36
35
 
37
- let files = globSync(`${docsDir}/**/*.html`, {
38
- ignore: [`${docsDir}/**/index.html`],
36
+ let files = globSync(`${docsDir}/**/*.adoc`, {
37
+ ignore: [`${docsDir}/**/*.test.adoc`, `${docsDir}/test/**/*`],
39
38
  });
40
39
  let coverages = [];
41
40
 
@@ -88,35 +87,41 @@ export async function docsCoverage(options: Partial<DocsCoverageOptions> = {}) {
88
87
  }
89
88
 
90
89
  function docFileCoverage(file: string) {
91
- let dom = new JSDOM(readFileSync(file, "utf-8"));
90
+ let content = readFileSync(file, "utf-8");
92
91
 
93
- let module = dom.window.document.querySelector("h1")?.textContent || "";
92
+ // Module name is on the line after the [[module.*]] anchor
93
+ let module =
94
+ content.match(/^\[\[module\.[^\]]+\]\]\n= (.+)$/m)?.[1]?.trim() || "";
94
95
  let moduleFile = `${module}.mo`;
95
96
 
96
- let items = [...dom.window.document.querySelectorAll("h4")].map((h4) => {
97
- let id = h4.getAttribute("id")?.replace("type.", "");
98
- let type = h4.className
99
- .replace("-declaration", "")
100
- .replace("function", "func");
101
- let definition = h4.textContent;
102
- let comment = h4.parentElement?.querySelector("p + p")?.textContent;
97
+ // Split into per-declaration sections at every [[id]] that is NOT [[module.*]]
98
+ let sections = content.split(/^(?=\[\[(?!module\.))/m).slice(1);
99
+
100
+ let items = sections.map((section) => {
101
+ let rawId = section.match(/^\[\[([^\]]+)\]\]/)?.[1] ?? "";
102
+ let id = rawId.replace(/^type\./, "");
103
+ // mo-doc anchors types as [[type.X]]; classes/values have no prefix → "func"
104
+ let type = rawId.startsWith("type.") ? "type" : "func";
105
+ let definition = section.match(/^== (.+)$/m)?.[1]?.trim() ?? "";
106
+
107
+ // Text after the closing ---- is the doc comment (empty when undocumented).
108
+ // slice(2).join preserves any ---- that appears inside the comment itself.
109
+ let parts = section.split(/^----$/m);
110
+ let comment = parts.slice(2).join("----").trim();
111
+
103
112
  return {
104
113
  file: moduleFile,
105
114
  id,
106
115
  type,
107
116
  definition,
108
117
  comment,
109
- covered: (comment || "").length >= 5,
118
+ covered: comment.length >= 5,
110
119
  };
111
120
  });
112
121
 
113
- let coverage = 0;
114
- if (!items.length) {
115
- coverage = 100;
116
- } else {
117
- coverage =
118
- (items.filter((item) => item.covered).length / items.length) * 100;
119
- }
122
+ let coverage = !items.length
123
+ ? 100
124
+ : (items.filter((item) => item.covered).length / items.length) * 100;
120
125
 
121
126
  return { file: moduleFile, coverage, items };
122
127
  }
@@ -20,12 +20,11 @@ export async function installDep(
20
20
  parentPkgPath?: string,
21
21
  ): Promise<boolean> {
22
22
  if (dep.repo) {
23
- await installFromGithub(dep.name, dep.repo, {
23
+ return installFromGithub(dep.name, dep.repo, {
24
24
  silent,
25
25
  verbose,
26
26
  ignoreTransitive,
27
27
  });
28
- return true;
29
28
  } else if (dep.path) {
30
29
  let depPath = dep.path;
31
30
  parentPkgPath = parentPkgPath || getRootDir();
@@ -46,5 +45,8 @@ export async function installDep(
46
45
  });
47
46
  }
48
47
 
49
- return true;
48
+ console.warn(
49
+ `Warning: dependency "${dep.name}" has no version, repo, or path`,
50
+ );
51
+ return false;
50
52
  }
package/commands/lint.ts CHANGED
@@ -3,15 +3,103 @@ import { execa } from "execa";
3
3
  import { globSync } from "glob";
4
4
  import path from "node:path";
5
5
  import { cliError } from "../error.js";
6
- import { getRootDir, readConfig } from "../mops.js";
6
+ import {
7
+ formatDir,
8
+ formatGithubDir,
9
+ getDependencyType,
10
+ getRootDir,
11
+ readConfig,
12
+ } from "../mops.js";
13
+ import { resolvePackages } from "../resolve-packages.js";
7
14
  import { toolchain } from "./toolchain/index.js";
8
15
  import { MOTOKO_GLOB_CONFIG } from "../constants.js";
9
16
  import { existsSync } from "node:fs";
17
+ import { Config } from "../types.js";
18
+
19
+ async function resolveDepRules(
20
+ config: Config,
21
+ rootDir: string,
22
+ ): Promise<string[]> {
23
+ const ext = config.lint?.extends;
24
+ if (!ext) {
25
+ return [];
26
+ }
27
+
28
+ const resolvedPackages = await resolvePackages();
29
+ const rules: string[] = [];
30
+ const matched = new Set<string>();
31
+ const hasRules = new Set<string>();
32
+
33
+ for (const [name, version] of Object.entries(resolvedPackages)) {
34
+ if (ext !== true && !ext.includes(name)) {
35
+ continue;
36
+ }
37
+ matched.add(name);
38
+
39
+ const depType = getDependencyType(version);
40
+ let pkgDir: string;
41
+ if (depType === "local") {
42
+ pkgDir = version;
43
+ } else if (depType === "github") {
44
+ pkgDir = formatGithubDir(name, version);
45
+ } else {
46
+ pkgDir = formatDir(name, version);
47
+ }
48
+
49
+ const rulesDir = path.join(pkgDir, "rules");
50
+ if (existsSync(rulesDir)) {
51
+ rules.push(path.relative(rootDir, rulesDir));
52
+ hasRules.add(name);
53
+ }
54
+ }
55
+
56
+ if (Array.isArray(ext)) {
57
+ const unresolved = ext.filter((n) => !matched.has(n));
58
+ if (unresolved.length > 0) {
59
+ console.warn(
60
+ chalk.yellow(
61
+ `[lint] extends: package(s) not found in dependencies: ${unresolved.join(", ")}`,
62
+ ),
63
+ );
64
+ }
65
+ const noRulesDir = ext.filter((n) => matched.has(n) && !hasRules.has(n));
66
+ if (noRulesDir.length > 0) {
67
+ console.warn(
68
+ chalk.yellow(
69
+ `[lint] extends: package(s) have no rules/ directory: ${noRulesDir.join(", ")}`,
70
+ ),
71
+ );
72
+ }
73
+ }
74
+
75
+ return rules;
76
+ }
77
+
78
+ export async function collectLintRules(
79
+ config: Config,
80
+ rootDir: string,
81
+ ): Promise<string[]> {
82
+ const configRules = config.lint?.rules ?? [];
83
+ for (const d of configRules) {
84
+ if (!existsSync(path.join(rootDir, d))) {
85
+ cliError(
86
+ `[lint] rules: directory '${d}' not found. Check your mops.toml [lint] config.`,
87
+ );
88
+ }
89
+ }
90
+ const localRules =
91
+ configRules.length > 0
92
+ ? configRules
93
+ : ["lint", "lints"].filter((d) => existsSync(path.join(rootDir, d)));
94
+ const depRules = await resolveDepRules(config, rootDir);
95
+ return [...localRules, ...depRules];
96
+ }
10
97
 
11
98
  export interface LintOptions {
12
99
  verbose: boolean;
13
100
  fix: boolean;
14
101
  rules?: string[];
102
+ files?: string[];
15
103
  extraArgs: string[];
16
104
  }
17
105
 
@@ -25,13 +113,22 @@ export async function lint(
25
113
  ? await toolchain.bin("lintoko")
26
114
  : "lintoko";
27
115
 
28
- let globStr = filter ? `**/*${filter}*.mo` : "**/*.mo";
29
- let filesToLint = globSync(path.join(rootDir, globStr), {
30
- ...MOTOKO_GLOB_CONFIG,
31
- cwd: rootDir,
32
- });
33
- if (filesToLint.length === 0) {
34
- cliError(`No files found for filter '${filter}'`);
116
+ let filesToLint: string[];
117
+ if (options.files && options.files.length > 0) {
118
+ filesToLint = options.files;
119
+ } else {
120
+ let globStr = filter ? `**/*${filter}*.mo` : "**/*.mo";
121
+ filesToLint = globSync(path.join(rootDir, globStr), {
122
+ ...MOTOKO_GLOB_CONFIG,
123
+ cwd: rootDir,
124
+ });
125
+ if (filesToLint.length === 0) {
126
+ cliError(
127
+ filter
128
+ ? `No files found for filter '${filter}'`
129
+ : "No .mo files found in the project",
130
+ );
131
+ }
35
132
  }
36
133
 
37
134
  let args: string[] = [];
@@ -42,9 +139,9 @@ export async function lint(
42
139
  args.push("--fix");
43
140
  }
44
141
  const rules =
45
- options.rules && options.rules.length > 0
142
+ options.rules !== undefined
46
143
  ? options.rules
47
- : ["lint", "lints"].filter((d) => existsSync(path.join(rootDir, d)));
144
+ : await collectLintRules(config, rootDir);
48
145
  rules.forEach((rule) => args.push("--rules", rule));
49
146
 
50
147
  if (config.lint?.args) {
@@ -271,6 +271,7 @@ export async function publish(
271
271
  "README.md",
272
272
  "LICENSE",
273
273
  "NOTICE",
274
+ "rules/*.toml",
274
275
  "!.mops/**",
275
276
  "!test/**",
276
277
  "!tests/**",
@@ -331,6 +332,16 @@ export async function publish(
331
332
  }
332
333
  }
333
334
 
335
+ // pre-flight file count check (must match MAX_PACKAGE_FILES in PackagePublisher.mo)
336
+ const FILE_LIMIT = 1000;
337
+ if (files.length > FILE_LIMIT) {
338
+ console.log(
339
+ chalk.red("Error: ") +
340
+ `Too many files (${files.length}). Maximum is ${FILE_LIMIT}.`,
341
+ );
342
+ process.exit(1);
343
+ }
344
+
334
345
  // parse changelog
335
346
  console.log("Parsing CHANGELOG.md...");
336
347
  let changelog = parseChangelog(config.package.version);
@@ -398,11 +409,11 @@ export async function publish(
398
409
  console.log(chalk.red("Error: ") + publishing.err);
399
410
  process.exit(1);
400
411
  }
401
- let puiblishingId = publishing.ok;
412
+ let publishingId = publishing.ok;
402
413
 
403
414
  // upload test stats
404
415
  if (options.test) {
405
- await actor.uploadTestStats(puiblishingId, {
416
+ await actor.uploadTestStats(publishingId, {
406
417
  passed: BigInt(reporter.passed),
407
418
  passedNames: reporter.passedNamesFlat,
408
419
  });
@@ -410,17 +421,17 @@ export async function publish(
410
421
 
411
422
  // upload benchmarks
412
423
  if (options.bench) {
413
- await actor.uploadBenchmarks(puiblishingId, benchmarks);
424
+ await actor.uploadBenchmarks(publishingId, benchmarks);
414
425
  }
415
426
 
416
427
  // upload changelog
417
428
  if (changelog) {
418
- await actor.uploadNotes(puiblishingId, changelog);
429
+ await actor.uploadNotes(publishingId, changelog);
419
430
  }
420
431
 
421
432
  // upload docs coverage
422
433
  if (options.docs) {
423
- await actor.uploadDocsCoverage(puiblishingId, docsCov);
434
+ await actor.uploadDocsCoverage(publishingId, docsCov);
424
435
  }
425
436
 
426
437
  // upload files
@@ -438,7 +449,7 @@ export async function publish(
438
449
  }
439
450
 
440
451
  let res = await actor.startFileUpload(
441
- puiblishingId,
452
+ publishingId,
442
453
  file,
443
454
  BigInt(chunkCount),
444
455
  firstChunk,
@@ -453,7 +464,7 @@ export async function publish(
453
464
  let start = i * chunkSize;
454
465
  let chunk = Array.from(content.slice(start, start + chunkSize));
455
466
  let res = await actor.uploadFileChunk(
456
- puiblishingId,
467
+ publishingId,
457
468
  fileId,
458
469
  BigInt(i),
459
470
  chunk,
@@ -474,7 +485,7 @@ export async function publish(
474
485
  progress();
475
486
  logUpdate.done();
476
487
 
477
- let res = await actor.finishPublish(puiblishingId);
488
+ let res = await actor.finishPublish(publishingId);
478
489
  if ("err" in res) {
479
490
  console.log(chalk.red("Error: ") + res.err);
480
491
  process.exit(1);
@@ -513,6 +524,8 @@ function parseChangelog(version: string): string {
513
524
  return changelog || "";
514
525
  }
515
526
 
527
+ type GitHubRelease = { message?: string; body?: string };
528
+
516
529
  async function fetchGitHubReleaseNotes(
517
530
  repo: string,
518
531
  version: string,
@@ -521,13 +534,13 @@ async function fetchGitHubReleaseNotes(
521
534
  let res = await fetch(
522
535
  `https://api.github.com/repos${repoPath}/releases/tags/${version}`,
523
536
  );
524
- let release = await res.json();
537
+ let release = (await res.json()) as GitHubRelease;
525
538
 
526
539
  if (release.message === "Not Found") {
527
540
  res = await fetch(
528
541
  `https://api.github.com/repos${repoPath}/releases/tags/v${version}`,
529
542
  );
530
- release = await res.json();
543
+ release = (await res.json()) as GitHubRelease;
531
544
 
532
545
  if (release.message === "Not Found") {
533
546
  console.log(
@@ -539,5 +552,5 @@ async function fetchGitHubReleaseNotes(
539
552
  }
540
553
  }
541
554
 
542
- return release.body;
555
+ return release.body ?? "";
543
556
  }
@@ -65,7 +65,10 @@ export async function remove(
65
65
  let config = readConfig(configFile);
66
66
  let deps: Dependency[] = Object.values(config.dependencies || {})
67
67
  .map((dep) => {
68
- return [dep, ...getTransitiveDependenciesOf(dep.name, dep.version)];
68
+ return [
69
+ dep,
70
+ ...getTransitiveDependenciesOf(dep.name, dep.version, dep.repo),
71
+ ];
69
72
  })
70
73
  .flat();
71
74
  return deps;
@@ -97,7 +100,7 @@ export async function remove(
97
100
  // transitive deps of this package (including itself)
98
101
  let transitiveDepsOfPackage = [
99
102
  pkgDetails,
100
- ...getTransitiveDependenciesOf(name, version),
103
+ ...getTransitiveDependenciesOf(name, version, pkgDetails.repo),
101
104
  ];
102
105
 
103
106
  // remove local cache
package/commands/self.ts CHANGED
@@ -29,7 +29,7 @@ function detectPackageManager() {
29
29
 
30
30
  export async function getLatestVersion() {
31
31
  let res = await fetch(url + "/tags/latest");
32
- return res.text();
32
+ return (await res.text()).trim();
33
33
  }
34
34
 
35
35
  export async function update() {
@@ -38,8 +38,9 @@ export async function sourcesArgs({
38
38
 
39
39
  // append baseDir
40
40
  let pkgBaseDir;
41
- if (fs.existsSync(path.join(pkgDir, "mops.toml"))) {
42
- let config = readConfig(path.join(pkgDir, "mops.toml"));
41
+ let resolvedMopsToml = path.resolve(cwd, pkgDir, "mops.toml");
42
+ if (fs.existsSync(resolvedMopsToml)) {
43
+ let config = readConfig(resolvedMopsToml);
43
44
  pkgBaseDir = path.join(pkgDir, config.package?.baseDir || "src");
44
45
  } else {
45
46
  pkgBaseDir = path.join(pkgDir, "src");
package/commands/sync.ts CHANGED
@@ -8,6 +8,7 @@ import { remove } from "./remove.js";
8
8
  import { checkIntegrity } from "../integrity.js";
9
9
  import { getMocPath } from "../helpers/get-moc-path.js";
10
10
  import { MOTOKO_IGNORE_PATTERNS } from "../constants.js";
11
+ import { getDepName } from "../helpers/get-dep-name.js";
11
12
 
12
13
  type SyncOptions = {
13
14
  lock?: "update" | "ignore";
@@ -80,26 +81,22 @@ async function getUsedPackages(): Promise<string[]> {
80
81
 
81
82
  async function getMissingPackages(): Promise<string[]> {
82
83
  let config = readConfig();
83
- let allDeps = [
84
- ...Object.keys(config.dependencies || {}),
85
- ...Object.keys(config["dev-dependencies"] || {}),
86
- ];
87
- let missing = new Set(await getUsedPackages());
88
- for (let pkg of allDeps) {
89
- missing.delete(pkg);
90
- }
91
- return [...missing];
84
+ let allDepNames = new Set(
85
+ [
86
+ ...Object.keys(config.dependencies || {}),
87
+ ...Object.keys(config["dev-dependencies"] || {}),
88
+ ].map((key) => getDepName(key)),
89
+ );
90
+ let used = await getUsedPackages();
91
+ return used.filter((pkg) => !allDepNames.has(pkg));
92
92
  }
93
93
 
94
94
  async function getUnusedPackages(): Promise<string[]> {
95
95
  let config = readConfig();
96
- let allDeps = new Set([
96
+ let allDeps = [
97
97
  ...Object.keys(config.dependencies || {}),
98
98
  ...Object.keys(config["dev-dependencies"] || {}),
99
- ]);
100
- let used = await getUsedPackages();
101
- for (let pkg of used) {
102
- allDeps.delete(pkg);
103
- }
104
- return [...allDeps];
99
+ ];
100
+ let used = new Set(await getUsedPackages());
101
+ return allDeps.filter((key) => !used.has(getDepName(key)));
105
102
  }
@@ -12,7 +12,7 @@ import debounce from "debounce";
12
12
  import { SemVer } from "semver";
13
13
  import { ActorMethod } from "@icp-sdk/core/agent";
14
14
 
15
- import { sources } from "../sources.js";
15
+ import { sourcesArgs } from "../sources.js";
16
16
  import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
17
17
  import { parallel } from "../../parallel.js";
18
18
 
@@ -231,7 +231,7 @@ export async function testWithReporter(
231
231
  reporter.addFiles(files);
232
232
 
233
233
  let config = readConfig();
234
- let sourcesArr = await sources();
234
+ let sourcesArr = (await sourcesArgs()).flat();
235
235
  let globalMocArgs = getGlobalMocArgs(config);
236
236
 
237
237
  if (!mocPath) {
@@ -298,7 +298,7 @@ export async function testWithReporter(
298
298
  let mocArgs = [
299
299
  "--hide-warnings",
300
300
  "--error-detail=2",
301
- ...sourcesArr.join(" ").split(" "),
301
+ ...sourcesArr,
302
302
  ...globalMocArgs,
303
303
  file,
304
304
  ].filter((x) => x);
@@ -42,12 +42,18 @@ export async function update(pkg?: string, { lock }: UpdateOptions = {}) {
42
42
  for (let dep of githubDeps) {
43
43
  let { org, gitName, branch, commitHash } = parseGithubURL(dep.repo || "");
44
44
  let dev = !!config["dev-dependencies"]?.[dep.name];
45
- let commit = await getGithubCommit(`${org}/${gitName}`, branch);
46
- if (commit.sha !== commitHash) {
47
- await add(
48
- `https://github.com/${org}/${gitName}#${branch}@${commit.sha}`,
49
- { dev },
50
- dep.name,
45
+ try {
46
+ let commit = await getGithubCommit(`${org}/${gitName}`, branch);
47
+ if (commit.sha !== commitHash) {
48
+ await add(
49
+ `https://github.com/${org}/${gitName}#${branch}@${commit.sha}`,
50
+ { dev, lock },
51
+ dep.name,
52
+ );
53
+ }
54
+ } catch (err: any) {
55
+ console.log(
56
+ chalk.red("Error: ") + `Failed to update ${dep.name}: ${err.message}`,
51
57
  );
52
58
  }
53
59
  }
@@ -87,7 +93,7 @@ export async function update(pkg?: string, { lock }: UpdateOptions = {}) {
87
93
  );
88
94
  }) || dep[0];
89
95
 
90
- await add(`${dep[0]}@${dep[2]}`, { dev }, asName);
96
+ await add(`${dep[0]}@${dep[2]}`, { dev, lock }, asName);
91
97
  }
92
98
  }
93
99
 
@@ -5,7 +5,7 @@ import chalk from "chalk";
5
5
 
6
6
  import { getMocPath } from "../../helpers/get-moc-path.js";
7
7
  import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
8
- import { sources } from "../sources.js";
8
+ import { sourcesArgs } from "../sources.js";
9
9
  import { parallel } from "../../parallel.js";
10
10
  import { globMoFiles } from "./globMoFiles.js";
11
11
 
@@ -43,7 +43,7 @@ export class ErrorChecker {
43
43
 
44
44
  let rootDir = getRootDir();
45
45
  let mocPath = getMocPath();
46
- let deps = await sources({ cwd: rootDir });
46
+ let deps = (await sourcesArgs({ cwd: rootDir })).flat();
47
47
  let globalMocArgs = getGlobalMocArgs(readConfig());
48
48
 
49
49
  let paths = globMoFiles(rootDir);
@@ -55,12 +55,7 @@ export class ErrorChecker {
55
55
  try {
56
56
  await promisify(execFile)(
57
57
  mocPath,
58
- [
59
- "--check",
60
- ...deps.flatMap((x) => x.split(" ")),
61
- ...globalMocArgs,
62
- file,
63
- ],
58
+ ["--check", ...deps, ...globalMocArgs, file],
64
59
  { cwd: rootDir },
65
60
  );
66
61
  } catch (error: any) {