ic-mops 0.21.1 → 0.23.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/cli.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import fs from 'node:fs';
4
- import {program} from 'commander';
4
+ import {program, Argument} from 'commander';
5
5
  import chalk from 'chalk';
6
6
  import {Principal} from '@dfinity/principal';
7
7
 
@@ -21,6 +21,7 @@ import {selfUpdate} from './commands/self-update.js';
21
21
  import {remove} from './commands/remove.js';
22
22
  import {getUserProp, setUserProp} from './commands/user.js';
23
23
  import {bump} from './commands/bump.js';
24
+ import {sync} from './commands/sync.js';
24
25
  // import {docs} from './commands/docs.js';
25
26
 
26
27
  program.name('mops');
@@ -166,8 +167,9 @@ program
166
167
 
167
168
  // cache
168
169
  program
169
- .command('cache [sub-command]')
170
+ .command('cache')
170
171
  .description('Manage cache')
172
+ .addArgument(new Argument('<sub>').choices(['size', 'clean']))
171
173
  .action(async (sub) => {
172
174
  if (sub == 'clean') {
173
175
  await cleanCache();
@@ -177,9 +179,6 @@ program
177
179
  let size = await cacheSize();
178
180
  console.log('Cache size is ' + size);
179
181
  }
180
- else {
181
- console.log('Unknown sub command. Available sub commands: clean, size');
182
- }
183
182
  });
184
183
 
185
184
  // test
@@ -188,7 +187,12 @@ program
188
187
  .description('Run tests')
189
188
  .option('-r, --reporter <reporter>', 'Choose reporter: verbose, compact, files')
190
189
  .option('-w, --watch', 'Enable watch mode')
190
+ .option('--mode <mode>', 'Test mode: \'interpreter\' or \'wasi\' (default \'interpreter\'')
191
191
  .action(async (filter, options) => {
192
+ if (options.mode && !['interpreter', 'wasi'].includes(options.mode)) {
193
+ console.log(`Unknown --mode value '${options.mode}'. Allowed: interpreter, wasi`);
194
+ process.exit(1);
195
+ }
192
196
  await test(filter, options);
193
197
  });
194
198
 
@@ -231,7 +235,10 @@ program
231
235
 
232
236
  // user
233
237
  program
234
- .command('user set|get <prop> [value]')
238
+ .command('user')
239
+ .addArgument(new Argument('<sub>').choices(['set', 'get']))
240
+ .addArgument(new Argument('<prop>').choices(['name', 'site', 'email', 'github', 'twitter']))
241
+ .addArgument(new Argument('[value]'))
235
242
  .description('User settings')
236
243
  .action(async (sub, prop, value) => {
237
244
  if (sub == 'get') {
@@ -244,9 +251,6 @@ program
244
251
  }
245
252
  await setUserProp(prop, value);
246
253
  }
247
- else {
248
- console.log('Unknown sub command. Available sub commands: set, get');
249
- }
250
254
  });
251
255
 
252
256
  // airdrop
@@ -289,4 +293,12 @@ program
289
293
  bump(part);
290
294
  });
291
295
 
296
+ // sync
297
+ program
298
+ .command('sync')
299
+ .description('Add missing packages and remove unused packages')
300
+ .action(async () => {
301
+ await sync();
302
+ });
303
+
292
304
  program.parse();
@@ -77,7 +77,7 @@ export async function remove(name: string, {dev = false, verbose = false, dryRun
77
77
  pkgDir = formatDir(dep.name, dep.version);
78
78
  }
79
79
  if (pkgDir && fs.existsSync(pkgDir)) {
80
- dryRun || deleteSync([`${pkgDir}`]);
80
+ dryRun || deleteSync([`${pkgDir}`], {force: true});
81
81
  verbose && console.log(`Removed local cache ${pkgDir}`);
82
82
  }
83
83
  }
