configure-bun-vite-env
Load this skill when setting up or configuring a Bun + Vite frontend: scripts, dependencies, TypeScript, linting, formatting. Auto-load when bun runs, or eslint.config.js, .prettierrc, or tsconfig.jso
SKILL.mdplugins/standalone/ceh-web-frontend/skills/configure-bun-vite-env/SKILL.md
GitHubConfigure the Bun + Vite Environment
Keep a frontend project on Bun and Vite with strict TypeScript and one lint and format config. Done
when lint, format:check, typecheck, and (SvelteKit) check all pass.
Procedure
- Run scripts and change dependencies only through
bun, and leavebun.lockalone. - Read browser-safe env vars through the framework's static env module, and keep secrets server-side.
- Write TypeScript to the style below.
- Before opening a PR, run the four checks under Linting and quality checks.
- Install the pre-commit hook once per clone, per Pre-commit hook below.
Rules
Environment
- Runtime and package manager: Bun | Build tool: Vite | Framework: SvelteKit or React
- Lockfile:
bun.lock— authoritative, never edit manually - Never commit
.env; keep a.env.examplewith placeholder values - Browser-safe env vars are prefixed and read via the framework's static env module (
$env/static/publicin SvelteKit,import.meta.env.VITE_*in React+Vite). Server-only secrets never reach the client.
Commands
| Action | Command |
|---|---|
| Install all dependencies | bun install |
| Add a production dependency | bun add <package> |
| Add a dev dependency | bun add -d <package> |
| Start dev server | bun run dev |
| Production build | bun run build |
| Run unit + component tests | bun run test |
| Type check (tsc) | bun run typecheck |
| Svelte template check (SvelteKit only) | bun run check |
| Lint | bun run lint |
| Format check | bun run format:check |
TypeScript style
- Line length: 100 characters (Prettier:
printWidth100, single quotes,prettier-plugin-svelte) - Local imports use the path alias (
$libin SvelteKit, the configured alias in React), never deep relative paths - Never use
any— useunknownwith type narrowing if the type is truly unknown - Prefer
undefinedovernullfor optional values: optional properties and default parameters already produceundefined, so one "absent" value avoids mismatches - Use
?.and??; do not use||for defaults on falsy inputs (it collapses0,'',false) - Set
strict: trueintsconfig.json. Fix a type error instead of suppressing it with// @ts-ignore.
type is the default, interface is the exception
type also covers unions and aliases, and it never merges declarations by accident, so one keyword
does the job.
// Good — use type for data shapes, unions, and aliases
type Status = "active" | "archived" | "deleted";
type SessionState = { sessionId: string; items: Item[] };
// Only use interface when you intentionally need declaration merging (rare)
interface PluginExtension {
onLoad(): void;
}
No TypeScript enum: use const assertions
An enum emits runtime code that behaves unlike plain JavaScript. A const object is a plain value
with a derived union type.
const ItemStatus = {
Open: "open",
Resolved: "resolved",
Archived: "archived",
} as const;
type ItemStatus = (typeof ItemStatus)[keyof typeof ItemStatus];
JSDoc (required on all exported symbols)
/**
* Sends a user message and returns the updated session state.
* @param sessionId - Active session identifier.
* @param content - User's message text.
* @returns Updated state snapshot.
* @throws {ApiRequestError} On non-2xx response.
*/
export async function sendMessage(
sessionId: string,
content: string,
): Promise<SessionState>;
Linting and quality checks
All checks must pass before a PR is opened:
bun run lint # ESLint (typescript-eslint recommended-type-checked + framework plugin)
bun run format:check # Prettier (does not modify files)
bun run check # svelte-check — SvelteKit only; catches template errors ESLint cannot see
bun run typecheck # tsc --noEmit
svelte-check is not optional in SvelteKit projects — it catches prop type mismatches, missing required props, and a11y warnings ESLint cannot see.
Pre-commit hook
Run lint and the format check on every commit, so neither fails first in a PR. Git runs hooks from
a committed directory with no package needed: add .githooks/pre-commit (executable, LF line
endings) and point git at it once per clone with git config core.hooksPath .githooks.
#!/bin/sh
# .githooks/pre-commit
set -e
bun run lint
bun run format:check
typecheck and check stay out of the hook: they check the whole program, which is too slow for
every commit, so they run before the PR. Never commit with --no-verify to get past a hook.
ESLint configuration
// eslint.config.js
import ts from "@typescript-eslint/eslint-plugin";
import svelte from "eslint-plugin-svelte"; // SvelteKit projects
// import react from 'eslint-plugin-react'; // React projects
// import reactHooks from 'eslint-plugin-react-hooks';
export default [
...ts.configs["recommended-type-checked"],
...svelte.configs["flat/recommended"], // or react / react-hooks configs
{
rules: {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_" },
],
},
},
];