meocord
Version:
Decorator-based Discord bot framework built on discord.js. Brings NestJS-style controllers, dependency injection, guards, and testing utilities to bot development — with a full CLI and TypeScript-first design.
148 lines (132 loc) • 4.68 kB
JavaScript
/**
* MeoCord Framework
* Copyright (c) 2025 Ukasyah Rahmatullah Zada
* SPDX-License-Identifier: MIT
*/
import path from 'path'
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import { loadMeoCordConfig } from './dist/esm/util/meocord-config-loader.util.js'
import { prepareModifiedTsConfig } from './dist/esm/util/tsconfig.util.js'
import nodeExternals from 'webpack-node-externals'
const CWD = process.cwd()
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
const SRC_DIR = path.resolve(CWD, 'src')
const DIST_DIR = path.resolve(CWD, 'dist')
// Note: meocordConfig is loaded lazily inside createWebpackConfig()
// to avoid Bun's aggressive module caching issues in Docker containers
const tsConfigPath = prepareModifiedTsConfig()
const baseRules = [
{
test: /\.ts$/,
use: {
loader: 'swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: false,
decorators: true,
},
transform: {
decoratorMetadata: true,
legacyDecorator: true,
},
},
},
},
exclude: /node_modules/,
},
]
/**
* Merges two arrays returning unique elements
*/
const mergeUnique = (base = [], additions = []) => Array.from(new Set([...base, ...additions]))
/**
* Creates webpack configuration with framework defaults
*/
const createWebpackConfig = (overrides = {}) => {
const baseConfig = {
mode: overrides.mode ?? (IS_PRODUCTION ? 'production' : 'development'),
entry: overrides.entry ?? path.resolve(SRC_DIR, 'main.ts'),
target: 'node',
externals: mergeUnique([nodeExternals({ importType: 'module' })], overrides.externals),
module: {
...overrides.module,
rules: mergeUnique(baseRules, overrides.module?.rules),
},
resolve: {
...overrides.resolve,
extensions: mergeUnique(['.ts', '.js'], overrides.resolve?.extensions),
plugins: mergeUnique([new TsconfigPathsPlugin({ configFile: tsConfigPath })], overrides.resolve?.plugins),
},
output: {
filename: 'main.js',
path: DIST_DIR,
publicPath: path.join(process.cwd(), 'dist/'),
library: { type: 'module' },
...overrides.output,
},
experiments: {
outputModule: true,
...overrides.experiments,
},
stats: overrides.stats ?? (IS_PRODUCTION ? 'normal' : 'errors-warnings'),
devtool: overrides.devtool ?? (IS_PRODUCTION ? 'source-map' : 'eval-source-map'),
performance: {
hints: overrides.performance?.hints ?? (IS_PRODUCTION ? 'warning' : false),
...overrides.performance,
},
...overrides,
optimization: {
...overrides.optimization,
minimize: overrides.optimization?.minimize ?? IS_PRODUCTION,
minimizer: [],
},
}
const finalMinimizerArray = []
const shouldMinimize = baseConfig.optimization.minimize
if (shouldMinimize) {
const userProvidedMinimizers = overrides.optimization?.minimizer
let lastTerserInstance = null
const otherMinimizers = []
if (Array.isArray(userProvidedMinimizers)) {
for (let i = userProvidedMinimizers.length - 1; i >= 0; i--) {
const minimizer = userProvidedMinimizers[i]
if (minimizer?.constructor?.name === 'TerserPlugin') {
if (!lastTerserInstance) {
lastTerserInstance = minimizer
}
} else if (minimizer) {
otherMinimizers.unshift(minimizer)
}
}
}
finalMinimizerArray.push(...otherMinimizers)
let terserPluginToUse
if (lastTerserInstance) {
terserPluginToUse = new TerserPlugin({
test: lastTerserInstance.options?.test,
include: lastTerserInstance.options?.include,
exclude: lastTerserInstance.options?.exclude,
extractComments: lastTerserInstance.options?.extractComments,
parallel: lastTerserInstance.options?.parallel,
minify: lastTerserInstance.options?.minify,
terserOptions: {
...(lastTerserInstance.options?.terserOptions || {}),
keep_classnames: true,
},
})
} else {
terserPluginToUse = new TerserPlugin({ terserOptions: { keep_classnames: true } })
}
finalMinimizerArray.push(terserPluginToUse)
}
baseConfig.optimization.minimizer = finalMinimizerArray
return baseConfig
}
// Load user config lazily (fixes Bun Docker caching issues)
const meocordConfig = loadMeoCordConfig()
const initialConfig = createWebpackConfig()
const userModifiedConfig = meocordConfig?.webpack?.(initialConfig)
export default createWebpackConfig(userModifiedConfig ?? {})