refactor: use eslint-config-neon for packages. (#8579)

Co-authored-by: Noel <buechler.noel@outlook.com>
This commit is contained in:
Suneet Tipirneni
2022-09-01 14:50:16 -04:00
committed by GitHub
parent 4bdb0593ae
commit edadb9fe5d
219 changed files with 2608 additions and 2053 deletions

View File

@@ -1,4 +1,9 @@
import { APIActionRowComponent, APIMessageActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types/v10';
import {
ButtonStyle,
ComponentType,
type APIActionRowComponent,
type APIMessageActionRowComponent,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
ActionRowBuilder,
@@ -6,7 +11,7 @@ import {
createComponentBuilder,
SelectMenuBuilder,
SelectMenuOptionBuilder,
} from '../../src';
} from '../../src/index.js';
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
type: ComponentType.ActionRow,

View File

@@ -1,12 +1,12 @@
import {
APIButtonComponentWithCustomId,
APIButtonComponentWithURL,
ButtonStyle,
ComponentType,
type APIButtonComponentWithCustomId,
type APIButtonComponentWithURL,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions';
import { ButtonBuilder } from '../../src/components/button/Button';
import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions.js';
import { ButtonBuilder } from '../../src/components/button/Button.js';
const buttonComponent = () => new ButtonBuilder();
@@ -71,7 +71,7 @@ describe('Button Components', () => {
}).toThrowError();
expect(() => {
// @ts-expect-error
// @ts-expect-error: invalid emoji
const button = buttonComponent().setEmoji('test');
button.toJSON();
}).toThrowError();
@@ -103,9 +103,9 @@ describe('Button Components', () => {
expect(() => buttonComponent().setStyle(24)).toThrowError();
expect(() => buttonComponent().setLabel(longStr)).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid parameter for disabled
expect(() => buttonComponent().setDisabled(0)).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid emoji
expect(() => buttonComponent().setEmoji('foo')).toThrowError();
expect(() => buttonComponent().setURL('foobar')).toThrowError();

View File

@@ -1,12 +1,12 @@
import {
APIActionRowComponent,
APIButtonComponent,
APIMessageActionRowComponent,
APISelectMenuComponent,
APITextInputComponent,
ButtonStyle,
ComponentType,
TextInputStyle,
type APIButtonComponent,
type APIMessageActionRowComponent,
type APISelectMenuComponent,
type APITextInputComponent,
type APIActionRowComponent,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
@@ -15,7 +15,7 @@ import {
createComponentBuilder,
SelectMenuBuilder,
TextInputBuilder,
} from '../../src/index';
} from '../../src/index.js';
describe('createComponentBuilder', () => {
test.each([ButtonBuilder, SelectMenuBuilder, TextInputBuilder])(
@@ -67,7 +67,7 @@ describe('createComponentBuilder', () => {
});
test('GIVEN an unknown component type THEN throws error', () => {
// @ts-expect-error
// @ts-expect-error: Unknown component type
expect(() => createComponentBuilder({ type: 'invalid' })).toThrowError();
});
});

View File

@@ -1,6 +1,6 @@
import { APISelectMenuComponent, APISelectMenuOption, ComponentType } from 'discord-api-types/v10';
import { ComponentType, type APISelectMenuComponent, type APISelectMenuOption } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { SelectMenuBuilder, SelectMenuOptionBuilder } from '../../src/index';
import { SelectMenuBuilder, SelectMenuOptionBuilder } from '../../src/index.js';
const selectMenu = () => new SelectMenuBuilder();
const selectMenuOption = () => new SelectMenuOptionBuilder();
@@ -74,7 +74,8 @@ describe('Select Menu Components', () => {
]),
).not.toThrowError();
const options = new Array<APISelectMenuOption>(25).fill({ label: 'test', value: 'test' });
const options = Array.from<APISelectMenuOption>({ length: 25 }).fill({ label: 'test', value: 'test' });
expect(() => selectMenu().addOptions(...options)).not.toThrowError();
expect(() => selectMenu().setOptions(...options)).not.toThrowError();
expect(() => selectMenu().addOptions(options)).not.toThrowError();
@@ -83,12 +84,13 @@ describe('Select Menu Components', () => {
expect(() =>
selectMenu()
.addOptions({ label: 'test', value: 'test' })
.addOptions(...new Array<APISelectMenuOption>(24).fill({ label: 'test', value: 'test' })),
.addOptions(...Array.from<APISelectMenuOption>({ length: 24 }).fill({ label: 'test', value: 'test' })),
).not.toThrowError();
expect(() =>
selectMenu()
.addOptions([{ label: 'test', value: 'test' }])
.addOptions(new Array<APISelectMenuOption>(24).fill({ label: 'test', value: 'test' })),
.addOptions(Array.from<APISelectMenuOption>({ length: 24 }).fill({ label: 'test', value: 'test' })),
).not.toThrowError();
});
@@ -96,33 +98,34 @@ describe('Select Menu Components', () => {
expect(() => selectMenu().setCustomId(longStr)).toThrowError();
expect(() => selectMenu().setMaxValues(30)).toThrowError();
expect(() => selectMenu().setMinValues(-20)).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid disabled value
expect(() => selectMenu().setDisabled(0)).toThrowError();
expect(() => selectMenu().setPlaceholder(longStr)).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: longStr, value: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ value: longStr, label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', description: longStr })).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', default: 100 })).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ value: 'test' })).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ default: true })).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', description: longStr }])).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', default: 100 }])).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ value: 'test' }])).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ default: true }])).toThrowError();
const tooManyOptions = new Array<APISelectMenuOption>(26).fill({ label: 'test', value: 'test' });
const tooManyOptions = Array.from<APISelectMenuOption>({ length: 26 }).fill({ label: 'test', value: 'test' });
expect(() => selectMenu().setOptions(...tooManyOptions)).toThrowError();
expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError();
@@ -141,9 +144,9 @@ describe('Select Menu Components', () => {
selectMenuOption()
.setLabel(longStr)
.setValue(longStr)
// @ts-expect-error
// @ts-expect-error: invalid default value
.setDefault(-1)
// @ts-expect-error
// @ts-expect-error: invalid emoji
.setEmoji({ name: 1 })
.setDescription(longStr);
}).toThrowError();

View File

