@tapestrylab/extract
v0.3.1
Published
Extract component metadata from source files for Tapestry design systems
Maintainers
Readme
@tapestrylab/extract
A powerful, fast component metadata extractor for component libraries. Parse React components and automatically extract structured metadata including names, props, types, defaults, and documentation.
Features
- ⚡ Lightning Fast: Built on
oxc-parser(Rust-based parser for blazing-fast AST parsing) - 📦 Component Extraction: Automatically finds and analyzes React components
- 🏷️ Prop Detection: Extracts prop names, types, defaults, and descriptions from JSDoc
- 🔤 TypeScript Support: Full TypeScript type information and complex type serialization
- 🎯 Flexible Matching: Include/exclude patterns for fine-grained file selection
- 📝 Multiple Formats: Outputs structured metadata JSON for use in dev/design tools
- 🔌 Plugin Architecture: Extensible system for adding custom extractors
Quick Start
Installation
npm install @tapestrylab/extract
# or
pnpm add @tapestrylab/extractUsage
CLI Usage: For command-line usage, install
@tapestrylab/cliwhich provides the unifiedtapestrycommand. See the CLI documentation for details.
Programmatic API
import { extract } from "@tapestrylab/extract";
// Simple usage - includes and excludes use smart defaults
const result = await extract({
root: "./src",
});
console.log(result.metadata); // Component metadata
console.log(result.stats); // Extraction statistics
// Or with custom patterns
const customResult = await extract({
root: "./src",
include: ["**/*.tsx"], // Optional - defaults to common patterns
exclude: ["**/*.test.tsx"], // Optional - defaults exclude tests, node_modules, etc.
});Convenience Functions:
import { extractComponent, extractFromPattern, extractFromDirectory } from "@tapestrylab/extract";
// Extract a single component
const button = await extractComponent('./src/components/Button.tsx');
// Extract components matching a pattern
const components = await extractFromPattern('**/*.tsx', {
root: './src'
});
// Extract from a directory (non-recursive by default)
const allComponents = await extractFromDirectory('./src/components');
// Extract recursively
const recursive = await extractFromDirectory('./src/components', {
recursive: true
});Browser-Compatible Core API
For edge runtimes, browsers, or environments without Node.js (like Convex, Cloudflare Workers, or Vercel Edge Functions), use the /core export which provides pure extraction without file system dependencies:
import { createReactExtractor } from "@tapestrylab/extract/core";
// Create an extractor
const extractor = createReactExtractor();
// Extract from in-memory code (no file I/O needed)
const sourceCode = `
export function Button({ label }: { label: string }) {
return <button>{label}</button>;
}
`;
const metadata = await extractor.extract("Button.tsx", sourceCode);
console.log(metadata); // [{ name: "Button", props: [...], ... }]Use cases for /core:
- ✅ Convex functions (TypeScript edge runtime)
- ✅ Cloudflare Workers / Vercel Edge Functions
- ✅ Browser-based code editors (CodeSandbox, StackBlitz)
- ✅ Build tools that work with in-memory code
- ✅ Any environment without Node.js file system APIs
The core API only extracts metadata from code strings - you're responsible for providing the source code (from a database, API, user input, etc.).
Configuration
Create a tapestry.config.js in your project root:
export default {
root: "./src",
include: ["**/*.tsx", "**/*.jsx"],
exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
output: "./metadata.json",
};Or use other config file formats:
.tapestryrc.tapestryrc.json.tapestryrc.jstapestry.config.mjstapestry.config.cjs
Example Output
{
"metadata": [
{
"name": "Button",
"filePath": "src/components/Button.tsx",
"exportType": "default",
"description": "A customizable button component",
"props": [
{
"name": "label",
"type": "string",
"required": true,
"description": "Button label text"
},
{
"name": "variant",
"type": "primary | secondary | danger",
"required": false,
"defaultValue": "primary",
"description": "Button style variant"
}
]
}
],
"stats": {
"filesScanned": 15,
"filesProcessed": 15,
"componentsFound": 8,
"duration": 245
}
}Development
Requirements
- Node 22+ (use
fnm useto switch to the correct version) - pnpm (fast, disk space efficient package manager)
Setup
# Install dependencies
pnpm install
# Watch mode development
pnpm dev
# Type checking
pnpm type-checkTesting
# Run extraction on test fixtures
pnpm test:extract
# Run full test suite
pnpm test:ciBuilding
# Build the project
pnpm buildArchitecture
The extraction pipeline consists of three main stages:
- Configuration: Loads settings from config files or CLI arguments using
cosmiconfig - Scanning: Uses
fast-globto find matching files based on include/exclude patterns - Extraction: Routes files to appropriate extractors via the plugin system
Plugin System
@tapestrylab/extract uses a flexible plugin architecture. The current React extractor handles TypeScript and JavaScript files:
- Parses files into an Abstract Syntax Tree (AST)
- Identifies components using PascalCase naming conventions
- Validates JSX return statements
- Extracts typed props with defaults
- Parses JSDoc comments for descriptions
Supported Component Patterns
// Function declarations
export function Button({ label }: { label: string }) {
return <button>{label}</button>;
}
// Arrow functions
export const Card = ({ title }: Props) => {
return <div>{title}</div>;
};
// Default exports
export default function Modal() {
return <div role="dialog" />;
}
// With JSDoc
/**
* A reusable button component
* @param {string} label - Display text
*/
export function Button({ label }) {
return <button>{label}</button>;
}Contributing
We welcome contributions! Whether you're fixing bugs, adding features, or improving documentation, your help makes this project better.
For detailed instructions on how to contribute, including step-by-step commands for every phase of the process, see CONTRIBUTING.md.
Quick start:
git checkout -b feat/your-feature
pnpm install
pnpm dev
# ... make your changes ...
pnpm build && pnpm test:ci
pnpm changeset
git add . && git commit -m "feat: describe your changes"
git push origin feat/your-featureThen open a pull request on GitHub!
License
MIT
Support
- 📖 Documentation - Architecture and implementation details
- 🐛 Report Issues
- 💬 Discussions
