unexpectedly 0.0.7 → 2.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 +73 -24
- package/bin/unexpectedly.js +45 -0
- package/index.js +138 -80
- package/package.json +29 -23
- package/runner.js +131 -60
- package/testFile.peg.js +499 -511
- package/testFile.peggy +111 -0
- package/utils.js +41 -26
- package/vendor/peggy/LICENSE +21 -0
- package/vendor/peggy/grammar-location.cjs +81 -0
- package/vendor/peggy/rationale.md +4 -0
- package/bin/unexpectedly +0 -5
package/README.md
CHANGED
|
@@ -4,41 +4,76 @@ 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 [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
Usage: unexpectedly [options] [...file]
|
|
15
|
+
|
|
16
|
+
Arguments:
|
|
17
|
+
...file Files or directories to test (default:
|
|
18
|
+
"./test/")
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
-V, --version output the version number
|
|
22
|
+
-d,--defaultScript <replacement> Find the script from the file name.
|
|
23
|
+
Replace `$<base>` with the basename of the
|
|
24
|
+
file. (default: "../$<base>.js")
|
|
25
|
+
-f,--function <functionName> Use this function for testing in the
|
|
26
|
+
associated script (default: "test")
|
|
27
|
+
-h, --help display help for command
|
|
19
28
|
```
|
|
20
29
|
|
|
21
30
|
## Example
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
This is the easiest case, when the function that you want to test is already
|
|
33
|
+
exposed as the default export or exported as a function named `test`:
|
|
34
|
+
|
|
35
|
+
tests/bar.tests:
|
|
24
36
|
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
8 7 1
|
|
37
|
+
# "8" is the expected result, "4" and "2" are the inputs to the tested function
|
|
38
|
+
8 4 2
|
|
28
39
|
```
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
bar.js:
|
|
42
|
+
```js
|
|
43
|
+
// The default or `test` export from the associated .js file is the
|
|
44
|
+
// tested function by default
|
|
45
|
+
export default mult(a, b) {
|
|
46
|
+
return a * b;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function baz(a) {
|
|
50
|
+
return a * 6;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To test the `baz` function, we needed to write a little wrapper code:
|
|
55
|
+
|
|
56
|
+
tests/baz.tests:
|
|
31
57
|
```
|
|
32
|
-
|
|
58
|
+
#! inline: `export {baz as test} from '../foo.js'`
|
|
59
|
+
6 1 # Expect 6 to result from `baz("1")`
|
|
60
|
+
12 2
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
run:
|
|
64
|
+
```sh
|
|
65
|
+
unexpectedly tests/
|
|
33
66
|
```
|
|
34
67
|
|
|
35
68
|
## API
|
|
36
69
|
|
|
37
70
|
```js
|
|
38
|
-
|
|
71
|
+
import {suite} from 'unexpectedly';
|
|
39
72
|
|
|
40
|
-
|
|
41
|
-
|
|
73
|
+
await suite(directoryOrFile, {
|
|
74
|
+
defaultScript: '../$<base>.js',
|
|
75
|
+
function: 'test',
|
|
76
|
+
}).catch(console.error)
|
|
42
77
|
```
|
|
43
78
|
|
|
44
79
|
## Test language
|
|
@@ -57,11 +92,12 @@ test"
|
|
|
57
92
|
```
|
|
58
93
|
|
|
59
94
|
Aggressive type coercion is used, including if the test JavaScript
|
|
60
|
-
returns an non-string the result string is parsed as
|
|
95
|
+
returns an non-string the result string is parsed as JavaScript before
|
|
61
96
|
comparison.
|
|
62
97
|
|
|
63
98
|
Set file-wide globals with lines that start with `#!`, separating
|
|
64
|
-
the name and the value with a colon
|
|
99
|
+
the name and the value with a colon. These globals will be made available
|
|
100
|
+
to your test script as global variables:
|
|
65
101
|
|
|
66
102
|
```
|
|
67
103
|
#! name: value
|
|
@@ -74,6 +110,11 @@ Special globals include:
|
|
|
74
110
|
instead of the default
|
|
75
111
|
- "timeout": If any test in the file takes longer than this (in ms),
|
|
76
112
|
it fails. Default: 2000
|
|
113
|
+
- "peggy": Treat the input as a compiled peggy grammar; look for a "parse"
|
|
114
|
+
function in the input script, provide better errors, and use the value
|
|
115
|
+
of the peggy global (if any) as the startRule.
|
|
116
|
+
|
|
117
|
+
Environment variables can be set for the test code with `#!! name: value`.
|
|
77
118
|
|
|
78
119
|
## JavaScript to test
|
|
79
120
|
|
|
@@ -81,19 +122,24 @@ You can specify JavaScript to run for each test in one of three ways:
|
|
|
81
122
|
|
|
82
123
|
- Use the mapping from test file name to a JavaScript file. By default, this
|
|
83
124
|
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.
|
|
125
|
+
file of the same basename. So `tests/foo.tests` looks for `foo.js`. If
|
|
126
|
+
needed you can replace the mapping with something else, that uses `$<base>`
|
|
127
|
+
as a placeholder for the basename.
|
|
87
128
|
- Specify a global in your .tests file with the name `script`. That is the name
|
|
88
129
|
of the file, relative to the directory that contains the .tests file, to look
|
|
89
130
|
for JavaScript.
|
|
90
131
|
- Specify a global in your .tests file with the name `inline`. That's just
|
|
91
132
|
JavaScript to run per-line.
|
|
92
133
|
|
|
93
|
-
|
|
94
|
-
export a function named `test`. For inline code, if you
|
|
95
|
-
`
|
|
96
|
-
can just put in a simple function.
|
|
134
|
+
In es6 module projects, for each of the above, make sure `export default` is a
|
|
135
|
+
function, or that you export a function named `test`. For inline code, if you
|
|
136
|
+
don't export anything, `export default` will be added to the beginning of your
|
|
137
|
+
code, so that you can just put in a simple function.
|
|
138
|
+
|
|
139
|
+
In commonjs projects, for each of the above, make sure `module.exports` is a
|
|
140
|
+
function, or that you export a function named `test`. For inline code, if you
|
|
141
|
+
don't export anything, `module.exports=` will be added to the beginning of
|
|
142
|
+
your code, so that you can just put in a simple function.
|
|
97
143
|
|
|
98
144
|
Special globals available in all forms of the JavaScript above include:
|
|
99
145
|
|
|
@@ -101,3 +147,6 @@ Special globals available in all forms of the JavaScript above include:
|
|
|
101
147
|
- `__expected` is the string form of the expected result
|
|
102
148
|
- `__line` is the line number where the test starts
|
|
103
149
|
- `__column` is the column where the test text starts
|
|
150
|
+
|
|
151
|
+
[](https://github.com/hildjj/unexpectedly/actions/workflows/node.js.yml)
|
|
152
|
+
[](https://codecov.io/gh/hildjj/unexpectedly)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env -S node --experimental-vm-modules --no-warnings
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
|
|
4
|
+
import {Command, Option} from 'commander';
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import {suite} from '../index.js';
|
|
7
|
+
|
|
8
|
+
let failures = 0;
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
const pkg = JSON.parse(await fs.readFile(
|
|
12
|
+
new URL('../package.json', import.meta.url),
|
|
13
|
+
'utf-8'
|
|
14
|
+
));
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
const opts = program
|
|
18
|
+
.version(pkg.version)
|
|
19
|
+
.argument('[...file]', 'Files or directories to test', './test/')
|
|
20
|
+
.option('-d,--defaultScript <replacement>', 'Find the script from the file name. Replace `$<base>` with the basename of the file.', '../$<base>.js')
|
|
21
|
+
.option('-f,--function <functionName>', 'Use this function for testing in the associated script', 'test')
|
|
22
|
+
.addOption(new Option('-s,--silent18', 'Silently skip mjs tests on node 18.').hideHelp())
|
|
23
|
+
.parse()
|
|
24
|
+
.opts();
|
|
25
|
+
|
|
26
|
+
const {args} = program;
|
|
27
|
+
if (args.length === 0) {
|
|
28
|
+
args.push('./test/');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const arg of args) {
|
|
32
|
+
// eslint-disable-next-line require-atomic-updates
|
|
33
|
+
failures += await suite(arg, opts);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await main().catch(er => {
|
|
38
|
+
console.error(er);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (failures) {
|
|
43
|
+
console.error(`Total of ${failures} failure${failures === 1 ? '' : 's'}`);
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
package/index.js
CHANGED
|
@@ -1,115 +1,173 @@
|
|
|
1
|
-
|
|
1
|
+
import {Runner, SKIPPED} from './runner.js';
|
|
2
|
+
import {hexlify, mapObj} from './utils.js';
|
|
3
|
+
import Mocha from 'mocha';
|
|
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];
|
|
15
|
+
}
|
|
16
|
+
if (actual === SKIPPED) {
|
|
17
|
+
return [SKIPPED, SKIPPED];
|
|
22
18
|
}
|
|
23
19
|
if (actual && (typeof actual === 'object') && actual.constructor) {
|
|
24
|
-
|
|
20
|
+
if (/^0x/.test(expected)) {
|
|
21
|
+
const {name} = actual.constructor;
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
switch (name) {
|
|
24
|
+
case 'Buffer':
|
|
25
|
+
return [`0x${actual.toString('hex')}`, expected];
|
|
26
|
+
case 'Float32Array':
|
|
27
|
+
case 'Float64Array':
|
|
28
|
+
case 'Int16Array':
|
|
29
|
+
case 'Int32Array':
|
|
30
|
+
case 'Int8Array':
|
|
31
|
+
case 'Uint16Array':
|
|
32
|
+
case 'Uint32Array':
|
|
33
|
+
case 'Uint8Array':
|
|
34
|
+
case 'Uint8ClampedArray':
|
|
35
|
+
return [`0x${hexlify(actual)}`, expected];
|
|
36
|
+
}
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
|
-
|
|
39
|
+
|
|
40
|
+
const exp = await runner.parse(expected, {
|
|
41
|
+
__line: pt.line,
|
|
42
|
+
__column: pt.column,
|
|
43
|
+
});
|
|
44
|
+
const def = Object.hasOwn(exp, 'default') ? exp.default : exp;
|
|
45
|
+
return [actual, def];
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
/**
|
|
37
|
-
*
|
|
49
|
+
* @typedef {object} SuiteOptions
|
|
50
|
+
* @property {string} [defaultScript='../$<base>.js'] Find the script from the
|
|
51
|
+
* file name. Replace `$<base>` with the basename of the file. For
|
|
52
|
+
* example, "foo.test" will use "../foo.js" by default.
|
|
53
|
+
* @property {string} [function='test'] Use this function for testing in the
|
|
54
|
+
* associated script.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run a test suite from a file or directory.
|
|
38
59
|
*
|
|
39
|
-
* @param {string} [target='.'] The file or directory to read. If a
|
|
40
|
-
* reads all files ending in `.
|
|
41
|
-
* @param {
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* @returns Promise fulfills on success
|
|
60
|
+
* @param {string} [target='.'] The file or directory to read. If a
|
|
61
|
+
* directory, reads all files ending in `.tests`.
|
|
62
|
+
* @param {SuiteOptions} [options={}] Options for processing the suite.
|
|
63
|
+
* @returns {Promise<number>} Promise fulfills on completion, with number of
|
|
64
|
+
* test failures.
|
|
45
65
|
*/
|
|
46
|
-
async function suite
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
export async function suite(target = '.', options = {}) {
|
|
67
|
+
options = {
|
|
68
|
+
defaultScript: '../$<base>.js',
|
|
69
|
+
function: 'test',
|
|
70
|
+
...options,
|
|
71
|
+
};
|
|
72
|
+
let dir = path.resolve(target);
|
|
73
|
+
let files = [dir];
|
|
74
|
+
const mocha = new Mocha();
|
|
75
|
+
mocha.suite.title = dir;
|
|
49
76
|
|
|
50
|
-
const stats = await
|
|
77
|
+
const stats = await fs.stat(dir);
|
|
51
78
|
if (stats.isDirectory()) {
|
|
52
|
-
files = (await
|
|
53
|
-
.filter(f => f
|
|
54
|
-
.map(f => path.join(dir, f))
|
|
79
|
+
files = (await fs.readdir(target))
|
|
80
|
+
.filter(f => /\.tests?$/.test(f))
|
|
81
|
+
.map(f => path.join(dir, f));
|
|
55
82
|
} else if (stats.isFile()) {
|
|
56
|
-
|
|
57
|
-
dir =
|
|
83
|
+
// eslint-disable-next-line require-atomic-updates
|
|
84
|
+
({dir} = path.parse(target));
|
|
85
|
+
} else {
|
|
86
|
+
throw new Error(`Unknown file type: "${target}"`);
|
|
58
87
|
}
|
|
59
|
-
const mocha = new Mocha()
|
|
60
88
|
|
|
61
89
|
for (const f of files) {
|
|
62
|
-
const
|
|
63
|
-
const contents = await
|
|
64
|
-
const parsed = parser.parse(contents, { EXCEPTION })
|
|
90
|
+
const msuite = Mocha.Suite.create(mocha.suite, f);
|
|
91
|
+
const contents = await fs.readFile(f, 'utf8');
|
|
65
92
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
93
|
+
let parsed = null;
|
|
94
|
+
try {
|
|
95
|
+
parsed = parse(contents, {EXCEPTION, grammarSource: f});
|
|
96
|
+
} catch (er) {
|
|
97
|
+
if (typeof er.format === 'function') {
|
|
98
|
+
er.message = er.format([{
|
|
99
|
+
source: f,
|
|
100
|
+
text: contents,
|
|
101
|
+
}]);
|
|
102
|
+
delete er.expected;
|
|
103
|
+
delete er.location;
|
|
72
104
|
}
|
|
73
|
-
|
|
74
|
-
opts.columnOffset = parsed.vars.inline.column
|
|
75
|
-
} else {
|
|
76
|
-
opts.filename = parsed.vars.script
|
|
77
|
-
? path.resolve(dir, parsed.vars.script.value)
|
|
78
|
-
: path.resolve(dir, f.replace(/([^./]*)\.tests?$/, defaultScript))
|
|
79
|
-
}
|
|
80
|
-
if (parsed.vars.timeout) {
|
|
81
|
-
suite.timeout(parseInt(parsed.vars.timeout.value))
|
|
105
|
+
throw er;
|
|
82
106
|
}
|
|
83
|
-
opts.sandbox = mapObj(
|
|
84
|
-
Object.entries(parsed.vars),
|
|
85
|
-
([key, value]) => [key, value.value])
|
|
86
107
|
|
|
87
|
-
|
|
108
|
+
for (const pt of parsed) {
|
|
109
|
+
const opts = {
|
|
110
|
+
testFunction: options.function,
|
|
111
|
+
};
|
|
112
|
+
let text = '';
|
|
113
|
+
if (pt.vars.inline) {
|
|
114
|
+
opts.filename = f;
|
|
115
|
+
text = pt.vars.inline.value;
|
|
116
|
+
opts.lineOffset = pt.vars.inline.line - 1;
|
|
117
|
+
opts.columnOffset = pt.vars.inline.column;
|
|
118
|
+
} else {
|
|
119
|
+
opts.filename = pt.vars.script ?
|
|
120
|
+
path.resolve(dir, pt.vars.script.value) :
|
|
121
|
+
path.resolve(dir,
|
|
122
|
+
f.replace(/(?<base>[^\\/]*)\.tests?$/, options.defaultScript));
|
|
123
|
+
text = await fs.readFile(opts.filename, 'utf8');
|
|
124
|
+
}
|
|
125
|
+
if (pt.vars.timeout) {
|
|
126
|
+
msuite.timeout(parseInt(pt.vars.timeout.value, 10));
|
|
127
|
+
}
|
|
128
|
+
opts.context = mapObj(
|
|
129
|
+
Object.entries(pt.vars).filter(([k, v]) => !v.env),
|
|
130
|
+
([key, value]) => [key, value.value]
|
|
131
|
+
);
|
|
132
|
+
opts.env = mapObj(
|
|
133
|
+
Object.entries(pt.vars).filter(([k, v]) => v.env),
|
|
134
|
+
([key, value]) => [key, value.value]
|
|
135
|
+
);
|
|
136
|
+
opts.silent18 = Boolean(options.silent18);
|
|
137
|
+
|
|
138
|
+
const runner = new Runner(opts);
|
|
88
139
|
|
|
89
|
-
|
|
90
|
-
const t = new Mocha.Test(`line ${pt.line}`, async
|
|
91
|
-
let actual = null
|
|
140
|
+
const firstLine = (pt.expected === EXCEPTION) ? `! ${pt.inputs[0]}` : pt.expected.split(/\n/)[0];
|
|
141
|
+
const t = new Mocha.Test(`line ${pt.line}: ${firstLine || '""'}`, async() => {
|
|
142
|
+
let actual = null;
|
|
92
143
|
try {
|
|
93
|
-
actual = await runner.run({
|
|
144
|
+
actual = await runner.run(text, {
|
|
94
145
|
__expected: pt.expected,
|
|
95
146
|
__line: pt.line,
|
|
96
|
-
__column: pt.column
|
|
97
|
-
|
|
147
|
+
__column: pt.column,
|
|
148
|
+
__offset: pt.offset,
|
|
149
|
+
}, ...pt.inputs);
|
|
150
|
+
if (actual === SKIPPED) {
|
|
151
|
+
t.skip('Node 20.8 required');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
98
154
|
} catch (e) {
|
|
99
|
-
|
|
100
|
-
|
|
155
|
+
if (pt.expected !== EXCEPTION) {
|
|
156
|
+
throw e;
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
101
159
|
}
|
|
102
|
-
assert.notStrictEqual(pt.expected, EXCEPTION)
|
|
103
|
-
assert.
|
|
104
|
-
})
|
|
105
|
-
|
|
160
|
+
assert.notStrictEqual(pt.expected, EXCEPTION);
|
|
161
|
+
assert.deepEqual.apply(null, await coerce(actual, pt, runner));
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
msuite.addTest(t);
|
|
106
165
|
}
|
|
107
166
|
}
|
|
108
167
|
|
|
109
168
|
return new Promise((resolve, reject) => {
|
|
110
|
-
mocha.run(resolve)
|
|
111
|
-
})
|
|
169
|
+
mocha.run(resolve);
|
|
170
|
+
});
|
|
112
171
|
}
|
|
113
172
|
|
|
114
|
-
suite.EXCEPTION = EXCEPTION
|
|
115
|
-
module.exports = suite
|
|
173
|
+
suite.EXCEPTION = EXCEPTION;
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unexpectedly",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "2.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.peggy",
|
|
9
|
+
"pretest": "npm run build",
|
|
10
|
+
"test": "c8 npm run runtests",
|
|
11
|
+
"runtests": "mocha && bin/unexpectedly.js -s 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,28 @@
|
|
|
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
|
+
"@peggyjs/from-mem": "1.3.0",
|
|
36
|
+
"commander": "12.0.0",
|
|
37
|
+
"mocha": "^10.3.0",
|
|
38
|
+
"semver": "7.6.0"
|
|
39
|
+
},
|
|
27
40
|
"devDependencies": {
|
|
28
|
-
"eslint": "
|
|
29
|
-
"
|
|
30
|
-
"eslint
|
|
31
|
-
"eslint-plugin-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
41
|
+
"@cto.af/eslint-config": "3.1.0",
|
|
42
|
+
"c8": "9.1.0",
|
|
43
|
+
"eslint": "^8.57.0",
|
|
44
|
+
"eslint-plugin-jsdoc": "48.2.0",
|
|
45
|
+
"peggy": "^4.0.2",
|
|
46
|
+
"rimraf": "5.0.5",
|
|
47
|
+
"zx": "7.2.3"
|
|
35
48
|
},
|
|
49
|
+
"packageManager": "pnpm@8.15.4",
|
|
36
50
|
"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"
|
|
51
|
+
"node": ">=18"
|
|
46
52
|
}
|
|
47
53
|
}
|