@@ -1,4 +1,4 @@
import { APITextInputComponent, ComponentType, TextInputStyle } from 'discord-api-types/v10';
import { ComponentType, TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
labelValidator,
@@ -7,10 +7,10 @@ import {
placeholderValidator,
valueValidator,
textInputStyleValidator,
} from '../../src/components/textInput/Assertions';
import { TextInputBuilder } from '../../src/components/textInput/TextInput';
} from '../../src/components/textInput/Assertions.js';
import { TextInputBuilder } from '../../src/components/textInput/TextInput.js';
const superLongStr = 'a'.repeat(5000);
const superLongStr = 'a'.repeat(5_000);
const textInputComponent = () => new TextInputBuilder();
@@ -47,7 +47,7 @@ describe('Text Input Components', () => {
});
test('GIVEN invalid min length THEN validator does throw 2', () => {
expect(() => maxLengthValidator.parse(4001)).toThrowError();
expect(() => maxLengthValidator.parse(4_001)).toThrowError();
});
test('GIVEN valid value THEN validator does not throw', () => {

View File

@@ -1,6 +1,6 @@
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { ContextMenuCommandAssertions, ContextMenuCommandBuilder } from '../../src/index';
import { ContextMenuCommandAssertions, ContextMenuCommandBuilder } from '../../src/index.js';
const getBuilder = () => new ContextMenuCommandBuilder();
@@ -105,9 +105,9 @@ describe('Context Menu Commands', () => {
});
test('GIVEN invalid name localizations THEN does throw error', () => {
// @ts-expect-error
// @ts-expect-error: invalid localization
expect(() => getBuilder().setNameLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid localization
expect(() => getBuilder().setNameLocalizations({ 'en-U': 'foobar' })).toThrowError();
});

View File

@@ -1,15 +1,15 @@
import {
APIApplicationCommandAttachmentOption,
APIApplicationCommandBooleanOption,
APIApplicationCommandChannelOption,
APIApplicationCommandIntegerOption,
APIApplicationCommandMentionableOption,
APIApplicationCommandNumberOption,
APIApplicationCommandRoleOption,
APIApplicationCommandStringOption,
APIApplicationCommandUserOption,
ApplicationCommandOptionType,
ChannelType,
type APIApplicationCommandAttachmentOption,
type APIApplicationCommandBooleanOption,
type APIApplicationCommandChannelOption,
type APIApplicationCommandIntegerOption,
type APIApplicationCommandMentionableOption,
type APIApplicationCommandNumberOption,
type APIApplicationCommandRoleOption,
type APIApplicationCommandStringOption,
type APIApplicationCommandUserOption,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
@@ -22,7 +22,7 @@ import {
SlashCommandRoleOption,
SlashCommandStringOption,
SlashCommandUserOption,
} from '../../../src/index';
} from '../../../src/index.js';
const getBooleanOption = () =>
new SlashCommandBooleanOption().setName('owo').setDescription('Testing 123').setRequired(true);

View File

@@ -1,4 +1,4 @@
import { APIApplicationCommandOptionChoice, ChannelType, PermissionFlagsBits } from 'discord-api-types/v10';
import { ChannelType, PermissionFlagsBits, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
SlashCommandAssertions,
@@ -14,7 +14,7 @@ import {
SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder,
SlashCommandUserOption,
} from '../../../src/index';
} from '../../../src/index.js';
const largeArray = Array.from({ length: 26 }, () => 1 as unknown as APIApplicationCommandOptionChoice);
@@ -33,9 +33,7 @@ const getSubcommandGroup = () => new SlashCommandSubcommandGroupBuilder().setNam
const getSubcommand = () => new SlashCommandSubcommandBuilder().setName('owo').setDescription('Testing 123');
class Collection {
public get [Symbol.toStringTag]() {
return 'Map';
}
public readonly [Symbol.toStringTag] = 'Map';
}
describe('Slash Commands', () => {
@@ -248,16 +246,16 @@ describe('Slash Commands', () => {
});
test('GIVEN a builder with invalid number min/max options THEN does throw an error', () => {
// @ts-expect-error
// @ts-expect-error: invalid max value
expect(() => getBuilder().addNumberOption(getNumberOption().setMaxValue('test'))).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid max value
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMaxValue('test'))).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid min value
expect(() => getBuilder().addNumberOption(getNumberOption().setMinValue('test'))).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid min value
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMinValue('test'))).toThrowError();
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMinValue(1.5))).toThrowError();
@@ -444,9 +442,9 @@ describe('Slash Commands', () => {
});
test('GIVEN invalid name localizations THEN does throw error', () => {
// @ts-expect-error
// @ts-expect-error: invalid localization
expect(() => getBuilder().setNameLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid localization
expect(() => getBuilder().setNameLocalizations({ 'en-U': 'foobar' })).toThrowError();
});
@@ -467,9 +465,9 @@ describe('Slash Commands', () => {
});
test('GIVEN invalid description localizations THEN does throw error', () => {
// @ts-expect-error
// @ts-expect-error: invalid localization description
expect(() => getBuilder().setDescriptionLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid localization description
expect(() => getBuilder().setDescriptionLocalizations({ 'en-U': 'foobar' })).toThrowError();
});

View File

@@ -1,22 +1,22 @@
import {
APIModalInteractionResponseCallbackData,
APITextInputComponent,
ComponentType,
TextInputStyle,
type APIModalInteractionResponseCallbackData,
type APITextInputComponent,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
ActionRowBuilder,
ButtonBuilder,
ModalBuilder,
ModalActionRowComponentBuilder,
TextInputBuilder,
} from '../../src';
type ModalActionRowComponentBuilder,
} from '../../src/index.js';
import {
componentsValidator,
titleValidator,
validateRequiredParameters,
} from '../../src/interactions/modals/Assertions';
} from '../../src/interactions/modals/Assertions.js';
const modal = () => new ModalBuilder();
@@ -46,7 +46,7 @@ describe('Modals', () => {
test('GIVEN invalid required parameters THEN validator does throw', () => {
expect(() =>
// @ts-expect-error
// @ts-expect-error: missing required parameter
validateRequiredParameters('123', undefined, [new ActionRowBuilder(), new ButtonBuilder()]),
).toThrowError();
});
@@ -66,7 +66,7 @@ describe('Modals', () => {
test('GIVEN invalid fields THEN builder does throw', () => {
expect(() => modal().setTitle('test').setCustomId('foobar').toJSON()).toThrowError();
// @ts-expect-error
// @ts-expect-error: customId is invalid
expect(() => modal().setTitle('test').setCustomId(42).toJSON()).toThrowError();
});

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest';
import { EmbedBuilder, embedLength } from '../../src';
import { EmbedBuilder, embedLength } from '../../src/index.js';
const alpha = 'abcdefghijklmnopqrstuvwxyz';
@@ -74,7 +74,7 @@ describe('Embed', () => {
test('GIVEN an embed with an invalid description THEN throws error', () => {
const embed = new EmbedBuilder();
expect(() => embed.setDescription('a'.repeat(4097))).toThrowError();
expect(() => embed.setDescription('a'.repeat(4_097))).toThrowError();
});
});
@@ -130,11 +130,11 @@ describe('Embed', () => {
test('GIVEN an embed with an invalid color THEN throws error', () => {
const embed = new EmbedBuilder();
// @ts-expect-error
// @ts-expect-error: invalid color
expect(() => embed.setColor('RED')).toThrowError();
// @ts-expect-error
// @ts-expect-error: invalid color
expect(() => embed.setColor([42, 36])).toThrowError();
expect(() => embed.setColor([42, 36, 1000])).toThrowError();
expect(() => embed.setColor([42, 36, 1_000])).toThrowError();
});
});
@@ -307,7 +307,7 @@ describe('Embed', () => {
test('GIVEN an embed with invalid footer text THEN throws error', () => {
const embed = new EmbedBuilder();
expect(() => embed.setFooter({ text: 'a'.repeat(2049) })).toThrowError();
expect(() => embed.setFooter({ text: 'a'.repeat(2_049) })).toThrowError();
});
});
@@ -411,7 +411,7 @@ describe('Embed', () => {
test('4', () => {
const embed = new EmbedBuilder();
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1025) })).toThrowError();
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1_025) })).toThrowError();
});
});
});

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-template-curly-in-string */
import { URL } from 'node:url';
import { describe, test, expect, vitest } from 'vitest';
import {
@@ -21,7 +22,7 @@ import {
TimestampStyles,
underscore,
userMention,
} from '../../src';
} from '../../src/index.js';
describe('Message formatters', () => {
describe('codeBlock', () => {
@@ -183,7 +184,7 @@ describe('Message formatters', () => {
describe('time', () => {
test('GIVEN no arguments THEN returns "<t:${bigint}>"', () => {
vitest.useFakeTimers();
vitest.setSystemTime(1566424897579);
vitest.setSystemTime(1_566_424_897_579);
expect<`<t:${bigint}>`>(time()).toEqual('<t:1566424897>');
@@ -191,29 +192,29 @@ describe('Message formatters', () => {
});
test('GIVEN a date THEN returns "<t:${bigint}>"', () => {
expect<`<t:${bigint}>`>(time(new Date(1867424897579))).toEqual('<t:1867424897>');
expect<`<t:${bigint}>`>(time(new Date(1_867_424_897_579))).toEqual('<t:1867424897>');
});
test('GIVEN a date and a style from string THEN returns "<t:${bigint}:${style}>"', () => {
expect<`<t:${bigint}:d>`>(time(new Date(1867424897579), 'd')).toEqual('<t:1867424897:d>');
expect<`<t:${bigint}:d>`>(time(new Date(1_867_424_897_579), 'd')).toEqual('<t:1867424897:d>');
});
test('GIVEN a date and a format from enum THEN returns "<t:${bigint}:${style}>"', () => {
expect<`<t:${bigint}:R>`>(time(new Date(1867424897579), TimestampStyles.RelativeTime)).toEqual(
expect<`<t:${bigint}:R>`>(time(new Date(1_867_424_897_579), TimestampStyles.RelativeTime)).toEqual(
'<t:1867424897:R>',
);
});
test('GIVEN a date THEN returns "<t:${time}>"', () => {
expect<'<t:1867424897>'>(time(1867424897)).toEqual('<t:1867424897>');
expect<'<t:1867424897>'>(time(1_867_424_897)).toEqual('<t:1867424897>');
});
test('GIVEN a date and a style from string THEN returns "<t:${time}:${style}>"', () => {
expect<'<t:1867424897:d>'>(time(1867424897, 'd')).toEqual('<t:1867424897:d>');
expect<'<t:1867424897:d>'>(time(1_867_424_897, 'd')).toEqual('<t:1867424897:d>');
});
test('GIVEN a date and a format from enum THEN returns "<t:${time}:${style}>"', () => {
expect<'<t:1867424897:R>'>(time(1867424897, TimestampStyles.RelativeTime)).toEqual('<t:1867424897:R>');
expect<'<t:1867424897:R>'>(time(1_867_424_897, TimestampStyles.RelativeTime)).toEqual('<t:1867424897:R>');
});
});

View File

@@ -6,7 +6,7 @@ import {
enableValidators,
disableValidators,
isValidationEnabled,
} from '../src/index';
} from '../src/index.js';
describe('isEquatable', () => {
test('returns true if the object is equatable', () => {

View File

@@ -65,16 +65,10 @@
"@favware/cliff-jumper": "^1.8.7",
"@microsoft/api-extractor": "^7.29.5",
"@types/node": "^16.11.56",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"@vitest/coverage-c8": "^0.22.1",
"downlevel-dts": "^0.10.1",
"eslint": "^8.23.0",
"eslint-config-marine": "^9.4.1",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.16",
"eslint-config-neon": "^0.1.23",
"prettier": "^2.7.1",
"rollup-plugin-typescript2": "^0.33.0",
"typescript": "^4.8.2",

View File

@@ -1,21 +1,21 @@
import {
type APIActionRowComponent,
ComponentType,
APIMessageActionRowComponent,
APIModalActionRowComponent,
APIActionRowComponentTypes,
type APIMessageActionRowComponent,
type APIModalActionRowComponent,
type APIActionRowComponentTypes,
} from 'discord-api-types/v10';
import { ComponentBuilder } from './Component';
import { createComponentBuilder } from './Components';
import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
import { ComponentBuilder } from './Component.js';
import { createComponentBuilder } from './Components.js';
import type { ButtonBuilder } from './button/Button';
import type { SelectMenuBuilder } from './selectMenu/SelectMenu';
import type { TextInputBuilder } from './textInput/TextInput';
import { normalizeArray, type RestOrArray } from '../util/normalizeArray';
export type MessageComponentBuilder =
| MessageActionRowComponentBuilder
| ActionRowBuilder<MessageActionRowComponentBuilder>;
export type ModalComponentBuilder = ModalActionRowComponentBuilder | ActionRowBuilder<ModalActionRowComponentBuilder>;
| ActionRowBuilder<MessageActionRowComponentBuilder>
| MessageActionRowComponentBuilder;
export type ModalComponentBuilder = ActionRowBuilder<ModalActionRowComponentBuilder> | ModalActionRowComponentBuilder;
export type MessageActionRowComponentBuilder = ButtonBuilder | SelectMenuBuilder;
export type ModalActionRowComponentBuilder = TextInputBuilder;
export type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder;
@@ -35,7 +35,7 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
public constructor({ components, ...data }: Partial<APIActionRowComponent<APIActionRowComponentTypes>> = {}) {
super({ type: ComponentType.ActionRow, ...data });
this.components = (components?.map((c) => createComponentBuilder(c)) ?? []) as T[];
this.components = (components?.map((component) => createComponentBuilder(component)) ?? []) as T[];
}
/**

View File

@@ -1,7 +1,7 @@
import { s } from '@sapphire/shapeshift';
import { APIMessageComponentEmoji, ButtonStyle } from 'discord-api-types/v10';
import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption';
import { isValidationEnabled } from '../util/validation';
import { ButtonStyle, type APIMessageComponentEmoji } from 'discord-api-types/v10';
import { isValidationEnabled } from '../util/validation.js';
import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption.js';
export const customIdValidator = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -6,7 +6,7 @@ import type {
} from 'discord-api-types/v10';
import type { JSONEncodable } from '../util/jsonEncodable';
export type AnyAPIActionRowComponent = APIActionRowComponentTypes | APIActionRowComponent<APIActionRowComponentTypes>;
export type AnyAPIActionRowComponent = APIActionRowComponent<APIActionRowComponentTypes> | APIActionRowComponentTypes;
/**
* Represents a discord component

View File

@@ -1,14 +1,14 @@
import { APIMessageComponent, APIModalComponent, ComponentType } from 'discord-api-types/v10';
import { ComponentType, type APIMessageComponent, type APIModalComponent } from 'discord-api-types/v10';
import {
ActionRowBuilder,
type AnyComponentBuilder,
type MessageComponentBuilder,
type ModalComponentBuilder,
} from './ActionRow';
import { ComponentBuilder } from './Component';
import { ButtonBuilder } from './button/Button';
import { SelectMenuBuilder } from './selectMenu/SelectMenu';
import { TextInputBuilder } from './textInput/TextInput';
} from './ActionRow.js';
import { ComponentBuilder } from './Component.js';
import { ButtonBuilder } from './button/Button.js';
import { SelectMenuBuilder } from './selectMenu/SelectMenu.js';
import { TextInputBuilder } from './textInput/TextInput.js';
export interface MappedComponentTypes {
[ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>;
@@ -23,7 +23,8 @@ export interface MappedComponentTypes {
* @param data - The api data to transform to a component class
*/
export function createComponentBuilder<T extends keyof MappedComponentTypes>(
data: (APIMessageComponent | APIModalComponent) & { type: T },
// eslint-disable-next-line @typescript-eslint/sort-type-union-intersection-members
data: (APIModalComponent | APIMessageComponent) & { type: T },
): MappedComponentTypes[T];
export function createComponentBuilder<C extends MessageComponentBuilder | ModalComponentBuilder>(data: C): C;
export function createComponentBuilder(
@@ -43,7 +44,7 @@ export function createComponentBuilder(
case ComponentType.TextInput:
return new TextInputBuilder(data);
default:
// @ts-expect-error
// @ts-expect-error: This case can still occur if we get a newer unsupported component type
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Cannot properly serialize component type: ${data.type}`);
}

View File

@@ -1,10 +1,10 @@
import {
ComponentType,
ButtonStyle,
type APIMessageComponentEmoji,
type APIButtonComponent,
type APIButtonComponentWithURL,
type APIButtonComponentWithCustomId,
type ButtonStyle,
} from 'discord-api-types/v10';
import {
buttonLabelValidator,
@@ -14,8 +14,8 @@ import {
emojiValidator,
urlValidator,
validateRequiredButtonParameters,
} from '../Assertions';
import { ComponentBuilder } from '../Component';
} from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
/**
* Represents a button component
@@ -23,8 +23,8 @@ import { ComponentBuilder } from '../Component';
export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Creates a new button from API data
* @param data - The API data to create this button with
*
* @param data - The API data to create this button with
* @example
* Creating a button from an API data object
* ```ts
@@ -38,7 +38,6 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
* custom_id: '12345678901234567890123456789012',
* });
* ```
*
* @example
* Creating a button using setters and API data
* ```ts
@@ -70,7 +69,6 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
* @remarks
* This method is only available to buttons using the `Link` button style.
* Only three types of URL schemes are currently supported: `https://`, `http://` and `discord://`
*
* @param url - The URL to open when this button is clicked
*/
public setURL(url: string) {
@@ -83,7 +81,6 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
*
* @remarks
* This method is only applicable to buttons that are not using the `Link` button style.
*
* @param customId - The custom id to use for this button
*/
public setCustomId(customId: string) {

View File

@@ -1,6 +1,5 @@
import { APISelectMenuOption, ComponentType, type APISelectMenuComponent } from 'discord-api-types/v10';
import { SelectMenuOptionBuilder } from './SelectMenuOption';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
import { ComponentType, type APISelectMenuComponent, type APISelectMenuOption } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import {
customIdValidator,
disabledValidator,
@@ -9,8 +8,9 @@ import {
optionsLengthValidator,
placeholderValidator,
validateRequiredSelectMenuParameters,
} from '../Assertions';
import { ComponentBuilder } from '../Component';
} from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
import { SelectMenuOptionBuilder } from './SelectMenuOption.js';
/**
* Represents a select menu component
@@ -24,7 +24,7 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
public constructor(data?: Partial<APISelectMenuComponent>) {
const { options, ...initData } = data ?? {};
super({ type: ComponentType.SelectMenu, ...initData });
this.options = options?.map((o) => new SelectMenuOptionBuilder(o)) ?? [];
this.options = options?.map((option) => new SelectMenuOptionBuilder(option)) ?? [];
}
/**
@@ -83,7 +83,8 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
* @param options - The options to add to this select menu
* @returns
*/
public addOptions(...options: RestOrArray<SelectMenuOptionBuilder | APISelectMenuOption>) {
public addOptions(...options: RestOrArray<APISelectMenuOption | SelectMenuOptionBuilder>) {
// eslint-disable-next-line no-param-reassign
options = normalizeArray(options);
optionsLengthValidator.parse(this.options.length + options.length);
this.options.push(
@@ -101,7 +102,8 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
*
* @param options - The options to set on this select menu
*/
public setOptions(...options: RestOrArray<SelectMenuOptionBuilder | APISelectMenuOption>) {
public setOptions(...options: RestOrArray<APISelectMenuOption | SelectMenuOptionBuilder>) {
// eslint-disable-next-line no-param-reassign
options = normalizeArray(options);
optionsLengthValidator.parse(options.length);
this.options.splice(
@@ -124,7 +126,7 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
...this.data,
options: this.options.map((o) => o.toJSON()),
options: this.options.map((option) => option.toJSON()),
} as APISelectMenuComponent;
}
}

View File

@@ -1,12 +1,11 @@
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
import type { JSONEncodable } from '../../util/jsonEncodable';
import type { JSONEncodable } from '../../util/jsonEncodable.js';
import {
defaultValidator,
emojiValidator,
labelValueDescriptionValidator,
validateRequiredSelectMenuOptionParameters,
} from '../Assertions';
} from '../Assertions.js';
/**
* Represents a option within a select menu component

View File

@@ -1,19 +1,19 @@
import { s } from '@sapphire/shapeshift';
import { TextInputStyle } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation';
import { customIdValidator } from '../Assertions';
import { isValidationEnabled } from '../../util/validation.js';
import { customIdValidator } from '../Assertions.js';
export const textInputStyleValidator = s.nativeEnum(TextInputStyle);
export const minLengthValidator = s.number.int
.greaterThanOrEqual(0)
.lessThanOrEqual(4000)
.lessThanOrEqual(4_000)
.setValidationEnabled(isValidationEnabled);
export const maxLengthValidator = s.number.int
.greaterThanOrEqual(1)
.lessThanOrEqual(4000)
.lessThanOrEqual(4_000)
.setValidationEnabled(isValidationEnabled);
export const requiredValidator = s.boolean;
export const valueValidator = s.string.lengthLessThanOrEqual(4000).setValidationEnabled(isValidationEnabled);
export const valueValidator = s.string.lengthLessThanOrEqual(4_000).setValidationEnabled(isValidationEnabled);
export const placeholderValidator = s.string.lengthLessThanOrEqual(100).setValidationEnabled(isValidationEnabled);
export const labelValidator = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -1,5 +1,9 @@
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import isEqual from 'fast-deep-equal';
import type { Equatable } from '../../util/equatable';
import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable.js';
import { customIdValidator } from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
import {
maxLengthValidator,
minLengthValidator,
@@ -9,15 +13,11 @@ import {
validateRequiredParameters,
labelValidator,
textInputStyleValidator,
} from './Assertions';
import type { Equatable } from '../../util/equatable';
import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable';
import { customIdValidator } from '../Assertions';
import { ComponentBuilder } from '../Component';
} from './Assertions.js';
export class TextInputBuilder
extends ComponentBuilder<APITextInputComponent>
implements Equatable<JSONEncodable<APITextInputComponent> | APITextInputComponent>
implements Equatable<APITextInputComponent | JSONEncodable<APITextInputComponent>>
{
public constructor(data?: APITextInputComponent & { type?: ComponentType.TextInput }) {
super({ type: ComponentType.TextInput, ...data });
@@ -117,7 +117,7 @@ export class TextInputBuilder
/**
* {@inheritDoc Equatable.equals}
*/
public equals(other: JSONEncodable<APITextInputComponent> | APITextInputComponent): boolean {
public equals(other: APITextInputComponent | JSONEncodable<APITextInputComponent>): boolean {
if (isJSONEncodable(other)) {
return isEqual(other.toJSON(), this.data);
}

View File

@@ -1,43 +1,43 @@
export * as EmbedAssertions from './messages/embed/Assertions';
export * from './messages/embed/Embed';
export * from './messages/formatters';
export * as EmbedAssertions from './messages/embed/Assertions.js';
export * from './messages/embed/Embed.js';
export * from './messages/formatters.js';
export * as ComponentAssertions from './components/Assertions';
export * from './components/ActionRow';
export * from './components/button/Button';
export * from './components/Component';
export * from './components/Components';
export * from './components/textInput/TextInput';
export * as TextInputAssertions from './components/textInput/Assertions';
export * from './interactions/modals/Modal';
export * as ModalAssertions from './interactions/modals/Assertions';
export * from './components/selectMenu/SelectMenu';
export * from './components/selectMenu/SelectMenuOption';
export * as ComponentAssertions from './components/Assertions.js';
export * from './components/ActionRow.js';
export * from './components/button/Button.js';
export * from './components/Component.js';
export * from './components/Components.js';
export * from './components/textInput/TextInput.js';
export * as TextInputAssertions from './components/textInput/Assertions.js';
export * from './interactions/modals/Modal.js';
export * as ModalAssertions from './interactions/modals/Assertions.js';
export * from './components/selectMenu/SelectMenu.js';
export * from './components/selectMenu/SelectMenuOption.js';
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions';
export * from './interactions/slashCommands/SlashCommandBuilder';
export * from './interactions/slashCommands/SlashCommandSubcommands';
export * from './interactions/slashCommands/options/boolean';
export * from './interactions/slashCommands/options/channel';
export * from './interactions/slashCommands/options/integer';
export * from './interactions/slashCommands/options/mentionable';
export * from './interactions/slashCommands/options/number';
export * from './interactions/slashCommands/options/role';
export * from './interactions/slashCommands/options/attachment';
export * from './interactions/slashCommands/options/string';
export * from './interactions/slashCommands/options/user';
export * from './interactions/slashCommands/mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionBase';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
export * from './interactions/slashCommands/mixins/NameAndDescription';
export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions';
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions.js';
export * from './interactions/slashCommands/SlashCommandBuilder.js';
export * from './interactions/slashCommands/SlashCommandSubcommands.js';
export * from './interactions/slashCommands/options/boolean.js';
export * from './interactions/slashCommands/options/channel.js';
export * from './interactions/slashCommands/options/integer.js';
export * from './interactions/slashCommands/options/mentionable.js';
export * from './interactions/slashCommands/options/number.js';
export * from './interactions/slashCommands/options/role.js';
export * from './interactions/slashCommands/options/attachment.js';
export * from './interactions/slashCommands/options/string.js';
export * from './interactions/slashCommands/options/user.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionBase.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.js';
export * from './interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
export * from './interactions/slashCommands/mixins/NameAndDescription.js';
export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions.js';
export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions';
export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder';
export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions.js';
export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder.js';
export * from './util/jsonEncodable';
export * from './util/equatable';
export * from './util/componentUtil';
export * from './util/normalizeArray';
export * from './util/validation';
export * from './util/jsonEncodable.js';
export * from './util/equatable.js';
export * from './util/componentUtil.js';
export * from './util/normalizeArray.js';
export * from './util/validation.js';

View File

@@ -1,11 +1,12 @@
import { s } from '@sapphire/shapeshift';
import { ApplicationCommandType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder';
import { isValidationEnabled } from '../../util/validation';
const namePredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(32)
// eslint-disable-next-line prefer-named-capture-group, unicorn/no-unsafe-regex
.regex(/^( *[\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+ *)+$/u)
.setValidationEnabled(isValidationEnabled);
const typePredicate = s

View File

@@ -5,6 +5,7 @@ import type {
Permissions,
RESTPostAPIApplicationCommandsJSONBody,
} from 'discord-api-types/v10';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions.js';
import {
validateRequiredParameters,
validateName,
@@ -12,8 +13,7 @@ import {
validateDefaultPermission,
validateDefaultMemberPermissions,
validateDMPermission,
} from './Assertions';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions';
} from './Assertions.js';
export class ContextMenuCommandBuilder {
/**
@@ -84,7 +84,6 @@ export class ContextMenuCommandBuilder {
* **Note**: If set to `false`, you will have to later `PUT` the permissions for this command.
*
* @param value - Whether or not to enable this command by default
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
* @deprecated Use {@link ContextMenuCommandBuilder.setDefaultMemberPermissions} or {@link ContextMenuCommandBuilder.setDMPermission} instead.
*/
@@ -103,7 +102,6 @@ export class ContextMenuCommandBuilder {
* **Note:** You can set this to `'0'` to disable the command by default.
*
* @param permissions - The permissions bit field to set
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
@@ -120,7 +118,6 @@ export class ContextMenuCommandBuilder {
* By default, commands are visible.
*
* @param enabled - If the command should be enabled in DMs
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDMPermission(enabled: boolean | null | undefined) {
@@ -169,9 +166,8 @@ export class ContextMenuCommandBuilder {
Reflect.set(this, 'name_localizations', {});
Object.entries(localizedNames).forEach((args) =>
this.setNameLocalization(...(args as [LocaleString, string | null])),
);
for (const args of Object.entries(localizedNames))
this.setNameLocalization(...(args as [LocaleString, string | null]));
return this;
}
@@ -189,4 +185,4 @@ export class ContextMenuCommandBuilder {
}
}
export type ContextMenuCommandType = ApplicationCommandType.User | ApplicationCommandType.Message;
export type ContextMenuCommandType = ApplicationCommandType.Message | ApplicationCommandType.User;

View File

@@ -1,7 +1,7 @@
import { s } from '@sapphire/shapeshift';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow';
import { customIdValidator } from '../../components/Assertions';
import { isValidationEnabled } from '../../util/validation';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { isValidationEnabled } from '../../util/validation.js';
export const titleValidator = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -3,20 +3,21 @@ import type {
APIModalActionRowComponent,
APIModalInteractionResponseCallbackData,
} from 'discord-api-types/v10';
import { titleValidator, validateRequiredParameters } from './Assertions';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow';
import { customIdValidator } from '../../components/Assertions';
import { createComponentBuilder } from '../../components/Components';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { createComponentBuilder } from '../../components/Components.js';
import type { JSONEncodable } from '../../util/jsonEncodable';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { titleValidator, validateRequiredParameters } from './Assertions.js';
export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
public readonly data: Partial<APIModalInteractionResponseCallbackData>;
public readonly components: ActionRowBuilder<ModalActionRowComponentBuilder>[] = [];
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
this.data = { ...data };
this.components = (components?.map((c) => createComponentBuilder(c)) ??
this.components = (components?.map((component) => createComponentBuilder(component)) ??
[]) as ActionRowBuilder<ModalActionRowComponentBuilder>[];
}

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { type APIApplicationCommandOptionChoice, Locale, LocalizationMap } from 'discord-api-types/v10';
import { Locale, type APIApplicationCommandOptionChoice, type LocalizationMap } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import { isValidationEnabled } from '../../util/validation';
const namePredicate = s.string
.lengthGreaterThanOrEqual(1)

View File

@@ -13,10 +13,10 @@ import {
validateDMPermission,
validateMaxOptionsLength,
validateRequiredParameters,
} from './Assertions';
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
import { SharedNameAndDescription } from './mixins/NameAndDescription';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions';
} from './Assertions.js';
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands.js';
import { SharedNameAndDescription } from './mixins/NameAndDescription.js';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions.js';
@mix(SharedSlashCommandOptions, SharedNameAndDescription)
export class SlashCommandBuilder {
@@ -87,7 +87,6 @@ export class SlashCommandBuilder {
* **Note**: If set to `false`, you will have to later `PUT` the permissions for this command.
*
* @param value - Whether or not to enable this command by default
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
* @deprecated Use {@link (SlashCommandBuilder:class).setDefaultMemberPermissions} or {@link (SlashCommandBuilder:class).setDMPermission} instead.
*/
@@ -106,7 +105,6 @@ export class SlashCommandBuilder {
* **Note:** You can set this to `'0'` to disable the command by default.
*
* @param permissions - The permissions bit field to set
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
@@ -123,7 +121,6 @@ export class SlashCommandBuilder {
* By default, commands are visible.
*
* @param enabled - If the command should be enabled in DMs
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDMPermission(enabled: boolean | null | undefined) {
@@ -192,7 +189,7 @@ export interface SlashCommandBuilder extends SharedNameAndDescription, SharedSla
export interface SlashCommandSubcommandsOnlyBuilder
extends SharedNameAndDescription,
Pick<SlashCommandBuilder, 'toJSON' | 'addSubcommand' | 'addSubcommandGroup'> {}
Pick<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup' | 'toJSON'> {}
export interface SlashCommandOptionsOnlyBuilder
extends SharedNameAndDescription,
@@ -200,5 +197,5 @@ export interface SlashCommandOptionsOnlyBuilder
Pick<SlashCommandBuilder, 'toJSON'> {}
export interface ToAPIApplicationCommandOptions {
toJSON: () => APIApplicationCommandOption;
toJSON(): APIApplicationCommandOption;
}

View File

@@ -1,14 +1,14 @@
import {
APIApplicationCommandSubcommandGroupOption,
APIApplicationCommandSubcommandOption,
ApplicationCommandOptionType,
type APIApplicationCommandSubcommandGroupOption,
type APIApplicationCommandSubcommandOption,
} from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
import { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions.js';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder.js';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import { SharedNameAndDescription } from './mixins/NameAndDescription';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions';
import { SharedNameAndDescription } from './mixins/NameAndDescription.js';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions.js';
/**
* Represents a folder for subcommands

View File

@@ -1,5 +1,6 @@
export abstract class ApplicationCommandNumericOptionMinMaxValueMixin {
public readonly max_value?: number;
public readonly min_value?: number;
/**

View File

@@ -1,6 +1,6 @@
import type { APIApplicationCommandBasicOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { SharedNameAndDescription } from './NameAndDescription';
import { validateRequiredParameters, validateRequired, validateLocalizationMap } from '../Assertions';
import { validateRequiredParameters, validateRequired, validateLocalizationMap } from '../Assertions.js';
import { SharedNameAndDescription } from './NameAndDescription.js';
export abstract class ApplicationCommandOptionBase extends SharedNameAndDescription {
public abstract readonly type: ApplicationCommandOptionType;

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { localizationMapPredicate, validateChoicesLength } from '../Assertions';
import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10';
import { localizationMapPredicate, validateChoicesLength } from '../Assertions.js';
const stringPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
const numberPredicate = s.number.greaterThan(-Infinity).lessThan(Infinity);
const numberPredicate = s.number.greaterThan(Number.NEGATIVE_INFINITY).lessThan(Number.POSITIVE_INFINITY);
const choicesPredicate = s.object({
name: stringPredicate,
name_localizations: localizationMapPredicate,
@@ -11,8 +11,9 @@ const choicesPredicate = s.object({
}).array;
const booleanPredicate = s.boolean;
export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends string | number> {
export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends number | string> {
public readonly choices?: APIApplicationCommandOptionChoice<T>[];
public readonly autocomplete?: boolean;
// Since this is present and this is a mixin, this is needed
@@ -65,6 +66,7 @@ export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends s
/**
* Marks the option as autocompletable
*
* @param autocomplete - If this option should be autocompletable
*/
public setAutocomplete(autocomplete: boolean): this {

View File

@@ -1,10 +1,13 @@
import type { LocaleString, LocalizationMap } from 'discord-api-types/v10';
import { validateDescription, validateLocale, validateName } from '../Assertions';
import { validateDescription, validateLocale, validateName } from '../Assertions.js';
export class SharedNameAndDescription {
public readonly name!: string;
public readonly name_localizations?: LocalizationMap;
public readonly description!: string;
public readonly description_localizations?: LocalizationMap;
/**
@@ -72,9 +75,10 @@ export class SharedNameAndDescription {
Reflect.set(this, 'name_localizations', {});
Object.entries(localizedNames).forEach((args) =>
this.setNameLocalization(...(args as [LocaleString, string | null])),
);
for (const args of Object.entries(localizedNames)) {
this.setNameLocalization(...(args as [LocaleString, string | null]));
}
return this;
}
@@ -114,9 +118,10 @@ export class SharedNameAndDescription {
}
Reflect.set(this, 'description_localizations', {});
Object.entries(localizedDescriptions).forEach((args) =>
this.setDescriptionLocalization(...(args as [LocaleString, string | null])),
);
for (const args of Object.entries(localizedDescriptions)) {
this.setDescriptionLocalization(...(args as [LocaleString, string | null]));
}
return this;
}
}

View File

@@ -1,15 +1,15 @@
import type { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase';
import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions';
import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions.js';
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';
import { SlashCommandAttachmentOption } from '../options/attachment';
import { SlashCommandBooleanOption } from '../options/boolean';
import { SlashCommandChannelOption } from '../options/channel';
import { SlashCommandIntegerOption } from '../options/integer';
import { SlashCommandMentionableOption } from '../options/mentionable';
import { SlashCommandNumberOption } from '../options/number';
import { SlashCommandRoleOption } from '../options/role';
import { SlashCommandStringOption } from '../options/string';
import { SlashCommandUserOption } from '../options/user';
import { SlashCommandAttachmentOption } from '../options/attachment.js';
import { SlashCommandBooleanOption } from '../options/boolean.js';
import { SlashCommandChannelOption } from '../options/channel.js';
import { SlashCommandIntegerOption } from '../options/integer.js';
import { SlashCommandMentionableOption } from '../options/mentionable.js';
import { SlashCommandNumberOption } from '../options/number.js';
import { SlashCommandRoleOption } from '../options/role.js';
import { SlashCommandStringOption } from '../options/string.js';
import { SlashCommandUserOption } from '../options/user.js';
import type { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase.js';
export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
public readonly options!: ToAPIApplicationCommandOptions[];
@@ -83,15 +83,15 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
*/
public addStringOption(
input:
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoices'>
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| SlashCommandStringOption
| ((
builder: SlashCommandStringOption,
) =>
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'addChoices'>
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoices'>),
| SlashCommandStringOption),
) {
return this._sharedAddOptionMethod(input, SlashCommandStringOption);
}
@@ -103,15 +103,15 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
*/
public addIntegerOption(
input:
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoices'>
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| SlashCommandIntegerOption
| ((
builder: SlashCommandIntegerOption,
) =>
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'addChoices'>
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoices'>),
| SlashCommandIntegerOption),
) {
return this._sharedAddOptionMethod(input, SlashCommandIntegerOption);
}
@@ -123,25 +123,25 @@ export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
*/
public addNumberOption(
input:
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoices'>
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| SlashCommandNumberOption
| ((
builder: SlashCommandNumberOption,
) =>
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'addChoices'>
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoices'>),
| SlashCommandNumberOption),
) {
return this._sharedAddOptionMethod(input, SlashCommandNumberOption);
}
private _sharedAddOptionMethod<T extends ApplicationCommandOptionBase>(
input:
| T
| Omit<T, 'setAutocomplete'>
| Omit<T, 'addChoices'>
| ((builder: T) => T | Omit<T, 'setAutocomplete'> | Omit<T, 'addChoices'>),
| Omit<T, 'setAutocomplete'>
| T
| ((builder: T) => Omit<T, 'addChoices'> | Omit<T, 'setAutocomplete'> | T),
Instance: new () => T,
): ShouldOmitSubcommandFunctions extends true ? Omit<this, 'addSubcommand' | 'addSubcommandGroup'> : this {
const { options } = this;

View File

@@ -1,5 +1,5 @@
import { APIApplicationCommandAttachmentOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionType, type APIApplicationCommandAttachmentOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
export class SlashCommandAttachmentOption extends ApplicationCommandOptionBase {
public override readonly type = ApplicationCommandOptionType.Attachment as const;

View File

@@ -1,5 +1,5 @@
import { APIApplicationCommandBooleanOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionType, type APIApplicationCommandBooleanOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
export class SlashCommandBooleanOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Boolean as const;

View File

@@ -1,7 +1,7 @@
import { APIApplicationCommandChannelOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionType, type APIApplicationCommandChannelOption } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin.js';
@mix(ApplicationCommandOptionChannelTypesMixin)
export class SlashCommandChannelOption extends ApplicationCommandOptionBase {

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { APIApplicationCommandIntegerOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionType, type APIApplicationCommandIntegerOption } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
const numberValidator = s.number.int;

View File

@@ -1,5 +1,5 @@
import { APIApplicationCommandMentionableOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionType, type APIApplicationCommandMentionableOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
export class SlashCommandMentionableOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Mentionable as const;

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { APIApplicationCommandNumberOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionType, type APIApplicationCommandNumberOption } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
const numberValidator = s.number;

View File

@@ -1,5 +1,5 @@
import { APIApplicationCommandRoleOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionType, type APIApplicationCommandRoleOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
export class SlashCommandRoleOption extends ApplicationCommandOptionBase {
public override readonly type = ApplicationCommandOptionType.Role as const;

View File

@@ -1,16 +1,18 @@
import { s } from '@sapphire/shapeshift';
import { APIApplicationCommandStringOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionType, type APIApplicationCommandStringOption } from 'discord-api-types/v10';
import { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
const minLengthValidator = s.number.greaterThanOrEqual(0).lessThanOrEqual(6000);
const maxLengthValidator = s.number.greaterThanOrEqual(1).lessThanOrEqual(6000);
const minLengthValidator = s.number.greaterThanOrEqual(0).lessThanOrEqual(6_000);
const maxLengthValidator = s.number.greaterThanOrEqual(1).lessThanOrEqual(6_000);
@mix(ApplicationCommandOptionWithChoicesAndAutocompleteMixin)
export class SlashCommandStringOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.String as const;
public readonly max_length?: number;
public readonly min_length?: number;
/**

View File

@@ -1,5 +1,5 @@
import { APIApplicationCommandUserOption, ApplicationCommandOptionType } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionType, type APIApplicationCommandUserOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
export class SlashCommandUserOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.User as const;

View File

@@ -1,6 +1,6 @@
import { s } from '@sapphire/shapeshift';
import type { APIEmbedField } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation';
import { isValidationEnabled } from '../../util/validation.js';
export const fieldNamePredicate = s.string
.lengthGreaterThanOrEqual(1)
@@ -9,7 +9,7 @@ export const fieldNamePredicate = s.string
export const fieldValuePredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(1024)
.lengthLessThanOrEqual(1_024)
.setValidationEnabled(isValidationEnabled);
export const fieldInlinePredicate = s.boolean.optional;
@@ -64,12 +64,12 @@ export const colorPredicate = s.number.int
export const descriptionPredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(4096)
.lengthLessThanOrEqual(4_096)
.nullable.setValidationEnabled(isValidationEnabled);
export const footerTextPredicate = s.string
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(2048)
.lengthLessThanOrEqual(2_048)
.nullable.setValidationEnabled(isValidationEnabled);
export const embedFooterPredicate = s

View File

@@ -1,4 +1,5 @@
import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import {
colorPredicate,
descriptionPredicate,
@@ -10,8 +11,7 @@ import {
titlePredicate,
urlPredicate,
validateFieldLength,
} from './Assertions';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
} from './Assertions.js';
export type RGBTuple = [red: number, green: number, blue: number];
@@ -26,11 +26,11 @@ export interface IconData {
proxyIconURL?: string;
}
export type EmbedAuthorData = Omit<APIEmbedAuthor, 'icon_url' | 'proxy_icon_url'> & IconData;
export type EmbedAuthorData = IconData & Omit<APIEmbedAuthor, 'icon_url' | 'proxy_icon_url'>;
export type EmbedAuthorOptions = Omit<EmbedAuthorData, 'proxyIconURL'>;
export type EmbedFooterData = Omit<APIEmbedFooter, 'icon_url' | 'proxy_icon_url'> & IconData;
export type EmbedFooterData = IconData & Omit<APIEmbedFooter, 'icon_url' | 'proxy_icon_url'>;
export type EmbedFooterOptions = Omit<EmbedFooterData, 'proxyIconURL'>;
@@ -57,7 +57,6 @@ export class EmbedBuilder {
* @remarks
* This method accepts either an array of fields or a variable number of field parameters.
* The maximum amount of fields that can be added is 25.
*
* @example
* Using an array
* ```ts
@@ -65,7 +64,6 @@ export class EmbedBuilder {
* const embed = new EmbedBuilder()
* .addFields(fields);
* ```
*
* @example
* Using rest parameters (variadic)
* ```ts
@@ -75,10 +73,10 @@ export class EmbedBuilder {
* { name: 'Field 2', value: 'Value 2' },
* );
* ```
*
* @param fields - The fields to add
*/
public addFields(...fields: RestOrArray<APIEmbedField>): this {
// eslint-disable-next-line no-param-reassign
fields = normalizeArray(fields);
// Ensure adding these fields won't exceed the 25 field limit
validateFieldLength(fields.length, this.data.fields);
@@ -100,26 +98,22 @@ export class EmbedBuilder {
* The maximum amount of fields that can be added is 25.
*
* It's useful for modifying and adjusting order of the already-existing fields of an embed.
*
* @example
* Remove the first field
* ```ts
* embed.spliceFields(0, 1);
* ```
*
* @example
* Remove the first n fields
* ```ts
* const n = 4
* embed.spliceFields(0, n);
* ```
*
* @example
* Remove the last field
* ```ts
* embed.spliceFields(-1, 1);
* ```
*
* @param index - The index to start at
* @param deleteCount - The number of fields to remove
* @param fields - The replacing field objects
@@ -143,7 +137,6 @@ export class EmbedBuilder {
* it splices the entire array of fields, replacing them with the provided fields.
*
* You can set a maximum of 25 fields.
*
* @param fields - The fields to set
*/
public setFields(...fields: RestOrArray<APIEmbedField>) {
@@ -175,7 +168,7 @@ export class EmbedBuilder {
*
* @param color - The color of the embed
*/
public setColor(color: number | RGBTuple | null): this {
public setColor(color: RGBTuple | number | null): this {
// Data assertions
colorPredicate.parse(color);
@@ -184,6 +177,7 @@ export class EmbedBuilder {
this.data.color = (red << 16) + (green << 8) + blue;
return this;
}
this.data.color = color ?? undefined;
return this;
}
@@ -250,7 +244,7 @@ export class EmbedBuilder {
*
* @param timestamp - The timestamp or date
*/
public setTimestamp(timestamp: number | Date | null = Date.now()): this {
public setTimestamp(timestamp: Date | number | null = Date.now()): this {
// Data assertions
timestampPredicate.parse(timestamp);

View File

@@ -1,4 +1,4 @@
import type { URL } from 'url';
import type { URL } from 'node:url';
import type { Snowflake } from 'discord-api-types/globals';
/**
@@ -95,7 +95,7 @@ export function hideLinkEmbed<C extends string>(url: C): `<${C}>`;
* @param url - The URL to wrap
*/
export function hideLinkEmbed(url: URL): `<${string}>`;
export function hideLinkEmbed(url: string | URL) {
export function hideLinkEmbed(url: URL | string) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `<${url}>`;
}
@@ -141,7 +141,7 @@ export function hyperlink<C extends string, U extends string, T extends string>(
url: U,
title: T,
): `[${C}](${U} "${T}")`;
export function hyperlink(content: string, url: string | URL, title?: string) {
export function hyperlink(content: string, url: URL | string, title?: string) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return title ? `[${content}](${url} "${title}")` : `[${content}](${url})`;
}
@@ -203,7 +203,7 @@ export function formatEmoji<C extends Snowflake>(emojiId: C, animated?: true): `
* @param emojiId - The emoji ID to format
* @param animated - Whether the emoji is animated or not. Defaults to `false`
*/
export function formatEmoji<C extends Snowflake>(emojiId: C, animated = false): `<a:_:${C}>` | `<:_:${C}>` {
export function formatEmoji<C extends Snowflake>(emojiId: C, animated = false): `<:_:${C}>` | `<a:_:${C}>` {
return `<${animated ? 'a' : ''}:_:${emojiId}>`;
}
@@ -293,9 +293,10 @@ export function time<C extends number>(seconds: C): `<t:${C}>`;
* @param style - The style to use
*/
export function time<C extends number, S extends TimestampStylesString>(seconds: C, style: S): `<t:${C}:${S}>`;
export function time(timeOrSeconds?: number | Date, style?: TimestampStylesString): string {
export function time(timeOrSeconds?: Date | number, style?: TimestampStylesString): string {
if (typeof timeOrSeconds !== 'number') {
timeOrSeconds = Math.floor((timeOrSeconds?.getTime() ?? Date.now()) / 1000);
// eslint-disable-next-line no-param-reassign
timeOrSeconds = Math.floor((timeOrSeconds?.getTime() ?? Date.now()) / 1_000);
}
return typeof style === 'string' ? `<t:${timeOrSeconds}:${style}>` : `<t:${timeOrSeconds}>`;

View File

@@ -8,11 +8,12 @@ export interface Equatable<T> {
/**
* Whether or not this is equal to another structure
*/
equals: (other: T) => boolean;
equals(other: T): boolean;
}
/**
* Indicates if an object is equatable or not.
*
* @param maybeEquatable - The object to check against
*/
export function isEquatable(maybeEquatable: unknown): maybeEquatable is Equatable<unknown> {

View File

@@ -7,11 +7,12 @@ export interface JSONEncodable<T> {
/**
* Transforms this object to its JSON format
*/
toJSON: () => T;
toJSON(): T;
}
/**
* Indicates if an object is encodable or not.
*
* @param maybeEncodable - The object to check against
*/
export function isJSONEncodable(maybeEncodable: unknown): maybeEncodable is JSONEncodable<unknown> {