This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

38
resources/app/node_modules/peggy/AUTHORS generated vendored Normal file
View File

@@ -0,0 +1,38 @@
# Original Author:
David Majda <david@majda.cz> (https://github.com/dmajda/)
# Current maintainer:
Joe Hildebrand <joe-github@cursive.net> (https://github.com/hildjj/)
# Contributors:
Adrian Sampson <adrian@radbox.org> (https://github.com/sampsyo/)
Ali Tavakoli <ali.tavakoli@gmail.com> (https://github.com/atavakoli/)
Almad <bugs@almad.net> (https://github.com/almad/)
Andrei Neculau <andrei.neculau@gmail.com> (https://github.com/andreineculau/)
Arlo Breault <arlolra@gmail.com> (https://github.com/arlolra/)
Balázs Kutil <bkutil@users.noreply.github.com> (https://github.com/bkutil/)
Caleb Hearon <crh0872@gmail.com> (https://github.com/chearon/)
Cam McHenry <hello@camchenry.com> (https://github.com/camchenry)
Charles Pick <charles@codemix.com> (https://github.com/phpnode/)
Christian Flach <github@christianflach.de> (https://github.com/cmfcmf/)
Dan Selman <danscode@selman.org> (https://github.com/dselman)
David Berneda <david@steema.com>
Futago-za Ryuu <futagoza.ryuu@gmail.com> (https://github.com/futagoza/)
Jakub Vrana <jakub@vrana.cz> (https://github.com/vrana/)
Jason Davies <jason@jasondavies.com> (https://github.com/jasondavies/)
Joseph Frazier <joseph@onsip.com> (https://github.com/joseph-onsip/)
Julian Aubourg <j@ubourg.net> (https://github.com/jaubourg)
Justin Blank <justin.blank@gmail.com> (https://github.com/hyperpape/)
Marcel Bolten <github@marcelbolten.de> (https://github.com/MarcelBolten/)
Marco Baumgartl <marco.baumgartl@boerse-go.de>
Mingun <alexander_sergey@mail.ru> (https://github.com/Mingun/)
Rene Saarsoo <nene@triin.net> (https://github.com/nene/)
Tony Lukasavage <anthony.lukasavage@gmail.com> (https://github.com/tonylukasavage/)
chunpu <fengtong@mail.ustc.edu.cn> (https://github.com/chunpu/)
fatfisz <fatfisz@gmail.com> (https://github.com/fatfisz/)
fpirsch <fpirsch@free.fr> (https://github.com/fpirsch/)
markw65 <mark@replayroutes.com> (https://github.com/markw65/)
Andy (https://github.com/AndrewRayCode)
Kristian Dupont <kristian@kristiandupont.com> (https://github.com/kristiandupont/)
Lumi Pakkanen <lumi.pakkanen@gmail.com> (https://github.com/frostburn/)
XenoS (https://github.com/XenoS-ITA)

21
resources/app/node_modules/peggy/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2010-2024 The Peggy AUTHORS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

778
resources/app/node_modules/peggy/bin/peggy-cli.js generated vendored Normal file
View File

@@ -0,0 +1,778 @@
"use strict";
const {
Command, CommanderError, InvalidArgumentError, Option,
} = require("commander");
const Module = require("module");
const fs = require("fs");
const path = require("path");
const peggy = require("../lib/peg.js");
const util = require("util");
exports.CommanderError = CommanderError;
exports.InvalidArgumentError = InvalidArgumentError;
// Options that aren't for the API directly:
const PROG_OPTIONS = ["ast", "input", "output", "sourceMap", "startRule", "test", "testFile", "verbose"];
const MODULE_FORMATS = ["amd", "bare", "commonjs", "es", "globals", "umd"];
const MODULE_FORMATS_WITH_DEPS = ["amd", "commonjs", "es", "umd"];
const MODULE_FORMATS_WITH_GLOBAL = ["globals", "umd"];
// Helpers
function select(obj, sel) {
const ret = {};
for (const s of sel) {
if (Object.prototype.hasOwnProperty.call(obj, s)) {
ret[s] = obj[s];
delete obj[s];
}
}
return ret;
}
function commaArg(val, prev) {
return (prev || []).concat(val.split(",").map(x => x.trim()));
}
// Files
function readStream(inputStream) {
return new Promise((resolve, reject) => {
const input = [];
inputStream.on("data", data => { input.push(data); });
inputStream.on("end", () => resolve(Buffer.concat(input).toString()));
inputStream.on("error", reject);
});
}
function readFile(name) {
let f = null;
try {
f = fs.readFileSync(name, "utf8");
} catch (e) {
throw new InvalidArgumentError(`Can't read from file "${name}".`, e);
}
return f;
}
async function ensureDirectoryExists(filename) {
const dir = path.dirname(filename);
try {
const stats = await fs.promises.stat(dir);
if (!stats.isDirectory()) {
throw new Error(`"${dir}" exists and is not a directory`);
}
} catch (er) {
if (er.code !== "ENOENT") {
throw er;
}
await fs.promises.mkdir(dir, { recursive: true });
}
}
/**
* @typedef {object} Stdio
* @property {stream.Readable} [in] StdIn.
* @property {stream.Writable} [out] StdOut.
* @property {stream.Writable} [err] StdErr.
*/
/**
* @typedef {object} ErrorOptions
* @property {string} [code="peggy.invalidArgument"] Code for exception if
* throwing.
* @property {number} [exitCode=1] Exit code if exiting.
* @property {peggy.SourceText[]} [sources=[]] Source text for formatting compile errors.
* @property {Error} [error] Error to extract message from.
* @property {string} [message] Error message, only used internally.
*/
// Command line processing
class PeggyCLI extends Command {
/**
* Create a CLI environment.
*
* @param {Stdio} [stdio] Replacement streams for stdio, for testing.
*/
constructor(stdio) {
super("peggy");
/** @type {Stdio} */
this.std = {
in: process.stdin,
out: process.stdout,
err: process.stderr,
...stdio,
};
/** @type {peggy.BuildOptionsBase} */
this.argv = {};
/** @type {string[]} */
this.inputFiles = [];
/** @type {string?} */
this.outputFile = null;
/** @type {object} */
this.progOptions = {};
/** @type {string?} */
this.testFile = null;
/** @type {string?} */
this.testGrammarSource = null;
/** @type {string?} */
this.testText = null;
/** @type {string?} */
this.outputJS = null;
/** @type {string?} */
this.lastError = null;
/** @type {import('./watcher.js')?} */
this.watcher = null;
this
.version(peggy.VERSION, "-v, --version")
.argument("[input_file...]", 'Grammar file(s) to read. Use "-" to read stdin. If multiple files are given, they are combined in the given order to produce a single output. Use npm:"<packageName>/file.peggy" to import from an npm dependency.', ["-"])
.allowExcessArguments(false)
.addOption(
new Option(
"--allowed-start-rules <rules>",
"Comma-separated list of rules the generated parser will be allowed to start parsing from. Use '*' if you want any rule to be allowed as a start rule. (Can be specified multiple times)"
)
.default([], "the first rule in the grammar")
.argParser(commaArg)
)
.addOption(
new Option(
"--ast",
"Output a grammar AST instead of a parser code"
)
.default(false)
.conflicts(["test", "testFile", "sourceMap"])
)
.option(
"--cache",
"Make generated parser cache results",
false
)
.option(
"-d, --dependency <dependency>",
"Comma-separated list of dependencies, either as a module name, or as `variable:module`. (Can be specified multiple times)",
commaArg
)
.option(
"-D, --dependencies <json>",
"Dependencies, in JSON object format with variable:module pairs. (Can be specified multiple times).",
(val, prev = {}) => {
let v = null;
try {
v = JSON.parse(val);
} catch (e) {
throw new InvalidArgumentError(`Error parsing JSON: ${e.message}`);
}
return Object.assign(prev, v);
}
)
.option(
"-e, --export-var <variable>",
"Name of a global variable into which the parser object is assigned to when no module loader is detected."
)
.option(
"--extra-options <options>",
"Additional options (in JSON format as an object) to pass to peggy.generate",
val => this.addExtraOptionsJSON(val, "extra-options")
)
.option(
"-c, --extra-options-file <file>",
"File with additional options (in JSON as an object or commonjs module format) to pass to peggy.generate",
val => {
if (/\.c?js$/.test(val)) {
return this.addExtraOptions(require(path.resolve(val)), "extra-options-file");
} else {
return this.addExtraOptionsJSON(readFile(val), "extra-options-file");
}
}
)
.addOption(
new Option(
"--format <format>",
"Format of the generated parser"
)
.choices(MODULE_FORMATS)
.default("commonjs")
)
.addOption(new Option("--library").hideHelp(), "Run tests in library mode. Maintainers only, for now.")
.option("-o, --output <file>", "Output file for generated parser. Use '-' for stdout (the default is a file next to the input file with the extension change to '.js', unless a test is specified, in which case no parser is output without this option)")
.option(
"--plugin <module>",
"Comma-separated list of plugins. (can be specified multiple times)",
commaArg
)
.option(
"-m, --source-map [mapfile]",
"Generate a source map. If name is not specified, the source map will be named \"<input_file>.map\" if input is a file and \"source.map\" if input is a standard input. If the special filename `inline` is given, the sourcemap will be embedded in the output file as a data URI. If the filename is prefixed with `hidden:`, no mapping URL will be included so that the mapping can be specified with an HTTP SourceMap: header. This option conflicts with the `-t/--test` and `-T/--test-file` options unless `-o/--output` is also specified"
)
.option(
"-S, --start-rule <rule>",
"When testing, use the given rule as the start rule. If this rule is not in the allowed start rules, it will be added."
)
.option(
"-t, --test <text>",
"Test the parser with the given text, outputting the result of running the parser instead of the parser itself. If the input to be tested is not parsed, the CLI will exit with code 2"
)
.addOption(new Option(
"-T, --test-file <filename>",
"Test the parser with the contents of the given file, outputting the result of running the parser instead of the parser itself. If the input to be tested is not parsed, the CLI will exit with code 2. A filename of '-' will read from stdin."
).conflicts("test"))
.option("--trace", "Enable tracing in generated parser", false)
.option("-w,--watch", "Watch the input file for changes, generating the output once at the start, and again whenever the file changes.")
.addOption(
// Not interesting yet. If it becomes so, unhide the help.
new Option("--verbose", "Enable verbose logging")
.hideHelp()
.default(false)
)
.action((inputFiles, opts) => { // On parse()
this.inputFiles = inputFiles;
this.argv = opts;
if (this.argv.library) {
this.peg$library = true;
delete this.argv.library;
}
if ((typeof this.argv.startRule === "string")
&& !this.argv.allowedStartRules.includes(this.argv.startRule)) {
this.argv.allowedStartRules.push(this.argv.startRule);
}
if (this.argv.allowedStartRules.length === 0) {
// [] is an invalid input, as is null
// undefined doesn't work as a default in commander
delete this.argv.allowedStartRules;
}
// Combine plugin/plugins
if ((this.argv.plugin && (this.argv.plugin.length > 0))
|| (this.argv.plugins && (this.argv.plugins.length > 0))) {
this.argv.plugins = [
...(this.argv.plugins || []),
...(this.argv.plugin || []),
].map(val => {
if (typeof val !== "string") {
return val;
}
// If this is an absolute or relative path (not a module name)
const id = (path.isAbsolute(val) || /^\.\.?[/\\]/.test(val))
? path.resolve(val)
: val;
let mod = null;
try {
mod = require(id);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") {
this.error(`requiring:\n${e.stack}`);
} else {
this.error(`requiring "${id}": ${e.message}`);
}
}
return mod;
});
}
delete this.argv.plugin;
// Combine dependency/dependencies
this.argv.dependencies = this.argv.dependencies || {};
if (this.argv.dependency) {
if (this.argv.dependency.length > 0) {
for (const dep of this.argv.dependency) {
const [name, val] = dep.split(":");
this.argv.dependencies[name] = val ? val : name;
}
}
delete this.argv.dependency;
}
if ((Object.keys(this.argv.dependencies).length > 0)
&& !MODULE_FORMATS_WITH_DEPS.includes(this.argv.format)) {
this.error(`Can't use the -d/--dependency or -D/--dependencies options with the "${this.argv.format}" module format.`);
}
if ((this.argv.exportVar !== undefined)
&& !MODULE_FORMATS_WITH_GLOBAL.includes(this.argv.format)) {
this.error(`Can't use the -e/--export-var option with the "${this.argv.format}" module format.`);
}
this.progOptions = select(this.argv, PROG_OPTIONS);
this.argv.output = "source";
if ((this.args.length === 0) && this.progOptions.input) {
// Allow command line to override config file.
this.inputFiles = Array.isArray(this.progOptions.input)
? this.progOptions.input
: [this.progOptions.input];
}
this.outputFile = this.progOptions.output;
this.outputJS = this.progOptions.output;
if ((this.inputFiles.includes("-")) && this.argv.watch) {
this.argv.watch = false; // Make error throw.
this.error("Can't watch stdin");
}
if (!this.outputFile) {
if (!this.inputFiles.includes("-")) {
let inFile = this.inputFiles[0];
// You might just want to run a fragment grammar as-is,
// particularly with a specified start rule.
const m = inFile.match(/^npm:.*\/([^/]+)$/);
if (m) {
inFile = m[1];
}
this.outputJS = inFile.slice(
0,
inFile.length
- path.extname(inFile).length
) + ".js";
this.outputFile = ((typeof this.progOptions.test !== "string")
&& !this.progOptions.testFile)
? this.outputJS
: "-";
} else {
this.outputFile = "-";
// Synthetic
this.outputJS = path.join(process.cwd(), "stdout.js");
}
}
// If CLI parameter was defined, enable source map generation
if (this.progOptions.sourceMap !== undefined) {
if (!this.progOptions.output
&& (this.progOptions.test || this.progOptions.testFile)) {
this.error("Generation of the source map is not useful if you don't output a parser file, perhaps you forgot to add an `-o/--output` option?");
}
this.argv.output = "source-and-map";
// If source map name is not specified, calculate it
if (this.progOptions.sourceMap === true) {
this.progOptions.sourceMap = this.outputFile === "-" ? "source.map" : this.outputFile + ".map";
}
if (this.progOptions.sourceMap === "hidden:inline") {
this.error("hidden + inline sourceMap makes no sense.");
}
}
if (this.progOptions.ast) {
this.argv.output = "ast";
}
// Empty string is a valid test input. Don't just test for falsy.
if (typeof this.progOptions.test === "string") {
this.testText = this.progOptions.test;
this.testGrammarSource = "command line";
}
if (this.progOptions.testFile) {
this.testFile = this.progOptions.testFile;
this.testGrammarSource = this.progOptions.testFile;
}
this.verbose("PARSER OPTIONS:", this.argv);
this.verbose("PROGRAM OPTIONS:", this.progOptions);
this.verbose('INPUT: "%s"', this.inputFiles);
this.verbose('OUTPUT: "%s"', this.outputFile);
if (this.progOptions.verbose) {
this.argv.info = (pass, msg) => PeggyCLI.print(this.std.err, `INFO(${pass}): ${msg}`);
}
this.argv.warning = (pass, msg) => PeggyCLI.print(this.std.err, `WARN(${pass}): ${msg}`);
});
}
/**
* Print error message to std.err, and either call process.exit or throw an
* exception if exitOverride() has been called. If opts.error is specified,
* it will be used to generate the error message, rather than using the
* message provided.
*
* @param {string} message The message to print.
* @param {ErrorOptions} [opts] Options
*/
error(message, opts = {}) {
opts = {
code: "peggy.invalidArgument",
exitCode: 1,
error: null,
sources: [],
...opts,
};
if (opts.error) {
if (typeof opts.error.format === "function") {
message = `${message}\n${opts.error.format(opts.sources)}`;
} else {
message = (this.progOptions.verbose || !opts.error.message)
? `${message}\n${opts.error.stack}`
: `${message}\n${opts.error.message}`;
}
}
if (!/^error/i.test(message)) {
message = `Error ${message}`;
}
if (this.argv.watch) {
this.lastError = message;
} else {
super.error(message, opts);
}
}
static print(stream, ...args) {
stream.write(util.formatWithOptions({
colors: stream.isTTY,
depth: Infinity,
maxArrayLength: Infinity,
maxStringLength: Infinity,
}, ...args));
stream.write("\n");
}
verbose(...args) {
if (!this.progOptions.verbose) {
return false;
}
PeggyCLI.print(this.std.err, ...args);
return true;
}
addExtraOptionsJSON(json, source) {
let extraOptions = undefined;
try {
extraOptions = JSON.parse(json);
} catch (e) {
throw new InvalidArgumentError(`Error parsing JSON: ${e.message}`);
}
return this.addExtraOptions(extraOptions, source);
}
addExtraOptions(extraOptions, source) {
if ((extraOptions === null)
|| (typeof extraOptions !== "object")
|| Array.isArray(extraOptions)) {
throw new InvalidArgumentError("The JSON with extra options has to represent an object.");
}
for (const [k, v] of Object.entries(extraOptions)) {
const prev = this.getOptionValue(k);
if ((this.getOptionValueSource(k) === "default")
|| (prev === "null")
|| (typeof prev !== "object")) {
// Overwrite
this.setOptionValueWithSource(k, v, source);
} else {
// Combine with previous if it's a non-default, non-null object.
if (Array.isArray(prev)) {
prev.push(...v);
} else {
Object.assign(prev, v);
}
}
}
return null;
}
openOutputStream() {
// If there is a valid outputFile, write the parser to it. Otherwise,
// if no test and no outputFile, write to stdout.
if (this.outputFile === "-") {
// Note: empty string is a valid input for testText.
// Don't just test for falsy.
const hasTest = !this.testFile && (typeof this.testText !== "string");
return Promise.resolve(hasTest ? this.std.out : null);
}
return ensureDirectoryExists(this.outputFile)
.then(() => new Promise((resolve, reject) => {
const outputStream = fs.createWriteStream(this.outputFile);
outputStream.on("error", reject);
outputStream.on("open", () => resolve(outputStream));
}));
}
/**
* Write a source map, and return the serialized plain text.
*
* @param {import("source-map-generator").SourceNode} source The SourceNode
* to serialize.
* @returns {Promise<string>} The plain text output.
*/
async writeSourceMap(source) {
if (!this.progOptions.sourceMap) {
return source;
}
let hidden = false;
if (this.progOptions.sourceMap.startsWith("hidden:")) {
hidden = true;
this.progOptions.sourceMap = this.progOptions.sourceMap.slice(7);
}
const inline = this.progOptions.sourceMap === "inline";
const mapDir = inline
? path.dirname(this.outputJS)
: path.dirname(this.progOptions.sourceMap);
const file = path.relative(mapDir, this.outputJS);
const sourceMap = source.toStringWithSourceMap({ file });
// According to specifications, paths in the "sources" array should be
// relative to the map file. Compiler cannot generate right paths, because
// it is unaware of the source map location
const json = sourceMap.map.toJSON();
json.sources = json.sources.map(
src => ((src === null) ? null : path.relative(mapDir, src))
);
if (inline) {
// Note: hidden + inline makes no sense.
const buf = Buffer.from(JSON.stringify(json));
// Use \x23 instead of # so that Jest won't treat this as a real
// source map URL for *this* file.
return sourceMap.code + `\
//\x23 sourceMappingURL=data:application/json;charset=utf-8;base64,${buf.toString("base64")}
`;
}
await ensureDirectoryExists(this.progOptions.sourceMap);
await fs.promises.writeFile(
this.progOptions.sourceMap,
JSON.stringify(json),
"utf8"
);
if (hidden) {
return sourceMap.code;
}
// Opposite direction from mapDir
return sourceMap.code + `\
//# sourceMappingURL=${path.relative(path.dirname(this.outputJS), this.progOptions.sourceMap)}
`;
}
writeOutput(outputStream, source) {
return new Promise((resolve, reject) => {
if (!outputStream) {
resolve();
return;
}
// Slightly odd formation in order to get test coverage without having to
// mock the whole file system.
outputStream.on("error", reject);
if (outputStream === this.std.out) {
outputStream.write(source, err => {
if (!err) {
resolve();
}
});
} else {
outputStream.end(source, err => {
if (!err) {
resolve();
}
});
}
});
}
async test(source) {
if (this.testFile) {
if (this.testFile === "-") {
this.testText = await readStream(this.std.in);
} else {
this.testText = await fs.promises.readFile(this.testFile, "utf8");
}
}
if (typeof this.testText === "string") {
this.verbose("TEST TEXT:", this.testText);
// Create a module that exports the parser, then load it from the
// correct directory, so that any modules that the parser requires will
// be loaded from the correct place.
const filename = this.outputJS
? path.resolve(this.outputJS)
: path.join(process.cwd(), "stdout.js"); // Synthetic
const fromMem = require("@peggyjs/from-mem");
const exec = await fromMem(source, {
filename,
format: this.argv.format,
globalExport: this.argv.exportVar,
});
const opts = {
grammarSource: this.testGrammarSource,
peg$library: this.peg$library,
};
if (typeof this.progOptions.startRule === "string") {
opts.startRule = this.progOptions.startRule;
}
const results = exec.parse(this.testText, opts);
PeggyCLI.print(this.std.out, "%O", results);
}
}
/**
* Process the command line once.
*
* @returns {Promise<number>}
*/
async run() {
const sources = [];
let exitCode = 1;
let errorText = "";
let prevSource = process.cwd() + "/";
try {
for (const source of this.inputFiles) {
const input = { source, text: null };
this.verbose("CLI", errorText = `reading input "${source}"`);
if (source === "-") {
input.source = "stdin";
this.std.in.resume();
input.text = await readStream(this.std.in);
} else if (source.startsWith("npm:")) {
const req = Module.createRequire(prevSource);
prevSource = req.resolve(source.slice(4)); // Skip "npm:"
input.source = prevSource;
input.text = await fs.promises.readFile(prevSource, "utf8");
} else {
prevSource = path.resolve(source);
input.text = await fs.promises.readFile(source, "utf8");
}
sources.push(input);
}
// This is wrong. It's a hack in place until source generation is fixed.
this.argv.grammarSource = sources[0].source;
this.verbose("CLI", errorText = "parsing grammar");
const source = peggy.generate(sources, this.argv); // All of the real work.
this.verbose("CLI", errorText = "opening output stream");
const outputStream = await this.openOutputStream();
// If option `--ast` is specified, `generate()` returns an AST object
if (this.progOptions.ast) {
this.verbose("CLI", errorText = "writing AST");
await this.writeOutput(outputStream, JSON.stringify(source, null, 2));
} else {
this.verbose("CLI", errorText = "writing sourceMap");
const mappedSource = await this.writeSourceMap(source);
this.verbose("CLI", errorText = "writing parser");
await this.writeOutput(outputStream, mappedSource);
exitCode = 2;
this.verbose("CLI", errorText = "running test");
await this.test(mappedSource);
}
} catch (error) {
if (this.testGrammarSource) {
sources.push({
source: this.testGrammarSource,
text: this.testText,
});
}
// Will either exit or throw.
this.error(errorText, {
error,
exitCode,
code: "peggy.cli",
sources,
});
}
return 0;
}
/**
* Stops watching input file.
*/
async stopWatching() {
if (!this.watcher) {
throw new Error("Not watching");
}
await this.watcher.close();
this.watcher = null;
}
/**
* Entry point. If in watch mode, does `run` in a loop, catching errors,
* otherwise does `run` once.
*
* @returns {Promise<number>}
*/
main() {
if (this.argv.watch) {
const Watcher = require("./watcher.js"); // Lazy: usually not needed.
const hasTest = this.progOptions.test || this.progOptions.testFile;
const watchFiles = [...this.inputFiles];
if (this.progOptions.testFile) {
watchFiles.push(this.progOptions.testFile);
}
this.watcher = new Watcher(...watchFiles);
this.watcher.on("change", async fn => {
PeggyCLI.print(this.std.err, `"${fn}" changed...`);
this.lastError = null;
await this.run();
if (this.lastError) {
PeggyCLI.print(this.std.err, this.lastError);
} else if (!hasTest) {
PeggyCLI.print(this.std.err, `Wrote: "${this.outputFile}"`);
}
});
return new Promise((resolve, reject) => {
this.watcher.on("error", er => {
reject(er);
});
this.watcher.on("close", () => resolve(0));
});
} else {
return this.run();
}
}
// For some reason, after running through rollup, typescript can't see
// methods from the base class.
/**
* @param {string[]} [argv] - optional, defaults to process.argv
* @param {Object} [parseOptions] - optionally specify style of options with from: node/user/electron
* @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
* @return {PeggyCLI} `this` command for chaining
*/
parse(argv, parseOptions) {
return super.parse(argv, parseOptions);
}
/**
* @param {Object} [configuration] - configuration options
* @return {PeggyCLI} `this` command for chaining, or stored configuration
*/
configureHelp(configuration) {
return super.configureHelp(configuration);
}
/**
* @param {Object} [configuration] - configuration options
* @return {PeggyCLI} `this` command for chaining, or stored configuration
*/
configureOutput(configuration) {
return super.configureOutput(configuration);
}
/**
* @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing
* @return {PeggyCLI} `this` command for chaining
*/
exitOverride(fn) {
return super.exitOverride(fn);
}
}
exports.PeggyCLI = PeggyCLI;

25
resources/app/node_modules/peggy/bin/peggy.js generated vendored Executable file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env -S node --experimental-vm-modules --no-warnings
"use strict";
const {
CommanderError, InvalidArgumentError, PeggyCLI,
} = require("./peggy-cli.js");
exports.CommanderError = CommanderError;
exports.InvalidArgumentError = InvalidArgumentError;
exports.PeggyCLI = PeggyCLI;
// Jest's coverage can't see into fork'd processes.
// See: https://github.com/facebook/jest/issues/5274
/* istanbul ignore if */
if (require.main === module) {
const cli = new PeggyCLI().parse();
cli.main().then(
code => process.exit(code),
er => {
console.error("Uncaught Error\n", er);
process.exit(1);
}
);
}

122
resources/app/node_modules/peggy/bin/watcher.js generated vendored Normal file
View File

@@ -0,0 +1,122 @@
"use strict";
const fs = require("fs");
const path = require("path");
const { EventEmitter } = require("events");
// This may have to be tweaked based on experience.
const DEBOUNCE_MS = 100;
const CLOSING = Symbol("CLOSING");
const ERROR = Symbol("ERROR");
/**
* Relatively feature-free file watcher that deals with some of the
* idiosyncrasies of fs.watch. On some OS's, change notifications are doubled
* up. Watch the owning directory instead of the file, so that when the file
* doesn't exist then gets created we get a change notification instead of an
* error. When the file is moved in or out of the directory, don't track the
* inode of the original file. No notification is given on file deletion,
* just when the file is ready to be read.
*/
class Watcher extends EventEmitter {
/**
* Creates an instance of Watcher. This only works for files in a small
* number of directories.
*
* @param {string[]} filenames The files to watch. Should be one or more
* strings, each of which is the name of a plain file, not a directory,
* pipe, etc.
*/
constructor(...filenames) {
super();
const resolved = new Set(filenames.map(fn => path.resolve(fn)));
const dirs = new Set([...resolved].map(fn => path.dirname(fn)));
this.timeout = null;
this.watchers = [];
for (const dir of dirs) {
// eslint-disable-next-line func-style -- Needs "this"
const changed = (_typ, fn) => {
if (typeof this.timeout === "symbol") {
return;
}
const filename = path.join(dir, fn);
// Might be a different file changing in one of the target dirs
if (resolved.has(filename)) {
if (!this.timeout) {
fs.stat(filename, (er, stats) => {
if (!er && stats.isFile()) {
this.emit("change", filename, stats);
}
});
} else {
clearTimeout(this.timeout);
}
// De-bounce, across all files
this.timeout = setTimeout(() => {
this.timeout = null;
}, Watcher.interval);
}
};
const w = fs.watch(dir);
w.on("error", er => {
const t = this.timeout;
this.timeout = ERROR;
if (t && (typeof t !== "symbol")) {
clearTimeout(t);
}
this.emit("error", er);
this.close();
});
w.on("change", changed);
this.watchers.push(w);
}
// Fire initial time if file exists.
setImmediate(() => {
if (this.watchers.length > 0) {
// First watcher will correspond to the directory of the first filename.
const w = this.watchers[0];
w.emit("change", "initial", path.basename([...resolved][0]));
}
});
}
/**
* Close the watcher. Safe to call multiple times.
*
* @returns {Promise<void>} Always resolves.
*/
close() {
// Stop any more events from firing, immediately
const t = this.timeout;
if (t) {
if (typeof t !== "symbol") {
this.timeout = CLOSING;
clearTimeout(t);
}
}
const p = [];
for (const w of this.watchers) {
p.push(new Promise(resolve => {
w.once("close", resolve);
}));
w.close();
}
return Promise.all(p).then(() => {
this.watchers = [];
if (t !== ERROR) {
this.emit("close");
}
});
}
}
Watcher.interval = DEBOUNCE_MS;
module.exports = Watcher;

File diff suppressed because one or more lines are too long

145
resources/app/node_modules/peggy/lib/compiler/asts.js generated vendored Normal file
View File

@@ -0,0 +1,145 @@
"use strict";
const visitor = require("./visitor");
/**
* Combine two things, each of which might be an array, into a single value,
* in the order [...a, ...b].
*
* @template T
* @param {T | T[]} a
* @param {T | T[]} b
* @returns {T | T[]}
*/
function combinePossibleArrays(a, b) {
// First might be an array, second will not. Either might be null.
if (!(a && b)) {
return a || b;
}
const aa = Array.isArray(a) ? a : [a];
aa.push(b);
return aa;
}
// AST utilities.
const asts = {
/**
* Find the rule with the given name, if it exists.
*
* @param {PEG.ast.Grammar} ast
* @param {string} name
* @returns {PEG.ast.Rule | undefined}
*/
findRule(ast, name) {
for (let i = 0; i < ast.rules.length; i++) {
if (ast.rules[i].name === name) {
return ast.rules[i];
}
}
return undefined;
},
/**
* Find the index of the rule with the given name, if it exists.
* Otherwise returns -1.
*
* @param {PEG.ast.Grammar} ast
* @param {string} name
* @returns {number}
*/
indexOfRule(ast, name) {
for (let i = 0; i < ast.rules.length; i++) {
if (ast.rules[i].name === name) {
return i;
}
}
// istanbul ignore next Presence of rules checked using another approach that not involve this function
// Any time when it is called the rules always exist
return -1;
},
alwaysConsumesOnSuccess(ast, node) {
function consumesTrue() { return true; }
function consumesFalse() { return false; }
const consumes = visitor.build({
choice(node) {
return node.alternatives.every(consumes);
},
sequence(node) {
return node.elements.some(consumes);
},
simple_and: consumesFalse,
simple_not: consumesFalse,
optional: consumesFalse,
zero_or_more: consumesFalse,
repeated(node) {
// If minimum is `null` it is equals to maximum (parsed from `|exact|` syntax)
const min = node.min ? node.min : node.max;
// If the low boundary is variable then it can be zero.
// Expression, repeated zero times, does not consume any input
// but always matched - so it does not always consumes on success
if (min.type !== "constant" || min.value === 0) {
return false;
}
if (consumes(node.expression)) {
return true;
}
// |node.delimiter| used only when |node.expression| match at least two times
// The first `if` filtered out all non-constant minimums, so at this point
// |min.value| is always a constant
if (min.value > 1 && node.delimiter && consumes(node.delimiter)) {
return true;
}
return false;
},
semantic_and: consumesFalse,
semantic_not: consumesFalse,
rule_ref(node) {
const rule = asts.findRule(ast, node.name);
// Because we run all checks in one stage, some rules could be missing.
// Checking for missing rules could be executed in parallel to this check
return rule ? consumes(rule) : undefined;
},
library_ref() {
// No way to know for external rules.
return false;
},
literal(node) {
return node.value !== "";
},
class: consumesTrue,
any: consumesTrue,
});
return consumes(node);
},
combine(asts) {
return asts.reduce((combined, ast) => {
combined.topLevelInitializer = combinePossibleArrays(
combined.topLevelInitializer,
ast.topLevelInitializer
);
combined.initializer = combinePossibleArrays(
combined.initializer,
ast.initializer
);
combined.rules = combined.rules.concat(ast.rules);
return combined;
});
},
};
module.exports = asts;

170
resources/app/node_modules/peggy/lib/compiler/index.js generated vendored Normal file
View File

@@ -0,0 +1,170 @@
"use strict";
const addImportedRules = require("./passes/add-imported-rules");
const fixLibraryNumbers = require("./passes/fix-library-numbers");
const generateBytecode = require("./passes/generate-bytecode");
const generateJS = require("./passes/generate-js");
const inferenceMatchResult = require("./passes/inference-match-result");
const removeProxyRules = require("./passes/remove-proxy-rules");
const mergeCharacterClasses = require("./passes/merge-character-classes");
const reportDuplicateImports = require("./passes/report-duplicate-imports");
const reportDuplicateLabels = require("./passes/report-duplicate-labels");
const reportDuplicateRules = require("./passes/report-duplicate-rules");
const reportInfiniteRecursion = require("./passes/report-infinite-recursion");
const reportInfiniteRepetition = require("./passes/report-infinite-repetition");
const reportUndefinedRules = require("./passes/report-undefined-rules");
const reportIncorrectPlucking = require("./passes/report-incorrect-plucking");
const Session = require("./session");
const visitor = require("./visitor");
const { base64 } = require("./utils");
function processOptions(options, defaults) {
const processedOptions = {};
Object.keys(options).forEach(name => {
processedOptions[name] = options[name];
});
Object.keys(defaults).forEach(name => {
if (!Object.prototype.hasOwnProperty.call(processedOptions, name)) {
processedOptions[name] = defaults[name];
}
});
return processedOptions;
}
function isSourceMapCapable(target) {
if (typeof target === "string") {
return target.length > 0;
}
return target && (typeof target.offset === "function");
}
const compiler = {
// AST node visitor builder. Useful mainly for plugins which manipulate the
// AST.
visitor,
// Compiler passes.
//
// Each pass is a function that is passed the AST. It can perform checks on it
// or modify it as needed. If the pass encounters a semantic error, it throws
// |peg.GrammarError|.
passes: {
prepare: [
addImportedRules,
reportInfiniteRecursion,
],
check: [
reportUndefinedRules,
reportDuplicateRules,
reportDuplicateLabels,
reportInfiniteRepetition,
reportIncorrectPlucking,
reportDuplicateImports,
],
transform: [
fixLibraryNumbers,
removeProxyRules,
mergeCharacterClasses,
inferenceMatchResult,
],
generate: [
generateBytecode,
generateJS,
],
},
// Generates a parser from a specified grammar AST. Throws |peg.GrammarError|
// if the AST contains a semantic error. Note that not all errors are detected
// during the generation and some may protrude to the generated parser and
// cause its malfunction.
compile(ast, passes, options) {
options = options !== undefined ? options : {};
options = processOptions(options, {
allowedStartRules: [ast.rules[0].name],
cache: false,
dependencies: {},
exportVar: null,
format: "bare",
output: "parser",
trace: false,
});
if (!Array.isArray(options.allowedStartRules)) {
throw new Error("allowedStartRules must be an array");
}
if (options.allowedStartRules.length === 0) {
throw new Error("Must have at least one start rule");
}
const allRules = ast.rules.map(r => r.name);
// "*" means all rules are start rules. "*" is not a valid rule name.
if (options.allowedStartRules.some(r => r === "*")) {
options.allowedStartRules = allRules;
} else {
for (const rule of options.allowedStartRules) {
if (allRules.indexOf(rule) === -1) {
throw new Error(`Unknown start rule "${rule}"`);
}
}
}
// Due to https://github.com/mozilla/source-map/issues/444
// grammarSource is required
if (((options.output === "source-and-map")
|| (options.output === "source-with-inline-map"))
&& !isSourceMapCapable(options.grammarSource)) {
throw new Error("Must provide grammarSource (as a string or GrammarLocation) in order to generate source maps");
}
const session = new Session(options);
Object.keys(passes).forEach(stage => {
session.stage = stage;
session.info(`Process stage ${stage}`);
passes[stage].forEach(pass => {
session.info(`Process pass ${stage}.${pass.name}`);
pass(ast, options, session);
});
// Collect all errors by stage
session.checkErrors();
});
switch (options.output) {
case "parser":
// eslint-disable-next-line no-eval -- Required
return eval(ast.code.toString());
case "source":
return ast.code.toString();
case "source-and-map":
return ast.code;
case "source-with-inline-map": {
if (typeof TextEncoder === "undefined") {
throw new Error("TextEncoder is not supported by this platform");
}
const sourceMap = ast.code.toStringWithSourceMap();
const encoder = new TextEncoder();
const b64 = base64(
encoder.encode(JSON.stringify(sourceMap.map.toJSON()))
);
return sourceMap.code + `\
//\x23 sourceMappingURL=data:application/json;charset=utf-8;base64,${b64}
`;
}
case "ast":
return ast;
default:
throw new Error("Invalid output format: " + options.output + ".");
}
},
};
module.exports = compiler;

View File

@@ -0,0 +1,77 @@
// @ts-check
"use strict";
/**
* Intern strings or objects, so there is only one copy of each, by value.
* Objects may need to be converted to another representation before storing.
* Each inputs corresponds to a number, starting with 0.
*
* @template [T=string],[V=T]
*/
class Intern {
/**
* @typedef {object} InternOptions
* @property {(input: V) => string} [stringify=String] Represent the
* converted input as a string, for value comparison.
* @property {(input: T) => V} [convert=(x) => x] Convert the input to its
* stored form. Required if type V is not the same as type T. Return
* falsy value to have this input not be added; add() will return -1 in
* this case.
*/
/**
* @param {InternOptions} [options]
*/
constructor(options) {
/** @type {Required<InternOptions>} */
this.options = {
stringify: String,
convert: x => /** @type {V} */ (/** @type {unknown} */ (x)),
...options,
};
/** @type {V[]} */
this.items = [];
/** @type {Record<string, number>} */
this.offsets = {};
}
/**
* Intern an item, getting it's asssociated number. Returns -1 for falsy
* inputs. O(1) with constants tied to the convert and stringify options.
*
* @param {T} input
* @return {number}
*/
add(input) {
const c = this.options.convert(input);
if (!c) {
return -1;
}
const s = this.options.stringify(c);
let num = this.offsets[s];
if (num === undefined) {
num = this.items.push(c) - 1;
this.offsets[s] = num;
}
return num;
}
/**
* @param {number} i
* @returns {V}
*/
get(i) {
return this.items[i];
}
/**
* @template U
* @param {(value: V, index: number, array: V[]) => U} fn
* @returns {U[]}
*/
map(fn) {
return this.items.map(fn);
}
}
module.exports = Intern;

View File

@@ -0,0 +1,83 @@
"use strict";
// Bytecode instruction opcodes.
const opcodes = {
// Stack Manipulation
/** @deprecated Unused */
PUSH: 0, // PUSH c
PUSH_EMPTY_STRING: 35, // PUSH_EMPTY_STRING
PUSH_UNDEFINED: 1, // PUSH_UNDEFINED
PUSH_NULL: 2, // PUSH_NULL
PUSH_FAILED: 3, // PUSH_FAILED
PUSH_EMPTY_ARRAY: 4, // PUSH_EMPTY_ARRAY
PUSH_CURR_POS: 5, // PUSH_CURR_POS
POP: 6, // POP
POP_CURR_POS: 7, // POP_CURR_POS
POP_N: 8, // POP_N n
NIP: 9, // NIP
APPEND: 10, // APPEND
WRAP: 11, // WRAP n
TEXT: 12, // TEXT
PLUCK: 36, // PLUCK n, k, p1, ..., pK
// Conditions and Loops
IF: 13, // IF t, f
IF_ERROR: 14, // IF_ERROR t, f
IF_NOT_ERROR: 15, // IF_NOT_ERROR t, f
IF_LT: 30, // IF_LT min, t, f
IF_GE: 31, // IF_GE max, t, f
IF_LT_DYNAMIC: 32, // IF_LT_DYNAMIC min, t, f
IF_GE_DYNAMIC: 33, // IF_GE_DYNAMIC max, t, f
WHILE_NOT_ERROR: 16, // WHILE_NOT_ERROR b
// Matching
MATCH_ANY: 17, // MATCH_ANY a, f, ...
MATCH_STRING: 18, // MATCH_STRING s, a, f, ...
MATCH_STRING_IC: 19, // MATCH_STRING_IC s, a, f, ...
MATCH_CHAR_CLASS: 20, // MATCH_CHAR_CLASS c, a, f, ...
/** @deprecated Replaced with `MATCH_CHAR_CLASS` */
MATCH_REGEXP: 20, // MATCH_REGEXP r, a, f, ...
ACCEPT_N: 21, // ACCEPT_N n
ACCEPT_STRING: 22, // ACCEPT_STRING s
FAIL: 23, // FAIL e
// Calls
LOAD_SAVED_POS: 24, // LOAD_SAVED_POS p
UPDATE_SAVED_POS: 25, // UPDATE_SAVED_POS
CALL: 26, // CALL f, n, pc, p1, p2, ..., pN
// Rules
RULE: 27, // RULE r
LIBRARY_RULE: 41, // LIBRARY_RULE moduleIndex, whatIndex
// Failure Reporting
SILENT_FAILS_ON: 28, // SILENT_FAILS_ON
SILENT_FAILS_OFF: 29, // SILENT_FAILS_OFF
// Because the tests have hard-coded opcode numbers, don't renumber
// existing opcodes. New opcodes that have been put in the correct
// sections above are repeated here in order to ensure we don't
// reuse them.
//
// IF_LT: 30
// IF_GE: 31
// IF_LT_DYNAMIC: 32
// IF_GE_DYNAMIC: 33
// 34 reserved for @mingun
// PUSH_EMPTY_STRING: 35
// PLUCK: 36
SOURCE_MAP_PUSH: 37, // SOURCE_MAP_PUSH loc-index
SOURCE_MAP_POP: 38, // SOURCE_MAP_POP
SOURCE_MAP_LABEL_PUSH: 39, // SOURCE_MAP_LABEL_PUSH sp, literal-index, loc-index
SOURCE_MAP_LABEL_POP: 40, // SOURCE_MAP_LABEL_POP sp
// LIBRARY_RULE: 41,
};
module.exports = opcodes;

View File

@@ -0,0 +1,52 @@
// @ts-check
"use strict";
/**
* Generate trampoline stubs for each rule imported into this namespace.
*
* @example
* import bar from "./lib.js" // Default rule imported into this namespace
* import {baz} from "./lib.js" // One rule imported into this namespace by name
*
* @type {PEG.Pass}
*/
function addImportedRules(ast) {
let libraryNumber = 0;
for (const imp of ast.imports) {
for (const what of imp.what) {
let original = undefined;
switch (what.type) {
case "import_binding_all":
// Don't create stub.
continue;
case "import_binding_default":
// Use the default (usually first) rule.
break;
case "import_binding":
original = what.binding;
break;
case "import_binding_rename":
original = what.rename;
break;
default:
throw new TypeError("Unknown binding type");
}
ast.rules.push({
type: "rule",
name: what.binding,
nameLocation: what.location,
expression: {
type: "library_ref",
name: original,
library: imp.from.module,
libraryNumber,
location: what.location,
},
location: imp.from.location,
});
}
libraryNumber++;
}
}
module.exports = addImportedRules;

View File

@@ -0,0 +1,43 @@
// @ts-check
"use strict";
const visitor = require("../visitor");
/**
* @param {PEG.ast.Grammar} ast
* @param {string} name
* @returns {number}
*/
function findLibraryNumber(ast, name) {
let libraryNumber = 0;
for (const imp of ast.imports) {
for (const what of imp.what) {
if ((what.type === "import_binding_all") && (what.binding === name)) {
return libraryNumber;
}
}
libraryNumber++;
}
return -1;
}
/** @type {PEG.Pass} */
function fixLibraryNumbers(ast, _options, session) {
const check = visitor.build({
library_ref(/** @type {PEG.ast.LibraryReference} */ node) {
if (node.libraryNumber === -1) {
node.libraryNumber = findLibraryNumber(ast, node.library);
if (node.libraryNumber === -1) {
session.error(
`Unknown module "${node.library}"`,
node.location
);
}
}
},
});
check(ast);
}
module.exports = fixLibraryNumbers;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
"use strict";
const visitor = require("../visitor");
const asts = require("../asts");
const GrammarError = require("../../grammar-error");
const ALWAYS_MATCH = 1;
const SOMETIMES_MATCH = 0;
const NEVER_MATCH = -1;
// Inference match result of the each node. Can be:
// -1: negative result, matching of that node always fails
// 0: neutral result, may be fail, may be match
// 1: positive result, always match
function inferenceMatchResult(ast) {
function sometimesMatch(node) { return (node.match = SOMETIMES_MATCH); }
function alwaysMatch(node) {
// eslint-disable-next-line no-use-before-define -- Mutual recursion
inference(node.expression);
return (node.match = ALWAYS_MATCH);
}
function inferenceExpression(node) {
// eslint-disable-next-line no-use-before-define -- Mutual recursion
return (node.match = inference(node.expression));
}
function inferenceElements(elements, forChoice) {
const length = elements.length;
let always = 0;
let never = 0;
for (let i = 0; i < length; ++i) {
// eslint-disable-next-line no-use-before-define -- Mutual recursion
const result = inference(elements[i]);
if (result === ALWAYS_MATCH) { ++always; }
if (result === NEVER_MATCH) { ++never; }
}
if (always === length) {
return ALWAYS_MATCH;
}
if (forChoice) {
return never === length ? NEVER_MATCH : SOMETIMES_MATCH;
}
return never > 0 ? NEVER_MATCH : SOMETIMES_MATCH;
}
const inference = visitor.build({
rule(node) {
let oldResult = undefined;
let count = 0;
// If property not yet calculated, do that
if (typeof node.match === "undefined") {
node.match = SOMETIMES_MATCH;
do {
oldResult = node.match;
node.match = inference(node.expression);
// 6 == 3! -- permutations count for all transitions from one match
// state to another.
// After 6 iterations the cycle with guarantee begins
// For example, an input of `start = [] start` will generate the
// sequence: 0 -> -1 -> -1 (then stop)
//
// A more complex grammar theoretically would generate the
// sequence: 0 -> 1 -> 0 -> -1 -> 0 -> 1 -> ... (then cycle)
// but there are no examples of such grammars yet (possible, they
// do not exist at all)
// istanbul ignore next This is canary test, shouldn't trigger in real life
if (++count > 6) {
throw new GrammarError(
"Infinity cycle detected when trying to evaluate node match result",
node.location
);
}
} while (oldResult !== node.match);
}
return node.match;
},
named: inferenceExpression,
choice(node) {
return (node.match = inferenceElements(node.alternatives, true));
},
action: inferenceExpression,
sequence(node) {
return (node.match = inferenceElements(node.elements, false));
},
labeled: inferenceExpression,
text: inferenceExpression,
simple_and: inferenceExpression,
simple_not(node) {
return (node.match = -inference(node.expression));
},
optional: alwaysMatch,
zero_or_more: alwaysMatch,
one_or_more: inferenceExpression,
repeated(node) {
const match = inference(node.expression);
const dMatch = node.delimiter ? inference(node.delimiter) : NEVER_MATCH;
// If minimum is `null` it is equals to maximum (parsed from `|exact|` syntax)
const min = node.min ? node.min : node.max;
// If any boundary are variable - it can be negative, and it that case
// node does not match, but it may be match with some other values
if (min.type !== "constant" || node.max.type !== "constant") {
return (node.match = SOMETIMES_MATCH);
}
// Now both boundaries is constants
// If the upper boundary is zero or minimum exceeds maximum,
// matching is impossible
if (node.max.value === 0
|| (node.max.value !== null && min.value > node.max.value)
) {
return (node.match = NEVER_MATCH);
}
if (match === NEVER_MATCH) {
// If an expression always fails, a range will also always fail
// (with the one exception - never matched expression repeated
// zero times always match and returns an empty array).
return (node.match = min.value === 0 ? ALWAYS_MATCH : NEVER_MATCH);
}
if (match === ALWAYS_MATCH) {
if (node.delimiter && min.value >= 2) {
// If an expression always match the final result determined only
// by the delimiter, but delimiter used only when count of elements
// two and more
return (node.match = dMatch);
}
return (node.match = ALWAYS_MATCH);
}
// Here `match === SOMETIMES_MATCH`
if (node.delimiter && min.value >= 2) {
// If an expression always match the final result determined only
// by the delimiter, but delimiter used only when count of elements
// two and more
return (
// If a delimiter never match then the range also never match (because
// there at least one delimiter)
node.match = dMatch === NEVER_MATCH ? NEVER_MATCH : SOMETIMES_MATCH
);
}
return (node.match = min.value === 0 ? ALWAYS_MATCH : SOMETIMES_MATCH);
},
group: inferenceExpression,
semantic_and: sometimesMatch,
semantic_not: sometimesMatch,
rule_ref(node) {
const rule = asts.findRule(ast, node.name);
if (!rule) {
return SOMETIMES_MATCH;
}
return (node.match = inference(rule));
},
library_ref() {
// Can't look into pre-compiled rules.
return 0;
},
literal(node) {
// Empty literal always match on any input
const match = node.value.length === 0 ? ALWAYS_MATCH : SOMETIMES_MATCH;
return (node.match = match);
},
class(node) {
// Empty character class never match on any input
const match = node.parts.length === 0 ? NEVER_MATCH : SOMETIMES_MATCH;
return (node.match = match);
},
// |any| not match on empty input
any: sometimesMatch,
});
inference(ast);
}
inferenceMatchResult.ALWAYS_MATCH = ALWAYS_MATCH;
inferenceMatchResult.SOMETIMES_MATCH = SOMETIMES_MATCH;
inferenceMatchResult.NEVER_MATCH = NEVER_MATCH;
module.exports = inferenceMatchResult;

View File

@@ -0,0 +1,191 @@
// @ts-check
"use strict";
/**
* @typedef {import("../../peg")} PEG
*/
/** @type {PEG.compiler.visitor} */
const visitor = require("../visitor");
/**
* @param {unknown} target
* @param {unknown} source
*/
function cloneOver(target, source) {
const t = /** @type {Record<string,unknown>} */ (target);
const s = /** @type {Record<string,unknown>} */ (source);
Object.keys(t).forEach(key => delete t[key]);
Object.keys(s).forEach(key => { t[key] = s[key]; });
}
/**
* Clean up the parts array of a `class` node, by sorting,
* then removing "contained" ranges, and merging overlapping
* or adjacent ranges.
*
* @param {PEG.ast.CharacterClass["parts"]} parts
*/
function cleanParts(parts) {
// Sort parts on increasing start, and then decreasing end.
parts.sort((a, b) => {
const [aStart, aEnd] = Array.isArray(a) ? a : [a, a];
const [bStart, bEnd] = Array.isArray(b) ? b : [b, b];
if (aStart !== bStart) {
return aStart < bStart ? -1 : 1;
}
if (aEnd !== bEnd) {
return aEnd > bEnd ? -1 : 1;
}
return 0;
});
let prevStart = "";
let prevEnd = "";
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const [curStart, curEnd] = Array.isArray(part) ? part : [part, part];
if (curEnd <= prevEnd) {
// Current range is contained in previous range,
// so drop it.
parts.splice(i--, 1);
continue;
}
if (prevEnd.charCodeAt(0) + 1 >= curStart.charCodeAt(0)) {
// Current and previous ranges overlap, or are adjacent.
// Drop the current, and extend the previous range.
parts.splice(i--, 1);
parts[i] = [prevStart, prevEnd = curEnd];
continue;
}
prevStart = curStart;
prevEnd = curEnd;
}
return parts;
}
/**
* Merges a choice character classes into a character class
* @param {PEG.ast.Grammar} ast
*/
function mergeCharacterClasses(ast) {
// Build a map from rule names to rules for quick lookup of
// ref_rules.
const rules = Object.create(null);
ast.rules.forEach(rule => (rules[rule.name] = rule.expression));
// Keep a map of which rules have been processed, so that when
// we find a ref_rule, we can make sure its processed, before we
// try to use it.
const processedRules = Object.create(null);
const [asClass, merge] = [
/**
* Determine whether a node can be represented as a simple character class,
* and return that class if so.
*
* @param {PEG.ast.Expression} node - the node to inspect
* @param {boolean} [clone] - if true, always return a new node that
* can be modified by the caller
* @returns {PEG.ast.CharacterClass | null}
*/
(node, clone) => {
if (node.type === "class" && !node.inverted) {
if (clone) {
node = { ...node };
node.parts = [...node.parts];
}
return node;
}
if (node.type === "literal" && node.value.length === 1) {
return {
type: "class",
parts: [node.value],
inverted: false,
ignoreCase: node.ignoreCase,
location: node.location,
};
}
if (node.type === "rule_ref") {
const ref = rules[node.name];
if (ref) {
if (!processedRules[node.name]) {
processedRules[node.name] = true;
merge(ref);
}
const cls = asClass(ref, true);
if (cls) {
cls.location = node.location;
}
return cls;
}
}
return null;
},
visitor.build({
choice(node) {
/** @type {PEG.ast.CharacterClass | null} */
let prev = null;
let changed = false;
node.alternatives.forEach((alt, i) => {
merge(alt);
const cls = asClass(alt);
if (!cls) {
prev = null;
return;
}
if (prev && prev.ignoreCase === cls.ignoreCase) {
prev.parts.push(...cls.parts);
node.alternatives[i - 1] = prev;
node.alternatives[i] = prev;
prev.location = {
source: prev.location.source,
start: prev.location.start,
end: cls.location.end,
};
changed = true;
} else {
prev = cls;
}
});
if (changed) {
node.alternatives = node.alternatives.filter(
(alt, i, arr) => !i || alt !== arr[i - 1]
);
node.alternatives.forEach((alt, i) => {
if (alt.type === "class") {
alt.parts = cleanParts(alt.parts);
if (alt.parts.length === 1
&& !Array.isArray(alt.parts[0])
&& !alt.inverted) {
node.alternatives[i] = {
type: "literal",
value: alt.parts[0],
ignoreCase: alt.ignoreCase,
location: alt.location,
};
}
}
});
if (node.alternatives.length === 1) {
cloneOver(node, node.alternatives[0]);
}
}
},
text(node) {
merge(node.expression);
if (node.expression.type === "class"
|| node.expression.type === "literal") {
const location = node.location;
cloneOver(node, node.expression);
node.location = location;
}
},
}),
];
ast.rules.forEach(rule => {
processedRules[rule.name] = true;
merge(rule.expression);
});
}
module.exports = mergeCharacterClasses;

View File

@@ -0,0 +1,49 @@
"use strict";
const asts = require("../asts");
const visitor = require("../visitor");
// Removes proxy rules -- that is, rules that only delegate to other rule.
function removeProxyRules(ast, options, session) {
function isProxyRule(node) {
return node.type === "rule" && node.expression.type === "rule_ref";
}
function replaceRuleRefs(ast, from, to) {
const replace = visitor.build({
rule_ref(node) {
if (node.name === from) {
node.name = to;
session.info(
`Proxy rule "${from}" replaced by the rule "${to}"`,
node.location,
[{
message: "This rule will be used",
location: asts.findRule(ast, to).nameLocation,
}]
);
}
},
});
replace(ast);
}
const indices = [];
ast.rules.forEach((rule, i) => {
if (isProxyRule(rule)) {
replaceRuleRefs(ast, rule.name, rule.expression.name);
if (options.allowedStartRules.indexOf(rule.name) === -1) {
indices.push(i);
}
}
});
indices.reverse();
indices.forEach(i => { ast.rules.splice(i, 1); });
}
module.exports = removeProxyRules;

View File

@@ -0,0 +1,28 @@
// @ts-check
"use strict";
/** @type {PEG.Pass} */
function reportDuplicateImports(ast, _options, session) {
/** @type {Record<string, PEG.LocationRange>} */
const all = {};
for (const imp of ast.imports) {
for (const what of imp.what) {
if (what.type === "import_binding_all") {
if (Object.prototype.hasOwnProperty.call(all, what.binding)) {
session.error(
`Module "${what.binding}" is already imported`,
what.location,
[{
message: "Original module location",
location: all[what.binding],
}]
);
}
all[what.binding] = what.location;
}
}
}
}
module.exports = reportDuplicateImports;

View File

@@ -0,0 +1,72 @@
"use strict";
const visitor = require("../visitor");
// Checks that each label is defined only once within each scope.
function reportDuplicateLabels(ast, options, session) {
function cloneEnv(env) {
const clone = {};
Object.keys(env).forEach(name => {
clone[name] = env[name];
});
return clone;
}
function checkExpressionWithClonedEnv(node, env) {
// eslint-disable-next-line no-use-before-define -- Mutual recursion
check(node.expression, cloneEnv(env));
}
const check = visitor.build({
rule(node) {
check(node.expression, { });
},
choice(node, env) {
node.alternatives.forEach(alternative => {
check(alternative, cloneEnv(env));
});
},
action: checkExpressionWithClonedEnv,
labeled(node, env) {
const label = node.label;
if (label && Object.prototype.hasOwnProperty.call(env, label)) {
session.error(
`Label "${node.label}" is already defined`,
node.labelLocation,
[{
message: "Original label location",
location: env[label],
}]
);
}
check(node.expression, env);
env[node.label] = node.labelLocation;
},
text: checkExpressionWithClonedEnv,
simple_and: checkExpressionWithClonedEnv,
simple_not: checkExpressionWithClonedEnv,
optional: checkExpressionWithClonedEnv,
zero_or_more: checkExpressionWithClonedEnv,
one_or_more: checkExpressionWithClonedEnv,
repeated(node, env) {
if (node.delimiter) {
check(node.delimiter, cloneEnv(env));
}
check(node.expression, cloneEnv(env));
},
group: checkExpressionWithClonedEnv,
});
check(ast);
}
module.exports = reportDuplicateLabels;

View File

@@ -0,0 +1,32 @@
"use strict";
const visitor = require("../visitor");
// Checks that each rule is defined only once.
function reportDuplicateRules(ast, options, session) {
const rules = {};
const check = visitor.build({
rule(node) {
if (Object.prototype.hasOwnProperty.call(rules, node.name)) {
session.error(
`Rule "${node.name}" is already defined`,
node.nameLocation,
[{
message: "Original rule location",
location: rules[node.name],
}]
);
// Do not rewrite original rule location
return;
}
rules[node.name] = node.nameLocation;
},
});
check(ast);
}
module.exports = reportDuplicateRules;

View File

@@ -0,0 +1,37 @@
"use strict";
const visitor = require("../visitor");
//
// Compiler pass to ensure the following are enforced:
//
// - plucking can not be done with an action block
//
function reportIncorrectPlucking(ast, options, session) {
const check = visitor.build({
action(node) {
check(node.expression, node);
},
labeled(node, action) {
if (node.pick) {
if (action) {
session.error(
"\"@\" cannot be used with an action block",
node.labelLocation,
[{
message: "Action block location",
location: action.codeLocation,
}]
);
}
}
check(node.expression);
},
});
check(ast);
}
module.exports = reportIncorrectPlucking;

View File

@@ -0,0 +1,101 @@
"use strict";
const asts = require("../asts");
const visitor = require("../visitor");
// Reports left recursion in the grammar, which prevents infinite recursion in
// the generated parser.
//
// Both direct and indirect recursion is detected. The pass also correctly
// reports cases like this:
//
// start = "a"? start
//
// In general, if a rule reference can be reached without consuming any input,
// it can lead to left recursion.
function reportInfiniteRecursion(ast, options, session) {
// Array with rule names for error message
const visitedRules = [];
// Array with rule_refs for diagnostic
const backtraceRefs = [];
const check = visitor.build({
rule(node) {
if (session.errors > 0) {
return;
}
visitedRules.push(node.name);
check(node.expression);
visitedRules.pop();
},
sequence(node) {
if (session.errors > 0) {
return;
}
node.elements.every(element => {
check(element);
if (session.errors > 0) {
return false;
}
return !asts.alwaysConsumesOnSuccess(ast, element);
});
},
repeated(node) {
if (session.errors > 0) {
return;
}
check(node.expression);
// If an expression does not consume input then recursion
// over delimiter is possible
if (node.delimiter
&& !asts.alwaysConsumesOnSuccess(ast, node.expression)
) {
check(node.delimiter);
}
},
rule_ref(node) {
if (session.errors > 0) {
return;
}
backtraceRefs.push(node);
const rule = asts.findRule(ast, node.name);
if (visitedRules.indexOf(node.name) !== -1) {
visitedRules.push(node.name);
session.error(
"Possible infinite loop when parsing (left recursion: "
+ visitedRules.join(" -> ")
+ ")",
rule.nameLocation,
backtraceRefs.map((ref, i, a) => ({
message: i + 1 !== a.length
? `Step ${i + 1}: call of the rule "${ref.name}" without input consumption`
: `Step ${i + 1}: call itself without input consumption - left recursion`,
location: ref.location,
}))
);
// Because we enter into recursion we should break it
return;
}
// Because we run all checks in one stage, some rules could be missing - this check
// executed in parallel
if (rule) {
check(rule);
}
backtraceRefs.pop();
},
});
check(ast);
}
module.exports = reportInfiniteRecursion;

View File

@@ -0,0 +1,64 @@
"use strict";
const asts = require("../asts");
const visitor = require("../visitor");
// Reports expressions that don't consume any input inside |*|, |+| or repeated in the
// grammar, which prevents infinite loops in the generated parser.
function reportInfiniteRepetition(ast, options, session) {
const check = visitor.build({
zero_or_more(node) {
if (!asts.alwaysConsumesOnSuccess(ast, node.expression)) {
session.error(
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input)",
node.location
);
}
},
one_or_more(node) {
if (!asts.alwaysConsumesOnSuccess(ast, node.expression)) {
session.error(
"Possible infinite loop when parsing (repetition used with an expression that may not consume any input)",
node.location
);
}
},
repeated(node) {
// No need to check min or max. They can only be numbers, variable
// names, or code blocks.
if (node.delimiter) {
check(node.delimiter);
}
if (asts.alwaysConsumesOnSuccess(ast, node.expression)
|| (node.delimiter
&& asts.alwaysConsumesOnSuccess(ast, node.delimiter))) {
return;
}
if (node.max.value === null) {
session.error(
"Possible infinite loop when parsing (unbounded range repetition used with an expression that may not consume any input)",
node.location
);
} else {
// If minimum is `null` it is equals to maximum (parsed from `|exact|` syntax)
const min = node.min ? node.min : node.max;
// Because the high boundary is defined, infinity repetition is not possible
// but the grammar will waste of CPU
session.warning(
min.type === "constant" && node.max.type === "constant"
? `An expression may not consume any input and may always match ${node.max.value} times`
: "An expression may not consume any input and may always match with a maximum repetition count",
node.location
);
}
},
});
check(ast);
}
module.exports = reportInfiniteRepetition;

View File

@@ -0,0 +1,22 @@
"use strict";
const asts = require("../asts");
const visitor = require("../visitor");
// Checks that all referenced rules exist.
function reportUndefinedRules(ast, options, session) {
const check = visitor.build({
rule_ref(node) {
if (!asts.findRule(ast, node.name)) {
session.error(
`Rule "${node.name}" is not defined`,
node.location
);
}
},
});
check(ast);
}
module.exports = reportUndefinedRules;

View File

@@ -0,0 +1,84 @@
"use strict";
const GrammarError = require("../grammar-error");
class Defaults {
constructor(options) {
options = typeof options !== "undefined" ? options : {};
if (typeof options.error === "function") { this.error = options.error; }
if (typeof options.warning === "function") { this.warning = options.warning; }
if (typeof options.info === "function") { this.info = options.info; }
}
// eslint-disable-next-line class-methods-use-this -- Abstract
error() {
// Intentionally empty placeholder
}
// eslint-disable-next-line class-methods-use-this -- Abstract
warning() {
// Intentionally empty placeholder
}
// eslint-disable-next-line class-methods-use-this -- Abstract
info() {
// Intentionally empty placeholder
}
}
class Session {
constructor(options) {
this._callbacks = new Defaults(options);
this._firstError = null;
this.errors = 0;
/** @type {import("../peg").Problem[]} */
this.problems = [];
/** @type {import("../peg").Stage} */
this.stage = null;
}
error(...args) {
++this.errors;
// In order to preserve backward compatibility we cannot change `GrammarError`
// constructor, nor throw another class of error:
// - if we change `GrammarError` constructor, this will break plugins that
// throws `GrammarError`
// - if we throw another Error class, this will break parser clients that
// catches GrammarError
//
// So we use a compromise: we throw an `GrammarError` with all found problems
// in the `problems` property, but the thrown error itself is the first
// registered error.
//
// Thus when the old client catches the error it can find all properties on
// the Grammar error that it want. On the other hand the new client can
// inspect the `problems` property to get all problems.
if (this._firstError === null) {
this._firstError = new GrammarError(...args);
this._firstError.stage = this.stage;
this._firstError.problems = this.problems;
}
this.problems.push(["error", ...args]);
this._callbacks.error(this.stage, ...args);
}
warning(...args) {
this.problems.push(["warning", ...args]);
this._callbacks.warning(this.stage, ...args);
}
info(...args) {
this.problems.push(["info", ...args]);
this._callbacks.info(this.stage, ...args);
}
checkErrors() {
if (this.errors !== 0) {
throw this._firstError;
}
}
}
module.exports = Session;

365
resources/app/node_modules/peggy/lib/compiler/stack.js generated vendored Normal file
View File

@@ -0,0 +1,365 @@
// @ts-check
"use strict";
const { SourceNode } = require("source-map-generator");
const GrammarLocation = require("../grammar-location.js");
/**
* @typedef {(string|SourceNode)[]} SourceArray
*/
/** Utility class that helps generating code for C-like languages. */
class Stack {
/**
* Constructs the helper for tracking variable slots of the stack virtual machine
*
* @param {string} ruleName The name of rule that will be used in error messages
* @param {string} varName The prefix for generated names of variables
* @param {string} type The type of the variables. For JavaScript there are `var` or `let`
* @param {number[]} bytecode Bytecode for error messages
*/
constructor(ruleName, varName, type, bytecode) {
/** Last used variable in the stack. */
this.sp = -1;
/** Maximum stack size. */
this.maxSp = -1;
this.varName = varName;
this.ruleName = ruleName;
this.type = type;
this.bytecode = bytecode;
/**
* Map from stack index, to label targetting that index
* @type {Record<number,{label:string,location:import("../peg.js").LocationRange}>}
*/
this.labels = {};
/**
* Stack of in-flight source mappings
* @type {[SourceArray, number, PEG.LocationRange][]}
*/
this.sourceMapStack = [];
}
/**
* Returns name of the variable at the index `i`.
*
* @param {number} i Index for which name must be generated
* @return {string} Generated name
*
* @throws {RangeError} If `i < 0`, which means a stack underflow (there are more `pop`s than `push`es)
*/
name(i) {
if (i < 0) {
throw new RangeError(
`Rule '${this.ruleName}': The variable stack underflow: attempt to use a variable '${this.varName}<x>' at an index ${i}.\nBytecode: ${this.bytecode}`
);
}
return this.varName + i;
}
/**
*
* @param {PEG.LocationRange} location
* @param {SourceArray} chunks
* @param {string} [name]
* @returns
*/
static sourceNode(location, chunks, name) {
const start = GrammarLocation.offsetStart(location);
return new SourceNode(
start.line,
start.column ? start.column - 1 : null,
String(location.source),
chunks,
name
);
}
/**
* Assigns `exprCode` to the new variable in the stack, returns generated code.
* As the result, the size of a stack increases on 1.
*
* @param {string} exprCode Any expression code that must be assigned to the new variable in the stack
* @return {string|SourceNode} Assignment code
*/
push(exprCode) {
if (++this.sp > this.maxSp) { this.maxSp = this.sp; }
const label = this.labels[this.sp];
const code = [this.name(this.sp), " = ", exprCode, ";"];
if (label) {
if (this.sourceMapStack.length) {
const sourceNode = Stack.sourceNode(
label.location,
code.splice(0, 2),
label.label
);
const { parts, location } = this.sourceMapPopInternal();
const newLoc = (location.start.offset < label.location.end.offset)
? {
start: label.location.end,
end: location.end,
source: location.source,
}
: location;
const outerNode = Stack.sourceNode(
newLoc,
code.concat("\n")
);
this.sourceMapStack.push([parts, parts.length + 1, location]);
return new SourceNode(
null,
null,
label.location.source,
[sourceNode, outerNode]
);
} else {
return Stack.sourceNode(
label.location,
code.concat("\n")
);
}
}
return code.join("");
}
/**
* @overload
* @param {undefined} [n]
* @return {string}
*/
/**
* @overload
* @param {number} n
* @return {string[]}
*/
/**
* Returns name or `n` names of the variable(s) from the top of the stack.
*
* @param {number} [n] Quantity of variables, which need to be removed from the stack
* @returns {string[]|string} Generated name(s). If n is defined then it returns an
* array of length `n`
*
* @throws {RangeError} If the stack underflow (there are more `pop`s than `push`es)
*/
pop(n) {
if (n !== undefined) {
this.sp -= n;
return Array.from({ length: n }, (v, i) => this.name(this.sp + 1 + i));
}
return this.name(this.sp--);
}
/**
* Returns name of the first free variable. The same as `index(0)`.
*
* @return {string} Generated name
*
* @throws {RangeError} If the stack is empty (there was no `push`'s yet)
*/
top() { return this.name(this.sp); }
/**
* Returns name of the variable at index `i`.
*
* @param {number} i Index of the variable from top of the stack
* @return {string} Generated name
*
* @throws {RangeError} If `i < 0` or more than the stack size
*/
index(i) {
if (i < 0) {
throw new RangeError(
`Rule '${this.ruleName}': The variable stack overflow: attempt to get a variable at a negative index ${i}.\nBytecode: ${this.bytecode}`
);
}
return this.name(this.sp - i);
}
/**
* Returns variable name that contains result (bottom of the stack).
*
* @return {string} Generated name
*
* @throws {RangeError} If the stack is empty (there was no `push`es yet)
*/
result() {
if (this.maxSp < 0) {
throw new RangeError(
`Rule '${this.ruleName}': The variable stack is empty, can't get the result.\nBytecode: ${this.bytecode}`
);
}
return this.name(0);
}
/**
* Returns defines of all used variables.
*
* @return {string} Generated define variable expression with the type `this.type`.
* If the stack is empty, returns empty string
*/
defines() {
if (this.maxSp < 0) {
return "";
}
return this.type + " " + Array.from({ length: this.maxSp + 1 }, (v, i) => this.name(i)).join(", ") + ";";
}
/**
* Checks that code in the `generateIf` and `generateElse` move the stack pointer in the same way.
*
* @template T
* @param {number} pos Opcode number for error messages
* @param {() => T} generateIf First function that works with this stack
* @param {(() => T)|null} [generateElse] Second function that works with this stack
* @return {T[]}
*
* @throws {Error} If `generateElse` is defined and the stack pointer moved differently in the
* `generateIf` and `generateElse`
*/
checkedIf(pos, generateIf, generateElse) {
const baseSp = this.sp;
const ifResult = generateIf();
if (!generateElse) {
return [ifResult];
}
const thenSp = this.sp;
this.sp = baseSp;
const elseResult = generateElse();
if (thenSp !== this.sp) {
throw new Error(
"Rule '" + this.ruleName + "', position " + pos + ": "
+ "Branches of a condition can't move the stack pointer differently "
+ "(before: " + baseSp + ", after then: " + thenSp + ", after else: " + this.sp + "). "
+ "Bytecode: " + this.bytecode
);
}
return [ifResult, elseResult];
}
/**
* Checks that code in the `generateBody` do not move stack pointer.
*
* @template T
* @param {number} pos Opcode number for error messages
* @param {() => T} generateBody Function that works with this stack
* @return {T}
*
* @throws {Error} If `generateBody` move the stack pointer (if it contains unbalanced `push`es and `pop`s)
*/
checkedLoop(pos, generateBody) {
const baseSp = this.sp;
const result = generateBody();
if (baseSp !== this.sp) {
throw new Error(
"Rule '" + this.ruleName + "', position " + pos + ": "
+ "Body of a loop can't move the stack pointer "
+ "(before: " + baseSp + ", after: " + this.sp + "). "
+ "Bytecode: " + this.bytecode
);
}
return result;
}
/**
*
* @param {SourceArray} parts
* @param {PEG.LocationRange} location
*/
sourceMapPush(parts, location) {
if (this.sourceMapStack.length) {
const top = this.sourceMapStack[this.sourceMapStack.length - 1];
// If the current top of stack starts at the same location as
// the about to be pushed item, we should update its start location to
// be past the new one. Otherwise any code it generates will
// get allocated to the inner node.
if (top[2].start.offset === location.start.offset
&& top[2].end.offset > location.end.offset) {
top[2] = {
start: location.end,
end: top[2].end,
source: top[2].source,
};
}
}
this.sourceMapStack.push([
parts,
parts.length,
location,
]);
}
/**
* @returns {{parts:SourceArray,location:PEG.LocationRange}}
*/
sourceMapPopInternal() {
const elt = this.sourceMapStack.pop();
if (!elt) {
throw new RangeError(
`Rule '${this.ruleName}': Attempting to pop an empty source map stack.\nBytecode: ${this.bytecode}`
);
}
const [
parts,
index,
location,
] = elt;
const chunks = parts.splice(index).map(
chunk => (chunk instanceof SourceNode
? chunk
: chunk + "\n"
)
);
if (chunks.length) {
const start = GrammarLocation.offsetStart(location);
parts.push(new SourceNode(
start.line,
start.column - 1,
String(location.source),
chunks
));
}
return { parts, location };
}
/**
* @param {number} [offset]
* @returns {[SourceArray, number, PEG.LocationRange]|undefined}
*/
sourceMapPop(offset) {
const { location } = this.sourceMapPopInternal();
if (this.sourceMapStack.length
&& location.end.offset
< this.sourceMapStack[this.sourceMapStack.length - 1][2].end.offset) {
const { parts, location: outer } = this.sourceMapPopInternal();
const newLoc = (outer.start.offset < location.end.offset)
? {
start: location.end,
end: outer.end,
source: outer.source,
}
: outer;
this.sourceMapStack.push([
parts,
parts.length + (offset || 0),
newLoc,
]);
}
return undefined;
}
}
module.exports = Stack;

91
resources/app/node_modules/peggy/lib/compiler/utils.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
"use strict";
function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
exports.hex = hex;
function stringEscape(s) {
// ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string
// literal except for the closing quote character, backslash, carriage
// return, line separator, paragraph separator, and line feed. Any character
// may appear in the form of an escape sequence.
//
// For portability, we also escape all control and non-ASCII characters.
return s
.replace(/\\/g, "\\\\") // Backslash
.replace(/"/g, "\\\"") // Closing double quote
.replace(/\0/g, "\\0") // Null
.replace(/\x08/g, "\\b") // Backspace
.replace(/\t/g, "\\t") // Horizontal tab
.replace(/\n/g, "\\n") // Line feed
.replace(/\v/g, "\\v") // Vertical tab
.replace(/\f/g, "\\f") // Form feed
.replace(/\r/g, "\\r") // Carriage return
.replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch))
.replace(/[\x10-\x1F\x7F-\xFF]/g, ch => "\\x" + hex(ch))
.replace(/[\u0100-\u0FFF]/g, ch => "\\u0" + hex(ch))
.replace(/[\u1000-\uFFFF]/g, ch => "\\u" + hex(ch));
}
exports.stringEscape = stringEscape;
function regexpClassEscape(s) {
// Based on ECMA-262, 5th ed., 7.8.5 & 15.10.1.
//
// For portability, we also escape all control and non-ASCII characters.
return s
.replace(/\\/g, "\\\\") // Backslash
.replace(/\//g, "\\/") // Closing slash
.replace(/]/g, "\\]") // Closing bracket
.replace(/\^/g, "\\^") // Caret
.replace(/-/g, "\\-") // Dash
.replace(/\0/g, "\\0") // Null
.replace(/\x08/g, "\\b") // Backspace
.replace(/\t/g, "\\t") // Horizontal tab
.replace(/\n/g, "\\n") // Line feed
.replace(/\v/g, "\\v") // Vertical tab
.replace(/\f/g, "\\f") // Form feed
.replace(/\r/g, "\\r") // Carriage return
.replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch))
.replace(/[\x10-\x1F\x7F-\xFF]/g, ch => "\\x" + hex(ch))
.replace(/[\u0100-\u0FFF]/g, ch => "\\u0" + hex(ch))
.replace(/[\u1000-\uFFFF]/g, ch => "\\u" + hex(ch));
}
exports.regexpClassEscape = regexpClassEscape;
/**
* Base64 encode a Uint8Array. Needed for browser compatibility where
* the Buffer class is not available.
*
* @param {Uint8Array} u8 Bytes to encode
* @returns {string} Base64 encoded string
*/
function base64(u8) {
// Note: btoa has the worst API, and even mentioning Buffer here will
// cause rollup to suck it in.
// See RFC4648, sec. 4.
// https://datatracker.ietf.org/doc/html/rfc4648#section-4
const A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const rem = u8.length % 3;
const len = u8.length - rem;
let res = "";
for (let i = 0; i < len; i += 3) {
res += A[u8[i] >> 2];
res += A[((u8[i] & 0x3) << 4) | (u8[i + 1] >> 4)];
res += A[((u8[i + 1] & 0xf) << 2) | (u8[i + 2] >> 6)];
res += A[u8[i + 2] & 0x3f];
}
if (rem === 1) {
res += A[u8[len] >> 2];
res += A[(u8[len] & 0x3) << 4];
res += "==";
} else if (rem === 2) {
res += A[u8[len] >> 2];
res += A[((u8[len] & 0x3) << 4) | (u8[len + 1] >> 4)];
res += A[(u8[len + 1] & 0xf) << 2];
res += "=";
}
return res;
}
exports.base64 = base64;

View File

@@ -0,0 +1,100 @@
"use strict";
// Simple AST node visitor builder.
const visitor = {
build(functions) {
function visit(node, ...args) {
return functions[node.type](node, ...args);
}
function visitNop() {
// Do nothing.
}
function visitExpression(node, ...args) {
return visit(node.expression, ...args);
}
function visitChildren(property) {
return function(node, ...args) {
// We do not use .map() here, because if you need the result
// of applying visitor to children you probable also need to
// process it in some way, therefore you anyway have to override
// this method. If you do not needed that, we do not waste time
// and memory for creating the output array
node[property].forEach(child => visit(child, ...args));
};
}
const DEFAULT_FUNCTIONS = {
grammar(node, ...args) {
for (const imp of node.imports) {
visit(imp, ...args);
}
if (node.topLevelInitializer) {
if (Array.isArray(node.topLevelInitializer)) {
for (const tli of node.topLevelInitializer) {
visit(tli, ...args);
}
} else {
visit(node.topLevelInitializer, ...args);
}
}
if (node.initializer) {
if (Array.isArray(node.initializer)) {
for (const init of node.initializer) {
visit(init, ...args);
}
} else {
visit(node.initializer, ...args);
}
}
node.rules.forEach(rule => visit(rule, ...args));
},
grammar_import: visitNop,
top_level_initializer: visitNop,
initializer: visitNop,
rule: visitExpression,
named: visitExpression,
choice: visitChildren("alternatives"),
action: visitExpression,
sequence: visitChildren("elements"),
labeled: visitExpression,
text: visitExpression,
simple_and: visitExpression,
simple_not: visitExpression,
optional: visitExpression,
zero_or_more: visitExpression,
one_or_more: visitExpression,
repeated(node, ...args) {
if (node.delimiter) {
visit(node.delimiter, ...args);
}
return visit(node.expression, ...args);
},
group: visitExpression,
semantic_and: visitNop,
semantic_not: visitNop,
rule_ref: visitNop,
library_ref: visitNop,
literal: visitNop,
class: visitNop,
any: visitNop,
};
Object.keys(DEFAULT_FUNCTIONS).forEach(type => {
if (!Object.prototype.hasOwnProperty.call(functions, type)) {
functions[type] = DEFAULT_FUNCTIONS[type];
}
});
return visit;
},
};
module.exports = visitor;

175
resources/app/node_modules/peggy/lib/grammar-error.js generated vendored Normal file
View File

@@ -0,0 +1,175 @@
"use strict";
const GrammarLocation = require("./grammar-location");
// See: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
// This is roughly what typescript generates, it's not called after super(), where it's needed.
// istanbul ignore next This is a special black magic that cannot be covered everywhere
const setProtoOf = Object.setPrototypeOf
|| ({ __proto__: [] } instanceof Array
&& function(d, b) {
// eslint-disable-next-line no-proto -- Backward-compatibility
d.__proto__ = b;
})
|| function(d, b) {
for (const p in b) {
if (Object.prototype.hasOwnProperty.call(b, p)) {
d[p] = b[p];
}
}
};
// Thrown when the grammar contains an error.
/** @type {import("./peg").GrammarError} */
class GrammarError extends Error {
constructor(message, location, diagnostics) {
super(message);
setProtoOf(this, GrammarError.prototype);
this.name = "GrammarError";
this.location = location;
if (diagnostics === undefined) {
diagnostics = [];
}
this.diagnostics = diagnostics;
// All problems if this error is thrown by the plugin and not at stage
// checking phase
this.stage = null;
this.problems = [["error", message, location, diagnostics]];
}
toString() {
let str = super.toString();
if (this.location) {
str += "\n at ";
if ((this.location.source !== undefined)
&& (this.location.source !== null)) {
str += `${this.location.source}:`;
}
str += `${this.location.start.line}:${this.location.start.column}`;
}
for (const diag of this.diagnostics) {
str += "\n from ";
if ((diag.location.source !== undefined)
&& (diag.location.source !== null)) {
str += `${diag.location.source}:`;
}
str += `${diag.location.start.line}:${diag.location.start.column}: ${diag.message}`;
}
return str;
}
/**
* Format the error with associated sources. The `location.source` should have
* a `toString()` representation in order the result to look nice. If source
* is `null` or `undefined`, it is skipped from the output
*
* Sample output:
* ```
* Error: Label "head" is already defined
* --> examples/arithmetics.pegjs:15:17
* |
* 15 | = head:Factor head:(_ ("*" / "/") _ Factor)* {
* | ^^^^
* note: Original label location
* --> examples/arithmetics.pegjs:15:5
* |
* 15 | = head:Factor head:(_ ("*" / "/") _ Factor)* {
* | ^^^^
* ```
*
* @param {import("./peg").SourceText[]} sources mapping from location source to source text
*
* @returns {string} the formatted error
*/
format(sources) {
const srcLines = sources.map(({ source, text }) => ({
source,
text: (text !== null && text !== undefined)
? String(text).split(/\r\n|\n|\r/g)
: [],
}));
/**
* Returns a highlighted piece of source to which the `location` points
*
* @param {import("./peg").LocationRange} location
* @param {number} indent How much width in symbols line number strip should have
* @param {string} message Additional message that will be shown after location
* @returns {string}
*/
function entry(location, indent, message = "") {
let str = "";
const src = srcLines.find(({ source }) => source === location.source);
const s = location.start;
const offset_s = GrammarLocation.offsetStart(location);
if (src) {
const e = location.end;
const line = src.text[s.line - 1];
const last = s.line === e.line ? e.column : line.length + 1;
const hatLen = (last - s.column) || 1;
if (message) {
str += `\nnote: ${message}`;
}
str += `
--> ${location.source}:${offset_s.line}:${offset_s.column}
${"".padEnd(indent)} |
${offset_s.line.toString().padStart(indent)} | ${line}
${"".padEnd(indent)} | ${"".padEnd(s.column - 1)}${"".padEnd(hatLen, "^")}`;
} else {
str += `\n at ${location.source}:${offset_s.line}:${offset_s.column}`;
if (message) {
str += `: ${message}`;
}
}
return str;
}
/**
* Returns a formatted representation of the one problem in the error.
*
* @param {import("./peg").Severity} severity Importance of the message
* @param {string} message Test message of the problem
* @param {import("./peg").LocationRange?} location Location of the problem in the source
* @param {import("./peg").DiagnosticNote[]} diagnostics Additional notes about the problem
* @returns {string}
*/
function formatProblem(severity, message, location, diagnostics = []) {
// Calculate maximum width of all lines
let maxLine = -Infinity;
if (location) {
maxLine = diagnostics.reduce(
(t, { location }) => Math.max(
t, GrammarLocation.offsetStart(location).line
),
location.start.line
);
} else {
maxLine = Math.max.apply(
null,
diagnostics.map(d => d.location.start.line)
);
}
maxLine = maxLine.toString().length;
let str = `${severity}: ${message}`;
if (location) {
str += entry(location, maxLine);
}
for (const diag of diagnostics) {
str += entry(diag.location, maxLine, diag.message);
}
return str;
}
// "info" problems are only appropriate if in verbose mode.
// Handle them separately.
return this.problems
.filter(p => p[0] !== "info")
.map(p => formatProblem(...p)).join("\n\n");
}
}
module.exports = GrammarError;

View File

@@ -0,0 +1,81 @@
"use strict";
/**
* When used as a grammarSource, allows grammars embedded in larger files to
* specify their offset. The start location is the first character in the
* grammar. The first line is often moved to the right by some number of
* columns, but subsequent lines all start at the first column.
*/
class GrammarLocation {
/**
* Create an instance.
*
* @param {any} source The original grammarSource. Should be a string or
* have a toString() method.
* @param {import("./peg").Location} start The starting offset for the
* grammar in the larger file.
*/
constructor(source, start) {
this.source = source;
this.start = start;
}
/**
* Coerce to a string.
*
* @returns {string} The source, stringified.
*/
toString() {
return String(this.source);
}
/**
* Return a new Location offset from the given location by the start of the
* grammar.
*
* @param {import("./peg").Location} loc The location as if the start of the
* grammar was the start of the file.
* @returns {import("./peg").Location} The offset location.
*/
offset(loc) {
return {
line: loc.line + this.start.line - 1,
column: (loc.line === 1)
? loc.column + this.start.column - 1
: loc.column,
offset: loc.offset + this.start.offset,
};
}
/**
* If the range has a grammarSource that is a GrammarLocation, offset the
* start of that range by the GrammarLocation.
*
* @param {import("./peg").LocationRange} range The range to extract from.
* @returns {import("./peg").Location} The offset start if possible, or the
* original start.
*/
static offsetStart(range) {
if (range.source && (typeof range.source.offset === "function")) {
return range.source.offset(range.start);
}
return range.start;
}
/**
* If the range has a grammarSource that is a GrammarLocation, offset the
* end of that range by the GrammarLocation.
*
* @param {import("./peg").LocationRange} range The range to extract from.
* @returns {import("./peg").Location} The offset end if possible, or the
* original end.
*/
static offsetEnd(range) {
if (range.source && (typeof range.source.offset === "function")) {
return range.source.offset(range.end);
}
return range.end;
}
}
module.exports = GrammarLocation;

4117
resources/app/node_modules/peggy/lib/parser.js generated vendored Normal file

File diff suppressed because one or more lines are too long

172
resources/app/node_modules/peggy/lib/peg.js generated vendored Normal file
View File

@@ -0,0 +1,172 @@
"use strict";
const GrammarError = require("./grammar-error");
const GrammarLocation = require("./grammar-location");
const asts = require("./compiler/asts.js");
const compiler = require("./compiler");
const parser = require("./parser");
const VERSION = require("./version");
const RESERVED_WORDS = [
// Reserved keywords as of ECMAScript 2015
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
// Special constants
"null",
"true",
"false",
// These are always reserved:
"enum",
// The following are only reserved when they are found in strict mode code
// Peggy generates code in strict mode, so they are applicable
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
// The following are only reserved when they are found in module code:
"await",
// The following are reserved as future keywords by ECMAScript 1..3
// specifications, but not any more in modern ECMAScript. We don't need these
// because the code-generation of Peggy only targets ECMAScript >= 5.
//
// - abstract
// - boolean
// - byte
// - char
// - double
// - final
// - float
// - goto
// - int
// - long
// - native
// - short
// - synchronized
// - throws
// - transient
// - volatile
// These are not reserved keywords, but using them as variable names is problematic.
"arguments", // Conflicts with a special variable available inside functions.
"eval", // Redeclaring eval() is prohibited in strict mode
// A few identifiers have a special meaning in some contexts without being
// reserved words of any kind. These we don't need to worry about as they can
// all be safely used as variable names.
//
// - as
// - async
// - from
// - get
// - of
// - set
];
const peg = {
// Peggy version (filled in by /tools/release).
VERSION,
/**
* Default list of reserved words. Contains list of currently and future
* JavaScript (ECMAScript 2015) reserved words.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words
*/
RESERVED_WORDS,
GrammarError,
GrammarLocation,
parser,
compiler,
// Generates a parser from a specified grammar and returns it.
//
// The grammar must be a string in the format described by the meta-grammar in
// the parser.pegjs file.
//
// Throws |peg.parser.SyntaxError| if the grammar contains a syntax error or
// |peg.GrammarError| if it contains a semantic error. Note that not all
// errors are detected during the generation and some may protrude to the
// generated parser and cause its malfunction.
generate(grammar, options) {
options = options !== undefined ? options : {};
function copyPasses(passes) {
const converted = {};
Object.keys(passes).forEach(stage => {
converted[stage] = passes[stage].slice();
});
return converted;
}
const plugins = "plugins" in options ? options.plugins : [];
const config = {
parser: peg.parser,
passes: copyPasses(peg.compiler.passes),
reservedWords: peg.RESERVED_WORDS.slice(),
};
plugins.forEach(p => { p.use(config, options); });
if (!Array.isArray(grammar)) {
grammar = [{
source: options.grammarSource,
text: grammar,
}];
}
const combined = asts.combine(
grammar.map(({ source, text }) => config.parser.parse(text, {
grammarSource: source,
reservedWords: config.reservedWords,
}))
);
return peg.compiler.compile(
combined,
config.passes,
options
);
},
};
module.exports = peg;

9
resources/app/node_modules/peggy/lib/version.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// This file is generated.
// Do not edit it! Your work will be overwritten.
//
// Instead, please look at ./tools/set_version.js
"use strict";
module.exports = "4.0.2";

View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,16 @@
import commander from './index.js';
// wrapper to provide named exports for ESM.
export const {
program,
createCommand,
createArgument,
createOption,
CommanderError,
InvalidArgumentError,
InvalidOptionArgumentError, // deprecated old name
Command,
Argument,
Option,
Help,
} = commander;

View File

@@ -0,0 +1,24 @@
const { Argument } = require('./lib/argument.js');
const { Command } = require('./lib/command.js');
const { CommanderError, InvalidArgumentError } = require('./lib/error.js');
const { Help } = require('./lib/help.js');
const { Option } = require('./lib/option.js');
exports.program = new Command();
exports.createCommand = (name) => new Command(name);
exports.createOption = (flags, description) => new Option(flags, description);
exports.createArgument = (name, description) => new Argument(name, description);
/**
* Expose classes
*/
exports.Command = Command;
exports.Option = Option;
exports.Argument = Argument;
exports.Help = Help;
exports.CommanderError = CommanderError;
exports.InvalidArgumentError = InvalidArgumentError;
exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated

View File

@@ -0,0 +1,149 @@
const { InvalidArgumentError } = require('./error.js');
class Argument {
/**
* Initialize a new command argument with the given name and description.
* The default is that the argument is required, and you can explicitly
* indicate this with <> around the name. Put [] around the name for an optional argument.
*
* @param {string} name
* @param {string} [description]
*/
constructor(name, description) {
this.description = description || '';
this.variadic = false;
this.parseArg = undefined;
this.defaultValue = undefined;
this.defaultValueDescription = undefined;
this.argChoices = undefined;
switch (name[0]) {
case '<': // e.g. <required>
this.required = true;
this._name = name.slice(1, -1);
break;
case '[': // e.g. [optional]
this.required = false;
this._name = name.slice(1, -1);
break;
default:
this.required = true;
this._name = name;
break;
}
if (this._name.length > 3 && this._name.slice(-3) === '...') {
this.variadic = true;
this._name = this._name.slice(0, -3);
}
}
/**
* Return argument name.
*
* @return {string}
*/
name() {
return this._name;
}
/**
* @package
*/
_concatValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [value];
}
return previous.concat(value);
}
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {*} value
* @param {string} [description]
* @return {Argument}
*/
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
/**
* Set the custom handler for processing CLI command arguments into argument values.
*
* @param {Function} [fn]
* @return {Argument}
*/
argParser(fn) {
this.parseArg = fn;
return this;
}
/**
* Only allow argument value to be one of choices.
*
* @param {string[]} values
* @return {Argument}
*/
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError(
`Allowed choices are ${this.argChoices.join(', ')}.`,
);
}
if (this.variadic) {
return this._concatValue(arg, previous);
}
return arg;
};
return this;
}
/**
* Make argument required.
*
* @returns {Argument}
*/
argRequired() {
this.required = true;
return this;
}
/**
* Make argument optional.
*
* @returns {Argument}
*/
argOptional() {
this.required = false;
return this;
}
}
/**
* Takes an argument and returns its human readable equivalent for help usage.
*
* @param {Argument} arg
* @return {string}
* @private
*/
function humanReadableArgName(arg) {
const nameOutput = arg.name() + (arg.variadic === true ? '...' : '');
return arg.required ? '<' + nameOutput + '>' : '[' + nameOutput + ']';
}
exports.Argument = Argument;
exports.humanReadableArgName = humanReadableArgName;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/**
* CommanderError class
*/
class CommanderError extends Error {
/**
* Constructs the CommanderError class
* @param {number} exitCode suggested exit code which could be used with process.exit
* @param {string} code an id string representing the error
* @param {string} message human-readable description of the error
*/
constructor(exitCode, code, message) {
super(message);
// properly capture stack trace in Node.js
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.code = code;
this.exitCode = exitCode;
this.nestedError = undefined;
}
}
/**
* InvalidArgumentError class
*/
class InvalidArgumentError extends CommanderError {
/**
* Constructs the InvalidArgumentError class
* @param {string} [message] explanation of why argument is invalid
*/
constructor(message) {
super(1, 'commander.invalidArgument', message);
// properly capture stack trace in Node.js
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
}
}
exports.CommanderError = CommanderError;
exports.InvalidArgumentError = InvalidArgumentError;

View File

@@ -0,0 +1,520 @@
const { humanReadableArgName } = require('./argument.js');
/**
* TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
* https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
* @typedef { import("./argument.js").Argument } Argument
* @typedef { import("./command.js").Command } Command
* @typedef { import("./option.js").Option } Option
*/
// Although this is a class, methods are static in style to allow override using subclass or just functions.
class Help {
constructor() {
this.helpWidth = undefined;
this.sortSubcommands = false;
this.sortOptions = false;
this.showGlobalOptions = false;
}
/**
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
*
* @param {Command} cmd
* @returns {Command[]}
*/
visibleCommands(cmd) {
const visibleCommands = cmd.commands.filter((cmd) => !cmd._hidden);
const helpCommand = cmd._getHelpCommand();
if (helpCommand && !helpCommand._hidden) {
visibleCommands.push(helpCommand);
}
if (this.sortSubcommands) {
visibleCommands.sort((a, b) => {
// @ts-ignore: because overloaded return type
return a.name().localeCompare(b.name());
});
}
return visibleCommands;
}
/**
* Compare options for sort.
*
* @param {Option} a
* @param {Option} b
* @returns {number}
*/
compareOptions(a, b) {
const getSortKey = (option) => {
// WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
return option.short
? option.short.replace(/^-/, '')
: option.long.replace(/^--/, '');
};
return getSortKey(a).localeCompare(getSortKey(b));
}
/**
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleOptions(cmd) {
const visibleOptions = cmd.options.filter((option) => !option.hidden);
// Built-in help option.
const helpOption = cmd._getHelpOption();
if (helpOption && !helpOption.hidden) {
// Automatically hide conflicting flags. Bit dubious but a historical behaviour that is convenient for single-command programs.
const removeShort = helpOption.short && cmd._findOption(helpOption.short);
const removeLong = helpOption.long && cmd._findOption(helpOption.long);
if (!removeShort && !removeLong) {
visibleOptions.push(helpOption); // no changes needed
} else if (helpOption.long && !removeLong) {
visibleOptions.push(
cmd.createOption(helpOption.long, helpOption.description),
);
} else if (helpOption.short && !removeShort) {
visibleOptions.push(
cmd.createOption(helpOption.short, helpOption.description),
);
}
}
if (this.sortOptions) {
visibleOptions.sort(this.compareOptions);
}
return visibleOptions;
}
/**
* Get an array of the visible global options. (Not including help.)
*
* @param {Command} cmd
* @returns {Option[]}
*/
visibleGlobalOptions(cmd) {
if (!this.showGlobalOptions) return [];
const globalOptions = [];
for (
let ancestorCmd = cmd.parent;
ancestorCmd;
ancestorCmd = ancestorCmd.parent
) {
const visibleOptions = ancestorCmd.options.filter(
(option) => !option.hidden,
);
globalOptions.push(...visibleOptions);
}
if (this.sortOptions) {
globalOptions.sort(this.compareOptions);
}
return globalOptions;
}
/**
* Get an array of the arguments if any have a description.
*
* @param {Command} cmd
* @returns {Argument[]}
*/
visibleArguments(cmd) {
// Side effect! Apply the legacy descriptions before the arguments are displayed.
if (cmd._argsDescription) {
cmd.registeredArguments.forEach((argument) => {
argument.description =
argument.description || cmd._argsDescription[argument.name()] || '';
});
}
// If there are any arguments with a description then return all the arguments.
if (cmd.registeredArguments.find((argument) => argument.description)) {
return cmd.registeredArguments;
}
return [];
}
/**
* Get the command term to show in the list of subcommands.
*
* @param {Command} cmd
* @returns {string}
*/
subcommandTerm(cmd) {
// Legacy. Ignores custom usage string, and nested commands.
const args = cmd.registeredArguments
.map((arg) => humanReadableArgName(arg))
.join(' ');
return (
cmd._name +
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
(cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
(args ? ' ' + args : '')
);
}
/**
* Get the option term to show in the list of options.
*
* @param {Option} option
* @returns {string}
*/
optionTerm(option) {
return option.flags;
}
/**
* Get the argument term to show in the list of arguments.
*
* @param {Argument} argument
* @returns {string}
*/
argumentTerm(argument) {
return argument.name();
}
/**
* Get the longest command term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestSubcommandTermLength(cmd, helper) {
return helper.visibleCommands(cmd).reduce((max, command) => {
return Math.max(max, helper.subcommandTerm(command).length);
}, 0);
}
/**
* Get the longest option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestOptionTermLength(cmd, helper) {
return helper.visibleOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}
/**
* Get the longest global option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestGlobalOptionTermLength(cmd, helper) {
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}
/**
* Get the longest argument term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
longestArgumentTermLength(cmd, helper) {
return helper.visibleArguments(cmd).reduce((max, argument) => {
return Math.max(max, helper.argumentTerm(argument).length);
}, 0);
}
/**
* Get the command usage to be displayed at the top of the built-in help.
*
* @param {Command} cmd
* @returns {string}
*/
commandUsage(cmd) {
// Usage
let cmdName = cmd._name;
if (cmd._aliases[0]) {
cmdName = cmdName + '|' + cmd._aliases[0];
}
let ancestorCmdNames = '';
for (
let ancestorCmd = cmd.parent;
ancestorCmd;
ancestorCmd = ancestorCmd.parent
) {
ancestorCmdNames = ancestorCmd.name() + ' ' + ancestorCmdNames;
}
return ancestorCmdNames + cmdName + ' ' + cmd.usage();
}
/**
* Get the description for the command.
*
* @param {Command} cmd
* @returns {string}
*/
commandDescription(cmd) {
// @ts-ignore: because overloaded return type
return cmd.description();
}
/**
* Get the subcommand summary to show in the list of subcommands.
* (Fallback to description for backwards compatibility.)
*
* @param {Command} cmd
* @returns {string}
*/
subcommandDescription(cmd) {
// @ts-ignore: because overloaded return type
return cmd.summary() || cmd.description();
}
/**
* Get the option description to show in the list of options.
*
* @param {Option} option
* @return {string}
*/
optionDescription(option) {
const extraInfo = [];
if (option.argChoices) {
extraInfo.push(
// use stringify to match the display of the default value
`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`,
);
}
if (option.defaultValue !== undefined) {
// default for boolean and negated more for programmer than end user,
// but show true/false for boolean option as may be for hand-rolled env or config processing.
const showDefault =
option.required ||
option.optional ||
(option.isBoolean() && typeof option.defaultValue === 'boolean');
if (showDefault) {
extraInfo.push(
`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`,
);
}
}
// preset for boolean and negated are more for programmer than end user
if (option.presetArg !== undefined && option.optional) {
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
}
if (option.envVar !== undefined) {
extraInfo.push(`env: ${option.envVar}`);
}
if (extraInfo.length > 0) {
return `${option.description} (${extraInfo.join(', ')})`;
}
return option.description;
}
/**
* Get the argument description to show in the list of arguments.
*
* @param {Argument} argument
* @return {string}
*/
argumentDescription(argument) {
const extraInfo = [];
if (argument.argChoices) {
extraInfo.push(
// use stringify to match the display of the default value
`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`,
);
}
if (argument.defaultValue !== undefined) {
extraInfo.push(
`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`,
);
}
if (extraInfo.length > 0) {
const extraDescripton = `(${extraInfo.join(', ')})`;
if (argument.description) {
return `${argument.description} ${extraDescripton}`;
}
return extraDescripton;
}
return argument.description;
}
/**
* Generate the built-in help text.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {string}
*/
formatHelp(cmd, helper) {
const termWidth = helper.padWidth(cmd, helper);
const helpWidth = helper.helpWidth || 80;
const itemIndentWidth = 2;
const itemSeparatorWidth = 2; // between term and description
function formatItem(term, description) {
if (description) {
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
return helper.wrap(
fullText,
helpWidth - itemIndentWidth,
termWidth + itemSeparatorWidth,
);
}
return term;
}
function formatList(textArray) {
return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
}
// Usage
let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
// Description
const commandDescription = helper.commandDescription(cmd);
if (commandDescription.length > 0) {
output = output.concat([
helper.wrap(commandDescription, helpWidth, 0),
'',
]);
}
// Arguments
const argumentList = helper.visibleArguments(cmd).map((argument) => {
return formatItem(
helper.argumentTerm(argument),
helper.argumentDescription(argument),
);
});
if (argumentList.length > 0) {
output = output.concat(['Arguments:', formatList(argumentList), '']);
}
// Options
const optionList = helper.visibleOptions(cmd).map((option) => {
return formatItem(
helper.optionTerm(option),
helper.optionDescription(option),
);
});
if (optionList.length > 0) {
output = output.concat(['Options:', formatList(optionList), '']);
}
if (this.showGlobalOptions) {
const globalOptionList = helper
.visibleGlobalOptions(cmd)
.map((option) => {
return formatItem(
helper.optionTerm(option),
helper.optionDescription(option),
);
});
if (globalOptionList.length > 0) {
output = output.concat([
'Global Options:',
formatList(globalOptionList),
'',
]);
}
}
// Commands
const commandList = helper.visibleCommands(cmd).map((cmd) => {
return formatItem(
helper.subcommandTerm(cmd),
helper.subcommandDescription(cmd),
);
});
if (commandList.length > 0) {
output = output.concat(['Commands:', formatList(commandList), '']);
}
return output.join('\n');
}
/**
* Calculate the pad width from the maximum term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/
padWidth(cmd, helper) {
return Math.max(
helper.longestOptionTermLength(cmd, helper),
helper.longestGlobalOptionTermLength(cmd, helper),
helper.longestSubcommandTermLength(cmd, helper),
helper.longestArgumentTermLength(cmd, helper),
);
}
/**
* Wrap the given string to width characters per line, with lines after the first indented.
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
*
* @param {string} str
* @param {number} width
* @param {number} indent
* @param {number} [minColumnWidth=40]
* @return {string}
*
*/
wrap(str, width, indent, minColumnWidth = 40) {
// Full \s characters, minus the linefeeds.
const indents =
' \\f\\t\\v\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff';
// Detect manually wrapped and indented strings by searching for line break followed by spaces.
const manualIndent = new RegExp(`[\\n][${indents}]+`);
if (str.match(manualIndent)) return str;
// Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
const columnWidth = width - indent;
if (columnWidth < minColumnWidth) return str;
const leadingStr = str.slice(0, indent);
const columnText = str.slice(indent).replace('\r\n', '\n');
const indentString = ' '.repeat(indent);
const zeroWidthSpace = '\u200B';
const breaks = `\\s${zeroWidthSpace}`;
// Match line end (so empty lines don't collapse),
// or as much text as will fit in column, or excess text up to first break.
const regex = new RegExp(
`\n|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`,
'g',
);
const lines = columnText.match(regex) || [];
return (
leadingStr +
lines
.map((line, i) => {
if (line === '\n') return ''; // preserve empty lines
return (i > 0 ? indentString : '') + line.trimEnd();
})
.join('\n')
);
}
}
exports.Help = Help;

View File

@@ -0,0 +1,330 @@
const { InvalidArgumentError } = require('./error.js');
class Option {
/**
* Initialize a new `Option` with the given `flags` and `description`.
*
* @param {string} flags
* @param {string} [description]
*/
constructor(flags, description) {
this.flags = flags;
this.description = description || '';
this.required = flags.includes('<'); // A value must be supplied when the option is specified.
this.optional = flags.includes('['); // A value is optional when the option is specified.
// variadic test ignores <value,...> et al which might be used to describe custom splitting of single argument
this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values.
this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line.
const optionFlags = splitOptionFlags(flags);
this.short = optionFlags.shortFlag;
this.long = optionFlags.longFlag;
this.negate = false;
if (this.long) {
this.negate = this.long.startsWith('--no-');
}
this.defaultValue = undefined;
this.defaultValueDescription = undefined;
this.presetArg = undefined;
this.envVar = undefined;
this.parseArg = undefined;
this.hidden = false;
this.argChoices = undefined;
this.conflictsWith = [];
this.implied = undefined;
}
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {*} value
* @param {string} [description]
* @return {Option}
*/
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
/**
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
* The custom processing (parseArg) is called.
*
* @example
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
*
* @param {*} arg
* @return {Option}
*/
preset(arg) {
this.presetArg = arg;
return this;
}
/**
* Add option name(s) that conflict with this option.
* An error will be displayed if conflicting options are found during parsing.
*
* @example
* new Option('--rgb').conflicts('cmyk');
* new Option('--js').conflicts(['ts', 'jsx']);
*
* @param {(string | string[])} names
* @return {Option}
*/
conflicts(names) {
this.conflictsWith = this.conflictsWith.concat(names);
return this;
}
/**
* Specify implied option values for when this option is set and the implied options are not.
*
* The custom processing (parseArg) is not called on the implied values.
*
* @example
* program
* .addOption(new Option('--log', 'write logging information to file'))
* .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
*
* @param {object} impliedOptionValues
* @return {Option}
*/
implies(impliedOptionValues) {
let newImplied = impliedOptionValues;
if (typeof impliedOptionValues === 'string') {
// string is not documented, but easy mistake and we can do what user probably intended.
newImplied = { [impliedOptionValues]: true };
}
this.implied = Object.assign(this.implied || {}, newImplied);
return this;
}
/**
* Set environment variable to check for option value.
*
* An environment variable is only used if when processed the current option value is
* undefined, or the source of the current value is 'default' or 'config' or 'env'.
*
* @param {string} name
* @return {Option}
*/
env(name) {
this.envVar = name;
return this;
}
/**
* Set the custom handler for processing CLI option arguments into option values.
*
* @param {Function} [fn]
* @return {Option}
*/
argParser(fn) {
this.parseArg = fn;
return this;
}
/**
* Whether the option is mandatory and must have a value after parsing.
*
* @param {boolean} [mandatory=true]
* @return {Option}
*/
makeOptionMandatory(mandatory = true) {
this.mandatory = !!mandatory;
return this;
}
/**
* Hide option in help.
*
* @param {boolean} [hide=true]
* @return {Option}
*/
hideHelp(hide = true) {
this.hidden = !!hide;
return this;
}
/**
* @package
*/
_concatValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [value];
}
return previous.concat(value);
}
/**
* Only allow option value to be one of choices.
*
* @param {string[]} values
* @return {Option}
*/
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError(
`Allowed choices are ${this.argChoices.join(', ')}.`,
);
}
if (this.variadic) {
return this._concatValue(arg, previous);
}
return arg;
};
return this;
}
/**
* Return option name.
*
* @return {string}
*/
name() {
if (this.long) {
return this.long.replace(/^--/, '');
}
return this.short.replace(/^-/, '');
}
/**
* Return option name, in a camelcase format that can be used
* as a object attribute key.
*
* @return {string}
*/
attributeName() {
return camelcase(this.name().replace(/^no-/, ''));
}
/**
* Check if `arg` matches the short or long flag.
*
* @param {string} arg
* @return {boolean}
* @package
*/
is(arg) {
return this.short === arg || this.long === arg;
}
/**
* Return whether a boolean option.
*
* Options are one of boolean, negated, required argument, or optional argument.
*
* @return {boolean}
* @package
*/
isBoolean() {
return !this.required && !this.optional && !this.negate;
}
}
/**
* This class is to make it easier to work with dual options, without changing the existing
* implementation. We support separate dual options for separate positive and negative options,
* like `--build` and `--no-build`, which share a single option value. This works nicely for some
* use cases, but is tricky for others where we want separate behaviours despite
* the single shared option value.
*/
class DualOptions {
/**
* @param {Option[]} options
*/
constructor(options) {
this.positiveOptions = new Map();
this.negativeOptions = new Map();
this.dualOptions = new Set();
options.forEach((option) => {
if (option.negate) {
this.negativeOptions.set(option.attributeName(), option);
} else {
this.positiveOptions.set(option.attributeName(), option);
}
});
this.negativeOptions.forEach((value, key) => {
if (this.positiveOptions.has(key)) {
this.dualOptions.add(key);
}
});
}
/**
* Did the value come from the option, and not from possible matching dual option?
*
* @param {*} value
* @param {Option} option
* @returns {boolean}
*/
valueFromOption(value, option) {
const optionKey = option.attributeName();
if (!this.dualOptions.has(optionKey)) return true;
// Use the value to deduce if (probably) came from the option.
const preset = this.negativeOptions.get(optionKey).presetArg;
const negativeValue = preset !== undefined ? preset : false;
return option.negate === (negativeValue === value);
}
}
/**
* Convert string from kebab-case to camelCase.
*
* @param {string} str
* @return {string}
* @private
*/
function camelcase(str) {
return str.split('-').reduce((str, word) => {
return str + word[0].toUpperCase() + word.slice(1);
});
}
/**
* Split the short and long flag out of something like '-m,--mixed <value>'
*
* @private
*/
function splitOptionFlags(flags) {
let shortFlag;
let longFlag;
// Use original very loose parsing to maintain backwards compatibility for now,
// which allowed for example unintended `-sw, --short-word` [sic].
const flagParts = flags.split(/[ |,]+/);
if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1]))
shortFlag = flagParts.shift();
longFlag = flagParts.shift();
// Add support for lone short flag without significantly changing parsing!
if (!shortFlag && /^-[^-]$/.test(longFlag)) {
shortFlag = longFlag;
longFlag = undefined;
}
return { shortFlag, longFlag };
}
exports.Option = Option;
exports.DualOptions = DualOptions;

View File

@@ -0,0 +1,101 @@
const maxDistance = 3;
function editDistance(a, b) {
// https://en.wikipedia.org/wiki/DamerauLevenshtein_distance
// Calculating optimal string alignment distance, no substring is edited more than once.
// (Simple implementation.)
// Quick early exit, return worst case.
if (Math.abs(a.length - b.length) > maxDistance)
return Math.max(a.length, b.length);
// distance between prefix substrings of a and b
const d = [];
// pure deletions turn a into empty string
for (let i = 0; i <= a.length; i++) {
d[i] = [i];
}
// pure insertions turn empty string into b
for (let j = 0; j <= b.length; j++) {
d[0][j] = j;
}
// fill matrix
for (let j = 1; j <= b.length; j++) {
for (let i = 1; i <= a.length; i++) {
let cost = 1;
if (a[i - 1] === b[j - 1]) {
cost = 0;
} else {
cost = 1;
}
d[i][j] = Math.min(
d[i - 1][j] + 1, // deletion
d[i][j - 1] + 1, // insertion
d[i - 1][j - 1] + cost, // substitution
);
// transposition
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
}
}
}
return d[a.length][b.length];
}
/**
* Find close matches, restricted to same number of edits.
*
* @param {string} word
* @param {string[]} candidates
* @returns {string}
*/
function suggestSimilar(word, candidates) {
if (!candidates || candidates.length === 0) return '';
// remove possible duplicates
candidates = Array.from(new Set(candidates));
const searchingOptions = word.startsWith('--');
if (searchingOptions) {
word = word.slice(2);
candidates = candidates.map((candidate) => candidate.slice(2));
}
let similar = [];
let bestDistance = maxDistance;
const minSimilarity = 0.4;
candidates.forEach((candidate) => {
if (candidate.length <= 1) return; // no one character guesses
const distance = editDistance(word, candidate);
const length = Math.max(word.length, candidate.length);
const similarity = (length - distance) / length;
if (similarity > minSimilarity) {
if (distance < bestDistance) {
// better edit distance, throw away previous worse matches
bestDistance = distance;
similar = [candidate];
} else if (distance === bestDistance) {
similar.push(candidate);
}
}
});
similar.sort((a, b) => a.localeCompare(b));
if (searchingOptions) {
similar = similar.map((candidate) => `--${candidate}`);
}
if (similar.length > 1) {
return `\n(Did you mean one of ${similar.join(', ')}?)`;
}
if (similar.length === 1) {
return `\n(Did you mean ${similar[0]}?)`;
}
return '';
}
exports.suggestSimilar = suggestSimilar;

View File

@@ -0,0 +1,16 @@
{
"versions": [
{
"version": "*",
"target": {
"node": "supported"
},
"response": {
"type": "time-permitting"
},
"backing": {
"npm-funding": true
}
}
]
}

View File

@@ -0,0 +1,60 @@
{
"name": "commander",
"version": "12.1.0",
"description": "the complete solution for node.js command-line programs",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/tj/commander.js.git"
},
"files": [
"index.js",
"lib/*.js",
"esm.mjs",
"typings/index.d.ts",
"typings/esm.d.mts",
"package-support.json"
],
"type": "commonjs",
"main": "./index.js",
"exports": {
".": {
"require": {
"types": "./typings/index.d.ts",
"default": "./index.js"
},
"import": {
"types": "./typings/esm.d.mts",
"default": "./esm.mjs"
},
"default": "./index.js"
},
"./esm.mjs": {
"types": "./typings/esm.d.mts",
"import": "./esm.mjs"
}
},
"devDependencies": {
"@eslint/js": "^8.56.0",
"@types/jest": "^29.2.4",
"@types/node": "^20.2.5",
"eslint": "^8.30.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.3.0",
"eslint-plugin-jsdoc": "^48.1.0",
"globals": "^13.24.0",
"jest": "^29.3.1",
"prettier": "^3.2.5",
"prettier-plugin-jsdoc": "^1.3.0",
"ts-jest": "^29.0.3",
"tsd": "^0.31.0",
"typescript": "^5.0.4",
"typescript-eslint": "^7.0.1"
},
"types": "typings/index.d.ts",
"engines": {
"node": ">=18"
},
"support": true
}

View File

@@ -0,0 +1,3 @@
// Just reexport the types from cjs
// This is a bit indirect. There is not an index.js, but TypeScript will look for index.d.ts for types.
export * from './index.js';

58
resources/app/node_modules/peggy/package.json generated vendored Normal file
View File

@@ -0,0 +1,58 @@
{
"name": "peggy",
"version": "4.0.2",
"description": "Parser generator for JavaScript",
"homepage": "https://peggyjs.org/",
"repository": "peggyjs/peggy",
"license": "MIT",
"author": "David Majda <david@majda.cz> (https://majda.cz/)",
"main": "lib/peg.js",
"browser": "browser/peggy.min.js",
"unpkg": "browser/peggy.min.js",
"jsdelivr": "browser/peggy.min.js",
"types": "lib/peg.d.ts",
"bin": {
"peggy": "bin/peggy.js"
},
"devDependencies": {
"@peggyjs/eslint-config": "^3.2.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-multi-entry": "^6.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@types/chai": "^4.3.11",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.20",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"chai": "^4.3.11",
"chai-like": "^1.1.1",
"copyfiles": "^2.4.1",
"eslint": "^8.57.0",
"eslint-plugin-compat": "4.2.0",
"eslint-plugin-mocha": "10.3.0",
"express": "4.18.2",
"glob": "^10.3.10",
"jest": "^29.7.0",
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"rollup-plugin-ignore": "1.0.10",
"source-map": "^0.8.0-beta.0",
"terser": "^5.28.1",
"ts-jest": "^29.1.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3"
},
"dependencies": {
"@peggyjs/from-mem": "1.2.1",
"commander": "^12.0.0",
"source-map-generator": "0.8.0"
},
"browserslist": [
"defaults, maintained node versions, not op_mini all"
],
"engines": {
"node": ">=18"
}
}