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/runner.js
CHANGED
|
@@ -1,81 +1,152 @@
|
|
|
1
|
-
|
|
1
|
+
import GrammarLocation from './vendor/peggy/grammar-location.cjs';
|
|
2
|
+
import fromMem from '@peggyjs/from-mem';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import semver from 'semver';
|
|
2
5
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
+
const PREFIX_MJS = 'export default ';
|
|
7
|
+
const PREFIX_CJS = 'module.exports = ';
|
|
8
|
+
const PREFIX_MJS_LEN = PREFIX_MJS.length;
|
|
9
|
+
const PREFIX_CJS_LEN = PREFIX_CJS.length;
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
constructor (code, { filename, lineOffset = 0, columnOffset = 0 } = {}) {
|
|
9
|
-
super(code, filename)
|
|
10
|
-
this._lineOffset = lineOffset
|
|
11
|
-
this._columnOffset = columnOffset
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
compile () {
|
|
15
|
-
if (this._compiled) {
|
|
16
|
-
return this
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
this._compiled = new vm.Script(this.code, {
|
|
20
|
-
filename: this.filename,
|
|
21
|
-
displayErrors: false,
|
|
22
|
-
lineOffset: this._lineOffset,
|
|
23
|
-
columnOffset: this._columnOffset
|
|
24
|
-
})
|
|
11
|
+
const is20 = semver.satisfies(process.version, '>=20.8');
|
|
25
12
|
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
13
|
+
export const SKIPPED = Symbol('SKIPPED');
|
|
29
14
|
|
|
30
15
|
/**
|
|
31
|
-
* Fancy "eval"
|
|
16
|
+
* Fancy "eval". This is NOT for adding security. This is about providing:
|
|
17
|
+
*
|
|
18
|
+
* - Good backtraces with file/line/column information correct, even when the
|
|
19
|
+
* code was inlined into a test.
|
|
20
|
+
* - A require() and import() that are relative to the source file.
|
|
32
21
|
*
|
|
33
22
|
* @class Runner
|
|
34
23
|
*/
|
|
35
|
-
class Runner {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
export class Runner {
|
|
25
|
+
#columnOffset;
|
|
26
|
+
#context;
|
|
27
|
+
#dirname;
|
|
28
|
+
#env;
|
|
29
|
+
#filename;
|
|
30
|
+
#lineOffset;
|
|
31
|
+
#silent18;
|
|
32
|
+
#testFunction;
|
|
33
|
+
#type;
|
|
34
|
+
#skipped;
|
|
35
|
+
|
|
36
|
+
constructor({
|
|
37
|
+
filename = null, // Fully-qualified
|
|
39
38
|
lineOffset = 0,
|
|
40
39
|
columnOffset = 0,
|
|
41
|
-
|
|
40
|
+
context = {},
|
|
41
|
+
env = {},
|
|
42
|
+
silent18 = false,
|
|
43
|
+
testFunction = 'test',
|
|
44
|
+
type = 'guess',
|
|
42
45
|
} = {}) {
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
throw new Error('Either file or text is required')
|
|
46
|
-
}
|
|
47
|
-
text = fs.readFileSync(filename, 'utf8')
|
|
46
|
+
if (typeof filename !== 'string') {
|
|
47
|
+
throw new TypeError('filename not specified');
|
|
48
48
|
}
|
|
49
|
-
this
|
|
50
|
-
this
|
|
51
|
-
this
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
this.#filename = filename;
|
|
50
|
+
this.#dirname = path.dirname(filename);
|
|
51
|
+
this.#env = env;
|
|
52
|
+
this.#context = context;
|
|
53
|
+
this.#columnOffset = columnOffset;
|
|
54
|
+
this.#lineOffset = lineOffset;
|
|
55
|
+
this.#silent18 = silent18;
|
|
56
|
+
this.#skipped = null;
|
|
57
|
+
this.#type = type;
|
|
58
|
+
this.#testFunction = testFunction;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get skipped() {
|
|
62
|
+
return this.#skipped;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
async parse(text, extra = {}) {
|
|
66
|
+
const format = this.#type === 'guess' ?
|
|
67
|
+
await fromMem.guessModuleType(this.#filename) :
|
|
68
|
+
this.#type;
|
|
69
|
+
|
|
70
|
+
let columnOffset = this.#columnOffset;
|
|
71
|
+
if (format === 'es') {
|
|
72
|
+
if (this.#silent18 && !is20) {
|
|
73
|
+
this.#skipped = true;
|
|
74
|
+
return SKIPPED;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (text.indexOf('export') === -1) {
|
|
78
|
+
text = PREFIX_MJS + text; // Most common case
|
|
79
|
+
columnOffset -= PREFIX_MJS_LEN;
|
|
80
|
+
}
|
|
81
|
+
} else if (text.indexOf('exports') === -1) {
|
|
82
|
+
text = PREFIX_CJS + text; // Most common case
|
|
83
|
+
columnOffset -= PREFIX_CJS_LEN;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return await fromMem(text, {
|
|
87
|
+
filename: this.#filename,
|
|
88
|
+
format,
|
|
89
|
+
env: this.#env,
|
|
90
|
+
lineOffset: this.#lineOffset,
|
|
91
|
+
columnOffset,
|
|
92
|
+
context: {
|
|
93
|
+
filename: this.#filename,
|
|
94
|
+
id: this.#filename,
|
|
95
|
+
path: this.#dirname,
|
|
96
|
+
...this.#context,
|
|
97
|
+
...extra,
|
|
64
98
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async run(text, extra = {}, ...params) {
|
|
103
|
+
let f = await this.parse(text, extra);
|
|
104
|
+
if (f === SKIPPED) {
|
|
105
|
+
return f;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const possibleFunctions = [
|
|
109
|
+
this.#context.function,
|
|
110
|
+
this.#testFunction,
|
|
111
|
+
'default',
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
let source = null;
|
|
115
|
+
if ('peggy' in this.#context) {
|
|
116
|
+
source = new GrammarLocation(this.#filename, {
|
|
117
|
+
line: extra.__line,
|
|
118
|
+
column: extra.__column,
|
|
119
|
+
offset: extra.__offset,
|
|
120
|
+
});
|
|
121
|
+
const popts = {
|
|
122
|
+
grammarSource: source,
|
|
123
|
+
};
|
|
124
|
+
if (this.#context.peggy?.length > 0) {
|
|
125
|
+
popts.startRule = this.#context.peggy;
|
|
126
|
+
}
|
|
127
|
+
params[1] = popts;
|
|
128
|
+
possibleFunctions.splice(1, 0, 'parse');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (f && (typeof f !== 'function')) {
|
|
132
|
+
for (const possible of possibleFunctions) {
|
|
133
|
+
if (possible && (typeof f[possible] === 'function')) {
|
|
134
|
+
f = f[possible];
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
70
138
|
}
|
|
71
139
|
if (typeof f !== 'function') {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
140
|
+
throw new Error('Must export function or {test}');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
return f(...params);
|
|
145
|
+
} catch (er) {
|
|
146
|
+
if (source && (typeof er.format === 'function')) {
|
|
147
|
+
er.message = er.format([{source, text: params[0]}]);
|
|
75
148
|
}
|
|
149
|
+
throw er;
|
|
76
150
|
}
|
|
77
|
-
return f.apply(null, params)
|
|
78
151
|
}
|
|
79
152
|
}
|
|
80
|
-
|
|
81
|
-
module.exports = Runner
|