Skip to content

Commit 9d5c6b4

Browse files
authored
fix(Util): Resolve circular dependency (#11276)
* fix(Util): resolve circular * refactor: lazily load all other circulars
1 parent 7349a6e commit 9d5c6b4

File tree

5 files changed

+81
-71
lines changed

5 files changed

+81
-71
lines changed

packages/discord.js/src/structures/MessagePayload.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
'use strict';
22

33
const { Buffer } = require('node:buffer');
4-
const { isJSONEncodable } = require('@discordjs/util');
4+
const { isJSONEncodable, lazy } = require('@discordjs/util');
55
const { DiscordSnowflake } = require('@sapphire/snowflake');
66
const { DiscordjsError, DiscordjsRangeError, ErrorCodes } = require('../errors/index.js');
77
const { resolveFile } = require('../util/DataResolver.js');
88
const { MessageFlagsBitField } = require('../util/MessageFlagsBitField.js');
99
const { findName, verifyString, resolvePartialEmoji } = require('../util/Util.js');
1010

11+
// Fixes circular dependencies.
12+
const getWebhook = lazy(() => require('./Webhook.js').Webhook);
13+
const getUser = lazy(() => require('./User.js').User);
14+
const getGuildMember = lazy(() => require('./GuildMember.js').GuildMember);
15+
const getMessage = lazy(() => require('./Message.js').Message);
16+
const getMessageManager = lazy(() => require('../managers/MessageManager.js').MessageManager);
17+
1118
/**
1219
* Represents a message to be sent to the API.
1320
*/
@@ -53,8 +60,7 @@ class MessagePayload {
5360
* @readonly
5461
*/
5562
get isWebhook() {
56-
const { Webhook } = require('./Webhook.js');
57-
return this.target instanceof Webhook;
63+
return this.target instanceof getWebhook();
5864
}
5965

6066
/**
@@ -64,9 +70,7 @@ class MessagePayload {
6470
* @readonly
6571
*/
6672
get isUser() {
67-
const { User } = require('./User.js');
68-
const { GuildMember } = require('./GuildMember.js');
69-
return this.target instanceof User || this.target instanceof GuildMember;
73+
return this.target instanceof getUser() || this.target instanceof getGuildMember();
7074
}
7175

7276
/**
@@ -76,8 +80,7 @@ class MessagePayload {
7680
* @readonly
7781
*/
7882
get isMessage() {
79-
const { Message } = require('./Message.js');
80-
return this.target instanceof Message;
83+
return this.target instanceof getMessage();
8184
}
8285

8386
/**
@@ -87,8 +90,7 @@ class MessagePayload {
8790
* @readonly
8891
*/
8992
get isMessageManager() {
90-
const { MessageManager } = require('../managers/MessageManager.js');
91-
return this.target instanceof MessageManager;
93+
return this.target instanceof getMessageManager();
9294
}
9395

9496
/**

packages/discord.js/src/structures/interfaces/TextBasedChannel.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
'use strict';
22

33
const { Collection } = require('@discordjs/collection');
4+
const { lazy } = require('@discordjs/util');
45
const { DiscordSnowflake } = require('@sapphire/snowflake');
56
const { InteractionType, Routes } = require('discord-api-types/v10');
67
const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../../errors/index.js');
78
const { MaxBulkDeletableMessageAge } = require('../../util/Constants.js');
89
const { InteractionCollector } = require('../InteractionCollector.js');
9-
// eslint-disable-next-line import-x/order
1010
const { MessageCollector } = require('../MessageCollector.js');
1111

12+
// Fixes circular dependencies.
13+
const getGuildMessageManager = lazy(() => require('../../managers/GuildMessageManager.js').GuildMessageManager);
14+
1215
/**
1316
* Interface for classes that have text-channel-like features.
1417
*
@@ -21,8 +24,7 @@ class TextBasedChannel {
2124
*
2225
* @type {GuildMessageManager}
2326
*/
24-
// eslint-disable-next-line no-use-before-define
25-
this.messages = new GuildMessageManager(this);
27+
this.messages = new (getGuildMessageManager())(this);
2628

2729
/**
2830
* The channel's last message id, if one was sent
@@ -427,7 +429,3 @@ class TextBasedChannel {
427429
}
428430

429431
exports.TextBasedChannel = TextBasedChannel;
430-
431-
// Fixes Circular
432-
// eslint-disable-next-line import-x/order
433-
const { GuildMessageManager } = require('../../managers/GuildMessageManager.js');

packages/discord.js/src/util/Components.js

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
1-
/* eslint-disable no-use-before-define */
21
'use strict';
32

4-
// eslint-disable-next-line import-x/order
3+
const { lazy } = require('@discordjs/util');
54
const { ComponentType } = require('discord-api-types/v10');
65

6+
// Fixes circular dependencies.
7+
const getActionRow = lazy(() => require('../structures/ActionRow.js').ActionRow);
8+
const getButtonComponent = lazy(() => require('../structures/ButtonComponent.js').ButtonComponent);
9+
const getChannelSelectMenuComponent = lazy(
10+
() => require('../structures/ChannelSelectMenuComponent.js').ChannelSelectMenuComponent,
11+
);
12+
const getComponent = lazy(() => require('../structures/Component.js').Component);
13+
const getContainerComponent = lazy(() => require('../structures/ContainerComponent.js').ContainerComponent);
14+
const getFileComponent = lazy(() => require('../structures/FileComponent.js').FileComponent);
15+
const getLabelComponent = lazy(() => require('../structures/LabelComponent.js').LabelComponent);
16+
const getMediaGalleryComponent = lazy(() => require('../structures/MediaGalleryComponent.js').MediaGalleryComponent);
17+
const getMentionableSelectMenuComponent = lazy(
18+
() => require('../structures/MentionableSelectMenuComponent.js').MentionableSelectMenuComponent,
19+
);
20+
const getRoleSelectMenuComponent = lazy(
21+
() => require('../structures/RoleSelectMenuComponent.js').RoleSelectMenuComponent,
22+
);
23+
const getSectionComponent = lazy(() => require('../structures/SectionComponent.js').SectionComponent);
24+
const getSeparatorComponent = lazy(() => require('../structures/SeparatorComponent.js').SeparatorComponent);
25+
const getStringSelectMenuComponent = lazy(
26+
() => require('../structures/StringSelectMenuComponent.js').StringSelectMenuComponent,
27+
);
28+
const getTextDisplayComponent = lazy(() => require('../structures/TextDisplayComponent.js').TextDisplayComponent);
29+
const getTextInputComponent = lazy(() => require('../structures/TextInputComponent.js').TextInputComponent);
30+
const getThumbnailComponent = lazy(() => require('../structures/ThumbnailComponent.js').ThumbnailComponent);
31+
const getUserSelectMenuComponent = lazy(
32+
() => require('../structures/UserSelectMenuComponent.js').UserSelectMenuComponent,
33+
);
34+
735
/**
836
* @typedef {Object} BaseComponentData
937
* @property {number} [id] the id of this component
@@ -192,6 +220,25 @@ const { ComponentType } = require('discord-api-types/v10');
192220
* SectionComponent|SeparatorComponent|TextDisplayComponent} MessageTopLevelComponent
193221
*/
194222

223+
const ComponentTypeToClass = {
224+
[ComponentType.ActionRow]: getActionRow,
225+
[ComponentType.Button]: getButtonComponent,
226+
[ComponentType.StringSelect]: getStringSelectMenuComponent,
227+
[ComponentType.TextInput]: getTextInputComponent,
228+
[ComponentType.UserSelect]: getUserSelectMenuComponent,
229+
[ComponentType.RoleSelect]: getRoleSelectMenuComponent,
230+
[ComponentType.MentionableSelect]: getMentionableSelectMenuComponent,
231+
[ComponentType.ChannelSelect]: getChannelSelectMenuComponent,
232+
[ComponentType.Container]: getContainerComponent,
233+
[ComponentType.TextDisplay]: getTextDisplayComponent,
234+
[ComponentType.File]: getFileComponent,
235+
[ComponentType.MediaGallery]: getMediaGalleryComponent,
236+
[ComponentType.Section]: getSectionComponent,
237+
[ComponentType.Separator]: getSeparatorComponent,
238+
[ComponentType.Thumbnail]: getThumbnailComponent,
239+
[ComponentType.Label]: getLabelComponent,
240+
};
241+
195242
/**
196243
* Transforms API data into a component
197244
*
@@ -200,7 +247,7 @@ const { ComponentType } = require('discord-api-types/v10');
200247
* @ignore
201248
*/
202249
function createComponent(data) {
203-
return data instanceof Component ? data : new (ComponentTypeToClass[data.type] ?? Component)(data);
250+
return data instanceof getComponent() ? data : new (ComponentTypeToClass[data.type]?.() ?? getComponent())(data);
204251
}
205252

206253
/**
@@ -241,40 +288,3 @@ function findComponentByCustomId(components, customId) {
241288

242289
exports.createComponent = createComponent;
243290
exports.findComponentByCustomId = findComponentByCustomId;
244-
245-
const { ActionRow } = require('../structures/ActionRow.js');
246-
const { ButtonComponent } = require('../structures/ButtonComponent.js');
247-
const { ChannelSelectMenuComponent } = require('../structures/ChannelSelectMenuComponent.js');
248-
const { Component } = require('../structures/Component.js');
249-
const { ContainerComponent } = require('../structures/ContainerComponent.js');
250-
const { FileComponent } = require('../structures/FileComponent.js');
251-
const { LabelComponent } = require('../structures/LabelComponent.js');
252-
const { MediaGalleryComponent } = require('../structures/MediaGalleryComponent.js');
253-
const { MentionableSelectMenuComponent } = require('../structures/MentionableSelectMenuComponent.js');
254-
const { RoleSelectMenuComponent } = require('../structures/RoleSelectMenuComponent.js');
255-
const { SectionComponent } = require('../structures/SectionComponent.js');
256-
const { SeparatorComponent } = require('../structures/SeparatorComponent.js');
257-
const { StringSelectMenuComponent } = require('../structures/StringSelectMenuComponent.js');
258-
const { TextDisplayComponent } = require('../structures/TextDisplayComponent.js');
259-
const { TextInputComponent } = require('../structures/TextInputComponent.js');
260-
const { ThumbnailComponent } = require('../structures/ThumbnailComponent.js');
261-
const { UserSelectMenuComponent } = require('../structures/UserSelectMenuComponent.js');
262-
263-
const ComponentTypeToClass = {
264-
[ComponentType.ActionRow]: ActionRow,
265-
[ComponentType.Button]: ButtonComponent,
266-
[ComponentType.StringSelect]: StringSelectMenuComponent,
267-
[ComponentType.TextInput]: TextInputComponent,
268-
[ComponentType.UserSelect]: UserSelectMenuComponent,
269-
[ComponentType.RoleSelect]: RoleSelectMenuComponent,
270-
[ComponentType.MentionableSelect]: MentionableSelectMenuComponent,
271-
[ComponentType.ChannelSelect]: ChannelSelectMenuComponent,
272-
[ComponentType.Container]: ContainerComponent,
273-
[ComponentType.TextDisplay]: TextDisplayComponent,
274-
[ComponentType.File]: FileComponent,
275-
[ComponentType.MediaGallery]: MediaGalleryComponent,
276-
[ComponentType.Section]: SectionComponent,
277-
[ComponentType.Separator]: SeparatorComponent,
278-
[ComponentType.Thumbnail]: ThumbnailComponent,
279-
[ComponentType.Label]: LabelComponent,
280-
};

packages/discord.js/src/util/DataResolver.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
const { Buffer } = require('node:buffer');
44
const fs = require('node:fs/promises');
55
const path = require('node:path');
6+
const { lazy } = require('@discordjs/util');
67
const { fetch } = require('undici');
78
const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
89
const { BaseInvite } = require('../structures/BaseInvite.js');
910

11+
// Fixes circular dependencies.
12+
const getGuildTemplate = lazy(() => require('../structures/GuildTemplate.js').GuildTemplate);
13+
1014
/**
1115
* Data that can be resolved to give an invite code. This can be:
1216
* - An invite code
@@ -54,8 +58,7 @@ function resolveInviteCode(data) {
5458
* @private
5559
*/
5660
function resolveGuildTemplateCode(data) {
57-
const { GuildTemplate } = require('../structures/GuildTemplate.js');
58-
return resolveCode(data, GuildTemplate.GuildTemplatesPattern);
61+
return resolveCode(data, getGuildTemplate().GuildTemplatesPattern);
5962
}
6063

6164
/**

packages/discord.js/src/util/Util.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
const { parse } = require('node:path');
44
const { Collection } = require('@discordjs/collection');
5+
const { lazy } = require('@discordjs/util');
56
const { ChannelType, RouteBases, Routes } = require('discord-api-types/v10');
67
const { fetch } = require('undici');
7-
// eslint-disable-next-line import-x/order
88
const { Colors } = require('./Colors.js');
99
// eslint-disable-next-line import-x/order
1010
const { DiscordjsError, DiscordjsRangeError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
1111

12+
// Fixes circular dependencies.
13+
const getAttachment = lazy(() => require('../structures/Attachment.js').Attachment);
14+
const getGuildChannel = lazy(() => require('../structures/GuildChannel.js').GuildChannel);
15+
const getSKU = lazy(() => require('../structures/SKU.js').SKU);
16+
1217
const isObject = data => typeof data === 'object' && data !== null;
1318

1419
/**
@@ -352,8 +357,7 @@ function resolveColor(color) {
352357
* @returns {Collection}
353358
*/
354359
function discordSort(collection) {
355-
// eslint-disable-next-line no-use-before-define
356-
const isGuildChannel = collection.first() instanceof GuildChannel;
360+
const isGuildChannel = collection.first() instanceof getGuildChannel();
357361
return collection.toSorted(
358362
isGuildChannel
359363
? (a, b) => a.rawPosition - b.rawPosition || Number(BigInt(a.id) - BigInt(b.id))
@@ -555,8 +559,7 @@ function transformResolved(
555559
if (attachments) {
556560
result.attachments = new Collection();
557561
for (const attachment of Object.values(attachments)) {
558-
// eslint-disable-next-line no-use-before-define
559-
const patched = new Attachment(attachment);
562+
const patched = new (getAttachment())(attachment);
560563
result.attachments.set(attachment.id, patched);
561564
}
562565
}
@@ -572,8 +575,7 @@ function transformResolved(
572575
*/
573576
function resolveSKUId(resolvable) {
574577
if (typeof resolvable === 'string') return resolvable;
575-
// eslint-disable-next-line no-use-before-define
576-
if (resolvable instanceof SKU) return resolvable.id;
578+
if (resolvable instanceof getSKU()) return resolvable.id;
577579
return null;
578580
}
579581

@@ -600,8 +602,3 @@ exports.setPosition = setPosition;
600602
exports.basename = basename;
601603
exports.findName = findName;
602604
exports.transformResolved = transformResolved;
603-
604-
// Fixes Circular
605-
const { Attachment } = require('../structures/Attachment.js');
606-
const { GuildChannel } = require('../structures/GuildChannel.js');
607-
const { SKU } = require('../structures/SKU.js');

0 commit comments

Comments
 (0)