@tapestrylab/resolve
Unified system for resolving component, docs, and dependency references across the Tapestry ecosystem
Overview
The @tapestrylab/resolve package provides a flexible, plugin-based resolution system for locating modules, components, and dependencies. It abstracts away where things live—whether in a local filesystem, fetched from a CDN, or retrieved from a remote registry.
Perfect for:
- Playground environments that need to resolve user-uploaded components and npm dependencies
- Build tools that need consistent module resolution across different sources
- Design system tooling that needs to locate components and documentation
Features
Module Resolution
- Multiple Resolution Strategies: Local filesystem, CDN (esm.sh, jsDelivr, unpkg), and remote registries
- Alias Support: Map short names (e.g.,
@ui/Button) to actual paths
- Browser & Node Compatible: Works in both environments
- Caching: Built-in caching for fast repeated resolutions
- Extensible: Easy to add custom resolution strategies
Component Documentation
- Server-Side Rendering: Pre-render component previews at build time
- Metadata Extraction: Integration with
@tapestrylab/extract for props, types, and JSDoc
- Interactive Sandboxes: Optional sandbox configuration generation for live examples
- Static Site Compatible: Zero-config deployable documentation
- TypeScript: Fully typed with extensive type definitions
Installation
pnpm add @tapestrylab/resolve
Quick Start
import { createResolver, strategies } from "@tapestrylab/resolve"
const resolver = createResolver({
strategies: [
strategies.local({
root: "/src",
alias: {
"@ui": "components/ui",
"@core": "components/core",
},
}),
strategies.cdn({
provider: "esm.sh",
versionMap: {
react: "18.3.1",
},
}),
],
})
const button = await resolver.resolve("@ui/Button")
console.log(button)
const react = await resolver.resolve("react")
console.log(react)
Component Documentation with SSR
import { createComponentResolver, strategies } from '@tapestrylab/resolve';
const docResolver = createComponentResolver({
strategies: [
strategies.local({
root: '/src',
alias: { '@ui': 'components/ui' }
})
]
});
const docs = await docResolver.resolve({
entry: '/src/components/Button.tsx',
renderPreview: true,
sandbox: true
});
console.log(docs);
API
Module Resolution
createResolver(config)
Create a new module resolver instance.
const resolver = createResolver({
strategies: [strategies.local(), strategies.cdn()],
cache: true,
context: {
root: "/project",
metadata: {
},
},
})
resolver.resolve(id, context?)
Resolve a single module identifier.
const result = await resolver.resolve("@ui/Button")
const result = await resolver.resolve("./Button", {
importer: "/src/components/index.ts",
})
resolver.resolveMany(ids, context?)
Resolve multiple identifiers in parallel.
const results = await resolver.resolveMany(["@ui/Button", "@ui/Input", "react", "lodash/debounce"])
resolver.clearCache()
Clear all cached resolutions.
resolver.clearCache()
resolver.addStrategy(strategy, prepend?)
Add a new strategy dynamically.
resolver.addStrategy(strategies.remote(), false)
resolver.addStrategy(strategies.local(), true)
Component Documentation
createComponentResolver(config, options?)
Create a component documentation resolver.
const docResolver = createComponentResolver(
{
strategies: [strategies.local({ root: '/src' })]
},
{
renderFunction: (component, props) => {
return customRender(component, props);
}
}
);
docResolver.resolve(options)
Resolve component documentation with optional SSR preview and sandbox config.
const docs = await docResolver.resolve({
entry: '/src/components/Button.tsx',
renderPreview: true,
sandbox: true,
previewProps: {
variant: 'primary'
}
});
docResolver.resolveMany(entries, options?)
Resolve multiple components in parallel.
const allDocs = await docResolver.resolveMany(
['/src/components/Button.tsx', '/src/components/Input.tsx'],
{ renderPreview: true, sandbox: true }
);
Strategies
Local Strategy
Resolves local filesystem paths with alias support.
strategies.local({
root: "/project/src",
alias: {
"@ui": "components/ui",
"@core": "components/core",
"@utils": "lib/utils",
},
extensions: [".ts", ".tsx", ".js", ".jsx"],
checkExists: true,
})
Features:
- Alias mapping (
@ui/Button → components/ui/Button)
- Relative imports (
./Button, ../utils/helper)
- Absolute paths (
/usr/local/lib/module)
- Extension resolution (tries
.ts, .tsx, .js, .jsx)
- Index file resolution (
./components → ./components/index.ts)
CDN Strategy
Resolves npm packages from CDN providers.
strategies.cdn({
provider: "esm.sh",
versionMap: {
react: "18.3.1",
"react-dom": "18.3.1",
"@radix-ui/react-popover": "1.0.7",
},
verifyAvailability: true,
timeout: 5000,
})
Providers:
esm.sh (default): Modern ESM CDN
jsdelivr: Fast, reliable CDN
unpkg: Popular npm CDN
Features:
- Scoped packages (
@radix-ui/react-popover)
- Subpath imports (
lodash/debounce)
- Version pinning via
versionMap
- Availability verification (optional)
Custom Strategies
Create your own resolution strategy:
import type { ResolverStrategy } from "@tapestrylab/resolve"
const myStrategy: ResolverStrategy = {
name: "my-custom-strategy",
async resolve(id: string, context?) {
if (shouldResolve(id)) {
return {
id,
path: "/resolved/path",
source: "local",
}
}
return null
},
}
const resolver = createResolver({
strategies: [myStrategy, strategies.cdn()],
})
Use Cases
Playground Environment
Resolve user-uploaded components and npm dependencies in the browser:
const resolver = createResolver({
strategies: [
strategies.local({
root: "/playground/uploads",
checkExists: false,
alias: {
"@ui": "components/ui",
},
}),
strategies.cdn({
provider: "esm.sh",
verifyAvailability: false,
}),
],
})
const userButton = await resolver.resolve("@ui/Button")
const radix = await resolver.resolve("@radix-ui/react-popover")
Monorepo support
Resolve internal packages and external dependencies:
const resolver = createResolver({
strategies: [
strategies.local({
root: "/monorepo",
alias: {
"@company/ui": "packages/ui/src",
"@company/core": "packages/core/src",
},
}),
],
})
const component = await resolver.resolve("@company/ui/Button")
Types
interface ResolvedEntry {
id: string
path: string
source?: "local" | "cdn" | "remote"
}
interface ResolutionContext {
root?: string
importer?: string
metadata?: Record<string, unknown>
}
interface ResolverStrategy {
name: string
resolve(id: string, context?: ResolutionContext): Promise<ResolvedEntry | null>
}
Performance
- Caching: Successful and failed resolutions are cached by default
- Parallel Resolution:
resolveMany() resolves multiple identifiers concurrently
- Lazy Verification: CDN availability checks can be disabled for faster resolution
- Early Exit: Strategy pipeline stops at first successful resolution
Browser Support
The package works in both Node.js and browser environments. For browser usage:
const resolver = createResolver({
strategies: [
strategies.local({
checkExists: false,
}),
strategies.cdn({
verifyAvailability: false,
}),
],
})
Development
pnpm install
pnpm build
pnpm test
pnpm dev
pnpm type-check
pnpm demo
Demo Script
The package includes an interactive demo script that showcases all resolver features using real fixtures:
pnpm demo
This will demonstrate:
- Local file resolution with and without aliases
- CDN package resolution from multiple providers
- Multi-strategy pipelines with fallbacks
- Parallel batch resolution
- Performance metrics
See scripts/README.md for more details.
License
MIT