Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 0 additions & 40 deletions .github/workflows/translationDryRun.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
pull_request:
types: [opened, synchronize]
branches-ignore: [staging, production]
paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', '**/tsconfig.json']
paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', '**/tsconfig*.json']

concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-typecheck
Expand Down
34 changes: 4 additions & 30 deletions config/eslint/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -715,46 +715,20 @@ const config = defineConfig([
},

{
files: ['server/**/*.ts', 'server/**/*.tsx'],
files: ['scripts/**/*.ts', 'tests/tooling/**/*.ts', 'server/{libs,plugins,stubs}/**/*.{ts,tsx}', 'evals/**/*.ts'],
languageOptions: {
parserOptions: {
project: path.resolve(projectRoot, 'server/tsconfig.json'),
project: path.resolve(projectRoot, 'tsconfig.bun.json'),
projectService: false,
},
},
},

{
// Its own project because `@types/bun`'s globals conflict with the app's, so it is excluded from
// the root tsconfig and would otherwise belong to no project at all.
files: ['evals/**/*.ts'],
files: ['.github/**/*.{ts,tsx,js}', 'web/proxy.ts', 'config/**/*.{ts,tsx,mts,mjs,cjs,js}'],
languageOptions: {
parserOptions: {
project: path.resolve(projectRoot, 'evals/tsconfig.json'),
projectService: false,
},
},
},

{
// CIGitLogic is excluded from the root tsconfig because it needs @types/bun, so type-aware rules have to
// be pointed at the project that does own it. See tests/tooling/README.md.
files: ['tests/tooling/CIGitLogic.test.ts'],
languageOptions: {
parserOptions: {
project: path.resolve(projectRoot, 'tests/tooling/tsconfig.json'),
projectService: false,
},
},
},

{
// Bun-only scripts are excluded from the root tsconfig because they need @types/bun, so type-aware rules
// have to be pointed at the project that owns them. See scripts/tsconfig.json.
files: ['scripts/applyPatches.ts', 'scripts/lint.ts', 'scripts/typecheck.ts'],
languageOptions: {
parserOptions: {
project: path.resolve(projectRoot, 'scripts/tsconfig.json'),
project: path.resolve(projectRoot, 'tsconfig.node.json'),
projectService: false,
},
},
Expand Down
2 changes: 1 addition & 1 deletion config/rsbuild/rsbuild.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ const getCommonConfiguration = async ({file = '.env', platform = 'web', isDevSer
...(sentryWebpackPlugin
? ([
sentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN as string | undefined,
authToken: process.env.SENTRY_AUTH_TOKEN,
org: 'expensify',
project: 'app',
release: {
Expand Down
21 changes: 0 additions & 21 deletions evals/tsconfig.json

This file was deleted.

53 changes: 26 additions & 27 deletions scripts/generateTranslations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import hashStr from '@libs/StringUtils/hash';

import {isTranslationTargetLocale, TRANSLATION_TARGET_LOCALES} from '@src/CONST/LOCALES';
import type {TranslationTargetLocale} from '@src/CONST/LOCALES';
import en from '@src/languages/en';
import type {TranslationPaths} from '@src/languages/types';

import type {TemplateExpression} from '@typescript/typescript6';

Expand All @@ -21,8 +19,6 @@ import * as dotenv from 'dotenv';
import {Str} from 'expensify-common';
import CLI from 'expensify-common/CLI';
import fs from 'fs';
// eslint-disable-next-line you-dont-need-lodash-underscore/get
import get from 'lodash/get';
import path from 'path';

import type {DiffResult} from './utils/Git';
Expand Down Expand Up @@ -108,6 +104,11 @@ class TranslationGenerator {
*/
private readonly sourceFile: ts.SourceFile;

/**
* The translation object parsed from the source file.
*/
private readonly translationsNode: ts.ObjectLiteralExpression;

/**
* Translator module to perform translations.
*/
Expand All @@ -121,17 +122,17 @@ class TranslationGenerator {
/**
* Paths to add (don't exist in target file yet).
*/
private readonly pathsToAdd: Set<TranslationPaths>;
private readonly pathsToAdd: Set<string>;

/**
* Paths to modify (exist in target file and need retranslation).
*/
private readonly pathsToModify: Set<TranslationPaths>;
private readonly pathsToModify: Set<string>;

/**
* Paths to remove (only populated when using compareRef).
*/
private readonly pathsToRemove: Set<TranslationPaths>;
private readonly pathsToRemove: Set<string>;

/**
* Should we print verbose logs?
Expand Down Expand Up @@ -190,7 +191,7 @@ class TranslationGenerator {
};
paths: {
description: string;
parse: (val: string) => Set<TranslationPaths>;
parse: (val: string) => Set<string>;
supersedes: string[];
required: false;
};
Expand All @@ -214,6 +215,9 @@ class TranslationGenerator {
constructor() {
this.languagesDir = process.env.LANGUAGES_DIR ?? path.join(__dirname, '../src/languages');
const enSourceFile = path.join(this.languagesDir, 'en.ts');
const sourceCode = fs.readFileSync(enSourceFile, 'utf8');
this.sourceFile = ts.createSourceFile(enSourceFile, sourceCode, ts.ScriptTarget.Latest, true);
this.translationsNode = this.findTranslationsNode(this.sourceFile);

/* eslint-disable @typescript-eslint/naming-convention */
this.cli = new CLI({
Expand Down Expand Up @@ -268,13 +272,13 @@ class TranslationGenerator {
},
paths: {
description: 'Comma-separated list of specific translation paths to retranslate (e.g., "common.save,errors.generic").',
parse: (val: string): Set<TranslationPaths> => {
parse: (val: string): Set<string> => {
const rawPaths = val.split(',').map((translationPath) => translationPath.trim());
const validatedPaths = new Set<TranslationPaths>();
const validatedPaths = new Set<string>();
const invalidPaths: string[] = [];
for (const rawPath of rawPaths) {
if (get(en, rawPath)) {
validatedPaths.add(rawPath as TranslationPaths);
if (TSCompilerUtils.objectHas(this.translationsNode, rawPath)) {
Comment thread
roryabraham marked this conversation as resolved.
Outdated
validatedPaths.add(rawPath);
} else {
invalidPaths.push(rawPath);
}
Expand All @@ -296,19 +300,16 @@ class TranslationGenerator {
this.prNumber = this.cli.namedArgs['pr-number'];
this.diffBase = this.compareRef;
this.useGitHubAPI = false;
this.pathsToAdd = new Set<TranslationPaths>();
this.pathsToModify = this.cli.namedArgs.paths ?? new Set<TranslationPaths>();
this.pathsToRemove = new Set<TranslationPaths>();
this.pathsToAdd = new Set<string>();
this.pathsToModify = this.cli.namedArgs.paths ?? new Set<string>();
this.pathsToRemove = new Set<string>();
this.verbose = this.cli.flags.verbose;
this.isIncremental = this.pathsToModify.size > 0 || !!this.compareRef || !!this.prNumber;

if (this.prNumber && !process.env.GITHUB_TOKEN) {
throw new Error('GITHUB_TOKEN environment variable is required when using --pr-number');
}

const sourceCode = fs.readFileSync(enSourceFile, 'utf8');
this.sourceFile = ts.createSourceFile(enSourceFile, sourceCode, ts.ScriptTarget.Latest, true);

if (this.cli.flags['dry-run']) {
console.log('🍸 Dry run enabled');
this.translator = new DummyTranslator();
Expand Down Expand Up @@ -1029,8 +1030,6 @@ class TranslationGenerator {
}

// Find the main translation object in en.ts
const translationsNode = this.findTranslationsNode(this.sourceFile);

// Get changed lines from the diff
const changedLines = diffResult.files.at(0);
if (!changedLines) {
Expand All @@ -1042,7 +1041,7 @@ class TranslationGenerator {
}

// Traverse current en.ts for added and modified paths
this.extractPathsFromChangedLines(translationsNode, new Set([...changedLines.addedLines, ...changedLines.modifiedLines]), changedLines.removedLines);
this.extractPathsFromChangedLines(this.translationsNode, new Set([...changedLines.addedLines, ...changedLines.modifiedLines]), changedLines.removedLines);

// For removed paths, we need to traverse the old version of en.ts
if (changedLines.removedLines.size > 0) {
Expand All @@ -1054,7 +1053,7 @@ class TranslationGenerator {
for (const removedPath of this.pathsToRemove) {
if (this.pathsToModify.has(removedPath)) {
this.pathsToRemove.delete(removedPath); // It's modified, not removed
} else if (get(en, removedPath) !== undefined) {
} else if (TSCompilerUtils.objectHas(this.translationsNode, removedPath)) {
// Path still exists in en.ts, so it's modified not removed
this.pathsToRemove.delete(removedPath);
this.pathsToModify.add(removedPath);
Expand Down Expand Up @@ -1144,10 +1143,10 @@ class TranslationGenerator {
if (dotPath) {
if (isOldVersion && (isOnRemovedLine || hasContextChange)) {
// When traversing old version, removed lines indicate paths to remove
this.pathsToRemove.add(dotPath as TranslationPaths);
this.pathsToRemove.add(dotPath);
} else if (!isOldVersion && (isOnAddedLine || hasContextChange)) {
// When traversing current version, added lines indicate paths to modify/add
this.pathsToModify.add(dotPath as TranslationPaths);
this.pathsToModify.add(dotPath);
}

if (this.verbose) {
Expand Down Expand Up @@ -1220,8 +1219,8 @@ class TranslationGenerator {
private extractTranslatedNodes(sourceFile: ts.SourceFile, translatedCodeMap: Map<string, string>): void {
const visitWithPath = (node: ts.Node, currentPath = '') => {
// Only extract code strings for exact paths in our sets (not hierarchical matches)
const isAddedPath = this.pathsToAdd.has(currentPath as TranslationPaths);
const isModifiedPath = this.pathsToModify.has(currentPath as TranslationPaths);
const isAddedPath = this.pathsToAdd.has(currentPath);
const isModifiedPath = this.pathsToModify.has(currentPath);

if ((isAddedPath || isModifiedPath) && ts.isPropertyAssignment(node)) {
if (!node.initializer) {
Expand Down Expand Up @@ -1422,7 +1421,7 @@ class TranslationGenerator {
}

// Check if this path should be removed
if (currentPath && this.pathsToRemove.has(currentPath as TranslationPaths)) {
if (currentPath && this.pathsToRemove.has(currentPath)) {
return {action: TransformerAction.Remove};
}

Expand Down
24 changes: 0 additions & 24 deletions scripts/tsconfig.json

This file was deleted.

13 changes: 7 additions & 6 deletions scripts/typecheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Type-check the repo with the TypeScript 7 native compiler.
*
* bun scripts/typecheck.ts -> check every project CI gates on
* bun scripts/typecheck.ts evals -> check just the named project directories
* bun scripts/typecheck.ts tsconfig.bun.json -> check just the named project
*
* Every project is checked even after one fails, so a single run reports every error in the repo.
*/
Expand All @@ -18,14 +18,14 @@ const projectRoot = `${import.meta.dir}/..`;
// Invoke that bin by path so a leftover `.bin/tsc` from `@typescript/old` cannot win.
const tsc = `${projectRoot}/node_modules/typescript/bin/tsc`;

/** Project directories, relative to the repo root, that `npm run typecheck` and CI check. */
const DEFAULT_PROJECTS = ['.', 'tests/tooling', 'server', 'server/victory-chart-renderer', 'scripts'];
/** TypeScript projects, relative to the repo root, that `npm run typecheck` and CI check. */
const DEFAULT_PROJECTS = ['tsconfig.json', 'tsconfig.bun.json', 'tsconfig.node.json', 'server/victory-chart-renderer/tsconfig.json'];

const cli = new CLI({
positionalArgs: [
{
name: 'projects',
description: 'Project directories to type-check, relative to the repo root (default: the five CI-gated projects)',
description: 'tsconfig paths to type-check, relative to the repo root',
variadic: true,
default: DEFAULT_PROJECTS,
},
Expand All @@ -36,13 +36,14 @@ const {projects} = cli.positionalArgs;

const failed: string[] = [];
for (const project of projects) {
const tsconfig = `${project}/tsconfig.json`;
const tsconfig = project.endsWith('.json') ? project : `${project}/tsconfig.json`;
console.log(`\nType checking ${tsconfig}...`);

// The build info file lets repeat runs skip unchanged projects. It is named apart from the
// `tsconfig.tsbuildinfo` that `incremental` defaults to so that running TypeScript 6 by hand in
// the same worktree can't feed it a build info file written by a different compiler.
const result = await $`${tsc} --noEmit --incremental -p ${tsconfig} --tsBuildInfoFile ${project}/tsconfig.ts7.tsbuildinfo`.cwd(projectRoot).nothrow();
const tsBuildInfoFile = `${tsconfig.replace(/\.json$/, '')}.ts7.tsbuildinfo`;
const result = await $`${tsc} --noEmit --incremental -p ${tsconfig} --tsBuildInfoFile ${tsBuildInfoFile}`.cwd(projectRoot).nothrow();
if (result.exitCode !== 0) {
failed.push(tsconfig);
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/utils/Git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function execSync(command: string, options?: ExecSyncOptions) {
}

const IS_CI = process.env.CI === 'true';
const GITHUB_BASE_REF = process.env.GITHUB_BASE_REF as string | undefined;
const GITHUB_BASE_REF = process.env.GITHUB_BASE_REF;

/**
* Represents a single changed line in a git diff.
Expand Down
Loading
Loading