Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add @discordjs/util #8591

Merged
merged 14 commits into from Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/labeler.yml
Expand Up @@ -37,3 +37,7 @@
'packages:ws':
- packages/ws/*
- packages/ws/**/*

'packages:util':
- packages/util/*
- packages/util/**/*
2 changes: 2 additions & 0 deletions .github/labels.yml
Expand Up @@ -66,6 +66,8 @@
color: 'fbca04'
- name: 'packages:ws'
color: 'fbca04'
- name: 'packages:util'
color: 'fbca04'
- name: 'performance'
color: '80c042'
- name: 'permissions'
Expand Down
6 changes: 6 additions & 0 deletions packages/actions/src/uploadCoverage/action.yml
Expand Up @@ -51,6 +51,12 @@ runs:
files: ./packages/ws/coverage/cobertura-coverage.xml
flags: ws

- name: Upload Util Coverage
uses: codecov/codecov-action@v3
with:
files: ./packages/util/coverage/cobertura-coverage.xml
flags: util

- name: Upload Utilities Coverage
uses: codecov/codecov-action@v3
with:
Expand Down
30 changes: 1 addition & 29 deletions packages/builders/__tests__/util.test.ts
@@ -1,33 +1,5 @@
import { describe, test, expect } from 'vitest';
import {
isJSONEncodable,
isEquatable,
ActionRowBuilder,
enableValidators,
disableValidators,
isValidationEnabled,
} from '../src/index.js';

describe('isEquatable', () => {
test('returns true if the object is equatable', () => {
expect(isEquatable({ equals: () => true })).toBeTruthy();
});

test('returns false if the object is not equatable', () => {
expect(isEquatable({})).toBeFalsy();
});
});

describe('isJSONEncodable', () => {
test('returns true if the object is JSON encodable', () => {
expect(isJSONEncodable({ toJSON: () => ({}) })).toBeTruthy();
expect(isJSONEncodable(new ActionRowBuilder())).toBeTruthy();
});

test('returns false if the object is not JSON encodable', () => {
expect(isJSONEncodable({})).toBeFalsy();
});
});
import { enableValidators, disableValidators, isValidationEnabled } from '../src/index.js';

