mirror of
https://github.com/xHyroM/roles-bot.git
synced 2024-11-22 08:21:05 +01:00
refactor(eslint)
This commit is contained in:
parent
85dbdfe907
commit
a93f35b2ef
14 changed files with 2152 additions and 215 deletions
4
.eslintignore
Normal file
4
.eslintignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
dist/
|
||||||
|
tests/
|
||||||
|
src/web/
|
||||||
|
webpack.config.js
|
33
.eslintrc.json
Normal file
33
.eslintrc.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 12
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
"tab"
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"prefer-const": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off"
|
||||||
|
}
|
||||||
|
}
|
1909
package-lock.json
generated
1909
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,9 @@
|
||||||
"@cloudflare/workers-types": "^3.3.0",
|
"@cloudflare/workers-types": "^3.3.0",
|
||||||
"@types/jest": "^27.0.3",
|
"@types/jest": "^27.0.3",
|
||||||
"@types/service-worker-mock": "^2.0.1",
|
"@types/service-worker-mock": "^2.0.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||||
"discord-api-types": "^0.25.2",
|
"discord-api-types": "^0.25.2",
|
||||||
|
"eslint": "^8.6.0",
|
||||||
"service-worker-mock": "^2.0.5",
|
"service-worker-mock": "^2.0.5",
|
||||||
"ts-loader": "^9.2.6",
|
"ts-loader": "^9.2.6",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
|
|
221
src/bot/bot.ts
221
src/bot/bot.ts
|
@ -3,138 +3,139 @@ import { isJSON } from './isJson';
|
||||||
import { isSnowflake } from './snowflakeUtils';
|
import { isSnowflake } from './snowflakeUtils';
|
||||||
import { verify } from './verify';
|
import { verify } from './verify';
|
||||||
|
|
||||||
const respond = (response: APIInteractionResponse) => new Response(JSON.stringify(response), {headers: {'content-type': 'application/json'}})
|
const respond = (response: APIInteractionResponse) => new Response(JSON.stringify(response), {headers: {'content-type': 'application/json'}});
|
||||||
|
|
||||||
const badFormatting = (rolesMax?: boolean) => {
|
const badFormatting = (rolesMax?: boolean) => {
|
||||||
return respond({
|
return respond({
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
type: InteractionResponseType.ChannelMessageWithSource,
|
||||||
data: {
|
data: {
|
||||||
flags: 64,
|
flags: 64,
|
||||||
content: `${rolesMax ? 'You can have maximum 25 buttons. (5x5)' : 'Bad formatting, generate [here](https://xhyrom.github.io/roles-bot)'}`
|
content: `${rolesMax ? 'You can have maximum 25 buttons. (5x5)' : 'Bad formatting, generate [here](https://xhyrom.github.io/roles-bot)'}`
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const handleRequest = async(request: Request): Promise<Response> => {
|
export const handleRequest = async(request: Request): Promise<Response> => {
|
||||||
if (!request.headers.get('X-Signature-Ed25519') || !request.headers.get('X-Signature-Timestamp')) return Response.redirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
|
if (!request.headers.get('X-Signature-Ed25519') || !request.headers.get('X-Signature-Timestamp')) return Response.redirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
|
||||||
if (!await verify(request)) return new Response('', { status: 401 })
|
if (!await verify(request)) return new Response('', { status: 401 });
|
||||||
|
|
||||||
const interaction = await request.json() as APIPingInteraction | APIApplicationCommandInteraction | APIMessageComponentInteraction;
|
const interaction = await request.json() as APIPingInteraction | APIApplicationCommandInteraction | APIMessageComponentInteraction;
|
||||||
|
|
||||||
if (interaction.type === InteractionType.Ping)
|
if (interaction.type === InteractionType.Ping)
|
||||||
return respond({
|
return respond({
|
||||||
type: InteractionResponseType.Pong
|
type: InteractionResponseType.Pong
|
||||||
})
|
});
|
||||||
|
|
||||||
if (interaction.type === InteractionType.ApplicationCommand && interaction.data.name === 'setup') {
|
if (interaction.type === InteractionType.ApplicationCommand && interaction.data.name === 'setup') {
|
||||||
// @ts-ignore
|
|
||||||
if ((interaction.member?.permissions & 0x10) !== 0x10) return respond({
|
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
|
||||||
flags: 64,
|
|
||||||
content: `Required permissions: \`MANAGE_ROLES\``
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// @ts-ignore
|
if ((Number(interaction.member?.permissions) & 0x10) !== 0x10) return respond({
|
||||||
const json = isJSON(interaction.data.options[0].value) ? JSON.parse(interaction.data.options[0].value) : null;
|
type: InteractionResponseType.ChannelMessageWithSource,
|
||||||
|
data: {
|
||||||
|
flags: 64,
|
||||||
|
content: 'Required permissions: `MANAGE_ROLES`'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!json) return badFormatting();
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
const json = isJSON(interaction.data.options[0].value) ? JSON.parse(interaction.data.options[0].value) : null;
|
||||||
|
|
||||||
const channelId = json.channel;
|
if (!json) return badFormatting();
|
||||||
const message = json.message?.toString();
|
|
||||||
let roles = json.roles;
|
|
||||||
|
|
||||||
if (!channelId) return badFormatting();
|
const channelId = json.channel;
|
||||||
if (!message) return badFormatting();
|
const message = json.message?.toString();
|
||||||
if (!roles || Object.values(json.roles).filter((role: any) => role.id && role.label).length === 0 || roles.length === 0 || roles.length > 25) return badFormatting(roles.length > 25);
|
let roles = json.roles;
|
||||||
|
|
||||||
roles = roles.map((r: any) => {
|
if (!channelId) return badFormatting();
|
||||||
let o: any = {
|
if (!message) return badFormatting();
|
||||||
type: 2,
|
if (!roles || Object.values(json.roles).filter((role: any) => role.id && role.label).length === 0 || roles.length === 0 || roles.length > 25) return badFormatting(roles.length > 25);
|
||||||
style: r.style || 2,
|
|
||||||
label: r.label,
|
|
||||||
custom_id: r.id
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r.emoji) {
|
roles = roles.map((r: any) => {
|
||||||
if (isSnowflake(r.emoji)) o.emoji = { id: r.emoji, name: null };
|
const o: any = {
|
||||||
else o.emoji = { id: null, name: r.emoji };
|
type: 2,
|
||||||
}
|
style: r.style || 2,
|
||||||
|
label: r.label,
|
||||||
|
custom_id: r.id
|
||||||
|
};
|
||||||
|
|
||||||
return o;
|
if (r.emoji) {
|
||||||
})
|
if (isSnowflake(r.emoji)) o.emoji = { id: r.emoji, name: null };
|
||||||
|
else o.emoji = { id: null, name: r.emoji };
|
||||||
|
}
|
||||||
|
|
||||||
const finalComponents = [];
|
return o;
|
||||||
for (let i = 0; i <= roles.length; i += 5) {
|
});
|
||||||
const row: any = {
|
|
||||||
type: 1,
|
|
||||||
components: []
|
|
||||||
}
|
|
||||||
|
|
||||||
const btnslice: any = roles.slice(i, i + 5);
|
const finalComponents = [];
|
||||||
|
for (let i = 0; i <= roles.length; i += 5) {
|
||||||
|
const row: any = {
|
||||||
|
type: 1,
|
||||||
|
components: []
|
||||||
|
};
|
||||||
|
|
||||||
for (let y: number = 0; y < btnslice.length; y++) row.components.push(btnslice[y]);
|
const btnslice: any = roles.slice(i, i + 5);
|
||||||
|
|
||||||
finalComponents.push(row);
|
for (let y = 0; y < btnslice.length; y++) row.components.push(btnslice[y]);
|
||||||
}
|
|
||||||
|
|
||||||
await fetch(`${RouteBases.api}/channels/${channelId}/messages`, {
|
finalComponents.push(row);
|
||||||
method: 'POST',
|
}
|
||||||
headers: {
|
|
||||||
'Authorization': `Bot ${CLIENT_TOKEN}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
content: message,
|
|
||||||
components: finalComponents
|
|
||||||
})
|
|
||||||
}).catch(e => e)
|
|
||||||
|
|
||||||
return respond({
|
await fetch(`${RouteBases.api}/channels/${channelId}/messages`, {
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
method: 'POST',
|
||||||
data: {
|
headers: {
|
||||||
flags: 64,
|
'Authorization': `Bot ${CLIENT_TOKEN}`,
|
||||||
content: 'Done!'
|
'Content-Type': 'application/json'
|
||||||
}
|
},
|
||||||
})
|
body: JSON.stringify({
|
||||||
} else if (interaction.type === InteractionType.MessageComponent) {
|
content: message,
|
||||||
const roleId = interaction.data.custom_id;
|
components: finalComponents
|
||||||
const url = `${RouteBases.api}${Routes.guildMemberRole(interaction.guild_id || '', interaction.member?.user.id || '', roleId)}`;
|
})
|
||||||
|
}).catch(e => e);
|
||||||
|
|
||||||
let method = "";
|
return respond({
|
||||||
let content = "";
|
type: InteractionResponseType.ChannelMessageWithSource,
|
||||||
|
data: {
|
||||||
|
flags: 64,
|
||||||
|
content: 'Done!'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (interaction.type === InteractionType.MessageComponent) {
|
||||||
|
const roleId = interaction.data.custom_id;
|
||||||
|
const url = `${RouteBases.api}${Routes.guildMemberRole(interaction.guild_id || '', interaction.member?.user.id || '', roleId)}`;
|
||||||
|
|
||||||
if (!interaction?.member?.roles?.includes(roleId)) {
|
let method = '';
|
||||||
content = `Gave the <@&${roleId}> role!`;
|
let content = '';
|
||||||
method = 'PUT';
|
|
||||||
} else {
|
|
||||||
content = `Removed the <@&${roleId}> role!`;
|
|
||||||
method = 'DELETE';
|
|
||||||
}
|
|
||||||
|
|
||||||
await fetch(url, {
|
if (!interaction?.member?.roles?.includes(roleId)) {
|
||||||
method: method,
|
content = `Gave the <@&${roleId}> role!`;
|
||||||
headers: {
|
method = 'PUT';
|
||||||
'Authorization': `Bot ${CLIENT_TOKEN}`
|
} else {
|
||||||
}
|
content = `Removed the <@&${roleId}> role!`;
|
||||||
}).catch(e => e);
|
method = 'DELETE';
|
||||||
|
}
|
||||||
|
|
||||||
return respond({
|
await fetch(url, {
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
method: method,
|
||||||
data: {
|
headers: {
|
||||||
flags: MessageFlags.Ephemeral,
|
'Authorization': `Bot ${CLIENT_TOKEN}`
|
||||||
content: content,
|
}
|
||||||
allowed_mentions: { parse: [] }
|
}).catch(e => e);
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return respond({
|
return respond({
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
type: InteractionResponseType.ChannelMessageWithSource,
|
||||||
data: {
|
data: {
|
||||||
flags: MessageFlags.Ephemeral,
|
flags: MessageFlags.Ephemeral,
|
||||||
content: 'Beep boop, boop beep?'
|
content: content,
|
||||||
}
|
allowed_mentions: { parse: [] }
|
||||||
})
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return respond({
|
||||||
|
type: InteractionResponseType.ChannelMessageWithSource,
|
||||||
|
data: {
|
||||||
|
flags: MessageFlags.Ephemeral,
|
||||||
|
content: 'Beep boop, boop beep?'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
import { handleRequest } from './bot'
|
import { handleRequest } from './bot';
|
||||||
|
|
||||||
addEventListener('fetch', (event) => {
|
addEventListener('fetch', (event) => {
|
||||||
event.respondWith(handleRequest(event.request))
|
event.respondWith(handleRequest(event.request));
|
||||||
})
|
});
|
|
@ -1,11 +1,11 @@
|
||||||
export const isJSON = (data: any): boolean => {
|
export const isJSON = (data: any): boolean => {
|
||||||
if (typeof data !== 'string') return false;
|
if (typeof data !== 'string') return false;
|
||||||
try {
|
try {
|
||||||
const result = JSON.parse(data);
|
const result = JSON.parse(data);
|
||||||
const type = result.toString();
|
const type = result.toString();
|
||||||
|
|
||||||
return type === '[object Object]' || type === '[object Array]';
|
return type === '[object Object]' || type === '[object Array]';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
export const toSnowflake = (snowflake: number, epoch = DISCORD_EPOCH) => {
|
export const toSnowflake = (snowflake: number, epoch = DISCORD_EPOCH) => {
|
||||||
return new Date(snowflake / 4194304 + epoch)
|
return new Date(snowflake / 4194304 + epoch);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const DISCORD_EPOCH = 1420070400000;
|
export const DISCORD_EPOCH = 1420070400000;
|
||||||
|
|
||||||
|
@ -12,4 +12,4 @@ export const isSnowflake = (snowflake: number, epoch?: number) => {
|
||||||
if (isNaN(timestamp.getTime())) return false;
|
if (isNaN(timestamp.getTime())) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
4
src/bot/types.d.ts
vendored
4
src/bot/types.d.ts
vendored
|
@ -1,2 +1,2 @@
|
||||||
declare const CLIENT_PUBLIC_KEY: string
|
declare const CLIENT_PUBLIC_KEY: string;
|
||||||
declare const CLIENT_TOKEN: string
|
declare const CLIENT_TOKEN: string;
|
|
@ -3,35 +3,35 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function hex2bin(hex: string) {
|
function hex2bin(hex: string) {
|
||||||
const buf = new Uint8Array(Math.ceil(hex.length / 2));
|
const buf = new Uint8Array(Math.ceil(hex.length / 2));
|
||||||
for (var i = 0; i < buf.length; i++) {
|
for (let i = 0; i < buf.length; i++) {
|
||||||
buf[i] = parseInt(hex.substr(i * 2, 2), 16);
|
buf[i] = parseInt(hex.substr(i * 2, 2), 16);
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PUBLIC_KEY = crypto.subtle.importKey(
|
const PUBLIC_KEY = crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
hex2bin(CLIENT_PUBLIC_KEY || ''),
|
hex2bin(CLIENT_PUBLIC_KEY || ''),
|
||||||
{
|
{
|
||||||
name: 'NODE-ED25519',
|
name: 'NODE-ED25519',
|
||||||
namedCurve: 'NODE-ED25519',
|
namedCurve: 'NODE-ED25519',
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
['verify'],
|
['verify'],
|
||||||
);
|
);
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
export async function verify(request: Request) {
|
export async function verify(request: Request) {
|
||||||
const signature = hex2bin(request.headers.get('X-Signature-Ed25519')!);
|
const signature = hex2bin(request.headers.get('X-Signature-Ed25519') || '');
|
||||||
const timestamp = request.headers.get('X-Signature-Timestamp');
|
const timestamp = request.headers.get('X-Signature-Timestamp');
|
||||||
const unknown = await request.clone().text();
|
const unknown = await request.clone().text();
|
||||||
|
|
||||||
return await crypto.subtle.verify(
|
return await crypto.subtle.verify(
|
||||||
'NODE-ED25519',
|
'NODE-ED25519',
|
||||||
await PUBLIC_KEY,
|
await PUBLIC_KEY,
|
||||||
signature,
|
signature,
|
||||||
encoder.encode(timestamp + unknown),
|
encoder.encode(timestamp + unknown),
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -2,5 +2,5 @@ import '../styles/css/style.css';
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
|
|
||||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return <Component {...pageProps} />
|
return <Component {...pageProps} />;
|
||||||
}
|
}
|
|
@ -1,34 +1,34 @@
|
||||||
import Head from "next/head";
|
import Head from 'next/head';
|
||||||
import Script from "next/script";
|
import Script from 'next/script';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Roles Bot</title>
|
<title>Roles Bot</title>
|
||||||
<link rel="icon" href="logo.ico" />
|
<link rel="icon" href="logo.ico" />
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css" />
|
||||||
|
|
||||||
<script src="https://kit.fontawesome.com/5acf4d9e80.js" crossOrigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/5acf4d9e80.js" crossOrigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
|
||||||
</Head>
|
</Head>
|
||||||
<section className="flex-container">
|
<section className="flex-container">
|
||||||
<div className="container animate__animated animate__fadeIn">
|
<div className="container animate__animated animate__fadeIn">
|
||||||
<h1>Generate</h1>
|
<h1>Generate</h1>
|
||||||
<form>
|
<form>
|
||||||
<input placeholder="Your Message" name="message" id="message"/><br />
|
<input placeholder="Your Message" name="message" id="message"/><br />
|
||||||
<input placeholder="Channel Id" name="channel" id="channel"/>
|
<input placeholder="Channel Id" name="channel" id="channel"/>
|
||||||
</form>
|
</form>
|
||||||
<button id="addRole">Add Role</button>
|
<button id="addRole">Add Role</button>
|
||||||
<button id="buttonCopy">Copy</button>
|
<button id="buttonCopy">Copy</button>
|
||||||
<pre className={`hljs language-json copy`} id="jsonPre"><code id="json" className="code"></code></pre>
|
<pre className={'hljs language-json copy'} id="jsonPre"><code id="json" className="code"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
<Script src="script.js"></Script>
|
<Script src="script.js"></Script>
|
||||||
<Script id='hljs'>hljs.initHighlightingOnLoad();</Script>
|
<Script id='hljs'>hljs.initHighlightingOnLoad();</Script>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"message": "lol",
|
'message': 'lol',
|
||||||
"channel": "862700556438732851",
|
'channel': '862700556438732851',
|
||||||
"roles": [
|
'roles': [
|
||||||
{"id": "777805077825060867","label":"Bots","emoji":"😦"},
|
{'id': '777805077825060867','label':'Bots','emoji':'😦'},
|
||||||
{"id":"922762668841009152","label":"Ping","emoji":"🤩"}
|
{'id':'922762668841009152','label':'Ping','emoji':'🤩'}
|
||||||
]
|
]
|
||||||
}
|
};
|
|
@ -3,25 +3,25 @@ const path = require('path');
|
||||||
const mode = process.env.NODE_ENV || 'production';
|
const mode = process.env.NODE_ENV || 'production';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
output: {
|
output: {
|
||||||
filename: `worker.${mode}.js`,
|
filename: `worker.${mode}.js`,
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
},
|
},
|
||||||
mode,
|
mode,
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.js'],
|
extensions: ['.ts', '.tsx', '.js'],
|
||||||
plugins: [],
|
plugins: [],
|
||||||
fallback: { util: false }
|
fallback: { util: false }
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
loader: 'ts-loader',
|
loader: 'ts-loader',
|
||||||
options: {
|
options: {
|
||||||
transpileOnly: true,
|
transpileOnly: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
};
|
Loading…
Reference in a new issue