init: typescript esm boilerplate

This commit is contained in:
devilreef 2026-04-29 12:14:17 +06:00
commit 6b522a0803
Signed by: devilreef
SSH key fingerprint: SHA256:UZisRr4iuXx+IhkbZnR655L2RWAT6o2rgbGv5F/6m3Y
16 changed files with 3518 additions and 0 deletions

10
.dockerignore Normal file
View file

@ -0,0 +1,10 @@
.git
.gitignore
.husky
node_modules
dist
*.md
.env*
!.env.example
.vscode
.omc

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

1
.env.example Normal file
View file

@ -0,0 +1 @@
NODE_ENV=development

39
.gitignore vendored Normal file
View file

@ -0,0 +1,39 @@
node_modules/
dist/
*.tsbuildinfo
*.log
npm-debug.log*
pnpm-debug.log*
.env
.env.*
!.env.example
.DS_Store
Thumbs.db
._*
.directory
.cache
.parcel-cache
.next
.nuxt
out
coverage
*.lcov
.nyc_output
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
.history/
*.vsix
*.pid
*.seed
*.pid.lock
*.tgz
.omc/

4
.tokeignore Normal file
View file

@ -0,0 +1,4 @@
pnpm-lock.yaml
node_modules
dist
.omc

34
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,34 @@
{
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml",
"css",
"postcss"
]
}

60
AGENTS.md Normal file
View file

@ -0,0 +1,60 @@
# Agents
## Project Overview
TypeScript ESM boilerplate. Runtime-only via `tsx`, no build step.
## Stack
- **Runtime**: Node 24+, tsx
- **Package manager**: pnpm
- **Linting**: @antfu/eslint-config v8 (eslint.config.mjs)
- **Env**: env-var + native `loadEnvFile`
- **Container**: Docker, node:24-alpine
## Structure
```
src/
├── index.ts # entrypoint
└── shared/
└── config.ts # env config
```
## Preferred Folder Structure
```
src/
├── index.ts # entrypoint
├── shared/ # cross-cutting concerns (config, types, utilities)
└── lib/ # third-party wrappers, adapters
```
Expand based on project type as needed. Keep `src/index.ts` as the single entrypoint.
## Conventions
- ESM only (`"type": "module"`)
- Path alias `@/*` maps to `src/*` — imports must end with `.js` extension (nodenext resolution)
- No build step — tsx runs TS directly in dev and prod
- `cross-env` for cross-platform env vars in npm scripts
- No useless comments
- Split code into multiple files rather than monoliths
## Commands
- `pnpm dev` — watch mode
- `pnpm start` — production run
- `pnpm lint` — check
- `pnpm lint:fix` — auto-fix
## Lint Rules
- `no-console: off` — console.log is allowed
- `antfu/no-top-level-await: off`
- Stylistic: 2-space indent, single quotes
- JSON key ordering enforced (auto-fixed)
## Docker
Single-stage, no build. Copies source, installs prod deps, runs via `pnpm start`.

3
CLAUDE.md Normal file
View file

@ -0,0 +1,3 @@
# Claude
Read [AGENTS.md](./AGENTS.md) for project context, conventions, and structure.

11
Dockerfile Normal file
View file

@ -0,0 +1,11 @@
FROM node:24-alpine
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY . /app
WORKDIR /app
RUN pnpm install --prod --frozen-lockfile
CMD ["pnpm", "start"]

14
eslint.config.mjs Normal file
View file

@ -0,0 +1,14 @@
import antfu from '@antfu/eslint-config'
export default antfu({
stylistic: {
indent: 2,
quotes: 'single',
},
typescript: true,
ignores: ['.omc'],
rules: {
'no-console': 'off',
'antfu/no-top-level-await': 'off',
},
})

26
package.json Normal file
View file

@ -0,0 +1,26 @@
{
"name": "boilerplate",
"type": "module",
"version": "0.1.0",
"packageManager": "pnpm@10.13.1",
"description": "typescript esm boilerplate",
"engines": {
"node": ">=24.0.0"
},
"scripts": {
"dev": "tsx --watch src/index.ts",
"start": "cross-env NODE_ENV=production tsx src/index.ts",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"dependencies": {
"cross-env": "^7.0.3",
"env-var": "^7.5.0",
"tsx": "^4.21.0"
},
"devDependencies": {
"@antfu/eslint-config": "^8.0.0",
"@types/node": "^22.15.0",
"eslint": "^9.27.0"
}
}

3268
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

6
pnpm-workspace.yaml Normal file
View file

@ -0,0 +1,6 @@
shellEmulator: true
trustPolicy: no-downgrade
onlyBuiltDependencies:
- esbuild

3
src/index.ts Normal file
View file

@ -0,0 +1,3 @@
import { config } from '@/shared/config.js'
console.log(`starting in ${config.mode} mode...`)

10
src/shared/config.ts Normal file
View file

@ -0,0 +1,10 @@
import { existsSync } from 'node:fs'
import { loadEnvFile } from 'node:process'
import env from 'env-var'
if (existsSync('.env'))
loadEnvFile('.env')
export const config = {
mode: env.get('NODE_ENV').default('production').asString(),
}

20
tsconfig.json Normal file
View file

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es2024",
"rootDir": "./src",
"module": "nodenext",
"moduleResolution": "nodenext",
"paths": {
"@/*": ["./src/*"]
},
"strict": true,
"noEmit": true,
"outDir": "./dist",
"sourceMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}