All skills
onmax avatar

/ts-library

@50538df

Use when authoring TypeScript libraries or npm packages - covers project setup, package.json exports, build tooling (tsdown/unbuild), API design patterns, type inference tricks, testing, and publishing to npm. Use when bundling, configuring dual CJS/ESM output, or setting up release workflows.

  • 20 files
  • 73.4 KB
  • MIT
  • Updated 7 months ago
  • GitHub

Use this Skill: https://skilld.dev/gh/onmax/claude-config/ts-library

This session only. Nothing lands on disk.

referencesapi-design.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

API Design Patterns

Options Pattern

User-facing options with internal resolved version:

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:

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:

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:

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:

// 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:

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:

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:

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:

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:

// src/index.ts
export * from './config'
export * from './types'
export { createContext } from './context'
export { default } from './plugin'

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at 50538df. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 14 hours ago.

Activeupdated 7 months ago

README badge

README badge for onmax/claude-config/ts-library