unexpectedly 1.0.0 → 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 +21 -9
- package/bin/unexpectedly.js +25 -12
- package/index.js +58 -32
- package/package.json +13 -15
- package/runner.js +99 -99
- package/testFile.peg.js +122 -72
- package/testFile.peggy +111 -0
- package/vendor/peggy/LICENSE +21 -0
- package/vendor/peggy/grammar-location.cjs +81 -0
- package/vendor/peggy/rationale.md +4 -0
- package/modtype.js +0 -60
package/README.md
CHANGED
|
@@ -11,13 +11,20 @@ npm install -g unexpectedly
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
unexpectedly [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
21
28
|
```
|
|
22
29
|
|
|
23
30
|
## Example
|
|
@@ -63,8 +70,10 @@ unexpectedly tests/
|
|
|
63
70
|
```js
|
|
64
71
|
import {suite} from 'unexpectedly';
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
await suite(directoryOrFile, {
|
|
74
|
+
defaultScript: '../$<base>.js',
|
|
75
|
+
function: 'test',
|
|
76
|
+
}).catch(console.error)
|
|
68
77
|
```
|
|
69
78
|
|
|
70
79
|
## Test language
|
|
@@ -101,6 +110,9 @@ Special globals include:
|
|
|
101
110
|
instead of the default
|
|
102
111
|
- "timeout": If any test in the file takes longer than this (in ms),
|
|
103
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.
|
|
104
116
|
|
|
105
117
|
Environment variables can be set for the test code with `#!! name: value`.
|
|
106
118
|
|
package/bin/unexpectedly.js
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --experimental-vm-modules --no-warnings
|
|
2
2
|
/* eslint-disable no-console */
|
|
3
3
|
|
|
4
|
+
import {Command, Option} from 'commander';
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
4
6
|
import {suite} from '../index.js';
|
|
5
7
|
|
|
6
8
|
let failures = 0;
|
|
7
9
|
|
|
8
10
|
async function main() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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);
|
|
21
34
|
}
|
|
22
35
|
}
|
|
23
36
|
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import {Runner, SKIPPED} from './runner.js';
|
|
1
2
|
import {hexlify, mapObj} from './utils.js';
|
|
2
3
|
import Mocha from 'mocha';
|
|
3
|
-
import {Runner} from './runner.js';
|
|
4
4
|
import assert from 'node:assert';
|
|
5
5
|
import fs from 'node:fs/promises';
|
|
6
6
|
import {parse} from './testFile.peg.js';
|
|
@@ -13,6 +13,9 @@ async function coerce(actual, pt, runner) {
|
|
|
13
13
|
if (typeof actual === 'string') {
|
|
14
14
|
return [actual, expected];
|
|
15
15
|
}
|
|
16
|
+
if (actual === SKIPPED) {
|
|
17
|
+
return [SKIPPED, SKIPPED];
|
|
18
|
+
}
|
|
16
19
|
if (actual && (typeof actual === 'object') && actual.constructor) {
|
|
17
20
|
if (/^0x/.test(expected)) {
|
|
18
21
|
const {name} = actual.constructor;
|
|
@@ -42,18 +45,30 @@ async function coerce(actual, pt, runner) {
|
|
|
42
45
|
return [actual, def];
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
/**
|
|
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
|
+
|
|
45
57
|
/**
|
|
46
58
|
* Run a test suite from a file or directory.
|
|
47
59
|
*
|
|
48
60
|
* @param {string} [target='.'] The file or directory to read. If a
|
|
49
61
|
* directory, reads all files ending in `.tests`.
|
|
50
|
-
* @param {
|
|
51
|
-
* file name. Replace $1 with the basename of the file. For example,
|
|
52
|
-
* "foo.test" will use "../foo.js" by default.
|
|
62
|
+
* @param {SuiteOptions} [options={}] Options for processing the suite.
|
|
53
63
|
* @returns {Promise<number>} Promise fulfills on completion, with number of
|
|
54
64
|
* test failures.
|
|
55
65
|
*/
|
|
56
|
-
export async function suite(target = '.',
|
|
66
|
+
export async function suite(target = '.', options = {}) {
|
|
67
|
+
options = {
|
|
68
|
+
defaultScript: '../$<base>.js',
|
|
69
|
+
function: 'test',
|
|
70
|
+
...options,
|
|
71
|
+
};
|
|
57
72
|
let dir = path.resolve(target);
|
|
58
73
|
let files = [dir];
|
|
59
74
|
const mocha = new Mocha();
|
|
@@ -90,34 +105,39 @@ export async function suite(target = '.', defaultScript = '../$<base>.js') {
|
|
|
90
105
|
throw er;
|
|
91
106
|
}
|
|
92
107
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
text =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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);
|
|
118
139
|
|
|
119
|
-
|
|
120
|
-
const firstLine = (pt.expected === EXCEPTION) ? '!' : pt.expected.split(/\n/)[0];
|
|
140
|
+
const firstLine = (pt.expected === EXCEPTION) ? `! ${pt.inputs[0]}` : pt.expected.split(/\n/)[0];
|
|
121
141
|
const t = new Mocha.Test(`line ${pt.line}: ${firstLine || '""'}`, async() => {
|
|
122
142
|
let actual = null;
|
|
123
143
|
try {
|
|
@@ -125,7 +145,12 @@ export async function suite(target = '.', defaultScript = '../$<base>.js') {
|
|
|
125
145
|
__expected: pt.expected,
|
|
126
146
|
__line: pt.line,
|
|
127
147
|
__column: pt.column,
|
|
148
|
+
__offset: pt.offset,
|
|
128
149
|
}, ...pt.inputs);
|
|
150
|
+
if (actual === SKIPPED) {
|
|
151
|
+
t.skip('Node 20.8 required');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
129
154
|
} catch (e) {
|
|
130
155
|
if (pt.expected !== EXCEPTION) {
|
|
131
156
|
throw e;
|
|
@@ -135,6 +160,7 @@ export async function suite(target = '.', defaultScript = '../$<base>.js') {
|
|
|
135
160
|
assert.notStrictEqual(pt.expected, EXCEPTION);
|
|
136
161
|
assert.deepEqual.apply(null, await coerce(actual, pt, runner));
|
|
137
162
|
});
|
|
163
|
+
|
|
138
164
|
msuite.addTest(t);
|
|
139
165
|
}
|
|
140
166
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unexpectedly",
|
|
3
|
-
"version": "
|
|
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
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "peggy -o testFile.peg.js --format es testFile.
|
|
8
|
+
"build": "peggy -o testFile.peg.js --format es testFile.peggy",
|
|
9
9
|
"pretest": "npm run build",
|
|
10
10
|
"test": "c8 npm run runtests",
|
|
11
|
-
"runtests": "mocha && bin/unexpectedly.js test test/pkg_mjs/test test/pkg_cjs/test",
|
|
11
|
+
"runtests": "mocha && bin/unexpectedly.js -s test test/pkg_mjs/test test/pkg_cjs/test",
|
|
12
12
|
"lint": "eslint --ext js,cjs,mjs . bin/*",
|
|
13
13
|
"ci": "npm run lint && npm run test",
|
|
14
14
|
"clean": "rimraf coverage testFile.peg.js"
|
|
@@ -32,23 +32,21 @@
|
|
|
32
32
|
},
|
|
33
33
|
"license": "MPL-2.0",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"
|
|
35
|
+
"@peggyjs/from-mem": "1.3.0",
|
|
36
|
+
"commander": "12.0.0",
|
|
37
|
+
"mocha": "^10.3.0",
|
|
38
|
+
"semver": "7.6.0"
|
|
36
39
|
},
|
|
37
40
|
"devDependencies": {
|
|
38
|
-
"@cto.af/eslint-config": "3.0
|
|
39
|
-
"c8": "
|
|
40
|
-
"eslint": "^8.
|
|
41
|
-
"eslint-
|
|
42
|
-
"
|
|
43
|
-
"eslint-plugin-jsdoc": "46.9.0",
|
|
44
|
-
"eslint-plugin-n": "16.3.1",
|
|
45
|
-
"eslint-plugin-node": "^11.1.0",
|
|
46
|
-
"eslint-plugin-promise": "^6.1.1",
|
|
47
|
-
"eslint-plugin-standard": "^4.1.0",
|
|
48
|
-
"peggy": "^3.0.2",
|
|
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",
|
|
49
46
|
"rimraf": "5.0.5",
|
|
50
47
|
"zx": "7.2.3"
|
|
51
48
|
},
|
|
49
|
+
"packageManager": "pnpm@8.15.4",
|
|
52
50
|
"engines": {
|
|
53
51
|
"node": ">=18"
|
|
54
52
|
}
|
package/runner.js
CHANGED
|
@@ -1,38 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {mapObj} from './utils.js';
|
|
1
|
+
import GrammarLocation from './vendor/peggy/grammar-location.cjs';
|
|
2
|
+
import fromMem from '@peggyjs/from-mem';
|
|
4
3
|
import path from 'node:path';
|
|
5
|
-
import
|
|
4
|
+
import semver from 'semver';
|
|
6
5
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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;
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
// Note: not convinced the cache actually does anything, due to timing.
|
|
14
|
-
if (cache?.has(specifier)) {
|
|
15
|
-
return cache.get(specifier);
|
|
16
|
-
}
|
|
17
|
-
const m = await import(specifier);
|
|
18
|
-
const exportNames = Object.keys(m);
|
|
19
|
-
const imported = new vm.SyntheticModule(
|
|
20
|
-
exportNames,
|
|
21
|
-
// eslint-disable-next-line func-names
|
|
22
|
-
function() {
|
|
23
|
-
// eslint-disable-next-line no-invalid-this
|
|
24
|
-
exportNames.forEach(key => this.setExport(key, m[key]));
|
|
25
|
-
},
|
|
26
|
-
{identifier: specifier, context: referencingModule.context}
|
|
27
|
-
);
|
|
11
|
+
const is20 = semver.satisfies(process.version, '>=20.8');
|
|
28
12
|
|
|
29
|
-
|
|
30
|
-
return imported;
|
|
31
|
-
}
|
|
13
|
+
export const SKIPPED = Symbol('SKIPPED');
|
|
32
14
|
|
|
33
15
|
/**
|
|
34
|
-
* Fancy "eval". This is NOT for adding security
|
|
35
|
-
* node:vm. This is about providing:
|
|
16
|
+
* Fancy "eval". This is NOT for adding security. This is about providing:
|
|
36
17
|
*
|
|
37
18
|
* - Good backtraces with file/line/column information correct, even when the
|
|
38
19
|
* code was inlined into a test.
|
|
@@ -42,111 +23,130 @@ async function linker(specifier, referencingModule, cache) {
|
|
|
42
23
|
*/
|
|
43
24
|
export class Runner {
|
|
44
25
|
#columnOffset;
|
|
26
|
+
#context;
|
|
45
27
|
#dirname;
|
|
46
28
|
#env;
|
|
47
29
|
#filename;
|
|
48
30
|
#lineOffset;
|
|
49
|
-
#
|
|
31
|
+
#silent18;
|
|
32
|
+
#testFunction;
|
|
50
33
|
#type;
|
|
34
|
+
#skipped;
|
|
51
35
|
|
|
52
36
|
constructor({
|
|
53
|
-
filename = null, //
|
|
37
|
+
filename = null, // Fully-qualified
|
|
54
38
|
lineOffset = 0,
|
|
55
39
|
columnOffset = 0,
|
|
56
|
-
|
|
40
|
+
context = {},
|
|
57
41
|
env = {},
|
|
58
|
-
|
|
42
|
+
silent18 = false,
|
|
43
|
+
testFunction = 'test',
|
|
44
|
+
type = 'guess',
|
|
59
45
|
} = {}) {
|
|
46
|
+
if (typeof filename !== 'string') {
|
|
47
|
+
throw new TypeError('filename not specified');
|
|
48
|
+
}
|
|
60
49
|
this.#filename = filename;
|
|
61
50
|
this.#dirname = path.dirname(filename);
|
|
62
51
|
this.#env = env;
|
|
63
|
-
this.#
|
|
52
|
+
this.#context = context;
|
|
64
53
|
this.#columnOffset = columnOffset;
|
|
65
54
|
this.#lineOffset = lineOffset;
|
|
55
|
+
this.#silent18 = silent18;
|
|
56
|
+
this.#skipped = null;
|
|
66
57
|
this.#type = type;
|
|
58
|
+
this.#testFunction = testFunction;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get skipped() {
|
|
62
|
+
return this.#skipped;
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
async parse(text, extra = {}) {
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
72
84
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)),
|
|
82
|
-
require: createRequire(this.#filename),
|
|
83
|
-
exports,
|
|
84
|
-
module: {
|
|
85
|
-
exports,
|
|
85
|
+
|
|
86
|
+
return await fromMem(text, {
|
|
87
|
+
filename: this.#filename,
|
|
88
|
+
format,
|
|
89
|
+
env: this.#env,
|
|
90
|
+
lineOffset: this.#lineOffset,
|
|
91
|
+
columnOffset,
|
|
92
|
+
context: {
|
|
86
93
|
filename: this.#filename,
|
|
87
94
|
id: this.#filename,
|
|
88
95
|
path: this.#dirname,
|
|
96
|
+
...this.#context,
|
|
97
|
+
...extra,
|
|
89
98
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
...this.#sandbox,
|
|
93
|
-
...extra,
|
|
94
|
-
};
|
|
95
|
-
context.process.env = this.#env;
|
|
96
|
-
context.global = context;
|
|
97
|
-
context.globalThis = context;
|
|
98
|
-
vm.createContext(context);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
async run(text, extra = {}, ...params) {
|
|
103
|
+
let f = await this.parse(text, extra);
|
|
104
|
+
if (f === SKIPPED) {
|
|
105
|
+
return f;
|
|
106
|
+
}
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
const possibleFunctions = [
|
|
109
|
+
this.#context.function,
|
|
110
|
+
this.#testFunction,
|
|
111
|
+
'default',
|
|
112
|
+
];
|
|
107
113
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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,
|
|
113
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
|
+
}
|
|
114
130
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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;
|
|
118
136
|
}
|
|
119
|
-
return linker(
|
|
120
|
-
path.resolve(path.dirname(this.#filename), specifier),
|
|
121
|
-
referencingModule,
|
|
122
|
-
imports
|
|
123
|
-
);
|
|
124
|
-
});
|
|
125
|
-
await mod.evaluate();
|
|
126
|
-
f = mod.namespace;
|
|
127
|
-
} else {
|
|
128
|
-
if (text.indexOf('exports') === -1) {
|
|
129
|
-
text = `module.exports= ${text}`; // Most common case
|
|
130
137
|
}
|
|
131
|
-
|
|
132
|
-
const script = new vm.Script(text, {
|
|
133
|
-
filename: this.#filename,
|
|
134
|
-
lineOffset: this.#lineOffset,
|
|
135
|
-
columnOffset: this.#columnOffset,
|
|
136
|
-
});
|
|
137
|
-
f = script.runInContext(context);
|
|
138
138
|
}
|
|
139
|
-
return f;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async run(text, extra = {}, ...params) {
|
|
143
|
-
let f = await this.parse(text, extra);
|
|
144
139
|
if (typeof f !== 'function') {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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]}]);
|
|
148
148
|
}
|
|
149
|
+
throw er;
|
|
149
150
|
}
|
|
150
|
-
return f(...params);
|
|
151
151
|
}
|
|
152
152
|
}
|
package/testFile.peg.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
//
|
|
1
|
+
// @generated by Peggy 4.0.2.
|
|
2
2
|
//
|
|
3
3
|
// https://peggyjs.org/
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
import { mapObj } from './utils.js'
|
|
7
8
|
|
|
8
9
|
function peg$subclass(child, parent) {
|
|
@@ -248,17 +249,22 @@ function peg$parse(input, options) {
|
|
|
248
249
|
var peg$e37 = peg$literalExpectation("\r", false);
|
|
249
250
|
|
|
250
251
|
var peg$f0 = function(all) {
|
|
251
|
-
|
|
252
|
-
({name, ...rest}) => [name, rest])
|
|
253
|
-
return {
|
|
254
|
-
tests: all.filter(t => t && t.hasOwnProperty('expected')),
|
|
255
|
-
vars
|
|
256
|
-
}
|
|
252
|
+
return all.filter(t => t && t.hasOwnProperty('expected'));
|
|
257
253
|
};
|
|
258
254
|
var peg$f1 = function(env, name, value) {
|
|
259
|
-
value
|
|
255
|
+
if (!value) {
|
|
256
|
+
const start = location().start;
|
|
257
|
+
value = {
|
|
258
|
+
value: undefined,
|
|
259
|
+
line: start.line,
|
|
260
|
+
column: start.column,
|
|
261
|
+
}
|
|
262
|
+
}
|
|
260
263
|
value.env = Boolean(env);
|
|
261
|
-
|
|
264
|
+
vars = {
|
|
265
|
+
...vars,
|
|
266
|
+
[name]: value
|
|
267
|
+
}
|
|
262
268
|
};
|
|
263
269
|
var peg$f2 = function(value) {
|
|
264
270
|
const start = location().start;
|
|
@@ -266,7 +272,7 @@ function peg$parse(input, options) {
|
|
|
266
272
|
};
|
|
267
273
|
var peg$f3 = function(expected, inputs) {
|
|
268
274
|
const start = location().start;
|
|
269
|
-
return { expected, inputs, line: start.line, column: start.column }
|
|
275
|
+
return { expected, inputs, vars, line: start.line, column: start.column }
|
|
270
276
|
};
|
|
271
277
|
var peg$f4 = function() { return options.EXCEPTION || "!" };
|
|
272
278
|
var peg$f5 = function(info) { return info.join('') };
|
|
@@ -288,16 +294,16 @@ function peg$parse(input, options) {
|
|
|
288
294
|
var peg$f21 = function() { return undefined };
|
|
289
295
|
var peg$f22 = function() { return undefined};
|
|
290
296
|
var peg$f23 = function() { return undefined };
|
|
291
|
-
var peg$currPos = 0;
|
|
292
|
-
var peg$savedPos =
|
|
297
|
+
var peg$currPos = options.peg$currPos | 0;
|
|
298
|
+
var peg$savedPos = peg$currPos;
|
|
293
299
|
var peg$posDetailsCache = [{ line: 1, column: 1 }];
|
|
294
|
-
var peg$maxFailPos =
|
|
295
|
-
var peg$maxFailExpected = [];
|
|
296
|
-
var peg$silentFails = 0;
|
|
300
|
+
var peg$maxFailPos = peg$currPos;
|
|
301
|
+
var peg$maxFailExpected = options.peg$maxFailExpected || [];
|
|
302
|
+
var peg$silentFails = options.peg$silentFails | 0;
|
|
297
303
|
|
|
298
304
|
var peg$result;
|
|
299
305
|
|
|
300
|
-
if (
|
|
306
|
+
if (options.startRule) {
|
|
301
307
|
if (!(options.startRule in peg$startRuleFunctions)) {
|
|
302
308
|
throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
|
|
303
309
|
}
|
|
@@ -372,9 +378,11 @@ function peg$parse(input, options) {
|
|
|
372
378
|
if (details) {
|
|
373
379
|
return details;
|
|
374
380
|
} else {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
381
|
+
if (pos >= peg$posDetailsCache.length) {
|
|
382
|
+
p = peg$posDetailsCache.length - 1;
|
|
383
|
+
} else {
|
|
384
|
+
p = pos;
|
|
385
|
+
while (!peg$posDetailsCache[--p]) {}
|
|
378
386
|
}
|
|
379
387
|
|
|
380
388
|
details = peg$posDetailsCache[p];
|
|
@@ -504,7 +512,7 @@ function peg$parse(input, options) {
|
|
|
504
512
|
}
|
|
505
513
|
|
|
506
514
|
function peg$parsevar() {
|
|
507
|
-
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
|
|
515
|
+
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;
|
|
508
516
|
|
|
509
517
|
s0 = peg$currPos;
|
|
510
518
|
if (input.substr(peg$currPos, 2) === peg$c0) {
|
|
@@ -538,15 +546,20 @@ function peg$parse(input, options) {
|
|
|
538
546
|
}
|
|
539
547
|
if (s6 !== peg$FAILED) {
|
|
540
548
|
s7 = peg$parse_();
|
|
541
|
-
s8 = peg$
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
549
|
+
s8 = peg$currPos;
|
|
550
|
+
s9 = peg$parseval();
|
|
551
|
+
if (s9 !== peg$FAILED) {
|
|
552
|
+
s10 = peg$parse_();
|
|
553
|
+
s8 = s9;
|
|
546
554
|
} else {
|
|
547
|
-
peg$currPos =
|
|
548
|
-
|
|
555
|
+
peg$currPos = s8;
|
|
556
|
+
s8 = peg$FAILED;
|
|
549
557
|
}
|
|
558
|
+
if (s8 === peg$FAILED) {
|
|
559
|
+
s8 = null;
|
|
560
|
+
}
|
|
561
|
+
peg$savedPos = s0;
|
|
562
|
+
s0 = peg$f1(s2, s4, s8);
|
|
550
563
|
} else {
|
|
551
564
|
peg$currPos = s0;
|
|
552
565
|
s0 = peg$FAILED;
|
|
@@ -798,8 +811,8 @@ function peg$parse(input, options) {
|
|
|
798
811
|
if (s0 === peg$FAILED) {
|
|
799
812
|
s0 = peg$currPos;
|
|
800
813
|
s1 = [];
|
|
801
|
-
|
|
802
|
-
|
|
814
|
+
s2 = input.charAt(peg$currPos);
|
|
815
|
+
if (peg$r0.test(s2)) {
|
|
803
816
|
peg$currPos++;
|
|
804
817
|
} else {
|
|
805
818
|
s2 = peg$FAILED;
|
|
@@ -808,8 +821,8 @@ function peg$parse(input, options) {
|
|
|
808
821
|
if (s2 !== peg$FAILED) {
|
|
809
822
|
while (s2 !== peg$FAILED) {
|
|
810
823
|
s1.push(s2);
|
|
811
|
-
|
|
812
|
-
|
|
824
|
+
s2 = input.charAt(peg$currPos);
|
|
825
|
+
if (peg$r0.test(s2)) {
|
|
813
826
|
peg$currPos++;
|
|
814
827
|
} else {
|
|
815
828
|
s2 = peg$FAILED;
|
|
@@ -857,8 +870,8 @@ function peg$parse(input, options) {
|
|
|
857
870
|
if (s0 === peg$FAILED) {
|
|
858
871
|
s0 = peg$currPos;
|
|
859
872
|
s1 = [];
|
|
860
|
-
|
|
861
|
-
|
|
873
|
+
s2 = input.charAt(peg$currPos);
|
|
874
|
+
if (peg$r1.test(s2)) {
|
|
862
875
|
peg$currPos++;
|
|
863
876
|
} else {
|
|
864
877
|
s2 = peg$FAILED;
|
|
@@ -867,8 +880,8 @@ function peg$parse(input, options) {
|
|
|
867
880
|
if (s2 !== peg$FAILED) {
|
|
868
881
|
while (s2 !== peg$FAILED) {
|
|
869
882
|
s1.push(s2);
|
|
870
|
-
|
|
871
|
-
|
|
883
|
+
s2 = input.charAt(peg$currPos);
|
|
884
|
+
if (peg$r1.test(s2)) {
|
|
872
885
|
peg$currPos++;
|
|
873
886
|
} else {
|
|
874
887
|
s2 = peg$FAILED;
|
|
@@ -916,8 +929,8 @@ function peg$parse(input, options) {
|
|
|
916
929
|
if (s0 === peg$FAILED) {
|
|
917
930
|
s0 = peg$currPos;
|
|
918
931
|
s1 = [];
|
|
919
|
-
|
|
920
|
-
|
|
932
|
+
s2 = input.charAt(peg$currPos);
|
|
933
|
+
if (peg$r2.test(s2)) {
|
|
921
934
|
peg$currPos++;
|
|
922
935
|
} else {
|
|
923
936
|
s2 = peg$FAILED;
|
|
@@ -926,8 +939,8 @@ function peg$parse(input, options) {
|
|
|
926
939
|
if (s2 !== peg$FAILED) {
|
|
927
940
|
while (s2 !== peg$FAILED) {
|
|
928
941
|
s1.push(s2);
|
|
929
|
-
|
|
930
|
-
|
|
942
|
+
s2 = input.charAt(peg$currPos);
|
|
943
|
+
if (peg$r2.test(s2)) {
|
|
931
944
|
peg$currPos++;
|
|
932
945
|
} else {
|
|
933
946
|
s2 = peg$FAILED;
|
|
@@ -967,8 +980,8 @@ function peg$parse(input, options) {
|
|
|
967
980
|
if (s1 !== peg$FAILED) {
|
|
968
981
|
s2 = peg$currPos;
|
|
969
982
|
s3 = [];
|
|
970
|
-
|
|
971
|
-
|
|
983
|
+
s4 = input.charAt(peg$currPos);
|
|
984
|
+
if (peg$r3.test(s4)) {
|
|
972
985
|
peg$currPos++;
|
|
973
986
|
} else {
|
|
974
987
|
s4 = peg$FAILED;
|
|
@@ -977,8 +990,8 @@ function peg$parse(input, options) {
|
|
|
977
990
|
if (s4 !== peg$FAILED) {
|
|
978
991
|
while (s4 !== peg$FAILED) {
|
|
979
992
|
s3.push(s4);
|
|
980
|
-
|
|
981
|
-
|
|
993
|
+
s4 = input.charAt(peg$currPos);
|
|
994
|
+
if (peg$r3.test(s4)) {
|
|
982
995
|
peg$currPos++;
|
|
983
996
|
} else {
|
|
984
997
|
s4 = peg$FAILED;
|
|
@@ -1048,8 +1061,8 @@ function peg$parse(input, options) {
|
|
|
1048
1061
|
if (s0 === peg$FAILED) {
|
|
1049
1062
|
s0 = peg$currPos;
|
|
1050
1063
|
s1 = [];
|
|
1051
|
-
|
|
1052
|
-
|
|
1064
|
+
s2 = input.charAt(peg$currPos);
|
|
1065
|
+
if (peg$r4.test(s2)) {
|
|
1053
1066
|
peg$currPos++;
|
|
1054
1067
|
} else {
|
|
1055
1068
|
s2 = peg$FAILED;
|
|
@@ -1058,8 +1071,8 @@ function peg$parse(input, options) {
|
|
|
1058
1071
|
if (s2 !== peg$FAILED) {
|
|
1059
1072
|
while (s2 !== peg$FAILED) {
|
|
1060
1073
|
s1.push(s2);
|
|
1061
|
-
|
|
1062
|
-
|
|
1074
|
+
s2 = input.charAt(peg$currPos);
|
|
1075
|
+
if (peg$r4.test(s2)) {
|
|
1063
1076
|
peg$currPos++;
|
|
1064
1077
|
} else {
|
|
1065
1078
|
s2 = peg$FAILED;
|
|
@@ -1201,7 +1214,7 @@ function peg$parse(input, options) {
|
|
|
1201
1214
|
}
|
|
1202
1215
|
|
|
1203
1216
|
function peg$parsecomment() {
|
|
1204
|
-
var s0, s1, s2, s3, s4;
|
|
1217
|
+
var s0, s1, s2, s3, s4, s5;
|
|
1205
1218
|
|
|
1206
1219
|
s0 = peg$currPos;
|
|
1207
1220
|
s1 = peg$parseWS();
|
|
@@ -1213,26 +1226,47 @@ function peg$parse(input, options) {
|
|
|
1213
1226
|
if (peg$silentFails === 0) { peg$fail(peg$e27); }
|
|
1214
1227
|
}
|
|
1215
1228
|
if (s2 !== peg$FAILED) {
|
|
1216
|
-
s3 =
|
|
1217
|
-
|
|
1218
|
-
|
|
1229
|
+
s3 = peg$currPos;
|
|
1230
|
+
peg$silentFails++;
|
|
1231
|
+
if (input.charCodeAt(peg$currPos) === 33) {
|
|
1232
|
+
s4 = peg$c1;
|
|
1219
1233
|
peg$currPos++;
|
|
1220
1234
|
} else {
|
|
1221
1235
|
s4 = peg$FAILED;
|
|
1222
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1236
|
+
if (peg$silentFails === 0) { peg$fail(peg$e1); }
|
|
1223
1237
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1238
|
+
peg$silentFails--;
|
|
1239
|
+
if (s4 === peg$FAILED) {
|
|
1240
|
+
s3 = undefined;
|
|
1241
|
+
} else {
|
|
1242
|
+
peg$currPos = s3;
|
|
1243
|
+
s3 = peg$FAILED;
|
|
1244
|
+
}
|
|
1245
|
+
if (s3 !== peg$FAILED) {
|
|
1246
|
+
s4 = [];
|
|
1247
|
+
s5 = input.charAt(peg$currPos);
|
|
1248
|
+
if (peg$r5.test(s5)) {
|
|
1228
1249
|
peg$currPos++;
|
|
1229
1250
|
} else {
|
|
1230
|
-
|
|
1251
|
+
s5 = peg$FAILED;
|
|
1231
1252
|
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
|
1232
1253
|
}
|
|
1254
|
+
while (s5 !== peg$FAILED) {
|
|
1255
|
+
s4.push(s5);
|
|
1256
|
+
s5 = input.charAt(peg$currPos);
|
|
1257
|
+
if (peg$r5.test(s5)) {
|
|
1258
|
+
peg$currPos++;
|
|
1259
|
+
} else {
|
|
1260
|
+
s5 = peg$FAILED;
|
|
1261
|
+
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
peg$savedPos = s0;
|
|
1265
|
+
s0 = peg$f20();
|
|
1266
|
+
} else {
|
|
1267
|
+
peg$currPos = s0;
|
|
1268
|
+
s0 = peg$FAILED;
|
|
1233
1269
|
}
|
|
1234
|
-
peg$savedPos = s0;
|
|
1235
|
-
s0 = peg$f20();
|
|
1236
1270
|
} else {
|
|
1237
1271
|
peg$currPos = s0;
|
|
1238
1272
|
s0 = peg$FAILED;
|
|
@@ -1247,8 +1281,8 @@ function peg$parse(input, options) {
|
|
|
1247
1281
|
peg$silentFails++;
|
|
1248
1282
|
s0 = peg$currPos;
|
|
1249
1283
|
s1 = [];
|
|
1250
|
-
|
|
1251
|
-
|
|
1284
|
+
s2 = input.charAt(peg$currPos);
|
|
1285
|
+
if (peg$r6.test(s2)) {
|
|
1252
1286
|
peg$currPos++;
|
|
1253
1287
|
} else {
|
|
1254
1288
|
s2 = peg$FAILED;
|
|
@@ -1256,8 +1290,8 @@ function peg$parse(input, options) {
|
|
|
1256
1290
|
}
|
|
1257
1291
|
while (s2 !== peg$FAILED) {
|
|
1258
1292
|
s1.push(s2);
|
|
1259
|
-
|
|
1260
|
-
|
|
1293
|
+
s2 = input.charAt(peg$currPos);
|
|
1294
|
+
if (peg$r6.test(s2)) {
|
|
1261
1295
|
peg$currPos++;
|
|
1262
1296
|
} else {
|
|
1263
1297
|
s2 = peg$FAILED;
|
|
@@ -1280,8 +1314,8 @@ function peg$parse(input, options) {
|
|
|
1280
1314
|
peg$silentFails++;
|
|
1281
1315
|
s0 = peg$currPos;
|
|
1282
1316
|
s1 = [];
|
|
1283
|
-
|
|
1284
|
-
|
|
1317
|
+
s2 = input.charAt(peg$currPos);
|
|
1318
|
+
if (peg$r7.test(s2)) {
|
|
1285
1319
|
peg$currPos++;
|
|
1286
1320
|
} else {
|
|
1287
1321
|
s2 = peg$FAILED;
|
|
@@ -1289,8 +1323,8 @@ function peg$parse(input, options) {
|
|
|
1289
1323
|
}
|
|
1290
1324
|
while (s2 !== peg$FAILED) {
|
|
1291
1325
|
s1.push(s2);
|
|
1292
|
-
|
|
1293
|
-
|
|
1326
|
+
s2 = input.charAt(peg$currPos);
|
|
1327
|
+
if (peg$r7.test(s2)) {
|
|
1294
1328
|
peg$currPos++;
|
|
1295
1329
|
} else {
|
|
1296
1330
|
s2 = peg$FAILED;
|
|
@@ -1313,8 +1347,8 @@ function peg$parse(input, options) {
|
|
|
1313
1347
|
peg$silentFails++;
|
|
1314
1348
|
s0 = peg$currPos;
|
|
1315
1349
|
s1 = [];
|
|
1316
|
-
|
|
1317
|
-
|
|
1350
|
+
s2 = input.charAt(peg$currPos);
|
|
1351
|
+
if (peg$r6.test(s2)) {
|
|
1318
1352
|
peg$currPos++;
|
|
1319
1353
|
} else {
|
|
1320
1354
|
s2 = peg$FAILED;
|
|
@@ -1323,8 +1357,8 @@ function peg$parse(input, options) {
|
|
|
1323
1357
|
if (s2 !== peg$FAILED) {
|
|
1324
1358
|
while (s2 !== peg$FAILED) {
|
|
1325
1359
|
s1.push(s2);
|
|
1326
|
-
|
|
1327
|
-
|
|
1360
|
+
s2 = input.charAt(peg$currPos);
|
|
1361
|
+
if (peg$r6.test(s2)) {
|
|
1328
1362
|
peg$currPos++;
|
|
1329
1363
|
} else {
|
|
1330
1364
|
s2 = peg$FAILED;
|
|
@@ -1386,8 +1420,20 @@ function peg$parse(input, options) {
|
|
|
1386
1420
|
return s0;
|
|
1387
1421
|
}
|
|
1388
1422
|
|
|
1423
|
+
|
|
1424
|
+
let vars = {};
|
|
1425
|
+
|
|
1389
1426
|
peg$result = peg$startRuleFunction();
|
|
1390
1427
|
|
|
1428
|
+
if (options.peg$library) {
|
|
1429
|
+
return /** @type {any} */ ({
|
|
1430
|
+
peg$result,
|
|
1431
|
+
peg$currPos,
|
|
1432
|
+
peg$FAILED,
|
|
1433
|
+
peg$maxFailExpected,
|
|
1434
|
+
peg$maxFailPos
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1391
1437
|
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
|
|
1392
1438
|
return peg$result;
|
|
1393
1439
|
} else {
|
|
@@ -1405,8 +1451,12 @@ function peg$parse(input, options) {
|
|
|
1405
1451
|
}
|
|
1406
1452
|
}
|
|
1407
1453
|
|
|
1454
|
+
const peg$allowedStartRules = [
|
|
1455
|
+
"start"
|
|
1456
|
+
];
|
|
1457
|
+
|
|
1408
1458
|
export {
|
|
1459
|
+
peg$allowedStartRules as StartRules,
|
|
1409
1460
|
peg$SyntaxError as SyntaxError,
|
|
1410
|
-
|
|
1411
1461
|
peg$parse as parse
|
|
1412
1462
|
};
|
package/testFile.peggy
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{{
|
|
2
|
+
import { mapObj } from './utils.js'
|
|
3
|
+
}}
|
|
4
|
+
|
|
5
|
+
{
|
|
6
|
+
let vars = {};
|
|
7
|
+
}
|
|
8
|
+
start
|
|
9
|
+
= all:(WS @line? _)|.., EOL| {
|
|
10
|
+
return all.filter(t => t && t.hasOwnProperty('expected'));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
line
|
|
14
|
+
= var
|
|
15
|
+
/ comment
|
|
16
|
+
/ test
|
|
17
|
+
/ '' // hack
|
|
18
|
+
|
|
19
|
+
var
|
|
20
|
+
= '#!' env:"!"? _ name:chunk _ ':' _ value:(@val _)? {
|
|
21
|
+
if (!value) {
|
|
22
|
+
const start = location().start;
|
|
23
|
+
value = {
|
|
24
|
+
value: undefined,
|
|
25
|
+
line: start.line,
|
|
26
|
+
column: start.column,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
value.env = Boolean(env);
|
|
30
|
+
vars = {
|
|
31
|
+
...vars,
|
|
32
|
+
[name]: value
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
val
|
|
37
|
+
= value:chunk {
|
|
38
|
+
const start = location().start;
|
|
39
|
+
return { value, line: start.line, column: start.column}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test
|
|
43
|
+
= expected:results inputs:(LWS @chunk)* comment? {
|
|
44
|
+
const start = location().start;
|
|
45
|
+
return { expected, inputs, vars, line: start.line, column: start.column }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
results
|
|
49
|
+
= "!" { return options.EXCEPTION || "!" }
|
|
50
|
+
/ chunk
|
|
51
|
+
|
|
52
|
+
chunk
|
|
53
|
+
= quoted
|
|
54
|
+
/ hex
|
|
55
|
+
/ word
|
|
56
|
+
|
|
57
|
+
quoted
|
|
58
|
+
= '"' info:dqchar* '"' { return info.join('') }
|
|
59
|
+
/ "'" info:sqchar* "'" { return info.join('') }
|
|
60
|
+
/ "`" info:bqchar* "`" { return info.join('') }
|
|
61
|
+
|
|
62
|
+
dqchar "characters that go inside double quotes, including escaped dquotes"
|
|
63
|
+
= '\\"' { return '"' }
|
|
64
|
+
/ qchar
|
|
65
|
+
/ $[^\\"]+
|
|
66
|
+
|
|
67
|
+
sqchar "characters that go inside single quotes, including escaped squotes"
|
|
68
|
+
= "\\'" { return "'" }
|
|
69
|
+
/ qchar
|
|
70
|
+
/ $[^\\']+
|
|
71
|
+
|
|
72
|
+
bqchar "characters that go inside back quotes, including escaped bquotes"
|
|
73
|
+
= "\\`" { return "`" }
|
|
74
|
+
/ qchar
|
|
75
|
+
/ $[^\\`]+
|
|
76
|
+
|
|
77
|
+
hex
|
|
78
|
+
= '0x' hx:$[a-fA-F0-9]+ { return '0x' + hx.toLowerCase(hx) }
|
|
79
|
+
|
|
80
|
+
word
|
|
81
|
+
= chars:wchar+ { return chars.join('') }
|
|
82
|
+
|
|
83
|
+
wchar "words can contain quoted bits"
|
|
84
|
+
= qchar+
|
|
85
|
+
/ $[^ #:'"\t\r\n]+
|
|
86
|
+
|
|
87
|
+
qchar "other quoted characters, as needed"
|
|
88
|
+
= '\\\\' { return '\\' }
|
|
89
|
+
/ '\\r' { return '\r' }
|
|
90
|
+
/ '\\n' { return '\n' }
|
|
91
|
+
/ '\\t' { return '\t' }
|
|
92
|
+
/ '\\#' { return '#' }
|
|
93
|
+
/ '\\:' { return ':' }
|
|
94
|
+
/ '\\ ' { return ' ' }
|
|
95
|
+
|
|
96
|
+
comment
|
|
97
|
+
= WS "#" !"!" [^\r\n]* { return undefined }
|
|
98
|
+
|
|
99
|
+
_ "optional whitespace"
|
|
100
|
+
= [ \t]* { return undefined }
|
|
101
|
+
|
|
102
|
+
WS "optional whitespace, including newlines"
|
|
103
|
+
= [ \t\r\n]* { return undefined}
|
|
104
|
+
|
|
105
|
+
LWS "required whitespace"
|
|
106
|
+
= [ \t]+ { return undefined }
|
|
107
|
+
|
|
108
|
+
EOL "End of line"
|
|
109
|
+
= '\n'
|
|
110
|
+
/ '\r\n'
|
|
111
|
+
/ '\r'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2010-2024 The Peggy AUTHORS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* When used as a grammarSource, allows grammars embedded in larger files to
|
|
5
|
+
* specify their offset. The start location is the first character in the
|
|
6
|
+
* grammar. The first line is often moved to the right by some number of
|
|
7
|
+
* columns, but subsequent lines all start at the first column.
|
|
8
|
+
*/
|
|
9
|
+
class GrammarLocation {
|
|
10
|
+
/**
|
|
11
|
+
* Create an instance.
|
|
12
|
+
*
|
|
13
|
+
* @param {any} source The original grammarSource. Should be a string or
|
|
14
|
+
* have a toString() method.
|
|
15
|
+
* @param {import("./peg").Location} start The starting offset for the
|
|
16
|
+
* grammar in the larger file.
|
|
17
|
+
*/
|
|
18
|
+
constructor(source, start) {
|
|
19
|
+
this.source = source;
|
|
20
|
+
this.start = start;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Coerce to a string.
|
|
25
|
+
*
|
|
26
|
+
* @returns {string} The source, stringified.
|
|
27
|
+
*/
|
|
28
|
+
toString() {
|
|
29
|
+
return String(this.source);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return a new Location offset from the given location by the start of the
|
|
34
|
+
* grammar.
|
|
35
|
+
*
|
|
36
|
+
* @param {import("./peg").Location} loc The location as if the start of the
|
|
37
|
+
* grammar was the start of the file.
|
|
38
|
+
* @returns {import("./peg").Location} The offset location.
|
|
39
|
+
*/
|
|
40
|
+
offset(loc) {
|
|
41
|
+
return {
|
|
42
|
+
line: loc.line + this.start.line - 1,
|
|
43
|
+
column: (loc.line === 1)
|
|
44
|
+
? loc.column + this.start.column - 1
|
|
45
|
+
: loc.column,
|
|
46
|
+
offset: loc.offset + this.start.offset,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* If the range has a grammarSource that is a GrammarLocation, offset the
|
|
52
|
+
* start of that range by the GrammarLocation.
|
|
53
|
+
*
|
|
54
|
+
* @param {import("./peg").LocationRange} range The range to extract from.
|
|
55
|
+
* @returns {import("./peg").Location} The offset start if possible, or the
|
|
56
|
+
* original start.
|
|
57
|
+
*/
|
|
58
|
+
static offsetStart(range) {
|
|
59
|
+
if (range.source && (typeof range.source.offset === "function")) {
|
|
60
|
+
return range.source.offset(range.start);
|
|
61
|
+
}
|
|
62
|
+
return range.start;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* If the range has a grammarSource that is a GrammarLocation, offset the
|
|
67
|
+
* end of that range by the GrammarLocation.
|
|
68
|
+
*
|
|
69
|
+
* @param {import("./peg").LocationRange} range The range to extract from.
|
|
70
|
+
* @returns {import("./peg").Location} The offset end if possible, or the
|
|
71
|
+
* original end.
|
|
72
|
+
*/
|
|
73
|
+
static offsetEnd(range) {
|
|
74
|
+
if (range.source && (typeof range.source.offset === "function")) {
|
|
75
|
+
return range.source.offset(range.end);
|
|
76
|
+
}
|
|
77
|
+
return range.end;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = GrammarLocation;
|
package/modtype.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
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
|
-
}
|