unexpectedly 0.0.7 → 1.0.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/README.md +57 -20
- package/bin/unexpectedly.js +32 -0
- package/index.js +108 -76
- package/modtype.js +60 -0
- package/package.json +29 -21
- package/runner.js +129 -58
- package/testFile.peg.js +429 -491
- package/utils.js +41 -26
- package/bin/unexpectedly +0 -5
package/README.md
CHANGED
|
@@ -4,38 +4,64 @@ Write a bunch of unit tests in a simple file.
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```sh
|
|
8
8
|
npm install -g unexpectedly
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
unexpectedly [file|directory
|
|
14
|
+
unexpectedly [--defaultScript jsPattern] <...file|directory>
|
|
15
|
+
jsPattern (default: "../$<base>.js"): find the JS file relative to
|
|
16
|
+
the .tests file, replacing `$<base>` with the basename of the test file.
|
|
17
|
+
This may be specified multiple times, and modifies all files/directories
|
|
18
|
+
after.
|
|
15
19
|
file: a file that contains tests
|
|
16
20
|
directory (default: "."): a directory containing ".tests" files
|
|
17
|
-
jsPattern (default: "../$1.js"): find the JS file relative to
|
|
18
|
-
the .tests file, replacing $1 with the basename of the test file
|
|
19
21
|
```
|
|
20
22
|
|
|
21
23
|
## Example
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
This is the easiest case, when the function that you want to test is already
|
|
26
|
+
exposed as the default export or exported as a function named `test`:
|
|
27
|
+
|
|
28
|
+
tests/bar.tests:
|
|
24
29
|
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
8 7 1
|
|
30
|
+
# "8" is the expected result, "4" and "2" are the inputs to the tested function
|
|
31
|
+
8 4 2
|
|
28
32
|
```
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
bar.js:
|
|
35
|
+
```js
|
|
36
|
+
// The default or `test` export from the associated .js file is the
|
|
37
|
+
// tested function by default
|
|
38
|
+
export default mult(a, b) {
|
|
39
|
+
return a * b;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function baz(a) {
|
|
43
|
+
return a * 6;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
To test the `baz` function, we needed to write a little wrapper code:
|
|
48
|
+
|
|
49
|
+
tests/baz.tests:
|
|
31
50
|
```
|
|
32
|
-
|
|
51
|
+
#! inline: `export {baz as test} from '../foo.js'`
|
|
52
|
+
6 1 # Expect 6 to result from `baz("1")`
|
|
53
|
+
12 2
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
run:
|
|
57
|
+
```sh
|
|
58
|
+
unexpectedly tests/
|
|
33
59
|
```
|
|
34
60
|
|
|
35
61
|
## API
|
|
36
62
|
|
|
37
63
|
```js
|
|
38
|
-
|
|
64
|
+
import {suite} from 'unexpectedly';
|
|
39
65
|
|
|
40
66
|
// returns a Promise
|
|
41
67
|
suite(directoryOrFile, jsPattern).catch(console.error)
|
|
@@ -57,11 +83,12 @@ test"
|
|
|
57
83
|
```
|
|
58
84
|
|
|
59
85
|
Aggressive type coercion is used, including if the test JavaScript
|
|
60
|
-
returns an non-string the result string is parsed as
|
|
86
|
+
returns an non-string the result string is parsed as JavaScript before
|
|
61
87
|
comparison.
|
|
62
88
|
|
|
63
89
|
Set file-wide globals with lines that start with `#!`, separating
|
|
64
|
-
the name and the value with a colon
|
|
90
|
+
the name and the value with a colon. These globals will be made available
|
|
91
|
+
to your test script as global variables:
|
|
65
92
|
|
|
66
93
|
```
|
|
67
94
|
#! name: value
|
|
@@ -75,25 +102,32 @@ Special globals include:
|
|
|
75
102
|
- "timeout": If any test in the file takes longer than this (in ms),
|
|
76
103
|
it fails. Default: 2000
|
|
77
104
|
|
|
105
|
+
Environment variables can be set for the test code with `#!! name: value`.
|
|
106
|
+
|
|
78
107
|
## JavaScript to test
|
|
79
108
|
|
|
80
109
|
You can specify JavaScript to run for each test in one of three ways:
|
|
81
110
|
|
|
82
111
|
- Use the mapping from test file name to a JavaScript file. By default, this
|
|
83
112
|
takes the basename of the .tests file and looks up one directory for a .js
|
|
84
|
-
file of the same basename. So `tests/foo.tests` looks for `foo.js`. If
|
|
85
|
-
you can replace the mapping with something else, that uses
|
|
86
|
-
for the basename.
|
|
113
|
+
file of the same basename. So `tests/foo.tests` looks for `foo.js`. If
|
|
114
|
+
needed you can replace the mapping with something else, that uses `$<base>`
|
|
115
|
+
as a placeholder for the basename.
|
|
87
116
|
- Specify a global in your .tests file with the name `script`. That is the name
|
|
88
117
|
of the file, relative to the directory that contains the .tests file, to look
|
|
89
118
|
for JavaScript.
|
|
90
119
|
- Specify a global in your .tests file with the name `inline`. That's just
|
|
91
120
|
JavaScript to run per-line.
|
|
92
121
|
|
|
93
|
-
|
|
94
|
-
export a function named `test`. For inline code, if you
|
|
95
|
-
`
|
|
96
|
-
can just put in a simple function.
|
|
122
|
+
In es6 module projects, for each of the above, make sure `export default` is a
|
|
123
|
+
function, or that you export a function named `test`. For inline code, if you
|
|
124
|
+
don't export anything, `export default` will be added to the beginning of your
|
|
125
|
+
code, so that you can just put in a simple function.
|
|
126
|
+
|
|
127
|
+
In commonjs projects, for each of the above, make sure `module.exports` is a
|
|
128
|
+
function, or that you export a function named `test`. For inline code, if you
|
|
129
|
+
don't export anything, `module.exports=` will be added to the beginning of
|
|
130
|
+
your code, so that you can just put in a simple function.
|
|
97
131
|
|
|
98
132
|
Special globals available in all forms of the JavaScript above include:
|
|
99
133
|
|
|
@@ -101,3 +135,6 @@ Special globals available in all forms of the JavaScript above include:
|
|
|
101
135
|
- `__expected` is the string form of the expected result
|
|
102
136
|
- `__line` is the line number where the test starts
|
|
103
137
|
- `__column` is the column where the test text starts
|
|
138
|
+
|
|
139
|
+
[](https://github.com/hildjj/unexpectedly/actions/workflows/node.js.yml)
|
|
140
|
+
[](https://codecov.io/gh/hildjj/unexpectedly)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env -S node --experimental-vm-modules --no-warnings
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
|
|
4
|
+
import {suite} from '../index.js';
|
|
5
|
+
|
|
6
|
+
let failures = 0;
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
let defaultScript = undefined;
|
|
10
|
+
let nextDefault = false;
|
|
11
|
+
for (const arg of process.argv.slice(2)) {
|
|
12
|
+
if (nextDefault) {
|
|
13
|
+
defaultScript = arg;
|
|
14
|
+
nextDefault = false;
|
|
15
|
+
} else if (arg === '--defaultScript') {
|
|
16
|
+
nextDefault = true;
|
|
17
|
+
} else {
|
|
18
|
+
// eslint-disable-next-line require-atomic-updates
|
|
19
|
+
failures += await suite(arg, defaultScript);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await main().catch(er => {
|
|
25
|
+
console.error(er);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (failures) {
|
|
30
|
+
console.error(`Total of ${failures} failure${failures === 1 ? '' : 's'}`);
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|
package/index.js
CHANGED
|
@@ -1,115 +1,147 @@
|
|
|
1
|
-
|
|
1
|
+
import {hexlify, mapObj} from './utils.js';
|
|
2
|
+
import Mocha from 'mocha';
|
|
3
|
+
import {Runner} from './runner.js';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import {parse} from './testFile.peg.js';
|
|
7
|
+
import path from 'node:path';
|
|
2
8
|
|
|
3
|
-
const
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const util = require('util')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const Mocha = require('mocha') // TODO: just use one of the reporters
|
|
8
|
-
const parser = require('./testFile.peg')
|
|
9
|
-
const Runner = require('./runner')
|
|
10
|
-
const { mapObj } = require('./utils')
|
|
9
|
+
const EXCEPTION = Symbol('EXCEPTION');
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// TODO: once the warning goes away
|
|
15
|
-
// const fsp = require('fs').promises
|
|
16
|
-
const fsp = mapObj(['readdir', 'readFile', 'stat'],
|
|
17
|
-
s => util.promisify(fs[s]))
|
|
18
|
-
|
|
19
|
-
function coerce (actual, expected) {
|
|
11
|
+
async function coerce(actual, pt, runner) {
|
|
12
|
+
const {expected} = pt;
|
|
20
13
|
if (typeof actual === 'string') {
|
|
21
|
-
return [actual, expected]
|
|
14
|
+
return [actual, expected];
|
|
22
15
|
}
|
|
23
16
|
if (actual && (typeof actual === 'object') && actual.constructor) {
|
|
24
|
-
|
|
17
|
+
if (/^0x/.test(expected)) {
|
|
18
|
+
const {name} = actual.constructor;
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
switch (name) {
|
|
21
|
+
case 'Buffer':
|
|
22
|
+
return [`0x${actual.toString('hex')}`, expected];
|
|
23
|
+
case 'Float32Array':
|
|
24
|
+
case 'Float64Array':
|
|
25
|
+
case 'Int16Array':
|
|
26
|
+
case 'Int32Array':
|
|
27
|
+
case 'Int8Array':
|
|
28
|
+
case 'Uint16Array':
|
|
29
|
+
case 'Uint32Array':
|
|
30
|
+
case 'Uint8Array':
|
|
31
|
+
case 'Uint8ClampedArray':
|
|
32
|
+
return [`0x${hexlify(actual)}`, expected];
|
|
33
|
+
}
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
|
-
|
|
36
|
+
|
|
37
|
+
const exp = await runner.parse(expected, {
|
|
38
|
+
__line: pt.line,
|
|
39
|
+
__column: pt.column,
|
|
40
|
+
});
|
|
41
|
+
const def = Object.hasOwn(exp, 'default') ? exp.default : exp;
|
|
42
|
+
return [actual, def];
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
/**
|
|
37
|
-
* Run a test suite from a file or directory
|
|
46
|
+
* Run a test suite from a file or directory.
|
|
38
47
|
*
|
|
39
|
-
* @param {string} [target='.'] The file or directory to read. If a
|
|
40
|
-
* reads all files ending in `.
|
|
41
|
-
* @param {string} [defaultScript='
|
|
42
|
-
* name. Replace $1 with the basename of the file. For example,
|
|
43
|
-
* will use "../foo.js" by default
|
|
44
|
-
* @returns Promise fulfills on
|
|
48
|
+
* @param {string} [target='.'] The file or directory to read. If a
|
|
49
|
+
* directory, reads all files ending in `.tests`.
|
|
50
|
+
* @param {string} [defaultScript='../$<base>.js'] Find the script from the
|
|
51
|
+
* file name. Replace $1 with the basename of the file. For example,
|
|
52
|
+
* "foo.test" will use "../foo.js" by default.
|
|
53
|
+
* @returns {Promise<number>} Promise fulfills on completion, with number of
|
|
54
|
+
* test failures.
|
|
45
55
|
*/
|
|
46
|
-
async function suite
|
|
47
|
-
let dir = path.resolve(target)
|
|
48
|
-
let files = [dir]
|
|
56
|
+
export async function suite(target = '.', defaultScript = '../$<base>.js') {
|
|
57
|
+
let dir = path.resolve(target);
|
|
58
|
+
let files = [dir];
|
|
59
|
+
const mocha = new Mocha();
|
|
60
|
+
mocha.suite.title = dir;
|
|
49
61
|
|
|
50
|
-
const stats = await
|
|
62
|
+
const stats = await fs.stat(dir);
|
|
51
63
|
if (stats.isDirectory()) {
|
|
52
|
-
files = (await
|
|
53
|
-
.filter(f => f
|
|
54
|
-
.map(f => path.join(dir, f))
|
|
64
|
+
files = (await fs.readdir(target))
|
|
65
|
+
.filter(f => /\.tests?$/.test(f))
|
|
66
|
+
.map(f => path.join(dir, f));
|
|
55
67
|
} else if (stats.isFile()) {
|
|
56
|
-
|
|
57
|
-
dir =
|
|
68
|
+
// eslint-disable-next-line require-atomic-updates
|
|
69
|
+
({dir} = path.parse(target));
|
|
70
|
+
} else {
|
|
71
|
+
throw new Error(`Unknown file type: "${target}"`);
|
|
58
72
|
}
|
|
59
|
-
const mocha = new Mocha()
|
|
60
73
|
|
|
61
74
|
for (const f of files) {
|
|
62
|
-
const
|
|
63
|
-
const contents = await
|
|
64
|
-
const parsed = parser.parse(contents, { EXCEPTION })
|
|
75
|
+
const msuite = Mocha.Suite.create(mocha.suite, f);
|
|
76
|
+
const contents = await fs.readFile(f, 'utf8');
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
78
|
+
let parsed = null;
|
|
79
|
+
try {
|
|
80
|
+
parsed = parse(contents, {EXCEPTION, grammarSource: f});
|
|
81
|
+
} catch (er) {
|
|
82
|
+
if (typeof er.format === 'function') {
|
|
83
|
+
er.message = er.format([{
|
|
84
|
+
source: f,
|
|
85
|
+
text: contents,
|
|
86
|
+
}]);
|
|
87
|
+
delete er.expected;
|
|
88
|
+
delete er.location;
|
|
72
89
|
}
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
throw er;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const opts = {};
|
|
94
|
+
let text = '';
|
|
95
|
+
if (parsed.vars.inline) {
|
|
96
|
+
opts.filename = f;
|
|
97
|
+
text = parsed.vars.inline.value;
|
|
98
|
+
opts.lineOffset = parsed.vars.inline.line - 1;
|
|
99
|
+
opts.columnOffset = parsed.vars.inline.column;
|
|
75
100
|
} else {
|
|
76
|
-
opts.filename = parsed.vars.script
|
|
77
|
-
|
|
78
|
-
|
|
101
|
+
opts.filename = parsed.vars.script ?
|
|
102
|
+
path.resolve(dir, parsed.vars.script.value) :
|
|
103
|
+
path.resolve(dir, f.replace(/(?<base>[^./]*)\.tests?$/, defaultScript));
|
|
104
|
+
text = await fs.readFile(opts.filename, 'utf8');
|
|
79
105
|
}
|
|
80
106
|
if (parsed.vars.timeout) {
|
|
81
|
-
|
|
107
|
+
msuite.timeout(parseInt(parsed.vars.timeout.value, 10));
|
|
82
108
|
}
|
|
83
109
|
opts.sandbox = mapObj(
|
|
84
|
-
Object.entries(parsed.vars),
|
|
85
|
-
([key, value]) => [key, value.value]
|
|
86
|
-
|
|
87
|
-
|
|
110
|
+
Object.entries(parsed.vars).filter(([k, v]) => !v.env),
|
|
111
|
+
([key, value]) => [key, value.value]
|
|
112
|
+
);
|
|
113
|
+
opts.env = mapObj(
|
|
114
|
+
Object.entries(parsed.vars).filter(([k, v]) => v.env),
|
|
115
|
+
([key, value]) => [key, value.value]
|
|
116
|
+
);
|
|
117
|
+
const runner = new Runner(opts);
|
|
88
118
|
|
|
89
119
|
for (const pt of parsed.tests) {
|
|
90
|
-
const
|
|
91
|
-
|
|
120
|
+
const firstLine = (pt.expected === EXCEPTION) ? '!' : pt.expected.split(/\n/)[0];
|
|
121
|
+
const t = new Mocha.Test(`line ${pt.line}: ${firstLine || '""'}`, async() => {
|
|
122
|
+
let actual = null;
|
|
92
123
|
try {
|
|
93
|
-
actual = await runner.run({
|
|
124
|
+
actual = await runner.run(text, {
|
|
94
125
|
__expected: pt.expected,
|
|
95
126
|
__line: pt.line,
|
|
96
|
-
__column: pt.column
|
|
97
|
-
}, ...pt.inputs)
|
|
127
|
+
__column: pt.column,
|
|
128
|
+
}, ...pt.inputs);
|
|
98
129
|
} catch (e) {
|
|
99
|
-
|
|
100
|
-
|
|
130
|
+
if (pt.expected !== EXCEPTION) {
|
|
131
|
+
throw e;
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
101
134
|
}
|
|
102
|
-
assert.notStrictEqual(pt.expected, EXCEPTION)
|
|
103
|
-
assert.
|
|
104
|
-
})
|
|
105
|
-
|
|
135
|
+
assert.notStrictEqual(pt.expected, EXCEPTION);
|
|
136
|
+
assert.deepEqual.apply(null, await coerce(actual, pt, runner));
|
|
137
|
+
});
|
|
138
|
+
msuite.addTest(t);
|
|
106
139
|
}
|
|
107
140
|
}
|
|
108
141
|
|
|
109
142
|
return new Promise((resolve, reject) => {
|
|
110
|
-
mocha.run(resolve)
|
|
111
|
-
})
|
|
143
|
+
mocha.run(resolve);
|
|
144
|
+
});
|
|
112
145
|
}
|
|
113
146
|
|
|
114
|
-
suite.EXCEPTION = EXCEPTION
|
|
115
|
-
module.exports = suite
|
|
147
|
+
suite.EXCEPTION = EXCEPTION;
|
package/modtype.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export class ModType {
|
|
5
|
+
static #cache = new Map(); // Filename => 'commonjs', 'module', 'unknown'
|
|
6
|
+
|
|
7
|
+
static async #readPkg(dir) {
|
|
8
|
+
const pkg = path.join(dir, 'package.json');
|
|
9
|
+
try {
|
|
10
|
+
const {type = 'commonjs'} = JSON.parse(await fs.readFile(pkg, 'utf8'));
|
|
11
|
+
return type;
|
|
12
|
+
} catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static async find(name, dir = false, root = undefined) {
|
|
18
|
+
const prev = this.#cache.get(name);
|
|
19
|
+
if (prev) {
|
|
20
|
+
return prev;
|
|
21
|
+
}
|
|
22
|
+
let res = 'unknown';
|
|
23
|
+
|
|
24
|
+
if (!root) {
|
|
25
|
+
const p = path.parse(name);
|
|
26
|
+
switch (p.ext) {
|
|
27
|
+
case '.mjs':
|
|
28
|
+
res = 'module';
|
|
29
|
+
this.#cache.set(name, res);
|
|
30
|
+
return res;
|
|
31
|
+
case '.cjs':
|
|
32
|
+
res = 'commonjs';
|
|
33
|
+
this.#cache.set(name, res);
|
|
34
|
+
return res;
|
|
35
|
+
}
|
|
36
|
+
({root} = p);
|
|
37
|
+
try {
|
|
38
|
+
// Avoid problems if an existing directory name is passed in originally.
|
|
39
|
+
dir = (await fs.stat(name)).isDirectory();
|
|
40
|
+
} catch {
|
|
41
|
+
// File doesn't exist. Safe to assume it's file-ish, keep dir = false.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (name !== root) { // Never go all the way to the root directory.
|
|
46
|
+
const t = dir ? await this.#readPkg(name) : undefined;
|
|
47
|
+
res = t ?? this.find(path.dirname(name), true, root);
|
|
48
|
+
}
|
|
49
|
+
this.#cache.set(name, res);
|
|
50
|
+
return res;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static clear() {
|
|
54
|
+
this.#cache.clear();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get cacheSize() {
|
|
58
|
+
return this.#cache.size;
|
|
59
|
+
}
|
|
60
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unexpectedly",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Keep a bunch of test cases in a file, with their expected results",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"pretest": "npm run
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
8
|
+
"build": "peggy -o testFile.peg.js --format es testFile.pegjs",
|
|
9
|
+
"pretest": "npm run build",
|
|
10
|
+
"test": "c8 npm run runtests",
|
|
11
|
+
"runtests": "mocha && bin/unexpectedly.js test test/pkg_mjs/test test/pkg_cjs/test",
|
|
12
|
+
"lint": "eslint --ext js,cjs,mjs . bin/*",
|
|
13
|
+
"ci": "npm run lint && npm run test",
|
|
14
|
+
"clean": "rimraf coverage testFile.peg.js"
|
|
12
15
|
},
|
|
13
16
|
"bin": {
|
|
14
|
-
"unexpectedly": "bin/unexpectedly"
|
|
17
|
+
"unexpectedly": "bin/unexpectedly.js"
|
|
15
18
|
},
|
|
16
19
|
"keywords": [
|
|
17
20
|
"test",
|
|
@@ -23,25 +26,30 @@
|
|
|
23
26
|
"harness"
|
|
24
27
|
],
|
|
25
28
|
"author": "Joe Hildebrand <joe-github@cursive.net>",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "http://github.com/hildjj/unexpectedly.git"
|
|
32
|
+
},
|
|
26
33
|
"license": "MPL-2.0",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"mocha": "^10.2.0"
|
|
36
|
+
},
|
|
27
37
|
"devDependencies": {
|
|
28
|
-
"eslint": "
|
|
29
|
-
"
|
|
30
|
-
"eslint
|
|
38
|
+
"@cto.af/eslint-config": "3.0.2",
|
|
39
|
+
"c8": "8.0.1",
|
|
40
|
+
"eslint": "^8.54.0",
|
|
41
|
+
"eslint-config-standard": "^17.1.0",
|
|
42
|
+
"eslint-plugin-import": "^2.29.0",
|
|
43
|
+
"eslint-plugin-jsdoc": "46.9.0",
|
|
44
|
+
"eslint-plugin-n": "16.3.1",
|
|
31
45
|
"eslint-plugin-node": "^11.1.0",
|
|
32
|
-
"eslint-plugin-promise": "^
|
|
46
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
33
47
|
"eslint-plugin-standard": "^4.1.0",
|
|
34
|
-
"peggy": "^
|
|
48
|
+
"peggy": "^3.0.2",
|
|
49
|
+
"rimraf": "5.0.5",
|
|
50
|
+
"zx": "7.2.3"
|
|
35
51
|
},
|
|
36
52
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
38
|
-
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"mocha": "^8.3.2",
|
|
41
|
-
"vm2": "^3.9.3"
|
|
42
|
-
},
|
|
43
|
-
"repository": {
|
|
44
|
-
"type": "git",
|
|
45
|
-
"url": "http://github.com/hildjj/unexpectedly.git"
|
|
53
|
+
"node": ">=18"
|
|
46
54
|
}
|
|
47
55
|
}
|