ic-mops 2.8.1 → 2.10.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.
- package/CHANGELOG.md +10 -0
- package/bundle/cli.tgz +0 -0
- package/cli.ts +21 -11
- package/commands/build.ts +134 -114
- package/commands/check-stable.ts +110 -17
- package/commands/check.ts +183 -102
- package/commands/info.ts +103 -0
- package/commands/lint.ts +132 -42
- package/commands/toolchain/moc.ts +5 -5
- package/dist/cli.js +19 -10
- package/dist/commands/build.js +105 -82
- package/dist/commands/check-stable.d.ts +7 -1
- package/dist/commands/check-stable.js +83 -19
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +127 -78
- package/dist/commands/info.d.ts +4 -0
- package/dist/commands/info.js +75 -0
- package/dist/commands/lint.js +84 -37
- package/dist/commands/toolchain/moc.js +5 -5
- package/dist/helpers/resolve-canisters.d.ts +3 -1
- package/dist/helpers/resolve-canisters.js +20 -5
- package/dist/package.json +3 -1
- package/dist/templates/mops-publish.yml +1 -1
- package/dist/templates/mops-test.yml +1 -1
- package/dist/tests/build.test.js +17 -0
- package/dist/tests/check-stable.test.js +18 -0
- package/dist/tests/check.test.js +23 -5
- package/dist/tests/lint.test.js +33 -0
- package/dist/types.d.ts +1 -0
- package/dist/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/dist/wasm/pkg/web/wasm_bg.wasm +0 -0
- package/helpers/resolve-canisters.ts +36 -5
- package/package.json +3 -1
- package/templates/mops-publish.yml +1 -1
- package/templates/mops-test.yml +1 -1
- package/tests/__snapshots__/check.test.ts.snap +2 -2
- package/tests/__snapshots__/lint.test.ts.snap +163 -5
- package/tests/build.test.ts +17 -0
- package/tests/check/canisters-canister-args/Warning.mo +5 -0
- package/tests/check/canisters-canister-args/mops.toml +9 -0
- package/tests/check-stable/canister-args/migrations/20250101_000000_Init.mo +8 -0
- package/tests/check-stable/canister-args/migrations/20250201_000000_AddField.mo +9 -0
- package/tests/check-stable/canister-args/mops.toml +9 -0
- package/tests/check-stable/canister-args/old.most +8 -0
- package/tests/check-stable/canister-args/src/main.mo +11 -0
- package/tests/check-stable.test.ts +21 -0
- package/tests/check.test.ts +26 -5
- package/tests/lint-extra/mops.toml +5 -0
- package/tests/lint-extra/src/Ok.mo +5 -0
- package/tests/lint-extra/src/restricted/B.mo +8 -0
- package/tests/lint-extra/src/restricted/Restricted.mo +8 -0
- package/tests/lint-extra-edge-cases/mops.toml +8 -0
- package/tests/lint-extra-edge-cases/src/Clean.mo +5 -0
- package/tests/lint-extra-example-rules/lint/migration-only/migration-only.toml +9 -0
- package/tests/lint-extra-example-rules/lint/no-types/no-types.toml +5 -0
- package/tests/lint-extra-example-rules/lint/types-only/types-only.toml +6 -0
- package/tests/lint-extra-example-rules/mops.toml +7 -0
- package/tests/lint-extra-example-rules/src/Main.mo +10 -0
- package/tests/lint-extra-example-rules/src/Migration.mo +9 -0
- package/tests/lint-extra-example-rules/src/Types.mo +10 -0
- package/tests/lint-extra-with-base/mops.toml +8 -0
- package/tests/lint-extra-with-base/src/BadBase.mo +8 -0
- package/tests/lint-extra-with-base/src/Ok.mo +5 -0
- package/tests/lint-extra-with-base/src/Restricted.mo +5 -0
- package/tests/lint-extra-with-cli-rules/empty-rules/.gitkeep +0 -0
- package/tests/lint-extra-with-cli-rules/mops.toml +5 -0
- package/tests/lint-extra-with-cli-rules/rules-b/no-bool-switch-2.toml +9 -0
- package/tests/lint-extra-with-cli-rules/src/Ok.mo +5 -0
- package/tests/lint-extra-with-cli-rules/src/Restricted.mo +8 -0
- package/tests/lint.test.ts +42 -0
- package/types.ts +1 -0
- package/wasm/pkg/nodejs/wasm_bg.wasm +0 -0
- package/wasm/pkg/web/wasm_bg.wasm +0 -0
package/commands/check.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
2
|
import chalk from "chalk";
|
|
4
3
|
import { execa } from "execa";
|
|
5
4
|
import { cliError } from "../error.js";
|
|
@@ -9,13 +8,16 @@ import {
|
|
|
9
8
|
readConfig,
|
|
10
9
|
resolveConfigPath,
|
|
11
10
|
} from "../mops.js";
|
|
12
|
-
import { autofixMotoko } from "../helpers/autofix-motoko.js";
|
|
11
|
+
import { AutofixResult, autofixMotoko } from "../helpers/autofix-motoko.js";
|
|
13
12
|
import { getMocSemVer } from "../helpers/get-moc-version.js";
|
|
14
13
|
import {
|
|
14
|
+
filterCanisters,
|
|
15
|
+
looksLikeFile,
|
|
15
16
|
resolveCanisterConfigs,
|
|
16
|
-
|
|
17
|
+
validateCanisterArgs,
|
|
17
18
|
} from "../helpers/resolve-canisters.js";
|
|
18
|
-
import {
|
|
19
|
+
import { CanisterConfig, Config } from "../types.js";
|
|
20
|
+
import { resolveStablePath, runStableCheck } from "./check-stable.js";
|
|
19
21
|
import { sourcesArgs } from "./sources.js";
|
|
20
22
|
import { toolchain } from "./toolchain/index.js";
|
|
21
23
|
import { collectLintRules, lint } from "./lint.js";
|
|
@@ -33,52 +35,197 @@ export interface CheckOptions {
|
|
|
33
35
|
extraArgs: string[];
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function checkAllLibsSupport(verbose?: boolean): boolean {
|
|
39
|
+
const allLibs = supportsAllLibsFlag();
|
|
40
|
+
if (!allLibs) {
|
|
41
|
+
console.log(
|
|
42
|
+
chalk.yellow(
|
|
43
|
+
`moc < ${MOC_ALL_LIBS_MIN_VERSION}: some diagnostic hints may be missing`,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
} else if (verbose) {
|
|
47
|
+
console.log(
|
|
48
|
+
chalk.blue("check"),
|
|
49
|
+
chalk.gray("Using --all-libs for richer diagnostics"),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return allLibs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function logAutofixResult(
|
|
56
|
+
fixResult: AutofixResult | null,
|
|
57
|
+
verbose?: boolean,
|
|
58
|
+
): void {
|
|
59
|
+
if (fixResult) {
|
|
60
|
+
for (const [file, codes] of fixResult.fixedFiles) {
|
|
61
|
+
const unique = [...new Set(codes)].sort();
|
|
62
|
+
const n = codes.length;
|
|
63
|
+
const rel = path.relative(process.cwd(), file);
|
|
64
|
+
console.log(
|
|
65
|
+
chalk.green(
|
|
66
|
+
`Fixed ${rel} (${n} ${n === 1 ? "fix" : "fixes"}: ${unique.join(", ")})`,
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const fileCount = fixResult.fixedFiles.size;
|
|
71
|
+
console.log(
|
|
72
|
+
chalk.green(
|
|
73
|
+
`\n✓ ${fixResult.totalFixCount} ${fixResult.totalFixCount === 1 ? "fix" : "fixes"} applied to ${fileCount} ${fileCount === 1 ? "file" : "files"}`,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
} else if (verbose) {
|
|
77
|
+
console.log(chalk.yellow("No fixes were needed"));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
36
81
|
export async function check(
|
|
37
|
-
|
|
82
|
+
args: string[],
|
|
38
83
|
options: Partial<CheckOptions> = {},
|
|
39
84
|
): Promise<void> {
|
|
40
|
-
const explicitFiles = Array.isArray(files) ? files : files ? [files] : [];
|
|
41
|
-
let fileList = [...explicitFiles];
|
|
42
|
-
|
|
43
85
|
const config = readConfig();
|
|
86
|
+
const canisters = resolveCanisterConfigs(config);
|
|
87
|
+
const hasCanisters = Object.keys(canisters).length > 0;
|
|
88
|
+
const fileArgs = args.filter(looksLikeFile);
|
|
89
|
+
const nonFileArgs = args.filter((a) => !looksLikeFile(a));
|
|
90
|
+
const isFileMode = fileArgs.length > 0;
|
|
44
91
|
|
|
45
|
-
if (
|
|
46
|
-
fileList = resolveCanisterEntrypoints(config).map(resolveConfigPath);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (fileList.length === 0) {
|
|
92
|
+
if (isFileMode && nonFileArgs.length > 0) {
|
|
50
93
|
cliError(
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
"Or define canisters in mops.toml:\n\n" +
|
|
54
|
-
" [canisters.backend]\n" +
|
|
55
|
-
' main = "src/main.mo"',
|
|
94
|
+
`Cannot mix file paths and canister names: ${args.join(", ")}\n` +
|
|
95
|
+
"Pass either file paths (e.g. mops check src/main.mo) or canister names (e.g. mops check backend)",
|
|
56
96
|
);
|
|
57
97
|
}
|
|
98
|
+
|
|
99
|
+
if (isFileMode) {
|
|
100
|
+
await checkFiles(config, fileArgs, options);
|
|
101
|
+
} else {
|
|
102
|
+
if (!hasCanisters) {
|
|
103
|
+
cliError(
|
|
104
|
+
"No canisters defined in mops.toml.\n" +
|
|
105
|
+
"Either pass files: mops check <files...>\n" +
|
|
106
|
+
"Or define canisters in mops.toml:\n\n" +
|
|
107
|
+
" [canisters.backend]\n" +
|
|
108
|
+
' main = "src/main.mo"',
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const canisterNames = args.length > 0 ? args : undefined;
|
|
113
|
+
const filtered = filterCanisters(canisters, canisterNames);
|
|
114
|
+
await checkCanisters(config, filtered, options);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (config.toolchain?.lintoko) {
|
|
118
|
+
const rootDir = getRootDir();
|
|
119
|
+
const lintRules = await collectLintRules(config, rootDir);
|
|
120
|
+
const lintFiles = isFileMode ? fileArgs : undefined;
|
|
121
|
+
await lint(undefined, {
|
|
122
|
+
verbose: options.verbose,
|
|
123
|
+
fix: options.fix,
|
|
124
|
+
rules: lintRules,
|
|
125
|
+
files: lintFiles,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function checkCanisters(
|
|
131
|
+
config: Config,
|
|
132
|
+
canisters: Record<string, CanisterConfig>,
|
|
133
|
+
options: Partial<CheckOptions>,
|
|
134
|
+
): Promise<void> {
|
|
58
135
|
const mocPath = await toolchain.bin("moc", { fallback: true });
|
|
59
|
-
const sources = await sourcesArgs();
|
|
136
|
+
const sources = (await sourcesArgs()).flat();
|
|
60
137
|
const globalMocArgs = getGlobalMocArgs(config);
|
|
138
|
+
const allLibs = checkAllLibsSupport(options.verbose);
|
|
61
139
|
|
|
62
|
-
|
|
63
|
-
|
|
140
|
+
for (const [canisterName, canister] of Object.entries(canisters)) {
|
|
141
|
+
if (!canister.main) {
|
|
142
|
+
cliError(
|
|
143
|
+
`No main file specified for canister '${canisterName}' in mops.toml`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
64
146
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
147
|
+
validateCanisterArgs(canister, canisterName);
|
|
148
|
+
const motokoPath = resolveConfigPath(canister.main);
|
|
149
|
+
|
|
150
|
+
const mocArgs = [
|
|
151
|
+
"--check",
|
|
152
|
+
...(allLibs ? ["--all-libs"] : []),
|
|
153
|
+
...sources,
|
|
154
|
+
...globalMocArgs,
|
|
155
|
+
...(canister.args ?? []),
|
|
156
|
+
...(options.extraArgs ?? []),
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
if (options.fix) {
|
|
160
|
+
if (options.verbose) {
|
|
161
|
+
console.log(
|
|
162
|
+
chalk.blue("check"),
|
|
163
|
+
chalk.gray(`Attempting to fix ${canisterName}`),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const fixResult = await autofixMotoko(mocPath, [motokoPath], mocArgs);
|
|
168
|
+
logAutofixResult(fixResult, options.verbose);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const args = [motokoPath, ...mocArgs];
|
|
173
|
+
if (options.verbose) {
|
|
174
|
+
console.log(
|
|
175
|
+
chalk.blue("check"),
|
|
176
|
+
chalk.gray(`Checking canister ${canisterName}:`),
|
|
177
|
+
);
|
|
178
|
+
console.log(chalk.gray(mocPath, JSON.stringify(args)));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const result = await execa(mocPath, args, {
|
|
182
|
+
stdio: "inherit",
|
|
183
|
+
reject: false,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (result.exitCode !== 0) {
|
|
187
|
+
cliError(
|
|
188
|
+
`✗ Check failed for canister ${canisterName} (exit code: ${result.exitCode})`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log(chalk.green(`✓ ${canisterName}`));
|
|
193
|
+
} catch (err: any) {
|
|
194
|
+
cliError(
|
|
195
|
+
`Error while checking canister ${canisterName}${err?.message ? `\n${err.message}` : ""}`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const stablePath = resolveStablePath(canister, canisterName);
|
|
200
|
+
if (stablePath) {
|
|
201
|
+
await runStableCheck({
|
|
202
|
+
oldFile: stablePath,
|
|
203
|
+
canisterMain: motokoPath,
|
|
204
|
+
canisterName,
|
|
205
|
+
mocPath,
|
|
206
|
+
globalMocArgs,
|
|
207
|
+
canisterArgs: canister.args ?? [],
|
|
208
|
+
sources,
|
|
209
|
+
options: { verbose: options.verbose, extraArgs: options.extraArgs },
|
|
210
|
+
});
|
|
211
|
+
}
|
|
76
212
|
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function checkFiles(
|
|
216
|
+
config: Config,
|
|
217
|
+
files: string[],
|
|
218
|
+
options: Partial<CheckOptions>,
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
const mocPath = await toolchain.bin("moc", { fallback: true });
|
|
221
|
+
const sources = (await sourcesArgs()).flat();
|
|
222
|
+
const globalMocArgs = getGlobalMocArgs(config);
|
|
223
|
+
const allLibs = checkAllLibsSupport(options.verbose);
|
|
77
224
|
|
|
78
225
|
const mocArgs = [
|
|
79
226
|
"--check",
|
|
80
227
|
...(allLibs ? ["--all-libs"] : []),
|
|
81
|
-
...sources
|
|
228
|
+
...sources,
|
|
82
229
|
...globalMocArgs,
|
|
83
230
|
...(options.extraArgs ?? []),
|
|
84
231
|
];
|
|
@@ -88,32 +235,11 @@ export async function check(
|
|
|
88
235
|
console.log(chalk.blue("check"), chalk.gray("Attempting to fix files"));
|
|
89
236
|
}
|
|
90
237
|
|
|
91
|
-
const fixResult = await autofixMotoko(mocPath,
|
|
92
|
-
|
|
93
|
-
for (const [file, codes] of fixResult.fixedFiles) {
|
|
94
|
-
const unique = [...new Set(codes)].sort();
|
|
95
|
-
const n = codes.length;
|
|
96
|
-
const rel = path.relative(process.cwd(), file);
|
|
97
|
-
console.log(
|
|
98
|
-
chalk.green(
|
|
99
|
-
`Fixed ${rel} (${n} ${n === 1 ? "fix" : "fixes"}: ${unique.join(", ")})`,
|
|
100
|
-
),
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
const fileCount = fixResult.fixedFiles.size;
|
|
104
|
-
console.log(
|
|
105
|
-
chalk.green(
|
|
106
|
-
`\n✓ ${fixResult.totalFixCount} ${fixResult.totalFixCount === 1 ? "fix" : "fixes"} applied to ${fileCount} ${fileCount === 1 ? "file" : "files"}`,
|
|
107
|
-
),
|
|
108
|
-
);
|
|
109
|
-
} else {
|
|
110
|
-
if (options.verbose) {
|
|
111
|
-
console.log(chalk.yellow("No fixes were needed"));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
238
|
+
const fixResult = await autofixMotoko(mocPath, files, mocArgs);
|
|
239
|
+
logAutofixResult(fixResult, options.verbose);
|
|
114
240
|
}
|
|
115
241
|
|
|
116
|
-
for (const file of
|
|
242
|
+
for (const file of files) {
|
|
117
243
|
try {
|
|
118
244
|
const args = [file, ...mocArgs];
|
|
119
245
|
if (options.verbose) {
|
|
@@ -139,49 +265,4 @@ export async function check(
|
|
|
139
265
|
);
|
|
140
266
|
}
|
|
141
267
|
}
|
|
142
|
-
|
|
143
|
-
const canisters = resolveCanisterConfigs(config);
|
|
144
|
-
for (const [name, canister] of Object.entries(canisters)) {
|
|
145
|
-
const stableConfig = canister["check-stable"];
|
|
146
|
-
if (!stableConfig) {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!canister.main) {
|
|
151
|
-
cliError(`No main file specified for canister '${name}' in mops.toml`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const stablePath = resolveConfigPath(stableConfig.path);
|
|
155
|
-
if (!existsSync(stablePath)) {
|
|
156
|
-
if (stableConfig.skipIfMissing) {
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
cliError(
|
|
160
|
-
`Deployed file not found: ${stablePath} (canister '${name}')\n` +
|
|
161
|
-
"Set skipIfMissing = true in [canisters." +
|
|
162
|
-
name +
|
|
163
|
-
".check-stable] to skip this check when the file is missing.",
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
await runStableCheck({
|
|
168
|
-
oldFile: stablePath,
|
|
169
|
-
canisterMain: resolveConfigPath(canister.main),
|
|
170
|
-
canisterName: name,
|
|
171
|
-
mocPath,
|
|
172
|
-
globalMocArgs,
|
|
173
|
-
options: { verbose: options.verbose, extraArgs: options.extraArgs },
|
|
174
|
-
});
|
|
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
|
-
}
|
|
187
268
|
}
|
package/commands/info.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { mainActor } from "../api/actors.js";
|
|
4
|
+
import { resolveVersion } from "../api/resolveVersion.js";
|
|
5
|
+
import type { PackageDetails } from "../declarations/main/main.did.js";
|
|
6
|
+
|
|
7
|
+
function label(text: string): string {
|
|
8
|
+
return chalk.bold(text.padEnd(16));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface InfoOptions {
|
|
12
|
+
versions?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function info(pkgArg: string, options: InfoOptions = {}) {
|
|
16
|
+
let [name, versionArg] = pkgArg.split("@") as [string, string | undefined];
|
|
17
|
+
let actor = await mainActor();
|
|
18
|
+
|
|
19
|
+
let version: string;
|
|
20
|
+
try {
|
|
21
|
+
version = await resolveVersion(name, versionArg ?? "");
|
|
22
|
+
} catch (err) {
|
|
23
|
+
let message = err instanceof Error ? err.message : String(err);
|
|
24
|
+
console.error(chalk.red("Error: ") + message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let res = await actor.getPackageDetails(name, version);
|
|
29
|
+
if ("err" in res) {
|
|
30
|
+
console.error(chalk.red("Error: ") + res.err);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let d: PackageDetails = res.ok;
|
|
35
|
+
let c = d.config;
|
|
36
|
+
|
|
37
|
+
// d.versions is in ascending order (oldest first)
|
|
38
|
+
if (options.versions) {
|
|
39
|
+
for (let ver of d.versions) {
|
|
40
|
+
console.log(ver);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log(
|
|
47
|
+
`${chalk.green.bold(c.name)}${chalk.gray("@")}${chalk.yellow(c.version)}`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (c.description) {
|
|
51
|
+
console.log(chalk.dim(c.description));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (c.version !== d.highestVersion) {
|
|
55
|
+
console.log(chalk.yellow(`latest: ${d.highestVersion}`));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log("");
|
|
59
|
+
|
|
60
|
+
if (c.license) {
|
|
61
|
+
console.log(`${label("license")}${c.license}`);
|
|
62
|
+
}
|
|
63
|
+
if (c.repository) {
|
|
64
|
+
console.log(`${label("repository")}${chalk.cyan(c.repository)}`);
|
|
65
|
+
}
|
|
66
|
+
if (c.homepage) {
|
|
67
|
+
console.log(`${label("homepage")}${chalk.cyan(c.homepage)}`);
|
|
68
|
+
}
|
|
69
|
+
if (c.documentation) {
|
|
70
|
+
console.log(`${label("documentation")}${chalk.cyan(c.documentation)}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (c.dependencies.length > 0) {
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log(
|
|
76
|
+
`${label("dependencies")}${c.dependencies.map((dep) => `${dep.name}${chalk.gray("@")}${dep.version || dep.repo}`).join(", ")}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (c.devDependencies.length > 0) {
|
|
80
|
+
console.log(
|
|
81
|
+
`${label("dev-deps")}${c.devDependencies.map((dep) => `${dep.name}${chalk.gray("@")}${dep.version || dep.repo}`).join(", ")}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (c.keywords.length > 0) {
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log(
|
|
88
|
+
`${label("keywords")}${c.keywords.map((k) => chalk.yellow(k)).join(", ")}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (d.versions.length > 0) {
|
|
93
|
+
let versionsDisplay = d.versions.slice(-10).reverse().join(", ");
|
|
94
|
+
let extra =
|
|
95
|
+
d.versions.length > 10
|
|
96
|
+
? ` ${chalk.gray(`(+${d.versions.length - 10} more)`)}`
|
|
97
|
+
: "";
|
|
98
|
+
console.log("");
|
|
99
|
+
console.log(`${label("versions")}${versionsDisplay}${extra}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log("");
|
|
103
|
+
}
|
package/commands/lint.ts
CHANGED
|
@@ -103,6 +103,62 @@ export interface LintOptions {
|
|
|
103
103
|
extraArgs: string[];
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
function buildCommonArgs(
|
|
107
|
+
options: Partial<LintOptions>,
|
|
108
|
+
config: Config,
|
|
109
|
+
): string[] {
|
|
110
|
+
const args: string[] = [];
|
|
111
|
+
if (options.verbose) {
|
|
112
|
+
args.push("--verbose");
|
|
113
|
+
}
|
|
114
|
+
if (options.fix) {
|
|
115
|
+
args.push("--fix");
|
|
116
|
+
}
|
|
117
|
+
if (config.lint?.args) {
|
|
118
|
+
if (typeof config.lint.args === "string") {
|
|
119
|
+
cliError(
|
|
120
|
+
`[lint] config 'args' should be an array of strings in mops.toml config file`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
args.push(...config.lint.args);
|
|
124
|
+
}
|
|
125
|
+
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
126
|
+
args.push(...options.extraArgs);
|
|
127
|
+
}
|
|
128
|
+
return args;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function runLintoko(
|
|
132
|
+
lintokoBinPath: string,
|
|
133
|
+
rootDir: string,
|
|
134
|
+
args: string[],
|
|
135
|
+
options: Partial<LintOptions>,
|
|
136
|
+
label: string,
|
|
137
|
+
): Promise<boolean> {
|
|
138
|
+
try {
|
|
139
|
+
if (options.verbose) {
|
|
140
|
+
console.log(
|
|
141
|
+
chalk.blue("lint"),
|
|
142
|
+
chalk.gray(`Running lintoko (${label}):`),
|
|
143
|
+
);
|
|
144
|
+
console.log(chalk.gray(lintokoBinPath));
|
|
145
|
+
console.log(chalk.gray(JSON.stringify(args)));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const result = await execa(lintokoBinPath, args, {
|
|
149
|
+
cwd: rootDir,
|
|
150
|
+
stdio: "inherit",
|
|
151
|
+
reject: false,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return result.exitCode === 0;
|
|
155
|
+
} catch (err: any) {
|
|
156
|
+
cliError(
|
|
157
|
+
`Error while running lintoko${err?.message ? `\n${err.message}` : ""}`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
106
162
|
export async function lint(
|
|
107
163
|
filter: string | undefined,
|
|
108
164
|
options: Partial<LintOptions>,
|
|
@@ -131,59 +187,93 @@ export async function lint(
|
|
|
131
187
|
}
|
|
132
188
|
}
|
|
133
189
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (options.fix) {
|
|
139
|
-
args.push("--fix");
|
|
140
|
-
}
|
|
190
|
+
const commonArgs = buildCommonArgs(options, config);
|
|
191
|
+
|
|
192
|
+
// --- base run ---
|
|
193
|
+
const baseArgs: string[] = [...commonArgs];
|
|
141
194
|
const rules =
|
|
142
195
|
options.rules !== undefined
|
|
143
196
|
? options.rules
|
|
144
197
|
: await collectLintRules(config, rootDir);
|
|
145
|
-
rules.forEach((rule) =>
|
|
198
|
+
rules.forEach((rule) => baseArgs.push("--rules", rule));
|
|
199
|
+
baseArgs.push(...filesToLint);
|
|
146
200
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
201
|
+
let failed = !(await runLintoko(
|
|
202
|
+
lintokoBinPath,
|
|
203
|
+
rootDir,
|
|
204
|
+
baseArgs,
|
|
205
|
+
options,
|
|
206
|
+
"base",
|
|
207
|
+
));
|
|
155
208
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
209
|
+
// --- extra runs ---
|
|
210
|
+
const extraEntries = config.lint?.extra;
|
|
211
|
+
if (extraEntries) {
|
|
212
|
+
const isFiltered = filter || (options.files && options.files.length > 0);
|
|
213
|
+
const baseFileSet = isFiltered
|
|
214
|
+
? new Set(filesToLint.map((f) => path.resolve(rootDir, f)))
|
|
215
|
+
: undefined;
|
|
159
216
|
|
|
160
|
-
|
|
217
|
+
for (const [globPattern, ruleDirs] of Object.entries(extraEntries)) {
|
|
218
|
+
if (!Array.isArray(ruleDirs) || ruleDirs.length === 0) {
|
|
219
|
+
console.warn(
|
|
220
|
+
chalk.yellow(
|
|
221
|
+
`[lint.extra] skipping '${globPattern}': value must be a non-empty array of rule directories`,
|
|
222
|
+
),
|
|
223
|
+
);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
161
226
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
227
|
+
for (const dir of ruleDirs) {
|
|
228
|
+
if (!existsSync(path.join(rootDir, dir))) {
|
|
229
|
+
cliError(
|
|
230
|
+
`[lint.extra] rule directory '${dir}' not found (referenced by glob '${globPattern}')`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
168
234
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
235
|
+
let matchedFiles = globSync(path.join(rootDir, globPattern), {
|
|
236
|
+
...MOTOKO_GLOB_CONFIG,
|
|
237
|
+
cwd: rootDir,
|
|
238
|
+
});
|
|
174
239
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
240
|
+
if (baseFileSet) {
|
|
241
|
+
matchedFiles = matchedFiles.filter((f) =>
|
|
242
|
+
baseFileSet.has(path.resolve(rootDir, f)),
|
|
243
|
+
);
|
|
244
|
+
}
|
|
178
245
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
246
|
+
if (matchedFiles.length === 0) {
|
|
247
|
+
console.warn(
|
|
248
|
+
chalk.yellow(
|
|
249
|
+
`[lint.extra] no files matched glob '${globPattern}', skipping`,
|
|
250
|
+
),
|
|
251
|
+
);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const extraArgs: string[] = [...commonArgs];
|
|
256
|
+
for (const dir of ruleDirs) {
|
|
257
|
+
extraArgs.push("--rules", dir);
|
|
258
|
+
}
|
|
259
|
+
extraArgs.push(...matchedFiles);
|
|
260
|
+
|
|
261
|
+
const passed = await runLintoko(
|
|
262
|
+
lintokoBinPath,
|
|
263
|
+
rootDir,
|
|
264
|
+
extraArgs,
|
|
265
|
+
options,
|
|
266
|
+
`extra: ${globPattern}`,
|
|
267
|
+
);
|
|
268
|
+
failed ||= !passed;
|
|
183
269
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (failed) {
|
|
273
|
+
cliError("Lint failed");
|
|
274
|
+
} else if (options.fix) {
|
|
275
|
+
console.log(chalk.green("✓ Lint fixes applied"));
|
|
276
|
+
} else {
|
|
277
|
+
console.log(chalk.green("✓ Lint succeeded"));
|
|
188
278
|
}
|
|
189
279
|
}
|
|
@@ -8,7 +8,7 @@ import * as toolchainUtils from "./toolchain-utils.js";
|
|
|
8
8
|
|
|
9
9
|
let cacheDir = path.join(globalCacheDir, "moc");
|
|
10
10
|
|
|
11
|
-
export let repo = "
|
|
11
|
+
export let repo = "caffeinelabs/motoko";
|
|
12
12
|
|
|
13
13
|
export let getLatestReleaseTag = async () => {
|
|
14
14
|
return toolchainUtils.getLatestReleaseTag(repo);
|
|
@@ -44,7 +44,7 @@ export let download = async (
|
|
|
44
44
|
}
|
|
45
45
|
} else {
|
|
46
46
|
// Download the .js artifact
|
|
47
|
-
const jsUrl = `https://github.com/
|
|
47
|
+
const jsUrl = `https://github.com/caffeinelabs/motoko/releases/download/${version}/moc-${version}.js`;
|
|
48
48
|
const jsDestPath = path.join(destDir, "moc.js");
|
|
49
49
|
|
|
50
50
|
if (verbose && !silent) {
|
|
@@ -75,14 +75,14 @@ export let download = async (
|
|
|
75
75
|
? "arm64"
|
|
76
76
|
: "aarch64"
|
|
77
77
|
: "x86_64";
|
|
78
|
-
url = `https://github.com/
|
|
78
|
+
url = `https://github.com/caffeinelabs/motoko/releases/download/${version}/motoko-${platfrom}-${arch}-${version}.tar.gz`;
|
|
79
79
|
} else if (new SemVer(version).compare(new SemVer("0.9.5")) >= 0) {
|
|
80
80
|
let platfrom = process.platform == "darwin" ? "Darwin" : "Linux";
|
|
81
81
|
let arch = "x86_64";
|
|
82
|
-
url = `https://github.com/
|
|
82
|
+
url = `https://github.com/caffeinelabs/motoko/releases/download/${version}/motoko-${platfrom}-${arch}-${version}.tar.gz`;
|
|
83
83
|
} else {
|
|
84
84
|
let platfrom = process.platform == "darwin" ? "macos" : "linux64";
|
|
85
|
-
url = `https://github.com/
|
|
85
|
+
url = `https://github.com/caffeinelabs/motoko/releases/download/${version}/motoko-${platfrom}-${version}.tar.gz`;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
if (verbose && !silent) {
|