Skip to content

[No QA] Clean up tsconfig projects to split by runtime rather than directory - #99354

Merged
roryabraham merged 10 commits into
mainfrom
rory/tsconfig-cleanup-phase-1
Aug 25, 2026
Merged

[No QA] Clean up tsconfig projects to split by runtime rather than directory#99354
roryabraham merged 10 commits into
mainfrom
rory/tsconfig-cleanup-phase-1

Conversation

@roryabraham

@roryabraham roryabraham commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

This organizes TypeScript projects by runtime rather than directory, and removes some hacks that were in-place to hardcode certain files in tsconfigs.

  • Add shared, Bun, and Node TypeScript configs with directory-scoped ownership.
  • Move tooling tests, scripts, server tooling, and evals into the Bun project; move GitHub tooling, web/proxy.ts, and config into the Node project.
  • Keep generateTranslations importing the real English translations while isolating CONST's React Native-specific runtime values behind platform adapters.
  • Remove the redundant translation dry-run workflow. The Bun tooling test now imports the real English translations and validates the fallback CONST graph.

Fixed Issues

$ #99290

Tests

CI only.

  • Verify that no errors appear in the JS console

Offline tests

n/a

QA Steps

Same as tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • 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
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms (not applicable: tooling-only change)
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (not applicable: tooling-only change)
  • 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 it was doing.
  • I added unit tests for the changed generator behavior

Screenshots/Videos

Not applicable: tooling-only change.

@roryabraham

Copy link
Copy Markdown
Contributor Author

@codex review

@roryabraham

Copy link
Copy Markdown
Contributor Author

@MelvinBot review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d3baadbf68

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread scripts/generateTranslations.ts Outdated
@MelvinBot

Copy link
Copy Markdown
Contributor

Reviewed the tsconfig consolidation end-to-end. The restructure is clean and the tricky parts are handled well — decoupling generateTranslations from @src (parsing en.ts instead of importing it) is what makes the Bun project's RN-independent type graph actually hold together, and the colors.ts change is type-equivalent (Color is just string, so Record<string, string> loses nothing) while letting eslint-report.ts import @styles/theme/colors from the Bun project without dragging in RN types via ./types.

One concrete thing worth fixing before merge, plus two minor notes.

🟡 tsconfig.node.json drops the bundled-action exclusion

The old root tsconfig.json deliberately excluded .github/actions/**/index.js. The new tsconfig.node.json now owns .github/**/* but only excludes node_modules/dist — so that exclusion is gone.

allowJs: true is inherited from expo/tsconfig.basetsconfig.base.json, and .github/**/* matches the 24 committed ncc bundles at .github/actions/javascript/*/index.js (several over 2 MB each). They'll get pulled into the Node program. It won't produce errors — checkJs is off — which is why your local typecheck still passed, but the Node project now parses ~two dozen large minified bundles it has no reason to look at, slowing the check and bloating the program.

Suggested fix: re-add the exclusion to tsconfig.node.json:

"exclude": ["**/node_modules/*", "**/dist/*", ".github/actions/**/index.js"]
Minor notes + what else I checked

