chore: initial commit
This commit is contained in:
commit
8d84ebe491
23 changed files with 1968 additions and 0 deletions
178
.gitignore
vendored
Normal file
178
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Private
|
||||||
|
.private
|
||||||
15
README.md
Normal file
15
README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# @revenge-mod/revenge
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run src/index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.2.2. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||||
69
biome.json
Normal file
69
biome.json
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.8.2/schema.json",
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"a11y": {
|
||||||
|
"useKeyWithClickEvents": {
|
||||||
|
"level": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"noCommaOperator": {
|
||||||
|
"level": "off"
|
||||||
|
},
|
||||||
|
"noNonNullAssertion": {
|
||||||
|
"level": "off"
|
||||||
|
},
|
||||||
|
"useNodejsImportProtocol": {
|
||||||
|
"level": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noAssignInExpressions": {
|
||||||
|
"level": "off"
|
||||||
|
},
|
||||||
|
"noExplicitAny": {
|
||||||
|
"level": "off"
|
||||||
|
},
|
||||||
|
"noConfusingVoidType": {
|
||||||
|
"level": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 2
|
||||||
|
},
|
||||||
|
"parser": {
|
||||||
|
"allowComments": true,
|
||||||
|
"allowTrailingCommas": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"lineEnding": "crlf",
|
||||||
|
"arrowParentheses": "asNeeded",
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 4,
|
||||||
|
"lineWidth": 120,
|
||||||
|
"quoteProperties": "asNeeded",
|
||||||
|
"quoteStyle": "single",
|
||||||
|
"semicolons": "asNeeded",
|
||||||
|
"trailingCommas": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": true,
|
||||||
|
"include": ["*.js", "*.json", "*.ts", "*.tsx", "*.jsx", "*.mjs"],
|
||||||
|
"ignore": ["dist/**/*", "node_modules/**/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/init/package.json
Normal file
10
lib/init/package.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "@revenge-mod/init",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./src/index.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
lib/init/src/index.ts
Normal file
75
lib/init/src/index.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import './patches/prevent-immutablize'
|
||||||
|
import './patches/proxy'
|
||||||
|
|
||||||
|
import { getErrorStack } from '@revenge-mod/utils/errors'
|
||||||
|
|
||||||
|
import { logger } from './shared'
|
||||||
|
|
||||||
|
import type { Metro } from '@revenge-mod/modules'
|
||||||
|
|
||||||
|
let metroRequire: Metro.RequireFn | undefined = globalThis.__r
|
||||||
|
let metroDefine: Metro.DefineFn | undefined = globalThis.__d
|
||||||
|
|
||||||
|
Object.defineProperties(globalThis, {
|
||||||
|
__r: {
|
||||||
|
configurable: true,
|
||||||
|
get: () => metroRequire,
|
||||||
|
set: originalMetroRequire => {
|
||||||
|
metroRequire = id =>
|
||||||
|
id
|
||||||
|
? // Early modules (React, React Native, polyfills, etc.) are required even before index (module 0) is required
|
||||||
|
originalMetroRequire(id)
|
||||||
|
: // Once module 0 is required, we initialize our patches
|
||||||
|
// We also revert __r to its original value, for performance reasons
|
||||||
|
// biome-ignore lint/performance/noDelete: This is faster than using Object.defineProperty
|
||||||
|
(delete globalThis.__r, (globalThis.__r = originalMetroRequire), onceIndexModuleRequired())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
__d: {
|
||||||
|
configurable: true,
|
||||||
|
// __d() is used to define a module
|
||||||
|
// Before the first module is being defined, we clear the modules list (so we can get the module list instance)
|
||||||
|
// We also revert __d to its original value, for performance reasons
|
||||||
|
get: () => {
|
||||||
|
// biome-ignore lint/performance/noDelete: This is faster than using Object.defineProperty
|
||||||
|
delete globalThis.__d
|
||||||
|
globalThis.__d = metroDefine
|
||||||
|
|
||||||
|
modules = __c!()
|
||||||
|
return metroDefine
|
||||||
|
},
|
||||||
|
set: v => {
|
||||||
|
metroDefine = v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export let modules: Metro.ModuleList
|
||||||
|
|
||||||
|
function onceIndexModuleRequired() {
|
||||||
|
if (!__BUILD_FLAG_INIT_DISABLE_PATCH_LOG_PROMISE_REJECTIONS__) require('./patches/log-promise-rejections')
|
||||||
|
|
||||||
|
initialize().catch(e => {
|
||||||
|
const { ClientInfoModule, DeviceModule } = require('@revenge-mod/modules/native')
|
||||||
|
|
||||||
|
alert(
|
||||||
|
[
|
||||||
|
'Failed to load Revenge!',
|
||||||
|
'',
|
||||||
|
`Discord: ${ClientInfoModule.Version} (${ClientInfoModule.Build})`,
|
||||||
|
`Device: ${DeviceModule.deviceManufacturer} ${DeviceModule.deviceModel}`,
|
||||||
|
'',
|
||||||
|
getErrorStack(e),
|
||||||
|
].join('\n'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
! This function is blocking app startup !
|
||||||
|
We need to make sure it's as fast as possible
|
||||||
|
*/
|
||||||
|
async function initialize() {
|
||||||
|
logger.log('Revenge loaded!')
|
||||||
|
__r(0)
|
||||||
|
}
|
||||||
23
lib/init/src/patches/log-promise-rejections.ts
Normal file
23
lib/init/src/patches/log-promise-rejections.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Hermes doesn't natively support Promises, it instead has a polyfill for it.
|
||||||
|
* It doesn't throw and never logs rejections in production, so we are patching it to catch errors when using async functions.
|
||||||
|
*
|
||||||
|
* See for reference: https://github.com/facebook/hermes/blob/3332fa020cae0bab751f648db7c94e1d687eeec7/lib/InternalBytecode/01-Promise.js#L446
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getErrorStack } from '@revenge-mod/utils/errors'
|
||||||
|
|
||||||
|
import { logger } from '../shared'
|
||||||
|
|
||||||
|
const ErrorTypeWhitelist = [ReferenceError, TypeError, RangeError]
|
||||||
|
|
||||||
|
Promise._m = (promise, err) => {
|
||||||
|
if (err)
|
||||||
|
setTimeout(
|
||||||
|
() => {
|
||||||
|
if (promise._h === 0) logger.error(`Unhandled promise rejection: ${getErrorStack(err)}`)
|
||||||
|
},
|
||||||
|
ErrorTypeWhitelist.some(it => err instanceof it) ? 0 : 2000,
|
||||||
|
// The time is completely arbitary. I've picked what Hermes chose.
|
||||||
|
)
|
||||||
|
}
|
||||||
8
lib/init/src/patches/prevent-immutablize.ts
Normal file
8
lib/init/src/patches/prevent-immutablize.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* This patch prevents objects from being made immutable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Retain reference to the functions so the actual functions can be used later
|
||||||
|
import '@revenge-mod/utils/functions'
|
||||||
|
|
||||||
|
Object.freeze = Object.seal = Object.preventExtensions = o => o
|
||||||
18
lib/init/src/patches/proxy.ts
Normal file
18
lib/init/src/patches/proxy.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* This patch allows us to store instances of Proxy, so we can check whether a value is created using Proxy or not.
|
||||||
|
* This is especially useful for blacklisting discord-intl modules exports, and exports that cannot be patched.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const _instances = new WeakSet<object>()
|
||||||
|
// export const _targets = new WeakMap<object, object>()
|
||||||
|
|
||||||
|
const OriginalProxy = globalThis.Proxy
|
||||||
|
globalThis.Proxy = new Proxy(OriginalProxy, {
|
||||||
|
construct(_target, args) {
|
||||||
|
// @ts-expect-error
|
||||||
|
const prox = new OriginalProxy(...args)
|
||||||
|
_instances.add(prox)
|
||||||
|
// _targets.set(prox, args[0])
|
||||||
|
return prox
|
||||||
|
},
|
||||||
|
})
|
||||||
3
lib/init/src/shared.ts
Normal file
3
lib/init/src/shared.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { createLogger } from '@revenge-mod/utils/logger'
|
||||||
|
|
||||||
|
export const logger = createLogger('revenge.init')
|
||||||
19
lib/utils/package.json
Normal file
19
lib/utils/package.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "@revenge-mod/utils",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
"./errors": {
|
||||||
|
"default": "./src/errors.ts"
|
||||||
|
},
|
||||||
|
"./functions": {
|
||||||
|
"default": "./src/functions.ts"
|
||||||
|
},
|
||||||
|
"./logger": {
|
||||||
|
"default": "./src/logger.ts"
|
||||||
|
},
|
||||||
|
"./types": {
|
||||||
|
"types": "./src/types.d.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
lib/utils/src/errors.ts
Normal file
3
lib/utils/src/errors.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function getErrorStack(e: unknown) {
|
||||||
|
return e instanceof Error ? e.stack : String(e)
|
||||||
|
}
|
||||||
5
lib/utils/src/functions.ts
Normal file
5
lib/utils/src/functions.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// This file is imported before the functions are modified
|
||||||
|
|
||||||
|
export const objectFreeze = Object.freeze
|
||||||
|
export const objectSeal = Object.seal
|
||||||
|
export const objectPreventExtensions = Object.preventExtensions
|
||||||
35
lib/utils/src/logger.ts
Normal file
35
lib/utils/src/logger.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
export function createLogger(tag: string) {
|
||||||
|
return {
|
||||||
|
log: (message: string) => console.log(`[${tag}] ${message}`),
|
||||||
|
warn: (message: string) => console.warn(`[${tag}] ${message}`),
|
||||||
|
error: (message: string) => console.error(`[${tag}] ${message}`),
|
||||||
|
wtf: (message: string) => console.error(`\u001b[31m[${tag}] ${message}\u001b[0m`),
|
||||||
|
} satisfies BasicLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BasicLogger {
|
||||||
|
/**
|
||||||
|
* Log a message to the console.
|
||||||
|
*/
|
||||||
|
log(...args: unknown[]): void
|
||||||
|
/**
|
||||||
|
* Log a warning message to the console.
|
||||||
|
*
|
||||||
|
* On Android, this will print to logcat with the `WARN` level.
|
||||||
|
*/
|
||||||
|
warn(...args: unknown[]): void
|
||||||
|
/**
|
||||||
|
* Log an error message to the console.
|
||||||
|
*
|
||||||
|
* On Android, this will print to logcat with the `ERROR` level.
|
||||||
|
*/
|
||||||
|
error(...args: unknown[]): void
|
||||||
|
/**
|
||||||
|
* [*What a Terrible Failure!*](https://developer.android.com/reference/android/util/Log#wtf(java.lang.String,%20java.lang.String))
|
||||||
|
*
|
||||||
|
* Log an error message to the console in red (ANSI)
|
||||||
|
*
|
||||||
|
* On Android, this will print to logcat with the `ERROR` level.
|
||||||
|
*/
|
||||||
|
wtf(...args: unknown[]): void
|
||||||
|
}
|
||||||
13
lib/utils/src/proxy.ts
Normal file
13
lib/utils/src/proxy.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { _instances /* _targets */ } from '../../init/src/patches/proxy'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the object is a proxy
|
||||||
|
* @param obj The object to check
|
||||||
|
*/
|
||||||
|
export function isProxy(obj: object) {
|
||||||
|
return _instances.has(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// export function getProxyTarget(obj: object) {
|
||||||
|
// return _targets.get(obj)
|
||||||
|
// }
|
||||||
3
lib/utils/src/types.d.ts
vendored
Normal file
3
lib/utils/src/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export type Nullish = null | undefined
|
||||||
|
export type If<T, Then, Else> = T extends true ? Then : Else
|
||||||
|
export type AnyObject = Record<any, any>
|
||||||
26
package.json
Normal file
26
package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "@revenge-mod/revenge",
|
||||||
|
"private": true,
|
||||||
|
"module": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "bun --bun run scripts/build.ts",
|
||||||
|
"dev": "bun --bun run scripts/dev.ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.9.4",
|
||||||
|
"@parcel/watcher": "^2.5.1",
|
||||||
|
"@swc/core": "^1.11.9",
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"@unbound-mod/hermesc": "^1.0.3",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"chalk": "^5.4.1",
|
||||||
|
"react-native": "^0.77.1",
|
||||||
|
"rolldown": "^1.0.0-beta.4-commit.beda261"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"workspaces": ["lib/*", "plugins/*"],
|
||||||
|
"trustedDependencies": ["@biomejs/biome", "@parcel/watcher", "@swc/core"]
|
||||||
|
}
|
||||||
145
scripts/build.ts
Normal file
145
scripts/build.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
import { transform } from '@swc/core'
|
||||||
|
import { main } from 'bun'
|
||||||
|
import chalk from 'chalk'
|
||||||
|
import { type OutputChunk, type RolldownPlugin, rolldown } from 'rolldown'
|
||||||
|
|
||||||
|
// If this file is being run directly, build the project
|
||||||
|
if (main === import.meta.filename) build()
|
||||||
|
|
||||||
|
export default async function build(dev = false, log = true) {
|
||||||
|
const start = performance.now()
|
||||||
|
|
||||||
|
const bundle = await rolldown({
|
||||||
|
input: 'src/index.ts',
|
||||||
|
platform: 'neutral',
|
||||||
|
treeshake: true,
|
||||||
|
keepNames: true,
|
||||||
|
moduleTypes: {
|
||||||
|
'.webp': 'dataurl',
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
__BUILD_COMMIT__: JSON.stringify(''),
|
||||||
|
__BUILD_ENV__: JSON.stringify(dev ? 'development' : 'production'),
|
||||||
|
__BUILD_FLAG_INIT_DISABLE_PATCH_LOG_PROMISE_REJECTIONS__: String(!dev),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
swcPlugin(),
|
||||||
|
hermesCPlugin({
|
||||||
|
flags: [
|
||||||
|
dev ? '-Og' : '-O',
|
||||||
|
'-eager',
|
||||||
|
'-finline',
|
||||||
|
'-fno-static-require',
|
||||||
|
'-Wno-direct-eval',
|
||||||
|
'-Wno-undefined-variable',
|
||||||
|
],
|
||||||
|
afterComplete() {
|
||||||
|
if (log) console.debug(chalk.gray('\u{1F5CE} Bytecode compilation finished'))
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await bundle.write({
|
||||||
|
file: 'dist/revenge.js',
|
||||||
|
format: 'iife',
|
||||||
|
footer: '//# sourceURL=Revenge',
|
||||||
|
inlineDynamicImports: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (log)
|
||||||
|
console.info(
|
||||||
|
chalk.greenBright(
|
||||||
|
`\u{1F5CE} Compiled successfully! ${chalk.gray(`(took ${(performance.now() - start).toFixed(2)}ms)`)}`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function swcPlugin() {
|
||||||
|
return {
|
||||||
|
name: 'swc',
|
||||||
|
transform: {
|
||||||
|
filter: {
|
||||||
|
id: /\.[cm]?[jt]sx?$/,
|
||||||
|
},
|
||||||
|
handler(code) {
|
||||||
|
return transform(code, {
|
||||||
|
jsc: {
|
||||||
|
transform: {
|
||||||
|
react: {
|
||||||
|
runtime: 'automatic',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parser: {
|
||||||
|
syntax: 'typescript',
|
||||||
|
tsx: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
// https://github.com/discord/hermes/blob/main/doc/Features.md
|
||||||
|
targets: 'fully supports es6',
|
||||||
|
include: [
|
||||||
|
'transform-block-scoping',
|
||||||
|
'transform-classes',
|
||||||
|
'transform-async-to-generator',
|
||||||
|
'transform-async-generator-functions',
|
||||||
|
'transform-named-capturing-groups-regex',
|
||||||
|
],
|
||||||
|
exclude: [
|
||||||
|
'transform-parameters',
|
||||||
|
'transform-template-literals',
|
||||||
|
'transform-exponentiation-operator',
|
||||||
|
'transform-nullish-coalescing-operator',
|
||||||
|
'transform-object-rest-spread',
|
||||||
|
'transform-optional-chaining',
|
||||||
|
'transform-logical-assignment-operators',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies RolldownPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
function hermesCPlugin({ afterComplete, flags }: { flags?: string[]; afterComplete?: () => void } = {}) {
|
||||||
|
const paths = {
|
||||||
|
win32: 'hermesc.exe',
|
||||||
|
darwin: 'darwin/hermesc',
|
||||||
|
linux: 'linux/hermesc',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(process.platform in paths)) throw new Error(`Unsupported platform: ${process.platform}`)
|
||||||
|
|
||||||
|
const binPath = paths[process.platform as keyof typeof paths]
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'hermesc',
|
||||||
|
generateBundle(_, bundle) {
|
||||||
|
const file = bundle['revenge.js'] as OutputChunk
|
||||||
|
if (!file || !file.code) throw new Error('No code to compile')
|
||||||
|
|
||||||
|
const cmd = Bun.spawnSync(
|
||||||
|
[
|
||||||
|
`./node_modules/@unbound-mod/hermesc/${process.platform}/${binPath}`,
|
||||||
|
'-emit-binary',
|
||||||
|
...(flags ?? []),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
stdin: new Blob([file.code]),
|
||||||
|
stdout: 'pipe',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const buf = cmd.stdout
|
||||||
|
if (!buf.length) throw new Error('No output from hermesc')
|
||||||
|
|
||||||
|
this.emitFile({
|
||||||
|
type: 'asset',
|
||||||
|
fileName: `${file.fileName.split('.')[0]!}.bundle`,
|
||||||
|
source: buf,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (afterComplete) afterComplete()
|
||||||
|
},
|
||||||
|
} satisfies RolldownPlugin
|
||||||
|
}
|
||||||
51
scripts/dev.ts
Normal file
51
scripts/dev.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { join } from 'path'
|
||||||
|
import watcher from '@parcel/watcher'
|
||||||
|
import chalk from 'chalk'
|
||||||
|
import build from './build'
|
||||||
|
|
||||||
|
const cwdify = (path: string) => join(process.cwd(), path)
|
||||||
|
|
||||||
|
const Sources = ['src', 'lib', 'plugins'].map(cwdify)
|
||||||
|
const ExitTriggers = ['scripts', 'package.json', 'bun.lock'].map(cwdify)
|
||||||
|
|
||||||
|
function debounce<F extends (...args: any[]) => any>(func: F, timeout: number) {
|
||||||
|
let timer: NodeJS.Timer
|
||||||
|
return (...args: Parameters<F>) =>
|
||||||
|
new Promise(rs => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
rs(func(...args))
|
||||||
|
}, timeout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildDevWithDebounceAndCatchErrors = debounce(async () => {
|
||||||
|
try {
|
||||||
|
return await build(true)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}, 250)
|
||||||
|
|
||||||
|
watcher.subscribe(process.cwd(), async (err, events) => {
|
||||||
|
if (err) return console.error(err)
|
||||||
|
|
||||||
|
if (events.some(it => ExitTriggers.some(se => it.path.startsWith(se)))) {
|
||||||
|
console.error(chalk.redBright('\u26A0 Scripts or packages has changed, exiting!'))
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.some(it => Sources.some(src => it.path.startsWith(src)))) await buildDevWithDebounceAndCatchErrors()
|
||||||
|
})
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
fetch(req, srv) {
|
||||||
|
console.debug(chalk.gray(`\u{1F79B} Receiving request from ${srv.requestIP(req)!.address}`))
|
||||||
|
return new Response(Bun.file('./dist/revenge.bundle'))
|
||||||
|
},
|
||||||
|
port: 4040,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.info(chalk.cyanBright(`\u24D8 Serving on port ${server.port}`))
|
||||||
|
|
||||||
|
await buildDevWithDebounceAndCatchErrors()
|
||||||
1
src/index.ts
Normal file
1
src/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
import '@revenge-mod/init'
|
||||||
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
},
|
||||||
|
"exclude": ["scripts/**"]
|
||||||
|
}
|
||||||
107
types.d.ts
vendored
Normal file
107
types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
// All of these typings are exported, be careful what you export here!
|
||||||
|
|
||||||
|
import type { ErrorUtils as RNErrorUtils } from 'react-native'
|
||||||
|
|
||||||
|
/// HERMES
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
declare function setTimeout(cb: (...args: unknown[]) => unknown, timeout?: number): number
|
||||||
|
/**
|
||||||
|
* Calls the garbage collector
|
||||||
|
*/
|
||||||
|
declare function gc(): void
|
||||||
|
|
||||||
|
interface HermesInternalObject {
|
||||||
|
getRuntimeProperties(): Record<string, string>
|
||||||
|
// biome-ignore lint/complexity/noBannedTypes: You can pass any function here
|
||||||
|
getFunctionLocation(fn: Function): {
|
||||||
|
fileName: string
|
||||||
|
lineNumber: number
|
||||||
|
columnNumber: number
|
||||||
|
segmentID: number
|
||||||
|
virtualOffset: number
|
||||||
|
isNative: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HERMES PROMISES
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Promise {
|
||||||
|
/// PROMISE POLYFILLS FROM: https://github.com/then/promise
|
||||||
|
/// AND: https://github.com/facebook/hermes/blob/main/lib/InternalBytecode/01-Promise.js
|
||||||
|
_h: 0 | 1 | 2
|
||||||
|
}
|
||||||
|
|
||||||
|
type HermesPromiseRejectionHandler = (promise: Promise<any>, error: any) => void
|
||||||
|
|
||||||
|
interface PromiseConstructor {
|
||||||
|
_m: HermesPromiseRejectionHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// REACT DEVTOOLS
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var __REACT_DEVTOOLS_GLOBAL_HOOK__: unknown | undefined
|
||||||
|
var __reactDevTools:
|
||||||
|
| {
|
||||||
|
version: string
|
||||||
|
exports: {
|
||||||
|
connectToDevTools(opts: {
|
||||||
|
host?: string
|
||||||
|
port?: number
|
||||||
|
websocket?: WebSocket
|
||||||
|
}): void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/// METRO
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var nativeModuleProxy: Record<string, unknown>
|
||||||
|
var __r: Metro.RequireFn
|
||||||
|
var __c: Metro.ClearFn
|
||||||
|
var __d: Metro.DefineFn
|
||||||
|
}
|
||||||
|
|
||||||
|
/// REACT NATIVE
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
declare function nativeLoggingHook(str: string, level: number): void
|
||||||
|
declare function alert(message: unknown): void
|
||||||
|
|
||||||
|
var nativePerformanceNow: typeof performance.now
|
||||||
|
var performance: {
|
||||||
|
now(): number
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalEvalWithSourceUrl: (code: string, sourceURL: string) => any
|
||||||
|
|
||||||
|
const ErrorUtils: RNErrorUtils
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace ReactNativeInternals {
|
||||||
|
namespace AssetsRegistry {
|
||||||
|
// export type AssetDestPathResolver = 'android' | 'generic'
|
||||||
|
|
||||||
|
export type PackagerAsset = {
|
||||||
|
__packager_asset: boolean
|
||||||
|
fileSystemLocation?: string
|
||||||
|
httpServerLocation?: string
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
scales: number[]
|
||||||
|
hash: string
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
// resolver?: AssetDestPathResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerAsset(asset: PackagerAsset): number
|
||||||
|
export function getAssetByID(assetId: number): PackagerAsset
|
||||||
|
}
|
||||||
|
}
|
||||||
16
types.internal.d.ts
vendored
Normal file
16
types.internal.d.ts
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Type definitions for things in this codebase ONLY!
|
||||||
|
|
||||||
|
import type { Buffer as _Buffer } from 'buffer'
|
||||||
|
import type MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// Discord has a Buffer polyfill that gets initialized a little later, be careful with this
|
||||||
|
var Buffer: typeof _Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
const __BUILD_ENV__: 'development' | 'production'
|
||||||
|
const __BUILD_COMMIT__: string
|
||||||
|
|
||||||
|
const __BUILD_FLAG_INIT_DISABLE_PATCH_LOG_PROMISE_REJECTIONS__: boolean
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue