chore: clear all Dependabot advisories and add CI (#16)

* build(deps): remove unused frameworks and clear all advisories

The v0 scaffold declared @remix-run/react, @sveltejs/kit, svelte, vue and
vue-router, none of which are imported anywhere in the app. Removing them
drops react-router, react-router-dom, turbo-stream, cookie, vite and their
trees, clearing 13 advisories outright.

Replace xlsx with write-excel-file. The npm release of xlsx is abandoned at
0.18.5, so its prototype pollution and ReDoS advisories have no registry fix,
and SheetJS only ships newer builds from its own CDN as a tarball with no
integrity hash, which pnpm rejects. write-excel-file is maintained, carries no
advisories, and pulls in only fflate. The export writes the same eight columns
in the same order to a sheet named "Exchange Rates", with every value still
written as a string. It also cuts the route's first-load JS from 264 kB to
163 kB.

Add pnpm overrides forcing the lowest release that closes every advisory
affecting the previously resolved version of each remaining transitive dep.
postcss is pinned exactly because pnpm's minimumReleaseAge policy rejects
releases younger than 24h during CI installs.

* ci: add typecheck and build workflow

The repository had no workflows. Type errors are skipped during builds via
next.config.mjs, so check them here where a regression fails the run.
Installs use --frozen-lockfile so dependency overrides cannot silently stop
applying.
This commit is contained in:
tiennm99 authored and GitHub committed 2026-07-25 21:18:14 +07:00
1 parent 5583743cc3
commit b74c97c74f
5 files changed
+498 -2002

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Typecheck and build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 11
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
# Fails if pnpm-lock.yaml drifts from package.json, so dependency
# overrides cannot silently stop applying.
- name: Install dependencies
run: pnpm install --frozen-lockfile
# next.config.mjs skips type errors during builds, so check types here
# where a regression actually fails the run.
- name: Typecheck
run: pnpm exec tsc --noEmit
- name: Build
run: pnpm run build
+27 -33
View File
@@ -8,7 +8,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Download, Loader2 } from "lucide-react"
import * as XLSX from "xlsx"
import writeXlsxFile, { type Column } from "write-excel-file/browser"
interface ExchangeRateData {
date: string
@@ -21,6 +21,19 @@ interface ExchangeRateData {
inputDate: string
}
// Mirrors the column order of the CSV export. Every rate arrives from the API
// as a string and is written as-is, so no cell is coerced to a number.
const XLSX_COLUMNS: Column<ExchangeRateData>[] = [
{ header: "Date", cell: (row) => row.date },
{ header: "Bank", cell: (row) => row.bank },
{ header: "Currency", cell: (row) => row.currency },
{ header: "Ask Rate", cell: (row) => row.askRate },
{ header: "Bid Rate CK", cell: (row) => row.bidRateCK },
{ header: "Bid Rate TM", cell: (row) => row.bidRateTM },
{ header: "Ask Rate TM", cell: (row) => row.askRateTM },
{ header: "Input Date", cell: (row) => row.inputDate },
]
type BankType = "techcombank" | "bidv"
// Currency options for each bank
@@ -257,40 +270,21 @@ export default function ExchangeRateExporter() {
document.body.removeChild(link)
}
const exportToXLSX = () => {
const exportToXLSX = async () => {
if (data.length === 0) return
const worksheet = XLSX.utils.json_to_sheet(
data.map((row) => ({
Date: row.date,
Bank: row.bank,
Currency: row.currency,
"Ask Rate": row.askRate,
"Bid Rate CK": row.bidRateCK,
"Bid Rate TM": row.bidRateTM,
"Ask Rate TM": row.askRateTM,
"Input Date": row.inputDate,
})),
)
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, "Exchange Rates")
const arrayBuffer = XLSX.write(workbook, {
bookType: "xlsx",
type: "array",
})
const blob = new Blob([arrayBuffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
})
const url = URL.createObjectURL(blob)
const link = document.createElement("a")
link.href = url
link.download = `exchange_rates_${bank}_${currency.replace(/[^a-zA-Z0-9]/g, "_")}_${startDate}_to_${endDate}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
// toFile triggers the browser download, so no manual object URL is needed.
try {
await writeXlsxFile(data, {
columns: XLSX_COLUMNS,
sheet: "Exchange Rates",
}).toFile(
`exchange_rates_${bank}_${currency.replace(/[^a-zA-Z0-9]/g, "_")}_${startDate}_to_${endDate}.xlsx`,
)
} catch (error) {
setError("An error occurred while exporting to XLSX")
console.error(error)
}
}
const formatNumber = (value: string) => {
+2 -7
View File
@@ -37,8 +37,6 @@
"@radix-ui/react-toggle": "1.1.1",
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"@remix-run/react": "latest",
"@sveltejs/kit": "latest",
"@vercel/analytics": "latest",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.1",
@@ -57,20 +55,17 @@
"react-resizable-panels": "^2.1.7",
"recharts": "2.15.0",
"sonner": "^1.7.1",
"svelte": "latest",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"vaul": "^0.9.6",
"vue": "latest",
"vue-router": "latest",
"xlsx": "latest",
"write-excel-file": "^4.1.1",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "^22",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8.5",
"postcss": "8.5.22",
"tailwindcss": "^3.4.17",
"typescript": "^5"
}
+406 -1962
View File
File diff suppressed because it is too large. Load diff
+22
View File
@@ -0,0 +1,22 @@
# Force patched versions of transitive dependencies that carry known advisories.
# Each entry is the lowest release that closes every advisory affecting the
# version previously resolved in pnpm-lock.yaml.
overrides:
brace-expansion: ^5.0.8
glob: ^10.5.0
lodash: ^4.18.1
minimatch: ^9.0.7
picomatch: ^2.3.2
# Pinned exactly rather than with a caret: pnpm's minimumReleaseAge policy
# rejects releases younger than 24h in CI, and a caret would keep drifting
# onto whatever postcss published today. 8.5.22 clears every advisory,
# which need 8.5.18 at the highest.
postcss: 8.5.22
sharp: ^0.35.3
yaml: ^2.8.3
# next lists sharp as an optional dependency for image optimization, which this
# app disables (images.unoptimized in next.config.mjs). Skipping its build
# script keeps installs hermetic and free of native compilation.
allowBuilds:
sharp: false