Minor:

  • Lost integration coverage from removing translationDryRun.yml. The Bun unit tests exercise the generator against synthetic en inputs, but the dry-run ran the real script against the actual it.ts/en.ts in CI. That real-file smoke test is gone. The risk is much lower now that the script parses en.ts instead of importing @src, so this is a reasonable tradeoff — just calling out it's not a 1:1 replacement.
  • paths is now duplicated between tsconfig.base.json and tsconfig.json (the Babel-can't-resolve-extends comment explains why). Intentional, but the two blocks can silently drift — worth a watchful eye when paths change.

Verified fine (no action needed):

  • Coverage is intact. web/ had only proxy.ts as a TS file (now in the Node project); server/ has no root-level TS beyond libs/plugins/stubs (Bun) + victory-chart-renderer (own project); no jest-global usage in .github, so types: ["node"] is safe there.
  • objectHas swap is sound. TSCompilerUtils.objectHas(translationsNode, path) is a correct AST equivalent of the old get(en, path) existence check, and it's parsed once in the constructor (before new CLI), so the --paths validator has it available. If anything it's slightly more correct — a real path whose value is '' no longer fails the old truthiness check.
  • No dangling references to the four deleted tsconfigs (server, scripts, evals, tests/tooling) anywhere in the repo.
  • typecheck.yml glob correctly widened to **/tsconfig*.json so the new root-level tsconfig.bun.json/tsconfig.node.json/tsconfig.base.json trigger the workflow.

This is a tooling-only change with no app-runtime surface, so there's nothing meaningful to verify by driving the app — the signal is entirely in typecheck/lint/test:bun, which you've run.


view run

@roryabraham roryabraham changed the title chore: clean up Bun and Node tsconfig projects [No QA] Clean up tsconfig projects to split by runtime rather than directory Aug 24, 2026
Keep Bun translation tooling off the React Native import graph while preserving shared runtime values and type contracts.
@roryabraham

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Re-added .github/actions/**/index.js to tsconfig.node.json's exclusions in 12999ea, so the Node project does not parse generated ncc bundles. Node typecheck, lint, and the translation-generator tests pass. The duplicated paths block remains intentional because Babel does not follow tsconfig extends; noted the real-file smoke-test tradeoff.

Keep the monolithic CONST as the source of truth while routing React Native-specific values through platform adapters for Bun tooling.
@OSBotify

Copy link
Copy Markdown
Contributor

🦜 Polyglot Parrot! 🦜

Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues:

View the translation diff
diff --git a/src/languages/de.ts b/src/languages/de.ts
index c5a95fd8..027b1547 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -1542,7 +1542,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'basierend auf bisherigen Aktivitäten',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `basierend auf den <a href="${rulesLink}">Workspace-Regeln</a>` : 'basierend auf dem Workspace-Regelwerk'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `für ${comment}` : 'Ausgabe'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Rechnungsbericht Nr. ${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Rechnungsbericht Nr. ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} gesendet${comment ? `für ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `Ausgabe von persönlichem Bereich nach ${workspaceName ?? `Chat mit ${reportName}`} verschoben`,
         movedToPersonalSpace: 'Ausgabe in persönlichen Bereich verschoben',
@@ -1866,7 +1866,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Wählen Sie die Details aus, die Sie behalten möchten:',
             noDifferences: 'Keine Unterschiede zwischen den Transaktionen gefunden',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'an' : 'a';
+                const article = startsWithVowel(field) ? 'an' : 'eine';
                 return `Bitte wählen Sie ${article} ${field} aus`;
             },
             pleaseSelectAttendees: 'Bitte wählen Sie Teilnehmende aus',
diff --git a/src/languages/el.ts b/src/languages/el.ts
index fa947524..d442d0f4 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -1265,7 +1265,6 @@ const translations: TranslationDeepObject<typeof en> = {
             if (count === 0) {
                 return duplicates > 0 ? 'Δεν προστέθηκαν κανόνες εμπόρων, καθώς υπάρχουν ήδη όλοι.' : 'Δεν έχουν προστεθεί κανόνες εμπόρου.';
             }
-
             return {
                 one: 'Προστέθηκε 1 κανόνας εμπόρου.',
                 other: `Έχουν προστεθεί ${count} κανόνες εμπόρων.`,
@@ -1597,7 +1596,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'βάσει προηγούμενης δραστηριότητας',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `βάσει των <a href="${rulesLink}">κανόνων του χώρου εργασίας</a>` : 'βάσει κανόνα χώρου εργασίας'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `για ${comment}` : 'δαπάνη'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Αναφορά τιμολογίου #${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Αναφορά τιμολογίου #${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `στάλθηκαν ${formattedAmount}${comment ? `για ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName, workspaceName) => `μετακινήθηκε η δαπάνη από τον προσωπικό χώρο στο ${workspaceName ?? `συνομιλήστε με τον/την ${reportName}`}`,
         movedToPersonalSpace: 'μετακινήθηκε η δαπάνη στον προσωπικό χώρο',
@@ -1916,7 +1915,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Επιλέξτε τις λεπτομέρειες που θέλετε να διατηρήσετε:',
             noDifferences: 'Δεν βρέθηκαν διαφορές μεταξύ των συναλλαγών',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'ένα' : 'α';
+                const article = startsWithVowel(field) ? 'ένα' : 'α';
                 return `Παρακαλούμε επιλέξτε ${article} ${field}`;
             },
             pleaseSelectAttendees: 'Παρακαλώ επιλέξτε συμμετέχοντες',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index f90ba1f6..8d0c912b 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1189,7 +1189,6 @@ const translations: TranslationDeepObject<typeof en> = {
             if (count === 0) {
                 return duplicates > 0 ? 'No se han añadido reglas de comerciante, ya que todas ya existen.' : 'No se han añadido reglas de comerciante.';
             }
-
             return {
                 one: 'Se ha añadido 1 regla de comerciante.',
                 other: `Se han añadido ${count} reglas de comerciante.`,
@@ -1540,7 +1539,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'basado en actividad pasada',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `basado en <a href="${rulesLink}">reglas del espacio de trabajo</a>` : 'basado en regla del espacio de trabajo'),
         threadExpenseReportName: (formattedAmount, comment) => `${comment ? `${formattedAmount} para ${comment}` : `Gasto de ${formattedAmount}`}`,
-        invoiceReportName: ({linkedReportID}) => `Informe de facturación #${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Informe de factura n.º ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount, comment) => `${formattedAmount} enviado${comment ? ` para ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName, workspaceName) => `movió el gasto desde su espacio personal a ${workspaceName ?? `un chat con ${reportName}`}`,
         movedToPersonalSpace: 'movió el gasto a su espacio personal',
@@ -1852,7 +1851,10 @@ const translations: TranslationDeepObject<typeof en> = {
             header: 'Selecciona los detalles',
             pageTitle: 'Selecciona los detalles que deseas conservar:',
             noDifferences: 'No se encontraron diferencias entre las transacciones',
-            pleaseSelectError: ({field}) => `Por favor, selecciona un(a) ${field}`,
+            pleaseSelectError: ({field}: {field: string}) => {
+                const article = startsWithVowel(field) ? 'un' : 'a';
+                return `Por favor, selecciona ${article} ${field}`;
+            },
             pleaseSelectAttendees: 'Por favor, selecciona asistentes',
             selectAllDetailsError: 'Selecciona todos los detalles antes de continuar.',
         },
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 38c034a3..8a7e43cb 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -1546,7 +1546,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnMCC: ({rulesLink}: {rulesLink: string}) =>
             rulesLink ? `en fonction des <a href="${rulesLink}">règles de l’espace de travail</a>` : 'en fonction de la règle de l’espace de travail',
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `pour ${comment}` : 'dépense'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Note de frais de facture n° ${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Note de frais de facture n° ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} envoyé${comment ? `pour ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `a déplacé la dépense de l’espace personnel vers ${workspaceName ?? `discuter avec ${reportName}`}`,
         movedToPersonalSpace: 'a déplacé la dépense vers l’espace personnel',
@@ -1872,7 +1872,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Sélectionnez les détails que vous souhaitez conserver :',
             noDifferences: 'Aucune différence trouvée entre les transactions',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'un' : 'un';
+                const article = startsWithVowel(field) ? 'un' : 'a';
                 return `Veuillez sélectionner ${article} ${field}`;
             },
             pleaseSelectAttendees: 'Veuillez sélectionner les participants',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 87a38c60..e5f94c38 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -1540,7 +1540,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'in base all’attività precedente',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `in base alle <a href="${rulesLink}">regole dello spazio di lavoro</a>` : 'in base alle regole della workspace'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `per ${comment}` : 'spesa'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Report fattura n. ${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Report fattura n. ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} inviato${comment ? `per ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `ha spostato la spesa dallo spazio personale a ${workspaceName ?? `chatta con ${reportName}`}`,
         movedToPersonalSpace: 'ha spostato la spesa nello spazio personale',
@@ -1863,7 +1863,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Seleziona i dettagli che vuoi mantenere:',
             noDifferences: 'Nessuna differenza trovata tra le transazioni',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'un' : 'a';
+                const article = startsWithVowel(field) ? 'un' : 'a';
                 return `Seleziona ${article} ${field}`;
             },
             pleaseSelectAttendees: 'Seleziona i partecipanti',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index fdb22137..a076a4ef 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -1521,7 +1521,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: '過去のアクティビティに基づく',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `<a href="${rulesLink}">ワークスペースルール</a>に基づく` : 'ワークスペースのルールに基づく'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `${comment} 用` : '経費'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `請求書レポート #${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `請求書レポート #${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} を送信済み${comment ? `${comment}用` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `経費を個人スペースから${workspaceName ?? `${reportName}とチャット`}に移動しました`,
         movedToPersonalSpace: '経費を個人スペースに移動しました',
@@ -1841,8 +1841,8 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: '保持したい詳細を選択してください:',
             noDifferences: '取引間に差異は見つかりませんでした',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'ある' : 'a';
-                return `${article} の${field}を選択してください`;
+                const article = startsWithVowel(field) ? '1つの' : 'a';
+                return `${article} ${field} を選択してください`;
             },
             pleaseSelectAttendees: '出席者を選択してください',
             selectAllDetailsError: '続行する前にすべての詳細を選択してください。',
@@ -9024,9 +9024,6 @@ ${reportName}`,
             reject: '却下',
             duplicateExpense: () => ({
                 one: '経費を複製',
-                // Japanese has no grammatical plural, so `Intl.PluralRules` selects `other` for every count,
-                // including 1. The single/bulk distinction here is semantic rather than grammatical, so `other`
-                // branches on the count itself to keep 一括 (bulk) on the multi-expense action only.
                 other: (count: number) => (count === 1 ? '経費を複製' : '経費を一括複製'),
             }),
             noOptionsAvailable: '選択した経費グループには利用できるオプションがありません。',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index b20d5623..92853b40 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -1538,7 +1538,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'op basis van eerdere activiteit',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `op basis van <a href="${rulesLink}">werkruimteregels</a>` : 'op basis van werkruimteregel'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `voor ${comment}` : 'uitgave'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Factuurrapport nr. ${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Factuurrapport nr. ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} verzonden${comment ? `voor ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `heeft uitgave verplaatst van persoonlijke ruimte naar ${workspaceName ?? `chat met ${reportName}`}`,
         movedToPersonalSpace: 'heeft uitgave verplaatst naar persoonlijke ruimte',
@@ -1858,8 +1858,8 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Selecteer de details die je wilt behouden:',
             noDifferences: 'Geen verschillen gevonden tussen de transacties',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'een' : 'een';
-                return `Selecteer ${article} ${field}`;
+                const article = startsWithVowel(field) ? 'een' : 'een';
+                return `Selecteer alsjeblieft ${article} ${field}`;
             },
             pleaseSelectAttendees: 'Selecteer aanwezigen',
             selectAllDetailsError: 'Selecteer alle details voordat je verdergaat.',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 7055403c..a4acf289 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -1567,7 +1567,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'na podstawie dotychczasowej aktywności',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `na podstawie <a href="${rulesLink}">zasad przestrzeni roboczej</a>` : 'na podstawie reguły przestrzeni roboczej'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `dla ${comment}` : 'wydatek'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Raport faktury nr ${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Raport faktury nr ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `Wysłano ${formattedAmount}${comment ? `za ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `przeniesiono wydatek z przestrzeni osobistej do ${workspaceName ?? `czat z ${reportName}`}`,
         movedToPersonalSpace: 'przeniesiono wydatek do przestrzeni prywatnej',
@@ -1890,7 +1890,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Wybierz szczegóły, które chcesz zachować:',
             noDifferences: 'Nie znaleziono różnic między transakcjami',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'włączony' : 'a';
+                const article = startsWithVowel(field) ? 'jeden' : 'a';
                 return `Wybierz ${article} ${field}`;
             },
             pleaseSelectAttendees: 'Wybierz uczestników',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index b72a7b75..c4591cdd 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -1537,7 +1537,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: 'com base na atividade anterior',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `com base nas <a href="${rulesLink}">regras do workspace</a>` : 'com base na regra do workspace'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `para ${comment}` : 'despesa'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `Relatório de fatura nº ${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `Relatório de fatura nº ${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} enviado${comment ? `para ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `moveu a despesa do espaço pessoal para ${workspaceName ?? `conversar com ${reportName}`}`,
         movedToPersonalSpace: 'moveu a despesa para o espaço pessoal',
@@ -1853,7 +1853,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: 'Selecione os detalhes que você quer manter:',
             noDifferences: 'Nenhuma diferença encontrada entre as transações',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? 'um' : 'a';
+                const article = startsWithVowel(field) ? 'um' : 'um';
                 return `Selecione ${article} ${field}`;
             },
             pleaseSelectAttendees: 'Selecione participantes',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 4ebbb342..49e6e3c7 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -1479,7 +1479,7 @@ const translations: TranslationDeepObject<typeof en> = {
         basedOnAI: '基于过去的活动',
         basedOnMCC: ({rulesLink}: {rulesLink: string}) => (rulesLink ? `基于<a href="${rulesLink}">工作区规则</a>` : '基于工作区规则'),
         threadExpenseReportName: (formattedAmount: string, comment?: string) => `${formattedAmount} ${comment ? `用于 ${comment}` : '报销'}`,
-        invoiceReportName: ({linkedReportID}: OriginalMessage<typeof CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW>) => `发票报告 #${linkedReportID}`,
+        invoiceReportName: ({linkedReportID}: OriginalMessageReportPreview) => `发票报表 #${linkedReportID}`,
         threadPaySomeoneReportName: (formattedAmount: string, comment?: string) => `已发送 ${formattedAmount}${comment ? `用于 ${comment}` : ''}`,
         movedFromPersonalSpace: (reportName?: string, workspaceName?: string) => `已将报销从个人空间移动到 ${workspaceName ?? `与 ${reportName} 聊天`}`,
         movedToPersonalSpace: '已将报销移动到个人空间',
@@ -1782,7 +1782,7 @@ const translations: TranslationDeepObject<typeof en> = {
             pageTitle: '选择要保留的详细信息:',
             noDifferences: '在这些交易之间未发现差异',
             pleaseSelectError: ({field}: {field: string}) => {
-                const article = StringUtils.startsWithVowel(field) ? '一个' : 'a';
+                const article = startsWithVowel(field) ? '一个' : '一个';
                 return `请选择${article}${field}`;
             },
             pleaseSelectAttendees: '请选择参会者',

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply 😉

View workflow run

Document the CONST runtime split, reuse existing translation parameter types, and apply generated locale updates.
@roryabraham

Copy link
Copy Markdown
Contributor Author

@codex review

@roryabraham

Copy link
Copy Markdown
Contributor Author

@MelvinBot review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 0d221220a3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@MelvinBot

Copy link
Copy Markdown
Contributor

Re-reviewed the changes since my last pass — the CONST runtime isolation and translation-type isolation that landed after it. LGTM, no blockers. The prior finding is fixed and the new work is clean.

✅ Prior finding resolved

tsconfig.node.json now re-excludes .github/actions/**/index.js, so the Node project no longer parses the committed ncc bundles.

What I verified on the new surface

  • Runtime isolation is behavior-preserving on web/native. runtime.web.ts / runtime.native.ts both resolve to runtimeConfigured.ts, which reads the exact same Config / KeyCommand / variables / ONYXKEYS / SCREENS values the old inline CONST used. .web.ts platform resolution is a pre-existing pattern (27 files), so real builds pick the configured module — the Bun-safe runtimeDefaults.ts only feeds the tooling program. Account-ID defaults match the old Config fallbacks 1:1, and CONSTRuntime.test.ts locks in the override-with-fallback behavior.
  • startsWithVowel extraction is cleanStringUtils/index.ts still re-exports it, so StringUtils.startsWithVowel consumers are unaffected.
  • Duplicated Bun-safe types are guarded. TranslationTypes.ts hand-copies OriginalMessageReportPreview, ConnectionName, etc. so en.ts/params.ts stay RN-free. en.ts uses these copies while the sibling locales (it.ts, de.ts, …) import the canonical @src/types/onyx versions — so any divergence would break TranslationDeepObject<typeof en> conformance and fail typecheck. Nice self-catching property; the copies currently match the canonical types exactly.
  • scripts/typecheck.ts correctly switched to per-tsconfig paths with distinct *.ts7.tsbuildinfo files (no buildinfo collisions), and typecheck.yml's glob is widened to **/tsconfig*.json.

🟡 Minor (optional)

The only residual is the one I flagged before: the duplicated types in TranslationTypes.ts that aren't also imported by a canonical-consuming locale have no assertion tying them to @src/types/onyx. A tiny expectTypeOf-style equality test would make future drift fail loudly instead of silently. Non-blocking.

Local web smoke test (inconclusive — environmental)

I ran a boot smoke test against the local dev server for the PR's "no console errors" step. The page stayed blank (0 DOM nodes) after a 120s wait, but I'm treating this as a dev-server/session bring-up issue in the test harness, not a regression — on this exact commit, CI's app-rendering checks are all green: the web build jobs, branch-perf-tests (which launch and render the app), Storybook tests, and all 8 test (Jest) jobs that import the reorganized CONST module graph. A broken CONST boot would have taken those down. The web tooling can't capture the JS console, so there's no stack trace to add.

Step Status Observation
App loads to Home without crash/error screen fail Blank white page, 0 DOM nodes after 120s wait — attributed to local dev/session bring-up, contradicted by green CI render checks
No JS errors on load / opening a report partial UI never rendered so couldn't reach a report; agent-device web has no console capture
Local dev smoke test: blank page after 120s (environmental — CI render checks are green)

view run

@roryabraham
roryabraham marked this pull request as ready for review August 25, 2026 19:28
@roryabraham
roryabraham requested review from a team as code owners August 25, 2026 19:28
@melvin-bot
melvin-bot Bot requested review from ChavdaSachin and heyjennahay and removed request for a team August 25, 2026 19:28
@melvin-bot

melvin-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

@ChavdaSachin 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]

@melvin-bot
melvin-bot Bot removed the request for review from a team August 25, 2026 19:28
@roryabraham
roryabraham removed the request for review from a team August 25, 2026 19:28
@roryabraham

Copy link
Copy Markdown
Contributor Author

No product, design, or C+ review should be needed here imo:

  • It has no visual changes and no changes to the actual app runtime (except moving some constants around).
  • There's no manual test steps or screenshots, since it's a CI-first change.

Comment thread src/CONST/runtime.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d221220a3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/languages/es.ts Outdated
@blimpich
blimpich self-requested a review August 25, 2026 20:26
Comment thread src/languages/fr.ts
blimpich
blimpich previously approved these changes Aug 25, 2026

@blimpich blimpich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't able to find anything to critique on my pass. Claude found a very minor translation issue but otherwise looks good 👍

@roryabraham
roryabraham merged commit 4e67bd1 into main Aug 25, 2026
44 of 50 checks passed
@roryabraham
roryabraham deleted the rory/tsconfig-cleanup-phase-1 branch August 25, 2026 21:24
@github-actions

Copy link
Copy Markdown
Contributor

🚧 roryabraham has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

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.

4 participants