ic-mops 2.14.1 → 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.
- package/CHANGELOG.md +14 -0
- package/bundle/cli.tgz +0 -0
- package/cli.ts +143 -3
- package/commands/build.ts +30 -77
- package/commands/check-stable.ts +4 -0
- package/commands/check.ts +26 -22
- package/commands/deployed.ts +162 -0
- package/commands/generate.ts +201 -0
- package/commands/lint.ts +6 -3
- package/dist/cli.js +75 -3
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.js +26 -52
- package/dist/commands/check-stable.d.ts +2 -0
- package/dist/commands/check-stable.js +2 -2
- package/dist/commands/check.d.ts +2 -0
- package/dist/commands/check.js +20 -21
- package/dist/commands/deployed.d.ts +10 -0
- package/dist/commands/deployed.js +97 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.js +124 -0
- package/dist/commands/lint.d.ts +2 -0
- package/dist/commands/lint.js +1 -1
- package/dist/helpers/autofix-motoko.d.ts +2 -0
- package/dist/helpers/autofix-motoko.js +52 -49
- package/dist/helpers/migrations.d.ts +1 -1
- package/dist/helpers/migrations.js +8 -4
- package/dist/helpers/moc-args.d.ts +25 -0
- package/dist/helpers/moc-args.js +58 -0
- package/dist/package.json +6 -6
- package/dist/tests/check-fix-utf8.test.d.ts +1 -0
- package/dist/tests/check-fix-utf8.test.js +38 -0
- package/dist/tests/check.test.js +21 -6
- package/dist/tests/deployed.test.d.ts +1 -0
- package/dist/tests/deployed.test.js +173 -0
- package/dist/tests/generate.test.d.ts +1 -0
- package/dist/tests/generate.test.js +107 -0
- package/dist/tests/helpers.d.ts +8 -0
- package/dist/tests/helpers.js +28 -3
- package/dist/tests/migrate.test.js +5 -19
- package/dist/types.d.ts +3 -0
- package/helpers/autofix-motoko.ts +73 -64
- package/helpers/migrations.ts +8 -3
- package/helpers/moc-args.ts +123 -0
- package/package.json +6 -6
- package/tests/__snapshots__/check-fix-utf8.test.ts.snap +27 -0
- package/tests/__snapshots__/check.test.ts.snap +4 -17
- package/tests/__snapshots__/deployed.test.ts.snap +10 -0
- package/tests/__snapshots__/generate.test.ts.snap +39 -0
- package/tests/check/fix-utf8/mops.toml +5 -0
- package/tests/check/fix-utf8/multibyte.mo +15 -0
- package/tests/check-fix-utf8.test.ts +51 -0
- package/tests/check.test.ts +24 -11
- package/tests/deployed/basic/main.mo +4 -0
- package/tests/deployed/basic/mops.toml +8 -0
- package/tests/deployed/multi/bar.mo +1 -0
- package/tests/deployed/multi/foo.mo +1 -0
- package/tests/deployed/multi/mops.toml +11 -0
- package/tests/deployed.test.ts +249 -0
- package/tests/generate/basic/candid/bar.did +4 -0
- package/tests/generate/basic/mops.toml +12 -0
- package/tests/generate/basic/src/Bar.mo +5 -0
- package/tests/generate/basic/src/Foo.mo +5 -0
- package/tests/generate/error/mops.toml +8 -0
- package/tests/generate/error/src/Broken.mo +5 -0
- package/tests/generate.test.ts +140 -0
- package/tests/helpers.ts +31 -3
- package/tests/migrate.test.ts +7 -22
- package/types.ts +3 -0
package/helpers/migrations.ts
CHANGED
|
@@ -119,6 +119,7 @@ function resolveMigrationChain(
|
|
|
119
119
|
migrations: MigrationsConfig,
|
|
120
120
|
canisterName: string,
|
|
121
121
|
mode: "check" | "build",
|
|
122
|
+
ignoreLimit = false,
|
|
122
123
|
): MigrationChain {
|
|
123
124
|
validateMigrationsConfig(migrations, canisterName);
|
|
124
125
|
|
|
@@ -149,8 +150,11 @@ function resolveMigrationChain(
|
|
|
149
150
|
all.push({ file: nextFile, dir: nextDir });
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
const limit =
|
|
153
|
-
|
|
153
|
+
const limit = ignoreLimit
|
|
154
|
+
? undefined
|
|
155
|
+
: mode === "check"
|
|
156
|
+
? migrations["check-limit"]
|
|
157
|
+
: migrations["build-limit"];
|
|
154
158
|
const isTrimming = limit !== undefined && limit < all.length;
|
|
155
159
|
const included = isTrimming ? all.slice(-limit!) : all;
|
|
156
160
|
// Dropped entries are always a chain-only prefix (next sorts last).
|
|
@@ -166,13 +170,14 @@ export async function prepareMigrationArgs(
|
|
|
166
170
|
canisterName: string,
|
|
167
171
|
mode: "check" | "build",
|
|
168
172
|
verbose?: boolean,
|
|
173
|
+
ignoreLimit = false,
|
|
169
174
|
): Promise<MigrationArgsResult> {
|
|
170
175
|
if (!migrations) {
|
|
171
176
|
return { migrationArgs: [], cleanup: async () => {} };
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
const { chainDir, nextDir, included, excludedChainFiles, isTrimming } =
|
|
175
|
-
resolveMigrationChain(migrations, canisterName, mode);
|
|
180
|
+
resolveMigrationChain(migrations, canisterName, mode, ignoreLimit);
|
|
176
181
|
|
|
177
182
|
const hasNext = included.some((e) => e.dir === nextDir);
|
|
178
183
|
const needsTempDir = hasNext || isTrimming;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { CanisterConfig, Config } from "../types.js";
|
|
3
|
+
import { cliError } from "../error.js";
|
|
4
|
+
import { getGlobalMocArgs, resolveConfigPath } from "../mops.js";
|
|
5
|
+
import { sourcesArgs } from "../commands/sources.js";
|
|
6
|
+
import { prepareMigrationArgs } from "./migrations.js";
|
|
7
|
+
import { validateCanisterArgs } from "./resolve-canisters.js";
|
|
8
|
+
|
|
9
|
+
/** Single source of truth for "how moc is invoked for canister X" — keeps
|
|
10
|
+
* `mops build` and `mops generate candid` in lockstep so the auto-generated
|
|
11
|
+
* `.did` they each produce stays subtype-compatible with the curated one
|
|
12
|
+
* embedded by `mops build`. */
|
|
13
|
+
export interface PrepareMocArgsOptions {
|
|
14
|
+
/** Selects the migration trim limit (`build-limit` vs `check-limit`). */
|
|
15
|
+
mode: "check" | "build";
|
|
16
|
+
/** Per-flag hints used when warning about conflicting `args` entries. */
|
|
17
|
+
managedFlags: Record<string, string>;
|
|
18
|
+
/** Command label shown in the warning, e.g. "mops build". */
|
|
19
|
+
commandName: string;
|
|
20
|
+
verbose?: boolean;
|
|
21
|
+
extraArgs?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PreparedMocArgs {
|
|
25
|
+
motokoPath: string;
|
|
26
|
+
/** sources + [moc].args + migration + [build].args + [canisters.x].args + CLI extras. */
|
|
27
|
+
args: string[];
|
|
28
|
+
/** Cleans up the migration staging dir; call from a `finally`. */
|
|
29
|
+
cleanup: () => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const BUILD_MANAGED_FLAGS: Record<string, string> = {
|
|
33
|
+
"-o": "use [build].outputDir in mops.toml or --output flag instead",
|
|
34
|
+
"-c": "this flag is always set by mops build",
|
|
35
|
+
"--idl": "this flag is always set by mops build",
|
|
36
|
+
"--stable-types": "this flag is always set by mops build",
|
|
37
|
+
"--public-metadata": "this flag is managed by mops build",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const GENERATE_CANDID_MANAGED_FLAGS: Record<string, string> = {
|
|
41
|
+
"-o": "use the --output flag on `mops generate candid` instead",
|
|
42
|
+
"-c": "this flag is incompatible with mops generate candid (would also emit .wasm)",
|
|
43
|
+
"--idl": "this flag is always set by mops generate candid",
|
|
44
|
+
"--stable-types":
|
|
45
|
+
"this flag is incompatible with mops generate candid (would also emit .most)",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export async function prepareMocArgs(
|
|
49
|
+
config: Config,
|
|
50
|
+
canister: CanisterConfig,
|
|
51
|
+
canisterName: string,
|
|
52
|
+
options: PrepareMocArgsOptions,
|
|
53
|
+
): Promise<PreparedMocArgs> {
|
|
54
|
+
if (!canister.main) {
|
|
55
|
+
cliError(`No main file is specified for canister ${canisterName}`);
|
|
56
|
+
}
|
|
57
|
+
const motokoPath = resolveConfigPath(canister.main);
|
|
58
|
+
|
|
59
|
+
const migration = await prepareMigrationArgs(
|
|
60
|
+
canister.migrations,
|
|
61
|
+
canisterName,
|
|
62
|
+
options.mode,
|
|
63
|
+
options.verbose,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const args = [
|
|
67
|
+
...(await sourcesArgs()).flat(),
|
|
68
|
+
...getGlobalMocArgs(config),
|
|
69
|
+
...migration.migrationArgs,
|
|
70
|
+
...collectExtraArgs(
|
|
71
|
+
config,
|
|
72
|
+
canister,
|
|
73
|
+
canisterName,
|
|
74
|
+
options.managedFlags,
|
|
75
|
+
options.commandName,
|
|
76
|
+
options.extraArgs,
|
|
77
|
+
),
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
return { motokoPath, args, cleanup: migration.cleanup };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function collectExtraArgs(
|
|
84
|
+
config: Config,
|
|
85
|
+
canister: CanisterConfig,
|
|
86
|
+
canisterName: string,
|
|
87
|
+
managedFlags: Record<string, string>,
|
|
88
|
+
commandName: string,
|
|
89
|
+
extraArgs?: string[],
|
|
90
|
+
): string[] {
|
|
91
|
+
const args: string[] = [];
|
|
92
|
+
|
|
93
|
+
if (config.build?.args) {
|
|
94
|
+
if (typeof config.build.args === "string") {
|
|
95
|
+
cliError(
|
|
96
|
+
`[build] config 'args' should be an array of strings in mops.toml config file`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
args.push(...config.build.args);
|
|
100
|
+
}
|
|
101
|
+
if (canister.args) {
|
|
102
|
+
validateCanisterArgs(canister, canisterName, config);
|
|
103
|
+
args.push(...canister.args);
|
|
104
|
+
}
|
|
105
|
+
if (extraArgs) {
|
|
106
|
+
args.push(...extraArgs);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const warned = new Set<string>();
|
|
110
|
+
for (const arg of args) {
|
|
111
|
+
const hint = managedFlags[arg];
|
|
112
|
+
if (hint && !warned.has(arg)) {
|
|
113
|
+
warned.add(arg);
|
|
114
|
+
console.warn(
|
|
115
|
+
chalk.yellow(
|
|
116
|
+
`Warning: '${arg}' in args for canister ${canisterName} may conflict with ${commandName} — ${hint}`,
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return args;
|
|
123
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "dist/bin/mops.js",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"markdown-table": "3.0.4",
|
|
77
77
|
"mdast-util-from-markdown": "2.0.2",
|
|
78
78
|
"mdast-util-to-markdown": "2.1.2",
|
|
79
|
-
"minimatch": "10.2.
|
|
79
|
+
"minimatch": "10.2.5",
|
|
80
80
|
"ncp": "2.0.0",
|
|
81
81
|
"octokit": "3.1.2",
|
|
82
82
|
"pem-file": "1.0.1",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"semver": "7.7.1",
|
|
91
91
|
"stream-to-promise": "3.0.0",
|
|
92
92
|
"string-width": "7.2.0",
|
|
93
|
-
"tar": "7.5.
|
|
93
|
+
"tar": "7.5.16",
|
|
94
94
|
"terminal-size": "4.0.0",
|
|
95
95
|
"vscode-languageserver-textdocument": "1.0.12"
|
|
96
96
|
},
|
|
@@ -106,13 +106,13 @@
|
|
|
106
106
|
"@types/proper-lockfile": "4.1.4",
|
|
107
107
|
"@types/semver": "7.5.8",
|
|
108
108
|
"@types/stream-to-promise": "2.2.4",
|
|
109
|
-
"@types/tar": "
|
|
110
|
-
"esbuild": "0.
|
|
109
|
+
"@types/tar": "7.0.87",
|
|
110
|
+
"esbuild": "0.28.1",
|
|
111
111
|
"eslint": "8.57.0",
|
|
112
112
|
"npm-run-all": "4.1.5",
|
|
113
113
|
"rexreplace": "7.1.13",
|
|
114
114
|
"ts-jest": "29.4.5",
|
|
115
|
-
"tsx": "4.
|
|
115
|
+
"tsx": "4.22.4",
|
|
116
116
|
"typescript": "5.9.2",
|
|
117
117
|
"wasm-pack": "0.15.0"
|
|
118
118
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
|
+
|
|
3
|
+
exports[`check --fix (utf-8 source) multi-byte characters do not corrupt the fix: fix output 1`] = `
|
|
4
|
+
"Fixed run/multibyte.mo (3 fixes: M0236)
|
|
5
|
+
|
|
6
|
+
✓ 3 fixes applied to 1 file
|
|
7
|
+
✓ <TEST_DIR>/check/fix-utf8/run/multibyte.mo"
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
exports[`check --fix (utf-8 source) multi-byte characters do not corrupt the fix: fixed file 1`] = `
|
|
11
|
+
"// Regression fixture: --fix must apply byte-accurately when a multi-byte UTF-8
|
|
12
|
+
// literal sits before the dot-notation span on the SAME line — that's where
|
|
13
|
+
// moc's byte columns and LSP's UTF-16 positions diverge. Each body is a single
|
|
14
|
+
// expression so prettier keeps it on one line (it splits \`;\`-separated
|
|
15
|
+
// statements). \`Char.toNat32(c)\` triggers M0236; a column-based edit over-deletes
|
|
16
|
+
// past the multi-byte prefix and drops the trailing \`)\`. Literal receivers are
|
|
17
|
+
// avoided on purpose — moc no longer suggests dot-rewrites for them
|
|
18
|
+
// (caffeinelabs/motoko#6173).
|
|
19
|
+
import Char "mo:core/Char";
|
|
20
|
+
|
|
21
|
+
module {
|
|
22
|
+
public func a(c : Char) : Text = "A" # debug_show (c.toNat32());
|
|
23
|
+
public func b(c : Char) : Text = "京" # debug_show (c.toNat32());
|
|
24
|
+
public func d(c : Char) : Text = "💩" # debug_show (c.toNat32());
|
|
25
|
+
};
|
|
26
|
+
"
|
|
27
|
+
`;
|
|
@@ -5,7 +5,7 @@ exports[`check [moc] args are passed to moc 1`] = `
|
|
|
5
5
|
"exitCode": 1,
|
|
6
6
|
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
7
7
|
help: if this is intentional, prefix it with an underscore: \`_unused\`
|
|
8
|
-
✗ Check failed (exit code: 1)",
|
|
8
|
+
✗ Check failed for file Warning.mo (exit code: 1)",
|
|
9
9
|
"stdout": "",
|
|
10
10
|
}
|
|
11
11
|
`;
|
|
@@ -18,7 +18,7 @@ exports[`check error 1`] = `
|
|
|
18
18
|
cannot produce expected type
|
|
19
19
|
()
|
|
20
20
|
Error.mo:7.1-7.21: type error [M0057], unbound variable thisshouldnotcompile
|
|
21
|
-
✗ Check failed (exit code: 1)",
|
|
21
|
+
✗ Check failed for file Error.mo (exit code: 1)",
|
|
22
22
|
"stdout": "",
|
|
23
23
|
}
|
|
24
24
|
`;
|
|
@@ -27,24 +27,11 @@ exports[`check error 2`] = `
|
|
|
27
27
|
{
|
|
28
28
|
"exitCode": 1,
|
|
29
29
|
"stderr": "Ok.mo: No such file or directory
|
|
30
|
-
✗ Check failed (exit code: 1)",
|
|
30
|
+
✗ Check failed for file Ok.mo (exit code: 1)",
|
|
31
31
|
"stdout": "",
|
|
32
32
|
}
|
|
33
33
|
`;
|
|
34
34
|
|
|
35
|
-
exports[`check multiple files in a single invocation 1`] = `
|
|
36
|
-
{
|
|
37
|
-
"exitCode": 0,
|
|
38
|
-
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
39
|
-
help: if this is intentional, prefix it with an underscore: \`_unused\`",
|
|
40
|
-
"stdout": "check Using --all-libs for richer diagnostics
|
|
41
|
-
check Running moc:
|
|
42
|
-
<CACHE>moc-wrapper ["Ok.mo","Warning.mo","--check","--all-libs","--default-persistent-actors"]
|
|
43
|
-
✓ Ok.mo
|
|
44
|
-
✓ Warning.mo",
|
|
45
|
-
}
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
35
|
exports[`check no args checks all canisters 1`] = `
|
|
49
36
|
{
|
|
50
37
|
"exitCode": 0,
|
|
@@ -98,7 +85,7 @@ exports[`check warning with -Werror flag 1`] = `
|
|
|
98
85
|
"exitCode": 1,
|
|
99
86
|
"stderr": "Warning.mo:3.9-3.15: warning [M0194], unused identifier: \`unused\`
|
|
100
87
|
help: if this is intentional, prefix it with an underscore: \`_unused\`
|
|
101
|
-
✗ Check failed (exit code: 1)",
|
|
88
|
+
✗ Check failed for file Warning.mo (exit code: 1)",
|
|
102
89
|
"stdout": "",
|
|
103
90
|
}
|
|
104
91
|
`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
|
+
|
|
3
|
+
exports[`deployed init creates baseline .most and sets [check-stable].path 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"exitCode": 0,
|
|
6
|
+
"stderr": "",
|
|
7
|
+
"stdout": "✓ Created baseline deployed/backend.most
|
|
8
|
+
✓ Set [canisters.backend.check-stable].path = "deployed/backend.most"",
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
|
2
|
+
|
|
3
|
+
exports[`generate candid configured path: overwrites in place and does not touch mops.toml 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"exitCode": 0,
|
|
6
|
+
"stderr": "",
|
|
7
|
+
"stdout": "generate candid bar → candid/bar.did
|
|
8
|
+
|
|
9
|
+
✓ Generated Candid for 1 canister",
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
exports[`generate candid default path: writes <main-dir>/<name>.did and sets [canisters.<name>].candid 1`] = `
|
|
14
|
+
{
|
|
15
|
+
"exitCode": 0,
|
|
16
|
+
"stderr": "",
|
|
17
|
+
"stdout": "generate candid foo → src/foo.did
|
|
18
|
+
|
|
19
|
+
✓ Generated Candid for 1 canister",
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
exports[`generate candid default path: writes <main-dir>/<name>.did and sets [canisters.<name>].candid: src/foo.did 1`] = `
|
|
24
|
+
"service : {
|
|
25
|
+
call: () -> (text);
|
|
26
|
+
}
|
|
27
|
+
"
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
exports[`generate candid no canister names: processes all canisters 1`] = `
|
|
31
|
+
{
|
|
32
|
+
"exitCode": 0,
|
|
33
|
+
"stderr": "",
|
|
34
|
+
"stdout": "generate candid foo → src/foo.did
|
|
35
|
+
generate candid bar → candid/bar.did
|
|
36
|
+
|
|
37
|
+
✓ Generated Candid for 2 canisters",
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Regression fixture: --fix must apply byte-accurately when a multi-byte UTF-8
|
|
2
|
+
// literal sits before the dot-notation span on the SAME line — that's where
|
|
3
|
+
// moc's byte columns and LSP's UTF-16 positions diverge. Each body is a single
|
|
4
|
+
// expression so prettier keeps it on one line (it splits `;`-separated
|
|
5
|
+
// statements). `Char.toNat32(c)` triggers M0236; a column-based edit over-deletes
|
|
6
|
+
// past the multi-byte prefix and drops the trailing `)`. Literal receivers are
|
|
7
|
+
// avoided on purpose — moc no longer suggests dot-rewrites for them
|
|
8
|
+
// (caffeinelabs/motoko#6173).
|
|
9
|
+
import Char "mo:core/Char";
|
|
10
|
+
|
|
11
|
+
module {
|
|
12
|
+
public func a(c : Char) : Text = "A" # debug_show (Char.toNat32(c));
|
|
13
|
+
public func b(c : Char) : Text = "京" # debug_show (Char.toNat32(c));
|
|
14
|
+
public func d(c : Char) : Text = "💩" # debug_show (Char.toNat32(c));
|
|
15
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, test, beforeAll } from "@jest/globals";
|
|
2
|
+
import { cpSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { cli, normalizePaths } from "./helpers";
|
|
5
|
+
|
|
6
|
+
// Regression: --fix must apply byte-accurately on UTF-8 source. The fixture
|
|
7
|
+
// pins a moc version that emits `byte_start`/`byte_end` so the byte path is
|
|
8
|
+
// exercised; without those fields the fixer over-deletes on multi-byte lines
|
|
9
|
+
// (e.g. `Char.toNat32(c)` after a `"京"` on the same line losing its `)`).
|
|
10
|
+
describe("check --fix (utf-8 source)", () => {
|
|
11
|
+
const fixDir = path.join(import.meta.dirname, "check/fix-utf8");
|
|
12
|
+
const runDir = path.join(fixDir, "run");
|
|
13
|
+
const warningFlags = "-W=M0236";
|
|
14
|
+
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
for (const file of readdirSync(runDir).filter((f) => f.endsWith(".mo"))) {
|
|
17
|
+
unlinkSync(path.join(runDir, file));
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("multi-byte characters do not corrupt the fix", async () => {
|
|
22
|
+
const src = "multibyte.mo";
|
|
23
|
+
const runFilePath = path.join(runDir, src);
|
|
24
|
+
cpSync(path.join(fixDir, src), runFilePath);
|
|
25
|
+
|
|
26
|
+
const fixResult = await cli(
|
|
27
|
+
["check", runFilePath, "--fix", "--", warningFlags],
|
|
28
|
+
{ cwd: fixDir },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(normalizePaths(fixResult.stdout)).toMatchSnapshot("fix output");
|
|
32
|
+
const fixedContent = readFileSync(runFilePath, "utf-8");
|
|
33
|
+
expect(fixedContent).toMatchSnapshot("fixed file");
|
|
34
|
+
|
|
35
|
+
// Concrete byte-accuracy assertions: every Char.toNat32 call must be
|
|
36
|
+
// rewritten to dot notation, with the multi-byte literal before it intact
|
|
37
|
+
// and the trailing `)` preserved (the original regression: 京/💩 used to
|
|
38
|
+
// drop it).
|
|
39
|
+
expect(fixedContent).toContain('"A" # debug_show (c.toNat32());');
|
|
40
|
+
expect(fixedContent).toContain('"京" # debug_show (c.toNat32());');
|
|
41
|
+
expect(fixedContent).toContain('"💩" # debug_show (c.toNat32());');
|
|
42
|
+
expect(fixedContent).not.toMatch(/debug_show \(Char\.toNat32/);
|
|
43
|
+
|
|
44
|
+
// Verify no remaining M0236 warnings.
|
|
45
|
+
const afterResult = await cli(
|
|
46
|
+
["check", runFilePath, "--", warningFlags, "--error-format=json"],
|
|
47
|
+
{ cwd: fixDir },
|
|
48
|
+
);
|
|
49
|
+
expect(afterResult.stdout).not.toContain('"code":"M0236"');
|
|
50
|
+
});
|
|
51
|
+
});
|
package/tests/check.test.ts
CHANGED
|
@@ -15,17 +15,6 @@ describe("check", () => {
|
|
|
15
15
|
await cliSnapshot(["check", "Ok.mo", "Error.mo"], { cwd }, 1);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
// The verbose snapshot shows a single "moc ... [both files]" line, proving the
|
|
19
|
-
// whole set is checked in one invocation rather than one moc call per file.
|
|
20
|
-
test("multiple files in a single invocation", async () => {
|
|
21
|
-
const cwd = path.join(import.meta.dirname, "check/success");
|
|
22
|
-
await cliSnapshot(
|
|
23
|
-
["check", "Ok.mo", "Warning.mo", "--verbose"],
|
|
24
|
-
{ cwd },
|
|
25
|
-
0,
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
18
|
test("warning", async () => {
|
|
30
19
|
const cwd = path.join(import.meta.dirname, "check/success");
|
|
31
20
|
const result = await cliSnapshot(["check", "Warning.mo"], { cwd }, 0);
|
|
@@ -182,4 +171,28 @@ describe("check", () => {
|
|
|
182
171
|
expect(result.exitCode).toBe(0);
|
|
183
172
|
expect(result.stdout).toMatch(/✓ Lint fixes applied/);
|
|
184
173
|
});
|
|
174
|
+
|
|
175
|
+
// The migrations-chain fixture has 4 migrations with check-limit=3, so the
|
|
176
|
+
// oldest is trimmed by default (staged dir + M0254 suppression). --no-check-limit
|
|
177
|
+
// must point moc at the real chain dir and drop the trimming side effects.
|
|
178
|
+
const migrationsChain = "check-stable/migrations-chain";
|
|
179
|
+
|
|
180
|
+
test("check-limit trims the migration chain by default", async () => {
|
|
181
|
+
const cwd = path.join(import.meta.dirname, migrationsChain);
|
|
182
|
+
const result = await cli(["check", "--verbose"], { cwd });
|
|
183
|
+
expect(result.exitCode).toBe(0);
|
|
184
|
+
expect(result.stdout).toMatch(/--enhanced-migration=[^"]*\.migrations-/);
|
|
185
|
+
expect(result.stdout).toMatch(/-A=M0254/);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("--no-check-limit uses the full migration chain", async () => {
|
|
189
|
+
const cwd = path.join(import.meta.dirname, migrationsChain);
|
|
190
|
+
const result = await cli(["check", "--verbose", "--no-check-limit"], {
|
|
191
|
+
cwd,
|
|
192
|
+
});
|
|
193
|
+
expect(result.exitCode).toBe(0);
|
|
194
|
+
expect(result.stdout).toMatch(/--enhanced-migration=/);
|
|
195
|
+
expect(result.stdout).not.toMatch(/\.migrations-/);
|
|
196
|
+
expect(result.stdout).not.toMatch(/-A=M0254/);
|
|
197
|
+
});
|
|
185
198
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
actor { stable var y : Text = "" };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
actor { stable var x : Nat = 0 };
|