Skip to content

[No QA] chore: consolidate script execution on Bun (drop ts-node and tsx)#95890

Open
roryabraham wants to merge 8 commits into
mainfrom
rory-bun-scripts-migration
Open

[No QA] chore: consolidate script execution on Bun (drop ts-node and tsx)#95890
roryabraham wants to merge 8 commits into
mainfrom
rory-bun-scripts-migration

Conversation

@roryabraham

@roryabraham roryabraham commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Consolidates all developer/CI script execution in Expensify/App onto Bun, removing ts-node and tsx entirely. This unblocks us to upgrade @actions and @octokit packages to their newer versions, which are ESM-only, which ts-node doesn't really support. Bun is also significantly faster than either alternative.

  • Migrated every ts-node and tsx invocation to bun.
  • Added a root bunfig.toml that preloads scripts/stubReactNative.js, rewritten to use Bun's Bun.plugin().module() API so both import and require() of Flow-typed React Native packages (react-native, react-native-onyx, etc.) are intercepted for scripts that don't need real RN at runtime. This was necessary for ESM runtime.
  • Updated translationDryRun.yml to run generateTranslations --dry-run --locales it under bun and assert the generated src/languages/it.ts contains the expected banner and [it] prefix, so the workflow still fails loudly if the script breaks.
  • Removed ts-node, tsx, and tsconfig-paths from devDependencies, deleted the now-unused ts-node blocks from tsconfig.json and scripts/tsconfig.json, and deduped @types/bun out of server/victory-chart-renderer/package.json (already present at the root).
  • Updated stale comments/docs referencing ts-node/tsx to reflect Bun.

No behavior change for anything other than which runtime executes these scripts.

Fixed Issues

$ N/A — internal tooling/CI consolidation, no linked issue
PROPOSAL: N/A

Tests

  • Verify that no errors appear in the JS console
  1. npm install && bun --version
  2. npm run compress-svg — completes without error
  3. npm run react-compiler-compliance-check -- check src/App.tsx — reports COMPILED
  4. npm run eslint-report -- --no-open — writes .eslint-reports/eslint-report.html
  5. USE_WEB_PROXY=false npm run web-proxy — exits cleanly
  6. npm run gh-actions-unused-styles — completes without error
  7. npm run build — production build completes and writes dist/merged-source-map.js.map (exercises the tsxbun rsbuild + combine-web-sourcemaps.ts path)
  8. bun scripts/generateTranslations.ts --dry-run --locales it — writes src/languages/it.ts with the generated banner and [it] prefix (then revert the file)
  9. npm test — full Jest suite passes (any HomePage.test.tsx failure only reproduces under full-suite parallel runs and also occurs on main; passes in isolation on both branches — pre-existing flake, unrelated to this change)
  10. npm run typecheck — 0 errors
  11. rg 'ts-node|(?<![./])tsx(?![./])' . --glob '!node_modules/**' --glob '!package-lock.json' --glob '!**/*.tsx' --pcre2 — no remaining runtime invocations (only .tsx file-extension globs/markdown fences/unrelated third-party patch remain)
  12. npm ls ts-node tsx tsconfig-paths — only present as transitive deps of third-party packages (eslint-config-expensify, jest-config, typeorm, storybook-react-rsbuild), never our own devDependencies
  13. npm run test:bun — still scoped to server/ only (3 files, 16 tests) after restoring bunfig.toml's [test] section
  14. npm run knip-changed — resolves 2 pre-existing findings (tsx devDependency + binary), introduces 0 new findings

Offline tests

N/A — this PR only changes which local/CI runtime executes developer and build scripts; no app runtime or network behavior changes.

QA Steps

N/A — no app-facing change. This is a "[No QA]" tooling/CI chore.

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above (N/A — no issue for this internal tooling chore)
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section (N/A)
    • I added steps for Staging and/or Production testing in the QA steps section (N/A — no app-facing change)
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct) (N/A — no user input/UI involved)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (N/A — build/CI tooling only)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (N/A — no app-facing change)
  • I included screenshots or videos for tests on all platforms (N/A — no UI change)
  • I ran the tests on all platforms & verified they passed on: (N/A — tooling/CI change only, verified via local script runs, full Jest suite, and typecheck above)
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. (N/A — no user-facing copy)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers (N/A — replaces one script runner with another, no new pattern)
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (verified generateTranslations, combine-web-sourcemaps, rsbuild build, checkOnyxConnectBypass, compareKnipReports, findUnusedStyles, eslint-report, react-compiler-compliance-check, compressSvg, octokit, and web/proxy.ts all still work under Bun)
  • If a new CSS style is added I verified that: (N/A)
  • If new assets were added or existing ones were modified, I verified that: (N/A — no assets changed)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior (N/A)
  • If the PR modifies a generic component (N/A — no components modified)
  • If the PR modifies a component related to any of the existing Storybook stories (N/A)
  • If the PR modifies a component or page that can be accessed by a direct deeplink (N/A)
  • If the PR modifies the UI (N/A)
  • I added unit tests for any new feature or bug fix in this PR (N/A — no new runtime behavior; existing generateTranslationsTest.ts updated to reflect process.argv[0] now being 'bun')
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

