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
@@ -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 { ErrorChecker } from "./error-checker.js";
10
10
  import { parallel } from "../../parallel.js";
11
11
  import { globMoFiles } from "./globMoFiles.js";
@@ -69,7 +69,7 @@ export class WarningChecker {
69
69
 
70
70
  let rootDir = getRootDir();
71
71
  let mocPath = getMocPath();
72
- let deps = await sources({ cwd: rootDir });
72
+ let deps = (await sourcesArgs({ cwd: rootDir })).flat();
73
73
  let globalMocArgs = getGlobalMocArgs(readConfig());
74
74
  let paths = globMoFiles(rootDir);
75
75
 
@@ -83,12 +83,7 @@ export class WarningChecker {
83
83
 
84
84
  let { stderr } = await promisify(execFile)(
85
85
  mocPath,
86
- [
87
- "--check",
88
- ...deps.flatMap((x) => x.split(" ")),
89
- ...globalMocArgs,
90
- file,
91
- ],
86
+ ["--check", ...deps, ...globalMocArgs, file],
92
87
  { cwd: rootDir, signal },
93
88
  ).catch((error) => {
94
89
  if (error.code === "ABORT_ERR") {
@@ -1,5 +1,5 @@
1
1
  export function getNetwork() {
2
- return globalThis.MOPS_NETWORK || "ic";
2
+ return process.env["MOPS_NETWORK"] || globalThis.MOPS_NETWORK || "ic";
3
3
  }
4
4
  export function getEndpoint(network) {
5
5
  let endpoint;
package/dist/cli.js CHANGED
@@ -255,7 +255,7 @@ program
255
255
  // check
256
256
  program
257
257
  .command("check [files...]")
258
- .description("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")
258
+ .description("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")
259
259
  .option("--verbose", "Verbose console output")
260
260
  .addOption(new Option("--fix", "Apply autofixes to all files, including transitively imported ones"))
261
261
  .allowUnknownOption(true)
@@ -77,9 +77,12 @@ export async function add(name, { verbose = false, dev = false, lock } = {}, asN
77
77
  };
78
78
  }
79
79
  if (pkgDetails.repo) {
80
- await installFromGithub(pkgDetails.name, pkgDetails.repo, {
80
+ let res = await installFromGithub(pkgDetails.name, pkgDetails.repo, {
81
81
  verbose: verbose,
82
82
  });
83
+ if (!res) {
84
+ process.exit(1);
85
+ }
83
86
  }
84
87
  else if (!pkgDetails.path) {
85
88
  let res = await installMopsDep(pkgDetails.name, pkgDetails.version, {
@@ -12,7 +12,7 @@ import { sourcesArgs } from "./sources.js";
12
12
  import { toolchain } from "./toolchain/index.js";
13
13
  export const DEFAULT_BUILD_OUTPUT_DIR = ".mops/.build";
14
14
  export async function build(canisterNames, options) {
15
- if (canisterNames?.length == 0) {
15
+ if (canisterNames?.length === 0) {
16
16
  cliError("No canisters specified to build");
17
17
  }
18
18
  let config = readConfig();
@@ -26,14 +26,9 @@ export async function build(canisterNames, options) {
26
26
  cliError(`No Motoko canisters found in mops.toml configuration`);
27
27
  }
28
28
  if (canisterNames) {
29
- canisterNames = canisterNames.filter((name) => name in canisters);
30
- if (canisterNames.length === 0) {
31
- throw new Error("No valid canister names specified");
32
- }
33
- for (let name of canisterNames) {
34
- if (!(name in canisters)) {
35
- cliError(`Motoko canister '${name}' not found in mops.toml configuration`);
36
- }
29
+ let invalidNames = canisterNames.filter((name) => !(name in canisters));
30
+ if (invalidNames.length) {
31
+ cliError(`Motoko canister(s) not found in mops.toml configuration: ${invalidNames.join(", ")}`);
37
32
  }
38
33
  }
39
34
  if (!(await exists(outputDir))) {
@@ -131,7 +126,7 @@ export async function build(canisterNames, options) {
131
126
  cliError(`Error while compiling canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`);
132
127
  }
133
128
  }
134
- console.log(chalk.green(`\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length == 1 ? "" : "s"} successfully`));
129
+ console.log(chalk.green(`\n✓ Built ${Object.keys(filteredCanisters).length} canister${Object.keys(filteredCanisters).length === 1 ? "" : "s"} successfully`));
135
130
  }
136
131
  const managedFlags = {
137
132
  "-o": "use [build].outputDir in mops.toml or --output flag instead",
@@ -3,20 +3,22 @@ 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 { getGlobalMocArgs, getRootDir, readConfig, resolveConfigPath, } from "../mops.js";
7
7
  import { autofixMotoko } from "../helpers/autofix-motoko.js";
8
8
  import { getMocSemVer } from "../helpers/get-moc-version.js";
9
9
  import { resolveCanisterConfigs, resolveCanisterEntrypoints, } from "../helpers/resolve-canisters.js";
10
10
  import { runStableCheck } from "./check-stable.js";
11
11
  import { sourcesArgs } from "./sources.js";
12
12
  import { toolchain } from "./toolchain/index.js";
13
+ import { collectLintRules, lint } from "./lint.js";
13
14
  const MOC_ALL_LIBS_MIN_VERSION = "1.3.0";
14
15
  function supportsAllLibsFlag() {
15
16
  const version = getMocSemVer();
16
17
  return version ? version.compare(MOC_ALL_LIBS_MIN_VERSION) >= 0 : false;
17
18
  }
18
19
  export async function check(files, options = {}) {
19
- let fileList = Array.isArray(files) ? files : files ? [files] : [];
20
+ const explicitFiles = Array.isArray(files) ? files : files ? [files] : [];
21
+ let fileList = [...explicitFiles];
20
22
  const config = readConfig();
21
23
  if (fileList.length === 0) {
22
24
  fileList = resolveCanisterEntrypoints(config).map(resolveConfigPath);
@@ -115,4 +117,14 @@ export async function check(files, options = {}) {
115
117
  options: { verbose: options.verbose, extraArgs: options.extraArgs },
116
118
  });
117
119
  }
120
+ if (config.toolchain?.lintoko) {
121
+ const rootDir = getRootDir();
122
+ const lintRules = await collectLintRules(config, rootDir);
123
+ await lint(undefined, {
124
+ verbose: options.verbose,
125
+ fix: options.fix,
126
+ rules: lintRules,
127
+ files: explicitFiles.length > 0 ? explicitFiles : undefined,
128
+ });
129
+ }
118
130
  }
@@ -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
  export async function docsCoverage(options = {}) {
7
6
  let docsDir = ".mops/.docs";
@@ -14,11 +13,11 @@ export async function docsCoverage(options = {}) {
14
13
  await docs({
15
14
  source,
16
15
  output: docsDir,
17
- format: "html",
16
+ format: "adoc",
18
17
  silent: true,
19
18
  });
20
- let files = globSync(`${docsDir}/**/*.html`, {
21
- ignore: [`${docsDir}/**/index.html`],
19
+ let files = globSync(`${docsDir}/**/*.adoc`, {
20
+ ignore: [`${docsDir}/**/*.test.adoc`, `${docsDir}/test/**/*`],
22
21
  });
23
22
  let coverages = [];
24
23
  for (let file of files) {
@@ -58,33 +57,34 @@ export async function docsCoverage(options = {}) {
58
57
  return totalCoverage;
59
58
  }
60
59
  function docFileCoverage(file) {
61
- let dom = new JSDOM(readFileSync(file, "utf-8"));
62
- let module = dom.window.document.querySelector("h1")?.textContent || "";
60
+ let content = readFileSync(file, "utf-8");
61
+ // Module name is on the line after the [[module.*]] anchor
62
+ let module = content.match(/^\[\[module\.[^\]]+\]\]\n= (.+)$/m)?.[1]?.trim() || "";
63
63
  let moduleFile = `${module}.mo`;
64
- let items = [...dom.window.document.querySelectorAll("h4")].map((h4) => {
65
- let id = h4.getAttribute("id")?.replace("type.", "");
66
- let type = h4.className
67
- .replace("-declaration", "")
68
- .replace("function", "func");
69
- let definition = h4.textContent;
70
- let comment = h4.parentElement?.querySelector("p + p")?.textContent;
64
+ // Split into per-declaration sections at every [[id]] that is NOT [[module.*]]
65
+ let sections = content.split(/^(?=\[\[(?!module\.))/m).slice(1);
66
+ let items = sections.map((section) => {
67
+ let rawId = section.match(/^\[\[([^\]]+)\]\]/)?.[1] ?? "";
68
+ let id = rawId.replace(/^type\./, "");
69
+ // mo-doc anchors types as [[type.X]]; classes/values have no prefix → "func"
70
+ let type = rawId.startsWith("type.") ? "type" : "func";
71
+ let definition = section.match(/^== (.+)$/m)?.[1]?.trim() ?? "";
72
+ // Text after the closing ---- is the doc comment (empty when undocumented).
73
+ // slice(2).join preserves any ---- that appears inside the comment itself.
74
+ let parts = section.split(/^----$/m);
75
+ let comment = parts.slice(2).join("----").trim();
71
76
  return {
72
77
  file: moduleFile,
73
78
  id,
74
79
  type,
75
80
  definition,
76
81
  comment,
77
- covered: (comment || "").length >= 5,
82
+ covered: comment.length >= 5,
78
83
  };
79
84
  });
80
- let coverage = 0;
81
- if (!items.length) {
82
- coverage = 100;
83
- }
84
- else {
85
- coverage =
86
- (items.filter((item) => item.covered).length / items.length) * 100;
87
- }
85
+ let coverage = !items.length
86
+ ? 100
87
+ : (items.filter((item) => item.covered).length / items.length) * 100;
88
88
  return { file: moduleFile, coverage, items };
89
89
  }
90
90
  function colorizeCoverage(coverage) {
@@ -7,12 +7,11 @@ import { getRootDir } from "../../mops.js";
7
7
  // returns false if failed
8
8
  export async function installDep(dep, { verbose, silent, threads, ignoreTransitive } = {}, parentPkgPath) {
9
9
  if (dep.repo) {
10
- await installFromGithub(dep.name, dep.repo, {
10
+ return installFromGithub(dep.name, dep.repo, {
11
11
  silent,
12
12
  verbose,
13
13
  ignoreTransitive,
14
14
  });
15
- return true;
16
15
  }
17
16
  else if (dep.path) {
18
17
  let depPath = dep.path;
@@ -34,5 +33,6 @@ export async function installDep(dep, { verbose, silent, threads, ignoreTransiti
34
33
  ignoreTransitive,
35
34
  });
36
35
  }
37
- return true;
36
+ console.warn(`Warning: dependency "${dep.name}" has no version, repo, or path`);
37
+ return false;
38
38
  }
@@ -1,7 +1,10 @@
1
+ import { Config } from "../types.js";
2
+ export declare function collectLintRules(config: Config, rootDir: string): Promise<string[]>;
1
3
  export interface LintOptions {
2
4
  verbose: boolean;
3
5
  fix: boolean;
4
6
  rules?: string[];
7
+ files?: string[];
5
8
  extraArgs: string[];
6
9
  }
7
10
  export declare function lint(filter: string | undefined, options: Partial<LintOptions>): Promise<void>;
@@ -3,23 +3,88 @@ 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 { formatDir, formatGithubDir, getDependencyType, getRootDir, readConfig, } from "../mops.js";
7
+ import { resolvePackages } from "../resolve-packages.js";
7
8
  import { toolchain } from "./toolchain/index.js";
8
9
  import { MOTOKO_GLOB_CONFIG } from "../constants.js";
9
10
  import { existsSync } from "node:fs";
11
+ async function resolveDepRules(config, rootDir) {
12
+ const ext = config.lint?.extends;
13
+ if (!ext) {
14
+ return [];
15
+ }
16
+ const resolvedPackages = await resolvePackages();
17
+ const rules = [];
18
+ const matched = new Set();
19
+ const hasRules = new Set();
20
+ for (const [name, version] of Object.entries(resolvedPackages)) {
21
+ if (ext !== true && !ext.includes(name)) {
22
+ continue;
23
+ }
24
+ matched.add(name);
25
+ const depType = getDependencyType(version);
26
+ let pkgDir;
27
+ if (depType === "local") {
28
+ pkgDir = version;
29
+ }
30
+ else if (depType === "github") {
31
+ pkgDir = formatGithubDir(name, version);
32
+ }
33
+ else {
34
+ pkgDir = formatDir(name, version);
35
+ }
36
+ const rulesDir = path.join(pkgDir, "rules");
37
+ if (existsSync(rulesDir)) {
38
+ rules.push(path.relative(rootDir, rulesDir));
39
+ hasRules.add(name);
40
+ }
41
+ }
42
+ if (Array.isArray(ext)) {
43
+ const unresolved = ext.filter((n) => !matched.has(n));
44
+ if (unresolved.length > 0) {
45
+ console.warn(chalk.yellow(`[lint] extends: package(s) not found in dependencies: ${unresolved.join(", ")}`));
46
+ }
47
+ const noRulesDir = ext.filter((n) => matched.has(n) && !hasRules.has(n));
48
+ if (noRulesDir.length > 0) {
49
+ console.warn(chalk.yellow(`[lint] extends: package(s) have no rules/ directory: ${noRulesDir.join(", ")}`));
50
+ }
51
+ }
52
+ return rules;
53
+ }
54
+ export async function collectLintRules(config, rootDir) {
55
+ const configRules = config.lint?.rules ?? [];
56
+ for (const d of configRules) {
57
+ if (!existsSync(path.join(rootDir, d))) {
58
+ cliError(`[lint] rules: directory '${d}' not found. Check your mops.toml [lint] config.`);
59
+ }
60
+ }
61
+ const localRules = configRules.length > 0
62
+ ? configRules
63
+ : ["lint", "lints"].filter((d) => existsSync(path.join(rootDir, d)));
64
+ const depRules = await resolveDepRules(config, rootDir);
65
+ return [...localRules, ...depRules];
66
+ }
10
67
  export async function lint(filter, options) {
11
68
  let config = readConfig();
12
69
  let rootDir = getRootDir();
13
70
  let lintokoBinPath = config.toolchain?.lintoko
14
71
  ? await toolchain.bin("lintoko")
15
72
  : "lintoko";
16
- let globStr = filter ? `**/*${filter}*.mo` : "**/*.mo";
17
- let filesToLint = globSync(path.join(rootDir, globStr), {
18
- ...MOTOKO_GLOB_CONFIG,
19
- cwd: rootDir,
20
- });
21
- if (filesToLint.length === 0) {
22
- cliError(`No files found for filter '${filter}'`);
73
+ let filesToLint;
74
+ if (options.files && options.files.length > 0) {
75
+ filesToLint = options.files;
76
+ }
77
+ else {
78
+ let globStr = filter ? `**/*${filter}*.mo` : "**/*.mo";
79
+ filesToLint = globSync(path.join(rootDir, globStr), {
80
+ ...MOTOKO_GLOB_CONFIG,
81
+ cwd: rootDir,
82
+ });
83
+ if (filesToLint.length === 0) {
84
+ cliError(filter
85
+ ? `No files found for filter '${filter}'`
86
+ : "No .mo files found in the project");
87
+ }
23
88
  }
24
89
  let args = [];
25
90
  if (options.verbose) {
@@ -28,9 +93,9 @@ export async function lint(filter, options) {
28
93
  if (options.fix) {
29
94
  args.push("--fix");
30
95
  }
31
- const rules = options.rules && options.rules.length > 0
96
+ const rules = options.rules !== undefined
32
97
  ? options.rules
33
- : ["lint", "lints"].filter((d) => existsSync(path.join(rootDir, d)));
98
+ : await collectLintRules(config, rootDir);
34
99
  rules.forEach((rule) => args.push("--rules", rule));
35
100
  if (config.lint?.args) {
36
101
  if (typeof config.lint.args === "string") {
@@ -210,6 +210,7 @@ export async function publish(options = {}) {
210
210
  "README.md",
211
211
  "LICENSE",
212
212
  "NOTICE",
213
+ "rules/*.toml",
213
214
  "!.mops/**",
214
215
  "!test/**",
215
216
  "!tests/**",
@@ -261,6 +262,13 @@ export async function publish(options = {}) {
261
262
  process.exit(1);
262
263
  }
263
264
  }
265
+ // pre-flight file count check (must match MAX_PACKAGE_FILES in PackagePublisher.mo)
266
+ const FILE_LIMIT = 1000;
267
+ if (files.length > FILE_LIMIT) {
268
+ console.log(chalk.red("Error: ") +
269
+ `Too many files (${files.length}). Maximum is ${FILE_LIMIT}.`);
270
+ process.exit(1);
271
+ }
264
272
  // parse changelog
265
273
  console.log("Parsing CHANGELOG.md...");
266
274
  let changelog = parseChangelog(config.package.version);
@@ -316,25 +324,25 @@ export async function publish(options = {}) {
316
324
  console.log(chalk.red("Error: ") + publishing.err);
317
325
  process.exit(1);
318
326
  }
319
- let puiblishingId = publishing.ok;
327
+ let publishingId = publishing.ok;
320
328
  // upload test stats
321
329
  if (options.test) {
322
- await actor.uploadTestStats(puiblishingId, {
330
+ await actor.uploadTestStats(publishingId, {
323
331
  passed: BigInt(reporter.passed),
324
332
  passedNames: reporter.passedNamesFlat,
325
333
  });
326
334
  }
327
335
  // upload benchmarks
328
336
  if (options.bench) {
329
- await actor.uploadBenchmarks(puiblishingId, benchmarks);
337
+ await actor.uploadBenchmarks(publishingId, benchmarks);
330
338
  }
331
339
  // upload changelog
332
340
  if (changelog) {
333
- await actor.uploadNotes(puiblishingId, changelog);
341
+ await actor.uploadNotes(publishingId, changelog);
334
342
  }
335
343
  // upload docs coverage
336
344
  if (options.docs) {
337
- await actor.uploadDocsCoverage(puiblishingId, docsCov);
345
+ await actor.uploadDocsCoverage(publishingId, docsCov);
338
346
  }
339
347
  // upload files
340
348
  await parallel(8, files, async (file) => {
@@ -347,7 +355,7 @@ export async function publish(options = {}) {
347
355
  if (file === docsFile) {
348
356
  file = path.basename(file);
349
357
  }
350
- let res = await actor.startFileUpload(puiblishingId, file, BigInt(chunkCount), firstChunk);
358
+ let res = await actor.startFileUpload(publishingId, file, BigInt(chunkCount), firstChunk);
351
359
  if ("err" in res) {
352
360
  console.log(chalk.red("Error: ") + res.err);
353
361
  process.exit(1);
@@ -356,7 +364,7 @@ export async function publish(options = {}) {
356
364
  for (let i = 1; i < chunkCount; i++) {
357
365
  let start = i * chunkSize;
358
366
  let chunk = Array.from(content.slice(start, start + chunkSize));
359
- let res = await actor.uploadFileChunk(puiblishingId, fileId, BigInt(i), chunk);
367
+ let res = await actor.uploadFileChunk(publishingId, fileId, BigInt(i), chunk);
360
368
  if ("err" in res) {
361
369
  console.log(chalk.red("Error: ") + res.err);
362
370
  process.exit(1);
@@ -370,7 +378,7 @@ export async function publish(options = {}) {
370
378
  // finish
371
379
  progress();
372
380
  logUpdate.done();
373
- let res = await actor.finishPublish(puiblishingId);
381
+ let res = await actor.finishPublish(publishingId);
374
382
  if ("err" in res) {
375
383
  console.log(chalk.red("Error: ") + res.err);
376
384
  process.exit(1);
@@ -402,14 +410,14 @@ function parseChangelog(version) {
402
410
  async function fetchGitHubReleaseNotes(repo, version) {
403
411
  let repoPath = new URL(repo).pathname;
404
412
  let res = await fetch(`https://api.github.com/repos${repoPath}/releases/tags/${version}`);
405
- let release = await res.json();
413
+ let release = (await res.json());
406
414
  if (release.message === "Not Found") {
407
415
  res = await fetch(`https://api.github.com/repos${repoPath}/releases/tags/v${version}`);
408
- release = await res.json();
416
+ release = (await res.json());
409
417
  if (release.message === "Not Found") {
410
418
  console.log(chalk.yellow(`No GitHub release found with name ${version} or v${version}`));
411
419
  return "";
412
420
  }
413
421
  }
414
- return release.body;
422
+ return release.body ?? "";
415
423
  }
@@ -42,7 +42,10 @@ export async function remove(name, { dev = false, verbose = false, dryRun = fals
42
42
  let config = readConfig(configFile);
43
43
  let deps = Object.values(config.dependencies || {})
44
44
  .map((dep) => {
45
- return [dep, ...getTransitiveDependenciesOf(dep.name, dep.version)];
45
+ return [
46
+ dep,
47
+ ...getTransitiveDependenciesOf(dep.name, dep.version, dep.repo),
48
+ ];
46
49
  })
47
50
  .flat();
48
51
  return deps;
@@ -65,7 +68,7 @@ export async function remove(name, { dev = false, verbose = false, dryRun = fals
65
68
  // transitive deps of this package (including itself)
66
69
  let transitiveDepsOfPackage = [
67
70
  pkgDetails,
68
- ...getTransitiveDependenciesOf(name, version),
71
+ ...getTransitiveDependenciesOf(name, version, pkgDetails.repo),
69
72
  ];
70
73
  // remove local cache
71
74
  for (let dep of transitiveDepsOfPackage) {
@@ -27,7 +27,7 @@ function detectPackageManager() {
27
27
  }
28
28
  export async function getLatestVersion() {
29
29
  let res = await fetch(url + "/tags/latest");
30
- return res.text();
30
+ return (await res.text()).trim();
31
31
  }
32
32
  export async function update() {
33
33
  let latest = await getLatestVersion();
@@ -27,8 +27,9 @@ export async function sourcesArgs({ conflicts = "ignore", cwd = process.cwd(), }
27
27
  }
28
28
  // append baseDir
29
29
  let pkgBaseDir;
30
- if (fs.existsSync(path.join(pkgDir, "mops.toml"))) {
31
- let config = readConfig(path.join(pkgDir, "mops.toml"));
30
+ let resolvedMopsToml = path.resolve(cwd, pkgDir, "mops.toml");
31
+ if (fs.existsSync(resolvedMopsToml)) {
32
+ let config = readConfig(resolvedMopsToml);
32
33
  pkgBaseDir = path.join(pkgDir, config.package?.baseDir || "src");
33
34
  }
34
35
  else {
@@ -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
  export async function sync({ lock } = {}) {
12
13
  if (!checkConfigFile()) {
13
14
  return;
@@ -58,25 +59,19 @@ async function getUsedPackages() {
58
59
  }
59
60
  async function getMissingPackages() {
60
61
  let config = readConfig();
61
- let allDeps = [
62
+ let allDepNames = new Set([
62
63
  ...Object.keys(config.dependencies || {}),
63
64
  ...Object.keys(config["dev-dependencies"] || {}),
64
- ];
65
- let missing = new Set(await getUsedPackages());
66
- for (let pkg of allDeps) {
67
- missing.delete(pkg);
68
- }
69
- return [...missing];
65
+ ].map((key) => getDepName(key)));
66
+ let used = await getUsedPackages();
67
+ return used.filter((pkg) => !allDepNames.has(pkg));
70
68
  }
71
69
  async function getUnusedPackages() {
72
70
  let config = readConfig();
73
- let allDeps = new Set([
71
+ let allDeps = [
74
72
  ...Object.keys(config.dependencies || {}),
75
73
  ...Object.keys(config["dev-dependencies"] || {}),
76
- ]);
77
- let used = await getUsedPackages();
78
- for (let pkg of used) {
79
- allDeps.delete(pkg);
80
- }
81
- return [...allDeps];
74
+ ];
75
+ let used = new Set(await getUsedPackages());
76
+ return allDeps.filter((key) => !used.has(getDepName(key)));
82
77
  }
@@ -9,7 +9,7 @@ import { globSync } from "glob";
9
9
  import chokidar from "chokidar";
10
10
  import debounce from "debounce";
11
11
  import { SemVer } from "semver";
12
- import { sources } from "../sources.js";
12
+ import { sourcesArgs } from "../sources.js";
13
13
  import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
14
14
  import { parallel } from "../../parallel.js";
15
15
  import { MMF1 } from "./mmf1.js";
@@ -151,7 +151,7 @@ export async function testWithReporter(reporterName, filter = "", defaultMode =
151
151
  }
152
152
  reporter.addFiles(files);
153
153
  let config = readConfig();
154
- let sourcesArr = await sources();
154
+ let sourcesArr = (await sourcesArgs()).flat();
155
155
  let globalMocArgs = getGlobalMocArgs(config);
156
156
  if (!mocPath) {
157
157
  mocPath = await toolchain.bin("moc", { fallback: true });
@@ -197,7 +197,7 @@ export async function testWithReporter(reporterName, filter = "", defaultMode =
197
197
  let mocArgs = [
198
198
  "--hide-warnings",
199
199
  "--error-detail=2",
200
- ...sourcesArr.join(" ").split(" "),
200
+ ...sourcesArr,
201
201
  ...globalMocArgs,
202
202
  file,
203
203
  ].filter((x) => x);
@@ -25,9 +25,14 @@ export async function update(pkg, { lock } = {}) {
25
25
  for (let dep of githubDeps) {
26
26
  let { org, gitName, branch, commitHash } = parseGithubURL(dep.repo || "");
27
27
  let dev = !!config["dev-dependencies"]?.[dep.name];
28
- let commit = await getGithubCommit(`${org}/${gitName}`, branch);
29
- if (commit.sha !== commitHash) {
30
- await add(`https://github.com/${org}/${gitName}#${branch}@${commit.sha}`, { dev }, dep.name);
28
+ try {
29
+ let commit = await getGithubCommit(`${org}/${gitName}`, branch);
30
+ if (commit.sha !== commitHash) {
31
+ await add(`https://github.com/${org}/${gitName}#${branch}@${commit.sha}`, { dev, lock }, dep.name);
32
+ }
33
+ }
34
+ catch (err) {
35
+ console.log(chalk.red("Error: ") + `Failed to update ${dep.name}: ${err.message}`);
31
36
  }
32
37
  }
33
38
  // update mops packages
@@ -58,7 +63,7 @@ export async function update(pkg, { lock } = {}) {
58
63
  return (getDepName(d) === dep[0] &&
59
64
  (!pinnedVersion || dep[1].startsWith(pinnedVersion)));
60
65
  }) || dep[0];
61
- await add(`${dep[0]}@${dep[2]}`, { dev }, asName);
66
+ await add(`${dep[0]}@${dep[2]}`, { dev, lock }, asName);
62
67
  }
63
68
  }
64
69
  await checkIntegrity(lock);
@@ -4,7 +4,7 @@ import os from "node:os";
4
4
  import chalk from "chalk";
5
5
  import { getMocPath } from "../../helpers/get-moc-path.js";
6
6
  import { getGlobalMocArgs, getRootDir, readConfig } from "../../mops.js";
7
- import { sources } from "../sources.js";
7
+ import { sourcesArgs } from "../sources.js";
8
8
  import { parallel } from "../../parallel.js";
9
9
  import { globMoFiles } from "./globMoFiles.js";
10
10
  export class ErrorChecker {
@@ -30,19 +30,14 @@ export class ErrorChecker {
30
30
  onProgress();
31
31
  let rootDir = getRootDir();
32
32
  let mocPath = getMocPath();
33
- let deps = await sources({ cwd: rootDir });
33
+ let deps = (await sourcesArgs({ cwd: rootDir })).flat();
34
34
  let globalMocArgs = getGlobalMocArgs(readConfig());
35
35
  let paths = globMoFiles(rootDir);
36
36
  this.totalFiles = paths.length;
37
37
  this.processedFiles = 0;
38
38
  await parallel(os.cpus().length, paths, async (file) => {
39
39
  try {
40
- await promisify(execFile)(mocPath, [
41
- "--check",
42
- ...deps.flatMap((x) => x.split(" ")),
43
- ...globalMocArgs,
44
- file,
45
- ], { cwd: rootDir });
40
+ await promisify(execFile)(mocPath, ["--check", ...deps, ...globalMocArgs, file], { cwd: rootDir });
46
41
  }
47
42
  catch (error) {
48
43
  error.message.split("\n").forEach((line) => {