Compare commits
2 Commits
01e4eaca5e
...
c669d60428
Author | SHA1 | Date | |
---|---|---|---|
c669d60428 | |||
eb4faa489b |
12
babel.config.cjs
Normal file
12
babel.config.cjs
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: 'current'
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [[ '@babel/preset-env', { targets: { node: 'current' } }]]
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@navy.gif/commandparser",
|
||||
"version": "1.4.3",
|
||||
"version": "1.4.4",
|
||||
"description": "Parser meant to parse commands and their options for discord bots",
|
||||
"author": "Navy.gif",
|
||||
"license": "MIT",
|
||||
@ -34,8 +34,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js",
|
||||
"test": "tsc && jest",
|
||||
"release": "tsc && yarn publish",
|
||||
"test": "yarn build && jest",
|
||||
"release": "yarn build && yarn publish",
|
||||
"lint": "eslint --fix"
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,6 @@ class Parser extends EventEmitter {
|
||||
return command || null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
@ -161,9 +160,12 @@ class Parser extends EventEmitter {
|
||||
continue;
|
||||
}
|
||||
|
||||
const short = match.groups.flag.startsWith('-') && !match.groups.flag.startsWith('--');
|
||||
const _flag = match.groups.flag.replace(/--?/u, '').toLowerCase();
|
||||
let aliased = false;
|
||||
const flag = flags.find((f) => {
|
||||
if (short)
|
||||
return f.name[0] === _flag;
|
||||
aliased = f.valueAsAlias && f.choices.some((c) => c === _flag);
|
||||
return f.name === _flag || aliased;
|
||||
});
|
||||
@ -192,7 +194,7 @@ class Parser extends EventEmitter {
|
||||
if (flag.choices.length) {
|
||||
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: ${flag.type}`); // return { option: flag, ...result.removed };
|
||||
throw new ParseError(`Failed to parse value for ${flag.name}, expected value type: ${OptionType[flag.type]}`); // return { option: flag, ...result.removed };
|
||||
}
|
||||
this.debug(`Cleaning up params after ${flag.name}`);
|
||||
for (const r of result.removed)
|
||||
|
@ -22,15 +22,26 @@ abstract class Command implements ICommand {
|
||||
|
||||
this.aliases = def.aliases || [];
|
||||
|
||||
this.options = [];
|
||||
// Add a built-in help flag to any non-subcommand option
|
||||
this.options = [
|
||||
new CommandOption({
|
||||
name: 'help',
|
||||
type: OptionType.BOOLEAN,
|
||||
flag: true,
|
||||
valueOptional: true,
|
||||
defaultValue: true
|
||||
})
|
||||
];
|
||||
|
||||
for (const opt of def.options || []) {
|
||||
if (opt instanceof CommandOption)
|
||||
this.options.push(opt);
|
||||
else
|
||||
this.options.push(new CommandOption(opt));
|
||||
}
|
||||
|
||||
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)))
|
||||
&& this.options.some(opt => ![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type) && opt.name !== 'help'))
|
||||
throw new Error('Cannot have subcommand(group)s on the same level as an option');
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
import ICommandOption, { Choice, CommandOptionDefinition, DependsOnMode, OptionType, ParseResult } from "../interfaces/CommandOption.js";
|
||||
import IResolver from "../interfaces/Resolver.js";
|
||||
|
||||
const SUB = [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ];
|
||||
class CommandOption implements ICommandOption {
|
||||
|
||||
[key: string]: unknown;
|
||||
@ -36,7 +37,18 @@ class CommandOption implements ICommandOption {
|
||||
this.flag = def.flag || false;
|
||||
|
||||
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 || []) {
|
||||
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)
|
||||
this.options.push(opt);
|
||||
else
|
||||
@ -49,12 +61,17 @@ class CommandOption implements ICommandOption {
|
||||
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))
|
||||
throw new Error('Cannot nest subcommands');
|
||||
if (![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(this.type)
|
||||
&& this.options.some(opt => [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type)))
|
||||
if (!SUB.includes(this.type)
|
||||
&& this.options.some(opt => SUB.includes(opt.type))
|
||||
&& this.name !== 'help') // Allow the help flag at any level
|
||||
throw new Error('Cannot have subcommand(group)s under an option');
|
||||
|
||||
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)))
|
||||
// if sub commands and normal options exists together, throw an error
|
||||
// Checks for subcommands: if opt.type is a subcommand 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');
|
||||
|
||||
this.choices = def.choices || [];
|
||||
@ -81,13 +98,12 @@ class CommandOption implements ICommandOption {
|
||||
if (!this[OptionType[this.type]])
|
||||
throw new Error(`Missing parsing function for ${this.type}`);
|
||||
if (typeof this[OptionType[this.type]] !== 'function')
|
||||
throw new Error(`Expected function type for memberr ${OptionType[this.type]}`);
|
||||
throw new Error(`Expected function type for member ${OptionType[this.type]}`);
|
||||
this.guild = guild;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const func = this[OptionType[this.type]] as Function;
|
||||
const func = this[OptionType[this.type]] as () => Promise<ParseResult>;
|
||||
const result = await func.bind(this)();
|
||||
this.value = result.value;
|
||||
return result as ParseResult;
|
||||
return result;
|
||||
}
|
||||
|
||||
get plural (): boolean {
|
||||
@ -149,14 +165,20 @@ class CommandOption implements ICommandOption {
|
||||
}
|
||||
|
||||
protected BOOLEAN () {
|
||||
if (!this.rawValue)
|
||||
if (!this.rawValue && !this.valueOptional)
|
||||
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)
|
||||
return { value: this.defaultValue, removed: [] };
|
||||
else if (boolean === null)
|
||||
return { error: true };
|
||||
return { value: boolean, removed: [ this.rawValue[0] ] };
|
||||
|
||||
const removed = [];
|
||||
if (this.rawValue)
|
||||
removed.push(this.rawValue[0]);
|
||||
return { value: boolean, removed };
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -65,7 +65,8 @@ type DependsOnMode = 'AND' | 'OR';
|
||||
|
||||
type ParseResult = {
|
||||
error: boolean,
|
||||
removed: string[]
|
||||
removed: string[],
|
||||
value: never
|
||||
}
|
||||
|
||||
type CommandOptionDefinition = {
|
||||
|
@ -1,4 +1,5 @@
|
||||
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({
|
||||
name: 'text',
|
||||
|
@ -1,4 +1,5 @@
|
||||
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', () => {
|
||||
expect(() => new Command()).toThrow();
|
||||
@ -42,6 +43,34 @@ test('Command definition', () => {
|
||||
}] })).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 () => {
|
||||
const command = new Command({
|
||||
name: 'create',
|
||||
|
21
tests/playground.js
Normal file
21
tests/playground.js
Normal file
@ -0,0 +1,21 @@
|
||||
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'))
|
Loading…
Reference in New Issue
Block a user