Loading...
Loading...
Eamon Hyland's opinionated tooling and conventions for TypeScript projects. Use when setting up new projects, configuring a linter, monorepos, library publishing, or when the user mentions Eamon's preferences.
npx skill4agent add ehyland/skills ehyland| Category | Preference |
|---|---|
| Package Manager | Bun for apps, pnpm for libraries |
| Language | TypeScript (strict mode, ESM only) |
| Linting & Formatting | Oxlint and Oxfmt |
| Testing | Bun for apps, Vitest for libraries |
| Git Hooks | simple-git-hooks + lint-staged |
| Bundler | Bun for apps, tsdown for libraries |
src/config.tssrc/server/config.ts// src/config.ts
import { z } from "zod";
const configSchema = z.object({
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
DATABASE_URL: z.string().default("sqlite.db"),
});
const parsed = configSchema.safeParse(process.env);
if (!parsed.success) {
console.error("❌ Invalid environment variables:", parsed.error.flatten().fieldErrors);
process.exit(1);
}
export const config = parsed.data// src/config.ts
import { z } from "zod";
const configSchema = z.object({
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
DATABASE_URL: z.string().default("sqlite.db"),
});
const parsed = configSchema.safeParse(process.env);
if (!parsed.success) {
console.error("❌ Invalid environment variables:", parsed.error.flatten().fieldErrors);
process.exit(1);
}
export const config = parsed.data// src/test/setup.ts
process.env.DATABASE_URL = ":memory:";@ts-ignoreas anyas unknown as <some-other-type>tsconfig.json{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}
}{
"scripts": {
"dev": "bun run --watch src/index.ts",
"db:generate": "drizzle-kit generate",
"db:studio": "drizzle-kit studio",
"typecheck": "tsc --noEmit",
"format": "oxfmt src",
"format:check": "oxfmt src --check",
"lint": "oxlint --type-aware",
"lint:fix": "oxlint --type-aware --fix --fix-suggestions --fix-dangerously",
"fix": "bun format && bun lint:fix",
"check": "concurrently 'bun:format' 'bun:lint:fix' 'bun:typecheck' 'bun:test' 'bun:test:e2e'",
"test": "bun test",
"test:e2e": "playwright test"
}
}// .oxlintrc.json
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript"],
"rules": {
"typescript/no-floating-promises": "error",
"typescript/no-unsafe-assignment": "warn"
}
}// .oxfmtrc.json
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"trailingComma": "all",
"printWidth": 80,
"ignorePatterns": []
}bun run fixpnpm run fixsimple-git-hookslint-staged{
"simple-git-hooks": {
"pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm lint-staged"
},
"lint-staged": { "*": "pnpm fix" },
"scripts": { "prepare": "pnpm simple-git-hooks" }
}foo.tsfoo.test.tsdescribeittest| Topic | Description | Reference |
|---|---|---|
| Bun and tRPC Setup | Configuration for Bun.serve with native routes and tRPC fetch adapter | bun-trpc-setup |
| Bun and GraphQL Setup | Configuration for Bun.serve with Yoga GraphQL | bun-graphql-setup |
| SQLite with Drizzle | SQLite setup with Drizzle ORM in Bun runtime, schema, migrations, testing | sqlite-drizzle |
| Deployment and CI/CD Setup | Buildkite pipelines, docker configuration | deployment |