Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Commits that only reformat code. To have `git blame` skip them, run
# git config blame.ignoreRevsFile .git-blame-ignore-revs

# Format the website and calculator with Prettier
e8d04db611dc231e6c27fe178893f55e3bfc0435
20 changes: 18 additions & 2 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,30 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check formatting with Prettier
- name: Check formatting with Prettier (app)
working-directory: ./app
run: bun run prettier

- name: Run ESLint
- name: Run ESLint (app)
working-directory: ./app
run: bun run eslint

- name: Check formatting with Prettier (website)
working-directory: ./website
run: bun run format:check

- name: Run ESLint (website)
working-directory: ./website
run: bun run lint

- name: Check formatting with Prettier (calculator)
working-directory: ./calculator-app
run: bun run format:check

- name: Run ESLint (calculator)
working-directory: ./calculator-app
run: bun run lint

typecheck:
name: Type checking
runs-on: ubuntu-latest
Expand Down
34 changes: 31 additions & 3 deletions .github/workflows/push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,30 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check formatting with Prettier
- name: Check formatting with Prettier (app)
working-directory: ./app
run: bun run prettier

- name: Run ESLint
- name: Run ESLint (app)
working-directory: ./app
run: bun run eslint

- name: Check formatting with Prettier (website)
working-directory: ./website
run: bun run format:check

- name: Run ESLint (website)
working-directory: ./website
run: bun run lint

- name: Check formatting with Prettier (calculator)
working-directory: ./calculator-app
run: bun run format:check

- name: Run ESLint (calculator)
working-directory: ./calculator-app
run: bun run lint

typecheck:
name: Type checking
runs-on: ubuntu-latest
Expand All @@ -42,10 +58,18 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run TypeScript type checks
- name: Run TypeScript type checks (app)
working-directory: ./app
run: bun run typecheck

- name: Run TypeScript type checks (website)
working-directory: ./website
run: bun run typecheck

- name: Typecheck calculator
working-directory: ./calculator-app
run: bun run typecheck

test:
name: Tests
runs-on: ubuntu-latest
Expand All @@ -64,6 +88,10 @@ jobs:
working-directory: ./app
run: bun run test

- name: Run website tests
working-directory: ./website
run: bun run test

check-embeds:
name: Check embedded site URLs
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ test:

lint:
cd app && bun run lint
cd website && bun run lint
cd calculator-app && bun run lint

format:
cd app && bun run prettier:write
cd website && bun run format
cd calculator-app && bun run format

clean:
rm -rf website/.next calculator-app/.next app/node_modules node_modules packages/*/node_modules
Expand Down
3 changes: 3 additions & 0 deletions app/.prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */
const config = {
// Set explicitly so an .editorconfig in a parent folder can't change them.
useTabs: false,
tabWidth: 2,
endOfLine: 'lf',
printWidth: 100,
singleQuote: true,
Expand Down
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build-parameter-dependencies": "python3 scripts/build-parameter-dependency-map.py",
"typecheck": "tsc --noEmit",
"lint": "bun run eslint && bun run stylelint",
"eslint": "eslint . --cache",
"eslint": "eslint . --cache --max-warnings 0",
"stylelint": "stylelint '**/*.css' --cache",
"prettier": "prettier --check \"**/*.{ts,tsx}\"",
"prettier:write": "prettier --write \"**/*.{ts,tsx}\"",
Expand Down
6 changes: 6 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions calculator-app/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Symlinks into app/public, which holds built assets.
public/
58 changes: 58 additions & 0 deletions calculator-app/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { FlatCompat } from "@eslint/eslintrc";
import { defineConfig, globalIgnores } from "eslint/config";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// @next/eslint-plugin-next reads settings.next.rootDir as a glob. Brackets
// make (, ), {, }, |, + and [ literal, so a parent folder named like
// "Projects (1)" doesn't stop the path matching. When rootDir matches
// nothing, @next/next/no-html-link-for-pages switches itself off without
// failing lint.
const rootDir = __dirname.replace(/[(){}|+[]/g, "[$&]");

const compat = new FlatCompat({
baseDirectory: __dirname,
});

// The Next.js rules the website uses. ESLint lints only this folder; the
// shared code it imports from ../app/src is linted by app/'s own config.
export default defineConfig([
...compat.config({
extends: ["next/core-web-vitals", "next/typescript"],
settings: {
next: {
// Absolute, so the rule finds src/app whichever folder ESLint runs
// from.
rootDir,
},
},
}),
{
// The calculator's routes are written in app/'s TypeScript style, so
// they follow app/'s rules for `any` and unused variables.
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "none",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
ignoreRestSiblings: true,
},
],
},
},
globalIgnores([
".next/**",
"out/**",
"build/**",
// A link to app/public, which holds built assets.
"public/**",
"next-env.d.ts",
]),
]);
3 changes: 1 addition & 2 deletions calculator-app/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ const nextConfig: NextConfig = {
];
},

