Project Config Guide
Use for tsconfig.json, vite.config.ts, and shared workspace tooling config in FaasJS projects.
Applicable Scenarios
- Initializing a new FaasJS app or package
- Simplifying an existing
tsconfig.json - Simplifying an existing
vite.config.ts - Deciding whether to inherit framework defaults or write local tooling rules
- Reviewing config files for duplicate declarations of FaasJS-provided settings
Default Workflow
- Start from
@faasjs/types/tsconfig/*instead of hand-writing a TypeScript baseline. - Keep local
tsconfig.jsonfocused on project-specifictypes,include,exclude,baseUrl, andpaths. - Start from
ViteConfigfor standard FaasJS React + local server apps. - Keep local
vite.config.tsfocused on runtime plugins, aliases, server options, tests, and build behavior. - Reuse
OxfmtConfigandOxlintConfigfrom@faasjs/dev; extend shared config instead of replacing it. - Prefer extending shared config over wholesale replacement when adjustments are needed.
Rules
1. Prefer shared TypeScript presets
- Use
@faasjs/types/tsconfig/build.jsonfor most Vite apps and packages (includingcreate-faas-appstarters). - Use
@faasjs/types/tsconfig/base.jsonfor non-React TypeScript apps. - Use
@faasjs/types/tsconfig/react.jsononly when a React project needs JSX defaults without build-oriented settings. - Do not duplicate strict mode, JSX, module resolution, or other shared baseline settings unless intentionally changing behavior.
- Prefer explicit
.jsonpreset paths because generated starters use them.
React app:
{
"extends": "@faasjs/types/tsconfig/build.json",
"compilerOptions": {
"types": ["node", "vitest/globals"]
},
"include": ["src", "vite.config.ts", "server.ts"]
}
Non-React app:
{
"extends": "@faasjs/types/tsconfig/base.json",
"compilerOptions": {
"types": ["node", "vitest/globals"]
},
"include": ["src", "vite.config.ts", "server.ts"]
}
Package:
{
"extends": "@faasjs/types/tsconfig/build.json"
}
Extension-less paths (e.g. @faasjs/types/tsconfig/build) still work, but explicit .json paths match generated starter output and reduce ambiguity.
2. Keep local tsconfig overrides minimal
Local compilerOptions should only add project-specific settings. Do not redeclare strict, jsx, moduleResolution, or other shared baseline values unless intentionally overriding behavior.
Prefer:
{
"extends": "@faasjs/types/tsconfig/build.json",
"compilerOptions": {
"types": ["node", "vitest/globals"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src", "vite.config.ts", "server.ts"]
}
Avoid:
{
"compilerOptions": {
"target": "ESNext",
"jsx": "react-jsx",
"strict": true,
"moduleResolution": "bundler",
"exactOptionalPropertyTypes": true,
"verbatimModuleSyntax": true,
"types": ["node", "vitest/globals"]
},
"include": ["src", "vite.config.ts", "server.ts"]
}
3. Let Vite config focus on app behavior
- Import
defineConfigfromvite-pluswhen usingfmtorlint. - Prefer
ViteConfigfrom@faasjs/devwhen the standard stack fits. vite.config.tsshould define project runtime behavior: plugins, aliases, server options, tests, and build settings.- Shared format and lint rules should come from
@faasjs/dev. - In mixed Node + UI tests, use separate
test.projectsentries. Treat*.test.tsxas UI tests and*.ui.test.tsfor UI tests without TSX syntax. - Put
*.types.test.ts(x)files in a dedicatedtypesproject and exclude them from runtime projects. Without this,src/**/*.test.tsglobs would also match*.types.test.tsfiles, and inherited root-level typecheck config could duplicate runs. - Keep PG-backed tests in the main Node runtime project unless setup must be isolated; then use a node-scoped project such as
node-pg.
Example with multi-project Vitest config:
import { ViteConfig } from '@faasjs/dev'
import { defineConfig } from 'vite-plus'
const tests = ['src/**/*.test.ts']
const uiTests = ['src/**/*.test.tsx', 'src/**/*.ui.test.ts']
const typeTests = ['src/**/*.types.test.ts', 'src/**/*.types.test.tsx']
export default defineConfig({
...ViteConfig,
test: {
projects: [
{
extends: true as const,
test: {
name: 'node',
include: tests,
exclude: uiTests.concat(typeTests),
environment: 'node',
},
},
{
extends: true as const,
test: {
name: 'ui',
include: uiTests,
environment: 'jsdom',
setupFiles: ['vitest.ui.setup.ts'],
exclude: typeTests,
},
},
{
extends: true as const,
test: {
name: 'types',
include: typeTests,
environment: 'node',
typecheck: {
enabled: true,
only: true,
include: typeTests,
},
},
},
],
},
})
Manual composition for projects that cannot use ViteConfig directly:
import { viteFaasJsServer, OxfmtConfig, OxlintConfig } from '@faasjs/dev'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite-plus'
export default defineConfig({
plugins: [react(), viteFaasJsServer()],
resolve: {
tsconfigPaths: true,
},
server: {
host: '0.0.0.0',
},
fmt: OxfmtConfig,
lint: OxlintConfig,
})
4. Extend shared Vite rules instead of copying
If only one or two local differences are needed, spread the shared config and add only the delta.
For lint/format overrides, extend OxfmtConfig or OxlintConfig:
import { OxfmtConfig, OxlintConfig } from '@faasjs/dev'
import { defineConfig } from 'vite-plus'
export default defineConfig({
fmt: {
...OxfmtConfig,
},
lint: {
...OxlintConfig,
rules: {
...OxlintConfig.rules,
'no-console': 'warn',
},
},
})
5. Make project-specific intent visible at a glance
- Readers should quickly see which settings come from FaasJS versus which are local decisions.
- Shared framework defaults should come through inheritance.
- Local config should only highlight settings that affect the current app, package, or workspace.
6. Respect existing alias configuration
- Read
tsconfig.jsonand extended configs before choosing import paths. - Use aliases already defined in
compilerOptions.paths; keep short relative imports for nearby files. - Prefer package imports such as
@faasjs/coreover long relative imports into another package. - Do not introduce an alias unless TypeScript and the runtime or bundler resolver are configured in the same change.
- In FaasJS workspaces,
resolve.tsconfigPathsinvite.config.tsis the preferred runtime support for path aliases.
Review Checklist
tsconfig.jsoninherits shared presets from@faasjs/typeswhere possible- local TypeScript overrides only keep project-specific settings
vite-plusis used whenfmtorlintis configuredvite.config.tsstarts fromViteConfigor reuses sharedfmt/lintrules from@faasjs/dev- config does not duplicate FaasJS baseline settings without reason
- when a project mixes UI and Node tests, UI tests are in a separate project with
environment: 'jsdom' - when a project has type tests, they are in a separate
typesproject instead of mixed into runtime tests - local config makes project-specific behavior identifiable
- imports follow existing aliases and runtime resolver support