fix: convert apps/website from submodule to regular directory

This commit is contained in:
yeuimu
2026-07-16 01:42:45 +08:00
parent f12ae12ad3
commit 9595030625
341 changed files with 50808 additions and 1 deletions
@@ -0,0 +1,210 @@
# API Design Patterns
## Options Pattern
User-facing options with internal resolved version:
```typescript
export interface Options {
verbose?: boolean
include?: string[]
exclude?: string[]
}
export interface ResolvedOptions extends Required<Options> {
root: string
}
function resolveOptions(options: Options = {}): ResolvedOptions {
return {
verbose: options.verbose ?? false,
include: options.include ?? ['**/*'],
exclude: options.exclude ?? ['node_modules'],
root: process.cwd(),
}
}
```
## Factory Functions
Create configured instances:
```typescript
export function createContext(options: Options = {}) {
const resolved = resolveOptions(options)
const filter = createFilter(resolved.include, resolved.exclude)
return {
options: resolved,
filter,
transform(code: string, id: string) { /* ... */ },
async scanDirs() { /* ... */ },
}
}
// Usage
const ctx = createContext({ verbose: true })
await ctx.scanDirs()
```
## Builder Pattern
Chainable API with type accumulation:
```typescript
export function createBuilder<TContext = unknown>() {
return {
context<T>(): Builder<T, unknown, unknown> {
return this as any
},
input<T>(schema: T): Builder<TContext, T, unknown> {
return this as any
},
output<T>(schema: T): Builder<TContext, unknown, T> {
return this as any
},
build(): Procedure<TContext> { /* ... */ },
}
}
// Usage - types flow through chain
const procedure = createBuilder()
.context<{ user: User }>()
.input(z.object({ id: z.string() }))
.build()
```
## Plugin Pattern (unplugin)
Universal plugin from single implementation:
```typescript
import { createUnplugin } from 'unplugin'
export default createUnplugin<Options>((options) => {
const ctx = createContext(options)
return {
name: 'my-plugin',
enforce: 'pre',
transformInclude(id) {
return ctx.filter(id)
},
transform(code, id) {
return ctx.transform(code, id)
},
// Bundler-specific hooks
vite: {
configResolved(config) { /* Vite-specific */ },
},
webpack(compiler) {
compiler.hooks.watchRun.tap('my-plugin', () => { /* ... */ })
},
}
})
```
Export per-bundler entries:
```typescript
// src/vite.ts
import unplugin from '.'
export default unplugin.vite
// src/webpack.ts
import unplugin from '.'
export default unplugin.webpack
```
## Lazy Getters (Tree-shaking)
Defer bundler-specific code until accessed:
```typescript
export function createPlugin<T>(factory: PluginFactory<T>) {
return {
get vite() { return getVitePlugin(factory) },
get webpack() { return getWebpackPlugin(factory) },
get rollup() { return getRollupPlugin(factory) },
}
}
```
Only the accessed getter runs, rest is tree-shaken.
## Smart Defaults
Detect environment instead of requiring config:
```typescript
import { isPackageExists } from 'local-pkg'
function resolveOptions(options: Options) {
return {
vue: options.vue ?? isPackageExists('vue'),
react: options.react ?? isPackageExists('react'),
typescript: options.typescript ?? isPackageExists('typescript'),
}
}
```
## Resolver Pattern
Flexible resolution with function or object:
```typescript
export type Resolver = ResolverFunction | ResolverObject
export type ResolverFunction = (name: string) => ResolveResult | undefined
export interface ResolverObject {
type: 'component' | 'directive'
resolve: ResolverFunction
}
export function ElementPlusResolver(): Resolver[] {
return [
{ type: 'component', resolve: (name) => resolveComponent(name) },
{ type: 'directive', resolve: (name) => resolveDirective(name) },
]
}
```
## Fluent API (Validation)
Method chaining with clone for immutability:
```typescript
class Schema<T> {
private _def: SchemaDef
min(value: number): Schema<T> {
return new Schema({ ...this._def, min: value })
}
max(value: number): Schema<T> {
return new Schema({ ...this._def, max: value })
}
optional(): Schema<T | undefined> {
return new Schema({ ...this._def, optional: true })
}
}
// Usage
const schema = z.string().min(5).max(10).optional()
```
## Barrel Exports
Clean public API:
```typescript
// src/index.ts
export * from './config'
export * from './types'
export { createContext } from './context'
export { default } from './plugin'
```
@@ -0,0 +1,191 @@
# Type Patterns
## Utility Types
Common helpers used across libraries:
```typescript
// Promise or sync
export type Awaitable<T> = T | Promise<T>
// Single or array
export type Arrayable<T> = T | T[]
// Nullable
export type Nullable<T> = T | null | undefined
// Deep partial
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
// Simplify intersection for better IDE display
export type Simplify<T> = { [K in keyof T]: T[K] } & {}
// Prevent inference in specific position
export type NoInfer<T> = [T][T extends any ? 0 : never]
```
## Conditional Extraction
Extract types from structures:
```typescript
// Extract input type from schema
export type Input<T> = T extends { _input: infer U } ? U : unknown
// Extract output type
export type Output<T> = T extends { _output: infer U } ? U : unknown
// Extract from nested property
export type InferContext<T> = T extends { context: infer C } ? C : never
```
## Brand Types
Nominal typing for primitives:
```typescript
declare const brand: unique symbol
export type Brand<T, B> = T & { readonly [brand]: B }
export type UserId = Brand<string, 'UserId'>
export type PostId = Brand<string, 'PostId'>
// Can't mix them up
function getUser(id: UserId) { /* ... */ }
getUser('abc' as UserId) // OK
getUser('abc' as PostId) // Error!
```
## Type Accumulation (Builders)
Each method updates generic parameters:
```typescript
interface ProcedureBuilder<TContext, TInput, TOutput> {
input<T>(schema: T): ProcedureBuilder<TContext, T, TOutput>
output<T>(schema: T): ProcedureBuilder<TContext, TInput, T>
query(fn: (opts: { ctx: TContext; input: TInput }) => TOutput): Procedure
}
// Types flow through the chain
const proc = builder
.input(z.object({ id: z.string() })) // TInput = { id: string }
.output(z.object({ name: z.string() })) // TOutput = { name: string }
.query(({ input }) => ({ name: input.id }))
```
## Module Augmentation
Allow users to extend library types:
```typescript
// Library code
export interface Register {}
export type DefaultError = Register extends { defaultError: infer E }
? E
: Error
// User code
declare module 'my-lib' {
interface Register {
defaultError: MyCustomError
}
}
```
## Data Tagging
Attach type metadata with symbols:
```typescript
declare const dataTagSymbol: unique symbol
declare const errorTagSymbol: unique symbol
export type DataTag<TType, TData, TError> = TType & {
[dataTagSymbol]: TData
[errorTagSymbol]: TError
}
// Extract tagged types
export type InferData<T> = T extends { [dataTagSymbol]: infer D } ? D : unknown
```
## Mapped Type Modifications
Column builder pattern (drizzle):
```typescript
type NotNull<T extends ColumnBuilder> = T & { _: { notNull: true } }
type HasDefault<T extends ColumnBuilder> = T & { _: { hasDefault: true } }
class ColumnBuilder<T extends ColumnConfig> {
notNull(): NotNull<this> {
// ...
return this as NotNull<this>
}
default(value: T['data']): HasDefault<this> {
// ...
return this as HasDefault<this>
}
}
```
## Compile-Time Errors
Return readable error messages:
```typescript
type TypeError<Message extends string> = { __error: Message }
type ValidateInput<T> = T extends string
? T
: TypeError<'Input must be a string'>
// Shows: Type 'TypeError<"Input must be a string">' is not assignable...
```
## Function Overloads
Multiple signatures for different inputs:
```typescript
export function useEventListener<E extends keyof WindowEventMap>(
event: E,
listener: (ev: WindowEventMap[E]) => any
): void
export function useEventListener<E extends keyof DocumentEventMap>(
target: Document,
event: E,
listener: (ev: DocumentEventMap[E]) => any
): void
export function useEventListener(...args: any[]) {
// Implementation
}
```
## Distributive Conditionals
Apply to each union member:
```typescript
type ToArray<T> = T extends any ? T[] : never
type Result = ToArray<string | number>
// Result = string[] | number[]
```
Disable distribution with tuple:
```typescript
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type Result = ToArrayNonDist<string | number>
// Result = (string | number)[]
```