Eslint
TypeScript software architecture baseline with VS Code Agent Skills for AI coding, review, security, testing, API design, and delivery standards.
npx -y skills add batur/ts-baseline-docs --skill eslintAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Enforce and review the TypeScript ESLint baseline for this project. Use when creating or reviewing ESLint flat config, lint rules, imports, exports, naming, file names, side-effect imports, environment access, component boundaries, or provider/SDK dependency restrictions in TypeScript code.
SKILL.md
12.1 KB, as published. Nobody here has run it
ESLint Skill
When to use this skill
Use this skill when the task involves any of the following:
- Creating or reviewing
eslint.config.js. - Updating TypeScript lint rules.
- Reviewing imports, exports, naming, file naming, side-effect imports, or module boundaries.
- Checking whether generated code follows the accepted TypeScript architecture baseline.
- Preventing database/provider SDK leakage into domain/application/use-case code.
- Reviewing whether
process.envis used only inside config modules. - Adapting ESLint rules for backend NodeNext, frontend bundler, framework, tooling, or test files.
Do not use this skill as the source of truth for TypeScript compiler options. Use the TypeScript skill for tsconfig decisions.
Goal
Make ESLint act as an automated reviewer for the project’s agreed engineering standards:
- ESM only.
- Named exports by default.
- Kebab-case file and folder names.
- Type-only imports where applicable.
- Stable import ordering.
- Node built-ins with the
node:protocol. - Side-effect imports only in approved entry/setup/instrumentation files.
process.envonly inside config modules.- Cross-component access only through public APIs.
- No provider, database, or SDK details inside domain/application/use-case code.
Lint rules should enforce conventions without hiding architecture problems behind formatting noise.
Baseline ESLint stack
Use ESLint flat config.
Recommended baseline packages:
eslint@eslint/jstypescript-eslinteslint-config-prettiereslint-plugin-importeslint-import-resolver-typescripteslint-plugin-unicornglobals
The baseline may use:
js.configs.recommendedtypescript-eslintstrict type-checked configstypescript-eslintstylistic type-checked configseslint-plugin-importrecommended + TypeScript configseslint-config-prettieras the last config item
Prettier owns formatting. ESLint owns code-quality, import, naming, and architecture constraints.
Core rules
1. Use named exports by default
Application modules must use named exports.
Default exports are forbidden unless required by a framework or tooling convention.
Allowed default export examples:
eslint.config.jsprettier.config.jsvite.config.tsvitest.config.tsplaywright.config.ts- Next.js
page.tsx,layout.tsx,loading.tsx,error.tsx,not-found.tsxwhen using a Next.js template
Review checks:
- Reject unnecessary default exports in application code.
- Allow default exports only through explicit file-pattern overrides.
2. Enforce kebab-case file names
Source file and folder names must use kebab-case.
Good:
create-user.use-case.ts
user.repository.ts
server-env.ts
request-logger.middleware.ts
Bad:
CreateUserUseCase.ts
userRepository.ts
ServerEnv.ts
Exceptions may be added only for tooling, framework, or repository convention files such as:
README.mdCODEOWNERS- config files
- framework-required files
3. Enforce project naming conventions
Use these naming conventions:
- Variables and functions:
camelCase - Types, interfaces, classes:
PascalCase - Interfaces: no
Iprefix - Constants:
SCREAMING_SNAKE_CASE - Enum-like const object keys:
SCREAMING_SNAKE_CASE - Enum members, if enums are used:
SCREAMING_SNAKE_CASE
Good:
export const APPLICATION_STATUS = {
DRAFT_CREATED: "draft_created",
APPLIED: "applied",
} as const;
export type ApplicationStatus =
(typeof APPLICATION_STATUS)[keyof typeof APPLICATION_STATUS];
Bad:
interface IUser {}
const applicationStatus = { draftCreated: "draft_created" };
4. Require type-only imports
Use import type for type-only imports.
Good:
import { z } from "zod";
import type { UserRepository } from "./user.repository.js";
Bad:
import { UserRepository } from "./user.repository.js";
Use separate type imports when auto-fixing.
5. Enforce import order
Import groups must be ordered consistently:
- Side-effect imports
- Node built-ins
- External packages
- Internal alias imports
- Parent imports
- Sibling/index imports
- Type-only imports
Keep blank lines between groups.
Example:
import "dotenv/config";
import { randomUUID } from "node:crypto";
import { z } from "zod";
import { LOGGER } from "@/shared/logger/logger.js";
import { CREATE_USER_SCHEMA } from "./create-user.schema.js";
import type { UserRepository } from "./user.repository.js";
6. Require node: protocol for Node built-ins
Good:
import path from "node:path";
import { randomUUID } from "node:crypto";
Bad:
import path from "path";
import { randomUUID } from "crypto";
7. Restrict side-effect imports
Side-effect imports are allowed only in approved entry/setup/instrumentation files.
Allowed examples:
src/main.tssrc/main.tsxsrc/app/bootstrap.tssrc/app/server.tssrc/**/*.setup.tssrc/**/*.instrumentation.tstests/**/*.setup.ts
Reject side-effect imports inside ordinary domain, application, repository, adapter, and UI modules.
8. Restrict process.env
Application code must not read process.env directly.
Allowed files:
src/**/config/**src/**/config.tssrc/shared/config/**- Tooling config files where required
Bad:
const apiKey = process.env.OPENAI_API_KEY;
Good:
import { SERVER_ENV } from "@/shared/config/server-env.js";
const apiKey = SERVER_ENV.OPENAI_API_KEY;
9. Enforce public component APIs
A component’s public API is its index.ts.
Cross-component imports must go through the target component’s public API.
Good:
import { createUser } from "@/modules/users/index.js";
Bad:
import { createUser } from "@/modules/users/create-user.use-case.js";
The baseline can restrict alias deep imports such as:
@/modules/<component>/<internal-file>
@/features/<feature>/<internal-file>
Relative cross-component deep imports are harder to enforce with simple ESLint patterns. If strict enforcement is required, add eslint-plugin-boundaries or dependency-cruiser later.
10. Block provider and database SDKs in domain/application/use-case code
Domain/application/use-case code must not import provider, database, or infrastructure SDK details.
Restricted in domain/application/use-case paths:
@prisma/clientdrizzle-orm@supabase/supabase-jsfirebasemongodbopenaistripe
Good:
export async function createInvoice(params: {
billingGateway: BillingGateway;
}) {
return params.billingGateway.createInvoice();
}
Bad:
import Stripe from "stripe";
export async function createInvoice() {
const stripe = new Stripe(...);
}
SDK imports belong in adapters, infrastructure, repositories, or integration modules.
File-pattern overrides
Tooling files
Tooling/config files may need looser rules.
Typical patterns:
*.config.{js,mjs,ts}
eslint.config.js
prettier.config.js
drizzle.config.ts
vite.config.ts
vitest.config.ts
playwright.config.ts
Allowed relaxations:
- Default export may be allowed.
process.envmay be allowed if the tool requires it.- Naming conventions may be relaxed.
- Some import plugin false positives may be disabled.
Framework files
For frontend/framework templates, add explicit overrides only when needed.
Next.js examples:
src/app/**/page.tsx
src/app/**/layout.tsx
src/app/**/loading.tsx
src/app/**/error.tsx
src/app/**/not-found.tsx
These may allow default exports because the framework requires them.
Entry/setup/instrumentation files
Allow side-effect imports only for approved patterns.
Do not globally disable import/no-unassigned-import.
Test files
Keep test files close to production rules by default.
Only relax naming or import rules if test fixtures represent external payload shapes that require non-camelCase keys.
Backend NodeNext import extension guidance
For backend NodeNext TypeScript, relative runtime imports should use .js in source files.
Good:
import { CREATE_USER_SCHEMA } from "./create-user.schema.js";
import type { UserRepository } from "./user.repository.js";
Bad:
import { CREATE_USER_SCHEMA } from "./create-user.schema";
import { CREATE_USER_SCHEMA } from "./create-user.schema.ts";
For frontend/bundler templates, extensionless imports can be allowed through a separate ESLint/TypeScript profile.
AI coding workflow
When writing or editing code:
- Identify the file category: app code, domain/application, infrastructure/adapter, config, test, tooling, framework, or generated.
- Apply the strictest relevant rule set.
- Use named exports unless a documented override applies.
- Use kebab-case file names.
- Use
import typefor type-only imports. - Keep import groups ordered.
- Do not read
process.envoutside config modules. - Do not import provider/database SDKs inside domain/application/use-case files.
- Do not deep-import another component’s internals.
- Add or adjust ESLint overrides only for clear framework/tooling needs.
AI review checklist
Use this checklist when reviewing generated or human-written code:
- Are file and folder names kebab-case?
- Are default exports avoided in application code?
- Are default exports allowed only by explicit tooling/framework overrides?
- Are all type-only imports written with
import type? - Are imports grouped and alphabetized consistently?
- Do Node built-in imports use
node:? - Are side-effect imports limited to entry/setup/instrumentation files?
- Is
process.envused only in config modules or tooling configs? - Are cross-component imports routed through
index.tspublic APIs? - Does domain/application/use-case code avoid Drizzle, Prisma, Supabase, Firebase, MongoDB, OpenAI, Stripe, and similar SDKs?
- Are framework-specific exceptions explicit rather than global?
- Are generated files ignored or clearly excluded from linting when appropriate?
- Does Prettier remain the formatting authority?
Common fixes
Fix type-only imports
Use import type for types.
Fix direct environment access
Move raw env reads to config modules and consume typed config objects.
Fix SDK leakage
Replace direct SDK imports in use-cases with interfaces such as repositories, gateways, clients, or adapters passed from the composition root.
Fix cross-component deep import
Bad:
import { hashPassword } from "@/modules/users/password/hash-password.js";
Good:
import { hashPassword } from "@/modules/users/index.js";
Only export the symbol from index.ts if it is truly part of the component’s public API.
Do not
- Do not disable ESLint globally to make generated code pass.
- Do not add broad overrides when a narrow file-pattern override is enough.
- Do not use default exports in ordinary application modules.
- Do not allow
process.envoutside config modules. - Do not allow SDK imports in domain/application/use-case code.
- Do not use ESLint as a replacement for TypeScript compiler strictness.
- Do not let Prettier and ESLint fight over formatting.
- Do not add framework exceptions to the generic backend profile unless the framework is actually used.
Escalation rules
Stop and request an ADR or architecture decision if broad rule disabling is needed, component boundaries cannot be enforced, provider SDKs appear necessary in domain/application code, framework exceptions would become global, generated code needs a dedicated lint profile, or stronger boundary tooling such as eslint-plugin-boundaries or dependency-cruiser is required.