webpack: (config, { dev }) => {
webpack: (config, { dev, webpack }) => {
// Polyfill import.meta.env for shared code written for Vite.
// These are replaced at build time — no runtime cost.
const webpack = require("webpack");
config.plugins.push(
new webpack.DefinePlugin({
"import.meta.env.DEV": JSON.stringify(dev),
Expand Down
8 changes: 8 additions & 0 deletions calculator-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"dev": "sh -c 'next dev --port ${PORT:-3001}'",
"build": "next build",
"start": "next start",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"format": "prettier --write \"**/*.{ts,tsx,mjs,css}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,mjs,css}\"",
"typecheck": "tsc --noEmit"
},
"dependencies": {
Expand Down Expand Up @@ -38,13 +42,17 @@
"tailwindcss": "^4.2.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4.1.8",
"@types/node": "^22.15.0",
"@types/react": "^19.1.0",
"@types/react-dom": "^19.1.0",
"@types/react-plotly.js": "^2.6.3",
"@types/react-simple-maps": "^3.0.6",
"eslint": "^9.29.0",
"eslint-config-next": "^15.3.3",
"postcss": "^8.5.4",
"prettier": "^3.5.3",
"typescript": "^5.8.0"
}
}
3 changes: 2 additions & 1 deletion calculator-app/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default {
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
15 changes: 15 additions & 0 deletions calculator-app/prettier.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// The calculator uses Prettier's default style; app/ has its own config.
// Having a config file here stops Prettier looking for one in parent
// folders. The options below are Prettier's defaults, written out because
// an .editorconfig in a parent folder (one outside this repo, such as a
// home folder) can otherwise change them.
/** @type {import("prettier").Config} */
const config = {
useTabs: false,
tabWidth: 2,
printWidth: 80,
singleQuote: false,
endOfLine: "lf",
};

export default config;
8 changes: 7 additions & 1 deletion calculator-app/src/app/[countryId]/(calculator)/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ export default function CountryError({
>
Try again
</button>
{/* A full page load rather than a client-side navigation, so the
client state that may have caused the error is discarded. */}
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a
href="/us/reports"
className="tw:px-4 tw:py-2 tw:text-sm tw:font-medium tw:border tw:rounded-md"
style={{ borderColor: colors.border.light, color: colors.text.primary }}
style={{
borderColor: colors.border.light,
color: colors.text.primary,
}}
>
Go to reports
</a>
Expand Down
9 changes: 8 additions & 1 deletion calculator-app/src/app/[countryId]/(calculator)/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
export default function CountryLoading() {
return (
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh" }}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100vh",
}}
>
<div
style={{
width: 32,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import FlagshipGate from "../../FlagshipGate";
// Client-only: the chart stack (plotly) cannot render during SSR, and
// the page's data (localStorage provenance, calculation polling) is
// browser-side anyway.
const FlagshipReportPage = dynamic(() => import("@/pages/flagship/Report.page"), {
ssr: false,
});
const FlagshipReportPage = dynamic(
() => import("@/pages/flagship/Report.page"),
{
ssr: false,
},
);

export default function FlagshipReportRoute({
params,
Expand Down
8 changes: 7 additions & 1 deletion calculator-app/src/app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ export default function GlobalError({
>
Try again
</button>
{/* A full page load rather than a client-side navigation, so the
client state that may have caused the error is discarded. */}
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
<a
href="/us/reports"
className="tw:px-4 tw:py-2 tw:text-sm tw:font-medium tw:border tw:rounded-md"
style={{ borderColor: colors.border.light, color: colors.text.primary }}
style={{
borderColor: colors.border.light,
color: colors.text.primary,
}}
>
Go to reports
</a>
Expand Down
6 changes: 3 additions & 3 deletions docs/engineering/skills/repository-guidance.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,6 @@ No route embeds a GitHub Pages site as a whole page any more: the 2025 year in r

## Before committing

1. Run `cd app && bun run prettier -- --write .` to format
2. Run `bun run lint` to check for errors
3. CI uses `--max-warnings 0` so fix all warnings
1. Run `make format` to format `app/`, `website/` and `calculator-app/` with Prettier. `app/` has its own Prettier config; `website/` and `calculator-app/` use Prettier's defaults.
2. Run `make lint` to run ESLint (and Stylelint in `app/`) in all three.
3. CI fails on ESLint warnings as well as errors (`--max-warnings 0`), and on files Prettier would reformat, so fix all of them.
2 changes: 2 additions & 0 deletions website/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Symlinks into app/public, which holds built assets.
public/
Loading
Loading