@@ -0,0 +1,98 @@
1
+ import path from 'node:path';
2
+ import {execSync} from 'node:child_process';
3
+ import {globSync} from 'glob';
4
+ import chalk from 'chalk';
5
+ import {checkConfigFile, getRootDir, readConfig} from '../mops.js';
6
+ import {add} from './add.js';
7
+ import {remove} from './remove.js';
8
+
9
+ let ignore = [
10
+ '**/node_modules/**',
11
+ '**/.vessel/**',
12
+ '**/.git/**',
13
+ '**/.mops/**',
14
+ '**/test/**',
15
+ '**/*.test.mo',
16
+ ];
17
+
18
+ let mocPath = '';
19
+ function getMocPath(): string {
20
+ if (!mocPath) {
21
+ mocPath = process.env.DFX_MOC_PATH || '';
22
+ }
23
+ if (!mocPath) {
24
+ try {
25
+ mocPath = execSync('dfx cache show').toString().trim() + '/moc';
26
+ }
27
+ catch {}
28
+ }
29
+ if (!mocPath) {
30
+ mocPath = 'moc';
31
+ }
32
+ return mocPath;
33
+ }
34
+
35
+ async function getUsedPackages(): Promise<string[]> {
36
+ let rootDir = getRootDir();
37
+ let files = globSync('**/*.mo', {
38
+ cwd: rootDir,
39
+ nocase: true,
40
+ ignore: ignore,
41
+ });
42
+
43
+ let packages: Set<string> = new Set;
44
+
45
+ for (let file of files) {
46
+ let deps: string[] = execSync(`${getMocPath()} --print-deps ${path.join(rootDir, file)}`).toString().trim().split('\n');
47
+
48
+ for (let dep of deps) {
49
+ if (dep.startsWith('mo:') && !dep.startsWith('mo:prim') && !dep.startsWith('mo:⛔')) {
50
+ packages.add(dep.replace(/^mo:([^/]+).*$/, '$1'));
51
+ }
52
+ }
53
+ }
54
+
55
+ return [...packages];
56
+ }
57
+
58
+ async function getMissingPackages(): Promise<string[]> {
59
+ let config = readConfig();
60
+ let allDeps = [...Object.keys(config.dependencies || {}), ...Object.keys(config['dev-dependencies'] || {})];
61
+ let missing = new Set(await getUsedPackages());
62
+ for (let pkg of allDeps) {
63
+ missing.delete(pkg);
64
+ }
65
+ return [...missing];
66
+ }
67
+
68
+ async function getUnusedPackages(): Promise<string[]> {
69
+ let config = readConfig();
70
+ let allDeps = new Set([...Object.keys(config.dependencies || {})]);
71
+ let used = await getUsedPackages();
72
+ for (let pkg of used) {
73
+ allDeps.delete(pkg);
74
+ }
75
+ return [...allDeps];
76
+ }
77
+
78
+ export async function sync() {
79
+ if (!checkConfigFile()) {
80
+ return;
81
+ }
82
+
83
+ let missing = await getMissingPackages();
84
+ let unused = await getUnusedPackages();
85
+
86
+ missing.length && console.log(`${chalk.yellow('Missing packages:')} ${missing.join(', ')}`);
87
+ unused.length && console.log(`${chalk.yellow('Unused packages:')} ${unused.join(', ')}`);
88
+
89
+ // add missing packages
90
+ for (let pkg of missing) {
91
+ await add(pkg);
92
+ }
93
+
94
+ // remove unused packages
95
+ for (let pkg of unused) {
96
+ await remove(pkg);
97
+ }
98
+ }
@@ -30,7 +30,9 @@ let globConfig = {
30
30
  ignore: ignore,
31
31
  };
32
32
 