N/A — no UI change.

roryabraham and others added 7 commits July 11, 2026 00:22
Adds root bunfig.toml wiring scripts/stubReactNative.js as a preload so Bun
scripts don't choke on Flow-typed React Native packages. Rewrites the stub to
use Bun.plugin's virtual-module API instead of a CJS require() hook, since
Bun's native ES `import` resolution bypasses Module.prototype.require
entirely (the old hook only ever covered `require()` calls). Also dedupes
@types/bun from the victory-chart-renderer workspace onto the root.

Co-authored-by: Cursor <cursoragent@cursor.com>
Swaps every ts-node/tsx invocation across package.json scripts, shell
wrappers (lint.sh, knip-changed.sh), GitHub workflow run: steps, and script
shebangs to bun. Updates generateTranslations.ts and translationDryRun.yml to
run under bun with an output-file assertion, rewrites octokit.ts's REPL
preload for Bun's REPL, and updates comments/help text that referenced
ts-node or tsx.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Drops the now-unused ts-node/tsx/tsconfig-paths devDependencies, deletes the
obsolete `ts-node` config blocks from the root and scripts/ tsconfig.json
files, removes tsconfig-paths from knip's ignoreDependencies, and refreshes
package-lock.json.

Co-authored-by: Cursor <cursoragent@cursor.com>
Satisfies unicorn/no-array-for-each caught by lint-changed.

Co-authored-by: Cursor <cursoragent@cursor.com>
…g 'bun'

Explicitly importing the "bun" module pulls @types/bun's global
augmentations (which redeclare `require` as non-generic NodeJS.Require)
into the whole root TS program, breaking the generic `require<T>`
overload src/types/global.d.ts registers. Using the ambient `Bun`
global avoids resolving @types/bun for this file entirely.

Co-authored-by: Cursor <cursoragent@cursor.com>
…heck

- bunfig.toml: adding root `preload` had dropped the existing [test]
  section that scopes `bun test` to server/ (Jest owns src/ and
  tests/). Restore it alongside preload.
- translationDryRun.yml: assert src/languages/it.ts's checksum
  actually changed, in addition to the existing content checks, so a
  silently no-op'd dry-run can't pass by coincidence against the
  already-committed file.
- stubReactNative.js: reference globalThis.Bun instead of the bare
  Bun global to satisfy eslint's no-undef without any config changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
CI runners don't have a global bun binary on PATH the way local dev
machines do; only `npm run <script>` steps get node_modules/.bin
prepended to PATH automatically. Direct `run: bun ...` steps in
workflows failed with "bun: command not found" (exit 127). npx
resolves the local bun devDependency, matching how these workflows
previously invoked `npx ts-node`.

Fixes translationDryRun, unused-styles, and knip CI failures observed
on this PR; also fixes generateTranslations, createDeployChecklist,
and deployExpensifyHelp which use the same pattern but weren't
triggered by this PR's checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
@OSBotify

Copy link
Copy Markdown
Contributor

A preview of your ExpensifyHelp changes have been deployed to https://873feacb.helpdot.pages.dev ⚡️

@roryabraham roryabraham requested a review from Julesssss July 11, 2026 09:34
@roryabraham roryabraham marked this pull request as ready for review July 11, 2026 09:34
@roryabraham roryabraham requested review from a team as code owners July 11, 2026 09:34
@melvin-bot melvin-bot Bot requested review from trjExpensify and removed request for a team July 11, 2026 09:34
@roryabraham roryabraham removed request for a team and trjExpensify July 11, 2026 09:34
@roryabraham roryabraham changed the title chore: consolidate script execution on Bun (drop ts-node and tsx) [No QA] chore: consolidate script execution on Bun (drop ts-node and tsx) Jul 11, 2026
@melvin-bot melvin-bot Bot requested a review from iwiznia July 11, 2026 09:35
@melvin-bot

melvin-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown

@iwiznia Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants