feat(deploy): production deployment setup and fixes

- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
yeuimu
2026-08-26 14:23:09 +08:00
parent be0b90e68f
commit 6c61a4e871
982 changed files with 74156 additions and 179393 deletions
@@ -1,154 +1,154 @@
# Package Exports
## Basic Single Entry
```json
{
"name": "my-lib",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"sideEffects": false,
"files": ["dist"]
}
```
## Multiple Entry Points
```json
{
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./utils": {
"types": "./dist/utils.d.mts",
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
},
"./*": "./dist/*"
}
}
```
## Plugin Entry Pattern (unplugin-\*)
```json
{
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./vite": {
"types": "./dist/vite.d.mts",
"import": "./dist/vite.mjs",
"require": "./dist/vite.cjs"
},
"./webpack": {
"types": "./dist/webpack.d.mts",
"import": "./dist/webpack.mjs",
"require": "./dist/webpack.cjs"
},
"./nuxt": {
"types": "./dist/nuxt.d.mts",
"import": "./dist/nuxt.mjs",
"require": "./dist/nuxt.cjs"
}
}
}
```
## Environment-Aware Exports
```json
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": {
"import": { "production": "./dist/index.prod.mjs", "development": "./dist/index.mjs" },
"require": { "production": "./dist/index.prod.cjs", "development": "./dist/index.cjs" }
},
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
```
## typesVersions Fallback
For older TypeScript versions without exports support:
```json
{
"typesVersions": {
"*": {
"*": ["./dist/*", "./*"]
}
}
}
```
## Field Reference
| Field | Purpose |
| ------------- | -------------------------------- |
| `exports` | Modern entry points (Node 12.7+) |
| `main` | CJS fallback for older bundlers |
| `module` | ESM fallback for bundlers |
| `types` | TypeScript fallback |
| `sideEffects` | `false` enables tree-shaking |
| `files` | What gets published to npm |
## Condition Order
Order matters! Put most specific first:
```json
{
".": {
"types": "...", // Always first
"import": "...", // ESM
"require": "..." // CJS fallback
}
}
```
## Peer Dependencies
External deps that consumers must provide:
```json
{
"peerDependencies": {
"vue": "^3.0.0"
},
"peerDependenciesMeta": {
"vue": { "optional": true }
}
}
```
## Package Validation
```bash
# Check exports are correct
pnpm dlx publint
pnpm dlx @arethetypeswrong/cli
```
Add to CI for continuous validation.
# Package Exports
## Basic Single Entry
```json
{
"name": "my-lib",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"sideEffects": false,
"files": ["dist"]
}
```
## Multiple Entry Points
```json
{
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./utils": {
"types": "./dist/utils.d.mts",
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
},
"./*": "./dist/*"
}
}
```
## Plugin Entry Pattern (unplugin-\*)
```json
{
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./vite": {
"types": "./dist/vite.d.mts",
"import": "./dist/vite.mjs",
"require": "./dist/vite.cjs"
},
"./webpack": {
"types": "./dist/webpack.d.mts",
"import": "./dist/webpack.mjs",
"require": "./dist/webpack.cjs"
},
"./nuxt": {
"types": "./dist/nuxt.d.mts",
"import": "./dist/nuxt.mjs",
"require": "./dist/nuxt.cjs"
}
}
}
```
## Environment-Aware Exports
```json
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": {
"import": { "production": "./dist/index.prod.mjs", "development": "./dist/index.mjs" },
"require": { "production": "./dist/index.prod.cjs", "development": "./dist/index.cjs" }
},
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
```
## typesVersions Fallback
For older TypeScript versions without exports support:
```json
{
"typesVersions": {
"*": {
"*": ["./dist/*", "./*"]
}
}
}
```
## Field Reference
| Field | Purpose |
| ------------- | -------------------------------- |
| `exports` | Modern entry points (Node 12.7+) |
| `main` | CJS fallback for older bundlers |
| `module` | ESM fallback for bundlers |
| `types` | TypeScript fallback |
| `sideEffects` | `false` enables tree-shaking |
| `files` | What gets published to npm |
## Condition Order
Order matters! Put most specific first:
```json
{
".": {
"types": "...", // Always first
"import": "...", // ESM
"require": "..." // CJS fallback
}
}
```
## Peer Dependencies
External deps that consumers must provide:
```json
{
"peerDependencies": {
"vue": "^3.0.0"
},
"peerDependenciesMeta": {
"vue": { "optional": true }
}
}
```
## Package Validation
```bash
# Check exports are correct
pnpm dlx publint
pnpm dlx @arethetypeswrong/cli
```
Add to CI for continuous validation.
@@ -1,157 +1,157 @@
# Project Setup
## Single Package
```bash
# Clone starter template
cp -r ~/templates/antfu/starter-ts my-lib
cd my-lib && rm -rf .git && git init
pnpm install
```
Or manual setup:
```bash
mkdir my-lib && cd my-lib
pnpm init
pnpm add -D typescript tsdown vitest eslint @antfu/eslint-config
```
### Directory Structure
```
my-lib/
├── src/
│ ├── index.ts # Main entry
│ └── types.ts # Type definitions
├── test/
│ └── index.test.ts
├── dist/ # Build output (gitignored)
├── package.json
├── tsconfig.json
├── tsdown.config.ts
├── eslint.config.ts
└── vitest.config.ts
```
## Monorepo
```bash
cp -r ~/templates/antfu/starter-monorepo my-monorepo
cd my-monorepo && rm -rf .git && git init
pnpm install
```
### Structure
```
my-monorepo/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsdown.config.ts
│ └── cli/
│ ├── src/
│ └── package.json
├── playground/ # Integration tests
├── pnpm-workspace.yaml
├── package.json # Root scripts, devDeps
├── tsconfig.json # Base config
└── eslint.config.ts
```
### pnpm-workspace.yaml
```yaml
packages:
- packages/*
- playground
catalogs:
build:
tsdown: ^0.15.0
unbuild: ^3.0.0
lint:
eslint: ^9.0.0
'@antfu/eslint-config': ^4.0.0
test:
vitest: ^3.0.0
types:
typescript: ^5.7.0
```
## pnpm Catalogs
Organize dependencies by purpose (from antfu's blog post):
| Category | Contents |
| -------- | ---------------------------------- |
| build | tsdown, unbuild, rollup plugins |
| lint | eslint, @antfu/eslint-config |
| test | vitest, @vue/test-utils |
| types | typescript, @types/\* |
| prod | Runtime deps: consola, defu, pathe |
### Using Catalogs
```json
{
"devDependencies": {
"tsdown": "catalog:build",
"eslint": "catalog:lint",
"vitest": "catalog:test",
"typescript": "catalog:types"
}
}
```
## ESLint Setup
```bash
pnpm add -D eslint @antfu/eslint-config
```
```typescript
// eslint.config.ts
import antfu from '@antfu/eslint-config'
export default antfu({
type: 'lib',
pnpm: true,
formatters: true,
})
```
## Git Hooks
```bash
pnpm add -D simple-git-hooks lint-staged
```
```json
{
"simple-git-hooks": { "pre-commit": "pnpm lint-staged" },
"lint-staged": { "*": "eslint --fix" },
"scripts": { "prepare": "simple-git-hooks" }
}
```
Run `pnpm prepare` after adding.
## Scripts
```json
{
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit",
"test": "vitest",
"release": "bumpp",
"prepublishOnly": "pnpm build"
}
}
```
# Project Setup
## Single Package
```bash
# Clone starter template
cp -r ~/templates/antfu/starter-ts my-lib
cd my-lib && rm -rf .git && git init
pnpm install
```
Or manual setup:
```bash
mkdir my-lib && cd my-lib
pnpm init
pnpm add -D typescript tsdown vitest eslint @antfu/eslint-config
```
### Directory Structure
```
my-lib/
├── src/
│ ├── index.ts # Main entry
│ └── types.ts # Type definitions
├── test/
│ └── index.test.ts
├── dist/ # Build output (gitignored)
├── package.json
├── tsconfig.json
├── tsdown.config.ts
├── eslint.config.ts
└── vitest.config.ts
```
## Monorepo
```bash
cp -r ~/templates/antfu/starter-monorepo my-monorepo
cd my-monorepo && rm -rf .git && git init
pnpm install
```
### Structure
```
my-monorepo/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsdown.config.ts
│ └── cli/
│ ├── src/
│ └── package.json
├── playground/ # Integration tests
├── pnpm-workspace.yaml
├── package.json # Root scripts, devDeps
├── tsconfig.json # Base config
└── eslint.config.ts
```
### pnpm-workspace.yaml
```yaml
packages:
- packages/*
- playground
catalogs:
build:
tsdown: ^0.15.0
unbuild: ^3.0.0
lint:
eslint: ^9.0.0
'@antfu/eslint-config': ^4.0.0
test:
vitest: ^3.0.0
types:
typescript: ^5.7.0
```
## pnpm Catalogs
Organize dependencies by purpose (from antfu's blog post):
| Category | Contents |
| -------- | ---------------------------------- |
| build | tsdown, unbuild, rollup plugins |
| lint | eslint, @antfu/eslint-config |
| test | vitest, @vue/test-utils |
| types | typescript, @types/\* |
| prod | Runtime deps: consola, defu, pathe |
### Using Catalogs
```json
{
"devDependencies": {
"tsdown": "catalog:build",
"eslint": "catalog:lint",
"vitest": "catalog:test",
"typescript": "catalog:types"
}
}
```
## ESLint Setup
```bash
pnpm add -D eslint @antfu/eslint-config
```
```typescript
// eslint.config.ts
import antfu from '@antfu/eslint-config'
export default antfu({
type: 'lib',
pnpm: true,
formatters: true,
})
```
## Git Hooks
```bash
pnpm add -D simple-git-hooks lint-staged
```
```json
{
"simple-git-hooks": { "pre-commit": "pnpm lint-staged" },
"lint-staged": { "*": "eslint --fix" },
"scripts": { "prepare": "simple-git-hooks" }
}
```
Run `pnpm prepare` after adding.
## Scripts
```json
{
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit",
"test": "vitest",
"release": "bumpp",
"prepublishOnly": "pnpm build"
}
}
```
@@ -1,144 +1,144 @@
# TypeScript Configuration
## Library Base Config
```json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ESNext"],
"strict": true,
"strictNullChecks": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noEmit": true,
"isolatedDeclarations": true,
"verbatimModuleSyntax": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
```
## Key Options Explained
| Option | Value | Why |
| ---------------------- | ------- | ------------------------------------------------ |
| `target` | ESNext | Modern output, bundlers downgrade |
| `module` | ESNext | ESM output |
| `moduleResolution` | Bundler | Works with modern bundlers, allows no extensions |
| `strict` | true | Catch errors early |
| `noEmit` | true | Build tool handles emit |
| `isolatedDeclarations` | true | Faster DTS generation |
| `verbatimModuleSyntax` | true | Explicit `import type` required |
| `skipLibCheck` | true | Faster builds |
## Monorepo Config
### Root tsconfig.json
```json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"verbatimModuleSyntax": true
}
}
```
### Package tsconfig.json
```json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"references": [
{ "path": "../utils" }
]
}
```
## Path Aliases
For internal imports in monorepos:
```json
{
"compilerOptions": {
"paths": {
"@my-lib/core": ["./packages/core/src"],
"@my-lib/utils": ["./packages/utils/src"],
"#internal/*": ["./virtual-shared/*"]
}
}
}
```
## Bundler vs Node Resolution
**Use `Bundler`** for libraries consumed by bundlers (Vite, webpack, etc.):
- Allows importing without extensions
- Supports `exports` field in package.json
- Modern, simpler setup
**Use `Node16/NodeNext`** for Node.js-only libraries:
- Requires explicit extensions (`.js`)
- Stricter, matches Node.js behavior exactly
## Type Declarations
Let build tool generate declarations:
```typescript
// tsdown.config.ts
export default defineConfig({
dts: true, // Generate .d.ts
dts: { resolve: ['@antfu/utils'] } // Inline specific types
})
```
Or with unbuild:
```typescript
// build.config.ts
export default defineBuildConfig({
declaration: 'node16', // For Node.js compatibility
declaration: true, // For bundler resolution
})
```
## Common Issues
### Module not found errors
Check `moduleResolution` matches your target:
- Bundler: `"Bundler"`
- Node.js: `"Node16"` or `"NodeNext"`
### Type imports not working
Enable `verbatimModuleSyntax` and use explicit:
```typescript
import type { Foo } from './types'
```
### Slow type checking
Enable `skipLibCheck: true` and `isolatedDeclarations: true`.
# TypeScript Configuration
## Library Base Config
```json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ESNext"],
"strict": true,
"strictNullChecks": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noEmit": true,
"isolatedDeclarations": true,
"verbatimModuleSyntax": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
```
## Key Options Explained
| Option | Value | Why |
| ---------------------- | ------- | ------------------------------------------------ |
| `target` | ESNext | Modern output, bundlers downgrade |
| `module` | ESNext | ESM output |
| `moduleResolution` | Bundler | Works with modern bundlers, allows no extensions |
| `strict` | true | Catch errors early |
| `noEmit` | true | Build tool handles emit |
| `isolatedDeclarations` | true | Faster DTS generation |
| `verbatimModuleSyntax` | true | Explicit `import type` required |
| `skipLibCheck` | true | Faster builds |
## Monorepo Config
### Root tsconfig.json
```json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"verbatimModuleSyntax": true
}
}
```
### Package tsconfig.json
```json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"references": [
{ "path": "../utils" }
]
}
```
## Path Aliases
For internal imports in monorepos:
```json
{
"compilerOptions": {
"paths": {
"@my-lib/core": ["./packages/core/src"],
"@my-lib/utils": ["./packages/utils/src"],
"#internal/*": ["./virtual-shared/*"]
}
}
}
```
## Bundler vs Node Resolution
**Use `Bundler`** for libraries consumed by bundlers (Vite, webpack, etc.):
- Allows importing without extensions
- Supports `exports` field in package.json
- Modern, simpler setup
**Use `Node16/NodeNext`** for Node.js-only libraries:
- Requires explicit extensions (`.js`)
- Stricter, matches Node.js behavior exactly
## Type Declarations
Let build tool generate declarations:
```typescript
// tsdown.config.ts
export default defineConfig({
dts: true, // Generate .d.ts
dts: { resolve: ['@antfu/utils'] } // Inline specific types
})
```
Or with unbuild:
```typescript
// build.config.ts
export default defineBuildConfig({
declaration: 'node16', // For Node.js compatibility
declaration: true, // For bundler resolution
})
```
## Common Issues
### Module not found errors
Check `moduleResolution` matches your target:
- Bundler: `"Bundler"`
- Node.js: `"Node16"` or `"NodeNext"`
### Type imports not working
Enable `verbatimModuleSyntax` and use explicit:
```typescript
import type { Foo } from './types'
```
### Slow type checking
Enable `skipLibCheck: true` and `isolatedDeclarations: true`.