33
- export async function test(filter = '', {watch = false, reporter = 'verbose'} = {}) {
33
+ type TestMode = 'interpreter' | 'wasi';
34
+
35
+ export async function test(filter = '', {watch = false, reporter = 'verbose', mode = 'interpreter' as TestMode} = {}) {
34
36
  let rootDir = getRootDir();
35
37
 
36
38
  if (watch) {
@@ -59,7 +61,7 @@ export async function test(filter = '', {watch = false, reporter = 'verbose'} =
59
61
  run();
60
62
  }
61
63
  else {
62
- let passed = await runAll(filter, reporter);
64
+ let passed = await runAll(filter, reporter, mode);
63
65
  if (!passed) {
64
66
  process.exit(1);
65
67
  }
@@ -68,7 +70,7 @@ export async function test(filter = '', {watch = false, reporter = 'verbose'} =
68
70
 
69
71
  let mocPath = process.env.DFX_MOC_PATH;
70
72
 
71
- export async function runAll(filter = '', reporterName = 'verbose') {
73
+ export async function runAll(filter = '', reporterName = 'verbose', mode: TestMode = 'interpreter') {
72
74
  let reporter: Reporter;
73
75
  if (reporterName == 'compact') {
74
76
  reporter = new CompactReporter;
@@ -116,7 +118,7 @@ export async function runAll(filter = '', reporterName = 'verbose') {
116
118
 
117
119
  await parallel(os.cpus().length, files, async (file: string) => {
118
120
  let mmf = new MMF1('store');
119
- let wasiMode = fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
121
+ let wasiMode = mode === 'wasi' || fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
120
122
 
121
123
  let promise = new Promise<void>((resolve) => {
122
124
  if (!mocPath) {
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- import { program } from 'commander';
3
+ import { program, Argument } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import { Principal } from '@dfinity/principal';
6
6
  import { init } from './commands/init.js';
@@ -19,6 +19,7 @@ import { selfUpdate } from './commands/self-update.js';
19
19
  import { remove } from './commands/remove.js';
20
20
  import { getUserProp, setUserProp } from './commands/user.js';
21
21
  import { bump } from './commands/bump.js';
22
+ import { sync } from './commands/sync.js';
22
23
  // import {docs} from './commands/docs.js';
23
24
  program.name('mops');
24
25
  // --version
@@ -148,8 +149,9 @@ program
148
149
  });
149
150
  // cache
150
151
  program
151
- .command('cache [sub-command]')
152
+ .command('cache')
152
153
  .description('Manage cache')
154
+ .addArgument(new Argument('<sub>').choices(['size', 'clean']))
153
155
  .action(async (sub) => {
154
156
  if (sub == 'clean') {
155
157
  await cleanCache();
@@ -159,9 +161,6 @@ program
159
161
  let size = await cacheSize();
160
162
  console.log('Cache size is ' + size);
161
163
  }
162
- else {
163
- console.log('Unknown sub command. Available sub commands: clean, size');
164
- }
165
164
  });
166
165
  // test
167
166
  program
@@ -169,7 +168,12 @@ program
169
168
  .description('Run tests')
170
169
  .option('-r, --reporter <reporter>', 'Choose reporter: verbose, compact, files')
171
170
  .option('-w, --watch', 'Enable watch mode')
171
+ .option('--mode <mode>', 'Test mode: \'interpreter\' or \'wasi\' (default \'interpreter\'')
172
172
  .action(async (filter, options) => {
173
+ if (options.mode && !['interpreter', 'wasi'].includes(options.mode)) {
174
+ console.log(`Unknown --mode value '${options.mode}'. Allowed: interpreter, wasi`);
175
+ process.exit(1);
176
+ }
173
177
  await test(filter, options);
174
178
  });
175
179
  // template
@@ -208,7 +212,10 @@ program
208
212
  });
209
213
  // user
210
214
  program
211
- .command('user set|get <prop> [value]')
215
+ .command('user')
216
+ .addArgument(new Argument('<sub>').choices(['set', 'get']))
217
+ .addArgument(new Argument('<prop>').choices(['name', 'site', 'email', 'github', 'twitter']))
218
+ .addArgument(new Argument('[value]'))
212
219
  .description('User settings')
213
220
  .action(async (sub, prop, value) => {
214
221
  if (sub == 'get') {
@@ -221,9 +228,6 @@ program
221
228
  }
222
229
  await setUserProp(prop, value);
223
230
  }
224
- else {
225
- console.log('Unknown sub command. Available sub commands: set, get');
226
- }
227
231
  });
228
232
  // airdrop
229
233
  program
@@ -263,4 +267,11 @@ program
263
267
  .action(async (part) => {
264
268
  bump(part);
265
269
  });
270
+ // sync
271
+ program
272
+ .command('sync')
273
+ .description('Add missing packages and remove unused packages')
274
+ .action(async () => {
275
+ await sync();
276
+ });
266
277
  program.parse();
@@ -67,7 +67,7 @@ export async function remove(name, { dev = false, verbose = false, dryRun = fals
67
67
  pkgDir = formatDir(dep.name, dep.version);
68
68
  }
69
69
  if (pkgDir && fs.existsSync(pkgDir)) {
70
- dryRun || deleteSync([`${pkgDir}`]);
70
+ dryRun || deleteSync([`${pkgDir}`], { force: true });
71
71
  verbose && console.log(`Removed local cache ${pkgDir}`);
72
72
  }
73
73
  }
@@ -0,0 +1 @@
1
+ export declare function sync(): Promise<void>;
@@ -0,0 +1,84 @@
1
+ import path from 'node:path';
2
+ import { execSync } from 'node:child_process';
3
+ import { globSync } from 'glob';
4
+ import chalk from 'chalk';
5
+ import { checkConfigFile, getRootDir, readConfig } from '../mops.js';
6
+ import { add } from './add.js';
7
+ import { remove } from './remove.js';
8
+ let ignore = [
9
+ '**/node_modules/**',
10
+ '**/.vessel/**',
11
+ '**/.git/**',
12
+ '**/.mops/**',
13
+ '**/test/**',
14
+ '**/*.test.mo',
15
+ ];
16
+ let mocPath = '';
17
+ function getMocPath() {
18
+ if (!mocPath) {
19
+ mocPath = process.env.DFX_MOC_PATH || '';
20
+ }
21
+ if (!mocPath) {
22
+ try {
23
+ mocPath = execSync('dfx cache show').toString().trim() + '/moc';
24
+ }
25
+ catch { }
26
+ }
27
+ if (!mocPath) {
28
+ mocPath = 'moc';
29
+ }
30
+ return mocPath;
31
+ }
32
+ async function getUsedPackages() {
33
+ let rootDir = getRootDir();
34
+ let files = globSync('**/*.mo', {
35
+ cwd: rootDir,
36
+ nocase: true,
37
+ ignore: ignore,
38
+ });
39
+ let packages = new Set;
40
+ for (let file of files) {
41
+ let deps = execSync(`${getMocPath()} --print-deps ${path.join(rootDir, file)}`).toString().trim().split('\n');
42
+ for (let dep of deps) {
43
+ if (dep.startsWith('mo:') && !dep.startsWith('mo:prim') && !dep.startsWith('mo:⛔')) {
44
+ packages.add(dep.replace(/^mo:([^/]+).*$/, '$1'));
45
+ }
46
+ }
47
+ }
48
+ return [...packages];
49
+ }
50
+ async function getMissingPackages() {
51
+ let config = readConfig();
52
+ let allDeps = [...Object.keys(config.dependencies || {}), ...Object.keys(config['dev-dependencies'] || {})];
53
+ let missing = new Set(await getUsedPackages());
54
+ for (let pkg of allDeps) {
55
+ missing.delete(pkg);
56
+ }
57
+ return [...missing];
58
+ }
59
+ async function getUnusedPackages() {
60
+ let config = readConfig();
61
+ let allDeps = new Set([...Object.keys(config.dependencies || {})]);
62
+ let used = await getUsedPackages();
63
+ for (let pkg of used) {
64
+ allDeps.delete(pkg);
65
+ }
66
+ return [...allDeps];
67
+ }
68
+ export async function sync() {
69
+ if (!checkConfigFile()) {
70
+ return;
71
+ }
72
+ let missing = await getMissingPackages();
73
+ let unused = await getUnusedPackages();
74
+ missing.length && console.log(`${chalk.yellow('Missing packages:')} ${missing.join(', ')}`);
75
+ unused.length && console.log(`${chalk.yellow('Unused packages:')} ${unused.join(', ')}`);
76
+ // add missing packages
77
+ for (let pkg of missing) {
78
+ await add(pkg);
79
+ }
80
+ // remove unused packages
81
+ for (let pkg of unused) {
82
+ await remove(pkg);
83
+ }
84
+ }
@@ -1,5 +1,8 @@
1
- export declare function test(filter?: string, { watch, reporter }?: {
1
+ type TestMode = 'interpreter' | 'wasi';
2
+ export declare function test(filter?: string, { watch, reporter, mode }?: {
2
3
  watch?: boolean | undefined;
3
4
  reporter?: string | undefined;
5
+ mode?: TestMode | undefined;
4
6
  }): Promise<void>;
5
- export declare function runAll(filter?: string, reporterName?: string): Promise<boolean | undefined>;
7
+ export declare function runAll(filter?: string, reporterName?: string, mode?: TestMode): Promise<boolean | undefined>;
8
+ export {};
@@ -24,7 +24,7 @@ let globConfig = {
24
24
  nocase: true,
25
25
  ignore: ignore,
26
26
  };
27
- export async function test(filter = '', { watch = false, reporter = 'verbose' } = {}) {
27
+ export async function test(filter = '', { watch = false, reporter = 'verbose', mode = 'interpreter' } = {}) {
28
28
  let rootDir = getRootDir();
29
29
  if (watch) {
30
30
  // todo: run only changed for *.test.mo?
@@ -50,14 +50,14 @@ export async function test(filter = '', { watch = false, reporter = 'verbose' }
50
50
  run();
51
51
  }
52
52
  else {
53
- let passed = await runAll(filter, reporter);
53
+ let passed = await runAll(filter, reporter, mode);
54
54
  if (!passed) {
55
55
  process.exit(1);
56
56
  }
57
57
  }
58
58
  }
59
59
  let mocPath = process.env.DFX_MOC_PATH;
60
- export async function runAll(filter = '', reporterName = 'verbose') {
60
+ export async function runAll(filter = '', reporterName = 'verbose', mode = 'interpreter') {
61
61
  let reporter;
62
62
  if (reporterName == 'compact') {
63
63
  reporter = new CompactReporter;
@@ -99,7 +99,7 @@ export async function runAll(filter = '', reporterName = 'verbose') {
99
99
  fs.mkdirSync(wasmDir, { recursive: true });
100
100
  await parallel(os.cpus().length, files, async (file) => {
101
101
  let mmf = new MMF1('store');
102
- let wasiMode = fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
102
+ let wasiMode = mode === 'wasi' || fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
103
103
  let promise = new Promise((resolve) => {
104
104
  if (!mocPath) {
105
105
  mocPath = 'moc';
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.21.1",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/cli.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ic-mops",
3
- "version": "0.21.1",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mops": "dist/cli.js"