2025-03-21 19:59:16 +07:00
import chalk from 'chalk'
2026-07-26 15:36:54 +07:00
import { execSync } from 'child_process'
import { mkdir , readdir , rm , writeFile } from 'fs/promises'
import { dirname , parse } from 'path'
2025-06-03 16:24:58 +07:00
import { rolldown } from 'rolldown'
2025-09-10 00:09:26 +07:00
import { importGlobPlugin } from 'rolldown/experimental'
2026-07-26 15:36:54 +07:00
import { fileURLToPath } from 'url'
import pkg from '../package.json' with { type : 'json' }
import { exists } from './_shared'
2025-09-10 00:18:10 +07:00
import asRequire from './plugins/as-require'
2025-09-10 00:09:26 +07:00
import hermesSwcPlugin from './plugins/hermes-swc'
import hermesCPlugin from './plugins/hermesc'
2025-09-10 00:48:42 +07:00
import importDefer from './plugins/import-defer'
2025-09-10 00:09:26 +07:00
import shimAliases from './plugins/shim-aliases'
2025-05-01 01:29:55 +07:00
2026-07-26 15:36:54 +07:00
const __dirname = dirname ( fileURLToPath ( import . meta . url ) )
const ShimsDir = ` ${ __dirname } /../shims `
const AssetsDir = ` ${ __dirname } /../src/assets `
const GeneratedAssetsDir = ` ${ __dirname } /../dist/assets/generated `
2025-05-19 01:42:47 +07:00
await rm ( GeneratedAssetsDir , { recursive : true , force : true } )
2025-06-15 17:43:27 +07:00
. then ( ( ) = >
console . debug ( chalk . gray ( '\u{1F5BB} Deleted old generated assets' ) ) ,
)
2025-05-19 01:42:47 +07:00
. catch ( )
2025-06-15 17:43:27 +07:00
const Dev =
process . argv . includes ( '--dev' ) || process . env . NODE_ENV === 'development'
2025-06-15 18:01:30 +07:00
// If this file is being run directly, build the project
2026-07-26 15:36:54 +07:00
if ( import . meta . main ) build ( )
2025-06-15 18:01:30 +07:00
2025-06-15 17:43:27 +07:00
export default async function build ( dev = Dev , log = true ) {
2025-03-21 19:59:16 +07:00
const start = performance . now ( )
2025-05-07 02:53:31 +07:00
if ( log ) console . info ( chalk . gray ( '\u{1F5BB} Generating assets...' ) )
2025-05-07 01:21:27 +07:00
await generateAssets ( )
2025-05-07 02:53:31 +07:00
if ( log ) console . info ( chalk . cyanBright ( '\u{1F5BB} Assets generated' ) )
if ( log ) console . info ( chalk . gray ( '\u{1F5CE} Compiling JS...' ) )
2025-05-07 01:21:27 +07:00
2026-07-26 15:36:54 +07:00
const COMMIT = execSync ( 'git rev-parse HEAD' )
. toString ( )
. trim ( )
. substring ( 0 , 7 )
2025-07-12 02:50:48 +07:00
const REPO = 'revenge-mod/revenge-bundle-next'
2025-03-21 19:59:16 +07:00
const bundle = await rolldown ( {
input : 'src/index.ts' ,
platform : 'neutral' ,
2025-10-14 00:16:49 +07:00
external : [ /^node:/ ] ,
2025-07-30 20:15:58 +07:00
optimization : {
2025-11-12 15:05:35 +07:00
inlineConst : {
mode : 'smart' ,
pass : 5 ,
} ,
2025-07-30 20:15:58 +07:00
pifeForModuleWrappers : true ,
} ,
2026-02-26 16:22:33 +07:00
experimental : {
lazyBarrel : true ,
} ,
2025-07-30 20:15:58 +07:00
preserveEntrySignatures : false ,
transform : {
2025-11-12 15:05:35 +07:00
define : {
__BUILD_DISCORD_SERVER_URL__ : JSON.stringify (
'https://discord.com/invite/ddcQf3s2Uq' ,
) ,
__BUILD_SOURCE_REPOSITORY_URL__ : JSON.stringify (
` https://github.com/ ${ REPO } ` ,
) ,
__BUILD_LICENSE_URL__ : JSON.stringify (
` https://raw.githubusercontent.com/ ${ REPO } / ${ COMMIT } /LICENSE ` ,
) ,
__BUILD_VERSION__ : JSON.stringify ( pkg . version ) ,
__BUILD_COMMIT__ : JSON.stringify ( COMMIT ) ,
__BUILD_BRANCH__ : JSON.stringify (
2026-07-26 15:36:54 +07:00
execSync ( 'git rev-parse --abbrev-ref HEAD' )
. toString ( )
. trim ( ) ,
2025-11-12 15:05:35 +07:00
) ,
__DEV__ : String ( dev ) ,
2026-07-28 12:42:29 +07:00
// __BUILD_DEFAULT_PLUGIN_REPOSITORY_URL__: ...,
2025-11-12 15:05:35 +07:00
// See types/build.d.ts for what these flags do
__BUILD_FLAG_DEBUG_MODULE_LOOKUPS__ : String ( dev ) ,
__BUILD_FLAG_DEBUG_MODULE_WAITS__ : String ( dev ) ,
__BUILD_FLAG_DEBUG_LAZY_VALUES__ : 'false' ,
__BUILD_FLAG_LOG_PROMISE_REJECTIONS__ : String ( dev ) ,
2025-07-30 20:15:58 +07:00
} ,
2025-04-30 20:20:16 +07:00
} ,
2025-08-25 19:19:22 +07:00
tsconfig : 'tsconfig.json' ,
2026-07-26 15:36:54 +07:00
// propertyReadSideEffects: false works around a Rolldown bug where
// property reads on tree-shaken import bindings are kept as "side
// effects" while their imports are removed, producing free-variable
// references (e.g. `FibonacciHeap.MinFibonacciHeap` from mnemonist's
// barrel) that throw ReferenceError at runtime.
2025-03-21 19:59:16 +07:00
treeshake : true ,
moduleTypes : {
'.webp' : 'dataurl' ,
} ,
plugins : [
2025-09-10 00:18:10 +07:00
asRequire ( ) ,
2025-09-10 00:09:26 +07:00
shimAliases ( ShimsDir ) ,
2025-05-22 00:06:36 +07:00
importGlobPlugin ( ) ,
2025-09-10 00:09:26 +07:00
hermesSwcPlugin ( ) ,
2025-09-10 00:54:55 +07:00
importDefer ( ) ,
2025-03-21 19:59:16 +07:00
hermesCPlugin ( {
flags : [
dev ? '-Og' : '-O' ,
2025-07-08 22:14:22 +07:00
dev ? '-g3' : '-g1' ,
'-reuse-prop-cache' ,
'-optimized-eval' ,
'-strict' ,
2025-03-21 19:59:16 +07:00
'-finline' ,
] ,
2025-06-17 19:12:04 +07:00
before ( ver ) {
if ( log ) {
2025-06-03 16:24:58 +07:00
console . debug (
chalk . cyanBright (
'\u{1F5CE} JS compilation finished...' ,
) ,
)
2025-06-17 19:12:04 +07:00
2025-06-03 16:24:58 +07:00
console . debug (
2025-06-17 19:12:04 +07:00
chalk . gray (
` \ u{1F5CE} Compiling bytecode with ${ ver } ... ` ,
) ,
2025-06-03 16:24:58 +07:00
)
2025-06-17 19:12:04 +07:00
}
2025-05-07 02:53:31 +07:00
} ,
after() {
2025-06-03 16:24:58 +07:00
if ( log )
console . debug (
chalk . cyanBright (
'\u{1F5CE} Bytecode compilation finished' ,
) ,
)
2025-03-21 19:59:16 +07:00
} ,
} ) ,
] ,
} )
await bundle . write ( {
2026-02-26 16:22:33 +07:00
minify : dev
? 'dce-only'
: {
codegen : {
removeWhitespace : false ,
} ,
mangle : {
keepNames : true ,
} ,
compress : {
joinVars : true ,
keepNames : {
class : true ,
function : true ,
} ,
unused : true ,
} ,
} ,
2025-07-30 20:15:58 +07:00
esModule : false ,
2025-07-11 00:17:18 +07:00
minifyInternalExports : true ,
2025-07-30 20:15:58 +07:00
hoistTransitiveImports : false ,
2025-03-21 19:59:16 +07:00
file : 'dist/revenge.js' ,
format : 'iife' ,
2025-11-12 15:05:35 +07:00
keepNames : true ,
2026-02-26 16:22:33 +07:00
postFooter : ` //# sourceURL=Revenge ` ,
topLevelVar : true ,
2025-03-21 19:59:16 +07:00
} )
if ( log )
console . info (
chalk . greenBright (
2025-05-07 02:53:31 +07:00
` \ u{2714} Compiled successfully! ${ chalk . gray ( ` (took ${ ( performance . now ( ) - start ) . toFixed ( 2 ) } ms) ` ) } ` ,
2025-03-21 19:59:16 +07:00
) ,
)
}
2025-05-07 01:21:27 +07:00
async function generateAssets() {
2025-06-03 16:24:58 +07:00
if ( ! ( await exists ( GeneratedAssetsDir ) ) )
await mkdir ( GeneratedAssetsDir , { recursive : true } )
2025-05-07 01:21:27 +07:00
2025-05-07 02:53:07 +07:00
const promises : Promise < void > [ ] = [ ]
2025-05-07 01:21:27 +07:00
for ( const file of await readdir ( AssetsDir ) ) {
const { name , ext } = parse ( file )
const path = ` ${ AssetsDir } / ${ file } `
const path2 = ` ${ GeneratedAssetsDir } / ${ name } .js `
if ( await exists ( path2 ) ) continue
2025-06-07 09:59:30 +07:00
// We attempt to sanitize the path, but not the name as it should fail if name contains invalid characters
const uriPath = JSON . stringify ( path )
const type = JSON . stringify ( ext . slice ( 1 ) )
2025-05-07 02:53:07 +07:00
promises . push (
writeFile (
path2 ,
2025-07-13 19:52:32 +07:00
` import{registerAsset}from'@revenge-mod/assets';import uri from ${ uriPath } ;const ${ name } =registerAsset({name:' ${ name } ',type: ${ type } ,uri});export { ${ name } as default } ` ,
2025-05-07 02:53:07 +07:00
) ,
2025-05-07 01:21:27 +07:00
)
}
2025-05-07 02:53:07 +07:00
await Promise . all ( promises )
2025-05-07 01:21:27 +07:00
}