feat: improve types generation
This commit is contained in:
parent
05c0b093ea
commit
ab5b131b8b
12 changed files with 271 additions and 182 deletions
4
bun.lock
4
bun.lock
|
|
@ -13,8 +13,8 @@
|
|||
"@biomejs/biome": "^2.1.3",
|
||||
"@parcel/watcher": "^2.5.1",
|
||||
"@react-native-clipboard/clipboard": "^1.16.3",
|
||||
"@revenge-mod/types": "workspace:*",
|
||||
"@types/bun": "^1.2.19",
|
||||
"@uwu/treetype": "^1.1.1",
|
||||
"rolldown-plugin-dts": "^0.15.4",
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
@ -313,8 +313,6 @@
|
|||
|
||||
"@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
|
||||
|
||||
"@uwu/treetype": ["@uwu/treetype@1.1.1", "", { "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "treetype": "dist/cli.js" } }, "sha512-sts8RcR8DzG6B2gnt3tbeSrn2N/ShI+3wVOWZHWtFT1fgrw/aDBbV6ePvXFobG0AKUrt/AQ5zV5E3JeX0P37eQ=="],
|
||||
|
||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||
|
||||
"anser": ["anser@1.4.10", "", {}, "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww=="],
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "@revenge-mod/types",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types.d.ts"
|
||||
},
|
||||
"./tsconfig": {
|
||||
"default": "./tsconfig.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Features
|
||||
"lib": ["ESNext"],
|
||||
"module": "ESNext",
|
||||
"target": "ES2015",
|
||||
// Bundler
|
||||
"moduleResolution": "bundler",
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
// Type checking
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"types": ["@revenge-mod/types", "react-native/types/index"]
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
"@biomejs/biome": "^2.1.3",
|
||||
"@parcel/watcher": "^2.5.1",
|
||||
"@react-native-clipboard/clipboard": "^1.16.3",
|
||||
"@revenge-mod/types": "workspace:*",
|
||||
"@types/bun": "^1.2.19",
|
||||
"@uwu/treetype": "^1.1.1",
|
||||
"rolldown-plugin-dts": "^0.15.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
@ -57,4 +57,4 @@
|
|||
"patchedDependencies": {
|
||||
"react-native-click-outside@0.1.1": "patches/react-native-click-outside@0.1.1.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
{
|
||||
"extends": "@revenge-mod/types/tsconfig",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"types": ["node/timers"]
|
||||
"moduleResolution": "Bundler",
|
||||
"paths": {
|
||||
"pkg:*": ["../lib/*/package.json"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
306
scripts/types.ts
306
scripts/types.ts
|
|
@ -1,89 +1,247 @@
|
|||
import { main } from 'bun'
|
||||
import chalk from 'chalk'
|
||||
import { existsSync, readdirSync } from 'fs'
|
||||
import { rm } from 'fs/promises'
|
||||
import { exists, mkdir, rm } from 'fs/promises'
|
||||
import { rolldown } from 'rolldown'
|
||||
import { dts } from 'rolldown-plugin-dts'
|
||||
import pkg from '../package.json'
|
||||
import type { Dirent } from 'fs'
|
||||
|
||||
const OutputDir = 'dist/types'
|
||||
const PATHS = {
|
||||
lib: 'lib',
|
||||
output: 'dist/types',
|
||||
outputTemp: 'dist/types/.build',
|
||||
tsconfig: 'tsconfig.json',
|
||||
} as const
|
||||
|
||||
await rm(OutputDir, { recursive: true, force: true })
|
||||
.then(() =>
|
||||
console.debug(chalk.gray('\u{1F5BB} Deleted generated types...')),
|
||||
)
|
||||
.catch()
|
||||
|
||||
// If this file is being run directly, build the project
|
||||
if (main === import.meta.filename) buildTypes()
|
||||
|
||||
function hasTypesExports(node: Dirent) {
|
||||
return (
|
||||
node.isDirectory() &&
|
||||
(existsSync(`./lib/${node.name}/src/types.ts`) ||
|
||||
existsSync(`./lib/${node.name}/src/types/index.ts`))
|
||||
)
|
||||
async function cleanup(dir: string, description: string): Promise<void> {
|
||||
try {
|
||||
if (await exists(dir)) {
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
console.debug(chalk.gray(`🗑️ Deleted ${description}...`))
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
chalk.yellow(`⚠️ Failed to delete ${description}: ${error}`),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default async function buildTypes(log = true) {
|
||||
let start = performance.now()
|
||||
await cleanup(PATHS.output, 'generated types')
|
||||
await mkdir(PATHS.outputTemp, { recursive: true })
|
||||
|
||||
if (log) console.info(chalk.gray('\u{1F5CE} Generating types...'))
|
||||
export default async function buildTypes(log = true): Promise<void> {
|
||||
const start = performance.now()
|
||||
|
||||
const bundle = await rolldown({
|
||||
input: {
|
||||
...Object.fromEntries(
|
||||
readdirSync('./lib', { withFileTypes: true })
|
||||
.filter(hasTypesExports)
|
||||
.map(node => [
|
||||
`lib/${node.name}/types`,
|
||||
`./lib/${node.name}/src/types`,
|
||||
]),
|
||||
),
|
||||
'lib/modules/finders/filters':
|
||||
'./lib/modules/src/finders/filters.ts',
|
||||
types: './types/lib.ts',
|
||||
},
|
||||
external: Object.keys({
|
||||
...pkg.dependencies,
|
||||
...pkg.devDependencies,
|
||||
...pkg.peerDependencies,
|
||||
}),
|
||||
plugins: [
|
||||
dts({
|
||||
tsconfig: 'tsconfig.json',
|
||||
parallel: true,
|
||||
emitDtsOnly: true,
|
||||
eager: true,
|
||||
resolve: true,
|
||||
if (log) console.info(chalk.gray('🏗️ Generating types...'))
|
||||
|
||||
try {
|
||||
const bundle = await rolldown({
|
||||
input: {
|
||||
...Object.fromEntries(
|
||||
Libraries.flatMap(([libName, exports]) =>
|
||||
exports.map(([exportName, path]) => [
|
||||
`lib/${libName}${exportName && `/${exportName}`}`,
|
||||
path,
|
||||
]),
|
||||
),
|
||||
),
|
||||
globals: './types/globals.consumers.ts',
|
||||
},
|
||||
external: Object.keys({
|
||||
...pkg.dependencies,
|
||||
...pkg.devDependencies,
|
||||
...pkg.peerDependencies,
|
||||
}),
|
||||
],
|
||||
})
|
||||
plugins: [
|
||||
dts({
|
||||
tsconfig: PATHS.tsconfig,
|
||||
parallel: true,
|
||||
emitDtsOnly: true,
|
||||
eager: true,
|
||||
resolve: true,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
await bundle.write({
|
||||
dir: 'dist/types',
|
||||
})
|
||||
await bundle.write({
|
||||
dir: PATHS.output,
|
||||
})
|
||||
|
||||
if (log)
|
||||
console.info(
|
||||
chalk.greenBright(
|
||||
`\u{2714} Generated library types! ${chalk.gray(`(took ${(performance.now() - start).toFixed(2)}ms)`)}`,
|
||||
),
|
||||
)
|
||||
|
||||
start = performance.now()
|
||||
|
||||
Bun.spawnSync({
|
||||
cmd: ['treetype', './types/lib.tt', './dist/types/modules.d.ts'],
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
})
|
||||
|
||||
if (log)
|
||||
console.info(
|
||||
chalk.greenBright(
|
||||
`\u{2714} Generated import types! ${chalk.gray(`(took ${(performance.now() - start).toFixed(2)}ms)`)}`,
|
||||
),
|
||||
)
|
||||
if (log) {
|
||||
const duration = (performance.now() - start).toFixed(2)
|
||||
console.info(
|
||||
chalk.greenBright(
|
||||
`✅ Generated library types! ${chalk.gray(`(took ${duration}ms)`)}`,
|
||||
),
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(chalk.red('❌ Failed to generate types:'), error)
|
||||
throw error
|
||||
} finally {
|
||||
await cleanup(PATHS.outputTemp, 'temporary build files')
|
||||
}
|
||||
}
|
||||
|
||||
import assets from 'pkg:assets'
|
||||
import components from 'pkg:components'
|
||||
import discord from 'pkg:discord'
|
||||
import externals from 'pkg:externals'
|
||||
import modules from 'pkg:modules'
|
||||
import patcher from 'pkg:patcher'
|
||||
import plugins from 'pkg:plugins'
|
||||
import react from 'pkg:react'
|
||||
import storage from 'pkg:storage'
|
||||
import utils from 'pkg:utils'
|
||||
import { writeFileSync } from 'fs'
|
||||
|
||||
type TrimLeadingDotAndMaybeSlash<T extends string> = T extends `.${infer R}`
|
||||
? R extends `/${infer S}`
|
||||
? S
|
||||
: R
|
||||
: T
|
||||
|
||||
type NameBinding<Exp extends string> = [original: Exp, to: string]
|
||||
type Exports<Exp extends string> = Exp | NameBinding<Exp>
|
||||
|
||||
function library<
|
||||
E extends Record<`.${string}`, { default?: string; types?: string }>,
|
||||
>(
|
||||
pkg: { name: string; exports: E },
|
||||
exports: Exports<TrimLeadingDotAndMaybeSlash<Extract<keyof E, string>>>[],
|
||||
): [name: string, exports: [exportName: string, path: string][]] {
|
||||
const mapped: [exportName: string, path: string][] = []
|
||||
const bindings: Record<string, [exportName: string, path: string][]> = {}
|
||||
const pkgName = pkg.name.split('/').at(-1)!
|
||||
|
||||
for (const nameOrBinding of exports) {
|
||||
const isBinding = Array.isArray(nameOrBinding)
|
||||
const name = isBinding ? nameOrBinding[0] : nameOrBinding
|
||||
|
||||
const exportEntry = pkg.exports[`.${name ? `/${name}` : ''}`]
|
||||
if (!exportEntry) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`⚠️ Export "${name}" not found in "${pkg.name}" package.json`,
|
||||
),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
const { default: defaultPath, types } = exportEntry
|
||||
const actualName = isBinding ? nameOrBinding[1] : name
|
||||
const path = defaultPath ?? types
|
||||
|
||||
if (!path) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`⚠️ No path found for export "${name}" in "${pkg.name}"`,
|
||||
),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (isBinding) (bindings[actualName] ??= []).push([actualName, name])
|
||||
else {
|
||||
console.debug(
|
||||
chalk.gray(
|
||||
`📄 ${pkgName}${name && `/${name}`} -> ${pkg.name}${name && `/${name}`}`,
|
||||
),
|
||||
)
|
||||
mapped.push([actualName, `${PATHS.lib}/${pkgName}/${path}`])
|
||||
}
|
||||
}
|
||||
|
||||
for (const [to, froms] of Object.entries(bindings)) {
|
||||
if (froms.length > 1) {
|
||||
const tempFileName = Array.from(
|
||||
crypto.getRandomValues(new Uint8Array(4)),
|
||||
)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
|
||||
const tempPath = `${PATHS.outputTemp}/${tempFileName}.ts`
|
||||
const content = froms
|
||||
.map(([, from]) => {
|
||||
const path = `${pkg.name}/${from}`
|
||||
console.debug(
|
||||
chalk.gray(`↔️ ${pkgName}${to && `/${to}`} -> ${path}`),
|
||||
)
|
||||
|
||||
return `export * from '${path}'`
|
||||
})
|
||||
.join('\n')
|
||||
|
||||
try {
|
||||
writeFileSync(tempPath, content)
|
||||
mapped.push([to, tempPath])
|
||||
} catch (error) {
|
||||
console.error(
|
||||
chalk.red(`❌ Failed to write temp file for ${to}:`),
|
||||
error,
|
||||
)
|
||||
}
|
||||
} else if (froms[0]) {
|
||||
mapped.push(froms[0])
|
||||
}
|
||||
}
|
||||
|
||||
return [pkgName, mapped]
|
||||
}
|
||||
|
||||
const Libraries = [
|
||||
library(assets, ['', 'types']),
|
||||
library(components, [
|
||||
'FormSwitch',
|
||||
'Page',
|
||||
'SearchInput',
|
||||
'TableRowAssetIcon',
|
||||
'types',
|
||||
]),
|
||||
library(discord, [
|
||||
'actions',
|
||||
'common',
|
||||
'common/flux',
|
||||
'common/utils',
|
||||
'design',
|
||||
'flux',
|
||||
'modules/main_tabs_v2',
|
||||
'modules/settings',
|
||||
'modules/settings/renderer',
|
||||
'native',
|
||||
'types',
|
||||
]),
|
||||
library(externals, [
|
||||
'browserify',
|
||||
'react-native-clipboard',
|
||||
'react-navigation',
|
||||
'shopify',
|
||||
'types',
|
||||
]),
|
||||
library(modules, [
|
||||
'finders/filters',
|
||||
['finders/get', 'finders'],
|
||||
['finders/lookup', 'finders'],
|
||||
['finders/wait', 'finders'],
|
||||
['metro/subscriptions', 'metro'],
|
||||
['metro/utils', 'metro'],
|
||||
'native',
|
||||
'types',
|
||||
]),
|
||||
library(patcher, ['', 'types']),
|
||||
library(plugins, ['constants', 'types']),
|
||||
library(react, ['', 'jsx-runtime', 'native', 'types']),
|
||||
library(storage, ['', 'types']),
|
||||
library(utils, [
|
||||
'callback',
|
||||
'discord',
|
||||
'error',
|
||||
'object',
|
||||
'promise',
|
||||
'proxy',
|
||||
'react',
|
||||
'tree',
|
||||
'types',
|
||||
]),
|
||||
] as const
|
||||
|
||||
if (main === import.meta.filename) await buildTypes()
|
||||
|
|
|
|||
|
|
@ -1,19 +1,33 @@
|
|||
{
|
||||
"extends": "@revenge-mod/types/tsconfig",
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"@revenge-mod/types",
|
||||
"./types",
|
||||
"./types.modules",
|
||||
"node/util",
|
||||
"node/events"
|
||||
],
|
||||
"lib": ["ESNext"],
|
||||
"module": "ESNext",
|
||||
"target": "ES2015",
|
||||
"moduleResolution": "bundler",
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"types": [],
|
||||
"typeRoots": [],
|
||||
"paths": {
|
||||
"~/*": ["./src/*"],
|
||||
"~*": ["./src/*"],
|
||||
"~assets/*": ["./dist/assets/generated/*"]
|
||||
}
|
||||
},
|
||||
"include": ["lib", "src", "types"],
|
||||
"include": [
|
||||
"lib",
|
||||
"src",
|
||||
"types",
|
||||
"./types/*.d.ts",
|
||||
"node_modules/@types/node/events.d.ts",
|
||||
"node_modules/@types/node/util.d.ts",
|
||||
"node_modules/react-native/types/index.d.ts"
|
||||
],
|
||||
"exclude": ["**/*.test.ts"]
|
||||
}
|
||||
|
|
|
|||
0
types.d.ts → types/build.d.ts
vendored
0
types.d.ts → types/build.d.ts
vendored
|
|
@ -11,4 +11,4 @@ declare global {
|
|||
): void
|
||||
}
|
||||
|
||||
export type * from '@revenge-mod/types'
|
||||
export * from './globals'
|
||||
12
lib/types/types.d.ts → types/globals.d.ts
vendored
12
lib/types/types.d.ts → types/globals.d.ts
vendored
|
|
@ -1,5 +1,3 @@
|
|||
// All of these typings are exported, be careful what you export here!
|
||||
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type {
|
||||
ImageProps,
|
||||
|
|
@ -25,6 +23,8 @@ declare module 'react' {
|
|||
/// HERMES
|
||||
|
||||
declare global {
|
||||
const HermesInternal: HermesInternalObject
|
||||
|
||||
function setTimeout(
|
||||
cb: (...args: unknown[]) => unknown,
|
||||
timeout?: number,
|
||||
|
|
@ -56,6 +56,10 @@ declare global {
|
|||
/// 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
|
||||
/**
|
||||
* The resolved value of the promise, if it has been resolved.
|
||||
*/
|
||||
_j: any
|
||||
}
|
||||
|
||||
type HermesPromiseRejectionHandler = (
|
||||
|
|
@ -103,7 +107,7 @@ declare global {
|
|||
|
||||
declare global {
|
||||
var nativeModuleProxy: Record<string, unknown>
|
||||
function __turboModuleProxy(name: string): unknown
|
||||
var __turboModuleProxy: ((name: string) => unknown) | undefined
|
||||
function nativeLoggingHook(str: string, level: number): void
|
||||
function alert(message: unknown): void
|
||||
|
||||
|
|
@ -111,6 +115,4 @@ declare global {
|
|||
var performance: {
|
||||
now(): number
|
||||
}
|
||||
|
||||
var globalEvalWithSourceUrl: (code: string, sourceURL: string) => any
|
||||
}
|
||||
51
types/lib.tt
51
types/lib.tt
|
|
@ -1,51 +0,0 @@
|
|||
\source ../lib/plugins/src/types.ts
|
||||
\import ./lib/plugins/types.d.ts
|
||||
|
||||
@revenge-mod from UnscopedPluginApi {
|
||||
assets
|
||||
components
|
||||
discord {
|
||||
common {
|
||||
flux
|
||||
utils
|
||||
}
|
||||
modules {
|
||||
settings {
|
||||
renderer
|
||||
}
|
||||
mainTabsV2
|
||||
}
|
||||
actions
|
||||
design
|
||||
flux
|
||||
native
|
||||
}
|
||||
externals {
|
||||
Browserify
|
||||
ReactNativeClipboard
|
||||
ReactNavigation
|
||||
Shopify
|
||||
}
|
||||
modules {
|
||||
finders
|
||||
metro
|
||||
native
|
||||
}
|
||||
patcher
|
||||
plugins {
|
||||
constants
|
||||
}
|
||||
react {
|
||||
native
|
||||
}
|
||||
utils {
|
||||
callback
|
||||
discord
|
||||
error
|
||||
object
|
||||
promise
|
||||
proxy
|
||||
react
|
||||
tree
|
||||
}
|
||||
}
|
||||
0
types.modules.d.ts → types/modules.d.ts
vendored
0
types.modules.d.ts → types/modules.d.ts
vendored
Loading…
Add table
Reference in a new issue