fix(Util): Resolve circular dependency (#11276)

* fix(Util): resolve circular

* refactor: lazily load all other circulars
This commit is contained in:
Jiralite
2025-11-15 10:06:05 +00:00
committed by GitHub
parent 7349a6ee3e
commit 9d5c6b4588
5 changed files with 81 additions and 71 deletions

View File

@@ -1,13 +1,20 @@
'use strict';
const { Buffer } = require('node:buffer');
const { isJSONEncodable } = require('@discordjs/util');
const { isJSONEncodable, lazy } = require('@discordjs/util');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const { DiscordjsError, DiscordjsRangeError, ErrorCodes } = require('../errors/index.js');
const { resolveFile } = require('../util/DataResolver.js');
const { MessageFlagsBitField } = require('../util/MessageFlagsBitField.js');
const { findName, verifyString, resolvePartialEmoji } = require('../util/Util.js');
// Fixes circular dependencies.
const getWebhook = lazy(() => require('./Webhook.js').Webhook);
const getUser = lazy(() => require('./User.js').User);
const getGuildMember = lazy(() => require('./GuildMember.js').GuildMember);
const getMessage = lazy(() => require('./Message.js').Message);
const getMessageManager = lazy(() => require('../managers/MessageManager.js').MessageManager);
/**
* Represents a message to be sent to the API.
*/
@@ -53,8 +60,7 @@ class MessagePayload {
* @readonly
*/
get isWebhook() {
const { Webhook } = require('./Webhook.js');
return this.target instanceof Webhook;
return this.target instanceof getWebhook();
}
/**
@@ -64,9 +70,7 @@ class MessagePayload {
* @readonly
*/
get isUser() {
const { User } = require('./User.js');
const { GuildMember } = require('./GuildMember.js');
return this.target instanceof User || this.target instanceof GuildMember;
return this.target instanceof getUser() || this.target instanceof getGuildMember();
}
/**
@@ -76,8 +80,7 @@ class MessagePayload {
* @readonly
*/
get isMessage() {
const { Message } = require('./Message.js');
return this.target instanceof Message;
return this.target instanceof getMessage();
}
/**
@@ -87,8 +90,7 @@ class MessagePayload {
* @readonly
*/
get isMessageManager() {
const { MessageManager } = require('../managers/MessageManager.js');
return this.target instanceof MessageManager;
return this.target instanceof getMessageManager();
}
/**

View File

@@ -1,14 +1,17 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { lazy } = require('@discordjs/util');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const { InteractionType, Routes } = require('discord-api-types/v10');
const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../../errors/index.js');
const { MaxBulkDeletableMessageAge } = require('../../util/Constants.js');
const { InteractionCollector } = require('../InteractionCollector.js');
// eslint-disable-next-line import-x/order
const { MessageCollector } = require('../MessageCollector.js');
// Fixes circular dependencies.
const getGuildMessageManager = lazy(() => require('../../managers/GuildMessageManager.js').GuildMessageManager);
/**
* Interface for classes that have text-channel-like features.
*
@@ -21,8 +24,7 @@ class TextBasedChannel {
*
* @type {GuildMessageManager}
*/
// eslint-disable-next-line no-use-before-define
this.messages = new GuildMessageManager(this);
this.messages = new (getGuildMessageManager())(this);
/**
* The channel's last message id, if one was sent
@@ -427,7 +429,3 @@ class TextBasedChannel {
}
exports.TextBasedChannel = TextBasedChannel;
// Fixes Circular
// eslint-disable-next-line import-x/order
const { GuildMessageManager } = require('../../managers/GuildMessageManager.js');

View File

@@ -1,9 +1,37 @@
/* eslint-disable no-use-before-define */
'use strict';
// eslint-disable-next-line import-x/order
const { lazy } = require('@discordjs/util');
const { ComponentType } = require('discord-api-types/v10');
// Fixes circular dependencies.
const getActionRow = lazy(() => require('../structures/ActionRow.js').ActionRow);
const getButtonComponent = lazy(() => require('../structures/ButtonComponent.js').ButtonComponent);
const getChannelSelectMenuComponent = lazy(
() => require('../structures/ChannelSelectMenuComponent.js').ChannelSelectMenuComponent,
);
const getComponent = lazy(() => require('../structures/Component.js').Component);
const getContainerComponent = lazy(() => require('../structures/ContainerComponent.js').ContainerComponent);
const getFileComponent = lazy(() => require('../structures/FileComponent.js').FileComponent);
const getLabelComponent = lazy(() => require('../structures/LabelComponent.js').LabelComponent);
const getMediaGalleryComponent = lazy(() => require('../structures/MediaGalleryComponent.js').MediaGalleryComponent);
const getMentionableSelectMenuComponent = lazy(
() => require('../structures/MentionableSelectMenuComponent.js').MentionableSelectMenuComponent,
);
const getRoleSelectMenuComponent = lazy(
() => require('../structures/RoleSelectMenuComponent.js').RoleSelectMenuComponent,
);
const getSectionComponent = lazy(() => require('../structures/SectionComponent.js').SectionComponent);
const getSeparatorComponent = lazy(() => require('../structures/SeparatorComponent.js').SeparatorComponent);
const getStringSelectMenuComponent = lazy(
() => require('../structures/StringSelectMenuComponent.js').StringSelectMenuComponent,
);
const getTextDisplayComponent = lazy(() => require('../structures/TextDisplayComponent.js').TextDisplayComponent);
const getTextInputComponent = lazy(() => require('../structures/TextInputComponent.js').TextInputComponent);
const getThumbnailComponent = lazy(() => require('../structures/ThumbnailComponent.js').ThumbnailComponent);
const getUserSelectMenuComponent = lazy(
() => require('../structures/UserSelectMenuComponent.js').UserSelectMenuComponent,
);
/**
* @typedef {Object} BaseComponentData
* @property {number} [id] the id of this component
@@ -192,6 +220,25 @@ const { ComponentType } = require('discord-api-types/v10');
* SectionComponent|SeparatorComponent|TextDisplayComponent} MessageTopLevelComponent
*/
const ComponentTypeToClass = {
[ComponentType.ActionRow]: getActionRow,
[ComponentType.Button]: getButtonComponent,
[ComponentType.StringSelect]: getStringSelectMenuComponent,
[ComponentType.TextInput]: getTextInputComponent,
[ComponentType.UserSelect]: getUserSelectMenuComponent,
[ComponentType.RoleSelect]: getRoleSelectMenuComponent,
[ComponentType.MentionableSelect]: getMentionableSelectMenuComponent,
[ComponentType.ChannelSelect]: getChannelSelectMenuComponent,
[ComponentType.Container]: getContainerComponent,
[ComponentType.TextDisplay]: getTextDisplayComponent,
[ComponentType.File]: getFileComponent,
[ComponentType.MediaGallery]: getMediaGalleryComponent,
[ComponentType.Section]: getSectionComponent,
[ComponentType.Separator]: getSeparatorComponent,
[ComponentType.Thumbnail]: getThumbnailComponent,
[ComponentType.Label]: getLabelComponent,
};
/**
* Transforms API data into a component
*
@@ -200,7 +247,7 @@ const { ComponentType } = require('discord-api-types/v10');
* @ignore
*/
function createComponent(data) {
return data instanceof Component ? data : new (ComponentTypeToClass[data.type] ?? Component)(data);
return data instanceof getComponent() ? data : new (ComponentTypeToClass[data.type]?.() ?? getComponent())(data);
}
/**
@@ -241,40 +288,3 @@ function findComponentByCustomId(components, customId) {
exports.createComponent = createComponent;
exports.findComponentByCustomId = findComponentByCustomId;
const { ActionRow } = require('../structures/ActionRow.js');
const { ButtonComponent } = require('../structures/ButtonComponent.js');
const { ChannelSelectMenuComponent } = require('../structures/ChannelSelectMenuComponent.js');
const { Component } = require('../structures/Component.js');
const { ContainerComponent } = require('../structures/ContainerComponent.js');
const { FileComponent } = require('../structures/FileComponent.js');
const { LabelComponent } = require('../structures/LabelComponent.js');
const { MediaGalleryComponent } = require('../structures/MediaGalleryComponent.js');
const { MentionableSelectMenuComponent } = require('../structures/MentionableSelectMenuComponent.js');
const { RoleSelectMenuComponent } = require('../structures/RoleSelectMenuComponent.js');
const { SectionComponent } = require('../structures/SectionComponent.js');
const { SeparatorComponent } = require('../structures/SeparatorComponent.js');
const { StringSelectMenuComponent } = require('../structures/StringSelectMenuComponent.js');
const { TextDisplayComponent } = require('../structures/TextDisplayComponent.js');
const { TextInputComponent } = require('../structures/TextInputComponent.js');
const { ThumbnailComponent } = require('../structures/ThumbnailComponent.js');
const { UserSelectMenuComponent } = require('../structures/UserSelectMenuComponent.js');
const ComponentTypeToClass = {
[ComponentType.ActionRow]: ActionRow,
[ComponentType.Button]: ButtonComponent,
[ComponentType.StringSelect]: StringSelectMenuComponent,
[ComponentType.TextInput]: TextInputComponent,
[ComponentType.UserSelect]: UserSelectMenuComponent,
[ComponentType.RoleSelect]: RoleSelectMenuComponent,
[ComponentType.MentionableSelect]: MentionableSelectMenuComponent,
[ComponentType.ChannelSelect]: ChannelSelectMenuComponent,
[ComponentType.Container]: ContainerComponent,
[ComponentType.TextDisplay]: TextDisplayComponent,
[ComponentType.File]: FileComponent,
[ComponentType.MediaGallery]: MediaGalleryComponent,
[ComponentType.Section]: SectionComponent,
[ComponentType.Separator]: SeparatorComponent,
[ComponentType.Thumbnail]: ThumbnailComponent,
[ComponentType.Label]: LabelComponent,
};

View File

@@ -3,10 +3,14 @@
const { Buffer } = require('node:buffer');
const fs = require('node:fs/promises');
const path = require('node:path');
const { lazy } = require('@discordjs/util');
const { fetch } = require('undici');
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
const { BaseInvite } = require('../structures/BaseInvite.js');
// Fixes circular dependencies.
const getGuildTemplate = lazy(() => require('../structures/GuildTemplate.js').GuildTemplate);
/**
* Data that can be resolved to give an invite code. This can be:
* - An invite code
@@ -54,8 +58,7 @@ function resolveInviteCode(data) {
* @private
*/
function resolveGuildTemplateCode(data) {
const { GuildTemplate } = require('../structures/GuildTemplate.js');
return resolveCode(data, GuildTemplate.GuildTemplatesPattern);
return resolveCode(data, getGuildTemplate().GuildTemplatesPattern);
}
/**

View File

@@ -2,13 +2,18 @@
const { parse } = require('node:path');
const { Collection } = require('@discordjs/collection');
const { lazy } = require('@discordjs/util');
const { ChannelType, RouteBases, Routes } = require('discord-api-types/v10');
const { fetch } = require('undici');
// eslint-disable-next-line import-x/order
const { Colors } = require('./Colors.js');
// eslint-disable-next-line import-x/order
const { DiscordjsError, DiscordjsRangeError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
// Fixes circular dependencies.
const getAttachment = lazy(() => require('../structures/Attachment.js').Attachment);
const getGuildChannel = lazy(() => require('../structures/GuildChannel.js').GuildChannel);
const getSKU = lazy(() => require('../structures/SKU.js').SKU);
const isObject = data => typeof data === 'object' && data !== null;
/**
@@ -352,8 +357,7 @@ function resolveColor(color) {
* @returns {Collection}
*/
function discordSort(collection) {
// eslint-disable-next-line no-use-before-define
const isGuildChannel = collection.first() instanceof GuildChannel;
const isGuildChannel = collection.first() instanceof getGuildChannel();
return collection.toSorted(
isGuildChannel
? (a, b) => a.rawPosition - b.rawPosition || Number(BigInt(a.id) - BigInt(b.id))
@@ -555,8 +559,7 @@ function transformResolved(
if (attachments) {
result.attachments = new Collection();
for (const attachment of Object.values(attachments)) {
// eslint-disable-next-line no-use-before-define
const patched = new Attachment(attachment);
const patched = new (getAttachment())(attachment);
result.attachments.set(attachment.id, patched);
}
}
@@ -572,8 +575,7 @@ function transformResolved(
*/
function resolveSKUId(resolvable) {
if (typeof resolvable === 'string') return resolvable;
// eslint-disable-next-line no-use-before-define
if (resolvable instanceof SKU) return resolvable.id;
if (resolvable instanceof getSKU()) return resolvable.id;
return null;
}
@@ -600,8 +602,3 @@ exports.setPosition = setPosition;
exports.basename = basename;
exports.findName = findName;
exports.transformResolved = transformResolved;
// Fixes Circular
const { Attachment } = require('../structures/Attachment.js');
const { GuildChannel } = require('../structures/GuildChannel.js');
const { SKU } = require('../structures/SKU.js');