diff --git a/.vscode/settings.json b/.vscode/settings.json index dfd80c5..3f012b8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, @@ -9,6 +8,9 @@ "[typescriptreact]": { "editor.defaultFormatter": "vscode.typescript-language-features" }, + "[json]": { + "editor.formatOnSave": false + }, "javascript.format.semicolons": "insert", "typescript.format.semicolons": "insert", "typescript.preferences.quoteStyle": "double", diff --git a/package.json b/package.json index f586bcb..8baaa8e 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@typescript-eslint/typescript-estree": "^7.1.1", "chalk": "^5.3.0", "esbuild": "^0.20.2", + "esbuild-plugin-globals": "^0.2.0", "eslint": "^8.57.0", "eslint-plugin-import-alias": "^1.2.0", "eslint-plugin-react": "^7.34.0", @@ -46,10 +47,7 @@ }, "pnpm": { "peerDependencyRules": { - "ignoreMissing": [ - "react", - "react-native" - ] + "ignoreMissing": ["react", "react-native"] } } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 310b834..cb6efb5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,9 @@ devDependencies: esbuild: specifier: ^0.20.2 version: 0.20.2 + esbuild-plugin-globals: + specifier: ^0.2.0 + version: 0.2.0 eslint: specifier: ^8.57.0 version: 8.57.0 @@ -1172,6 +1175,11 @@ packages: resolution: {integrity: sha512-ldETiVy9nu3uCHKsALW4pGitzASWivUq79DdMQqdLa2Nx0swhULpvXCqSoJrWYVI69bsgaNRqnFRwpHie8hY+A==} dev: false + /esbuild-plugin-globals@0.2.0: + resolution: {integrity: sha512-y+6utQVWrETQWs0J8EGLV5gEOP59mmjX+fKWoQHn4TYwFMaj0FxQYflc566tHuokBCzl+uNW2iIlM1o1jfNy6w==} + engines: {node: '>=7'} + dev: true + /esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} diff --git a/scripts/build.mjs b/scripts/build.mjs index 0bbfc96..2c879fa 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -3,6 +3,7 @@ import swc from "@swc/core"; import crypto from "crypto"; import { build } from "esbuild"; +import globalPlugin from "esbuild-plugin-globals"; import { readFile, writeFile } from "fs/promises"; import path from "path"; import { fileURLToPath } from "url"; @@ -41,28 +42,15 @@ const config = { __DEV__: JSON.stringify(releaseBranch !== "main") }, legalComments: "none", + alias: { + "spitroast": "./node_modules/spitroast", + "react/jsx-runtime": "./shims/jsxRuntime" + }, plugins: [ - { - name: "runtimeGlobalAlias", - setup: async build => { - const globalMap = { - "react": "globalThis.React", - "react-native": "globalThis.ReactNative" - }; - - Object.keys(globalMap).forEach(key => { - const filter = new RegExp(`^${key}$`); - build.onResolve({ filter }, args => ({ - namespace: "glob-" + key, path: args.path - })); - build.onLoad({ filter, namespace: "glob-" + key }, () => ({ - // @ts-ignore - contents: `Object.defineProperty(module, 'exports', { get: () => ${globalMap[key]} })`, - resolveDir: "src", - })); - }); - } - }, + globalPlugin({ + "react": "React", + "react-native": "ReactNative" + }), { name: "swc", setup(build) { @@ -79,7 +67,7 @@ const config = { } }, react: { - pragma: "__bunny_createElement" + runtime: "automatic" } }, }, diff --git a/shims/emptyModule.tsx b/shims/emptyModule.tsx new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/shims/emptyModule.tsx @@ -0,0 +1 @@ +export {}; diff --git a/shims/jsxRuntime.tsx b/shims/jsxRuntime.tsx new file mode 100644 index 0000000..0ab19a4 --- /dev/null +++ b/shims/jsxRuntime.tsx @@ -0,0 +1,14 @@ +import { getFactoryOfProxy } from "@lib/utils/lazy"; +import { findByPropsProxy } from "@metro/utils"; + +const jsxRuntime = findByPropsProxy("jsx", "jsxs", "Fragment"); + +function unproxyFirstArg(args: T[]) { + const factory = getFactoryOfProxy(args[0]); + if (factory) args[0] = factory(); + return args; +} + +export const Fragment = Symbol.for("react.fragment"); +export const jsx = (...args: any[]) => jsxRuntime.jsx(...unproxyFirstArg(args)); +export const jsxs = (...args: any[]) => jsxRuntime.jsxs(...unproxyFirstArg(args)); diff --git a/src/entry.ts b/src/entry.ts index f1e10e0..3231677 100644 --- a/src/entry.ts +++ b/src/entry.ts @@ -1,17 +1,9 @@ -import { getFactoryOfProxy } from "@lib/utils/lazy"; import { version } from "bunny-build-info"; -import { instead } from "spitroast"; +const { instead } = require("spitroast"); // @ts-ignore - shut up fr globalThis.window = globalThis; -// @ts-ignore && TODO: Move this to a more sensible place -globalThis.__bunny_createElement = function createElement(type, props, ...children) { - const factory = getFactoryOfProxy(type); - if (factory) type = factory(); - return React.createElement(type, props, ...children); -}; - async function initializeBunny() { try { // Make 'freeze' and 'seal' do nothing @@ -42,7 +34,7 @@ if (typeof globalThis.__r !== "undefined") { const batchedBridge = window.__fbBatchedBridge; const callQueue = new Array; - const unpatchHook = instead("callFunctionReturnFlushedQueue", batchedBridge, (args, orig) => { + const unpatchHook = instead("callFunctionReturnFlushedQueue", batchedBridge, (args: any, orig: any) => { if (args[0] === "AppRegistry" || !batchedBridge.getCallableModule(args[0])) { callQueue.push(args); return batchedBridge.flushedQueue(); diff --git a/src/lib/api/patcher.ts b/src/lib/api/patcher.ts index 8d13884..5f425b5 100644 --- a/src/lib/api/patcher.ts +++ b/src/lib/api/patcher.ts @@ -1,14 +1,16 @@ import { getFindContext } from "@metro/proxy"; -import { - after as _after, - before as _before, - instead as _instead -} from "spitroast"; +const { + after: _after, + before: _before, + instead: _instead +} = require("spitroast") as + // Temporary + typeof import("../../../node_modules/spitroast"); -type ParametersAfterOneArgs = F extends (x: any, y: any, ...args: infer P) => unknown ? P : never; -type ProxyPatchParameters = [func: string, parent: [P, (t: P) => any], ...args: ParametersAfterOneArgs]; +type ParametersAfterTwoArgs = F extends (x: any, y: any, ...args: infer P) => unknown ? P : never; +type ProxyPatchParameters = [func: string, parent: [P, (t: P) => any], ...args: ParametersAfterTwoArgs]; -function shim) => any>(fn: T) { +function shim) => any>(fn: T) { function shimmed(this: any, ...args: Parameters) { const proxyInfo = getFindContext(args[1]); if (proxyInfo && !proxyInfo.cache && proxyInfo.indexed) { diff --git a/src/metro/modules.ts b/src/metro/modules.ts index 1cc9c8d..92d5e6f 100644 --- a/src/metro/modules.ts +++ b/src/metro/modules.ts @@ -1,6 +1,7 @@ import { ExportsFlags, getMetroCache, indexBlacklistFlag, indexExportsFlags, ModulesMapInternal } from "@metro/caches"; import { Metro } from "@metro/types"; -import { before, instead } from "spitroast"; + +const { before, instead } = require("spitroast"); export const metroModules: Metro.ModuleList = window.modules; const metroRequire: Metro.Require = window.__r; @@ -87,7 +88,7 @@ function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { // There are modules registering the same native component if (moduleExports?.default?.name === "requireNativeComponent") { - instead("default", moduleExports, (args, origFunc) => { + instead("default", moduleExports, (args: any, origFunc: any) => { try { return origFunc(...args); } catch { @@ -112,7 +113,7 @@ function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { } if (!patchedImportTracker && moduleExports.fileFinishedImporting) { - before("fileFinishedImporting", moduleExports, ([filePath]) => { + before("fileFinishedImporting", moduleExports, ([filePath]: [string]) => { if (_importingModuleId === -1 || !filePath) return; metroModules[_importingModuleId]!.__filePath = filePath; }); @@ -137,7 +138,7 @@ function onModuleRequire(moduleExports: any, id: Metro.ModuleID) { // Hindi timestamps moment if (moduleExports.isMoment) { - instead("defineLocale", moduleExports, (args, orig) => { + instead("defineLocale", moduleExports, (args: [string], orig: (lcl: string) => string) => { const origLocale = moduleExports.locale(); orig(...args); moduleExports.locale(origLocale); diff --git a/tsconfig.json b/tsconfig.json index 3428aaa..dee7a45 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,33 +5,18 @@ "compilerOptions": { "strict": true, "paths": { - "@core/*": [ - "./src/core/*" - ], - "@lib": [ - "./src/lib" - ], - "@lib/*": [ - "./src/lib/*" - ], - "@metro": [ - "./src/metro" - ], - "@metro/*": [ - "./src/metro/*" - ], - "@ui": [ - "./src/lib/ui" - ], - "@ui/*": [ - "./src/lib/ui/*" - ], - "@types": [ - "./src/lib/utils/types.ts" - ], - "@assets/*": [ - "./src/assets/*" - ] + // Just to discourage from consuming the module directly + "spitroast": ["./shims/emptyModule"], + + "@core/*": ["./src/core/*"], + "@lib": ["./src/lib"], + "@lib/*": ["./src/lib/*"], + "@metro": ["./src/metro"], + "@metro/*": ["./src/metro/*"], + "@ui": ["./src/lib/ui"], + "@ui/*": ["./src/lib/ui/*"], + "@types": ["./src/lib/utils/types.ts"], + "@assets/*": ["./src/assets/*"] }, "module": "esnext", "moduleResolution": "bundler",