diff --git a/.bun-version b/.bun-version new file mode 100644 index 000000000000..085c0f2666ac --- /dev/null +++ b/.bun-version @@ -0,0 +1 @@ +1.3.14 diff --git a/.gitignore b/.gitignore index 63c8c7c6ee4c..9b179a0dc0d7 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,7 @@ tests/e2e/results/ # Typescript tsconfig.tsbuildinfo tsconfig.tsgo.tsbuildinfo +**/tsconfig.tsgo.tsbuildinfo # Mock-github /repo/ @@ -172,3 +173,6 @@ agent-device-output/ # cspell cache .cspellcache + +# Server CLI build artifacts +server/*/dist/ diff --git a/config/eslint/eslint.config.mjs b/config/eslint/eslint.config.mjs index 25500517ee64..3fcb2ccdfa32 100644 --- a/config/eslint/eslint.config.mjs +++ b/config/eslint/eslint.config.mjs @@ -631,7 +631,7 @@ const config = defineConfig([ }, { - files: ['.github/**/*', 'scripts/**/*'], + files: ['.github/**/*', 'scripts/**/*', 'server/**/*'], rules: { // For all these Node.js scripts, we do not want to disable `console` statements 'no-console': 'off', @@ -639,7 +639,7 @@ const config = defineConfig([ }, { - files: ['.github/**/*', 'scripts/**/*', 'tests/**/*'], + files: ['.github/**/*', 'scripts/**/*', 'server/**/*', 'tests/**/*'], rules: { 'no-await-in-loop': 'off', 'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'], @@ -735,6 +735,15 @@ const config = defineConfig([ }, }, + { + files: ['server/**/*.ts'], + languageOptions: { + parserOptions: { + project: path.resolve(projectRoot, 'server/tsconfig.json'), + }, + }, + }, + globalIgnores([ '!**/.storybook', '!**/.github', @@ -743,6 +752,7 @@ const config = defineConfig([ '**/*.config.mjs', '**/node_modules/**/*', '**/dist/**/*', + 'server/**/dist/**', '.eslint-reports/**/*', 'android/**/build/**/*', 'docs/vendor/**/*', diff --git a/package-lock.json b/package-lock.json index 9c9a7d3928c4..5676c6f4556a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -193,6 +193,7 @@ "@testing-library/react-native": "13.2.0", "@trivago/prettier-plugin-sort-imports": "^6.0.2", "@types/base-64": "^1.0.2", + "@types/bun": "^1.3.14", "@types/canvas-size": "^1.2.2", "@types/concurrently": "^7.0.3", "@types/d3-scale": "^4.0.9", @@ -299,6 +300,7 @@ "workbox-webpack-plugin": "^7.4.0" }, "engines": { + "bun": "1.3.14", "node": "20.20.0", "npm": "10.8.2" } @@ -16731,6 +16733,16 @@ "@types/node": "*" } }, + "node_modules/@types/bun": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.14.tgz", + "integrity": "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.3.14" + } + }, "node_modules/@types/canvas-size": { "version": "1.2.2", "dev": true, @@ -19606,6 +19618,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bun-types": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.14.tgz", + "integrity": "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "dev": true, diff --git a/package.json b/package.json index 3f71daff5564..e85eb21727b3 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "test:verbose": "TZ=utc NODE_OPTIONS=\"--experimental-vm-modules --max_old_space_size=4096\" JEST_VERBOSE=true jest", "test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand", "perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure", - "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc", - "typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo", + "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc && tsc -p server/tsconfig.json", + "typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/tsconfig.json --incremental --tsBuildInfoFile server/tsconfig.tsgo.tsbuildinfo", "lint": "./scripts/lint.sh", "lint-changed": "./scripts/lintChanged.sh", "lint-watch": "onchange '**/*.{js,jsx,ts,tsx,mjs,cjs}' -- ./scripts/lint.sh {{changed}}", @@ -71,7 +71,12 @@ "generate-autocomplete-parser": "peggy --format es -o src/libs/SearchParser/autocompleteParser.js src/libs/SearchParser/autocompleteParser.peggy src/libs/SearchParser/baseRules.peggy && ./scripts/parser-workletization.sh src/libs/SearchParser/autocompleteParser.js", "web:dist": "concurrently \"npm:web-proxy\" \"http-server ./dist --cors --port 8080 -P http://localhost:9000\"", "octokit": "cd scripts && npx ts-node -i -e \"$(cat ./octokit.ts)\"", - "compress-svg": "npx ts-node scripts/compressSvg.ts --dir assets/images && npx ts-node scripts/compressSvg.ts --dir docs/assets/images" + "compress-svg": "npx ts-node scripts/compressSvg.ts --dir assets/images && npx ts-node scripts/compressSvg.ts --dir docs/assets/images", + "server:vcr:dev": "bun run server/victory-chart-renderer/src/cli.ts", + "server:vcr:test": "bun test server/victory-chart-renderer/tests", + "server:vcr:build:linux": "bun build --compile --target=bun-linux-x64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-x64", + "server:vcr:build:linux-arm": "bun build --compile --target=bun-linux-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-arm64", + "server:vcr:build:macos": "bun build --compile --target=bun-darwin-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-darwin-arm64" }, "dependencies": { "@babel/runtime": "^7.25.0", @@ -257,6 +262,7 @@ "@testing-library/react-native": "13.2.0", "@trivago/prettier-plugin-sort-imports": "^6.0.2", "@types/base-64": "^1.0.2", + "@types/bun": "^1.3.14", "@types/canvas-size": "^1.2.2", "@types/concurrently": "^7.0.3", "@types/d3-scale": "^4.0.9", @@ -386,6 +392,7 @@ "react": "19.2.0", "react-dom": "19.2.0", "eslint-config-airbnb-typescript": { + "@types/bun": "^1.3.14", "@typescript-eslint/eslint-plugin": "^8.45.0", "@typescript-eslint/parser": "^8.45.0", "eslint": "^9.36.0", @@ -429,6 +436,7 @@ } }, "engines": { + "bun": "1.3.14", "node": "20.20.0", "npm": "10.8.2" } diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 000000000000..03533aa786e5 --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["@types/bun"] + }, + "include": ["victory-chart-renderer/src", "victory-chart-renderer/tests"] +} diff --git a/server/victory-chart-renderer/README.md b/server/victory-chart-renderer/README.md new file mode 100644 index 000000000000..8de24f6ddde6 --- /dev/null +++ b/server/victory-chart-renderer/README.md @@ -0,0 +1,40 @@ +# Victory Chart Renderer + +Standalone Bun CLI that will render Expensify chart XML to PNG using the same chart code as the App. + +## Project layout + +- `src/` — CLI source +- `tests/` — Bun integration tests (`bun:test`) +- `dist/` — compiled binaries (gitignored) + +## Development + +From the App repository root: + +```bash +npm run server:vcr:dev /tmp/out.txt +``` + +## Tests + +From the App repository root: + +```bash +npm run server:vcr:test +``` + +Or from this directory: + +```bash +bun test +``` + +## Compiled binaries + +```bash +npm run server:vcr:build:linux +npm run server:vcr:build:macos +``` + +Binaries are written to `server/victory-chart-renderer/dist/` (gitignored). diff --git a/server/victory-chart-renderer/src/cli.ts b/server/victory-chart-renderer/src/cli.ts new file mode 100644 index 000000000000..59adda34eb26 --- /dev/null +++ b/server/victory-chart-renderer/src/cli.ts @@ -0,0 +1,12 @@ +import CLI from '@scripts/utils/CLI'; + +const cli = new CLI({ + positionalArgs: [ + { + name: 'outPath', + description: 'Output file path for the scaffold message', + }, + ], +}); + +await Bun.write(cli.positionalArgs.outPath, 'victory-chart-renderer scaffolded\n'); diff --git a/server/victory-chart-renderer/tests/cli.test.ts b/server/victory-chart-renderer/tests/cli.test.ts new file mode 100644 index 000000000000..af8ade0c59ca --- /dev/null +++ b/server/victory-chart-renderer/tests/cli.test.ts @@ -0,0 +1,25 @@ +import {expect, test} from 'bun:test'; +import {spawnSync} from 'node:child_process'; +import {mkdtempSync, readFileSync, rmSync} from 'node:fs'; +import {tmpdir} from 'node:os'; +import {join} from 'node:path'; + +const projectRoot = join(import.meta.dir, '../../..'); +const cliPath = join(projectRoot, 'server/victory-chart-renderer/src/cli.ts'); + +test('CLI writes the scaffold message to outPath', () => { + const tempDir = mkdtempSync(join(tmpdir(), 'vcr-cli-test-')); + const outPath = join(tempDir, 'out.txt'); + + try { + const result = spawnSync('bun', ['run', cliPath, outPath], { + cwd: projectRoot, + encoding: 'utf8', + }); + + expect(result.status).toBe(0); + expect(readFileSync(outPath, 'utf8')).toBe('victory-chart-renderer scaffolded\n'); + } finally { + rmSync(tempDir, {recursive: true, force: true}); + } +});