mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 07:12:54 +01:00
381 lines
13 KiB
Markdown
381 lines
13 KiB
Markdown
# COMP-003: Component Export to Repository
|
||
|
||
## Overview
|
||
|
||
Enable users to export components from their project to a GitHub repository, creating a personal component library. This allows sharing components across projects and with team members.
|
||
|
||
## Context
|
||
|
||
Currently, component sharing is manual:
|
||
1. Export components as zip (Cmd+Shift+E)
|
||
2. Manually upload to GitHub or share file
|
||
3. Others download and import
|
||
|
||
This task streamlines the process:
|
||
1. Right-click component → "Export to Repository"
|
||
2. Select target repository
|
||
3. Component is committed with metadata
|
||
4. Available in NodePicker for other projects
|
||
|
||
### Existing Export Flow
|
||
|
||
From `exportProjectComponents.ts`:
|
||
```typescript
|
||
export function exportProjectComponents() {
|
||
ProjectImporter.instance.listComponentsAndDependencies(
|
||
ProjectModel.instance._retainedProjectDirectory,
|
||
(components) => {
|
||
// Shows export popup
|
||
// User selects components
|
||
// Creates zip file
|
||
}
|
||
);
|
||
}
|
||
```
|
||
|
||
## Requirements
|
||
|
||
### Functional Requirements
|
||
|
||
1. **Export Entry Points**
|
||
- Right-click component → "Export to Repository"
|
||
- Component sheet context menu → "Export Sheet to Repository"
|
||
- File menu → "Export Components to Repository"
|
||
|
||
2. **Repository Selection**
|
||
- List user's GitHub repositories
|
||
- "Create new repository" option
|
||
- Remember last used repository
|
||
- Suggest `noodl-components` naming convention
|
||
|
||
3. **Component Selection**
|
||
- Select individual components
|
||
- Select entire sheets
|
||
- Auto-select dependencies
|
||
- Preview what will be exported
|
||
|
||
4. **Metadata Entry**
|
||
- Component name (prefilled)
|
||
- Description
|
||
- Tags
|
||
- Version (auto-increment option)
|
||
- Category selection
|
||
|
||
5. **Export Process**
|
||
- Create component directory structure
|
||
- Generate prefab.json manifest
|
||
- Commit to repository
|
||
- Optional: Push immediately or stage
|
||
|
||
6. **Repository Structure**
|
||
- Standard directory layout
|
||
- index.json manifest for discovery
|
||
- README generation
|
||
- License file option
|
||
|
||
### Non-Functional Requirements
|
||
|
||
- Export completes in < 30 seconds
|
||
- Works with existing repositories
|
||
- Handles large components (100+ nodes)
|
||
- Conflict detection with existing exports
|
||
|
||
## Technical Approach
|
||
|
||
### 1. Repository Structure Convention
|
||
|
||
```
|
||
my-noodl-components/
|
||
├── index.json # Repository manifest
|
||
├── README.md # Auto-generated docs
|
||
├── LICENSE # Optional license
|
||
└── components/
|
||
├── my-button/
|
||
│ ├── prefab.json # Component metadata
|
||
│ ├── component.ndjson # Noodl component data
|
||
│ ├── dependencies/ # Style/variant dependencies
|
||
│ └── assets/ # Images, fonts
|
||
├── my-card/
|
||
│ └── ...
|
||
└── my-form/
|
||
└── ...
|
||
```
|
||
|
||
### 2. Repository Manifest (index.json)
|
||
|
||
```json
|
||
{
|
||
"$schema": "https://opennoodl.net/schemas/component-repo-v1.json",
|
||
"name": "My Noodl Components",
|
||
"description": "Personal component library",
|
||
"author": {
|
||
"name": "John Doe",
|
||
"github": "johndoe"
|
||
},
|
||
"version": "1.0.0",
|
||
"noodlVersion": ">=2.10.0",
|
||
"components": [
|
||
{
|
||
"id": "my-button",
|
||
"name": "My Button",
|
||
"description": "Custom styled button",
|
||
"version": "1.2.0",
|
||
"path": "components/my-button",
|
||
"tags": ["form", "button"],
|
||
"category": "Forms"
|
||
}
|
||
],
|
||
"updatedAt": "2024-01-15T10:30:00Z"
|
||
}
|
||
```
|
||
|
||
### 3. Component Export Service
|
||
|
||
```typescript
|
||
// packages/noodl-editor/src/editor/src/services/ComponentExportService.ts
|
||
|
||
interface ExportOptions {
|
||
components: ComponentModel[];
|
||
repository: GitHubRepo;
|
||
metadata: {
|
||
description: string;
|
||
tags: string[];
|
||
category: string;
|
||
version?: string;
|
||
};
|
||
commitMessage?: string;
|
||
pushImmediately?: boolean;
|
||
}
|
||
|
||
interface ExportResult {
|
||
success: boolean;
|
||
exportedComponents: string[];
|
||
commitSha?: string;
|
||
error?: string;
|
||
}
|
||
|
||
class ComponentExportService {
|
||
private static instance: ComponentExportService;
|
||
|
||
// Export flow
|
||
async exportToRepository(options: ExportOptions): Promise<ExportResult>;
|
||
|
||
// Repository management
|
||
async listUserRepositories(): Promise<GitHubRepo[]>;
|
||
async createComponentRepository(name: string): Promise<GitHubRepo>;
|
||
async validateRepository(repo: GitHubRepo): Promise<boolean>;
|
||
|
||
// Component preparation
|
||
async prepareExport(components: ComponentModel[]): Promise<ExportPackage>;
|
||
async resolveExportDependencies(components: ComponentModel[]): Promise<ComponentModel[]>;
|
||
|
||
// File generation
|
||
generatePrefabManifest(component: ComponentModel, metadata: ExportMetadata): PrefabManifest;
|
||
generateRepoManifest(repo: GitHubRepo, components: PrefabManifest[]): RepoManifest;
|
||
generateReadme(repo: GitHubRepo, components: PrefabManifest[]): string;
|
||
}
|
||
```
|
||
|
||
### 4. Export Modal Flow
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ Export to Repository [×] │
|
||
├─────────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ COMPONENTS TO EXPORT │
|
||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||
│ │ ☑ MyButton + 2 dependencies │ │
|
||
│ │ └─ ☑ ButtonStyles (variant) │ │
|
||
│ │ └─ ☑ PrimaryColor (color style) │ │
|
||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ TARGET REPOSITORY │
|
||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||
│ │ johndoe/noodl-components [▾] │ │
|
||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||
│ [+ Create new repository] │
|
||
│ │
|
||
│ METADATA │
|
||
│ Name: [My Button ] │
|
||
│ Description: [Custom styled button with loading state ] │
|
||
│ Tags: [form] [button] [+] │
|
||
│ Category: [Forms ▾] │
|
||
│ Version: [1.0.0 ] ☑ Auto-increment │
|
||
│ │
|
||
│ COMMIT │
|
||
│ Message: [Add MyButton component ] │
|
||
│ ☑ Push to GitHub immediately │
|
||
│ │
|
||
│ [Cancel] [Export] │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 5. Export Process Flow
|
||
|
||
```typescript
|
||
async exportToRepository(options: ExportOptions): Promise<ExportResult> {
|
||
const { components, repository, metadata } = options;
|
||
|
||
// 1. Clone or open repository locally
|
||
const localRepo = await this.getLocalRepository(repository);
|
||
|
||
// 2. Resolve all dependencies
|
||
const allComponents = await this.resolveExportDependencies(components);
|
||
|
||
// 3. Generate component files
|
||
for (const component of allComponents) {
|
||
const componentDir = path.join(localRepo.path, 'components', component.id);
|
||
|
||
// Export component data
|
||
await this.exportComponentData(component, componentDir);
|
||
|
||
// Generate prefab manifest
|
||
const manifest = this.generatePrefabManifest(component, metadata);
|
||
await fs.writeJson(path.join(componentDir, 'prefab.json'), manifest);
|
||
}
|
||
|
||
// 4. Update repository manifest
|
||
const repoManifest = await this.updateRepoManifest(localRepo, allComponents);
|
||
|
||
// 5. Update README
|
||
await this.updateReadme(localRepo, repoManifest);
|
||
|
||
// 6. Commit changes
|
||
const git = new Git(mergeProject);
|
||
await git.openRepository(localRepo.path);
|
||
await git.commit(options.commitMessage || `Add ${components[0].name}`);
|
||
|
||
// 7. Push if requested
|
||
if (options.pushImmediately) {
|
||
await git.push({});
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
exportedComponents: allComponents.map(c => c.name),
|
||
commitSha: await git.getHeadCommitId()
|
||
};
|
||
}
|
||
```
|
||
|
||
## Files to Create
|
||
|
||
1. `packages/noodl-editor/src/editor/src/services/ComponentExportService.ts`
|
||
2. `packages/noodl-core-ui/src/components/modals/ExportToRepoModal/ExportToRepoModal.tsx`
|
||
3. `packages/noodl-core-ui/src/components/modals/ExportToRepoModal/ComponentSelector.tsx`
|
||
4. `packages/noodl-core-ui/src/components/modals/ExportToRepoModal/RepoSelector.tsx`
|
||
5. `packages/noodl-core-ui/src/components/modals/ExportToRepoModal/MetadataForm.tsx`
|
||
6. `packages/noodl-core-ui/src/components/modals/CreateRepoModal/CreateRepoModal.tsx`
|
||
7. `packages/noodl-editor/src/editor/src/utils/componentExporter.ts` - Low-level export utilities
|
||
|
||
## Files to Modify
|
||
|
||
1. `packages/noodl-editor/src/editor/src/views/nodegrapheditor.js`
|
||
- Add right-click context menu option
|
||
|
||
2. `packages/noodl-editor/src/editor/src/views/panels/componentspanel.tsx`
|
||
- Add export option to component context menu
|
||
|
||
3. `packages/noodl-editor/src/editor/src/utils/exportProjectComponents.ts`
|
||
- Refactor to share code with repository export
|
||
|
||
4. `packages/noodl-editor/src/editor/src/models/prefab/sources/GitHubPrefabSource.ts`
|
||
- Implement full source for reading from component repos
|
||
|
||
## Implementation Steps
|
||
|
||
### Phase 1: Export Service Foundation
|
||
1. Create ComponentExportService
|
||
2. Implement dependency resolution
|
||
3. Create file generation utilities
|
||
4. Define repository structure
|
||
|
||
### Phase 2: Repository Management
|
||
1. List user repositories (via GitHub API)
|
||
2. Create new repository flow
|
||
3. Local repository management
|
||
4. Clone/pull existing repos
|
||
|
||
### Phase 3: Export Modal
|
||
1. Create ExportToRepoModal
|
||
2. Create ComponentSelector
|
||
3. Create RepoSelector
|
||
4. Create MetadataForm
|
||
|
||
### Phase 4: Git Integration
|
||
1. Stage exported files
|
||
2. Commit with message
|
||
3. Push to remote
|
||
4. Handle conflicts
|
||
|
||
### Phase 5: Context Menu Integration
|
||
1. Add to component right-click menu
|
||
2. Add to sheet context menu
|
||
3. Add to File menu
|
||
|
||
### Phase 6: Testing & Polish
|
||
1. Test with various component types
|
||
2. Test dependency resolution
|
||
3. Error handling
|
||
4. Progress indication
|
||
|
||
## Testing Checklist
|
||
|
||
- [ ] Export single component works
|
||
- [ ] Export multiple components works
|
||
- [ ] Dependencies auto-selected
|
||
- [ ] Repository selection lists repos
|
||
- [ ] Create new repository works
|
||
- [ ] Metadata saved correctly
|
||
- [ ] Files committed to repo
|
||
- [ ] Push to GitHub works
|
||
- [ ] Repository manifest updated
|
||
- [ ] README generated/updated
|
||
- [ ] Handles existing components (update)
|
||
- [ ] Version auto-increment works
|
||
- [ ] Error messages helpful
|
||
|
||
## Dependencies
|
||
|
||
- COMP-001 (Prefab System Refactoring)
|
||
- GIT-001 (GitHub OAuth) - for repository access
|
||
|
||
## Blocked By
|
||
|
||
- COMP-001
|
||
- GIT-001
|
||
|
||
## Blocks
|
||
|
||
- COMP-004 (Organization Components)
|
||
- COMP-005 (Component Import with Version Control)
|
||
|
||
## Estimated Effort
|
||
|
||
- Export service: 4-5 hours
|
||
- Repository management: 3-4 hours
|
||
- Export modal: 4-5 hours
|
||
- Git integration: 3-4 hours
|
||
- Context menu: 2-3 hours
|
||
- Testing & polish: 3-4 hours
|
||
- **Total: 19-25 hours**
|
||
|
||
## Success Criteria
|
||
|
||
1. Components can be exported via right-click
|
||
2. Dependencies are automatically included
|
||
3. Repository structure is consistent
|
||
4. Manifests are generated correctly
|
||
5. Git operations work smoothly
|
||
6. Components are importable via COMP-004+
|
||
|
||
## Future Enhancements
|
||
|
||
- Export to npm package
|
||
- Export to Noodl marketplace
|
||
- Batch export multiple components
|
||
- Export templates/starters
|
||
- Preview component before export
|
||
- Export history/versioning
|