feat(Browser): remove browser � (#5113)

This commit is contained in:
Noel 2020-12-14 13:56:16 +01:00 committed by GitHub
parent 5c4547e84d
commit 0a591a9697
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 895 additions and 3735 deletions

View file

@ -8,7 +8,6 @@
"es2020": true,
"node": true
},
"overrides": [{ "files": ["*.browser.js"], "env": { "browser": true } }],
"rules": {
"import/order": [
"error",

View file

@ -27,23 +27,3 @@ jobs:
uses: discordjs/action-docs@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
webpack:
name: webpack
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@master
- name: Install Node v14
uses: actions/setup-node@master
with:
node-version: 14
- name: Install dependencies
run: npm ci
- name: Build and deploy webpack
uses: discordjs/action-webpack@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -10,8 +10,5 @@
"fullDocs": true,
"strong": true
},
"webpack": {
"configPath": "./webpack.config.js"
}
}
}

View file

@ -10,8 +10,6 @@
files:
- name: Voice
path: voice.md
- name: Web builds
path: web.md
- name: Partials
path: partials.md
- name: Examples

View file

@ -1,52 +0,0 @@
# Web builds
In addition to your usual Node applications, discord.js has special distributions available that are capable of running in web browsers.
This is useful for client-side web apps that need to interact with the Discord API.
[Webpack 3](https://webpack.js.org/) is used to build these.
## Restrictions
- Any voice-related functionality is unavailable, as there is currently no audio encoding/decoding capabilities without external native libraries,
which web browsers do not support.
- The ShardingManager cannot be used, since it relies on being able to spawn child processes for shards.
- None of the native optional packages are usable.
### Require Library
If you are making your own webpack project, you can require `discord.js/browser` wherever you need to use discord.js, like so:
```js
const Discord = require('discord.js/browser');
// do something with Discord like you normally would
```
### Webpack File
You can obtain your desired version of discord.js' web build from the [webpack branch](https://github.com/discordjs/discord.js/tree/webpack) of the GitHub repository.
There is a file for each branch and version of the library, and the ones ending in `.min.js` are minified to substantially reduce the size of the source code.
Include the file on the page just as you would any other JS library, like so:
```html
<script type="text/javascript" src="discord.VERSION.min.js"></script>
```
Rather than importing discord.js with `require('discord.js')`, the entire `Discord` object is available as a global (on the `window`) object.
The usage of the API isn't any different from using it in Node.js.
#### Example
```html
<script type="text/javascript" src="discord.11.1.0.min.js"></script>
<script type="text/javascript">
const client = new Discord.Client();
client.on('message', msg => {
const guildTag = msg.channel.type === 'text' ? `[${msg.guild.name}]` : '[DM]';
const channelTag = msg.channel.type === 'text' ? `[#${msg.channel.name}]` : '';
console.log(`${guildTag}${channelTag} ${msg.author.tag}: ${msg.content}`);
});
client.login('some crazy token');
</script>
```

4313
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -23,8 +23,7 @@
"lint:fix": "eslint src --fix",
"lint:typings": "tslint typings/index.d.ts",
"prettier": "prettier --write src/**/*.js typings/**/*.ts",
"build:browser": "webpack",
"prepublishOnly": "npm run test && cross-env NODE_ENV=production npm run build:browser"
"prepublishOnly": "npm run test"
},
"repository": {
"type": "git",
@ -45,7 +44,6 @@
},
"homepage": "https://github.com/discordjs/discord.js#readme",
"runkitExampleFilename": "./docs/examples/ping.js",
"unpkg": "./webpack/discord.min.js",
"dependencies": {
"@discordjs/collection": "^0.1.6",
"@discordjs/form-data": "^3.0.1",
@ -73,47 +71,12 @@
"json-filter-loader": "^1.0.0",
"lint-staged": "^10.4.2",
"prettier": "^2.1.2",
"terser-webpack-plugin": "^4.2.3",
"tslint": "^6.1.3",
"typescript": "^4.0.3",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"
"typescript": "^4.0.3"
},
"engines": {
"node": ">=14.0.0"
},
"browser": {
"@discordjs/opus": false,
"https": false,
"ws": false,
"erlpack": false,
"prism-media": false,
"opusscript": false,
"node-opus": false,
"tweetnacl": false,
"sodium": false,
"worker_threads": false,
"zlib-sync": 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/VoiceBroadcast.js": false,
"src/client/voice/VoiceConnection.js": false,
"src/client/voice/dispatcher/BroadcastDispatcher.js": false,
"src/client/voice/dispatcher/StreamDispatcher.js": false,
"src/client/voice/networking/VoiceUDPClient.js": false,
"src/client/voice/networking/VoiceWebSocket.js": false,
"src/client/voice/player/AudioPlayer.js": false,
"src/client/voice/player/BasePlayer.js": false,
"src/client/voice/player/BroadcastAudioPlayer.js": false,
"src/client/voice/receiver/PacketHandler.js": false,
"src/client/voice/receiver/Receiver.js": false,
"src/client/voice/util/PlayInterface.js": false,
"src/client/voice/util/Secretbox.js": false,
"src/client/voice/util/Silence.js": false,
"src/client/voice/util/VolumeInterface.js": false
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",

View file

@ -1,7 +1,5 @@
'use strict';
const { browser } = require('./util/Constants');
let erlpack;
try {
@ -9,15 +7,7 @@ try {
if (!erlpack.pack) erlpack = null;
} catch {} // eslint-disable-line no-empty
let TextDecoder;
if (browser) {
TextDecoder = window.TextDecoder; // eslint-disable-line no-undef
exports.WebSocket = window.WebSocket; // eslint-disable-line no-undef
} else {
TextDecoder = require('util').TextDecoder;
exports.WebSocket = require('ws');
}
exports.WebSocket = require('ws');
const ab = new TextDecoder();
@ -42,7 +32,6 @@ exports.create = (gateway, query = {}, ...args) => {
query = new URLSearchParams(query);
if (q) new URLSearchParams(q).forEach((v, k) => query.set(k, v));
const ws = new exports.WebSocket(`${g}?${query}`, ...args);
if (browser) ws.binaryType = 'arraybuffer';
return ws;
};

View file

@ -17,7 +17,7 @@ const Invite = require('../structures/Invite');
const VoiceRegion = require('../structures/VoiceRegion');
const Webhook = require('../structures/Webhook');
const Collection = require('../util/Collection');
const { Events, browser, DefaultOptions } = require('../util/Constants');
const { Events, DefaultOptions } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Intents = require('../util/Intents');
const Permissions = require('../util/Permissions');
@ -89,19 +89,18 @@ class Client extends BaseClient {
this.actions = new ActionsManager(this);
/**
* The voice manager of the client (`null` in browsers)
* @type {?ClientVoiceManager}
* The voice manager of the client
* @type {ClientVoiceManager}
*/
this.voice = !browser ? new ClientVoiceManager(this) : null;
this.voice = new ClientVoiceManager(this);
/**
* Shard helpers for the client (only if the process was spawned from a {@link ShardingManager})
* @type {?ShardClientUtil}
*/
this.shard =
!browser && process.env.SHARDING_MANAGER
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
: null;
this.shard = process.env.SHARDING_MANAGER
? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE)
: null;
/**
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
@ -134,7 +133,7 @@ class Client extends BaseClient {
this.presence = new ClientPresence(this, options.presence);
Object.defineProperty(this, 'token', { writable: true });
if (!browser && !this.token && 'DISCORD_TOKEN' in process.env) {
if (!this.token && 'DISCORD_TOKEN' in process.env) {
/**
* Authorization token for the logged in bot.
* If present, this defaults to `process.env.DISCORD_TOKEN` when instantiating the client

View file

@ -2,18 +2,16 @@
const EventEmitter = require('events');
const WebSocket = require('../../WebSocket');
const { browser, Status, Events, ShardEvents, OPCodes, WSEvents } = require('../../util/Constants');
const { Status, Events, ShardEvents, OPCodes, WSEvents } = require('../../util/Constants');
const STATUS_KEYS = Object.keys(Status);
const CONNECTION_STATE = Object.keys(WebSocket.WebSocket);
let zlib;
if (!browser) {
try {
zlib = require('zlib-sync');
} catch {} // eslint-disable-line no-empty
}
try {
zlib = require('zlib-sync');
} catch {} // eslint-disable-line no-empty
/**
* Represents a Shard's WebSocket connection

View file

@ -50,7 +50,6 @@ const Messages = {
VOICE_TOKEN_ABSENT: 'Token not provided from voice server packet.',
VOICE_SESSION_ABSENT: 'Session ID not supplied.',
VOICE_INVALID_ENDPOINT: 'Invalid endpoint received.',
VOICE_NO_BROWSER: 'Voice connections are not available in browsers.',
VOICE_CONNECTION_ATTEMPTS_EXCEEDED: attempts => `Too many connection attempts (${attempts}).`,
VOICE_JOIN_SOCKET_CLOSED: 'Tried to send join packet, but the WebSocket is not open.',
VOICE_PLAY_INTERFACE_NO_BROADCAST: 'A broadcast cannot be played in this context.',

View file

@ -4,7 +4,7 @@ const https = require('https');
const FormData = require('@discordjs/form-data');
const AbortController = require('abort-controller');
const fetch = require('node-fetch');
const { browser, UserAgent } = require('../util/Constants');
const { UserAgent } = require('../util/Constants');
if (https.Agent) var agent = new https.Agent({ keepAlive: true });
@ -37,7 +37,7 @@ class APIRequest {
if (this.options.auth !== false) headers.Authorization = this.rest.getAuth();
if (this.options.reason) headers['X-Audit-Log-Reason'] = encodeURIComponent(this.options.reason);
if (!browser) headers['User-Agent'] = UserAgent;
headers['User-Agent'] = UserAgent;
if (this.options.headers) headers = Object.assign(headers, this.options.headers);
let body;
@ -45,7 +45,7 @@ class APIRequest {
body = new FormData();
for (const file of this.options.files) if (file && file.file) body.append(file.name, file.file, file.name);
if (typeof this.options.data !== 'undefined') body.append('payload_json', JSON.stringify(this.options.data));
if (!browser) headers = Object.assign(headers, body.getHeaders());
headers = Object.assign(headers, body.getHeaders());
// eslint-disable-next-line eqeqeq
} else if (this.options.data != null) {
body = JSON.stringify(this.options.data);

View file

@ -5,13 +5,11 @@ const DiscordAPIError = require('./DiscordAPIError');
const HTTPError = require('./HTTPError');
const {
Events: { RATE_LIMIT },
browser,
} = require('../util/Constants');
const Util = require('../util/Util');
function parseResponse(res) {
if (res.headers.get('content-type').startsWith('application/json')) return res.json();
if (browser) return res.blob();
return res.buffer();
}

View file

@ -3,7 +3,6 @@
const MessageAttachment = require('./MessageAttachment');
const MessageEmbed = require('./MessageEmbed');
const { RangeError } = require('../errors');
const { browser } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const MessageFlags = require('../util/MessageFlags');
const Util = require('../util/Util');
@ -272,9 +271,7 @@ class APIMessage {
};
const ownAttachment =
typeof fileLike === 'string' ||
fileLike instanceof (browser ? ArrayBuffer : Buffer) ||
typeof fileLike.pipe === 'function';
typeof fileLike === 'string' || fileLike instanceof Buffer || typeof fileLike.pipe === 'function';
if (ownAttachment) {
attachment = fileLike;
name = findName(attachment);

View file

@ -18,7 +18,6 @@ const RoleManager = require('../managers/RoleManager');
const VoiceStateManager = require('../managers/VoiceStateManager');
const Collection = require('../util/Collection');
const {
browser,
ChannelTypes,
DefaultMessageNotifications,
PartialTypes,
@ -991,7 +990,7 @@ class Guild extends Base {
}
const data = await this.client.api.guilds(this.id).members(user).put({ data: options });
// Data is an empty buffer if the member is already part of the guild.
return data instanceof (browser ? ArrayBuffer : Buffer) ? this.members.fetch(user) : this.members.add(data);
return data instanceof Buffer ? this.members.fetch(user) : this.members.add(data);
}
/**

View file

@ -1,9 +1,7 @@
'use strict';
const GuildChannel = require('./GuildChannel');
const { Error } = require('../errors');
const Collection = require('../util/Collection');
const { browser } = require('../util/Constants');
const Permissions = require('../util/Permissions');
/**
@ -74,7 +72,6 @@ class VoiceChannel extends GuildChannel {
* @readonly
*/
get joinable() {
if (browser) return false;
if (!this.viewable) return false;
if (!this.permissionsFor(this.client.user).has(Permissions.FLAGS.CONNECT, false)) return false;
if (this.full && !this.permissionsFor(this.client.user).has(Permissions.FLAGS.MOVE_MEMBERS, false)) return false;
@ -130,7 +127,6 @@ class VoiceChannel extends GuildChannel {
* .catch(console.error);
*/
join() {
if (browser) return Promise.reject(new Error('VOICE_NO_BROWSER'));
return this.client.voice.joinChannel(this);
}
@ -141,7 +137,6 @@ class VoiceChannel extends GuildChannel {
* voiceChannel.leave();
*/
leave() {
if (browser) return;
const connection = this.client.voice.connections.get(this.guild.id);
if (connection && connection.channel.id === this.id) connection.disconnect();
}

View file

@ -2,7 +2,6 @@
const Base = require('./Base');
const { Error, TypeError } = require('../errors');
const { browser } = require('../util/Constants');
/**
* Represents the voice state for a Guild Member.
@ -95,7 +94,7 @@ class VoiceState extends Base {
* @readonly
*/
get connection() {
if (browser || this.id !== this.client.user.id) return null;
if (this.id !== this.client.user.id) return null;
return this.client.voice.connections.get(this.guild.id) || null;
}

View file

@ -2,7 +2,6 @@
const Package = (exports.Package = require('../../package.json'));
const { Error, RangeError } = require('../errors');
const browser = (exports.browser = typeof window !== 'undefined');
/**
* Options for a client.
@ -65,7 +64,7 @@ exports.DefaultOptions = {
large_threshold: 50,
compress: false,
properties: {
$os: browser ? 'browser' : process.platform,
$os: process.platform,
$browser: 'discord.js',
$device: 'discord.js',
},
@ -90,9 +89,7 @@ exports.DefaultOptions = {
},
};
exports.UserAgent = browser
? null
: `DiscordBot (${Package.homepage.split('#')[0]}, ${Package.version}) Node.js/${process.version}`;
exports.UserAgent = `DiscordBot (${Package.homepage.split('#')[0]}, ${Package.version}) Node.js/${process.version}`;
exports.WSCodes = {
1000: 'WS_CLOSE_REQUESTED',

View file

@ -5,8 +5,6 @@ const path = require('path');
const stream = require('stream');
const fetch = require('node-fetch');
const { Error: DiscordError, TypeError } = require('../errors');
const { browser } = require('../util/Constants');
const Util = require('../util/Util');
/**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them.
@ -110,26 +108,21 @@ class DataResolver {
* @returns {Promise<Buffer|Stream>}
*/
static async resolveFile(resource) {
if (!browser && Buffer.isBuffer(resource)) return resource;
if (browser && resource instanceof ArrayBuffer) return Util.convertToBuffer(resource);
// eslint-disable-next-line no-undef
if (browser && resource instanceof Blob) return resource;
if (resource instanceof stream.Readable) return resource;
if (Buffer.isBuffer(resource) || resource instanceof stream.Readable) return resource;
if (typeof resource === 'string') {
if (/^https?:\/\//.test(resource)) {
const res = await fetch(resource);
return browser ? res.blob() : res.body;
} else if (!browser) {
return new Promise((resolve, reject) => {
const file = path.resolve(resource);
fs.stat(file, (err, stats) => {
if (err) return reject(err);
if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file));
return resolve(fs.createReadStream(file));
});
});
return res.body;
}
return new Promise((resolve, reject) => {
const file = path.resolve(resource);
fs.stat(file, (err, stats) => {
if (err) return reject(err);
if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file));
return resolve(fs.createReadStream(file));
});
});
}
throw new TypeError('REQ_RESOURCE_TYPE');

View file

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>discord.js Webpack test</title>
<meta charset="utf-8" />
</head>
<body>
<script type="text/javascript" src="../webpack/discord.js"></script>
<script type="text/javascript">
(() => {
const client = (window.client = new Discord.Client());
client.on('ready', () => {
console.log('[CLIENT] Ready!');
});
client.on('debug', console.log);
client.on('error', console.error);
client.ws.on('close', event => console.log('[CLIENT] Disconnect!', event));
client.on('message', message => {
console.log(message.author.username, message.author.id, message.content);
});
client
.login(localStorage.token || window.token || prompt('token pls', 'abcdef123456'))
.then(token => (localStorage.token = token), console.log);
})();
</script>
</body>
</html>

2
typings/index.d.ts vendored
View file

@ -324,7 +324,6 @@ declare module 'discord.js' {
keywords: string[];
bugs: { url: string };
repository: { type: string; url: string };
browser: { [key: string]: boolean };
scripts: { [key: string]: string };
engines: { [key: string]: string };
dependencies: { [key: string]: string };
@ -332,7 +331,6 @@ declare module 'discord.js' {
devDependencies: { [key: string]: string };
[key: string]: any;
};
browser: boolean;
DefaultOptions: ClientOptions;
UserAgent: string | null;
Endpoints: {

View file

@ -1,62 +0,0 @@
'use strict';
const path = require('path');
const TerserJSPlugin = require('terser-webpack-plugin');
const version = require('./package.json').version;
const prod = process.env.NODE_ENV === 'production';
// eslint-disable-next-line max-len
const filename = `discord${process.env.VERSIONED ? `.${version}` : ''}${prod ? '.min' : ''}.js`;
module.exports = {
entry: './src/index.js',
mode: prod ? 'production' : 'development',
output: {
path: path.resolve('./webpack'),
filename,
library: 'Discord',
libraryTarget: 'umd',
},
module: {
rules: [
{ test: /\.md$/, loader: 'ignore-loader' },
{
test: require.resolve('./package.json'),
type: 'javascript/auto',
use: {
loader: 'json-filter-loader',
options: {
used: ['version', 'homepage'],
},
},
},
],
},
node: {
fs: 'empty',
dns: 'mock',
tls: 'mock',
child_process: 'empty',
dgram: 'empty',
__dirname: true,
process: true,
path: 'empty',
Buffer: true,
zlib: 'empty',
},
optimization: {
minimize: true,
minimizer: [
new TerserJSPlugin({
cache: false,
terserOptions: {
mangle: { keep_classnames: true, keep_fnames: true },
keep_classnames: true,
keep_fnames: true,
output: { comments: false },
},
}),
],
},
};