Workspace
- Package manager: pnpm 10 (
packageManager: pnpm@10.28.2). Node 20 or newer. CI uses Node 24. - Workspaces in
pnpm-workspace.yaml:apps/*,packages/*,tooling/*. - Dependency versions are pinned once in the
catalog:block ofpnpm-workspace.yamland referenced as"catalog:"in eachpackage.json. Change a version there, not per package. - Add dependencies with
pnpm add, for examplepnpm --filter saas add some-package. Do not editpackage.jsonby hand. - Turborepo (
turbo.json) runs tasks.globalEnvlists the variables that affect the build cache:NODE_ENV,NEXT_PUBLIC_SITE_URL,NEXT_PUBLIC_SAAS_URL,AUTH_TRUSTED_ORIGINS,CORE_API_URL,CORE_API_SERVICE_ID,SERVICE_AUTH_SECRETand two Next flags.
Root scripts
Run fromfrontend/.
dotenv -c loads .env and its cascade (.env.local and so on) from the repo root before running Turborepo.
There are no db:* scripts. The Prisma package is gone from this repo.
App scripts
Production builds use webpack (
--webpack). next.config.ts also sets a turbopack.root, which applies to next dev.
Each package (ui, auth, i18n, payments, logs, notifications, utils) has its own type-check script (tsc --noEmit), but the root pnpm type-check filters to saas. Package code is still checked there, because saas imports it as source.
Type checking
next typegen generates the route types first, so typed params and links are current before tsc runs. Run pnpm type-check before every push. It is the slowest check, so run it once, not in parallel with other heavy jobs.
tooling/typescript (@repo/tsconfig) holds three presets:
noUnusedLocals and noUnusedParameters are off. oxlint reports unused code instead.
Linting
oxlint, configured in.oxlintrc.json:
oxlint-tsgolint, a root dependency. There is no ESLint in this repo.
Lint one file while working: pnpm exec oxlint path/to/file.tsx.
Disable comments use the oxlint form, for example // oxlint-disable-line eslint-plugin-react-hooks/exhaustive-deps.
Formatting
oxfmt, configured in.oxfmtrc.json:
- 100 columns, double quotes, semicolons, parentheses around arrow parameters.
- Imports are sorted into groups: builtin, external, internal and subpath, then parent, sibling and index, then styles.
- Tailwind classes are sorted in
classNameattributes and insidecn(...), usingtooling/tailwind/theme.cssas the stylesheet. package.jsonkeys are sorted.- JSON files get no trailing commas.
Git hooks
Husky, installed by theprepare script.
lint-staged
.lintstagedrc.json:
pre-push
The script collects the files that differ between the upstream andHEAD (falling back to the last commit), keeps script files, and skips any file that also has uncommitted changes. Then:
node tools/check-theme-tokens.mjson everything. A failure stops the push.pnpm exec oxlinton the collected files, if any.
frontend/docs/CI_AND_HOOKS.md says pre-push runs format:check, lint, type-check and check:core-api. The hook in the repo does not. Type-check and the core API boundary check run only in CI.Commit messages
commitlint.config.cjs extends the conventional config:
- Type is one of
feat,fix,docs,refactor,perf,test,build,ci,chore,revert. - Subject is required, in lower case or sentence case.
- Header at most 100 characters. Body lines over 120 characters produce a warning.
CI
.github/workflows/ci.yml runs on pull requests and on pushes to any branch except dev and main.
The job sets placeholder values for
DATABASE_URL and BETTER_AUTH_SECRET. No database is reached.
format:check, check:theme and Playwright are not CI steps.
Deploy workflows are described in Build and deploy.
Guard scripts in tools/
The workspace root (one level above
frontend) has tools/ensure-core-api.sh, used by pnpm dev. With --start it launches the backend in the background, writes its log to a temp file and waits up to 60 seconds for the health endpoint. Without the flag it prints how to start the API and exits non zero.
Shared Tailwind config
tooling/tailwind is the package @repo/tailwind-config. It ships two CSS files and no JavaScript config. Tailwind 4 is configured in CSS.
apps/saas/postcss.config.cjs wires @tailwindcss/postcss.