Skip to content

ADR-008: Dual Export Strategy ​

  • Status: accepted
  • Date: 2025-01-26

Context and Problem Statement ​

The package needs to provide utility libraries to consumers while maintaining both opinionated and transparent usage patterns. We need to balance convenience (opinionated API) with flexibility (direct access to third-party libraries) while preserving original library contracts and avoiding naming conflicts.

Decision Drivers ​

  • Need to provide both opinionated and transparent access to utilities
  • Requirement to preserve original library API contracts (especially default exports)
  • Desire to avoid naming conflicts between different libraries
  • Need to separate internal exports from third-party re-exports
  • Requirement for clear, discoverable export patterns
  • Need to maintain tsconfig and eslint as separate exports

Considered Options ​

  • Single index file with all exports
  • Separate files for each utility library
  • Namespace-based organization
  • Dual export strategy with opinionated and transparent paths

Decision Outcome ​

Chosen option: "Dual export strategy with opinionated and transparent paths", because it provides both convenience and flexibility while preserving library contracts.

Positive Consequences ​

  • Clear Separation: Opinionated vs transparent usage is explicit
  • Preserved Contracts: Original library APIs remain intact
  • No Naming Conflicts: Different export paths prevent conflicts
  • Discoverable: Clear naming convention for different use cases
  • Flexible: Consumers can choose their preferred approach
  • Maintainable: Easy to add new utilities without breaking existing patterns

Negative Consequences ​

  • More Complex: Two different import patterns to document
  • Larger API Surface: More export paths to maintain
  • Learning Curve: Consumers need to understand the dual approach

Implementation Details ​

Package Structure ​

src/
  kit.ts              # Opinionated catalogue
  _/
    defu.ts          # Direct re-export
    antfu.ts         # Direct re-export
    typescript.ts     # Direct re-export
  types/
    index.ts         # Internal types

Package Exports ​

json
{
  "exports": {
    ".": {
      "types": "./src/kit.ts",
      "import": "./src/kit.ts"
    },
    "./_/*": {
      "types": "./src/_/*.ts",
      "import": "./src/_/*.ts"
    },
    "./tsconfig": "./tsconfig.base.json",
    "./eslint": "./eslint.js"
  }
}

Usage Patterns ​

Opinionated (kit.ts):

typescript
import { defu } from '@relational-fabric/canon'
import createEslintConfig from '@relational-fabric/canon/eslint'

Transparent (_/):

typescript
import antfu from '@relational-fabric/canon/_/antfu'
import { defu } from '@relational-fabric/canon/_/defu'

Naming Rationale ​

  • kit: Short, clear synonym for "catalogue" without spelling ambiguity
  • _/: Non-word path that suggests "breaking the glass" for direct access
  • Excluded: tsconfig and eslint maintain their own dedicated exports

Released under the MIT License.