ic-mops 0.6.6 → 0.7.1
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.js +6 -3
- package/commands/add.js +11 -4
- package/commands/install-all.js +4 -2
- package/commands/install.js +2 -1
- package/commands/mmf1.js +83 -0
- package/commands/publish.js +17 -2
- package/commands/sources.js +8 -4
- package/commands/test.js +107 -27
- package/mops.js +24 -17
- package/package.json +4 -1
package/cli.js
CHANGED
|
@@ -39,6 +39,7 @@ program
|
|
|
39
39
|
program
|
|
40
40
|
.command('add <pkg>')
|
|
41
41
|
.description('Install the package and save it to mops.toml')
|
|
42
|
+
.option('--dev')
|
|
42
43
|
.option('--verbose')
|
|
43
44
|
.action(async (pkg, options) => {
|
|
44
45
|
await add(pkg, options);
|
|
@@ -114,7 +115,8 @@ program
|
|
|
114
115
|
.option('--verbose')
|
|
115
116
|
.action(async (options) => {
|
|
116
117
|
await installAll({silent: true});
|
|
117
|
-
await sources(options);
|
|
118
|
+
let sourcesArr = await sources(options);
|
|
119
|
+
console.log(sourcesArr.join('\n'));
|
|
118
120
|
});
|
|
119
121
|
|
|
120
122
|
// whoami
|
|
@@ -155,8 +157,9 @@ program
|
|
|
155
157
|
program
|
|
156
158
|
.command('test')
|
|
157
159
|
.description('Run tests')
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
+
.option('--watch', 'Enable watch mode')
|
|
161
|
+
.action(async (options) => {
|
|
162
|
+
await test(options);
|
|
160
163
|
});
|
|
161
164
|
|
|
162
165
|
// // upgrade
|
package/commands/add.js
CHANGED
|
@@ -5,14 +5,21 @@ import {checkConfigFile, getHighestVersion, parseGithubURL, readConfig, writeCon
|
|
|
5
5
|
import {installFromGithub} from '../vessel.js';
|
|
6
6
|
import {install} from './install.js';
|
|
7
7
|
|
|
8
|
-
export async function add(name, {verbose} = {}) {
|
|
8
|
+
export async function add(name, {verbose, dev} = {}) {
|
|
9
9
|
if (!checkConfigFile()) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
let config = readConfig();
|
|
14
|
-
if (
|
|
15
|
-
config
|
|
14
|
+
if (dev) {
|
|
15
|
+
if (!config['dev-dependencies']) {
|
|
16
|
+
config['dev-dependencies'] = {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
if (!config.dependencies) {
|
|
21
|
+
config.dependencies = {};
|
|
22
|
+
}
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
let pkgDetails;
|
|
@@ -68,7 +75,7 @@ export async function add(name, {verbose} = {}) {
|
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
config
|
|
78
|
+
config[dev ? 'dev-dependencies' : 'dependencies'][pkgDetails.name] = pkgDetails;
|
|
72
79
|
writeConfig(config);
|
|
73
80
|
|
|
74
81
|
logUpdate.clear();
|
package/commands/install-all.js
CHANGED
|
@@ -10,9 +10,11 @@ export async function installAll({verbose, silent} = {}) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
let config = readConfig();
|
|
13
|
-
|
|
13
|
+
let deps = Object.values(config.dependencies || {});
|
|
14
|
+
let devDeps = Object.values(config['dev-dependencies'] || {});
|
|
15
|
+
let allDeps = [...deps, ...devDeps];
|
|
14
16
|
|
|
15
|
-
for (let {name, repo, path, version} of
|
|
17
|
+
for (let {name, repo, path, version} of allDeps) {
|
|
16
18
|
if (repo) {
|
|
17
19
|
await installFromGithub(name, repo, {verbose, silent});
|
|
18
20
|
}
|
package/commands/install.js
CHANGED
|
@@ -110,7 +110,8 @@ export async function install(pkg, version = '', {verbose, silent, dep} = {}) {
|
|
|
110
110
|
// install dependencies
|
|
111
111
|
let ok = true;
|
|
112
112
|
let config = readConfig(path.join(dir, 'mops.toml'));
|
|
113
|
-
|
|
113
|
+
let deps = Object.values(config.dependencies || {});
|
|
114
|
+
for (const {name, repo, version} of deps) {
|
|
114
115
|
if (repo) {
|
|
115
116
|
await installFromGithub(name, repo, {silent, verbose});
|
|
116
117
|
}
|
package/commands/mmf1.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// mops message format v1
|
|
2
|
+
// mops:1:start
|
|
3
|
+
// mops:1:end
|
|
4
|
+
// mops:1:skip
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
export class MMF1 {
|
|
8
|
+
stack = [];
|
|
9
|
+
currSuite = '';
|
|
10
|
+
failed = 0;
|
|
11
|
+
passed = 0;
|
|
12
|
+
skipped = 0;
|
|
13
|
+
|
|
14
|
+
parseLine(line) {
|
|
15
|
+
if (line.startsWith('mops:1:start ')) {
|
|
16
|
+
this._testStart(line.split('mops:1:start ')[1]);
|
|
17
|
+
}
|
|
18
|
+
else if (line.startsWith('mops:1:end ')) {
|
|
19
|
+
this._testEnd(line.split('mops:1:end ')[1]);
|
|
20
|
+
}
|
|
21
|
+
else if (line.startsWith('mops:1:skip ')) {
|
|
22
|
+
this._testSkip(line.split('mops:1:skip ')[1]);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
console.log(' '.repeat(this.stack.length * 2), chalk.gray('stdout'), line);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_testStart(name) {
|
|
30
|
+
if (this.stack.length) {
|
|
31
|
+
let suite = this.stack.at(-1);
|
|
32
|
+
if (this.currSuite !== suite) {
|
|
33
|
+
this.currSuite = suite;
|
|
34
|
+
console.log(' '.repeat((this.stack.length - 1) * 2), (chalk.gray('•')) + '', this.stack.at(-1));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
this.stack.push(name);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_testEnd(name) {
|
|
41
|
+
if (name !== this.stack.pop()) {
|
|
42
|
+
throw 'mmf1._testEnd: start and end test mismatch';
|
|
43
|
+
}
|
|
44
|
+
this._status(name, 'pass');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_testSkip(name) {
|
|
48
|
+
this._status(name, 'skip');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_status(name, status) {
|
|
52
|
+
if (status === 'pass') {
|
|
53
|
+
// do not print suite at the end
|
|
54
|
+
if (name === this.currSuite) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.passed++;
|
|
58
|
+
console.log(' '.repeat(this.stack.length * 2), chalk.green('✓'), name);
|
|
59
|
+
}
|
|
60
|
+
else if (status === 'fail') {
|
|
61
|
+
this.failed++;
|
|
62
|
+
console.log(' '.repeat(this.stack.length * 2), chalk.red('×'), name);
|
|
63
|
+
}
|
|
64
|
+
else if (status === 'skip') {
|
|
65
|
+
this.skipped++;
|
|
66
|
+
console.log(' '.repeat(this.stack.length * 2), chalk.yellow('−'), name);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fail(stderr) {
|
|
71
|
+
let name = this.stack.pop() || '';
|
|
72
|
+
this._status(name, 'fail');
|
|
73
|
+
console.log(' '.repeat(this.stack.length * 2), chalk.red('FAIL'), stderr);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pass() {
|
|
77
|
+
let name = this.stack.pop();
|
|
78
|
+
if (name) {
|
|
79
|
+
this._status(name, 'pass');
|
|
80
|
+
}
|
|
81
|
+
console.log(' '.repeat(this.stack.length * 2), chalk.green('PASS'));
|
|
82
|
+
}
|
|
83
|
+
}
|
package/commands/publish.js
CHANGED
|
@@ -17,7 +17,7 @@ export async function publish() {
|
|
|
17
17
|
|
|
18
18
|
// validate
|
|
19
19
|
for (let key of Object.keys(config)) {
|
|
20
|
-
if (!['package', 'dependencies', 'scripts'].includes(key)) {
|
|
20
|
+
if (!['package', 'dependencies', 'dev-dependencies', 'scripts'].includes(key)) {
|
|
21
21
|
console.log(chalk.red('Error: ') + `Unknown config section [${key}]`);
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
@@ -112,6 +112,21 @@ export async function publish() {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
if (config['dev-dependencies']) {
|
|
116
|
+
if (Object.keys(config['dev-dependencies']).length > 100) {
|
|
117
|
+
console.log(chalk.red('Error: ') + 'max dev-dependencies is 100');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (let dep of Object.values(config['dev-dependencies'])) {
|
|
122
|
+
if (dep.path) {
|
|
123
|
+
console.log(chalk.red('Error: ') + 'you can\'t publish packages with local dev-dependencies');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
delete dep.path;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
115
130
|
if (config.package.keywords) {
|
|
116
131
|
for (let keyword of config.package.keywords) {
|
|
117
132
|
if (keyword.length > 20) {
|
|
@@ -147,7 +162,7 @@ export async function publish() {
|
|
|
147
162
|
moc: config.package.moc || '',
|
|
148
163
|
donation: config.package.donation || '',
|
|
149
164
|
dependencies: Object.values(config.dependencies || {}),
|
|
150
|
-
devDependencies: [],
|
|
165
|
+
devDependencies: Object.values(config['dev-dependencies'] || {}),
|
|
151
166
|
scripts: [],
|
|
152
167
|
};
|
|
153
168
|
|
package/commands/sources.js
CHANGED
|
@@ -59,7 +59,11 @@ export async function sources({verbose} = {}) {
|
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
let collectDeps = async (config, isRoot = false) => {
|
|
62
|
-
|
|
62
|
+
let allDeps = [...Object.values(config.dependencies || {})];
|
|
63
|
+
if (isRoot) {
|
|
64
|
+
allDeps = [...allDeps, ...Object.values(config['dev-dependencies'] || {})];
|
|
65
|
+
}
|
|
66
|
+
for (const pkgDetails of allDeps) {
|
|
63
67
|
const {name, repo, version} = pkgDetails;
|
|
64
68
|
|
|
65
69
|
// take root dep version or bigger one
|
|
@@ -117,7 +121,7 @@ export async function sources({verbose} = {}) {
|
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
// sources
|
|
120
|
-
|
|
124
|
+
return Object.entries(packages).map(([name, pkg]) => {
|
|
121
125
|
let pkgDir;
|
|
122
126
|
if (pkg.path) {
|
|
123
127
|
pkgDir = path.relative(process.cwd(), path.resolve(pkg.path));
|
|
@@ -139,6 +143,6 @@ export async function sources({verbose} = {}) {
|
|
|
139
143
|
pkgBaseDir = path.join(pkgDir, 'src');
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
}
|
|
146
|
+
return `--package ${name} ${pkgBaseDir}`;
|
|
147
|
+
});
|
|
144
148
|
}
|
package/commands/test.js
CHANGED
|
@@ -1,17 +1,59 @@
|
|
|
1
|
-
import {execSync} from 'child_process';
|
|
1
|
+
import {spawn, execSync} from 'child_process';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import glob from 'glob';
|
|
4
|
+
import chokidar from 'chokidar';
|
|
5
|
+
import debounce from 'debounce';
|
|
6
|
+
import {MMF1} from './mmf1.js';
|
|
7
|
+
import {sources} from './sources.js';
|
|
8
|
+
|
|
9
|
+
let ignore = [
|
|
10
|
+
'**/node_modules/**',
|
|
11
|
+
'**/.mops/**',
|
|
12
|
+
'**/.vessel/**',
|
|
13
|
+
'**/.git/**',
|
|
14
|
+
];
|
|
4
15
|
|
|
5
16
|
let globConfig = {
|
|
6
17
|
nocase: true,
|
|
7
|
-
ignore:
|
|
8
|
-
'**/node_modules/**',
|
|
9
|
-
'**/.mops/**',
|
|
10
|
-
'**/.vessel/**',
|
|
11
|
-
],
|
|
18
|
+
ignore: ignore,
|
|
12
19
|
};
|
|
13
20
|
|
|
14
|
-
export async function test() {
|
|
21
|
+
export async function test({watch = false} = {}) {
|
|
22
|
+
if (watch) {
|
|
23
|
+
// todo: run only changed for *.test.mo?
|
|
24
|
+
// todo: run all for *.mo?
|
|
25
|
+
let run = debounce(async () => {
|
|
26
|
+
console.clear();
|
|
27
|
+
process.stdout.write('\x1Bc');
|
|
28
|
+
await runAll();
|
|
29
|
+
console.log('-'.repeat(50));
|
|
30
|
+
console.log('Waiting for file changes...');
|
|
31
|
+
console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
|
|
32
|
+
}, 200);
|
|
33
|
+
|
|
34
|
+
let watcher = chokidar.watch('**/*.mo', {
|
|
35
|
+
ignored: ignore,
|
|
36
|
+
ignoreInitial: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
watcher.on('all', () => {
|
|
40
|
+
run();
|
|
41
|
+
});
|
|
42
|
+
run();
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
let failed = await runAll();
|
|
46
|
+
if (failed) {
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let dfxCache;
|
|
53
|
+
|
|
54
|
+
export async function runAll() {
|
|
55
|
+
let start = Date.now();
|
|
56
|
+
|
|
15
57
|
let files = [];
|
|
16
58
|
let libFiles = glob.sync('**/test?(s)/lib.mo', globConfig);
|
|
17
59
|
if (libFiles.length) {
|
|
@@ -22,6 +64,7 @@ export async function test() {
|
|
|
22
64
|
}
|
|
23
65
|
if (!files.length) {
|
|
24
66
|
console.log('No test files found');
|
|
67
|
+
console.log('Put your tests in \'test\' directory in *.test.mo files');
|
|
25
68
|
return;
|
|
26
69
|
}
|
|
27
70
|
|
|
@@ -31,31 +74,68 @@ export async function test() {
|
|
|
31
74
|
}
|
|
32
75
|
console.log('-'.repeat(50));
|
|
33
76
|
|
|
34
|
-
let start = Date.now();
|
|
35
77
|
let failed = 0;
|
|
36
78
|
let passed = 0;
|
|
37
|
-
let
|
|
38
|
-
let
|
|
79
|
+
let skipped = 0;
|
|
80
|
+
let sourcesArr = await sources();
|
|
81
|
+
if (!dfxCache) {
|
|
82
|
+
dfxCache = execSync('dfx cache show').toString().trim();
|
|
83
|
+
}
|
|
39
84
|
|
|
40
85
|
for (let file of files) {
|
|
41
|
-
|
|
86
|
+
let mmf1 = new MMF1;
|
|
87
|
+
|
|
88
|
+
await new Promise((resolve) => {
|
|
42
89
|
console.log(`Running ${chalk.gray(file)}`);
|
|
43
|
-
execSync(`${dfxCache}/moc -r -wasi-system-api --hide-warnings --error-detail 2 ${mopsSources} ${file}`, {stdio: 'pipe'});
|
|
44
|
-
console.log(' ', chalk.green('PASS'));
|
|
45
|
-
passed++;
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
failed++;
|
|
49
|
-
if (err.status === 1) {
|
|
50
|
-
console.log(' ', chalk.red('FAIL'), err.stderr.toString().trim());
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
console.log(chalk.red('Unknown status:'), err.status);
|
|
54
|
-
console.log(err.message);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
90
|
|
|
91
|
+
let proc = spawn(`${dfxCache}/moc`, ['-r', '-wasi-system-api', '-ref-system-api', '--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file]);
|
|
92
|
+
|
|
93
|
+
// stdout
|
|
94
|
+
proc.stdout.on('data', (data) => {
|
|
95
|
+
for (let line of data.toString().split('\n')) {
|
|
96
|
+
line = line.trim();
|
|
97
|
+
if (line) {
|
|
98
|
+
mmf1.parseLine(line);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// stderr
|
|
104
|
+
proc.stderr.on('data', (data) => {
|
|
105
|
+
let text = data.toString().trim();
|
|
106
|
+
text = text.replace(/:(\d+).(\d+)(-\d+.\d+)/, ':$1:$2');
|
|
107
|
+
mmf1.fail(text);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// exit
|
|
111
|
+
proc.on('exit', (code) => {
|
|
112
|
+
if (code === 0) {
|
|
113
|
+
mmf1.pass();
|
|
114
|
+
}
|
|
115
|
+
else if (code !== 1) {
|
|
116
|
+
console.log(chalk.red('unknown code:'), code);
|
|
117
|
+
}
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
passed += mmf1.passed;
|
|
123
|
+
failed += mmf1.failed;
|
|
124
|
+
skipped += mmf1.skipped;
|
|
125
|
+
}
|
|
59
126
|
console.log('-'.repeat(50));
|
|
60
|
-
|
|
127
|
+
if (failed) {
|
|
128
|
+
console.log(chalk.redBright('Tests failed'));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
console.log(chalk.greenBright('Tests passed'));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(`Done in ${chalk.gray(((Date.now() - start) / 1000).toFixed(2) + 's')}`
|
|
135
|
+
+ `, passed ${chalk.greenBright(passed)}`
|
|
136
|
+
+ (skipped ? `, skipped ${chalk[skipped ? 'yellowBright' : 'gray'](skipped)}` : '')
|
|
137
|
+
+ (failed ? `, failed ${chalk[failed ? 'redBright' : 'gray'](failed)}` : '')
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return failed === 0;
|
|
61
141
|
}
|
package/mops.js
CHANGED
|
@@ -124,33 +124,40 @@ export function readConfig(configFile = path.join(process.cwd(), 'mops.toml')) {
|
|
|
124
124
|
let text = fs.readFileSync(configFile).toString();
|
|
125
125
|
let toml = TOML.parse(text);
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
}
|
|
127
|
+
let processDeps = (deps) => {
|
|
128
|
+
Object.entries(deps).forEach(([name, data]) => {
|
|
129
|
+
if (!data || typeof data !== 'string') {
|
|
130
|
+
throw Error(`Invalid dependency value ${name} = "${data}"`);
|
|
131
|
+
}
|
|
132
|
+
if (data.startsWith('https://github.com/')) {
|
|
133
|
+
deps[name] = {name, repo: data, version: ''};
|
|
134
|
+
}
|
|
135
|
+
else if (data.match(/^(\.?\.)?\//)) {
|
|
136
|
+
deps[name] = {name, repo: '', path: data, version: ''};
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
deps[name] = {name, repo: '', version: data};
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
processDeps(toml.dependencies || {});
|
|
145
|
+
processDeps(toml['dev-dependencies'] || {});
|
|
143
146
|
|
|
144
147
|
return toml;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
export function writeConfig(config, configFile = path.join(process.cwd(), 'mops.toml')) {
|
|
148
151
|
const deps = config.dependencies || {};
|
|
149
|
-
|
|
150
152
|
Object.entries(deps).forEach(([name, {repo, path, version}]) => {
|
|
151
153
|
deps[name] = repo || path || version;
|
|
152
154
|
});
|
|
153
155
|
|
|
156
|
+
const devDeps = config['dev-dependencies'] || {};
|
|
157
|
+
Object.entries(devDeps).forEach(([name, {repo, path, version}]) => {
|
|
158
|
+
devDeps[name] = repo || path || version;
|
|
159
|
+
});
|
|
160
|
+
|
|
154
161
|
fs.writeFileSync(configFile, TOML.stringify(config).trim());
|
|
155
162
|
}
|
|
156
163
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ic-mops",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mops": "cli.js"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"url": "https://github.com/ZenVoich/mops.git"
|
|
12
12
|
},
|
|
13
13
|
"author": "Zen Voich <zen.voich@gmail.com>",
|
|
14
|
+
"license": "MIT",
|
|
14
15
|
"dependencies": {
|
|
15
16
|
"@dfinity/agent": "^0.11.0",
|
|
16
17
|
"@dfinity/candid": "^0.11.0",
|
|
@@ -19,7 +20,9 @@
|
|
|
19
20
|
"@iarna/toml": "^2.2.5",
|
|
20
21
|
"as-table": "^1.0.55",
|
|
21
22
|
"chalk": "^4.1.2",
|
|
23
|
+
"chokidar": "^3.5.3",
|
|
22
24
|
"commander": "^9.2.0",
|
|
25
|
+
"debounce": "^1.2.1",
|
|
23
26
|
"decompress": "^4.2.1",
|
|
24
27
|
"del": "^6.0.0",
|
|
25
28
|
"dhall-to-json-cli": "^1.7.6",
|