Compare commits

..

No commits in common. "c669d60428f5e7e3616a99da8d1a02102182562e" and "01e4eaca5eac6fd00a86d7dfc5553e496e7fa4bc" have entirely different histories.

11 changed files with 25 additions and 121 deletions

View File

@ -1,12 +0,0 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current'
}
}
]
]
};

3
babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: [[ '@babel/preset-env', { targets: { node: 'current' } }]]
};

View File

@ -1,6 +1,6 @@
{ {
"name": "@navy.gif/commandparser", "name": "@navy.gif/commandparser",
"version": "1.4.4", "version": "1.4.3",
"description": "Parser meant to parse commands and their options for discord bots", "description": "Parser meant to parse commands and their options for discord bots",
"author": "Navy.gif", "author": "Navy.gif",
"license": "MIT", "license": "MIT",
@ -34,8 +34,8 @@
}, },
"scripts": { "scripts": {
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js", "build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js",
"test": "yarn build && jest", "test": "tsc && jest",
"release": "yarn build && yarn publish", "release": "tsc && yarn publish",
"lint": "eslint --fix" "lint": "eslint --fix"
} }
} }

View File

@ -63,6 +63,7 @@ class Parser extends EventEmitter {
return command || null; return command || null;
} }
/** /**
* *
* *
@ -160,12 +161,9 @@ class Parser extends EventEmitter {
continue; continue;
} }
const short = match.groups.flag.startsWith('-') && !match.groups.flag.startsWith('--');
const _flag = match.groups.flag.replace(/--?/u, '').toLowerCase(); const _flag = match.groups.flag.replace(/--?/u, '').toLowerCase();
let aliased = false; let aliased = false;
const flag = flags.find((f) => { const flag = flags.find((f) => {
if (short)
return f.name[0] === _flag;
aliased = f.valueAsAlias && f.choices.some((c) => c === _flag); aliased = f.valueAsAlias && f.choices.some((c) => c === _flag);
return f.name === _flag || aliased; return f.name === _flag || aliased;
}); });
@ -194,7 +192,7 @@ class Parser extends EventEmitter {
if (flag.choices.length) { if (flag.choices.length) {
throw new ParseError(`Invalid choice for ${flag.name}, Valid choices are ${flag.choices.join(', ')}.`); throw new ParseError(`Invalid choice for ${flag.name}, Valid choices are ${flag.choices.join(', ')}.`);
} }
throw new ParseError(`Failed to parse value for ${flag.name}, expected value type: ${OptionType[flag.type]}`); // return { option: flag, ...result.removed }; throw new ParseError(`Failed to parse value for ${flag.name}, expected value type: ${flag.type}`); // return { option: flag, ...result.removed };
} }
this.debug(`Cleaning up params after ${flag.name}`); this.debug(`Cleaning up params after ${flag.name}`);
for (const r of result.removed) for (const r of result.removed)

View File

@ -22,26 +22,15 @@ abstract class Command implements ICommand {
this.aliases = def.aliases || []; this.aliases = def.aliases || [];
// Add a built-in help flag to any non-subcommand option this.options = [];
this.options = [
new CommandOption({
name: 'help',
type: OptionType.BOOLEAN,
flag: true,
valueOptional: true,
defaultValue: true
})
];
for (const opt of def.options || []) { for (const opt of def.options || []) {
if (opt instanceof CommandOption) if (opt instanceof CommandOption)
this.options.push(opt); this.options.push(opt);
else else
this.options.push(new CommandOption(opt)); this.options.push(new CommandOption(opt));
} }
if (this.options.some(opt => [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type)) if (this.options.some(opt => [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type))
&& this.options.some(opt => ![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type) && opt.name !== 'help')) && this.options.some(opt => ![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type)))
throw new Error('Cannot have subcommand(group)s on the same level as an option'); throw new Error('Cannot have subcommand(group)s on the same level as an option');
} }

View File

@ -2,7 +2,6 @@
import ICommandOption, { Choice, CommandOptionDefinition, DependsOnMode, OptionType, ParseResult } from "../interfaces/CommandOption.js"; import ICommandOption, { Choice, CommandOptionDefinition, DependsOnMode, OptionType, ParseResult } from "../interfaces/CommandOption.js";
import IResolver from "../interfaces/Resolver.js"; import IResolver from "../interfaces/Resolver.js";
const SUB = [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ];
class CommandOption implements ICommandOption { class CommandOption implements ICommandOption {
[key: string]: unknown; [key: string]: unknown;
@ -37,18 +36,7 @@ class CommandOption implements ICommandOption {
this.flag = def.flag || false; this.flag = def.flag || false;
this.options = []; this.options = [];
if (SUB.includes(this.type))
this.options.push(new CommandOption({
name: 'help',
type: OptionType.BOOLEAN,
flag: true,
valueOptional: true,
defaultValue: true
}));
for (const opt of def.options || []) { for (const opt of def.options || []) {
if (this.options.some(o => o.name === opt.name))
throw new Error(`An option by the name ${opt.name} already exists`);
if (opt instanceof CommandOption) if (opt instanceof CommandOption)
this.options.push(opt); this.options.push(opt);
else else
@ -61,17 +49,12 @@ class CommandOption implements ICommandOption {
throw new Error('Cannot have subcommand groups under a subcommand'); throw new Error('Cannot have subcommand groups under a subcommand');
if (this.type === OptionType.SUB_COMMAND && this.options.some(opt => opt.type === OptionType.SUB_COMMAND)) if (this.type === OptionType.SUB_COMMAND && this.options.some(opt => opt.type === OptionType.SUB_COMMAND))
throw new Error('Cannot nest subcommands'); throw new Error('Cannot nest subcommands');
if (!SUB.includes(this.type) if (![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(this.type)
&& this.options.some(opt => SUB.includes(opt.type)) && this.options.some(opt => [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type)))
&& this.name !== 'help') // Allow the help flag at any level
throw new Error('Cannot have subcommand(group)s under an option'); throw new Error('Cannot have subcommand(group)s under an option');
// if sub commands and normal options exists together, throw an error if (this.options.some(opt => [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type))
// Checks for subcommands: if opt.type is a subcommand type && this.options.some(opt => ![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type)))
// console.log(this.options, this.options.some(opt => SUB.includes(opt.type)), this.options.some(opt => !SUB.includes(opt.type)), this.options.some(opt => !SUB.includes(opt.type) && opt.name !== 'help'));
if (this.options.some(opt => SUB.includes(opt.type))
// Check for non-subcommands: if opt.type is not a sub type
&& this.options.some(opt => !SUB.includes(opt.type) && opt.name !== 'help'))
throw new Error('Cannot have subcommand(group)s on the same level as an option'); throw new Error('Cannot have subcommand(group)s on the same level as an option');
this.choices = def.choices || []; this.choices = def.choices || [];
@ -98,12 +81,13 @@ class CommandOption implements ICommandOption {
if (!this[OptionType[this.type]]) if (!this[OptionType[this.type]])
throw new Error(`Missing parsing function for ${this.type}`); throw new Error(`Missing parsing function for ${this.type}`);
if (typeof this[OptionType[this.type]] !== 'function') if (typeof this[OptionType[this.type]] !== 'function')
throw new Error(`Expected function type for member ${OptionType[this.type]}`); throw new Error(`Expected function type for memberr ${OptionType[this.type]}`);
this.guild = guild; this.guild = guild;
const func = this[OptionType[this.type]] as () => Promise<ParseResult>; // eslint-disable-next-line @typescript-eslint/ban-types
const func = this[OptionType[this.type]] as Function;
const result = await func.bind(this)(); const result = await func.bind(this)();
this.value = result.value; this.value = result.value;
return result; return result as ParseResult;
} }
get plural (): boolean { get plural (): boolean {
@ -165,20 +149,14 @@ class CommandOption implements ICommandOption {
} }
protected BOOLEAN () { protected BOOLEAN () {
if (!this.rawValue && !this.valueOptional) if (!this.rawValue)
return { error: true }; return { error: true };
const boolean = this.resolver?.resolveBoolean(this.rawValue[0]) || null;
const boolean = this.rawValue ? this.resolver?.resolveBoolean(this.rawValue[0]) : this.defaultValue;
if (boolean === null && this.valueOptional) if (boolean === null && this.valueOptional)
return { value: this.defaultValue, removed: [] }; return { value: this.defaultValue, removed: [] };
else if (boolean === null) else if (boolean === null)
return { error: true }; return { error: true };
return { value: boolean, removed: [ this.rawValue[0] ] };
const removed = [];
if (this.rawValue)
removed.push(this.rawValue[0]);
return { value: boolean, removed };
} }
} }

View File

@ -65,8 +65,7 @@ type DependsOnMode = 'AND' | 'OR';
type ParseResult = { type ParseResult = {
error: boolean, error: boolean,
removed: string[], removed: string[]
value: never
} }
type CommandOptionDefinition = { type CommandOptionDefinition = {

View File

@ -1,5 +1,4 @@
// import { Parser, Command, CommandOption, OptionType } from '../build/esm' import { Parser, Command, CommandOption, OptionType } from '../build/esm'
const {Parser, Command, CommandOption, OptionType} = require('../build/cjs')
const opt = new CommandOption({ const opt = new CommandOption({
name: 'text', name: 'text',

View File

@ -1,5 +1,4 @@
// import { Parser, Command, OptionType } from '../build/esm/index'; import { Parser, Command, OptionType } from '../build/esm/index';
const { Parser, Command, OptionType } = require('../build/cjs');
test('Command definition', () => { test('Command definition', () => {
expect(() => new Command()).toThrow(); expect(() => new Command()).toThrow();
@ -43,34 +42,6 @@ test('Command definition', () => {
}] })).not.toThrow(); }] })).not.toThrow();
}) })
test('Command Parser', async () => {
const command = new Command({
name: 'create',
options: [{
name: 'registration-code',
aliases: ['code'],
type: OptionType.SUB_COMMAND,
options: [{
name: 'amount',
flag: true,
type: OptionType.INTEGER,
defaultValue: 1,
valueOptional: true
}]
}]
});
const parser = new Parser({ commands: [command], prefix: '', debug: true });
await expect(parser.parseMessage('create')).rejects.toThrow();
// Cannot have an option at the sub-command level
await expect(parser.parseMessage('create test')).rejects.toThrow();
await expect(parser.parseMessage('create code')).resolves.toHaveProperty('command.name', 'create');
await expect(parser.parseMessage('create code -a 1')).resolves.toHaveProperty('args.amount.value', 1)
});
test('Command Parser', async () => { test('Command Parser', async () => {
const command = new Command({ const command = new Command({
name: 'create', name: 'create',

View File

@ -1,21 +0,0 @@
import { Parser, Command, OptionType } from '../build/esm/index.js';
const command = new Command({
name: 'create',
options: [{
name: 'registration-code',
aliases: ['code'],
type: OptionType.SUB_COMMAND,
options: [{
name: 'amount',
flag: true,
type: OptionType.INTEGER,
defaultValue: 1,
valueOptional: true
}]
}]
});
const parser = new Parser({ commands: [command], prefix: '', debug: true });
parser.on('debug', console.log)
console.log(await parser.parseMessage('create code -a 1'));
console.log(await parser.parseMessage('create code --help'))