feat: better types + typegen output
- Add dependencies to types package.json - Make temp file names deterministic
This commit is contained in:
parent
2e944b0f25
commit
6bdff25482
6 changed files with 320 additions and 153 deletions
|
|
@ -1,4 +1,3 @@
|
|||
import { EventEmitter } from 'node:events'
|
||||
import type { StackScreenProps } from '@react-navigation/stack'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
import type {
|
||||
|
|
@ -699,10 +698,40 @@ export namespace DiscordModules {
|
|||
export namespace TypedEventEmitter {
|
||||
export type DefaultEventMap = [never]
|
||||
export type EventMap<T> = Record<keyof T, any[]> | DefaultEventMap
|
||||
export type Listener<T, K extends keyof T> = T[K] extends any[]
|
||||
? (...args: T[K]) => void
|
||||
: never
|
||||
}
|
||||
|
||||
export class TypedEventEmitter<
|
||||
export declare class TypedEventEmitter<
|
||||
T extends Record<string, any[]> = Record<string, any[]>,
|
||||
> extends EventEmitter<T> {}
|
||||
> {
|
||||
addListener<K extends keyof T>(
|
||||
event: K,
|
||||
listener: TypedEventEmitter.Listener<T, K>,
|
||||
): this
|
||||
on<K extends keyof T>(
|
||||
event: K,
|
||||
listener: TypedEventEmitter.Listener<T, K>,
|
||||
): this
|
||||
once<K extends keyof T>(
|
||||
event: K,
|
||||
listener: TypedEventEmitter.Listener<T, K>,
|
||||
): this
|
||||
removeListener<K extends keyof T>(
|
||||
event: K,
|
||||
listener: TypedEventEmitter.Listener<T, K>,
|
||||
): this
|
||||
off<K extends keyof T>(
|
||||
event: K,
|
||||
listener: TypedEventEmitter.Listener<T, K>,
|
||||
): this
|
||||
removeAllListeners(event?: keyof T): this
|
||||
emit<K extends keyof T>(event: K, ...args: T[K]): boolean
|
||||
listenerCount<K extends keyof T>(
|
||||
event: K,
|
||||
listener?: TypedEventEmitter.Listener<T, K>,
|
||||
): number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import type { Buffer as _Buffer } from 'buffer'
|
||||
/**
|
||||
* Namespace import, as bundling types changes the named import back to `Buffer`,
|
||||
* which makes the global reference itself.
|
||||
*/
|
||||
import type * as NodeBuffer from 'buffer'
|
||||
|
||||
declare global {
|
||||
var Buffer: typeof _Buffer
|
||||
var Buffer: typeof NodeBuffer.Buffer
|
||||
}
|
||||
|
|
|
|||
416
scripts/types.ts
416
scripts/types.ts
|
|
@ -1,12 +1,45 @@
|
|||
import assets from 'pkg:assets'
|
||||
import components from 'pkg:components'
|
||||
import discord from 'pkg:discord'
|
||||
import externals from 'pkg:externals'
|
||||
import jsonStorage from 'pkg:json-storage'
|
||||
import modules from 'pkg:modules'
|
||||
import patcher from 'pkg:patcher'
|
||||
import plugins from 'pkg:plugins'
|
||||
import react from 'pkg:react'
|
||||
import utils from 'pkg:utils'
|
||||
import { main } from 'bun'
|
||||
import chalk from 'chalk'
|
||||
import { mkdir, readFile, rm, writeFile } from 'fs/promises'
|
||||
import { mkdir, readdir, readFile, rm, writeFile } from 'fs/promises'
|
||||
import { rolldown } from 'rolldown'
|
||||
import { dts } from 'rolldown-plugin-dts'
|
||||
import pkg from '../package.json'
|
||||
import { exists } from './_shared'
|
||||
|
||||
const TYPES_PACKAGE_NAME = '@revenge-mod/types'
|
||||
|
||||
const TYPES_PACKAGE_DEPENDENCIES = ['buffer']
|
||||
|
||||
const TYPES_PACKAGE_PEER_DEPENDENCIES = [
|
||||
'@react-navigation/core',
|
||||
'@react-navigation/native',
|
||||
'@react-navigation/stack',
|
||||
'@shopify/flash-list',
|
||||
'@types/node',
|
||||
'react',
|
||||
'react-native',
|
||||
'react-native-safe-area-context',
|
||||
]
|
||||
|
||||
const TYPES_PACKAGE_OPTIONAL_PEER_DEPENDENCIES = [
|
||||
'@react-navigation/core',
|
||||
'@react-navigation/native',
|
||||
'@react-navigation/stack',
|
||||
'@shopify/flash-list',
|
||||
'@types/node',
|
||||
'react-native-safe-area-context',
|
||||
]
|
||||
|
||||
const PATHS = {
|
||||
lib: 'lib',
|
||||
output: 'dist/types',
|
||||
|
|
@ -14,33 +47,28 @@ const PATHS = {
|
|||
tsconfig: 'tsconfig.json',
|
||||
} as const
|
||||
|
||||
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}`),
|
||||
)
|
||||
}
|
||||
}
|
||||
type TrimLeadingDot<T extends string> = T extends `.${infer R}`
|
||||
? R extends `/${infer S}`
|
||||
? S
|
||||
: R
|
||||
: T
|
||||
|
||||
await cleanup(PATHS.output, 'generated types')
|
||||
await mkdir(PATHS.outputTemp, { recursive: true })
|
||||
type ExportBinding<T extends string> = T | [from: T, to: string]
|
||||
|
||||
export default async function buildTypes(log = true): Promise<void> {
|
||||
const start = performance.now()
|
||||
|
||||
await cleanup(PATHS.output, 'generated types')
|
||||
await mkdir(PATHS.outputTemp, { recursive: true })
|
||||
|
||||
if (log) console.info(chalk.gray('🏗️ Generating types...'))
|
||||
|
||||
try {
|
||||
const input: Record<string, string> = {
|
||||
...Object.fromEntries(
|
||||
Libraries.flatMap(([libName, exports]) =>
|
||||
getLibraries().flatMap(([libName, exports]) =>
|
||||
exports.map(([exportName, path]) => [
|
||||
`lib/${libName}${exportName && `/${exportName}`}`,
|
||||
`lib/${libName}${exportName ? `/${exportName}` : ''}`,
|
||||
path,
|
||||
]),
|
||||
),
|
||||
|
|
@ -65,9 +93,9 @@ export default async function buildTypes(log = true): Promise<void> {
|
|||
],
|
||||
})
|
||||
|
||||
await bundle.write({
|
||||
dir: PATHS.output,
|
||||
})
|
||||
await bundle.write({ dir: PATHS.output })
|
||||
|
||||
await internalizeSpecifiers(input)
|
||||
|
||||
await writeFile(
|
||||
`${PATHS.output}/index.d.ts`,
|
||||
|
|
@ -88,12 +116,24 @@ export default async function buildTypes(log = true): Promise<void> {
|
|||
imports: {
|
||||
'#*': { types: './*.d.ts' },
|
||||
},
|
||||
dependencies: versionsOf(TYPES_PACKAGE_DEPENDENCIES),
|
||||
peerDependencies: versionsOf(
|
||||
TYPES_PACKAGE_PEER_DEPENDENCIES,
|
||||
),
|
||||
peerDependenciesMeta: Object.fromEntries(
|
||||
TYPES_PACKAGE_OPTIONAL_PEER_DEPENDENCIES.map(name => [
|
||||
name,
|
||||
{ optional: true },
|
||||
]),
|
||||
),
|
||||
},
|
||||
null,
|
||||
4,
|
||||
)}\n`,
|
||||
)
|
||||
|
||||
await auditExternalSpecifiers()
|
||||
|
||||
if (log) {
|
||||
const duration = (performance.now() - start).toFixed(2)
|
||||
console.info(
|
||||
|
|
@ -110,41 +150,20 @@ export default async function buildTypes(log = true): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
import assets from 'pkg:assets'
|
||||
import components from 'pkg:components'
|
||||
import discord from 'pkg:discord'
|
||||
import externals from 'pkg:externals'
|
||||
import jsonStorage from 'pkg:json-storage'
|
||||
import modules from 'pkg:modules'
|
||||
import patcher from 'pkg:patcher'
|
||||
import plugins from 'pkg:plugins'
|
||||
import react from 'pkg:react'
|
||||
import utils from 'pkg:utils'
|
||||
import { writeFileSync } from 'fs'
|
||||
import { exists } from './_shared'
|
||||
|
||||
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>>>[],
|
||||
exports: ExportBinding<TrimLeadingDot<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 bindings: Record<string, { from: 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
|
||||
for (const item of exports) {
|
||||
const isBinding = Array.isArray(item)
|
||||
const name = isBinding ? item[0] : item
|
||||
const actualName = isBinding ? item[1] : item
|
||||
|
||||
const exportEntry = pkg.exports[`.${name ? `/${name}` : ''}`]
|
||||
if (!exportEntry) {
|
||||
|
|
@ -156,10 +175,7 @@ function library<
|
|||
continue
|
||||
}
|
||||
|
||||
const { default: defaultPath, types } = exportEntry
|
||||
const actualName = isBinding ? nameOrBinding[1] : name
|
||||
const path = defaultPath ?? types
|
||||
|
||||
const path = exportEntry.default ?? exportEntry.types
|
||||
if (!path) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
|
|
@ -169,39 +185,39 @@ function library<
|
|||
continue
|
||||
}
|
||||
|
||||
if (isBinding) (bindings[actualName] ??= []).push([actualName, name])
|
||||
else {
|
||||
if (isBinding) {
|
||||
;(bindings[actualName] ??= []).push({
|
||||
from: name,
|
||||
path: `${PATHS.lib}/${pkgName}/${path}`,
|
||||
})
|
||||
} else {
|
||||
console.debug(
|
||||
chalk.gray(
|
||||
`📄 ${pkgName}${name && `/${name}`} -> ${pkg.name}${name && `/${name}`}`,
|
||||
`📄 ${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('')
|
||||
|
||||
for (const [to, items] of Object.entries(bindings)) {
|
||||
if (items.length > 1) {
|
||||
const tempFileName = `${pkgName}_${to.replace(/[/\\?%*:|"<>]/g, '_')}`
|
||||
const tempPath = `${PATHS.outputTemp}/${tempFileName}.ts`
|
||||
const content = froms
|
||||
.map(([, from]) => {
|
||||
const content = items
|
||||
.map(({ from }) => {
|
||||
const path = `${pkg.name}/${from}`
|
||||
console.debug(
|
||||
chalk.gray(`↔️ ${pkgName}${to && `/${to}`} -> ${path}`),
|
||||
chalk.gray(
|
||||
`↔️ ${pkgName}${to ? `/${to}` : ''} -> ${path}`,
|
||||
),
|
||||
)
|
||||
|
||||
return `export * from '${path}'`
|
||||
})
|
||||
.join('\n')
|
||||
|
||||
try {
|
||||
writeFileSync(tempPath, content)
|
||||
writeFile(tempPath, content)
|
||||
mapped.push([to, tempPath])
|
||||
} catch (error) {
|
||||
console.error(
|
||||
|
|
@ -209,35 +225,211 @@ function library<
|
|||
error,
|
||||
)
|
||||
}
|
||||
} else if (froms[0]) {
|
||||
mapped.push(froms[0])
|
||||
} else if (items[0]) {
|
||||
mapped.push([to, items[0].path])
|
||||
}
|
||||
}
|
||||
|
||||
return [pkgName, mapped]
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates `index.d.ts` for the published types package.
|
||||
*
|
||||
* Consumers install the types package and add it to their tsconfig `types`, which loads a file that:
|
||||
*
|
||||
* - References `globals.d.ts` so global declarations (eg. `plugin()`, `revenge`) apply.
|
||||
* - Declares an ambient module for every public entry that re-exports the generated types file
|
||||
* via a package-private `imports` (eg. `#lib/assets`), which resolves inside the types package
|
||||
* without being importable to consumers.
|
||||
*/
|
||||
function getLibraries() {
|
||||
return [
|
||||
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',
|
||||
'utils/modules/finders',
|
||||
'utils/modules/metro/subscriptions',
|
||||
]),
|
||||
library(externals, [
|
||||
'browserify',
|
||||
'react-native-clipboard',
|
||||
'react-native-safe-area-context',
|
||||
'react-navigation',
|
||||
'shopify',
|
||||
]),
|
||||
library(modules, [
|
||||
'finders',
|
||||
'finders/filters',
|
||||
['metro/subscriptions', 'metro'],
|
||||
['metro/utils', 'metro'],
|
||||
'native',
|
||||
'native/fs',
|
||||
'types',
|
||||
]),
|
||||
library(patcher, ['', 'types']),
|
||||
library(plugins, ['constants', 'types', 'utils']),
|
||||
library(react, ['', 'jsx-runtime', 'native', 'types']),
|
||||
library(jsonStorage, ['', 'types']),
|
||||
library(utils, [
|
||||
'callback',
|
||||
'discord',
|
||||
'error',
|
||||
'object',
|
||||
'promise',
|
||||
'proxy',
|
||||
'react',
|
||||
'tree',
|
||||
'types',
|
||||
]),
|
||||
] as const
|
||||
}
|
||||
|
||||
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}`),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Rewrites `@revenge-mod/*` to its package-private `#lib/*. */
|
||||
async function internalizeSpecifiers(
|
||||
input: Record<string, string>,
|
||||
): Promise<void> {
|
||||
const entries = new Set(
|
||||
Object.keys(input)
|
||||
.filter(entry => entry.startsWith(`${PATHS.lib}/`))
|
||||
.map(entry => entry.slice(`${PATHS.lib}/`.length)),
|
||||
)
|
||||
|
||||
const pattern = new RegExp(
|
||||
`(['"])@revenge-mod/(${Array.from(entries)
|
||||
.sort((a, b) => b.length - a.length)
|
||||
.map(entry => entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||||
.join('|')})\\1`,
|
||||
'g',
|
||||
)
|
||||
|
||||
const files = await readdir(PATHS.output, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
})
|
||||
|
||||
await Promise.all(
|
||||
files.map(async file => {
|
||||
if (!file.isFile() || !file.name.endsWith('.d.ts')) return
|
||||
|
||||
const path = `${file.parentPath}/${file.name}`
|
||||
const code = await readFile(path, 'utf8')
|
||||
const rewritten = code.replace(
|
||||
pattern,
|
||||
(_, quote, entry) => `${quote}#${PATHS.lib}/${entry}${quote}`,
|
||||
)
|
||||
|
||||
if (rewritten !== code) await writeFile(path, rewritten)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/** Resolves the version ranges this repository uses for the given packages. */
|
||||
function versionsOf(names: string[]): Record<string, string> {
|
||||
const versions: Record<string, string> = {
|
||||
...pkg.dependencies,
|
||||
...pkg.devDependencies,
|
||||
...pkg.peerDependencies,
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
names.map(name => {
|
||||
const version = versions[name]
|
||||
if (!version) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`⚠️ "${name}" is declared for the types package but not installed here`,
|
||||
),
|
||||
)
|
||||
}
|
||||
return [name, version ?? '*']
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/** Warns about packages generated types use without the types package.json declaring them. */
|
||||
async function auditExternalSpecifiers(): Promise<void> {
|
||||
const declared = new Set([
|
||||
...TYPES_PACKAGE_DEPENDENCIES,
|
||||
...TYPES_PACKAGE_PEER_DEPENDENCIES,
|
||||
])
|
||||
|
||||
const files = await readdir(PATHS.output, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
})
|
||||
|
||||
const undeclared = new Map<string, string>()
|
||||
|
||||
await Promise.all(
|
||||
files.map(async file => {
|
||||
if (!file.isFile() || !file.name.endsWith('.d.ts')) return
|
||||
|
||||
const path = `${file.parentPath}/${file.name}`
|
||||
const code = (await readFile(path, 'utf8'))
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
.replace(/^\s*\/\/.*$/gm, '')
|
||||
|
||||
for (const [, specifier] of code.matchAll(
|
||||
/(?:from|import\()\s*["']([^"']+)["']/g,
|
||||
)) {
|
||||
if (/^[.#]|^@revenge-mod\//.test(specifier!)) continue
|
||||
|
||||
const name = specifier!.startsWith('@')
|
||||
? specifier!.split('/').slice(0, 2).join('/')
|
||||
: specifier!.split('/')[0]!
|
||||
|
||||
if (!declared.has(name)) undeclared.set(name, path)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
for (const [name, path] of undeclared) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`⚠️ "${name}" is imported by ${path} but not declared by the types package`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates `index.d.ts` for the published types package. */
|
||||
async function generateIndex(input: Record<string, string>): Promise<string> {
|
||||
const entries = Object.keys(input).sort()
|
||||
|
||||
const references = entries.map(
|
||||
entry => `/// <reference path="./${entry}.d.ts" />`,
|
||||
)
|
||||
|
||||
const shims: string[] = []
|
||||
|
||||
for (const entry of Object.keys(input).sort()) {
|
||||
// not importable, it's included via the reference above
|
||||
for (const entry of entries) {
|
||||
if (entry === 'globals') continue
|
||||
|
||||
const moduleName = entry.replace(/^lib\//, '@revenge-mod/')
|
||||
const typesPath = `#${entry}`
|
||||
const dts = await readFile(`${PATHS.output}/${entry}.d.ts`, 'utf8')
|
||||
// `export *` does not re-export default exports
|
||||
const hasDefault = /\bas default\b|\bexport default\b/.test(dts)
|
||||
|
||||
shims.push(
|
||||
|
|
@ -250,65 +442,7 @@ async function generateIndex(input: Record<string, string>): Promise<string> {
|
|||
)
|
||||
}
|
||||
|
||||
return `/// <reference path="./globals.d.ts" />\n\n${shims.join('\n\n')}\n`
|
||||
return `${references.join('\n')}\n\n${shims.join('\n\n')}\n`
|
||||
}
|
||||
|
||||
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',
|
||||
'utils/modules/finders',
|
||||
'utils/modules/metro/subscriptions',
|
||||
]),
|
||||
library(externals, [
|
||||
'browserify',
|
||||
'react-native-clipboard',
|
||||
'react-native-safe-area-context',
|
||||
'react-navigation',
|
||||
'shopify',
|
||||
]),
|
||||
library(modules, [
|
||||
'finders',
|
||||
'finders/filters',
|
||||
['metro/subscriptions', 'metro'],
|
||||
['metro/utils', 'metro'],
|
||||
'native',
|
||||
'native/fs',
|
||||
'types',
|
||||
]),
|
||||
library(patcher, ['', 'types']),
|
||||
library(plugins, ['constants', 'types', 'utils']),
|
||||
library(react, ['', 'jsx-runtime', 'native', 'types']),
|
||||
library(jsonStorage, ['', 'types']),
|
||||
library(utils, [
|
||||
'callback',
|
||||
'discord',
|
||||
'error',
|
||||
'object',
|
||||
'promise',
|
||||
'proxy',
|
||||
'react',
|
||||
'tree',
|
||||
'types',
|
||||
]),
|
||||
] as const
|
||||
|
||||
if (main === import.meta.filename) await buildTypes()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
"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",
|
||||
// Build artifacts required to emit types
|
||||
|
|
|
|||
7
types/globals.d.ts
vendored
7
types/globals.d.ts
vendored
|
|
@ -23,8 +23,6 @@ declare module 'react' {
|
|||
/// HERMES
|
||||
|
||||
declare global {
|
||||
const HermesInternal: HermesInternalObject
|
||||
|
||||
function setTimeout(
|
||||
cb: (...args: unknown[]) => unknown,
|
||||
timeout?: number,
|
||||
|
|
@ -95,10 +93,7 @@ declare global {
|
|||
var __METRO_GLOBAL_PREFIX__: ''
|
||||
|
||||
var __d: Metro.DefineFn
|
||||
var __r: Metro.RequireFn & {
|
||||
importDefault: Metro.ImportDefaultFn
|
||||
importAll: Metro.ImportAllFn
|
||||
}
|
||||
var __r: Metro.Require
|
||||
var __c: Metro.ClearFn
|
||||
var __registerSegment: Metro.RegisterSegmentFn
|
||||
}
|
||||
|
|
|
|||
6
types/globals.internal.d.ts
vendored
Normal file
6
types/globals.internal.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Globals declared for this codebase ONLY!
|
||||
|
||||
// React Native declares `HermesInternal` as `null | {}`, so shipping a stricter redeclaration would give consumers an error.
|
||||
// This repository doesn't use React Native types, so it can use a stricter declaration.
|
||||
|
||||
declare const HermesInternal: HermesInternalObject
|
||||
Loading…
Add table
Reference in a new issue