From 27ccad1f1c209d1222d5846e44aa2caba2ed536d Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Tue, 26 Sep 2017 00:18:12 -0500 Subject: [PATCH] tinify webpacks (#1975) * tinify webpack * meme * fix long version * more changes * even smoler * fix up logic * fix build * undo changes to user agent manager because its not webpack'd anymore * the heck * fix stupid * clean up browser rules * typo --- package.json | 11 ++++++----- src/WebSocket.js | 15 ++++++++++----- src/client/BaseClient.js | 9 --------- src/client/Client.js | 16 +++++++++------- src/client/websocket/WebSocketConnection.js | 2 +- src/errors/DJSError.js | 18 ++++++------------ src/rest/APIRequest.js | 3 ++- src/rest/APIRouter.js | 4 +--- src/rest/RESTManager.js | 4 ++-- src/structures/ClientUser.js | 4 ++-- src/structures/GroupDMChannel.js | 2 +- src/structures/Guild.js | 10 +++++----- src/structures/TextChannel.js | 2 +- src/structures/VoiceChannel.js | 7 ++++--- src/structures/Webhook.js | 16 +++++++--------- src/structures/interfaces/TextBasedChannel.js | 13 +++++++------ src/util/Constants.js | 5 +++-- src/util/DataResolver.js | 11 +++++------ src/util/Util.js | 9 +++++++++ test/webpack.html | 1 + webpack.config.js | 3 +++ 21 files changed, 85 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index f0345bd50..7532afbb8 100644 --- a/package.json +++ b/package.json @@ -69,9 +69,14 @@ "node-opus": false, "tweetnacl": false, "sodium": false, + "src/rest/UserAgentManager.js": false, "src/sharding/Shard.js": false, "src/sharding/ShardClientUtil.js": false, "src/sharding/ShardingManager.js": false, + "src/client/voice/ClientVoiceManager.js": false, + "src/client/voice/VoiceConnection.js": false, + "src/client/voice/VoiceUDPClient.js": false, + "src/client/voice/VoiceWebSocket.js": false, "src/client/voice/dispatcher/StreamDispatcher.js": false, "src/client/voice/opus/BaseOpusEngine.js": false, "src/client/voice/opus/NodeOpusEngine.js": false, @@ -86,10 +91,6 @@ "src/client/voice/util/Secretbox.js": false, "src/client/voice/util/SecretKey.js": false, "src/client/voice/util/VolumeInterface.js": false, - "src/client/voice/ClientVoiceManager.js": false, - "src/client/voice/VoiceBroadcast.js": false, - "src/client/voice/VoiceConnection.js": false, - "src/client/voice/VoiceUDPClient.js": false, - "src/client/voice/VoiceWebSocket.js": false + "src/client/voice/VoiceBroadcast.js": false } } diff --git a/src/WebSocket.js b/src/WebSocket.js index 009a0f798..a4eed5558 100644 --- a/src/WebSocket.js +++ b/src/WebSocket.js @@ -1,4 +1,4 @@ -const browser = typeof window !== 'undefined'; +const { browser } = require('./util/Constants'); const zlib = require('zlib'); const querystring = require('querystring'); @@ -23,16 +23,21 @@ exports.pack = erlpack ? erlpack.pack : JSON.stringify; exports.unpack = data => { if (Array.isArray(data)) data = Buffer.concat(data); - if (data instanceof ArrayBuffer) data = Buffer.from(new Uint8Array(data)); + if (!browser && data instanceof ArrayBuffer) data = Buffer.from(new Uint8Array(data)); - if (erlpack && typeof data !== 'string') return erlpack.unpack(data); - else if (data instanceof Buffer) data = zlib.inflateSync(data).toString(); + if (erlpack && typeof data !== 'string') { + return erlpack.unpack(data); + } else if (data instanceof ArrayBuffer || (!browser && data instanceof Buffer)) { + data = zlib.inflateSync(data).toString(); + } return JSON.parse(data); }; exports.create = (gateway, query = {}, ...args) => { + const [g, q] = gateway.split('?'); query.encoding = exports.encoding; - const ws = new exports.WebSocket(`${gateway}?${querystring.stringify(query)}`, ...args); + if (q) query = Object.assign(querystring.parse(q), query); + const ws = new exports.WebSocket(`${g}?${querystring.stringify(query)}`, ...args); if (browser) ws.binaryType = 'arraybuffer'; return ws; }; diff --git a/src/client/BaseClient.js b/src/client/BaseClient.js index 7be9530fa..365e9dcb7 100644 --- a/src/client/BaseClient.js +++ b/src/client/BaseClient.js @@ -39,15 +39,6 @@ class BaseClient extends EventEmitter { this._intervals = new Set(); } - /** - * Whether the client is in a browser environment - * @type {boolean} - * @readonly - */ - get browser() { - return typeof window !== 'undefined'; - } - /** * API shortcut * @type {Object} diff --git a/src/client/Client.js b/src/client/Client.js index ce7539a83..6b8f8530b 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -17,7 +17,7 @@ const ChannelStore = require('../stores/ChannelStore'); const GuildStore = require('../stores/GuildStore'); const ClientPresenceStore = require('../stores/ClientPresenceStore'); const EmojiStore = require('../stores/EmojiStore'); -const { Events } = require('../util/Constants'); +const { Events, browser } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); const { Error, TypeError, RangeError } = require('../errors'); @@ -33,8 +33,10 @@ class Client extends BaseClient { super(Object.assign({ _tokenType: 'Bot' }, options)); // Obtain shard details from environment - if (!this.options.shardId && 'SHARD_ID' in process.env) this.options.shardId = Number(process.env.SHARD_ID); - if (!this.options.shardCount && 'SHARD_COUNT' in process.env) { + if (!browser && !this.options.shardId && 'SHARD_ID' in process.env) { + this.options.shardId = Number(process.env.SHARD_ID); + } + if (!browser && !this.options.shardCount && 'SHARD_COUNT' in process.env) { this.options.shardCount = Number(process.env.SHARD_COUNT); } @@ -73,14 +75,14 @@ class Client extends BaseClient { * @type {?ClientVoiceManager} * @private */ - this.voice = !this.browser ? new ClientVoiceManager(this) : null; + this.voice = !browser ? new ClientVoiceManager(this) : null; /** * The shard helpers for the client * (only if the process was spawned as a child, such as from a {@link ShardingManager}) * @type {?ShardClientUtil} */ - this.shard = process.send ? ShardClientUtil.singleton(this) : null; + this.shard = !browser && process.send ? ShardClientUtil.singleton(this) : null; /** * All of the {@link User} objects that have been cached at any point, mapped by their IDs @@ -110,7 +112,7 @@ class Client extends BaseClient { this.presences = new ClientPresenceStore(this); Object.defineProperty(this, 'token', { writable: true }); - if (!this.token && 'CLIENT_TOKEN' in process.env) { + if (!browser && !this.token && 'CLIENT_TOKEN' in process.env) { /** * Authorization token for the logged in user/bot * This should be kept private at all times. @@ -207,7 +209,7 @@ class Client extends BaseClient { * @readonly */ get voiceConnections() { - if (this.browser) return new Collection(); + if (browser) return new Collection(); return this.voice.connections; } diff --git a/src/client/websocket/WebSocketConnection.js b/src/client/websocket/WebSocketConnection.js index 80bad14f6..dd7419544 100644 --- a/src/client/websocket/WebSocketConnection.js +++ b/src/client/websocket/WebSocketConnection.js @@ -247,7 +247,7 @@ class WebSocketConnection extends EventEmitter { try { data = WebSocket.unpack(event.data); } catch (err) { - this.emit('debug', err); + this.client.emit('debug', err); } const ret = this.onPacket(data); this.client.emit('raw', data); diff --git a/src/errors/DJSError.js b/src/errors/DJSError.js index 55b549902..1d5aea124 100644 --- a/src/errors/DJSError.js +++ b/src/errors/DJSError.js @@ -2,8 +2,6 @@ const kCode = Symbol('code'); const messages = new Map(); -const assert = require('assert'); -const util = require('util'); /** * Extend an error of some sort into a DiscordjsError. @@ -35,17 +33,13 @@ function makeDiscordjsError(Base) { * @returns {string} Formatted string */ function message(key, args) { - assert.strictEqual(typeof key, 'string'); + if (typeof key !== 'string') throw new Error('Error message key must be a string'); const msg = messages.get(key); - assert(msg, `An invalid error message key was used: ${key}.`); - let fmt = util.format; - if (typeof msg === 'function') { - fmt = msg; - } else { - if (args === undefined || args.length === 0) return msg; - args.unshift(msg); - } - return String(fmt(...args)); + if (!msg) throw new Error(`An invalid error message key was used: ${key}.`); + if (typeof msg === 'function') return msg(...args); + if (args === undefined || args.length === 0) return msg; + args.unshift(msg); + return String(...args); } /** diff --git a/src/rest/APIRequest.js b/src/rest/APIRequest.js index ef13edefb..bec537d2e 100644 --- a/src/rest/APIRequest.js +++ b/src/rest/APIRequest.js @@ -1,6 +1,7 @@ const querystring = require('querystring'); const snekfetch = require('snekfetch'); const https = require('https'); +const { browser } = require('../util/Constants'); if (https.Agent) var agent = new https.Agent({ keepAlive: true }); @@ -27,7 +28,7 @@ class APIRequest { if (this.options.auth !== false) request.set('Authorization', this.rest.getAuth()); if (this.options.reason) request.set('X-Audit-Log-Reason', encodeURIComponent(this.options.reason)); - if (!this.rest.client.browser) request.set('User-Agent', this.rest.userAgentManager.userAgent); + if (!browser) request.set('User-Agent', this.rest.userAgentManager.userAgent); if (this.options.headers) request.set(this.options.headers); if (this.options.files) { diff --git a/src/rest/APIRouter.js b/src/rest/APIRouter.js index 14660d50e..6e0fb36a9 100644 --- a/src/rest/APIRouter.js +++ b/src/rest/APIRouter.js @@ -1,10 +1,8 @@ -const util = require('util'); - const noop = () => {}; // eslint-disable-line no-empty-function const methods = ['get', 'post', 'delete', 'patch', 'put']; const reflectors = [ 'toString', 'valueOf', 'inspect', 'constructor', - Symbol.toPrimitive, util.inspect.custom, + Symbol.toPrimitive, Symbol.for('util.inspect.custom'), ]; function buildRoute(manager) { diff --git a/src/rest/RESTManager.js b/src/rest/RESTManager.js index 784071f54..1dda4f3be 100644 --- a/src/rest/RESTManager.js +++ b/src/rest/RESTManager.js @@ -3,13 +3,13 @@ const handlers = require('./handlers'); const APIRequest = require('./APIRequest'); const routeBuilder = require('./APIRouter'); const { Error } = require('../errors'); -const { Endpoints } = require('../util/Constants'); +const { Endpoints, browser } = require('../util/Constants'); class RESTManager { constructor(client, tokenPrefix = 'Bot') { this.client = client; this.handlers = {}; - this.userAgentManager = new UserAgentManager(this); + if (!browser) this.userAgentManager = new UserAgentManager(this); this.rateLimitedEndpoints = {}; this.globallyRateLimited = false; this.tokenPrefix = tokenPrefix; diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index e4e16c6fe..4e1b465ba 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -178,7 +178,7 @@ class ClientUser extends User { * .catch(console.error); */ async setAvatar(avatar) { - return this.edit({ avatar: await DataResolver.resolveImage(avatar, this.client.browser) }); + return this.edit({ avatar: await DataResolver.resolveImage(avatar) }); } /** @@ -294,7 +294,7 @@ class ClientUser extends User { ); } - return DataResolver.resolveImage(icon, this.client.browser) + return DataResolver.resolveImage(icon) .then(data => this.createGuild(name, { region, icon: data || null })); } diff --git a/src/structures/GroupDMChannel.js b/src/structures/GroupDMChannel.js index fe6653956..e142b070d 100644 --- a/src/structures/GroupDMChannel.js +++ b/src/structures/GroupDMChannel.js @@ -161,7 +161,7 @@ class GroupDMChannel extends Channel { * @returns {Promise} */ async setIcon(icon) { - return this.edit({ icon: await DataResolver.resolveImage(icon, this.client.browser) }); + return this.edit({ icon: await DataResolver.resolveImage(icon) }); } /** diff --git a/src/structures/Guild.js b/src/structures/Guild.js index e0a3c240b..82df81486 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -3,7 +3,7 @@ const GuildAuditLogs = require('./GuildAuditLogs'); const Webhook = require('./Webhook'); const GuildMember = require('./GuildMember'); const VoiceRegion = require('./VoiceRegion'); -const { ChannelTypes, Events } = require('../util/Constants'); +const { ChannelTypes, Events, browser } = require('../util/Constants'); const Collection = require('../util/Collection'); const Util = require('../util/Util'); const DataResolver = require('../util/DataResolver'); @@ -315,7 +315,7 @@ class Guild extends Base { * @readonly */ get voiceConnection() { - if (this.client.browser) return null; + if (browser) return null; return this.client.voice.connections.get(this.id) || null; } @@ -717,7 +717,7 @@ class Guild extends Base { * .catch(console.error); */ async setIcon(icon, reason) { - return this.edit({ icon: await DataResolver.resolveImage(icon, this.client.browser), reason }); + return this.edit({ icon: await DataResolver.resolveImage(icon), reason }); } /** @@ -747,7 +747,7 @@ class Guild extends Base { * .catch(console.error); */ async setSplash(splash, reason) { - return this.edit({ splash: await DataResolver.resolveImage(splash, this.client.browser), reason }); + return this.edit({ splash: await DataResolver.resolveImage(splash), reason }); } /** @@ -1036,7 +1036,7 @@ class Guild extends Base { .then(emoji => this.client.actions.GuildEmojiCreate.handle(this, emoji).emoji); } - return DataResolver.resolveImage(attachment, this.client.browser) + return DataResolver.resolveImage(attachment) .then(image => this.createEmoji(image, name, { roles, reason })); } diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index 2c536045a..7c7e1b135 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -64,7 +64,7 @@ class TextChannel extends GuildChannel { */ async createWebhook(name, { avatar, reason } = {}) { if (typeof avatar === 'string' && !avatar.startsWith('data:')) { - avatar = await DataResolver.resolveImage(avatar, this.client.browser); + avatar = await DataResolver.resolveImage(avatar); } return this.client.api.channels[this.id].webhooks.post({ data: { name, avatar, diff --git a/src/structures/VoiceChannel.js b/src/structures/VoiceChannel.js index b2d6d4f48..2fa37d233 100644 --- a/src/structures/VoiceChannel.js +++ b/src/structures/VoiceChannel.js @@ -1,5 +1,6 @@ const GuildChannel = require('./GuildChannel'); const Collection = require('../util/Collection'); +const { browser } = require('../util/Constants'); const { Error } = require('../errors'); /** @@ -59,7 +60,7 @@ class VoiceChannel extends GuildChannel { * @readonly */ get joinable() { - if (this.client.browser) return false; + if (browser) return false; if (!this.permissionsFor(this.client.user).has('CONNECT')) return false; if (this.full && !this.permissionsFor(this.client.user).has('MOVE_MEMBERS')) return false; return true; @@ -115,7 +116,7 @@ class VoiceChannel extends GuildChannel { * .catch(console.error); */ join() { - if (this.client.browser) return Promise.reject(new Error('VOICE_NO_BROWSER')); + if (browser) return Promise.reject(new Error('VOICE_NO_BROWSER')); return this.client.voice.joinChannel(this); } @@ -126,7 +127,7 @@ class VoiceChannel extends GuildChannel { * voiceChannel.leave(); */ leave() { - if (this.client.browser) return; + if (browser) return; const connection = this.client.voice.connections.get(this.guild.id); if (connection && connection.channel.id === this.id) connection.disconnect(); } diff --git a/src/structures/Webhook.js b/src/structures/Webhook.js index a163a06b4..9f98233db 100644 --- a/src/structures/Webhook.js +++ b/src/structures/Webhook.js @@ -1,9 +1,9 @@ -const path = require('path'); const Util = require('../util/Util'); const DataResolver = require('../util/DataResolver'); const Embed = require('./MessageEmbed'); const MessageAttachment = require('./MessageAttachment'); const MessageEmbed = require('./MessageEmbed'); +const { browser } = require('../util/Constants'); /** * Represents a webhook. @@ -151,14 +151,14 @@ class Webhook { if (options.files) { for (let i = 0; i < options.files.length; i++) { let file = options.files[i]; - if (typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file }; + if (typeof file === 'string' || (!browser && Buffer.isBuffer(file))) file = { attachment: file }; if (!file.name) { if (typeof file.attachment === 'string') { - file.name = path.basename(file.attachment); + file.name = Util.basename(file.attachment); } else if (file.attachment && file.attachment.path) { - file.name = path.basename(file.attachment.path); + file.name = Util.basename(file.attachment.path); } else if (file instanceof MessageAttachment) { - file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' }; + file = { attachment: file.file, name: Util.basename(file.file) || 'file.jpg' }; } else { file.name = 'file.jpg'; } @@ -169,7 +169,7 @@ class Webhook { } return Promise.all(options.files.map(file => - DataResolver.resolveFile(file.attachment, this.client.browser).then(resource => { + DataResolver.resolveFile(file.attachment).then(resource => { file.file = resource; return file; }) @@ -249,9 +249,7 @@ class Webhook { */ edit({ name = this.name, avatar }, reason) { if (avatar && (typeof avatar === 'string' && !avatar.startsWith('data:'))) { - return DataResolver.resolveImage(avatar, this.client.browser).then(image => - this.edit({ name, avatar: image }, reason) - ); + return DataResolver.resolveImage(avatar).then(image => this.edit({ name, avatar: image }, reason)); } return this.client.api.webhooks(this.id, this.token).patch({ data: { name, avatar }, diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index 4293769f0..960faa1bb 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -1,6 +1,7 @@ -const path = require('path'); const MessageCollector = require('../MessageCollector'); const Shared = require('../shared'); +const Util = require('../../util/Util'); +const { browser } = require('../../util/Constants'); const Snowflake = require('../../util/Snowflake'); const Collection = require('../../util/Collection'); const DataResolver = require('../../util/DataResolver'); @@ -106,14 +107,14 @@ class TextBasedChannel { if (options.files) { for (let i = 0; i < options.files.length; i++) { let file = options.files[i]; - if (typeof file === 'string' || Buffer.isBuffer(file)) file = { attachment: file }; + if (typeof file === 'string' || (!browser && Buffer.isBuffer(file))) file = { attachment: file }; if (!file.name) { if (typeof file.attachment === 'string') { - file.name = path.basename(file.attachment); + file.name = Util.basename(file.attachment); } else if (file.attachment && file.attachment.path) { - file.name = path.basename(file.attachment.path); + file.name = Util.basename(file.attachment.path); } else if (file instanceof MessageAttachment) { - file = { attachment: file.file, name: path.basename(file.file) || 'file.jpg' }; + file = { attachment: file.file, name: Util.basename(file.file) || 'file.jpg' }; } else { file.name = 'file.jpg'; } @@ -124,7 +125,7 @@ class TextBasedChannel { } return Promise.all(options.files.map(file => - DataResolver.resolveFile(file.attachment, this.client.browser).then(resource => { + DataResolver.resolveFile(file.attachment).then(resource => { file.file = resource; return file; }) diff --git a/src/util/Constants.js b/src/util/Constants.js index 3d9065309..ceb0609d7 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -1,5 +1,6 @@ exports.Package = require('../../package.json'); const { Error, RangeError } = require('../errors'); +exports.browser = typeof window !== 'undefined'; /** * Options for a client. @@ -56,9 +57,9 @@ exports.DefaultOptions = { */ ws: { large_threshold: 250, - compress: require('os').platform() !== 'browser', + compress: !exports.browser, properties: { - $os: process ? process.platform : 'discord.js', + $os: exports.browser ? 'browser' : process.platform, $browser: 'discord.js', $device: 'discord.js', }, diff --git a/src/util/DataResolver.js b/src/util/DataResolver.js index 1aa0bb15c..ff2dc75b7 100644 --- a/src/util/DataResolver.js +++ b/src/util/DataResolver.js @@ -3,6 +3,7 @@ const fs = require('fs'); const snekfetch = require('snekfetch'); const Util = require('../util/Util'); const { Error, TypeError } = require('../errors'); +const { browser } = require('../util/Constants'); /** * The DataResolver identifies different objects and tries to resolve a specific piece of information from them. @@ -35,15 +36,14 @@ class DataResolver { /** * Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image. * @param {BufferResolvable|Base64Resolvable} image The image to be resolved - * @param {boolean} browser Whether this should resolve for a browser * @returns {Promise} */ - static async resolveImage(image, browser) { + static async resolveImage(image) { if (!image) return null; if (typeof image === 'string' && image.startsWith('data:')) { return image; } - const file = await this.resolveFile(image, browser); + const file = await this.resolveFile(image); return DataResolver.resolveBase64(file); } @@ -80,10 +80,9 @@ class DataResolver { /** * Resolves a BufferResolvable to a Buffer. * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve - * @param {boolean} browser Whether this should resolve for a browser * @returns {Promise} */ - static resolveFile(resource, browser) { + static resolveFile(resource) { if (resource instanceof Buffer) return Promise.resolve(resource); if (browser && resource instanceof ArrayBuffer) return Promise.resolve(Util.convertToBuffer(resource)); @@ -97,7 +96,7 @@ class DataResolver { return resolve(res.body); }); } else { - const file = path.resolve(resource); + const file = browser ? resource : path.resolve(resource); fs.stat(file, (err, stats) => { if (err) return reject(err); if (!stats || !stats.isFile()) return reject(new Error('FILE_NOT_FOUND', file)); diff --git a/src/util/Util.js b/src/util/Util.js index 1b44434ca..2d6f87cb7 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -3,6 +3,7 @@ const snekfetch = require('snekfetch'); const { Colors, DefaultOptions, Endpoints } = require('./Constants'); const { Error: DiscordError, RangeError, TypeError } = require('../errors'); const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); +const splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^/]+?|)(\.[^./]*|))(?:[/]*)$/; /** * Contains various general-purpose utility methods. These functions are also available on the base `Discord` object. @@ -307,6 +308,14 @@ class Util { updatedItems = updatedItems.map((r, i) => ({ id: r.id, position: i })); return route.patch({ data: updatedItems, reason }); } + + static basename(path, ext) { + let f = splitPathRe.exec(path).slice(1)[2]; + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; + } } module.exports = Util; diff --git a/test/webpack.html b/test/webpack.html index 95d9636ce..c8a23f984 100644 --- a/test/webpack.html +++ b/test/webpack.html @@ -18,6 +18,7 @@ client.on('debug', console.log); client.on('error', console.error); + client.on('debug', console.info); client.ws.on('close', (event) => console.log('[CLIENT] Disconnect!', event)); diff --git a/webpack.config.js b/webpack.config.js index f38376ec8..a100b1844 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -46,6 +46,9 @@ const createConfig = options => { dgram: 'empty', zlib: 'empty', __dirname: true, + process: false, + path: 'empty', + Buffer: false, }, plugins, };