GenerateSaaS

Development

The everyday workflow - root pnpm + Turborepo scripts, the enforced code style, the pre-commit translate hook, and the workspace-safe way to add dependencies.

Every task runs from the repo root through pnpm + Turborepo, which fans it out to each package and app that defines a matching script. Prettier, ESLint, and strict TypeScript are enforced.

Dev scripts

CommandWhat it does
pnpm devStart every app in watch mode (--continue keeps the rest running if one exits). Fullstack excludes the standalone apps/backend, since the frontend serves the API in-process
pnpm buildBuild every app and package
pnpm startServe the production build
pnpm lintESLint across the workspace
pnpm formatPrettier write across every source, config, and markdown file
pnpm check-typestsc --noEmit, no output emitted
pnpm testUnit tests. 0 failed, 0 skipped, with no .env, no Docker and no network - see testing

Turbo caches build, lint, and check-types; dev and test are uncached. Run pnpm lint and pnpm check-types before pushing - CI runs the same scripts.

pnpm dev writes nothing into your repo and prints no cache stack traces: Next's agent-rules file writer is off (agentRules: false), and the Redis client logs one line per state change rather than a stack per retry. A Redis that is down is a warning, never a wall of noise.

Side processes, run alongside pnpm dev:

CommandWhat it does
pnpm dev:mailEmail-template preview server on port 3030
pnpm dev:inngestInngest dev server for background jobs (UI on 8288)
pnpm mastra:studioMastra Studio for inspecting the AI assistant and its memory (UI on 4111)
pnpm drizzle:studioDrizzle Studio for browsing and editing your database tables
pnpm infra / pnpm infra:stopStart/stop the local Docker services (present only if you chose Docker-backed infra at init)

Code style

Formatting is owned by Prettier; TypeScript is strict - no any, as any, or unsafe casts. The committed .prettierrc:

.prettierrc
{
  "useTabs": true,          // indent with tabs
  "tabWidth": 2,            // tab renders as 2 spaces
  "printWidth": 100,        // 100-character line width
  "trailingComma": "none",  // no trailing commas
  "semi": true,             // semicolons required
  "singleQuote": false,     // double quotes
  "arrowParens": "always",  // always parenthesize arrow params
  "endOfLine": "lf"
}

Pre-commit hook

A simple-git-hooks pre-commit hook is installed by the prepare script on pnpm install. It regenerates non-English locale files for any changed keys, then git adds the output into the commit: packages/i18n/translations, packages/content, packages/translate/.meta, and the generated locale artifacts.

Accept that auto-staged output. Edit only the source en/*.json files and the locales record in i18n.ts; everything else is generated. Translation runs only when OPENROUTER_API_KEY is set - without it the hook skips, so commits never fail.

Adding dependencies

Install into the workspace that uses a package, never at the root by hand-editing package.json.

# add to an app or a backend package (<app> = your web app's package name)
pnpm --filter <app> add <pkg>
pnpm --filter @repo/api add <pkg>

# dev-only dependency
pnpm --filter @repo/api add -D <pkg>

# remove
pnpm --filter @repo/api remove <pkg>
Run the pnpm --filter <pkg> add command for the target workspace.
Run a plain pnpm install at the root to relink siblings whose peer hashes shifted, or their types stop resolving.

Never hand-edit pnpm-lock.yaml - let pnpm own it. Pin a shared version once in the catalog: block of pnpm-workspace.yaml, then set the dep to "catalog:" in each package's package.json.

Editor setup

The project ships .vscode/launch.json with debug configurations for your stack. There is no committed .vscode/settings.json - create one with these values:

SettingValue
eslint.workingDirectories[{ "mode": "auto" }] so ESLint resolves the right config per package
files.associations{ "*.css": "tailwindcss" } for class IntelliSense
tailwindCSS.experimental.configFiletooling/tailwind/globals.css
editor.quickSuggestions{ "strings": "on" } so class names autocomplete inside strings

Install the Prettier, ESLint, and Tailwind CSS IntelliSense extensions, then enable format-on-save.

On this page