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,9 +1,12 @@
{ {
"root": true, "root": true,
"extends": "marine/prettier/node", "extends": ["neon/common", "neon/node", "neon/typescript", "neon/prettier"],
"parserOptions": { "parserOptions": {
"project": "./tsconfig.eslint.json" "project": "./tsconfig.eslint.json"
}, },
"rules": {
"@typescript-eslint/consistent-type-definitions": ["error", "interface"]
},
"ignorePatterns": ["**/dist/*"], "ignorePatterns": ["**/dist/*"],
"env": { "env": {
"jest": true "jest": true

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { formatTag } from '../src'; import { formatTag } from '../src/index.js';
describe('Format Tag', () => { describe('Format Tag', () => {
test('GIVEN tag with a prefix THEN format tag to not contain the prefix', () => { test('GIVEN tag with a prefix THEN format tag to not contain the prefix', () => {

View File

@@ -44,14 +44,9 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"@vitest/coverage-c8": "^0.22.1", "@vitest/coverage-c8": "^0.22.1",
"eslint": "^8.23.0", "eslint": "^8.23.0",
"eslint-config-marine": "^9.4.1", "eslint-config-neon": "^0.1.23",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.0",
"eslint-plugin-import": "^2.26.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rollup-plugin-typescript2": "^0.33.0", "rollup-plugin-typescript2": "^0.33.0",
"typescript": "^4.8.2", "typescript": "^4.8.2",

View File

@@ -1,4 +1,5 @@
export function formatTag(tag: string) { export function formatTag(tag: string) {
// eslint-disable-next-line unicorn/no-unsafe-regex, prefer-named-capture-group
const parsed = /(^@.*\/(?<package>.*)@v?)?(?<semver>\d+.\d+.\d+)-?.*/.exec(tag); const parsed = /(^@.*\/(?<package>.*)@v?)?(?<semver>\d+.\d+.\d+)-?.*/.exec(tag);
if (parsed?.groups) { if (parsed?.groups) {

View File

@@ -1,5 +1,5 @@
import { getInput, setOutput } from '@actions/core'; import { getInput, setOutput } from '@actions/core';
import { formatTag } from './formatTag'; import { formatTag } from './formatTag.js';
const tag = getInput('tag', { required: true }); const tag = getInput('tag', { required: true });
const parsed = formatTag(tag); const parsed = formatTag(tag);

View File

@@ -1 +1 @@
export * from './formatTag/formatTag'; export * from './formatTag/formatTag.js';

View File

@@ -35,14 +35,8 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"eslint": "^8.23.0", "eslint": "^8.23.0",
"eslint-config-marine": "^9.4.1", "eslint-config-neon": "^0.1.23",
"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",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rollup-plugin-typescript2": "^0.33.0", "rollup-plugin-typescript2": "^0.33.0",
"typescript": "^4.8.2", "typescript": "^4.8.2",

View File

@@ -18,12 +18,12 @@ import {
type ApiConstructor, type ApiConstructor,
type ApiItemContainerMixin, type ApiItemContainerMixin,
} from '@microsoft/api-extractor-model'; } from '@microsoft/api-extractor-model';
import { generateTypeParamData } from './TypeParameterJSONEncoder'; import { generateTypeParamData } from './TypeParameterJSONEncoder.js';
import { type TokenDocumentation, resolveName, genReference, genToken, genParameter, generatePath } from './parse'; import { type TokenDocumentation, resolveName, genReference, genToken, genParameter, generatePath } from './parse.js';
import { createCommentNode } from './tsdoc';
import type { DocBlockJSON } from './tsdoc/CommentBlock'; import type { DocBlockJSON } from './tsdoc/CommentBlock';
import type { AnyDocNodeJSON } from './tsdoc/CommentNode'; import type { AnyDocNodeJSON } from './tsdoc/CommentNode';
import { type DocNodeContainerJSON, nodeContainer } from './tsdoc/CommentNodeContainer'; import { type DocNodeContainerJSON, nodeContainer } from './tsdoc/CommentNodeContainer.js';
import { createCommentNode } from './tsdoc/index.js';
export interface ReferenceData { export interface ReferenceData {
name: string; name: string;
@@ -31,9 +31,9 @@ export interface ReferenceData {
} }
export interface InheritanceData { export interface InheritanceData {
parentKey: string;
parentName: string; parentName: string;
path: string; path: string;
parentKey: string;
} }
export interface ApiInheritableJSON { export interface ApiInheritableJSON {
@@ -41,23 +41,23 @@ export interface ApiInheritableJSON {
} }
export interface ApiItemJSON { export interface ApiItemJSON {
kind: string;
name: string;
referenceData: ReferenceData;
excerpt: string;
excerptTokens: TokenDocumentation[];
remarks: DocNodeContainerJSON | null;
summary: DocNodeContainerJSON | null;
deprecated: DocNodeContainerJSON | null;
comment: AnyDocNodeJSON | null; comment: AnyDocNodeJSON | null;
containerKey: string; containerKey: string;
deprecated: DocNodeContainerJSON | null;
excerpt: string;
excerptTokens: TokenDocumentation[];
kind: string;
name: string;
path: string[]; path: string[];
referenceData: ReferenceData;
remarks: DocNodeContainerJSON | null;
summary: DocNodeContainerJSON | null;
} }
export interface ApiPropertyItemJSON extends ApiItemJSON, ApiInheritableJSON { export interface ApiPropertyItemJSON extends ApiItemJSON, ApiInheritableJSON {
optional: boolean;
propertyTypeTokens: TokenDocumentation[]; propertyTypeTokens: TokenDocumentation[];
readonly: boolean; readonly: boolean;
optional: boolean;
} }
export interface ApiTypeParameterListJSON { export interface ApiTypeParameterListJSON {
@@ -65,11 +65,11 @@ export interface ApiTypeParameterListJSON {
} }
export interface ApiTypeParameterJSON { export interface ApiTypeParameterJSON {
name: string; commentBlock: DocBlockJSON | null;
constraintTokens: TokenDocumentation[]; constraintTokens: TokenDocumentation[];
defaultTokens: TokenDocumentation[]; defaultTokens: TokenDocumentation[];
name: string;
optional: boolean; optional: boolean;
commentBlock: DocBlockJSON | null;
} }
export interface ApiParameterListJSON { export interface ApiParameterListJSON {
@@ -81,29 +81,29 @@ export interface ApiMethodSignatureJSON
ApiTypeParameterListJSON, ApiTypeParameterListJSON,
ApiParameterListJSON, ApiParameterListJSON,
ApiInheritableJSON { ApiInheritableJSON {
returnTypeTokens: TokenDocumentation[];
optional: boolean; optional: boolean;
overloadIndex: number; overloadIndex: number;
returnTypeTokens: TokenDocumentation[];
} }
export interface ApiMethodJSON extends ApiMethodSignatureJSON { export interface ApiMethodJSON extends ApiMethodSignatureJSON {
static: boolean;
protected: boolean; protected: boolean;
static: boolean;
} }
export interface ApiParameterJSON { export interface ApiParameterJSON {
name: string;
isOptional: boolean; isOptional: boolean;
tokens: TokenDocumentation[]; name: string;
paramCommentBlock: DocBlockJSON | null; paramCommentBlock: DocBlockJSON | null;
tokens: TokenDocumentation[];
} }
export interface ApiClassJSON extends ApiItemJSON, ApiTypeParameterListJSON { export interface ApiClassJSON extends ApiItemJSON, ApiTypeParameterListJSON {
constructor: ApiConstructorJSON | null; constructor: ApiConstructorJSON | null;
properties: ApiPropertyItemJSON[];
methods: ApiMethodJSON[];
extendsTokens: TokenDocumentation[]; extendsTokens: TokenDocumentation[];
implementsTokens: TokenDocumentation[][]; implementsTokens: TokenDocumentation[][];
methods: ApiMethodJSON[];
properties: ApiPropertyItemJSON[];
} }
export interface ApiTypeAliasJSON extends ApiItemJSON, ApiTypeParameterListJSON { export interface ApiTypeAliasJSON extends ApiItemJSON, ApiTypeParameterListJSON {
@@ -111,8 +111,8 @@ export interface ApiTypeAliasJSON extends ApiItemJSON, ApiTypeParameterListJSON
} }
export interface EnumMemberData { export interface EnumMemberData {
name: string;
initializerTokens: TokenDocumentation[]; initializerTokens: TokenDocumentation[];
name: string;
summary: DocNodeContainerJSON | null; summary: DocNodeContainerJSON | null;
} }
@@ -121,19 +121,19 @@ export interface ApiEnumJSON extends ApiItemJSON {
} }
export interface ApiInterfaceJSON extends ApiItemJSON, ApiTypeParameterListJSON { export interface ApiInterfaceJSON extends ApiItemJSON, ApiTypeParameterListJSON {
properties: ApiPropertyItemJSON[];
methods: ApiMethodSignatureJSON[];
extendsTokens: TokenDocumentation[][] | null; extendsTokens: TokenDocumentation[][] | null;
methods: ApiMethodSignatureJSON[];
properties: ApiPropertyItemJSON[];
} }
export interface ApiVariableJSON extends ApiItemJSON { export interface ApiVariableJSON extends ApiItemJSON {
typeTokens: TokenDocumentation[];
readonly: boolean; readonly: boolean;
typeTokens: TokenDocumentation[];
} }
export interface ApiFunctionJSON extends ApiItemJSON, ApiTypeParameterListJSON, ApiParameterListJSON { export interface ApiFunctionJSON extends ApiItemJSON, ApiTypeParameterListJSON, ApiParameterListJSON {
returnTypeTokens: TokenDocumentation[];
overloadIndex: number; overloadIndex: number;
returnTypeTokens: TokenDocumentation[];
} }
export interface ApiConstructorJSON extends ApiItemJSON, ApiParameterListJSON { export interface ApiConstructorJSON extends ApiItemJSON, ApiParameterListJSON {
@@ -203,7 +203,7 @@ export class ApiNodeJSONEncoder {
public static encodeParameterList( public static encodeParameterList(
model: ApiModel, model: ApiModel,
item: ApiParameterListMixin & ApiDeclaredItem, item: ApiDeclaredItem & ApiParameterListMixin,
version: string, version: string,
): { parameters: ApiParameterJSON[] } { ): { parameters: ApiParameterJSON[] } {
return { return {
@@ -213,7 +213,7 @@ export class ApiNodeJSONEncoder {
public static encodeTypeParameterList( public static encodeTypeParameterList(
model: ApiModel, model: ApiModel,
item: ApiTypeParameterListMixin & ApiDeclaredItem, item: ApiDeclaredItem & ApiTypeParameterListMixin,
version: string, version: string,
): ApiTypeParameterListJSON { ): ApiTypeParameterListJSON {
return { return {

View File

@@ -1,13 +1,13 @@
import type { TypeParameter, ApiModel, ApiItem } from '@microsoft/api-extractor-model'; import type { TypeParameter, ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import { type TokenDocumentation, genToken } from './parse'; import { type TokenDocumentation, genToken } from './parse.js';
import { type DocBlockJSON, block } from './tsdoc/CommentBlock'; import { type DocBlockJSON, block } from './tsdoc/CommentBlock.js';
export interface TypeParameterData { export interface TypeParameterData {
name: string; commentBlock: DocBlockJSON | null;
constraintTokens: TokenDocumentation[]; constraintTokens: TokenDocumentation[];
defaultTokens: TokenDocumentation[]; defaultTokens: TokenDocumentation[];
name: string;
optional: boolean; optional: boolean;
commentBlock: DocBlockJSON | null;
} }
export function generateTypeParamData( export function generateTypeParamData(

View File

@@ -1,4 +1,4 @@
export * from './ApiNodeJSONEncoder'; export * from './ApiNodeJSONEncoder.js';
export * from './parse'; export * from './parse.js';
export * from './tsdoc'; export * from './tsdoc/index.js';
export * from './TypeParameterJSONEncoder'; export * from './TypeParameterJSONEncoder.js';

View File

@@ -14,8 +14,8 @@ import {
} from '@microsoft/api-extractor-model'; } from '@microsoft/api-extractor-model';
import type { DocNode, DocParagraph, DocPlainText } from '@microsoft/tsdoc'; import type { DocNode, DocParagraph, DocPlainText } from '@microsoft/tsdoc';
import { type Meaning, ModuleSource } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; import { type Meaning, ModuleSource } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference';
import { createCommentNode } from './tsdoc'; import type { DocBlockJSON } from './tsdoc/CommentBlock.js';
import type { DocBlockJSON } from './tsdoc/CommentBlock'; import { createCommentNode } from './tsdoc/index.js';
export function findPackage(model: ApiModel, name: string): ApiPackage | undefined { export function findPackage(model: ApiModel, name: string): ApiPackage | undefined {
return (model.findMembersByName(name)[0] ?? model.findMembersByName(`@discordjs/${name}`)[0]) as return (model.findMembersByName(name)[0] ?? model.findMembersByName(`@discordjs/${name}`)[0]) as
@@ -54,6 +54,7 @@ export function generatePath(items: readonly ApiItem[], version: string) {
} }
} }
// eslint-disable-next-line prefer-named-capture-group, unicorn/no-unsafe-regex
return path.replace(/@discordjs\/(.*)\/(.*)?/, `$1/${version}/$2`); return path.replace(/@discordjs\/(.*)\/(.*)?/, `$1/${version}/$2`);
} }
@@ -70,26 +71,22 @@ export function resolveDocComment(item: ApiDocumentedItem) {
const { summarySection } = tsdocComment; const { summarySection } = tsdocComment;
function recurseNodes(nodes: readonly DocNode[] | undefined): string | null { function recurseNodes(node: DocNode | undefined): string | null {
if (!nodes) { if (!node) {
return null; return null;
} }
for (const node of nodes) { switch (node.kind) {
switch (node.kind) { case 'Paragraph':
case 'Paragraph': return recurseNodes(node as DocParagraph);
return recurseNodes((node as DocParagraph).nodes); case 'PlainText':
case 'PlainText': return (node as DocPlainText).text;
return (node as DocPlainText).text; default:
default: return null;
return null;
}
} }
return null;
} }
return recurseNodes(summarySection.nodes); return recurseNodes(summarySection);
} }
export function findReferences(model: ApiModel, excerpt: Excerpt) { export function findReferences(model: ApiModel, excerpt: Excerpt) {
@@ -107,6 +104,7 @@ export function findReferences(model: ApiModel, excerpt: Excerpt) {
break; break;
} }
default: default:
break; break;
} }
@@ -142,16 +140,16 @@ export function getProperties(item: ApiItem) {
} }
export interface TokenDocumentation { export interface TokenDocumentation {
text: string;
path: string | null;
kind: string; kind: string;
path: string | null;
text: string;
} }
export interface ParameterDocumentation { export interface ParameterDocumentation {
name: string;
isOptional: boolean; isOptional: boolean;
tokens: TokenDocumentation[]; name: string;
paramCommentBlock: DocBlockJSON | null; paramCommentBlock: DocBlockJSON | null;
tokens: TokenDocumentation[];
} }
function createDapiTypesURL(meaning: Meaning, name: string) { function createDapiTypesURL(meaning: Meaning, name: string) {
@@ -174,7 +172,7 @@ export function genReference(item: ApiItem, version: string) {
export function genToken(model: ApiModel, token: ExcerptToken, version: string) { export function genToken(model: ApiModel, token: ExcerptToken, version: string) {
if (token.canonicalReference) { if (token.canonicalReference) {
// @ts-expect-error // @ts-expect-error: Symbol is not publicly accessible
token.canonicalReference._navigation = '.'; token.canonicalReference._navigation = '.';
} }

View File

@@ -1,8 +1,8 @@
import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model'; import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import type { DocBlock } from '@microsoft/tsdoc'; import type { DocBlock } from '@microsoft/tsdoc';
import { blockTag, type DocBlockTagJSON } from './CommentBlockTag.js';
import { type AnyDocNodeJSON, type DocNodeJSON, node } from './CommentNode.js';
import { createCommentNode } from '.'; import { createCommentNode } from '.';
import { blockTag, type DocBlockTagJSON } from './CommentBlockTag';
import { type AnyDocNodeJSON, type DocNodeJSON, node } from './CommentNode';
export interface DocBlockJSON extends DocNodeJSON { export interface DocBlockJSON extends DocNodeJSON {
content: AnyDocNodeJSON[]; content: AnyDocNodeJSON[];

View File

@@ -1,5 +1,5 @@
import type { DocBlockTag } from '@microsoft/tsdoc'; import type { DocBlockTag } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode'; import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocBlockTagJSON extends DocNodeJSON { export interface DocBlockTagJSON extends DocNodeJSON {
tagName: string; tagName: string;

View File

@@ -1,5 +1,5 @@
import type { DocCodeSpan } from '@microsoft/tsdoc'; import type { DocCodeSpan } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode'; import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocCodeSpanJSON extends DocNodeJSON { export interface DocCodeSpanJSON extends DocNodeJSON {
code: string; code: string;

View File

@@ -12,14 +12,14 @@ export interface DocNodeJSON {
} }
export type AnyDocNodeJSON = export type AnyDocNodeJSON =
| DocNodeJSON
| DocPlainTextJSON
| DocNodeContainerJSON
| DocLinkTagJSON
| DocFencedCodeJSON
| DocBlockJSON | DocBlockJSON
| DocCodeSpanJSON
| DocCommentJSON | DocCommentJSON
| DocCodeSpanJSON; | DocFencedCodeJSON
| DocLinkTagJSON
| DocNodeContainerJSON
| DocNodeJSON
| DocPlainTextJSON;
export function node(node: DocNode): DocNodeJSON { export function node(node: DocNode): DocNodeJSON {
return { return {

View File

@@ -1,7 +1,7 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocNodeContainer } from '@microsoft/tsdoc'; import type { DocNodeContainer } from '@microsoft/tsdoc';
import { type AnyDocNodeJSON, type DocNodeJSON, node } from './CommentNode.js';
import { createCommentNode } from '.'; import { createCommentNode } from '.';
import { type AnyDocNodeJSON, type DocNodeJSON, node } from './CommentNode';
export interface DocNodeContainerJSON extends DocNodeJSON { export interface DocNodeContainerJSON extends DocNodeJSON {
nodes: AnyDocNodeJSON[]; nodes: AnyDocNodeJSON[];

View File

@@ -1,5 +1,5 @@
import type { DocFencedCode } from '@microsoft/tsdoc'; import type { DocFencedCode } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode'; import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocFencedCodeJSON extends DocNodeJSON { export interface DocFencedCodeJSON extends DocNodeJSON {
code: string; code: string;

View File

@@ -1,17 +1,17 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocDeclarationReference, DocLinkTag } from '@microsoft/tsdoc'; import type { DocDeclarationReference, DocLinkTag } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode'; import { resolveName, generatePath } from '../parse.js';
import { resolveName, generatePath } from '../parse'; import { type DocNodeJSON, node } from './CommentNode.js';
interface LinkTagCodeLink { interface LinkTagCodeLink {
name: string;
kind: string; kind: string;
name: string;
path: string; path: string;
} }
export interface DocLinkTagJSON extends DocNodeJSON { export interface DocLinkTagJSON extends DocNodeJSON {
text: string | null;
codeDestination: LinkTagCodeLink | null; codeDestination: LinkTagCodeLink | null;
text: string | null;
urlDestination: string | null; urlDestination: string | null;
} }

View File

@@ -1,6 +1,6 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocParamBlock } from '@microsoft/tsdoc'; import type { DocParamBlock } from '@microsoft/tsdoc';
import { block, type DocBlockJSON } from './CommentBlock'; import { block, type DocBlockJSON } from './CommentBlock.js';
interface DocParamBlockJSON extends DocBlockJSON { interface DocParamBlockJSON extends DocBlockJSON {
name: string; name: string;

View File

@@ -1,5 +1,5 @@
import type { DocPlainText } from '@microsoft/tsdoc'; import type { DocPlainText } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode'; import { type DocNodeJSON, node } from './CommentNode.js';
export interface DocPlainTextJSON extends DocNodeJSON { export interface DocPlainTextJSON extends DocNodeJSON {
text: string; text: string;

View File

@@ -1,14 +1,14 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocComment } from '@microsoft/tsdoc'; import type { DocComment } from '@microsoft/tsdoc';
import { block, type DocBlockJSON } from './CommentBlock.js';
import { type DocNodeJSON, node } from './CommentNode.js';
import { createCommentNode } from '.'; import { createCommentNode } from '.';
import { block, type DocBlockJSON } from './CommentBlock';
import { type DocNodeJSON, node } from './CommentNode';
export interface DocCommentJSON extends DocNodeJSON { export interface DocCommentJSON extends DocNodeJSON {
summary: DocNodeJSON[];
remarks: DocNodeJSON[];
deprecated: DocNodeJSON[];
customBlocks: DocBlockJSON[]; customBlocks: DocBlockJSON[];
deprecated: DocNodeJSON[];
remarks: DocNodeJSON[];
summary: DocNodeJSON[];
} }
export function comment(comment: DocComment, model: ApiModel, version: string, parentItem?: ApiItem): DocCommentJSON { export function comment(comment: DocComment, model: ApiModel, version: string, parentItem?: ApiItem): DocCommentJSON {

View File

@@ -11,16 +11,15 @@ import {
type DocCodeSpan, type DocCodeSpan,
type DocParamBlock, type DocParamBlock,
} from '@microsoft/tsdoc'; } from '@microsoft/tsdoc';
import { block } from './CommentBlock'; import { block } from './CommentBlock.js';
import { codeSpan } from './CommentCodeSpan'; import { codeSpan } from './CommentCodeSpan.js';
import type { AnyDocNodeJSON } from './CommentNode'; import { node as _node, type AnyDocNodeJSON } from './CommentNode.js';
import { node as _node } from './CommentNode'; import { nodeContainer } from './CommentNodeContainer.js';
import { nodeContainer } from './CommentNodeContainer'; import { fencedCode } from './FencedCodeCommentNode.js';
import { fencedCode } from './FencedCodeCommentNode'; import { linkTagNode } from './LinkTagCommentNode.js';
import { linkTagNode } from './LinkTagCommentNode'; import { paramBlock } from './ParamBlock.js';
import { paramBlock } from './ParamBlock'; import { plainTextNode } from './PlainTextCommentNode.js';
import { plainTextNode } from './PlainTextCommentNode'; import { comment } from './RootComment.js';
import { comment } from './RootComment';
export function createCommentNode( export function createCommentNode(
node: DocNode, node: DocNode,
@@ -51,13 +50,13 @@ export function createCommentNode(
} }
} }
export * from './CommentNode'; export * from './CommentNode.js';
export * from './CommentNodeContainer'; export * from './CommentNodeContainer.js';
export * from './CommentBlock'; export * from './CommentBlock.js';
export * from './CommentBlockTag'; export * from './CommentBlockTag.js';
export * from './CommentCodeSpan'; export * from './CommentCodeSpan.js';
export * from './FencedCodeCommentNode'; export * from './FencedCodeCommentNode.js';
export * from './LinkTagCommentNode'; export * from './LinkTagCommentNode.js';
export * from './ParamBlock'; export * from './ParamBlock.js';
export * from './PlainTextCommentNode'; export * from './PlainTextCommentNode.js';
export * from './RootComment'; export * from './RootComment.js';

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 { describe, test, expect } from 'vitest';
import { import {
ActionRowBuilder, ActionRowBuilder,
@@ -6,7 +11,7 @@ import {
createComponentBuilder, createComponentBuilder,
SelectMenuBuilder, SelectMenuBuilder,
SelectMenuOptionBuilder, SelectMenuOptionBuilder,
} from '../../src'; } from '../../src/index.js';
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = { const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
type: ComponentType.ActionRow, type: ComponentType.ActionRow,

View File

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

View File

@@ -1,12 +1,12 @@
import { import {
APIActionRowComponent,
APIButtonComponent,
APIMessageActionRowComponent,
APISelectMenuComponent,
APITextInputComponent,
ButtonStyle, ButtonStyle,
ComponentType, ComponentType,
TextInputStyle, TextInputStyle,
type APIButtonComponent,
type APIMessageActionRowComponent,
type APISelectMenuComponent,
type APITextInputComponent,
type APIActionRowComponent,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { import {
@@ -15,7 +15,7 @@ import {
createComponentBuilder, createComponentBuilder,
SelectMenuBuilder, SelectMenuBuilder,
TextInputBuilder, TextInputBuilder,
} from '../../src/index'; } from '../../src/index.js';
describe('createComponentBuilder', () => { describe('createComponentBuilder', () => {
test.each([ButtonBuilder, SelectMenuBuilder, TextInputBuilder])( test.each([ButtonBuilder, SelectMenuBuilder, TextInputBuilder])(
@@ -67,7 +67,7 @@ describe('createComponentBuilder', () => {
}); });
test('GIVEN an unknown component type THEN throws error', () => { test('GIVEN an unknown component type THEN throws error', () => {
// @ts-expect-error // @ts-expect-error: Unknown component type
expect(() => createComponentBuilder({ type: 'invalid' })).toThrowError(); 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 { describe, test, expect } from 'vitest';
import { SelectMenuBuilder, SelectMenuOptionBuilder } from '../../src/index'; import { SelectMenuBuilder, SelectMenuOptionBuilder } from '../../src/index.js';
const selectMenu = () => new SelectMenuBuilder(); const selectMenu = () => new SelectMenuBuilder();
const selectMenuOption = () => new SelectMenuOptionBuilder(); const selectMenuOption = () => new SelectMenuOptionBuilder();
@@ -74,7 +74,8 @@ describe('Select Menu Components', () => {
]), ]),
).not.toThrowError(); ).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().addOptions(...options)).not.toThrowError();
expect(() => selectMenu().setOptions(...options)).not.toThrowError(); expect(() => selectMenu().setOptions(...options)).not.toThrowError();
expect(() => selectMenu().addOptions(options)).not.toThrowError(); expect(() => selectMenu().addOptions(options)).not.toThrowError();
@@ -83,12 +84,13 @@ describe('Select Menu Components', () => {
expect(() => expect(() =>
selectMenu() selectMenu()
.addOptions({ label: 'test', value: 'test' }) .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(); ).not.toThrowError();
expect(() => expect(() =>
selectMenu() selectMenu()
.addOptions([{ label: 'test', value: 'test' }]) .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(); ).not.toThrowError();
}); });
@@ -96,33 +98,34 @@ describe('Select Menu Components', () => {
expect(() => selectMenu().setCustomId(longStr)).toThrowError(); expect(() => selectMenu().setCustomId(longStr)).toThrowError();
expect(() => selectMenu().setMaxValues(30)).toThrowError(); expect(() => selectMenu().setMaxValues(30)).toThrowError();
expect(() => selectMenu().setMinValues(-20)).toThrowError(); expect(() => selectMenu().setMinValues(-20)).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid disabled value
expect(() => selectMenu().setDisabled(0)).toThrowError(); expect(() => selectMenu().setDisabled(0)).toThrowError();
expect(() => selectMenu().setPlaceholder(longStr)).toThrowError(); expect(() => selectMenu().setPlaceholder(longStr)).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ label: 'test' })).toThrowError(); expect(() => selectMenu().addOptions({ label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: longStr, value: 'test' })).toThrowError(); expect(() => selectMenu().addOptions({ label: longStr, value: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ value: longStr, label: 'test' })).toThrowError(); expect(() => selectMenu().addOptions({ value: longStr, label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', description: longStr })).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(); expect(() => selectMenu().addOptions({ label: 'test', value: 'test', default: 100 })).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ value: 'test' })).toThrowError(); expect(() => selectMenu().addOptions({ value: 'test' })).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid option
expect(() => selectMenu().addOptions({ default: true })).toThrowError(); expect(() => selectMenu().addOptions({ default: true })).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError(); expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError(); expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError(); expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', description: longStr }])).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(); expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', default: 100 }])).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ value: 'test' }])).toThrowError(); expect(() => selectMenu().addOptions([{ value: 'test' }])).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid option
expect(() => selectMenu().addOptions([{ default: true }])).toThrowError(); 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();
expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError(); expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError();
@@ -141,9 +144,9 @@ describe('Select Menu Components', () => {
selectMenuOption() selectMenuOption()
.setLabel(longStr) .setLabel(longStr)
.setValue(longStr) .setValue(longStr)
// @ts-expect-error // @ts-expect-error: invalid default value
.setDefault(-1) .setDefault(-1)
// @ts-expect-error // @ts-expect-error: invalid emoji
.setEmoji({ name: 1 }) .setEmoji({ name: 1 })
.setDescription(longStr); .setDescription(longStr);
}).toThrowError(); }).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 { describe, test, expect } from 'vitest';
import { import {
labelValidator, labelValidator,
@@ -7,10 +7,10 @@ import {
placeholderValidator, placeholderValidator,
valueValidator, valueValidator,
textInputStyleValidator, textInputStyleValidator,
} from '../../src/components/textInput/Assertions'; } from '../../src/components/textInput/Assertions.js';
import { TextInputBuilder } from '../../src/components/textInput/TextInput'; import { TextInputBuilder } from '../../src/components/textInput/TextInput.js';
const superLongStr = 'a'.repeat(5000); const superLongStr = 'a'.repeat(5_000);
const textInputComponent = () => new TextInputBuilder(); const textInputComponent = () => new TextInputBuilder();
@@ -47,7 +47,7 @@ describe('Text Input Components', () => {
}); });
test('GIVEN invalid min length THEN validator does throw 2', () => { 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', () => { test('GIVEN valid value THEN validator does not throw', () => {

View File

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

View File

@@ -1,15 +1,15 @@
import { import {
APIApplicationCommandAttachmentOption,
APIApplicationCommandBooleanOption,
APIApplicationCommandChannelOption,
APIApplicationCommandIntegerOption,
APIApplicationCommandMentionableOption,
APIApplicationCommandNumberOption,
APIApplicationCommandRoleOption,
APIApplicationCommandStringOption,
APIApplicationCommandUserOption,
ApplicationCommandOptionType, ApplicationCommandOptionType,
ChannelType, ChannelType,
type APIApplicationCommandAttachmentOption,
type APIApplicationCommandBooleanOption,
type APIApplicationCommandChannelOption,
type APIApplicationCommandIntegerOption,
type APIApplicationCommandMentionableOption,
type APIApplicationCommandNumberOption,
type APIApplicationCommandRoleOption,
type APIApplicationCommandStringOption,
type APIApplicationCommandUserOption,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { import {
@@ -22,7 +22,7 @@ import {
SlashCommandRoleOption, SlashCommandRoleOption,
SlashCommandStringOption, SlashCommandStringOption,
SlashCommandUserOption, SlashCommandUserOption,
} from '../../../src/index'; } from '../../../src/index.js';
const getBooleanOption = () => const getBooleanOption = () =>
new SlashCommandBooleanOption().setName('owo').setDescription('Testing 123').setRequired(true); 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 { describe, test, expect } from 'vitest';
import { import {
SlashCommandAssertions, SlashCommandAssertions,
@@ -14,7 +14,7 @@ import {
SlashCommandSubcommandBuilder, SlashCommandSubcommandBuilder,
SlashCommandSubcommandGroupBuilder, SlashCommandSubcommandGroupBuilder,
SlashCommandUserOption, SlashCommandUserOption,
} from '../../../src/index'; } from '../../../src/index.js';
const largeArray = Array.from({ length: 26 }, () => 1 as unknown as APIApplicationCommandOptionChoice); 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'); const getSubcommand = () => new SlashCommandSubcommandBuilder().setName('owo').setDescription('Testing 123');
class Collection { class Collection {
public get [Symbol.toStringTag]() { public readonly [Symbol.toStringTag] = 'Map';
return 'Map';
}
} }
describe('Slash Commands', () => { 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', () => { 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(); expect(() => getBuilder().addNumberOption(getNumberOption().setMaxValue('test'))).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid max value
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMaxValue('test'))).toThrowError(); expect(() => getBuilder().addIntegerOption(getIntegerOption().setMaxValue('test'))).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid min value
expect(() => getBuilder().addNumberOption(getNumberOption().setMinValue('test'))).toThrowError(); 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('test'))).toThrowError();
expect(() => getBuilder().addIntegerOption(getIntegerOption().setMinValue(1.5))).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', () => { test('GIVEN invalid name localizations THEN does throw error', () => {
// @ts-expect-error // @ts-expect-error: invalid localization
expect(() => getBuilder().setNameLocalization('en-U', 'foobar')).toThrowError(); expect(() => getBuilder().setNameLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid localization
expect(() => getBuilder().setNameLocalizations({ 'en-U': 'foobar' })).toThrowError(); expect(() => getBuilder().setNameLocalizations({ 'en-U': 'foobar' })).toThrowError();
}); });
@@ -467,9 +465,9 @@ describe('Slash Commands', () => {
}); });
test('GIVEN invalid description localizations THEN does throw error', () => { 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(); expect(() => getBuilder().setDescriptionLocalization('en-U', 'foobar')).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid localization description
expect(() => getBuilder().setDescriptionLocalizations({ 'en-U': 'foobar' })).toThrowError(); expect(() => getBuilder().setDescriptionLocalizations({ 'en-U': 'foobar' })).toThrowError();
}); });

View File

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

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { EmbedBuilder, embedLength } from '../../src'; import { EmbedBuilder, embedLength } from '../../src/index.js';
const alpha = 'abcdefghijklmnopqrstuvwxyz'; const alpha = 'abcdefghijklmnopqrstuvwxyz';
@@ -74,7 +74,7 @@ describe('Embed', () => {
test('GIVEN an embed with an invalid description THEN throws error', () => { test('GIVEN an embed with an invalid description THEN throws error', () => {
const embed = new EmbedBuilder(); 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', () => { test('GIVEN an embed with an invalid color THEN throws error', () => {
const embed = new EmbedBuilder(); const embed = new EmbedBuilder();
// @ts-expect-error // @ts-expect-error: invalid color
expect(() => embed.setColor('RED')).toThrowError(); expect(() => embed.setColor('RED')).toThrowError();
// @ts-expect-error // @ts-expect-error: invalid color
expect(() => embed.setColor([42, 36])).toThrowError(); 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', () => { test('GIVEN an embed with invalid footer text THEN throws error', () => {
const embed = new EmbedBuilder(); 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', () => { test('4', () => {
const embed = new EmbedBuilder(); 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 { URL } from 'node:url';
import { describe, test, expect, vitest } from 'vitest'; import { describe, test, expect, vitest } from 'vitest';
import { import {
@@ -21,7 +22,7 @@ import {
TimestampStyles, TimestampStyles,
underscore, underscore,
userMention, userMention,
} from '../../src'; } from '../../src/index.js';
describe('Message formatters', () => { describe('Message formatters', () => {
describe('codeBlock', () => { describe('codeBlock', () => {
@@ -183,7 +184,7 @@ describe('Message formatters', () => {
describe('time', () => { describe('time', () => {
test('GIVEN no arguments THEN returns "<t:${bigint}>"', () => { test('GIVEN no arguments THEN returns "<t:${bigint}>"', () => {
vitest.useFakeTimers(); vitest.useFakeTimers();
vitest.setSystemTime(1566424897579); vitest.setSystemTime(1_566_424_897_579);
expect<`<t:${bigint}>`>(time()).toEqual('<t:1566424897>'); expect<`<t:${bigint}>`>(time()).toEqual('<t:1566424897>');
@@ -191,29 +192,29 @@ describe('Message formatters', () => {
}); });
test('GIVEN a date THEN returns "<t:${bigint}>"', () => { 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}>"', () => { 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}>"', () => { 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>', '<t:1867424897:R>',
); );
}); });
test('GIVEN a date THEN returns "<t:${time}>"', () => { 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}>"', () => { 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}>"', () => { 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, enableValidators,
disableValidators, disableValidators,
isValidationEnabled, isValidationEnabled,
} from '../src/index'; } from '../src/index.js';
describe('isEquatable', () => { describe('isEquatable', () => {
test('returns true if the object is equatable', () => { test('returns true if the object is equatable', () => {

View File

@@ -65,16 +65,10 @@
"@favware/cliff-jumper": "^1.8.7", "@favware/cliff-jumper": "^1.8.7",
"@microsoft/api-extractor": "^7.29.5", "@microsoft/api-extractor": "^7.29.5",
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"@vitest/coverage-c8": "^0.22.1", "@vitest/coverage-c8": "^0.22.1",
"downlevel-dts": "^0.10.1", "downlevel-dts": "^0.10.1",
"eslint": "^8.23.0", "eslint": "^8.23.0",
"eslint-config-marine": "^9.4.1", "eslint-config-neon": "^0.1.23",
"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",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rollup-plugin-typescript2": "^0.33.0", "rollup-plugin-typescript2": "^0.33.0",
"typescript": "^4.8.2", "typescript": "^4.8.2",

View File

@@ -1,21 +1,21 @@
import { import {
type APIActionRowComponent, type APIActionRowComponent,
ComponentType, ComponentType,
APIMessageActionRowComponent, type APIMessageActionRowComponent,
APIModalActionRowComponent, type APIModalActionRowComponent,
APIActionRowComponentTypes, type APIActionRowComponentTypes,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import { ComponentBuilder } from './Component'; import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
import { createComponentBuilder } from './Components'; import { ComponentBuilder } from './Component.js';
import { createComponentBuilder } from './Components.js';
import type { ButtonBuilder } from './button/Button'; import type { ButtonBuilder } from './button/Button';
import type { SelectMenuBuilder } from './selectMenu/SelectMenu'; import type { SelectMenuBuilder } from './selectMenu/SelectMenu';
import type { TextInputBuilder } from './textInput/TextInput'; import type { TextInputBuilder } from './textInput/TextInput';
import { normalizeArray, type RestOrArray } from '../util/normalizeArray';
export type MessageComponentBuilder = export type MessageComponentBuilder =
| MessageActionRowComponentBuilder | ActionRowBuilder<MessageActionRowComponentBuilder>
| ActionRowBuilder<MessageActionRowComponentBuilder>; | MessageActionRowComponentBuilder;
export type ModalComponentBuilder = ModalActionRowComponentBuilder | ActionRowBuilder<ModalActionRowComponentBuilder>; export type ModalComponentBuilder = ActionRowBuilder<ModalActionRowComponentBuilder> | ModalActionRowComponentBuilder;
export type MessageActionRowComponentBuilder = ButtonBuilder | SelectMenuBuilder; export type MessageActionRowComponentBuilder = ButtonBuilder | SelectMenuBuilder;
export type ModalActionRowComponentBuilder = TextInputBuilder; export type ModalActionRowComponentBuilder = TextInputBuilder;
export type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder; export type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder;
@@ -35,7 +35,7 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
public constructor({ components, ...data }: Partial<APIActionRowComponent<APIActionRowComponentTypes>> = {}) { public constructor({ components, ...data }: Partial<APIActionRowComponent<APIActionRowComponentTypes>> = {}) {
super({ type: ComponentType.ActionRow, ...data }); 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 { s } from '@sapphire/shapeshift';
import { APIMessageComponentEmoji, ButtonStyle } from 'discord-api-types/v10'; import { ButtonStyle, type APIMessageComponentEmoji } from 'discord-api-types/v10';
import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption'; import { isValidationEnabled } from '../util/validation.js';
import { isValidationEnabled } from '../util/validation'; import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption.js';
export const customIdValidator = s.string export const customIdValidator = s.string
.lengthGreaterThanOrEqual(1) .lengthGreaterThanOrEqual(1)

View File

@@ -6,7 +6,7 @@ import type {
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import type { JSONEncodable } from '../util/jsonEncodable'; import type { JSONEncodable } from '../util/jsonEncodable';
export type AnyAPIActionRowComponent = APIActionRowComponentTypes | APIActionRowComponent<APIActionRowComponentTypes>; export type AnyAPIActionRowComponent = APIActionRowComponent<APIActionRowComponentTypes> | APIActionRowComponentTypes;
/** /**
* Represents a discord component * 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 { import {
ActionRowBuilder, ActionRowBuilder,
type AnyComponentBuilder, type AnyComponentBuilder,
type MessageComponentBuilder, type MessageComponentBuilder,
type ModalComponentBuilder, type ModalComponentBuilder,
} from './ActionRow'; } from './ActionRow.js';
import { ComponentBuilder } from './Component'; import { ComponentBuilder } from './Component.js';
import { ButtonBuilder } from './button/Button'; import { ButtonBuilder } from './button/Button.js';
import { SelectMenuBuilder } from './selectMenu/SelectMenu'; import { SelectMenuBuilder } from './selectMenu/SelectMenu.js';
import { TextInputBuilder } from './textInput/TextInput'; import { TextInputBuilder } from './textInput/TextInput.js';
export interface MappedComponentTypes { export interface MappedComponentTypes {
[ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>; [ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>;
@@ -23,7 +23,8 @@ export interface MappedComponentTypes {
* @param data - The api data to transform to a component class * @param data - The api data to transform to a component class
*/ */
export function createComponentBuilder<T extends keyof MappedComponentTypes>( 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]; ): MappedComponentTypes[T];
export function createComponentBuilder<C extends MessageComponentBuilder | ModalComponentBuilder>(data: C): C; export function createComponentBuilder<C extends MessageComponentBuilder | ModalComponentBuilder>(data: C): C;
export function createComponentBuilder( export function createComponentBuilder(
@@ -43,7 +44,7 @@ export function createComponentBuilder(
case ComponentType.TextInput: case ComponentType.TextInput:
return new TextInputBuilder(data); return new TextInputBuilder(data);
default: 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 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Cannot properly serialize component type: ${data.type}`); throw new Error(`Cannot properly serialize component type: ${data.type}`);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
import { s } from '@sapphire/shapeshift'; import { s } from '@sapphire/shapeshift';
import { ApplicationCommandType } from 'discord-api-types/v10'; import { ApplicationCommandType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder'; import type { ContextMenuCommandType } from './ContextMenuCommandBuilder';
import { isValidationEnabled } from '../../util/validation';
const namePredicate = s.string const namePredicate = s.string
.lengthGreaterThanOrEqual(1) .lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(32) .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) .regex(/^( *[\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+ *)+$/u)
.setValidationEnabled(isValidationEnabled); .setValidationEnabled(isValidationEnabled);
const typePredicate = s const typePredicate = s

View File

@@ -5,6 +5,7 @@ import type {
Permissions, Permissions,
RESTPostAPIApplicationCommandsJSONBody, RESTPostAPIApplicationCommandsJSONBody,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions.js';
import { import {
validateRequiredParameters, validateRequiredParameters,
validateName, validateName,
@@ -12,8 +13,7 @@ import {
validateDefaultPermission, validateDefaultPermission,
validateDefaultMemberPermissions, validateDefaultMemberPermissions,
validateDMPermission, validateDMPermission,
} from './Assertions'; } from './Assertions.js';
import { validateLocale, validateLocalizationMap } from '../slashCommands/Assertions';
export class ContextMenuCommandBuilder { 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. * **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 * @param value - Whether or not to enable this command by default
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions * @see https://discord.com/developers/docs/interactions/application-commands#permissions
* @deprecated Use {@link ContextMenuCommandBuilder.setDefaultMemberPermissions} or {@link ContextMenuCommandBuilder.setDMPermission} instead. * @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. * **Note:** You can set this to `'0'` to disable the command by default.
* *
* @param permissions - The permissions bit field to set * @param permissions - The permissions bit field to set
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions * @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/ */
public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) { public setDefaultMemberPermissions(permissions: Permissions | bigint | number | null | undefined) {
@@ -120,7 +118,6 @@ export class ContextMenuCommandBuilder {
* By default, commands are visible. * By default, commands are visible.
* *
* @param enabled - If the command should be enabled in DMs * @param enabled - If the command should be enabled in DMs
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions * @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/ */
public setDMPermission(enabled: boolean | null | undefined) { public setDMPermission(enabled: boolean | null | undefined) {
@@ -169,9 +166,8 @@ export class ContextMenuCommandBuilder {
Reflect.set(this, 'name_localizations', {}); Reflect.set(this, 'name_localizations', {});
Object.entries(localizedNames).forEach((args) => for (const args of Object.entries(localizedNames))
this.setNameLocalization(...(args as [LocaleString, string | null])), this.setNameLocalization(...(args as [LocaleString, string | null]));
);
return this; 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 { s } from '@sapphire/shapeshift';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow'; import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions'; import { customIdValidator } from '../../components/Assertions.js';
import { isValidationEnabled } from '../../util/validation'; import { isValidationEnabled } from '../../util/validation.js';
export const titleValidator = s.string export const titleValidator = s.string
.lengthGreaterThanOrEqual(1) .lengthGreaterThanOrEqual(1)

View File

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

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift'; 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 { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands'; import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase'; import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import { isValidationEnabled } from '../../util/validation';
const namePredicate = s.string const namePredicate = s.string
.lengthGreaterThanOrEqual(1) .lengthGreaterThanOrEqual(1)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import { APIApplicationCommandBooleanOption, ApplicationCommandOptionType } from 'discord-api-types/v10'; import { ApplicationCommandOptionType, type APIApplicationCommandBooleanOption } from 'discord-api-types/v10';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
export class SlashCommandBooleanOption extends ApplicationCommandOptionBase { export class SlashCommandBooleanOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Boolean as const; 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 { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin'; import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin.js';
@mix(ApplicationCommandOptionChannelTypesMixin) @mix(ApplicationCommandOptionChannelTypesMixin)
export class SlashCommandChannelOption extends ApplicationCommandOptionBase { export class SlashCommandChannelOption extends ApplicationCommandOptionBase {

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift'; 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 { mix } from 'ts-mixer';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin'; import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin'; import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
const numberValidator = s.number.int; const numberValidator = s.number.int;

View File

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

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift'; 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 { mix } from 'ts-mixer';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin'; import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin.js';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase'; import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase.js';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin'; import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.js';
const numberValidator = s.number; const numberValidator = s.number;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10'; import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { import {
colorPredicate, colorPredicate,
descriptionPredicate, descriptionPredicate,
@@ -10,8 +11,7 @@ import {
titlePredicate, titlePredicate,
urlPredicate, urlPredicate,
validateFieldLength, validateFieldLength,
} from './Assertions'; } from './Assertions.js';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
export type RGBTuple = [red: number, green: number, blue: number]; export type RGBTuple = [red: number, green: number, blue: number];
@@ -26,11 +26,11 @@ export interface IconData {
proxyIconURL?: string; 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 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'>; export type EmbedFooterOptions = Omit<EmbedFooterData, 'proxyIconURL'>;
@@ -57,7 +57,6 @@ export class EmbedBuilder {
* @remarks * @remarks
* This method accepts either an array of fields or a variable number of field parameters. * 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. * The maximum amount of fields that can be added is 25.
*
* @example * @example
* Using an array * Using an array
* ```ts * ```ts
@@ -65,7 +64,6 @@ export class EmbedBuilder {
* const embed = new EmbedBuilder() * const embed = new EmbedBuilder()
* .addFields(fields); * .addFields(fields);
* ``` * ```
*
* @example * @example
* Using rest parameters (variadic) * Using rest parameters (variadic)
* ```ts * ```ts
@@ -75,10 +73,10 @@ export class EmbedBuilder {
* { name: 'Field 2', value: 'Value 2' }, * { name: 'Field 2', value: 'Value 2' },
* ); * );
* ``` * ```
*
* @param fields - The fields to add * @param fields - The fields to add
*/ */
public addFields(...fields: RestOrArray<APIEmbedField>): this { public addFields(...fields: RestOrArray<APIEmbedField>): this {
// eslint-disable-next-line no-param-reassign
fields = normalizeArray(fields); fields = normalizeArray(fields);
// Ensure adding these fields won't exceed the 25 field limit // Ensure adding these fields won't exceed the 25 field limit
validateFieldLength(fields.length, this.data.fields); validateFieldLength(fields.length, this.data.fields);
@@ -100,26 +98,22 @@ export class EmbedBuilder {
* The maximum amount of fields that can be added is 25. * 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. * It's useful for modifying and adjusting order of the already-existing fields of an embed.
*
* @example * @example
* Remove the first field * Remove the first field
* ```ts * ```ts
* embed.spliceFields(0, 1); * embed.spliceFields(0, 1);
* ``` * ```
*
* @example * @example
* Remove the first n fields * Remove the first n fields
* ```ts * ```ts
* const n = 4 * const n = 4
* embed.spliceFields(0, n); * embed.spliceFields(0, n);
* ``` * ```
*
* @example * @example
* Remove the last field * Remove the last field
* ```ts * ```ts
* embed.spliceFields(-1, 1); * embed.spliceFields(-1, 1);
* ``` * ```
*
* @param index - The index to start at * @param index - The index to start at
* @param deleteCount - The number of fields to remove * @param deleteCount - The number of fields to remove
* @param fields - The replacing field objects * @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. * it splices the entire array of fields, replacing them with the provided fields.
* *
* You can set a maximum of 25 fields. * You can set a maximum of 25 fields.
*
* @param fields - The fields to set * @param fields - The fields to set
*/ */
public setFields(...fields: RestOrArray<APIEmbedField>) { public setFields(...fields: RestOrArray<APIEmbedField>) {
@@ -175,7 +168,7 @@ export class EmbedBuilder {
* *
* @param color - The color of the embed * @param color - The color of the embed
*/ */
public setColor(color: number | RGBTuple | null): this { public setColor(color: RGBTuple | number | null): this {
// Data assertions // Data assertions
colorPredicate.parse(color); colorPredicate.parse(color);
@@ -184,6 +177,7 @@ export class EmbedBuilder {
this.data.color = (red << 16) + (green << 8) + blue; this.data.color = (red << 16) + (green << 8) + blue;
return this; return this;
} }
this.data.color = color ?? undefined; this.data.color = color ?? undefined;
return this; return this;
} }
@@ -250,7 +244,7 @@ export class EmbedBuilder {
* *
* @param timestamp - The timestamp or date * @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 // Data assertions
timestampPredicate.parse(timestamp); 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'; 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 * @param url - The URL to wrap
*/ */
export function hideLinkEmbed(url: URL): `<${string}>`; 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 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `<${url}>`; return `<${url}>`;
} }
@@ -141,7 +141,7 @@ export function hyperlink<C extends string, U extends string, T extends string>(
url: U, url: U,
title: T, title: T,
): `[${C}](${U} "${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 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return title ? `[${content}](${url} "${title}")` : `[${content}](${url})`; 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 emojiId - The emoji ID to format
* @param animated - Whether the emoji is animated or not. Defaults to `false` * @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}>`; return `<${animated ? 'a' : ''}:_:${emojiId}>`;
} }
@@ -293,9 +293,10 @@ export function time<C extends number>(seconds: C): `<t:${C}>`;
* @param style - The style to use * @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<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') { 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}>`; 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 * 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. * Indicates if an object is equatable or not.
*
* @param maybeEquatable - The object to check against * @param maybeEquatable - The object to check against
*/ */
export function isEquatable(maybeEquatable: unknown): maybeEquatable is Equatable<unknown> { 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 * Transforms this object to its JSON format
*/ */
toJSON: () => T; toJSON(): T;
} }
/** /**
* Indicates if an object is encodable or not. * Indicates if an object is encodable or not.
*
* @param maybeEncodable - The object to check against * @param maybeEncodable - The object to check against
*/ */
export function isJSONEncodable(maybeEncodable: unknown): maybeEncodable is JSONEncodable<unknown> { export function isJSONEncodable(maybeEncodable: unknown): maybeEncodable is JSONEncodable<unknown> {

View File

@@ -1,5 +1,7 @@
/* eslint-disable unicorn/no-array-method-this-argument */
/* eslint-disable id-length */
import { describe, test, expect } from 'vitest'; import { describe, test, expect } from 'vitest';
import { Collection } from '../src'; import { Collection } from '../src/index.js';
type TestCollection = Collection<string, number>; type TestCollection = Collection<string, number>;
@@ -131,9 +133,9 @@ describe('each() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.each()); expectInvalidFunctionError(() => coll.each());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.each(123), 123); expectInvalidFunctionError(() => coll.each(123), 123);
}); });
@@ -152,7 +154,7 @@ describe('each() tests', () => {
describe('ensure() tests', () => { describe('ensure() tests', () => {
test('throws if defaultValueGenerator is not a function', () => { test('throws if defaultValueGenerator is not a function', () => {
const coll = createTestCollection(); const coll = createTestCollection();
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.ensure('d', 'abc'), 'abc'); expectInvalidFunctionError(() => coll.ensure('d', 'abc'), 'abc');
}); });
@@ -176,7 +178,7 @@ describe('equals() tests', () => {
const coll2 = createTestCollection(); const coll2 = createTestCollection();
test('returns false if no collection is passed', () => { test('returns false if no collection is passed', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expect(coll1.equals()).toBeFalsy(); expect(coll1.equals()).toBeFalsy();
}); });
@@ -198,9 +200,9 @@ describe('every() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.every()); expectInvalidFunctionError(() => coll.every());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.every(123), 123); expectInvalidFunctionError(() => coll.every(123), 123);
}); });
@@ -224,9 +226,9 @@ describe('filter() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.filter()); expectInvalidFunctionError(() => coll.filter());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.filter(123), 123); expectInvalidFunctionError(() => coll.filter(123), 123);
}); });
@@ -251,9 +253,9 @@ describe('find() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => createCollection().find()); expectInvalidFunctionError(() => createCollection().find());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => createCollection().find(123), 123); expectInvalidFunctionError(() => createCollection().find(123), 123);
}); });
@@ -275,9 +277,9 @@ describe('findKey() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.findKey()); expectInvalidFunctionError(() => coll.findKey());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.findKey(123), 123); expectInvalidFunctionError(() => coll.findKey(123), 123);
}); });
@@ -506,9 +508,9 @@ describe('map() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.map()); expectInvalidFunctionError(() => coll.map());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.map(123), 123); expectInvalidFunctionError(() => coll.map(123), 123);
}); });
@@ -529,9 +531,9 @@ describe('mapValues() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.mapValues()); expectInvalidFunctionError(() => coll.mapValues());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.mapValues(123), 123); expectInvalidFunctionError(() => coll.mapValues(123), 123);
}); });
@@ -606,9 +608,9 @@ describe('partition() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.partition()); expectInvalidFunctionError(() => coll.partition());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.partition(123), 123); expectInvalidFunctionError(() => coll.partition(123), 123);
}); });
@@ -690,9 +692,9 @@ describe('reduce() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.reduce()); expectInvalidFunctionError(() => coll.reduce());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.reduce(123), 123); expectInvalidFunctionError(() => coll.reduce(123), 123);
}); });
@@ -729,19 +731,15 @@ describe('some() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.some()); expectInvalidFunctionError(() => coll.some());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.some(123), 123); expectInvalidFunctionError(() => coll.some(123), 123);
}); });
test('returns false if no items pass the predicate', () => { test('returns false if no items pass the predicate', () => {
expect(coll.some((v) => v > 3)).toBeFalsy(); expect(coll.some((v) => v > 3)).toBeFalsy();
}); });
test('returns true if at least one item passes the predicate', () => {
expect(coll.some((x) => x === 2)).toBeTruthy();
});
}); });
describe('sort() tests', () => { describe('sort() tests', () => {
@@ -777,9 +775,9 @@ describe('sweep() test', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.sweep()); expectInvalidFunctionError(() => coll.sweep());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.sweep(123), 123); expectInvalidFunctionError(() => coll.sweep(123), 123);
}); });
@@ -804,9 +802,9 @@ describe('tap() tests', () => {
const coll = createTestCollection(); const coll = createTestCollection();
test('throws if fn is not a function', () => { test('throws if fn is not a function', () => {
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.tap()); expectInvalidFunctionError(() => coll.tap());
// @ts-expect-error // @ts-expect-error: invalid function
expectInvalidFunctionError(() => coll.tap(123), 123); expectInvalidFunctionError(() => coll.tap(123), 123);
}); });

View File

@@ -54,16 +54,10 @@
"@favware/cliff-jumper": "^1.8.7", "@favware/cliff-jumper": "^1.8.7",
"@microsoft/api-extractor": "^7.29.5", "@microsoft/api-extractor": "^7.29.5",
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"@vitest/coverage-c8": "^0.22.1", "@vitest/coverage-c8": "^0.22.1",
"downlevel-dts": "^0.10.1", "downlevel-dts": "^0.10.1",
"eslint": "^8.23.0", "eslint": "^8.23.0",
"eslint-config-marine": "^9.4.1", "eslint-config-neon": "^0.1.23",
"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",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rollup-plugin-typescript2": "^0.33.0", "rollup-plugin-typescript2": "^0.33.0",
"typescript": "^4.8.2", "typescript": "^4.8.2",

View File

@@ -1,10 +1,12 @@
/* eslint-disable id-length */
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
/** /**
* @internal * @internal
*/ */
export interface CollectionConstructor { export interface CollectionConstructor {
new (): Collection<unknown, unknown>; new (): Collection<unknown, unknown>;
new <K, V>(entries?: ReadonlyArray<readonly [K, V]> | null): Collection<K, V>; new <K, V>(entries?: readonly (readonly [K, V])[] | null): Collection<K, V>;
new <K, V>(iterable: Iterable<readonly [K, V]>): Collection<K, V>; new <K, V>(iterable: Iterable<readonly [K, V]>): Collection<K, V>;
readonly prototype: Collection<unknown, unknown>; readonly prototype: Collection<unknown, unknown>;
readonly [Symbol.species]: CollectionConstructor; readonly [Symbol.species]: CollectionConstructor;
@@ -13,8 +15,11 @@ export interface CollectionConstructor {
/** /**
* Represents an immutable version of a collection * Represents an immutable version of a collection
*/ */
export type ReadonlyCollection<K, V> = ReadonlyMap<K, V> & export type ReadonlyCollection<K, V> = Omit<
Omit<Collection<K, V>, 'forEach' | 'ensure' | 'reverse' | 'sweep' | 'sort' | 'get' | 'set' | 'delete'>; Collection<K, V>,
'delete' | 'ensure' | 'forEach' | 'get' | 'reverse' | 'set' | 'sort' | 'sweep'
> &
ReadonlyMap<K, V>;
/** /**
* Separate interface for the constructor so that emitted js does not have a constructor that overwrites itself * Separate interface for the constructor so that emitted js does not have a constructor that overwrites itself
@@ -38,7 +43,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param key - The key to get if it exists, or set otherwise * @param key - The key to get if it exists, or set otherwise
* @param defaultValueGenerator - A function that generates the default value * @param defaultValueGenerator - A function that generates the default value
*
* @example * @example
* ```ts * ```ts
* collection.ensure(guildId, () => defaultGuildConfig); * collection.ensure(guildId, () => defaultGuildConfig);
@@ -56,7 +60,6 @@ export class Collection<K, V> extends Map<K, V> {
* Checks if all of the elements exist in the collection. * Checks if all of the elements exist in the collection.
* *
* @param keys - The keys of the elements to check for * @param keys - The keys of the elements to check for
*
* @returns `true` if all of the elements exist, `false` if at least one does not exist. * @returns `true` if all of the elements exist, `false` if at least one does not exist.
*/ */
public hasAll(...keys: K[]) { public hasAll(...keys: K[]) {
@@ -67,7 +70,6 @@ export class Collection<K, V> extends Map<K, V> {
* Checks if any of the elements exist in the collection. * Checks if any of the elements exist in the collection.
* *
* @param keys - The keys of the elements to check for * @param keys - The keys of the elements to check for
*
* @returns `true` if any of the elements exist, `false` if none exist. * @returns `true` if any of the elements exist, `false` if none exist.
*/ */
public hasAny(...keys: K[]) { public hasAny(...keys: K[]) {
@@ -78,7 +80,6 @@ export class Collection<K, V> extends Map<K, V> {
* Obtains the first value(s) in this collection. * Obtains the first value(s) in this collection.
* *
* @param amount - Amount of values to obtain from the beginning * @param amount - Amount of values to obtain from the beginning
*
* @returns A single value if no amount is provided or an array of values, starting from the end if amount is negative * @returns A single value if no amount is provided or an array of values, starting from the end if amount is negative
*/ */
public first(): V | undefined; public first(): V | undefined;
@@ -97,7 +98,6 @@ export class Collection<K, V> extends Map<K, V> {
* Obtains the first key(s) in this collection. * Obtains the first key(s) in this collection.
* *
* @param amount - Amount of keys to obtain from the beginning * @param amount - Amount of keys to obtain from the beginning
*
* @returns A single key if no amount is provided or an array of keys, starting from the end if * @returns A single key if no amount is provided or an array of keys, starting from the end if
* amount is negative * amount is negative
*/ */
@@ -117,7 +117,6 @@ export class Collection<K, V> extends Map<K, V> {
* Obtains the last value(s) in this collection. * Obtains the last value(s) in this collection.
* *
* @param amount - Amount of values to obtain from the end * @param amount - Amount of values to obtain from the end
*
* @returns A single value if no amount is provided or an array of values, starting from the start if * @returns A single value if no amount is provided or an array of values, starting from the start if
* amount is negative * amount is negative
*/ */
@@ -135,7 +134,6 @@ export class Collection<K, V> extends Map<K, V> {
* Obtains the last key(s) in this collection. * Obtains the last key(s) in this collection.
* *
* @param amount - Amount of keys to obtain from the end * @param amount - Amount of keys to obtain from the end
*
* @returns A single key if no amount is provided or an array of keys, starting from the start if * @returns A single key if no amount is provided or an array of keys, starting from the start if
* amount is negative * amount is negative
*/ */
@@ -179,7 +177,6 @@ export class Collection<K, V> extends Map<K, V> {
* Obtains unique random value(s) from this collection. * Obtains unique random value(s) from this collection.
* *
* @param amount - Amount of values to obtain randomly * @param amount - Amount of values to obtain randomly
*
* @returns A single value if no amount is provided or an array of values * @returns A single value if no amount is provided or an array of values
*/ */
public random(): V | undefined; public random(): V | undefined;
@@ -198,7 +195,6 @@ export class Collection<K, V> extends Map<K, V> {
* Obtains unique random key(s) from this collection. * Obtains unique random key(s) from this collection.
* *
* @param amount - Amount of keys to obtain randomly * @param amount - Amount of keys to obtain randomly
*
* @returns A single key if no amount is provided or an array * @returns A single key if no amount is provided or an array
*/ */
public randomKey(): K | undefined; public randomKey(): K | undefined;
@@ -233,7 +229,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - The function to test with (should return boolean) * @param fn - The function to test with (should return boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.find(user => user.username === 'Bob'); * collection.find(user => user.username === 'Bob');
@@ -252,6 +247,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) { for (const [key, val] of this) {
if (fn(val, key, this)) return val; if (fn(val, key, this)) return val;
} }
return undefined; return undefined;
} }
@@ -262,7 +258,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - The function to test with (should return boolean) * @param fn - The function to test with (should return boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.findKey(user => user.username === 'Bob'); * collection.findKey(user => user.username === 'Bob');
@@ -281,6 +276,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) { for (const [key, val] of this) {
if (fn(val, key, this)) return key; if (fn(val, key, this)) return key;
} }
return undefined; return undefined;
} }
@@ -289,7 +285,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function used to test (should return a boolean) * @param fn - Function used to test (should return a boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @returns The number of removed entries * @returns The number of removed entries
*/ */
public sweep(fn: (value: V, key: K, collection: this) => boolean): number; public sweep(fn: (value: V, key: K, collection: this) => boolean): number;
@@ -301,6 +296,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) { for (const [key, val] of this) {
if (fn(val, key, this)) this.delete(key); if (fn(val, key, this)) this.delete(key);
} }
return previousSize - this.size; return previousSize - this.size;
} }
@@ -311,7 +307,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - The function to test with (should return boolean) * @param fn - The function to test with (should return boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.filter(user => user.username === 'Bob'); * collection.filter(user => user.username === 'Bob');
@@ -336,6 +331,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) { for (const [key, val] of this) {
if (fn(val, key, this)) results.set(key, val); if (fn(val, key, this)) results.set(key, val);
} }
return results; return results;
} }
@@ -345,7 +341,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function used to test (should return a boolean) * @param fn - Function used to test (should return a boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* const [big, small] = collection.partition(guild => guild.memberCount > 250); * const [big, small] = collection.partition(guild => guild.memberCount > 250);
@@ -387,6 +382,7 @@ export class Collection<K, V> extends Map<K, V> {
results[1].set(key, val); results[1].set(key, val);
} }
} }
return results; return results;
} }
@@ -396,7 +392,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function that produces a new Collection * @param fn - Function that produces a new Collection
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.flatMap(guild => guild.members.cache); * collection.flatMap(guild => guild.members.cache);
@@ -408,6 +403,7 @@ export class Collection<K, V> extends Map<K, V> {
thisArg: This, thisArg: This,
): Collection<K, T>; ): Collection<K, T>;
public flatMap<T>(fn: (value: V, key: K, collection: this) => Collection<K, T>, thisArg?: unknown): Collection<K, T> { public flatMap<T>(fn: (value: V, key: K, collection: this) => Collection<K, T>, thisArg?: unknown): Collection<K, T> {
// eslint-disable-next-line unicorn/no-array-method-this-argument
const collections = this.map(fn, thisArg); const collections = this.map(fn, thisArg);
return new this.constructor[Symbol.species]<K, T>().concat(...collections); return new this.constructor[Symbol.species]<K, T>().concat(...collections);
} }
@@ -418,7 +414,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function that produces an element of the new array, taking three arguments * @param fn - Function that produces an element of the new array, taking three arguments
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.map(user => user.tag); * collection.map(user => user.tag);
@@ -444,7 +439,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function that produces an element of the new collection, taking three arguments * @param fn - Function that produces an element of the new collection, taking three arguments
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.mapValues(user => user.tag); * collection.mapValues(user => user.tag);
@@ -466,7 +460,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function used to test (should return a boolean) * @param fn - Function used to test (should return a boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.some(user => user.discriminator === '0000'); * collection.some(user => user.discriminator === '0000');
@@ -480,6 +473,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) { for (const [key, val] of this) {
if (fn(val, key, this)) return true; if (fn(val, key, this)) return true;
} }
return false; return false;
} }
@@ -489,7 +483,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function used to test (should return a boolean) * @param fn - Function used to test (should return a boolean)
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection.every(user => !user.bot); * collection.every(user => !user.bot);
@@ -513,6 +506,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) { for (const [key, val] of this) {
if (!fn(val, key, this)) return false; if (!fn(val, key, this)) return false;
} }
return true; return true;
} }
@@ -523,7 +517,6 @@ export class Collection<K, V> extends Map<K, V> {
* @param fn - Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`, * @param fn - Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`,
* and `collection` * and `collection`
* @param initialValue - Starting value for the accumulator * @param initialValue - Starting value for the accumulator
*
* @example * @example
* ```ts * ```ts
* collection.reduce((acc, guild) => acc + guild.memberCount, 0); * collection.reduce((acc, guild) => acc + guild.memberCount, 0);
@@ -538,6 +531,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [key, val] of this) accumulator = fn(accumulator, val, key, this); for (const [key, val] of this) accumulator = fn(accumulator, val, key, this);
return accumulator; return accumulator;
} }
let first = true; let first = true;
for (const [key, val] of this) { for (const [key, val] of this) {
if (first) { if (first) {
@@ -545,6 +539,7 @@ export class Collection<K, V> extends Map<K, V> {
first = false; first = false;
continue; continue;
} }
accumulator = fn(accumulator, val, key, this); accumulator = fn(accumulator, val, key, this);
} }
@@ -563,7 +558,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function to execute for each element * @param fn - Function to execute for each element
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection * collection
@@ -576,6 +570,7 @@ export class Collection<K, V> extends Map<K, V> {
public each<T>(fn: (this: T, value: V, key: K, collection: this) => void, thisArg: T): this; public each<T>(fn: (this: T, value: V, key: K, collection: this) => void, thisArg: T): this;
public each(fn: (value: V, key: K, collection: this) => void, thisArg?: unknown): this { public each(fn: (value: V, key: K, collection: this) => void, thisArg?: unknown): this {
if (typeof fn !== 'function') throw new TypeError(`${fn} is not a function`); if (typeof fn !== 'function') throw new TypeError(`${fn} is not a function`);
// eslint-disable-next-line unicorn/no-array-method-this-argument
this.forEach(fn as (value: V, key: K, map: Map<K, V>) => void, thisArg); this.forEach(fn as (value: V, key: K, map: Map<K, V>) => void, thisArg);
return this; return this;
} }
@@ -585,7 +580,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param fn - Function to execute * @param fn - Function to execute
* @param thisArg - Value to use as `this` when executing function * @param thisArg - Value to use as `this` when executing function
*
* @example * @example
* ```ts * ```ts
* collection * collection
@@ -619,7 +613,6 @@ export class Collection<K, V> extends Map<K, V> {
* Combines this collection with others into a new collection. None of the source collections are modified. * Combines this collection with others into a new collection. None of the source collections are modified.
* *
* @param collections - Collections to merge * @param collections - Collections to merge
*
* @example * @example
* ```ts * ```ts
* const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl); * const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl);
@@ -630,6 +623,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const coll of collections) { for (const coll of collections) {
for (const [key, val] of coll) newColl.set(key, val); for (const [key, val] of coll) newColl.set(key, val);
} }
return newColl; return newColl;
} }
@@ -639,7 +633,6 @@ export class Collection<K, V> extends Map<K, V> {
* the collections may be different objects, but contain the same data. * the collections may be different objects, but contain the same data.
* *
* @param collection - Collection to compare with * @param collection - Collection to compare with
*
* @returns Whether the collections have identical contents * @returns Whether the collections have identical contents
*/ */
public equals(collection: ReadonlyCollection<K, V>) { public equals(collection: ReadonlyCollection<K, V>) {
@@ -652,6 +645,7 @@ export class Collection<K, V> extends Map<K, V> {
return false; return false;
} }
} }
return true; return true;
} }
@@ -662,7 +656,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param compareFunction - Specifies a function that defines the sort order. * @param compareFunction - Specifies a function that defines the sort order.
* If omitted, the collection is sorted according to each character's Unicode code point value, according to the string conversion of each element. * If omitted, the collection is sorted according to each character's Unicode code point value, according to the string conversion of each element.
*
* @example * @example
* ```ts * ```ts
* collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); * collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp);
@@ -679,6 +672,7 @@ export class Collection<K, V> extends Map<K, V> {
for (const [k, v] of entries) { for (const [k, v] of entries) {
super.set(k, v); super.set(k, v);
} }
return this; return this;
} }
@@ -694,6 +688,7 @@ export class Collection<K, V> extends Map<K, V> {
coll.set(k, v); coll.set(k, v);
} }
} }
return coll; return coll;
} }
@@ -702,24 +697,26 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param other - The other Collection to filter against * @param other - The other Collection to filter against
*/ */
public difference<T>(other: ReadonlyCollection<K, T>): Collection<K, V | T> { public difference<T>(other: ReadonlyCollection<K, T>): Collection<K, T | V> {
const coll = new this.constructor[Symbol.species]<K, V | T>(); const coll = new this.constructor[Symbol.species]<K, T | V>();
for (const [k, v] of other) { for (const [k, v] of other) {
if (!this.has(k)) coll.set(k, v); if (!this.has(k)) coll.set(k, v);
} }
for (const [k, v] of this) { for (const [k, v] of this) {
if (!other.has(k)) coll.set(k, v); if (!other.has(k)) coll.set(k, v);
} }
return coll; return coll;
} }
/** /**
* Merges two Collections together into a new Collection. * Merges two Collections together into a new Collection.
*
* @param other - The other Collection to merge with * @param other - The other Collection to merge with
* @param whenInSelf - Function getting the result if the entry only exists in this Collection * @param whenInSelf - Function getting the result if the entry only exists in this Collection
* @param whenInOther - Function getting the result if the entry only exists in the other Collection * @param whenInOther - Function getting the result if the entry only exists in the other Collection
* @param whenInBoth - Function getting the result if the entry exists in both Collections * @param whenInBoth - Function getting the result if the entry exists in both Collections
*
* @example * @example
* ```ts * ```ts
* // Sums up the entries in two collections. * // Sums up the entries in two collections.
@@ -730,7 +727,6 @@ export class Collection<K, V> extends Map<K, V> {
* (x, y) => ({ keep: true, value: x + y }), * (x, y) => ({ keep: true, value: x + y }),
* ); * );
* ``` * ```
*
* @example * @example
* ```ts * ```ts
* // Intersects two collections in a left-biased manner. * // Intersects two collections in a left-biased manner.
@@ -765,6 +761,7 @@ export class Collection<K, V> extends Map<K, V> {
if (r.keep) coll.set(k, r.value); if (r.keep) coll.set(k, r.value);
} }
} }
return coll; return coll;
} }
@@ -776,7 +773,6 @@ export class Collection<K, V> extends Map<K, V> {
* @param compareFunction - Specifies a function that defines the sort order. * @param compareFunction - Specifies a function that defines the sort order.
* If omitted, the collection is sorted according to each character's Unicode code point value, * If omitted, the collection is sorted according to each character's Unicode code point value,
* according to the string conversion of each element. * according to the string conversion of each element.
*
* @example * @example
* ```ts * ```ts
* collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); * collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp);
@@ -800,7 +796,6 @@ export class Collection<K, V> extends Map<K, V> {
* *
* @param entries - The list of entries * @param entries - The list of entries
* @param combine - Function to combine an existing entry with a new one * @param combine - Function to combine an existing entry with a new one
*
* @example * @example
* ```ts * ```ts
* Collection.combineEntries([["a", 1], ["b", 2], ["a", 2]], (x, y) => x + y); * Collection.combineEntries([["a", 1], ["b", 2], ["a", 2]], (x, y) => x + y);
@@ -819,6 +814,7 @@ export class Collection<K, V> extends Map<K, V> {
coll.set(k, v); coll.set(k, v);
} }
} }
return coll; return coll;
} }
} }
@@ -826,7 +822,7 @@ export class Collection<K, V> extends Map<K, V> {
/** /**
* @internal * @internal
*/ */
export type Keep<V> = { keep: true; value: V } | { keep: false }; export type Keep<V> = { keep: false } | { keep: true; value: V };
/** /**
* @internal * @internal

View File

@@ -1 +1 @@
export * from './collection'; export * from './collection.js';

View File

@@ -51,13 +51,8 @@
"@favware/cliff-jumper": "^1.8.7", "@favware/cliff-jumper": "^1.8.7",
"@types/jsdoc-to-markdown": "^7.0.3", "@types/jsdoc-to-markdown": "^7.0.3",
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"eslint": "^8.23.0", "eslint": "^8.23.0",
"eslint-config-marine": "^9.4.1", "eslint-config-neon": "^0.1.23",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.0",
"eslint-plugin-import": "^2.26.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rollup-plugin-typescript2": "^0.33.0", "rollup-plugin-typescript2": "^0.33.0",
"typescript": "^4.8.2", "typescript": "^4.8.2",

View File

@@ -1,13 +1,15 @@
#!/usr/bin/env node #!/usr/bin/env node
/* eslint-disable n/shebang */
import process from 'node:process';
import { createCommand } from 'commander'; import { createCommand } from 'commander';
import { build } from './index.js';
import packageFile from '../package.json'; import packageFile from '../package.json';
import { build } from './index.js';
export interface CLIOptions { export interface CLIOptions {
input: string[];
custom: string; custom: string;
root: string; input: string[];
output: string; output: string;
root: string;
typescript: boolean; typescript: boolean;
} }

View File

@@ -1,5 +1,6 @@
import { dirname, join, relative } from 'node:path'; import { dirname, join, relative } from 'node:path';
import type { DeclarationReflection } from 'typedoc'; import type { DeclarationReflection } from 'typedoc';
import packageFile from '../package.json';
import type { ChildTypes, Class, Config, CustomDocs, RootTypes } from './interfaces/index.js'; import type { ChildTypes, Class, Config, CustomDocs, RootTypes } from './interfaces/index.js';
import { DocumentedClass } from './types/class.js'; import { DocumentedClass } from './types/class.js';
import { DocumentedConstructor } from './types/constructor.js'; import { DocumentedConstructor } from './types/constructor.js';
@@ -9,7 +10,6 @@ import { DocumentedInterface } from './types/interface.js';
import { DocumentedMember } from './types/member.js'; import { DocumentedMember } from './types/member.js';
import { DocumentedMethod } from './types/method.js'; import { DocumentedMethod } from './types/method.js';
import { DocumentedTypeDef } from './types/typedef.js'; import { DocumentedTypeDef } from './types/typedef.js';
import packageFile from '../package.json';
export class Documentation { export class Documentation {
public readonly classes = new Map<string, DocumentedClass>(); public readonly classes = new Map<string, DocumentedClass>();
@@ -23,7 +23,7 @@ export class Documentation {
public readonly externals = new Map<string, DocumentedExternal>(); public readonly externals = new Map<string, DocumentedExternal>();
public constructor( public constructor(
data: RootTypes[] | DeclarationReflection[], data: DeclarationReflection[] | RootTypes[],
private readonly config: Config, private readonly config: Config,
private readonly custom?: Record<string, CustomDocs>, private readonly custom?: Record<string, CustomDocs>,
) { ) {
@@ -37,6 +37,7 @@ export class Documentation {
if (item.children) { if (item.children) {
this.parse(item.children, item); this.parse(item.children, item);
} }
break; break;
} }
@@ -52,6 +53,7 @@ export class Documentation {
if (item.children) { if (item.children) {
this.parse(item.children, item); this.parse(item.children, item);
} }
break; break;
default: default:
@@ -60,37 +62,43 @@ export class Documentation {
} }
} else { } else {
let items = data as RootTypes[]; let items = data as RootTypes[];
items = items.filter((i) => !i.ignore); items = items.filter((item) => !item.ignore);
for (const item of items) { for (const item of items) {
switch (item.kind) { switch (item.kind) {
case 'class': { case 'class': {
this.classes.set(item.name, new DocumentedClass(item, config)); this.classes.set(item.name, new DocumentedClass(item, config));
items = items.filter((i) => i.longname !== item.longname || i.kind !== item.kind); items = items.filter((otherItem) => otherItem.longname !== item.longname || otherItem.kind !== item.kind);
break; break;
} }
case 'function': { case 'function': {
if (item.scope === 'global' || !item.memberof) { if (item.scope === 'global' || !item.memberof) {
this.functions.set(item.name, new DocumentedMethod(item, config)); this.functions.set(item.name, new DocumentedMethod(item, config));
items = items.filter((i) => i.longname !== item.longname); items = items.filter((otherItem) => otherItem.longname !== item.longname);
} }
break; break;
} }
case 'interface': { case 'interface': {
this.interfaces.set(item.name, new DocumentedInterface(item as unknown as Class, config)); this.interfaces.set(item.name, new DocumentedInterface(item as unknown as Class, config));
items = items.filter((i) => i.longname !== item.longname); items = items.filter((otherItem) => otherItem.longname !== item.longname);
break; break;
} }
case 'typedef': { case 'typedef': {
this.typedefs.set(item.name, new DocumentedTypeDef(item, config)); this.typedefs.set(item.name, new DocumentedTypeDef(item, config));
items = items.filter((i) => i.longname !== item.longname); items = items.filter((otherItem) => otherItem.longname !== item.longname);
break; break;
} }
case 'external': { case 'external': {
this.externals.set(item.name, new DocumentedExternal(item, config)); this.externals.set(item.name, new DocumentedExternal(item, config));
items = items.filter((i) => i.longname !== item.longname); items = items.filter((otherItem) => otherItem.longname !== item.longname);
break; break;
} }
default: default:
break; break;
} }
@@ -100,39 +108,43 @@ export class Documentation {
} }
} }
public parse(items: ChildTypes[] | DeclarationReflection[], p?: DeclarationReflection) { public parse(items: ChildTypes[] | DeclarationReflection[], prop?: DeclarationReflection) {
if (this.config.typescript) { if (this.config.typescript) {
const it = items as DeclarationReflection[]; const it = items as DeclarationReflection[];
for (const member of it) { for (const member of it) {
let item: DocumentedMethod | DocumentedConstructor | DocumentedMember | DocumentedEvent | null = null; let item: DocumentedConstructor | DocumentedEvent | DocumentedMember | DocumentedMethod | null = null;
switch (member.kindString) { switch (member.kindString) {
case 'Constructor': { case 'Constructor': {
item = new DocumentedConstructor(member, this.config); item = new DocumentedConstructor(member, this.config);
break; break;
} }
case 'Method': { case 'Method': {
const event = p?.groups?.find((group) => group.title === 'Events'); const event = prop?.groups?.find((group) => group.title === 'Events');
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if ((event?.children as unknown as number[])?.includes(member.id)) { if ((event?.children as unknown as number[])?.includes(member.id)) {
item = new DocumentedEvent(member, this.config); item = new DocumentedEvent(member, this.config);
break; break;
} }
item = new DocumentedMethod(member, this.config); item = new DocumentedMethod(member, this.config);
break; break;
} }
case 'Property': { case 'Property': {
item = new DocumentedMember(member, this.config); item = new DocumentedMember(member, this.config);
break; break;
} }
default: { default: {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
console.warn(`- Unknown documentation kind "${member.kindString}" - \n${JSON.stringify(member)}\n`); console.warn(`- Unknown documentation kind "${member.kindString}" - \n${JSON.stringify(member)}\n`);
} }
} }
const parent = this.classes.get(p!.name) ?? this.interfaces.get(p!.name); const parent = this.classes.get(prop!.name) ?? this.interfaces.get(prop!.name);
if (parent) { if (parent) {
if (item) { if (item) {
parent.add(item); parent.add(item);
@@ -141,6 +153,7 @@ export class Documentation {
`- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`, `- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`,
); );
} }
continue; continue;
} }
@@ -155,9 +168,10 @@ export class Documentation {
path: dirname(member.sources?.[0]?.fileName ?? ''), path: dirname(member.sources?.[0]?.fileName ?? ''),
}; };
if (p!.name) { if (prop!.name) {
info.push(`member of "${p!.name}"`); info.push(`member of "${prop!.name}"`);
} }
if (meta) { if (meta) {
info.push( info.push(
`${relative(this.config.root, join(meta.path, meta.file ?? ''))}${meta.line ? `:${meta.line}` : ''}`, `${relative(this.config.root, join(meta.path, meta.file ?? ''))}${meta.line ? `:${meta.line}` : ''}`,
@@ -173,27 +187,31 @@ export class Documentation {
const it = items as ChildTypes[]; const it = items as ChildTypes[];
for (const member of it) { for (const member of it) {
let item: DocumentedMethod | DocumentedConstructor | DocumentedMember | DocumentedEvent | null = null; let item: DocumentedConstructor | DocumentedEvent | DocumentedMember | DocumentedMethod | null = null;
switch (member.kind) { switch (member.kind) {
case 'constructor': { case 'constructor': {
item = new DocumentedConstructor(member, this.config); item = new DocumentedConstructor(member, this.config);
break; break;
} }
case 'function': { case 'function': {
item = new DocumentedMethod(member, this.config); item = new DocumentedMethod(member, this.config);
break; break;
} }
case 'member': { case 'member': {
item = new DocumentedMember(member, this.config); item = new DocumentedMember(member, this.config);
break; break;
} }
case 'event': { case 'event': {
item = new DocumentedEvent(member, this.config); item = new DocumentedEvent(member, this.config);
break; break;
} }
default: { default: {
// @ts-expect-error // @ts-expect-error: This is a valid case
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
console.warn(`- Unknown documentation kind "${member.kind}" - \n${JSON.stringify(member)}\n`); console.warn(`- Unknown documentation kind "${member.kind}" - \n${JSON.stringify(member)}\n`);
} }
@@ -208,12 +226,13 @@ export class Documentation {
`- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`, `- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`,
); );
} }
continue; continue;
} }
const info = []; const info = [];
const name = (member.name || item?.data.name) ?? 'UNKNOWN'; const name = (member.name || item?.data.name) ?? 'UNKNOWN';
// @ts-expect-error // @ts-expect-error: Typescript can't infer this
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unnecessary-condition
const memberof = member.memberof ?? item?.data?.memberof; const memberof = member.memberof ?? item?.data?.memberof;
const meta = const meta =
@@ -224,6 +243,7 @@ export class Documentation {
if (memberof) { if (memberof) {
info.push(`member of "${memberof as string}"`); info.push(`member of "${memberof as string}"`);
} }
if (meta) { if (meta) {
info.push(`${relative(this.config.root, join(meta.path, meta.file))}${meta.line ? `:${meta.line}` : ''}`); info.push(`${relative(this.config.root, join(meta.path, meta.file))}${meta.line ? `:${meta.line}` : ''}`);
} }
@@ -243,16 +263,14 @@ export class Documentation {
format: Documentation.FORMAT_VERSION, format: Documentation.FORMAT_VERSION,
date: Date.now(), date: Date.now(),
}, },
classes: [...this.classes.values()].map((c) => c.serialize()), classes: [...this.classes.values()].map((_class) => _class.serialize()),
functions: [...this.functions.values()].map((f) => f.serialize()), functions: [...this.functions.values()].map((_function) => _function.serialize()),
interfaces: [...this.interfaces.values()].map((i) => i.serialize()), interfaces: [...this.interfaces.values()].map((_interface) => _interface.serialize()),
typedefs: [...this.typedefs.values()].map((t) => t.serialize()), typedefs: [...this.typedefs.values()].map((_typedef) => _typedef.serialize()),
externals: [...this.externals.values()].map((e) => e.serialize()), externals: [...this.externals.values()].map((_external) => _external.serialize()),
custom: this.custom, custom: this.custom,
}; };
} }
public static get FORMAT_VERSION() { public static readonly FORMAT_VERSION = 30;
return 30;
}
} }

View File

@@ -3,22 +3,22 @@ import { dirname, join, extname, basename, relative } from 'node:path';
import jsdoc2md from 'jsdoc-to-markdown'; import jsdoc2md from 'jsdoc-to-markdown';
import { type DeclarationReflection, Application, TSConfigReader } from 'typedoc'; import { type DeclarationReflection, Application, TSConfigReader } from 'typedoc';
import type { CLIOptions } from './cli'; import type { CLIOptions } from './cli';
import { Documentation } from './documentation'; import { Documentation } from './documentation.js';
import type { RootTypes, ChildTypes, CustomDocs } from './interfaces'; import type { RootTypes, ChildTypes, CustomDocs } from './interfaces';
interface CustomFiles { interface CustomFiles {
id?: string;
name: string;
path?: string;
files: { files: {
id?: string; id?: string;
name: string; name: string;
path: string; path: string;
}[]; }[];
id?: string;
name: string;
path?: string;
} }
export function build({ input, custom: customDocs, root, output, typescript }: CLIOptions) { export function build({ input, custom: customDocs, root, output, typescript }: CLIOptions) {
let data: (RootTypes & ChildTypes)[] | DeclarationReflection[] = []; let data: (ChildTypes & RootTypes)[] | DeclarationReflection[] = [];
if (typescript) { if (typescript) {
console.log('Parsing Typescript in source files...'); console.log('Parsing Typescript in source files...');
const app = new Application(); const app = new Application();
@@ -26,13 +26,14 @@ export function build({ input, custom: customDocs, root, output, typescript }: C
app.bootstrap({ entryPoints: input }); app.bootstrap({ entryPoints: input });
const project = app.convert(); const project = app.convert();
if (project) { if (project) {
// @ts-expect-error // @ts-expect-error: Types are lost with this method
data = app.serializer.toObject(project).children!; data = app.serializer.toObject(project).children!;
console.log(`${data.length} items parsed.`); console.log(`${data.length} items parsed.`);
} }
} else { } else {
console.log('Parsing JSDocs in source files...'); console.log('Parsing JSDocs in source files...');
data = jsdoc2md.getTemplateDataSync({ files: input }) as (RootTypes & ChildTypes)[]; // eslint-disable-next-line n/no-sync
data = jsdoc2md.getTemplateDataSync({ files: input }) as (ChildTypes & RootTypes)[];
console.log(`${data.length} JSDoc items parsed.`); console.log(`${data.length} JSDoc items parsed.`);
} }
@@ -40,7 +41,7 @@ export function build({ input, custom: customDocs, root, output, typescript }: C
if (customDocs) { if (customDocs) {
console.log('Loading custom docs files...'); console.log('Loading custom docs files...');
const customDir = dirname(customDocs); const customDir = dirname(customDocs);
const file = readFileSync(customDocs, 'utf-8'); const file = readFileSync(customDocs, 'utf8');
const data = JSON.parse(file) as CustomFiles[]; const data = JSON.parse(file) as CustomFiles[];
for (const category of data) { for (const category of data) {
@@ -51,23 +52,23 @@ export function build({ input, custom: customDocs, root, output, typescript }: C
files: {}, files: {},
}; };
for (const f of category.files) { for (const file of category.files) {
const fileRootPath = join(dir, f.path); const fileRootPath = join(dir, file.path);
const extension = extname(f.path); const extension = extname(file.path);
const fileId = f.id ?? basename(f.path, extension); const fileId = file.id ?? basename(file.path, extension);
const fileData = readFileSync(fileRootPath, 'utf-8'); const fileData = readFileSync(fileRootPath, 'utf8');
custom[categoryId]!.files[fileId] = { custom[categoryId]!.files[fileId] = {
name: f.name, name: file.name,
type: extension.toLowerCase().replace(/^\./, ''), type: extension.toLowerCase().replace(/^\./, ''),
content: fileData, content: fileData,
path: relative(root, fileRootPath).replace(/\\/g, '/'), path: relative(root, fileRootPath).replaceAll('\\', '/'),
}; };
} }
} }
const fileCount = Object.keys(custom) const fileCount = Object.keys(custom)
.map((k) => Object.keys(custom[k]!)) .map((key) => Object.keys(custom[key]!))
.reduce((prev, c) => prev + c.length, 0); .reduce((prev, content) => prev + content.length, 0);
const categoryCount = Object.keys(custom).length; const categoryCount = Object.keys(custom).length;
console.log( console.log(
`${fileCount} custom docs file${fileCount === 1 ? '' : 's'} in ` + `${fileCount} custom docs file${fileCount === 1 ? '' : 's'} in ` +
@@ -82,5 +83,6 @@ export function build({ input, custom: customDocs, root, output, typescript }: C
console.log(`Writing to ${output}...`); console.log(`Writing to ${output}...`);
writeFileSync(output, JSON.stringify(docs.serialize())); writeFileSync(output, JSON.stringify(docs.serialize()));
} }
console.log('Done!'); console.log('Done!');
} }

View File

@@ -1 +1 @@
export type Access = 'public' | 'private' | 'protected'; export type Access = 'private' | 'protected' | 'public';

View File

@@ -1,3 +1,3 @@
import type { Constructor, Event, Member, Method } from './index.js'; import type { Constructor, Event, Member, Method } from './index.js';
export type ChildTypes = Constructor | Member | Method | Event; export type ChildTypes = Constructor | Event | Member | Method;

View File

@@ -1,13 +1,13 @@
import type { Access, Item, Meta, Scope } from './index.js'; import type { Access, Item, Meta, Scope } from './index.js';
export interface Class extends Item { export interface Class extends Item {
kind: 'class';
scope: Scope;
implements?: string[];
augments?: string[];
see?: string[];
access?: Access; access?: Access;
virtual?: boolean; augments?: string[];
deprecated?: boolean | string; deprecated?: boolean | string;
implements?: string[];
kind: 'class';
meta: Meta; meta: Meta;
scope: Scope;
see?: string[];
virtual?: boolean;
} }

View File

@@ -1,7 +1,7 @@
export interface Config { export interface Config {
input: string[];
custom: string; custom: string;
root: string; input: string[];
output: string; output: string;
root: string;
typescript: boolean; typescript: boolean;
} }

View File

@@ -1,9 +1,9 @@
import type { Access, Item, Param } from './index.js'; import type { Access, Item, Param } from './index.js';
export interface Constructor extends Item { export interface Constructor extends Item {
access?: Access;
kind: 'constructor'; kind: 'constructor';
memberof: string; memberof: string;
see?: string[];
access?: Access;
params?: Param[]; params?: Param[];
see?: string[];
} }

View File

@@ -1,12 +1,12 @@
export interface CustomDocs { export interface CustomDocs {
name?: string;
files: Record< files: Record<
string, string,
{ {
name?: string;
type?: string;
content?: string; content?: string;
name?: string;
path?: string; path?: string;
type?: string;
} }
>; >;
name?: string;
} }

View File

@@ -1,11 +1,11 @@
import type { Item, Meta, Param, Scope } from './index.js'; import type { Item, Meta, Param, Scope } from './index.js';
export interface Event extends Item { export interface Event extends Item {
kind: 'event';
scope: Scope;
memberof: string;
see?: string[];
deprecated?: boolean | string; deprecated?: boolean | string;
params?: Param[]; kind: 'event';
memberof: string;
meta: Meta; meta: Meta;
params?: Param[];
scope: Scope;
see?: string[];
} }

View File

@@ -1,7 +1,7 @@
import type { Type } from './index.js'; import type { Type } from './index.js';
export interface Exception { export interface Exception {
type: Type;
nullable?: boolean;
description?: string; description?: string;
nullable?: boolean;
type: Type;
} }

View File

@@ -2,6 +2,6 @@ import type { Item, Meta } from './index.js';
export interface External extends Item { export interface External extends Item {
kind: 'external'; kind: 'external';
see?: string[];
meta: Meta; meta: Meta;
see?: string[];
} }

View File

@@ -1,7 +1,7 @@
import type { Class } from './index.js'; import type { Class } from './index.js';
// @ts-expect-error // @ts-expect-error: Inheritance type error
export interface Interface extends Class { export interface Interface extends Class {
kind: 'interface';
classdesc: string; classdesc: string;
kind: 'interface';
} }

View File

@@ -1,9 +1,9 @@
export interface Item { export interface Item {
description: string;
id: string; id: string;
ignore?: boolean;
kind: string;
longname: string; longname: string;
name: string; name: string;
kind: string;
description: string;
order: number; order: number;
ignore?: boolean;
} }

View File

@@ -1,17 +1,17 @@
import type { Access, Item, Meta, Param, Scope, Type } from './index.js'; import type { Access, Item, Meta, Param, Scope, Type } from './index.js';
export interface Member extends Item { export interface Member extends Item {
kind: 'member';
see?: string[];
scope: Scope;
memberof: string;
type: Type;
access?: Access; access?: Access;
readonly?: boolean;
nullable?: boolean;
virtual?: boolean;
deprecated?: boolean | string;
default?: string; default?: string;
properties?: Param[]; deprecated?: boolean | string;
kind: 'member';
memberof: string;
meta: Meta; meta: Meta;
nullable?: boolean;
properties?: Param[];
readonly?: boolean;
scope: Scope;
see?: string[];
type: Type;
virtual?: boolean;
} }

View File

@@ -1,5 +1,5 @@
export interface Meta { export interface Meta {
lineno: number;
filename: string; filename: string;
lineno: number;
path: string; path: string;
} }

View File

@@ -1,22 +1,22 @@
import type { Access, Exception, Item, Meta, Param, Return, Scope } from './index.js'; import type { Access, Exception, Item, Meta, Param, Return, Scope } from './index.js';
export interface Method extends Item { export interface Method extends Item {
kind: 'function';
see?: string[];
scope: Scope;
access?: Access; access?: Access;
inherits?: string;
inherited?: boolean;
implements?: string[];
examples?: string[];
virtual?: boolean;
deprecated?: boolean | string;
memberof?: string;
params?: Param[];
async?: boolean; async?: boolean;
generator?: boolean; deprecated?: boolean | string;
fires?: string[]; examples?: string[];
returns?: Return[];
exceptions?: Exception[]; exceptions?: Exception[];
fires?: string[];
generator?: boolean;
implements?: string[];
inherited?: boolean;
inherits?: string;
kind: 'function';
memberof?: string;
meta: Meta; meta: Meta;
params?: Param[];
returns?: Return[];
scope: Scope;
see?: string[];
virtual?: boolean;
} }

View File

@@ -1,11 +1,11 @@
import type { Type } from './index.js'; import type { Type } from './index.js';
export interface Param { export interface Param {
type: Type; defaultvalue?: string;
description: string; description: string;
name: string; name: string;
optional?: boolean;
defaultvalue?: string;
variable?: string;
nullable?: boolean; nullable?: boolean;
optional?: boolean;
type: Type;
variable?: string;
} }

View File

@@ -1,7 +1,7 @@
import type { Type } from './index.js'; import type { Type } from './index.js';
export interface Return { export interface Return {
type: Required<Type>;
nullable?: boolean;
description?: string; description?: string;
nullable?: boolean;
type: Required<Type>;
} }

View File

@@ -1,3 +1,3 @@
import type { Class, External, Interface, Method, Typedef } from './index.js'; import type { Class, External, Interface, Method, Typedef } from './index.js';
export type RootTypes = Class | Method | Interface | Typedef | External; export type RootTypes = Class | External | Interface | Method | Typedef;

View File

@@ -1,14 +1,14 @@
import type { Access, Item, Meta, Param, Return, Scope, Type } from './index.js'; import type { Access, Item, Meta, Param, Return, Scope, Type } from './index.js';
export interface Typedef extends Item { export interface Typedef extends Item {
kind: 'typedef';
scope: Scope;
see?: string[];
access?: Access; access?: Access;
deprecated?: boolean | string; deprecated?: boolean | string;
type: Type; kind: 'typedef';
properties?: Param[];
params?: Param[];
returns?: Return[];
meta: Meta; meta: Meta;
params?: Param[];
properties?: Param[];
returns?: Return[];
scope: Scope;
see?: string[];
type: Type;
} }

View File

@@ -1,7 +1,7 @@
import type { Type } from './index.js'; import type { Type } from './index.js';
export interface VarType extends Type { export interface VarType extends Type {
type?: Required<Type> | undefined;
description?: string | undefined; description?: string | undefined;
nullable?: boolean | undefined; nullable?: boolean | undefined;
type?: Required<Type> | undefined;
} }

View File

@@ -1,5 +1,7 @@
import { parse } from 'node:path'; import { parse } from 'node:path';
import type { DeclarationReflection } from 'typedoc'; import type { DeclarationReflection } from 'typedoc';
import type { Class, Config } from '../interfaces/index.js';
import { parseType } from '../util/parseType.js';
import { DocumentedConstructor } from './constructor.js'; import { DocumentedConstructor } from './constructor.js';
import { DocumentedEvent } from './event.js'; import { DocumentedEvent } from './event.js';
import { DocumentedItemMeta } from './item-meta.js'; import { DocumentedItemMeta } from './item-meta.js';
@@ -7,8 +9,6 @@ import { DocumentedItem } from './item.js';
import { DocumentedMember } from './member.js'; import { DocumentedMember } from './member.js';
import { DocumentedMethod } from './method.js'; import { DocumentedMethod } from './method.js';
import { DocumentedVarType } from './var-type.js'; import { DocumentedVarType } from './var-type.js';
import type { Class, Config } from '../interfaces/index.js';
import { parseType } from '../util/parseType.js';
export class DocumentedClass extends DocumentedItem<Class | DeclarationReflection> { export class DocumentedClass extends DocumentedItem<Class | DeclarationReflection> {
public readonly props = new Map<string, DocumentedMember>(); public readonly props = new Map<string, DocumentedMember>();
@@ -27,51 +27,55 @@ export class DocumentedClass extends DocumentedItem<Class | DeclarationReflectio
super(data, config); super(data, config);
if (config.typescript) { if (config.typescript) {
const d = data as DeclarationReflection; const newData = data as DeclarationReflection;
const extended = d.extendedTypes?.[0]; const extended = newData.extendedTypes?.[0];
if (extended) { if (extended) {
this.extends = new DocumentedVarType({ names: [parseType(extended)] }, this.config); this.extends = new DocumentedVarType({ names: [parseType(extended)] }, this.config);
} }
const implemented = d.implementedTypes?.[0]; const implemented = newData.implementedTypes?.[0];
if (implemented) { if (implemented) {
this.implements = new DocumentedVarType({ names: [parseType(implemented)] }, this.config); this.implements = new DocumentedVarType({ names: [parseType(implemented)] }, this.config);
} }
} else { } else {
const d = data as Class; const newData = data as Class;
if (d.augments) { if (newData.augments) {
this.extends = new DocumentedVarType({ names: d.augments }, this.config); this.extends = new DocumentedVarType({ names: newData.augments }, this.config);
} }
if (d.implements) { if (newData.implements) {
this.implements = new DocumentedVarType({ names: d.implements }, this.config); this.implements = new DocumentedVarType({ names: newData.implements }, this.config);
} }
} }
} }
public add(item: DocumentedConstructor | DocumentedMethod | DocumentedMember | DocumentedEvent) { public add(item: DocumentedConstructor | DocumentedEvent | DocumentedMember | DocumentedMethod) {
if (item instanceof DocumentedConstructor) { if (item instanceof DocumentedConstructor) {
if (this.construct) { if (this.construct) {
throw new Error(`Doc ${this.data.name} already has constructor`); throw new Error(`Doc ${this.data.name} already has constructor`);
} }
this.construct = item; this.construct = item;
} else if (item instanceof DocumentedMethod) { } else if (item instanceof DocumentedMethod) {
// @ts-expect-error // @ts-expect-error: No type for methods
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const prefix = item.data.scope === 'static' || item.data.flags?.isStatic ? 's-' : ''; const prefix = item.data.scope === 'static' || item.data.flags?.isStatic ? 's-' : '';
if (this.methods.has(prefix + item.data.name)) { if (this.methods.has(prefix + item.data.name)) {
throw new Error(`Doc ${this.data.name} already has method ${item.data.name}`); throw new Error(`Doc ${this.data.name} already has method ${item.data.name}`);
} }
this.methods.set(prefix + item.data.name, item); this.methods.set(prefix + item.data.name, item);
} else if (item instanceof DocumentedMember) { } else if (item instanceof DocumentedMember) {
if (this.props.has(item.data.name)) { if (this.props.has(item.data.name)) {
throw new Error(`Doc ${this.data.name} already has prop ${item.data.name}`); throw new Error(`Doc ${this.data.name} already has prop ${item.data.name}`);
} }
this.props.set(item.data.name, item); this.props.set(item.data.name, item);
} else if (item instanceof DocumentedEvent) { } else if (item instanceof DocumentedEvent) {
if (this.events.has(item.data.name)) { if (this.events.has(item.data.name)) {
throw new Error(`Doc ${this.data.name} already has event ${item.data.name}`); throw new Error(`Doc ${this.data.name} already has event ${item.data.name}`);
} }
this.events.set(item.data.name, item); this.events.set(item.data.name, item);
} }
} }
@@ -88,17 +92,17 @@ export class DocumentedClass extends DocumentedItem<Class | DeclarationReflectio
} }
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const see = signature.comment?.blockTags?.filter((t) => t.tag === '@see').length const see = signature.comment?.blockTags?.filter((block) => block.tag === '@see').length
? signature.comment.blockTags ? signature.comment.blockTags
.filter((t) => t.tag === '@see') .filter((block) => block.tag === '@see')
.map((t) => t.content.find((c) => c.kind === 'text')?.text.trim()) .map((block) => block.content.find((contentText) => contentText.kind === 'text')?.text.trim())
: undefined; : undefined;
return { return {
// @ts-expect-error // @ts-expect-error: Type cannot be inferred
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
name: signature.name === 'default' ? parse(meta?.file ?? 'default').name : signature.name, name: signature.name === 'default' ? parse(meta?.file ?? 'default').name : signature.name,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing, no-param-reassign
description: signature.comment?.summary.reduce((prev, curr) => (prev += curr.text), '').trim() || undefined, description: signature.comment?.summary.reduce((prev, curr) => (prev += curr.text), '').trim() || undefined,
see, see,
extends: this.extends?.serialize(), extends: this.extends?.serialize(),
@@ -106,22 +110,23 @@ export class DocumentedClass extends DocumentedItem<Class | DeclarationReflectio
access: access:
data.flags.isPrivate || data.flags.isPrivate ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
signature.comment?.blockTags?.some((t) => t.tag === '@private' || t.tag === '@internal') signature.comment?.blockTags?.some((block) => block.tag === '@private' || block.tag === '@internal')
? 'private' ? 'private'
: undefined, : undefined,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition
abstract: signature.comment?.blockTags?.some((t) => t.tag === '@abstract') || undefined, abstract: signature.comment?.blockTags?.some((block) => block.tag === '@abstract') || undefined,
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
deprecated: signature.comment?.blockTags?.some((t) => t.tag === '@deprecated') deprecated: signature.comment?.blockTags?.some((block) => block.tag === '@deprecated')
? signature.comment.blockTags ? signature.comment.blockTags
.find((t) => t.tag === '@deprecated') .find((block) => block.tag === '@deprecated')
// eslint-disable-next-line no-param-reassign
?.content.reduce((prev, curr) => (prev += curr.text), '') ?.content.reduce((prev, curr) => (prev += curr.text), '')
.trim() ?? true .trim() ?? true
: undefined, : undefined,
construct: this.construct?.serialize(), construct: this.construct?.serialize(),
props: this.props.size ? [...this.props.values()].map((p) => p.serialize()) : undefined, props: this.props.size ? [...this.props.values()].map((param) => param.serialize()) : undefined,
methods: this.methods.size ? [...this.methods.values()].map((m) => m.serialize()) : undefined, methods: this.methods.size ? [...this.methods.values()].map((method) => method.serialize()) : undefined,
events: this.events.size ? [...this.events.values()].map((e) => e.serialize()) : undefined, events: this.events.size ? [...this.events.values()].map((event) => event.serialize()) : undefined,
meta, meta,
}; };
} }
@@ -137,9 +142,9 @@ export class DocumentedClass extends DocumentedItem<Class | DeclarationReflectio
abstract: data.virtual, abstract: data.virtual,
deprecated: data.deprecated, deprecated: data.deprecated,
construct: this.construct?.serialize(), construct: this.construct?.serialize(),
props: this.props.size ? [...this.props.values()].map((p) => p.serialize()) : undefined, props: this.props.size ? [...this.props.values()].map((param) => param.serialize()) : undefined,
methods: this.methods.size ? [...this.methods.values()].map((m) => m.serialize()) : undefined, methods: this.methods.size ? [...this.methods.values()].map((method) => method.serialize()) : undefined,
events: this.events.size ? [...this.events.values()].map((e) => e.serialize()) : undefined, events: this.events.size ? [...this.events.values()].map((event) => event.serialize()) : undefined,
meta: new DocumentedItemMeta(data.meta, this.config).serialize(), meta: new DocumentedItemMeta(data.meta, this.config).serialize(),
}; };
} }

View File

@@ -1,7 +1,7 @@
import type { DeclarationReflection, SignatureReflection } from 'typedoc'; import type { DeclarationReflection, SignatureReflection } from 'typedoc';
import type { Constructor } from '../interfaces/index.js';
import { DocumentedItem } from './item.js'; import { DocumentedItem } from './item.js';
import { DocumentedParam } from './param.js'; import { DocumentedParam } from './param.js';
import type { Constructor } from '../interfaces/index.js';
export class DocumentedConstructor extends DocumentedItem<Constructor | DeclarationReflection> { export class DocumentedConstructor extends DocumentedItem<Constructor | DeclarationReflection> {
public override serializer() { public override serializer() {
@@ -10,26 +10,28 @@ export class DocumentedConstructor extends DocumentedItem<Constructor | Declarat
const signature = (data.signatures ?? [])[0] ?? data; const signature = (data.signatures ?? [])[0] ?? data;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const see = signature.comment?.blockTags?.filter((t) => t.tag === '@see').length const see = signature.comment?.blockTags?.filter((block) => block.tag === '@see').length
? signature.comment.blockTags ? signature.comment.blockTags
.filter((t) => t.tag === '@see') .filter((block) => block.tag === '@see')
.map((t) => t.content.find((c) => c.kind === 'text')?.text.trim()) .map((block) => block.content.find((textContent) => textContent.kind === 'text')?.text.trim())
: undefined; : undefined;
return { return {
name: signature.name, name: signature.name,
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/prefer-nullish-coalescing // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/prefer-nullish-coalescing, no-param-reassign
description: signature.comment?.summary?.reduce((prev, curr) => (prev += curr.text), '').trim() || undefined, description: signature.comment?.summary?.reduce((prev, curr) => (prev += curr.text), '').trim() || undefined,
see, see,
access: access:
data.flags.isPrivate || data.flags.isPrivate ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
signature.comment?.blockTags?.some((t) => t.tag === '@private' || t.tag === '@internal') signature.comment?.blockTags?.some((block) => block.tag === '@private' || block.tag === '@internal')
? 'private' ? 'private'
: undefined, : undefined,
// @ts-expect-error // @ts-expect-error: No type for params
params: signature.parameters params: signature.parameters
? (signature as SignatureReflection).parameters?.map((p) => new DocumentedParam(p, this.config).serialize()) ? (signature as SignatureReflection).parameters?.map((param) =>
new DocumentedParam(param, this.config).serialize(),
)
: undefined, : undefined,
}; };
} }
@@ -40,7 +42,9 @@ export class DocumentedConstructor extends DocumentedItem<Constructor | Declarat
description: data.description, description: data.description,
see: data.see, see: data.see,
access: data.access, access: data.access,
params: data.params?.length ? data.params.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, params: data.params?.length
? data.params.map((param) => new DocumentedParam(param, this.config).serialize())
: undefined,
}; };
} }
} }

Some files were not shown because too many files have changed in this diff Show More