feat: documentation

This commit is contained in:
Snowflake107 2021-06-20 20:07:09 +05:45
parent c172af0fbf
commit fcd8a8e186
19 changed files with 364 additions and 70 deletions

View file

@ -32,9 +32,6 @@ Your extractor should have 2 methods (required):
url: "Some Link"
}
```
- `important: boolean`
You can mark your Extractor as `important` by adding `important: true` to your extractor object. Doing this will disable rest of the extractors that comes after your extractor and use your extractor to get data. By default, it is set to `false`.
- `version: string`

View file

@ -8,5 +8,5 @@ const { AudioFilters } = require("discord-player");
AudioFilters.define("3D", "apulsator=hz=0.128");
// later, it can be used like this
player.setFilters(message, { "3D": true });
queue.setFilters(message, { "3D": true });
```

View file

@ -1,11 +0,0 @@
# How to play live videos?
You cannot play live videos by default. If you need to play the live video, just add this option:
```js
const player = new Player(client, {
enableLive: true // enables livestream
});
```
However, you cannot use audio filters with livestreams using this library!

View file

@ -1,15 +0,0 @@
# Pause and Resume is not working properly
This is a bug in **[discord.js#5300](https://github.com/discordjs/discord.js/issues/5300)**.
# Fix
You have to update your command something like this:
```diff
- client.player.resume(message);
+ client.player.resume(message);
+ client.player.pause(message);
+ client.player.resume(message);
```

View file

@ -3,6 +3,10 @@ Complete framework to facilitate music commands using **[discord.js](https://dis
[![downloadsBadge](https://img.shields.io/npm/dt/discord-player?style=for-the-badge)](https://npmjs.com/discord-player)
[![versionBadge](https://img.shields.io/npm/v/discord-player?style=for-the-badge)](https://npmjs.com/discord-player)
[![discordBadge](https://img.shields.io/discord/558328638911545423?style=for-the-badge&color=7289da)](https://androz2091.fr/discord)
[![wakatime](https://wakatime.com/badge/github/Androz2091/discord-player.svg)](https://wakatime.com/badge/github/Androz2091/discord-player)
> V5 WIP
## Installation
@ -43,13 +47,13 @@ Here is the code you will need to get started with discord-player. Then, you wil
```js
const Discord = require("discord.js"),
client = new Discord.Client,
client = new Discord.Client({ intents: ["GUILD_VOICE_STATES", "GUILD_MESSAGES", "GUILDS"] }),
settings = {
prefix: "!",
token: "Your Discord Token"
};
const { Player } = require("discord-player");
const { Player, QueryType } = require("discord-player");
// Create a new Player (you don't need any API Key)
const player = new Player(client);
@ -58,7 +62,7 @@ const player = new Player(client);
client.player = player;
// add the trackStart event so when a song will be played this message will be sent
client.player.on("trackStart", (message, track) => message.channel.send(`Now playing ${track.title}...`))
client.player.on("trackStart", (queue, track) => queue.metadata.channel.send(`Now playing ${track.title}...`))
client.once("ready", () => {
console.log("I'm ready !");
@ -70,10 +74,29 @@ client.on("message", async (message) => {
const command = args.shift().toLowerCase();
// !play Despacito
// will play the song "Despacito" in the voice channel
if(command === "play"){
client.player.play(message, args[0]);
// as we registered the event above, no need to send a success message here
// will play "Despacito" in the voice channel
if (command === "play") {
if (!message.member.voice.channel) return void message.reply("You are not in a voice channel!");
if (message.guild.me.voice.channel && message.member.voice.channelID !== message.guild.me.voice.channelID) return void message.reply("You are not in my voice channel!");
const queue = client.player.createQueue(message.guild, {
metadata: message
});
// verify vc connection
try {
if (!queue.connection) await queue.connect(message.member.voice.channel);
} catch {
queue.destroy();
return void message.reply("Could not join your voice channel!");
}
const track = await client.player.search(args[0], {
searchEngine: QueryType.YOUTUBE_SEARCH
}).then(x => x.tracks[1]);
if (!track) return void message.reply("Track not found!");
queue.play(track);
}
});
@ -106,13 +129,13 @@ These bots are made by the community, they can help you build your own!
* [Discord-Music](https://github.com/inhydrox/discord-music) by [inhydrox](https://github.com/inhydrox)
* [Music-bot](https://github.com/ZerioDev/Music-bot) by [ZerioDev](https://github.com/ZerioDev)
## FAQ
## Advanced
### How to use cookies
### Use cookies
```js
const player = new Player(client, {
ytdlDownloadOptions: {
ytdlOptions: {
requestOptions: {
headers: {
cookie: "YOUR_YOUTUBE_COOKIE"
@ -122,7 +145,7 @@ const player = new Player(client, {
});
```
### How to use custom proxies
### Use custom proxies
```js
const HttpsProxyAgent = require("https-proxy-agent");
@ -132,7 +155,7 @@ const proxy = "http://user:pass@111.111.111.111:8080";
const agent = HttpsProxyAgent(proxy);
const player = new Player(client, {
ytdlDownloadOptions: {
ytdlOptions: {
requestOptions: { agent }
}
});

View file

@ -10,10 +10,6 @@
files:
- name: Custom Filters
path: custom_filters.md
- name: Livestreams
path: live_video.md
- name: Pause & Resume
path: pause_resume.md
- name: YouTube
files:
- name: Using Cookies

View file

@ -4,7 +4,7 @@
const { Player } = require("discord-player");
const player = new Player(client, {
ytdlDownloadOptions: {
ytdlOptions: {
requestOptions: {
headers: {
cookie: "YOUR_YOUTUBE_COOKIE"

View file

@ -9,7 +9,7 @@ const proxy = "http://user:pass@111.111.111.111:8080";
const agent = HttpsProxyAgent(proxy);
const player = new Player(client, {
ytdlDownloadOptions: {
ytdlOptions: {
requestOptions: { agent }
}
});

View file

@ -56,6 +56,13 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
}
}
/**
* Handles voice state update
* @param {VoiceState} oldState The old voice state
* @param {VoiceState} newState The new voice state
* @returns {void}
* @private
*/
private _handleVoiceState(oldState: VoiceState, newState: VoiceState): void {
const queue = this.getQueue(oldState.guild.id);
if (!queue) return;
@ -141,7 +148,7 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
* Search tracks
* @param {string|Track} query The search query
* @param {UserResolvable} requestedBy The person who requested track search
* @returns {Promise<{playlist?: Playlist; tracks: Track[]}>}
* @returns {Promise<object>}
*/
async search(query: string | Track, options: SearchOptions) {
if (query instanceof Track) return { playlist: null, tracks: [query] };
@ -400,6 +407,12 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
}
}
/**
* @param {string} extractorName The extractor name
* @param {ExtractorModel|any} extractor The extractor object
* @param {boolean} [force=false]
* @returns {ExtractorModel}
*/
use(extractorName: string, extractor: ExtractorModel | any, force = false): ExtractorModel {
if (!extractorName) throw new Error("Cannot use unknown extractor!");
if (this.extractors.has(extractorName) && !force) return this.extractors.get(extractorName);
@ -418,6 +431,11 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
return model;
}
/**
* Removes registered extractor
* @param {string} extractorName The extractor name
* @returns {ExtractorModel}
*/
unuse(extractorName: string) {
if (!this.extractors.has(extractorName)) throw new Error(`Cannot find extractor "${extractorName}"`);
const prev = this.extractors.get(extractorName);
@ -425,6 +443,10 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
return prev;
}
/**
* Generates a report of the dependencies used by the `@discordjs/voice` module. Useful for debugging.
* @returns {string}
*/
scanDeps() {
return generateDependencyReport();
}

View file

@ -16,6 +16,12 @@ class ExtractorModel {
*/
this.name = extractorName;
/**
* The raw model
* @name ExtractorModel#_raw
* @type {any}
* @private
*/
Object.defineProperty(this, "_raw", { value: data, configurable: false, writable: false, enumerable: false });
}

View file

@ -16,25 +16,106 @@ class Playlist {
};
public id: string;
public url: string;
public rawPlaylist?: any;
public readonly rawPlaylist?: any;
/**
* Playlist constructor
* @param {Player} player The player
* @param {PlaylistInitData} data The data
*/
constructor(player: Player, data: PlaylistInitData) {
/**
* The player
* @name Playlist#player
* @type {Player}
* @readonly
*/
this.player = player;
/**
* The tracks in this playlist
* @name Playlist#tracks
* @type {Track[]}
*/
this.tracks = data.tracks ?? [];
/**
* The author of this playlist
* @name Playlist#author
* @type {object}
*/
this.author = data.author;
/**
* The description
* @name Playlist#description
* @type {string}
*/
this.description = data.description;
/**
* The thumbnail of this playlist
* @name Playlist#thumbnail
* @type {string}
*/
this.thumbnail = data.thumbnail;
/**
* The playlist type:
* - `album`
* - `playlist`
* @name Playlist#type
* @type {string}
*/
this.type = data.type;
/**
* The source of this playlist:
* - `youtube`
* - `soundcloud`
* - `spotify`
* - `arbitrary`
* @name Playlist#source
* @type {string}
*/
this.source = data.source;
/**
* The playlist id
* @name Playlist#id
* @type {string}
*/
this.id = data.id;
/**
* The playlist url
* @name Playlist#url
* @type {string}
*/
this.url = data.url;
/**
* The playlist title
* @type {string}
*/
this.title = data.title;
/**
* @name Playlist#rawPlaylist
* @type {any}
* @readonly
*/
}
*[Symbol.iterator]() {
yield* this.tracks;
}
/**
* JSON representation of this playlist
* @param {boolean} [withTracks=true] If it should build json with tracks
* @returns {PlaylistJSON}
*/
toJSON(withTracks = true) {
const payload = {
id: this.id,

View file

@ -27,18 +27,20 @@ class Queue<T = unknown> {
* Queue constructor
* @param {Player} player The player that instantiated this queue
* @param {Guild} guild The guild that instantiated this queue
* @param {PlayerOptions={}} options Player options for the queue
* @param {PlayerOptions} [options={}] Player options for the queue
*/
constructor(player: Player, guild: Guild, options: PlayerOptions = {}) {
/**
* The player that instantiated this queue
* @type {Player}
* @readonly
*/
this.player = player;
/**
* The guild that instantiated this queue
* @type {Guild}
* @readonly
*/
this.guild = guild;
@ -69,7 +71,7 @@ class Queue<T = unknown> {
/**
* Returns current track
* @returns {Track}
* @type {Track}
*/
get current() {
return this.connection.audioResource?.metadata ?? this.tracks[0];
@ -85,7 +87,7 @@ class Queue<T = unknown> {
/**
* Connects to a voice channel
* @param {StageChannel|VoiceChannel} channel
* @param {StageChannel|VoiceChannel} channel The voice/stage channel
* @returns {Promise<Queue>}
*/
async connect(channel: StageChannel | VoiceChannel) {
@ -110,6 +112,8 @@ class Queue<T = unknown> {
/**
* Destroys this queue
* @param {boolean} [disconnect=this.options.leaveOnStop] If it should leave on destroy
* @returns {void}
*/
destroy(disconnect = this.options.leaveOnStop) {
this.connection.end();
@ -159,6 +163,7 @@ class Queue<T = unknown> {
/**
* Sets bitrate
* @param {number|"auto"} bitrate bitrate to set
* @returns {void}
*/
setBitrate(bitrate: number | "auto") {
if (!this.connection?.audioResource?.encoder) return;
@ -189,20 +194,22 @@ class Queue<T = unknown> {
}
/**
* Returns current volume amount
* The current volume amount
* @type {number}
*/
get volume() {
if (!this.connection) return 100;
return this.connection.volume;
}
/**
* Alternative volume setter
*/
set volume(amount: number) {
this.setVolume(amount);
}
/**
* The stream time of this queue
* @type {number}
*/
get streamTime() {
if (!this.connection) return 0;
const playbackTime = this._streamTime + this.connection.streamTime;
@ -213,14 +220,27 @@ class Queue<T = unknown> {
return NC ? playbackTime * NC : VW ? playbackTime * VW : playbackTime;
}
/**
* Returns enabled filters
* @returns {AudioFilters}
*/
getFiltersEnabled() {
return AudioFilters.names.filter((x) => this._activeFilters.includes(x));
}
/**
* Returns disabled filters
* @returns {AudioFilters}
*/
getFiltersDisabled() {
return AudioFilters.names.filter((x) => !this._activeFilters.includes(x));
}
/**
* Sets filters
* @param {QueueFilters} filters Queue filters
* @returns {Promise<void>}
*/
async setFilters(filters?: QueueFilters) {
if (!filters || !Object.keys(filters).length) {
// reset filters
@ -254,6 +274,11 @@ class Queue<T = unknown> {
});
}
/**
* Seeks to the given time
* @param {number} position The position
* @returns {boolean}
*/
async seek(position: number) {
if (!this.playing || !this.current) return false;
if (position < 1) position = 0;
@ -277,8 +302,8 @@ class Queue<T = unknown> {
}
/**
* @param {Track} [src] The track to play (if empty, uses first track from the queue)
* @param {PlayOptions={}} options The options
* @param {Track} [src] The track to play (if empty, uses first track from the queue)
* @param {PlayOptions} [options={}] The options
* @returns {Promise<void>}
*/
async play(src?: Track, options: PlayOptions = {}): Promise<void> {

View file

@ -92,6 +92,13 @@ class Track {
* @type {RawTrackData}
*/
/**
* The track id
* @name Track#_trackID
* @type {number}
* @readonly
*/
void this._patch(data);
}
@ -153,7 +160,7 @@ class Track {
/**
* Raw JSON representation of this track
* @returns {object}
* @returns {TrackJSON}
*/
toJSON(hidePlaylist?: boolean) {
return {

View file

@ -24,18 +24,37 @@ export interface VoiceEvents {
finish: () => any;
}
class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
class StreamDispatcher extends EventEmitter<VoiceEvents> {
public readonly voiceConnection: VoiceConnection;
public readonly audioPlayer: AudioPlayer;
public readonly channel: VoiceChannel | StageChannel;
public audioResource?: AudioResource<Track>;
private readyLock: boolean = false;
/**
* Creates new connection object
* @param {VoiceConnection} connection The connection
* @param {VoiceChannel|StageChannel} channel The connected channel
*/
constructor(connection: VoiceConnection, channel: VoiceChannel | StageChannel) {
super();
/**
* The voice connection
* @type {VoiceConnection}
*/
this.voiceConnection = connection;
/**
* The audio player
* @type {AudioPlayer}
*/
this.audioPlayer = createAudioPlayer();
/**
* The voice channel
* @type {VoiceChannel|StageChannel}
*/
this.channel = channel;
this.voiceConnection.on("stateChange", async (_, newState) => {
@ -85,7 +104,7 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
/**
* Creates stream
* @param {Readable|Duplex|string} src The stream source
* @param {({type?:StreamType;data?:any;})} [ops] Options
* @param {object} [ops={}] Options
* @returns {AudioResource}
*/
createStream(src: Readable | Duplex | string, ops?: { type?: StreamType; data?: any }) {
@ -100,6 +119,7 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
/**
* The player status
* @type {AudioPlayerStatus}
*/
get status() {
return this.audioPlayer.state.status;
@ -107,6 +127,7 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
/**
* Disconnects from voice
* @returns {void}
*/
disconnect() {
try {
@ -116,16 +137,26 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
/**
* Stops the player
* @returns {void}
*/
end() {
this.audioPlayer.stop();
}
/**
* Pauses the stream playback
* @param {boolean} [interpolateSilence=false] If true, the player will play 5 packets of silence after pausing to prevent audio glitches.
* @returns {boolean}
*/
pause(interpolateSilence?: boolean) {
const success = this.audioPlayer.pause(interpolateSilence);
return success;
}
/**
* Resumes the stream playback
* @returns {boolean}
*/
resume() {
const success = this.audioPlayer.unpause();
return success;
@ -133,7 +164,8 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
/**
* Play stream
* @param {AudioResource} resource The audio resource to play
* @param {AudioResource<Track>} [resource=this.audioResource] The audio resource to play
* @returns {Promise<StreamDispatcher>}
*/
async playStream(resource: AudioResource<Track> = this.audioResource) {
if (!resource) throw new Error("Audio resource is not available!");
@ -144,6 +176,11 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
return this;
}
/**
* Sets playback volume
* @param {number} value The volume amount
* @returns {boolean}
*/
setVolume(value: number) {
if (!this.audioResource || isNaN(value) || value < 0 || value > Infinity) return false;
@ -152,20 +189,32 @@ class BasicStreamDispatcher extends EventEmitter<VoiceEvents> {
return true;
}
/**
* The current volume
* @type {number}
*/
get volume() {
if (!this.audioResource || !this.audioResource.volume) return 100;
const currentVol = this.audioResource.volume.volume;
return Math.round(Math.pow(currentVol, 1 / 1.660964) * 100);
}
/**
* The playback time
* @type {number}
*/
get streamTime() {
if (!this.audioResource) return 0;
return this.audioResource.playbackDuration;
}
/**
* The paused state
* @type {boolean}
*/
get paused() {
return [AudioPlayerStatus.AutoPaused, AudioPlayerStatus.Paused].includes(this.audioPlayer.state.status);
}
}
export { BasicStreamDispatcher as StreamDispatcher };
export { StreamDispatcher as StreamDispatcher };

View file

@ -3,13 +3,24 @@ import { entersState, joinVoiceChannel, VoiceConnection, VoiceConnectionStatus }
import { StreamDispatcher } from "./BasicStreamDispatcher";
class VoiceUtils {
public cache = new Collection<Snowflake, StreamDispatcher>();
public cache: Collection<Snowflake, StreamDispatcher>;
/**
* Joins a voice channel
* The voice utils
*/
constructor() {
/**
* The cache where voice utils stores stream managers
* @type {Collection<Snowflake, StreamDispatcher>}
*/
this.cache = new Collection<Snowflake, StreamDispatcher>();
}
/**
* Joins a voice channel, creating basic stream dispatch manager
* @param {StageChannel|VoiceChannel} channel The voice channel
* @param {({deaf?: boolean;maxTime?: number;})} [options] Join options
* @returns {Promise<BasicStreamDispatcher>}
* @param {object} [options={}] Join options
* @returns {Promise<StreamDispatcher>}
*/
public async connect(
channel: VoiceChannel | StageChannel,
@ -24,6 +35,12 @@ class VoiceUtils {
return sub;
}
/**
* Joins a voice channel
* @param {StageChannel|VoiceChannel} [channel] The voice/stage channel to join
* @param {object} [options={}] Join options
* @returns {VoiceConnection}
*/
public async join(
channel: VoiceChannel | StageChannel,
options?: {
@ -50,12 +67,18 @@ class VoiceUtils {
/**
* Disconnects voice connection
* @param {VoiceConnection} connection The voice connection
* @returns {void}
*/
public disconnect(connection: VoiceConnection | StreamDispatcher) {
if (connection instanceof StreamDispatcher) return connection.voiceConnection.destroy();
return connection.destroy();
}
/**
* Returns Discord Player voice connection
* @param {Snowflake} guild The guild id
* @returns {StreamDispatcher}
*/
public getConnection(guild: Snowflake) {
return this.cache.get(guild);
}

View file

@ -140,38 +140,71 @@ export enum QueryType {
export interface PlayerEvents {
/**
* Emitted when bot gets disconnected from a voice channel
* @event Player#botDisconnect
* @param {Queue} queue The queue
*/
botDisconnect: (queue: Queue) => any;
/**
* Emitted when the voice channel is empty
* @event Player#channelEmpty
* @param {Queue} queue The queue
*/
channelEmpty: (queue: Queue) => any;
/**
* Emitted when bot connects to a voice channel
* @event Player#connectionCreate
* @param {Queue} queue The queue
* @param {StreamDispatcher} connection The discord player connection object
*/
connectionCreate: (queue: Queue, connection: StreamDispatcher) => any;
/**
* Debug information
* @event Player#debug
* @param {Queue} queue The queue
* @param {string} message The message
*/
debug: (queue: Queue, message: string) => any;
/**
* Emitted on error
* <warn>This event should handled properly otherwise it may crash your process!</warn>
* @event Player#error
* @param {Queue} queue The queue
* @param {Error} error The error
*/
error: (queue: Queue, error: Error) => any;
/**
* Emitted when queue ends
* @event Player#queueEnd
* @param {Queue} queue The queue
*/
queueEnd: (queue: Queue) => any;
/**
* Emitted when a single track is added
* @event Player#trackAdd
* @param {Queue} queue The queue
* @param {Track} track The track
*/
trackAdd: (queue: Queue, track: Track) => any;
/**
* Emitted when multiple tracks are added
* @event Player#tracksAdd
* @param {Queue} queue The queue
* @param {Track[]} tracks The tracks
*/
tracksAdd: (queue: Queue, track: Track[]) => any;
/**
* Emitted when a track starts playing
* @event Player#trackStart
* @param {Queue} queue The queue
* @param {Track} track The track
*/
trackStart: (queue: Queue, track: Track) => any;
}

View file

@ -16,6 +16,18 @@ const attachmentRegex =
// scary things above *sigh*
class QueryResolver {
/**
* Query resolver
*/
constructor() {
throw new Error("Cannot instantiate static class!");
}
/**
* Resolves the given search query
* @param {string} query The query
* @returns {QueryType}
*/
static resolve(query: string): QueryType {
if (SoundcloudValidateURL(query, "track")) return QueryType.SOUNDCLOUD_TRACK;
if (SoundcloudValidateURL(query, "playlist") || query.includes("/sets/")) return QueryType.SOUNDCLOUD_PLAYLIST;
@ -32,6 +44,11 @@ class QueryResolver {
return QueryType.YOUTUBE_SEARCH;
}
/**
* Parses vimeo id from url
* @param {string} query The query
* @returns {string}
*/
static getVimeoID(query: string): string {
return QueryResolver.resolve(query) === QueryType.VIMEO
? query

View file

@ -2,12 +2,29 @@ import { StageChannel, VoiceChannel } from "discord.js";
import { TimeData } from "../types/types";
class Util {
/**
* Utils
*/
constructor() {
throw new Error("Cannot instantiate static class");
}
/**
* Creates duration string
* @param {object} durObj The duration object
* @returns {string}
*/
static durationString(durObj: object) {
return Object.values(durObj)
.map((m) => (isNaN(m) ? 0 : m))
.join(":");
}
/**
* Parses milliseconds to consumable time object
* @param {number} milliseconds The time in ms
* @returns {TimeData}
*/
static parseMS(milliseconds: number) {
const round = milliseconds > 0 ? Math.floor : Math.ceil;
@ -19,6 +36,11 @@ class Util {
} as TimeData;
}
/**
* Builds time code
* @param {TimeData} duration The duration object
* @returns {string}
*/
static buildTimeCode(duration: TimeData) {
const items = Object.keys(duration);
const required = ["days", "hours", "minutes", "seconds"];
@ -31,15 +53,30 @@ class Util {
return final.length <= 3 ? `0:${final.padStart(2, "0") || 0}` : final;
}
/**
* Picks last item of the given array
* @param {any[]} arr The array
* @returns {any}
*/
static last<T = any>(arr: T[]): T {
if (!Array.isArray(arr)) return;
return arr[arr.length - 1];
}
/**
* Checks if the voice channel is empty
* @param {VoiceChannel|StageChannel} channel The voice channel
* @returns {boolean}
*/
static isVoiceEmpty(channel: VoiceChannel | StageChannel) {
return channel.members.filter((member) => !member.user.bot).size === 0;
}
/**
* Safer require
* @param {string} id Node require id
* @returns {any}
*/
static require(id: string) {
try {
return require(id);
@ -48,6 +85,11 @@ class Util {
}
}
/**
* Asynchronous timeout
* @param {number} time The time in ms to wait
* @returns {Promise<unknown>}
*/
static wait(time: number) {
return new Promise((r) => setTimeout(r, time).unref());
}

View file

@ -6,8 +6,7 @@
"outDir": "./lib",
"strict": true,
"strictNullChecks": false,
"esModuleInterop": true,
"removeComments": true
"esModuleInterop": true
},
"include": [
"src/**/*"