describe('validation', () => {
test('enables validation', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/builders/package.json
Expand Up @@ -54,6 +54,7 @@
},
"homepage": "https://discord.js.org",
"dependencies": {
"@discordjs/util": "workspace:^",
"@sapphire/shapeshift": "^3.6.0",
"discord-api-types": "^0.37.11",
"fast-deep-equal": "^3.1.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/builders/src/components/Component.ts
@@ -1,10 +1,10 @@
import type { JSONEncodable } from '@discordjs/util';
import type {
APIActionRowComponent,
APIActionRowComponentTypes,
APIBaseComponent,
ComponentType,
} from 'discord-api-types/v10';
import type { JSONEncodable } from '../util/jsonEncodable';

export type AnyAPIActionRowComponent = APIActionRowComponent<APIActionRowComponentTypes> | APIActionRowComponentTypes;

Expand Down
@@ -1,5 +1,5 @@
import type { JSONEncodable } from '@discordjs/util';
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
import type { JSONEncodable } from '../../util/jsonEncodable.js';
import {
defaultValidator,
emojiValidator,
Expand Down
3 changes: 1 addition & 2 deletions packages/builders/src/components/textInput/TextInput.ts
@@ -1,7 +1,6 @@
import { isJSONEncodable, type Equatable, type JSONEncodable } from '@discordjs/util';
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import isEqual from 'fast-deep-equal';
import type { Equatable } from '../../util/equatable';
import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable.js';
import { customIdValidator } from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
import {
Expand Down
3 changes: 1 addition & 2 deletions packages/builders/src/index.ts
Expand Up @@ -36,8 +36,7 @@ export * from './interactions/slashCommands/mixins/SharedSlashCommandOptions.js'
export * as ContextMenuCommandAssertions from './interactions/contextMenuCommands/Assertions.js';
export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder.js';

export * from './util/jsonEncodable.js';
suneettipirneni marked this conversation as resolved.
Show resolved Hide resolved
export * from './util/equatable.js';
export * from './util/componentUtil.js';
export * from './util/normalizeArray.js';
export * from './util/validation.js';
export * from '@discordjs/util';
2 changes: 1 addition & 1 deletion packages/builders/src/interactions/modals/Modal.ts
@@ -1,3 +1,4 @@
import type { JSONEncodable } from '@discordjs/util';
import type {
APIActionRowComponent,
APIModalActionRowComponent,
Expand All @@ -6,7 +7,6 @@ import type {
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { createComponentBuilder } from '../../components/Components.js';
import type { JSONEncodable } from '../../util/jsonEncodable';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { titleValidator, validateRequiredParameters } from './Assertions.js';

Expand Down
1 change: 1 addition & 0 deletions packages/discord.js/package.json
Expand Up @@ -52,6 +52,7 @@
"@discordjs/builders": "workspace:^",
"@discordjs/collection": "workspace:^",
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/snowflake": "^3.2.2",
"@types/ws": "^8.5.3",
"discord-api-types": "^0.37.11",
Expand Down
@@ -1,9 +1,9 @@
'use strict';

const { lazy } = require('@discordjs/util');
const { ApplicationCommandOptionType } = require('discord-api-types/v10');
const CommandInteraction = require('./CommandInteraction');
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

Expand Down
@@ -1,9 +1,9 @@
'use strict';

const { lazy } = require('@discordjs/util');
const BaseInteraction = require('./BaseInteraction');
const InteractionWebhook = require('./InteractionWebhook');
const InteractionResponses = require('./interfaces/InteractionResponses');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

Expand Down
3 changes: 2 additions & 1 deletion packages/discord.js/src/structures/MessagePayload.js
Expand Up @@ -2,12 +2,13 @@

const { Buffer } = require('node:buffer');
const { isJSONEncodable } = require('@discordjs/builders');
const { lazy } = require('@discordjs/util');
const { MessageFlags } = require('discord-api-types/v10');
const ActionRowBuilder = require('./ActionRowBuilder');
const { RangeError, ErrorCodes } = require('../errors');
const DataResolver = require('../util/DataResolver');
const MessageFlagsBitField = require('../util/MessageFlagsBitField');
const { basename, verifyString, lazy } = require('../util/Util');
const { basename, verifyString } = require('../util/Util');
suneettipirneni marked this conversation as resolved.
Show resolved Hide resolved

const getBaseInteraction = lazy(() => require('./BaseInteraction'));

Expand Down
@@ -1,10 +1,10 @@
'use strict';

const { lazy } = require('@discordjs/util');
const BaseInteraction = require('./BaseInteraction');
const InteractionWebhook = require('./InteractionWebhook');
const ModalSubmitFields = require('./ModalSubmitFields');
const InteractionResponses = require('./interfaces/InteractionResponses');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

Expand Down
2 changes: 1 addition & 1 deletion packages/discord.js/src/structures/Webhook.js
@@ -1,12 +1,12 @@
'use strict';

const { makeURLSearchParams } = require('@discordjs/rest');
const { lazy } = require('@discordjs/util');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const { Routes, WebhookType } = require('discord-api-types/v10');
const MessagePayload = require('./MessagePayload');
const { Error, ErrorCodes } = require('../errors');
const DataResolver = require('../util/DataResolver');
const { lazy } = require('../util/Util');

const getMessage = lazy(() => require('./Message').Message);

Expand Down
2 changes: 1 addition & 1 deletion packages/discord.js/src/util/Channels.js
@@ -1,7 +1,7 @@
'use strict';

const { lazy } = require('@discordjs/util');
const { ChannelType } = require('discord-api-types/v10');
const { lazy } = require('./Util');

const getCategoryChannel = lazy(() => require('../structures/CategoryChannel'));
const getDMChannel = lazy(() => require('../structures/DMChannel'));
Expand Down
11 changes: 0 additions & 11 deletions packages/discord.js/src/util/Util.js
Expand Up @@ -509,16 +509,6 @@ function cleanCodeBlockContent(text) {
return text.replaceAll('```', '`\u200b``');
}

/**
* Lazily evaluates a callback function
* @param {Function} cb The callback to lazily evaluate
* @returns {Function}
*/
function lazy(cb) {
let defaultValue;
return () => (defaultValue ??= cb());
}
suneettipirneni marked this conversation as resolved.
Show resolved Hide resolved

/**
* Parses a webhook URL for the id and token.
* @param {string} url The URL to parse
Expand Down Expand Up @@ -562,7 +552,6 @@ module.exports = {
basename,
cleanContent,
cleanCodeBlockContent,
lazy,
parseWebhookURL,
};

Expand Down
5 changes: 2 additions & 3 deletions packages/discord.js/typings/index.d.ts
Expand Up @@ -12,7 +12,6 @@ import {
hyperlink,
inlineCode,
italic,
JSONEncodable,
quote,
roleMention,
SelectMenuBuilder as BuilderSelectMenuComponent,
Expand All @@ -31,6 +30,7 @@ import {
ComponentBuilder,
type RestOrArray,
} from '@discordjs/builders';
import { Awaitable, JSONEncodable } from '@discordjs/util';
import { Collection } from '@discordjs/collection';
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
import {
Expand Down Expand Up @@ -4144,8 +4144,6 @@ export interface AuditLogChange {
new?: APIAuditLogChange['new_value'];
}

export type Awaitable<T> = T | PromiseLike<T>;

export type AwaitMessageComponentOptions<T extends CollectedMessageInteraction> = Omit<
MessageComponentCollectorOptions<T>,
'max' | 'maxComponents' | 'maxUsers'
Expand Down Expand Up @@ -5835,3 +5833,4 @@ export type InternalDiscordGatewayAdapterCreator = (
export * from 'discord-api-types/v10';
export * from '@discordjs/builders';
export * from '@discordjs/rest';
export * from '@discordjs/util';
1 change: 1 addition & 0 deletions packages/proxy/package.json
Expand Up @@ -55,6 +55,7 @@
"homepage": "https://discord.js.org",
"dependencies": {
"@discordjs/rest": "^1.0.0",
"@discordjs/util": "workspace:^",
"tslib": "^2.4.0",
"undici": "^5.10.0"
},
Expand Down
6 changes: 1 addition & 5 deletions packages/proxy/src/util/util.ts
@@ -1,9 +1,5 @@
import type { IncomingMessage, ServerResponse } from 'node:http';

/**
* Represents a potentially awaitable value
*/
export type Awaitable<T> = PromiseLike<T> | T;
import type { Awaitable } from '@discordjs/util';

/**
* Represents a simple HTTP request handler
Expand Down
1 change: 1 addition & 0 deletions packages/rest/package.json
Expand Up @@ -53,6 +53,7 @@
"homepage": "https://discord.js.org",
"dependencies": {
"@discordjs/collection": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/async-queue": "^1.5.0",
"@sapphire/snowflake": "^3.2.2",
"discord-api-types": "^0.37.11",
Expand Down
8 changes: 2 additions & 6 deletions packages/rest/src/lib/RequestManager.ts
Expand Up @@ -3,6 +3,7 @@ import { EventEmitter } from 'node:events';
import { setInterval, clearInterval } from 'node:timers';
import type { URLSearchParams } from 'node:url';
import { Collection } from '@discordjs/collection';
import { lazy } from '@discordjs/util';
import { DiscordSnowflake } from '@sapphire/snowflake';
import { FormData, type RequestInit, type BodyInit, type Dispatcher, type Agent } from 'undici';
import type { RESTOptions, RestEvents, RequestOptions } from './REST.js';
Expand All @@ -12,12 +13,7 @@ import { DefaultRestOptions, DefaultUserAgent, RESTEvents } from './utils/consta
import { resolveBody } from './utils/utils.js';

// Make this a lazy dynamic import as file-type is a pure ESM package
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const getFileType = async (): Promise<typeof import('file-type')> => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
let cached: Promise<typeof import('file-type')>;
return (cached ??= import('file-type'));
};
const getFileType = lazy(async () => import('file-type'));

/**
* Represents a file to be added to the request
Expand Down
3 changes: 3 additions & 0 deletions packages/util/.eslintrc.json
@@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc.json"
}
25 changes: 25 additions & 0 deletions packages/util/.gitignore
@@ -0,0 +1,25 @@
# Packages
node_modules/

# Log files
logs/
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Env
.env

# Dist
dist/
typings/

docs/**/*

# Miscellaneous
.tmp/
coverage/
1 change: 1 addition & 0 deletions packages/util/.lintstagedrc.js
@@ -0,0 +1 @@
module.exports = require('../../.lintstagedrc.json');
8 changes: 8 additions & 0 deletions packages/util/.prettierignore
@@ -0,0 +1,8 @@
# Autogenerated
CHANGELOG.md
.turbo
dist/
docs/**/*
!docs/index.yml
!docs/README.md
coverage/
1 change: 1 addition & 0 deletions packages/util/.prettierrc.js
@@ -0,0 +1 @@
module.exports = require('../../.prettierrc.json');