mirror of
https://github.com/sern-handler/handler
synced 2026-06-19 22:32:14 +00:00
build: refactor/building (#252)
* refactor: conditional compilation of loading esm/cjs modules * refactor: move file loading file * refactor: add conditional compilation for building modules * refactor: add conditional compilation for building modules * perf: decrease build times * test * revert: typo and clean code * build: smaller build * chore:cleanscripts * chore:refactor readme * build:automerge lockfile * chore: remove build and upgrade readme * fix: dropdown * chore: fix * chore: more docs --------- Co-authored-by: jacoobes <jacobnguyend@gmail.com>
This commit is contained in:
@@ -52,6 +52,7 @@ typings/
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
@@ -108,6 +109,8 @@ tsup.config.js
|
||||
|
||||
tsconfig-base.json
|
||||
|
||||
tsconfig.cjs.json
|
||||
tsconfig-cjs.json
|
||||
|
||||
tsconfig.esm.json
|
||||
tsconfig-esm.json
|
||||
|
||||
renovate.json
|
||||
|
||||
82
README.md
82
README.md
@@ -16,7 +16,7 @@
|
||||
|
||||
## Why?
|
||||
- Most handlers don't support discord.js 14.7+
|
||||
- Customizable commands
|
||||
- Customizable, composable commands
|
||||
- Plug and play or customize to your liking
|
||||
- Embraces reactive programming for consistent and reliable backend
|
||||
- Customizable logger, error handling, and more
|
||||
@@ -44,27 +44,54 @@ pnpm add @sern/handler
|
||||
```
|
||||
|
||||
## 👶 Basic Usage
|
||||
<details open><summary>ping.ts</summary>
|
||||
|
||||
#### ` index.js (CommonJS)`
|
||||
|
||||
```js
|
||||
// Import the discord.js Client and GatewayIntentBits
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
|
||||
// Import Sern namespace
|
||||
const { Sern, single } = require('@sern/handler');
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages
|
||||
]
|
||||
```ts
|
||||
export default commandModule({
|
||||
type: CommandType.Slash,
|
||||
//Installed plugin to publish to discord api and allow access to owners only.
|
||||
plugins: [publish(), ownerOnly()],
|
||||
description: 'A ping pong command',
|
||||
execute(ctx) {
|
||||
ctx.reply('Hello owner of the bot');
|
||||
}
|
||||
});
|
||||
export const useContainer = Sern.makeDependencies({
|
||||
```
|
||||
</details>
|
||||
<details open><summary>modal.ts</summary>
|
||||
|
||||
```ts
|
||||
export default commandModule({
|
||||
type: CommandType.Modal,
|
||||
//Installed a plugin to make sure modal fields pass a validation.
|
||||
plugins : [
|
||||
assertFields({
|
||||
fields: {
|
||||
name: /^([^0-9]*)$/
|
||||
},
|
||||
failure: (errors, modal) => modal.reply('your submission did not pass the validations')
|
||||
})
|
||||
],
|
||||
execute : (modal) => {
|
||||
modal.reply('thanks for the submission!');
|
||||
}
|
||||
})
|
||||
```
|
||||
</details>
|
||||
<details open><summary>index.ts</summary>
|
||||
|
||||
```ts
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import { Sern, single, type Dependencies } from '@sern/handler';
|
||||
|
||||
//client has been declared previously
|
||||
|
||||
interface MyDependencies extends Dependencies {
|
||||
'@sern/client': Singleton<Client>;
|
||||
}
|
||||
export const useContainer = Sern.makeDependencies<MyDependencies>({
|
||||
build: root => root
|
||||
.add({ '@sern/client': single(() => client) })
|
||||
.upsert({ '@sern/logger': single(() => new DefaultLogging()) })
|
||||
});
|
||||
|
||||
//View docs for all options
|
||||
@@ -73,26 +100,13 @@ Sern.init({
|
||||
commands: 'src/commands',
|
||||
// events: 'src/events' (optional),
|
||||
containerConfig : {
|
||||
get: useContainer
|
||||
get: useContainer
|
||||
}
|
||||
});
|
||||
|
||||
client.login("YOUR_BOT_TOKEN_HERE");
|
||||
```
|
||||
|
||||
#### ` ping.js (CommonJS)`
|
||||
|
||||
```js
|
||||
const { CommandType, commandModule } = require('@sern/handler');
|
||||
|
||||
exports.default = commandModule({
|
||||
type: CommandType.Slash,
|
||||
description: 'A ping pong command',
|
||||
execute(ctx) {
|
||||
ctx.reply('pong!');
|
||||
}
|
||||
});
|
||||
```
|
||||
</details>
|
||||
|
||||
## 🤖 Bots Using sern
|
||||
- [Community Bot](https://github.com/sern-handler/sern-community), the community bot for our [discord server](https://sern.dev/discord).
|
||||
@@ -112,9 +126,7 @@ It is **highly encouraged** to use the [command line interface](https://github.c
|
||||
- [Support Server](https://sern.dev/discord)
|
||||
|
||||
## 👋 Contribute
|
||||
|
||||
- Read our contribution [guidelines](https://github.com/sern-handler/handler/blob/main/.github/CONTRIBUTING.md) carefully
|
||||
- Pull up on [issues](https://github.com/sern-handler/handler/issues) and report bugs
|
||||
- All kinds of contributions are welcomed.
|
||||
|
||||
|
||||
|
||||
14
package.json
14
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "@sern/handler",
|
||||
"packageManager": "pnpm@7.28.0",
|
||||
"version": "2.6.0",
|
||||
"description": "A customizable, batteries-included, powerful discord.js framework to automate and streamline bot development.",
|
||||
"description": "A complete, customizable, typesafe, & reactive framework for discord bots.",
|
||||
"main": "dist/cjs/index.cjs",
|
||||
"module": "dist/esm/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -13,12 +13,13 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "tsup --dts --watch",
|
||||
"watch": "tsup --watch",
|
||||
"clean-modules": "rimraf node_modules/ && npm install",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"format": "eslint src/**/*.ts --fix",
|
||||
"build": "tsup && node scripts/mkjson.mjs dist/cjs dist/esm && tsup --dts-only --outDir dist",
|
||||
"publish": "npm run build && npm publish",
|
||||
"build:dev": "tsup && tsup --dts-only --outDir dist",
|
||||
"build:prod" : "tsup --minify && tsup --dts-only --outDir dist",
|
||||
"publish": "npm run build:prod && npm publish",
|
||||
"pretty": "prettier --write ."
|
||||
},
|
||||
"keywords": [
|
||||
@@ -36,15 +37,16 @@
|
||||
"iti": "^0.6.0",
|
||||
"rxjs": "^7.8.0",
|
||||
"ts-pattern": "^4.1.4",
|
||||
"ts-results-es": "^3.5.0"
|
||||
"ts-results-es": "^3.5.0",
|
||||
"tsup": "^6.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "5.54.0",
|
||||
"@typescript-eslint/parser": "5.54.0",
|
||||
"discord.js": "^14.7.1",
|
||||
"esbuild-ifdef": "^0.2.0",
|
||||
"eslint": "8.30.0",
|
||||
"prettier": "2.8.4",
|
||||
"tsup": "^6.6.3",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
4084
pnpm-lock.yaml
generated
4084
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,6 @@
|
||||
"schedule": ["every weekend"],
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true,
|
||||
"automerge": false
|
||||
"automerge": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { writeFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
// A quick script to regenerate package.jsons for each cjs and esm after tsup cleans distributions
|
||||
const locations = process.argv;
|
||||
locations.shift();
|
||||
locations.shift();
|
||||
for (const loc of locations) {
|
||||
if (loc.endsWith('cjs')) {
|
||||
await writeFile(join(loc, 'package.json'), JSON.stringify({ type: 'commonjs' }));
|
||||
} else {
|
||||
await writeFile(join(loc, 'package.json'), JSON.stringify({ type: 'module' }));
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ const createMessageProcessor = (
|
||||
//This concatMap checks if module is undefined, and if it is, do not continue.
|
||||
// Synonymous to filterMap, but I haven't thought of a generic implementation for filterMap yet
|
||||
concatMap(message => {
|
||||
const [prefix, ...rest] = fmt(message, defaultPrefix);
|
||||
const [prefix, ...rest] = fmt(message.content, defaultPrefix);
|
||||
const module = get(ms => ms.TextCommands.get(prefix) ?? ms.BothCommands.get(prefix));
|
||||
if (module === undefined) {
|
||||
return EMPTY;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { concatMap, EMPTY, from, Observable, of, pipe, tap, throwError } from 'r
|
||||
import type { SernError } from '../structures';
|
||||
import { Result } from 'ts-results-es';
|
||||
import type { AnyModule, CommandModule, EventModule, Module } from '../../types/module';
|
||||
import { _const as i } from '../utilities/functions';
|
||||
import SernEmitter from '../sernEmitter';
|
||||
import { callPlugin, everyPluginOk, filterMapTo } from './operators';
|
||||
import type { Processed } from '../../types/handler';
|
||||
@@ -78,7 +77,7 @@ export function executeModule(
|
||||
emitter.emit('module.activate', SernEmitter.success(module));
|
||||
return EMPTY;
|
||||
} else {
|
||||
return throwError(i(SernEmitter.failure(module, result.val)));
|
||||
return throwError(() => SernEmitter.failure(module, result.val));
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fromEvent, pipe, switchMap, take } from 'rxjs';
|
||||
import * as Files from '../utilities/readFile';
|
||||
import * as Files from '../module-loading/readFile';
|
||||
import { errTap, scanModule } from './observableHandling';
|
||||
import { CommandType, type ModuleStore, SernError } from '../structures';
|
||||
import { match } from 'ts-pattern';
|
||||
@@ -8,13 +8,13 @@ import { ApplicationCommandType, ComponentType } from 'discord.js';
|
||||
import type { CommandModule } from '../../types/module';
|
||||
import type { Processed } from '../../types/handler';
|
||||
import type { ErrorHandling, Logging, ModuleManager } from '../contracts';
|
||||
import { _const, err, ok } from '../utilities/functions';
|
||||
import { err, ok } from '../utilities/functions';
|
||||
import { defineAllFields } from './operators';
|
||||
import SernEmitter from '../sernEmitter';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
|
||||
export function makeReadyEvent(
|
||||
[sEmitter, client, errorHandler, , moduleManager]: [
|
||||
[sEmitter, client, errorHandler, ,moduleManager]: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
@@ -61,7 +61,7 @@ function registerModule<T extends Processed<CommandModule>>(
|
||||
): Result<void, void> {
|
||||
const name = mod.name;
|
||||
const insert = (cb: (ms: ModuleStore) => void) => {
|
||||
const set = Result.wrap(_const(manager.set(cb)));
|
||||
const set = Result.wrap(() => manager.set(cb));
|
||||
return set.ok ? ok() : err();
|
||||
};
|
||||
return match(mod as Processed<CommandModule>)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { catchError, finalize, map, tap, mergeAll } from 'rxjs';
|
||||
import { buildData } from '../utilities/readFile';
|
||||
import { catchError, finalize, map, mergeAll } from 'rxjs';
|
||||
import { buildData } from '../module-loading/readFile';
|
||||
import type { Dependencies, Processed } from '../../types/handler';
|
||||
import { errTap, scanModule } from './observableHandling';
|
||||
import type { CommandModule, EventModule } from '../../types/module';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { readdirSync, statSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { type Observable, from, concatAll } from 'rxjs';
|
||||
import { type Observable, from, mergeAll } from 'rxjs';
|
||||
import { SernError } from '../structures/errors';
|
||||
import { type Result, Err, Ok } from 'ts-results-es';
|
||||
|
||||
@@ -18,16 +18,13 @@ function readPath(dir: string, arrayOfFiles: string[] = []): string[] {
|
||||
|
||||
return arrayOfFiles;
|
||||
}
|
||||
|
||||
export const fmtFileName = (n: string) => n.substring(0, n.length - 3);
|
||||
|
||||
/**
|
||||
* a directory string is converted into a stream of modules.
|
||||
* starts the stream of modules that sern needs to process on init
|
||||
* @returns {Observable<{ mod: Module; absPath: string; }[]>} data from command files
|
||||
* @param commandDir
|
||||
*/
|
||||
|
||||
export function buildData<T>(commandDir: string): Observable<
|
||||
Result<
|
||||
{
|
||||
@@ -41,13 +38,14 @@ export function buildData<T>(commandDir: string): Observable<
|
||||
return from(
|
||||
Promise.all(
|
||||
commands.map(async absPath => {
|
||||
let module: T | undefined;
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
module = require(absPath).default;
|
||||
} catch {
|
||||
module = (await import(`file:///` + absPath)).default;
|
||||
}
|
||||
let module: T | undefined
|
||||
|
||||
/// #if MODE === 'esm'
|
||||
= (await import(`file:///` + absPath)).default
|
||||
/// #elif MODE === 'cjs'
|
||||
= require(absPath).default;
|
||||
/// #endif
|
||||
|
||||
if (module === undefined) {
|
||||
return Err(SernError.UndefinedModule);
|
||||
}
|
||||
@@ -57,7 +55,7 @@ export function buildData<T>(commandDir: string): Observable<
|
||||
return Ok({ module, absPath });
|
||||
}),
|
||||
),
|
||||
).pipe(concatAll());
|
||||
).pipe(mergeAll());
|
||||
}
|
||||
|
||||
export function getCommands(dir: string): string[] {
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as Files from './readFile';
|
||||
import * as Files from '../module-loading/readFile';
|
||||
import { basename } from 'path';
|
||||
import { Err, Ok } from 'ts-results-es';
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Message } from 'discord.js';
|
||||
|
||||
/**
|
||||
* Removes the first character(s) _[depending on prefix length]_ of the message
|
||||
@@ -10,6 +9,6 @@ import type { Message } from 'discord.js';
|
||||
* console.log(fmt(message, '!'));
|
||||
* // [ 'ping' ]
|
||||
*/
|
||||
export function fmt(msg: Message, prefix: string): string[] {
|
||||
return msg.content.slice(prefix.length).trim().split(/\s+/g);
|
||||
export function fmt(msg: string, prefix: string): string[] {
|
||||
return msg.slice(prefix.length).trim().split(/\s+/g);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
import { writeFile } from 'fs/promises';
|
||||
import ifdefPlugin from 'esbuild-ifdef'
|
||||
const shared = {
|
||||
entry: ['src/index.ts'],
|
||||
external: ['discord.js'],
|
||||
platform: 'node',
|
||||
clean: true,
|
||||
sourcemap: false,
|
||||
minify: true,
|
||||
};
|
||||
export default defineConfig([
|
||||
{
|
||||
@@ -14,15 +15,23 @@ export default defineConfig([
|
||||
tsconfig: './tsconfig-esm.json',
|
||||
outDir: './dist/esm',
|
||||
treeshake: true,
|
||||
esbuildPlugins: [
|
||||
ifdefPlugin({ variables: { MODE: 'esm' }, verbose: true })
|
||||
],
|
||||
outExtension() {
|
||||
return {
|
||||
js: '.mjs',
|
||||
};
|
||||
},
|
||||
async onSuccess() {
|
||||
console.log('writing json esm')
|
||||
await writeFile('./dist/esm/package.json', JSON.stringify({ type: 'module' }))
|
||||
},
|
||||
...shared,
|
||||
},
|
||||
{
|
||||
format: 'cjs',
|
||||
esbuildPlugins: [ifdefPlugin({ variables: { MODE: 'cjs' }, verbose: true })],
|
||||
splitting: false,
|
||||
target: 'node16',
|
||||
tsconfig: './tsconfig-cjs.json',
|
||||
@@ -32,6 +41,10 @@ export default defineConfig([
|
||||
js: '.cjs',
|
||||
};
|
||||
},
|
||||
async onSuccess() {
|
||||
console.log('writing json commonjs')
|
||||
await writeFile('./dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }));
|
||||
},
|
||||
...shared,
|
||||
},
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user