Finished task 1. Added task for backwards compatibility on existing Noodl projects

This commit is contained in:
Richard Osborne
2025-12-06 23:24:55 +01:00
parent 9a5952ec13
commit 2153baf627
14 changed files with 1147 additions and 84 deletions

View File

@@ -4,112 +4,136 @@ Track all changes made during this task. Update this file as you work.
---
## [Date] - [Your Name/Handle]
## [2025-06-12] - Cline (AI-assisted)
### Summary
[To be filled in as work progresses]
Fixed React 19 TypeScript compatibility errors that were preventing the build from completing. The previous developer updated dependencies but did not address all the TypeScript compatibility issues with the new `@types/react` package.
### Starting Point
- Based on branch: `12-upgrade-dependencies`
- Previous work by: [previous developer]
- Previous work by: previous developer
- Previous commits include:
- Package.json dependency updates
- Package.json dependency updates (React 17 → 19)
- "Update rendering to use non-deprecated react-dom calls"
---
## Dependency Fixes
## React 19 TypeScript Fixes
### Package: [package-name]
- **Issue**: [What was wrong]
- **Fix**: [What was changed]
- **File**: `path/to/package.json`
### Issue 1: Unused `@ts-expect-error` directives
React 19's types fixed some underlying issues that were previously suppressed with `@ts-expect-error`. These now cause errors when the underlying issue no longer exists.
---
### Issue 2: `useRef()` requires explicit type parameter
In React 19's types, `useRef()` without a type parameter returns `RefObject<unknown>`, which is not assignable to more specific ref types.
## React 19 Migration
**Fix**: Changed `useRef()` to `useRef<HTMLDivElement>(null)`
### File: [filename]
- **Before**: `ReactDOM.render(<App />, element)`
- **After**: `createRoot(element).render(<App />)`
- **Notes**: [Any relevant context]
### Issue 3: `JSX` namespace moved
In React 19, `JSX` is no longer a global namespace. It must be accessed as `React.JSX`.
---
**Fix**: Changed `keyof JSX.IntrinsicElements` to `keyof React.JSX.IntrinsicElements`
## react-instantsearch Migration
### Issue 4: `ReactFragment` export removed
React 19 no longer exports `ReactFragment`. Use `Iterable<React.ReactNode>` instead.
### File: HelpCenter.tsx
- **Before**: `import { ... } from 'react-instantsearch-hooks-web'`
- **After**: `import { ... } from 'react-instantsearch'`
- **API Changes**: [List any API differences encountered]
### Issue 5: `children` not implicitly included in props
React 19 no longer implicitly includes `children` in component props. It must be explicitly declared.
**Fix**: Added `children?: React.ReactNode` to component prop interfaces.
### Issue 6: `ReactDOM.findDOMNode` removed
React 19 removed the deprecated `findDOMNode` API.
**Fix**: Access DOM elements directly from refs rather than using `findDOMNode`.
---
## Build Fixes
### Error: [Error message]
- **Cause**: [Why it happened]
- **Fix**: [What was changed]
- **File**: `path/to/file`
### Error: TS2578: Unused '@ts-expect-error' directive
- **Cause**: React 19 types fixed the type inference for `React.cloneElement()`
- **Fix**: Removed the `@ts-expect-error` comment
- **Files**:
- `packages/noodl-core-ui/src/components/layout/Columns/Columns.tsx`
- `packages/noodl-viewer-react/src/components/visual/Columns/Columns.tsx`
---
### Error: TS2554: Expected 1 arguments, but got 0 (useRef)
- **Cause**: React 19's types require an initial value for `useRef()`
- **Fix**: Added type parameter and null initial value: `useRef<HTMLDivElement>(null)`
- **Files**:
- `packages/noodl-core-ui/src/components/app/SideNavigation/SideNavigation.tsx`
- `packages/noodl-core-ui/src/components/popups/ContextMenu/ContextMenu.tsx`
## Build Optimizations
### Error: TS2322: RefObject<unknown> not assignable to RefObject<HTMLElement>
- **Cause**: Untyped `useRef()` returns `RefObject<unknown>`
- **Fix**: Same as above - add explicit type parameter
- **Files**: Same as above
### Optimization: [Name]
- **Before**: Build time X seconds
- **After**: Build time Y seconds
- **Change**: [What was optimized]
### Error: TS2305: Module 'react' has no exported member 'ReactFragment'
- **Cause**: `ReactFragment` was removed in React 19
- **Fix**: Replaced with `Iterable<React.ReactNode>`
- **File**: `packages/noodl-viewer-react/src/types.ts`
### Error: TS2503: Cannot find namespace 'JSX'
- **Cause**: `JSX` is no longer a global namespace in React 19
- **Fix**: Changed `JSX.IntrinsicElements` to `React.JSX.IntrinsicElements`
- **Files**:
- `packages/noodl-viewer-react/src/components/visual/Group/Group.tsx`
- `packages/noodl-viewer-react/src/components/visual/Text/Text.tsx`
### Error: TS2339: Property 'children' does not exist on type
- **Cause**: React 19 no longer implicitly includes `children` in props
- **Fix**: Added `children?: React.ReactNode` to component interfaces
- **Files**:
- `packages/noodl-viewer-react/src/components/visual/Drag/Drag.tsx`
- `packages/noodl-viewer-react/src/components/visual/Group/Group.tsx`
### Error: Property 'findDOMNode' does not exist on ReactDOM
- **Cause**: `findDOMNode` removed from React 19
- **Fix**: Access DOM element directly from ref instead of using findDOMNode
- **File**: `packages/noodl-viewer-react/src/components/visual/Group/Group.tsx`
---
## Files Modified
<!-- Update this list as you make changes -->
- [ ] `package.json` (root)
- [ ] `packages/noodl-editor/package.json`
- [ ] `packages/noodl-core-ui/package.json`
- [ ] `packages/noodl-viewer-react/package.json`
- [ ] `packages/noodl-editor/src/editor/src/views/HelpCenter/HelpCenter.tsx`
- [ ] [Add more as needed]
- [x] `packages/noodl-core-ui/src/components/layout/Columns/Columns.tsx`
- [x] `packages/noodl-core-ui/src/components/app/SideNavigation/SideNavigation.tsx`
- [x] `packages/noodl-core-ui/src/components/popups/ContextMenu/ContextMenu.tsx`
- [x] `packages/noodl-viewer-react/src/types.ts`
- [x] `packages/noodl-viewer-react/src/components/visual/Columns/Columns.tsx`
- [x] `packages/noodl-viewer-react/src/components/visual/Drag/Drag.tsx`
- [x] `packages/noodl-viewer-react/src/components/visual/Group/Group.tsx`
- [x] `packages/noodl-viewer-react/src/components/visual/Text/Text.tsx`
---
## Files Created
<!-- List any new files created -->
- None expected for this task
- None
---
## Files Deleted
<!-- List any files removed -->
- None expected for this task
- None
---
## Breaking Changes
- React 19 requires Node.js 18+ (documented in root package.json engines)
- [Add any other breaking changes discovered]
- `findDOMNode` usage removed - code now accesses refs directly
---
## Testing Notes
### Automated Tests
- `npm run test:editor`: [PASS/FAIL] - [Notes]
- `npm run test:platform`: [PASS/FAIL] - [Notes]
- `npx tsc --noEmit`: [PASS/FAIL] - [Notes]
- `npm run dev`: **PASS** - All three packages (Editor, Viewer, Cloud) compile successfully
### Manual Tests
- Dev server start: [PASS/FAIL]
- Create project: [PASS/FAIL]
- Node operations: [PASS/FAIL]
- Preview: [PASS/FAIL]
- Help Center search: [PASS/FAIL]
- Hot reload: [PASS/FAIL]
- Dev server start: **PASS** - Editor launches, Viewer compiles, Cloud compiles
---
@@ -117,24 +141,20 @@ Track all changes made during this task. Update this file as you work.
<!-- Document any issues discovered that aren't fixed in this task -->
1. [Issue description] - [Will be addressed in TASK-XXX]
1. Deprecation warnings for Sass legacy JS API - Non-blocking, can be addressed in future task
2. Deprecation warning for `Buffer()` - Non-blocking, comes from dependency
---
## Follow-up Tasks
<!-- Note any work that should be done in future tasks -->
1. [Follow-up item] - Suggested for TASK-XXX
1. Consider updating Sass configuration to use modern API - Future task
2. Review `scrollToElement` functionality to ensure it works correctly with the new ref-based approach - Manual testing needed
---
## Final Summary
[To be completed when task is done]
**Total files modified**: X
**Total lines changed**: +X / -Y
**Build time**: Before X sec → After Y sec
**Tests**: All passing / X failures
**Confidence**: X/10
**Total files modified**: 8
**Build status**: All packages compiling successfully (was 95 errors, now 0)
**Confidence**: 8/10 (code compiles, but manual testing of `scrollToElement` functionality recommended)

View File

@@ -0,0 +1,69 @@
# TASK-002 Changelog: Legacy Project Migration
---
## [2025-01-XX] - Task Created
### Summary
Task documentation created for legacy project migration and backward compatibility system.
### Files Created
- `dev-docs/tasks/phase-1/TASK-002-legacy-project-migration/README.md` - Full task specification
- `dev-docs/tasks/phase-1/TASK-002-legacy-project-migration/CHECKLIST.md` - Implementation checklist
- `dev-docs/tasks/phase-1/TASK-002-legacy-project-migration/CHANGELOG.md` - This file
- `dev-docs/tasks/phase-1/TASK-002-legacy-project-migration/NOTES.md` - Working notes
### Notes
- This task depends on TASK-001 (Dependency Updates) being complete or in progress
- Critical for ensuring existing Noodl users can migrate their production projects
- Scope includes CLI tool, migration engine, and editor integration
---
## Template for Future Entries
```markdown
## [YYYY-MM-DD] - [Phase/Step Name]
### Summary
[Brief description of what was accomplished]
### Files Created
- `path/to/file.ts` - [Purpose]
### Files Modified
- `path/to/file.ts` - [What changed and why]
### Files Deleted
- `path/to/file.ts` - [Why removed]
### Breaking Changes
- [Any breaking changes and migration path]
### Testing Notes
- [What was tested]
- [Any edge cases discovered]
### Known Issues
- [Any remaining issues or follow-up needed]
### Next Steps
- [What needs to be done next]
```
---
## Progress Summary
| Phase | Status | Date Started | Date Completed |
|-------|--------|--------------|----------------|
| Phase 1: Research & Discovery | Not Started | - | - |
| Phase 2: Version Detection | Not Started | - | - |
| Phase 3: Migration Engine | Not Started | - | - |
| Phase 4: Individual Migrations | Not Started | - | - |
| Phase 5: Backup System | Not Started | - | - |
| Phase 6: CLI Tool | Not Started | - | - |
| Phase 7: Editor Integration | Not Started | - | - |
| Phase 8: Validation & Testing | Not Started | - | - |
| Phase 9: Documentation | Not Started | - | - |
| Phase 10: Completion | Not Started | - | - |

View File

@@ -0,0 +1,330 @@
# TASK-002 Checklist: Legacy Project Migration
## Prerequisites
- [ ] Read README.md completely
- [ ] Understand the scope and success criteria
- [ ] Ensure TASK-001 (Dependency Updates) is complete or in progress
- [ ] Create branch: `git checkout -b task/002-legacy-project-migration`
- [ ] Verify build works: `npm run build:editor`
---
## Phase 1: Research & Discovery
### Project Format Analysis
- [ ] Locate sample Noodl projects (old versions)
- [ ] Document the folder structure of a `.noodl` project
- [ ] Identify all JSON file types within projects
- [ ] Document schema for each file type
- [ ] Check for existing version metadata in project files
- [ ] Update NOTES.md with findings
### Node Definition Analysis
- [ ] Catalog all node types in `packages/noodl-runtime/src/nodes/`
- [ ] Document input/output schemas for nodes
- [ ] Identify any deprecated node types
- [ ] Note any node API changes over versions
- [ ] Update NOTES.md with findings
### Breaking Changes Audit
- [ ] Review TASK-001 dependency update list
- [ ] For each updated dependency, identify breaking changes:
- [ ] React 17 → 19 impacts
- [ ] react-instantsearch changes
- [ ] Other dependency changes
- [ ] Map breaking changes to project file impact
- [ ] Create comprehensive migration requirements list
- [ ] Update NOTES.md with findings
---
## Phase 2: Version Detection System
### Design
- [ ] Define `ProjectVersion` interface
- [ ] Define version detection strategy
- [ ] Document how to infer version from project structure
### Implementation
- [ ] Create `packages/noodl-editor/src/editor/src/utils/migration/` folder
- [ ] Create `version-detect.ts` module
- [ ] Implement explicit version metadata check
- [ ] Implement file structure inference
- [ ] Implement node usage pattern inference
- [ ] Add fallback for "unknown/legacy" projects
### Testing
- [ ] Write unit tests for version detection
- [ ] Test with sample projects from different versions
- [ ] Document in CHANGELOG.md
---
## Phase 3: Migration Engine Core
### Design
- [ ] Define `Migration` interface
- [ ] Define `MigrationResult` interface
- [ ] Design migration path calculation algorithm
- [ ] Document migration registration pattern
### Implementation
- [ ] Create `migration-engine.ts` module
- [ ] Implement `MigrationEngine` class
- [ ] Implement `registerMigration()` method
- [ ] Implement `getMigrationPath()` method
- [ ] Implement `migrateProject()` method
- [ ] Implement rollback capability
- [ ] Add progress reporting hooks
### Testing
- [ ] Write unit tests for migration engine
- [ ] Test migration path calculation
- [ ] Test rollback functionality
- [ ] Document in CHANGELOG.md
---
## Phase 4: Individual Migrations
### Migration: React 17 → 19 Patterns
- [ ] Identify all React-specific patterns in project files
- [ ] Create `v17-to-v19-react.ts` migration
- [ ] Write migration transform logic
- [ ] Write validation logic
- [ ] Write unit tests
### Migration: Node Format Changes (if needed)
- [ ] Identify node format changes between versions
- [ ] Create `node-format-update.ts` migration
- [ ] Write migration transform logic
- [ ] Write validation logic
- [ ] Write unit tests
### Migration: Connection Schema (if needed)
- [ ] Identify connection schema changes
- [ ] Create `connection-schema.ts` migration
- [ ] Write migration transform logic
- [ ] Write validation logic
- [ ] Write unit tests
### Additional Migrations (as discovered)
- [ ] Document each new migration needed
- [ ] Implement migrations as needed
- [ ] Write tests for each
---
## Phase 5: Backup System
### Implementation
- [ ] Create `backup.ts` utility module
- [ ] Implement `backupProject()` function
- [ ] Implement `restoreFromBackup()` function
- [ ] Implement backup verification (checksums)
- [ ] Implement backup cleanup/rotation
### Testing
- [ ] Test backup creates valid copy
- [ ] Test restore works correctly
- [ ] Test with large projects
- [ ] Document in CHANGELOG.md
---
## Phase 6: CLI Tool
### Package Setup
- [ ] Create `packages/noodl-cli/` directory structure
- [ ] Create `package.json` with dependencies
- [ ] Create `tsconfig.json`
- [ ] Set up build scripts
- [ ] Add to root workspace configuration
### Commands Implementation
- [ ] Implement `validate` command
- [ ] Parse project path argument
- [ ] Run version detection
- [ ] Report findings
- [ ] Return exit code
- [ ] Implement `upgrade` command
- [ ] Parse arguments (project path, options)
- [ ] Create backup
- [ ] Run migrations
- [ ] Report results
- [ ] Implement `batch-upgrade` command
- [ ] Parse folder argument
- [ ] Discover all projects
- [ ] Process each project
- [ ] Generate summary report
- [ ] Implement `report` command
- [ ] Analyze project
- [ ] Generate markdown report
- [ ] Output to stdout
### CLI UX
- [ ] Add help messages for all commands
- [ ] Add `--dry-run` option
- [ ] Add `--verbose` option
- [ ] Add `--no-backup` option (with warning)
- [ ] Add progress indicators
- [ ] Add colored output
### Testing
- [ ] Write integration tests for CLI
- [ ] Test each command
- [ ] Test error handling
- [ ] Document in CHANGELOG.md
### Documentation
- [ ] Create CLI README.md
- [ ] Document all commands and options
- [ ] Add usage examples
---
## Phase 7: Editor Integration
### Migration Dialog UI
- [ ] Design migration dialog mockup
- [ ] Create `MigrationDialog/` component folder
- [ ] Implement `MigrationDialog.tsx`
- [ ] Implement `MigrationDialog.module.scss`
- [ ] Add progress indicator
- [ ] Add backup confirmation
- [ ] Add cancel option
### Project Loading Integration
- [ ] Locate project loading code (likely `projectmodel.js`)
- [ ] Add version detection on project open
- [ ] Add migration check logic
- [ ] Trigger migration dialog when needed
- [ ] Handle user choices (migrate/cancel)
- [ ] Show progress during migration
- [ ] Handle migration errors gracefully
### Testing
- [ ] Test opening legacy project triggers dialog
- [ ] Test migration completes successfully
- [ ] Test cancellation works
- [ ] Test error handling
- [ ] Manual testing scenarios
- [ ] Document in CHANGELOG.md
---
## Phase 8: Validation & Testing
### Test Project Corpus
- [ ] Collect/create minimal test project
- [ ] Collect/create complex test project
- [ ] Collect/create project with deprecated nodes
- [ ] Collect/create project with custom JavaScript
- [ ] Collect/create project from each known version
- [ ] Document all test projects
### Integration Testing
- [ ] Run CLI migration on all test projects
- [ ] Verify each migrated project opens correctly
- [ ] Verify node graphs render correctly
- [ ] Verify connections work correctly
- [ ] Verify preview runs correctly
- [ ] Document any failures
### Edge Cases
- [ ] Test with corrupted project files
- [ ] Test with missing files
- [ ] Test with extremely large projects
- [ ] Test with read-only filesystem
- [ ] Test interrupted migration (power loss scenario)
- [ ] Document findings
---
## Phase 9: Documentation
### User Documentation
- [ ] Create migration guide for users
- [ ] Document what changes during migration
- [ ] Document how to manually fix issues
- [ ] Add FAQ section
- [ ] Add troubleshooting guide
### Developer Documentation
- [ ] Document migration engine architecture
- [ ] Document how to add new migrations
- [ ] Document testing procedures
- [ ] Update NOTES.md with learnings
### Update Existing Docs
- [ ] Update main README if needed
- [ ] Update dev-docs if needed
- [ ] Link to this task from relevant docs
---
## Phase 10: Completion
### Final Validation
- [ ] All success criteria from README.md met
- [ ] All unit tests pass
- [ ] All integration tests pass
- [ ] Manual testing complete
- [ ] No TypeScript errors: `npx tsc --noEmit`
- [ ] No console warnings/errors
### Cleanup
- [ ] Remove any debug code
- [ ] Remove any TODO comments (or convert to issues)
- [ ] Clean up NOTES.md
- [ ] Finalize CHANGELOG.md
### Submission
- [ ] Self-review all changes
- [ ] Create pull request
- [ ] Update task status
- [ ] Notify stakeholders
---
## Quick Reference
### Key Files
```
packages/noodl-cli/ # CLI tool package
packages/noodl-editor/src/editor/src/utils/migration/
├── version-detect.ts # Version detection
├── migration-engine.ts # Core engine
├── backup.ts # Backup utilities
└── migrations/ # Individual migrations
├── index.ts
├── v17-to-v19-react.ts
└── ...
packages/noodl-editor/src/editor/src/views/MigrationDialog/
├── MigrationDialog.tsx
└── MigrationDialog.module.scss
```
### Useful Commands
```bash
# Build editor
npm run build:editor
# Run tests
npm run test:editor
# Type check
npx tsc --noEmit
# Search for patterns
grep -r "pattern" packages/ --include="*.ts"
# Run CLI locally
node packages/noodl-cli/bin/noodl-migrate.js validate ./test-project
```
### Emergency Rollback
If migration breaks something:
1. Restore from backup folder
2. Disable migration in project loading code
3. Document the issue in NOTES.md

View File

@@ -0,0 +1,217 @@
# TASK-002 Working Notes: Legacy Project Migration
## Research Findings
### Project File Structure
> Document findings about Noodl project structure here
**TODO:** Analyze sample .noodl project folders
```
Expected structure (to be verified):
my-project/
├── project.json # Project metadata
├── components/ # Component definitions
│ └── component-name/
│ └── component.json
├── pages/ # Page definitions
├── styles/ # Style definitions
└── assets/ # Project assets (if any)
```
### Known Version Indicators
> Document where version information is stored
**TODO:** Check these locations for version metadata:
- [ ] `project.json` root object
- [ ] File header comments
- [ ] Metadata fields in component files
- [ ] Any `.noodl-version` or similar files
### Node Definition Changes
> Track changes to node definitions across versions
| Node Type | Old API | New API | Version Changed |
|-----------|---------|---------|-----------------|
| TBD | TBD | TBD | TBD |
---
## Breaking Changes Map
### From TASK-001 Dependency Updates
#### React 17 → 19
| Change | Impact on Projects | Migration Strategy |
|--------|-------------------|-------------------|
| `ReactDOM.render()` deprecated | May affect stored render patterns | TBD |
| New Suspense behavior | May affect loading states | TBD |
| Concurrent features | May affect event handling | TBD |
#### react-instantsearch-hooks-web → react-instantsearch
| Change | Impact on Projects | Migration Strategy |
|--------|-------------------|-------------------|
| Package rename | Import paths | N/A (editor code only) |
| API changes | TBD | TBD |
#### Other Dependencies
> Add findings here as TASK-001 progresses
---
## Design Decisions
### Migration Engine Architecture
**Decision:** [TBD]
**Alternatives Considered:**
1. Option A: ...
2. Option B: ...
**Rationale:** ...
### Backup Strategy
**Decision:** [TBD]
**Options:**
1. In-place backup (`.backup` folder in project)
2. External backup location (user-configurable)
3. Timestamped copies
**Rationale:** ...
### CLI Tool Location
**Decision:** [TBD]
**Options:**
1. New `packages/noodl-cli/` package
2. Add to existing `packages/noodl-platform-node/`
3. Scripts in `scripts/` directory
**Rationale:** ...
---
## Questions & Answers
### Q: Where is project version stored?
**A:** [TBD - needs research]
### Q: What's the oldest supported Noodl version?
**A:** [TBD - needs community input]
### Q: Do we have sample legacy projects for testing?
**A:** [TBD - need to source these]
### Q: Should migration be automatic or opt-in?
**A:** [TBD - needs UX decision]
---
## Gotchas & Surprises
> Document unexpected discoveries here
### [Discovery 1]
- **Date:** TBD
- **Finding:** ...
- **Impact:** ...
- **Resolution:** ...
---
## Debug Log
### Research Phase
```
[Date/Time] - Starting project format analysis
- Trying: ...
- Result: ...
- Next: ...
```
---
## Useful References
### Codebase Locations
```bash
# Project loading code
packages/noodl-editor/src/editor/src/models/projectmodel.js
# Node definitions
packages/noodl-runtime/src/nodes/
# Runtime context
packages/noodl-runtime/src/nodecontext.js
# Viewer React components
packages/noodl-viewer-react/src/nodes/
```
### Search Commands
```bash
# Find project loading logic
grep -r "loadProject\|openProject" packages/noodl-editor/ --include="*.ts" --include="*.js"
# Find version references
grep -r "version" packages/noodl-editor/src/editor/src/models/ --include="*.ts" --include="*.js"
# Find serialization logic
grep -r "serialize\|deserialize\|toJSON\|fromJSON" packages/ --include="*.ts" --include="*.js"
```
### External Documentation
- React 19 Migration: https://react.dev/blog/2024/04/25/react-19
- react-instantsearch v7: https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/react/
---
## Community Feedback
> Collect feedback from Noodl users about migration concerns
### User Concerns
1. [TBD]
### User Requests
1. [TBD]
### Known Legacy Projects in the Wild
1. [TBD - need to identify common project patterns]
---
## Test Project Inventory
| Name | Version | Complexity | Contains | Location |
|------|---------|------------|----------|----------|
| TBD | TBD | TBD | TBD | TBD |
---
## Migration Algorithm Pseudocode
```
function migrateProject(projectPath):
1. detectVersion(projectPath)
2. if currentVersion >= targetVersion:
return SUCCESS (no migration needed)
3. migrationPath = calculateMigrationPath(currentVersion, targetVersion)
4. if migrationPath.length == 0:
return ERROR (no migration path)
5. backup = createBackup(projectPath)
6. for migration in migrationPath:
result = migration.execute(projectPath)
if result.failed:
restoreBackup(backup)
return ERROR (migration failed)
updateVersionMetadata(projectPath, migration.toVersion)
7. validate(projectPath)
8. return SUCCESS
```
---
## Open Items
- [ ] Get access to legacy Noodl projects for testing
- [ ] Confirm oldest version we need to support
- [ ] Determine if cloud configurations need migration
- [ ] Design migration dialog UX
- [ ] Decide on CLI package location and build strategy

View File

@@ -0,0 +1,421 @@
# TASK-002: Legacy Project Migration & Backward Compatibility
## Metadata
| Field | Value |
|-------|-------|
| **ID** | TASK-002 |
| **Phase** | Phase 1 - Foundation |
| **Priority** | 🔴 Critical |
| **Difficulty** | 🔴 Hard |
| **Estimated Time** | 5-7 days |
| **Prerequisites** | TASK-001 (Dependency Updates) |
| **Branch** | `task/002-legacy-project-migration` |
## Objective
Develop a robust migration system that ensures all existing Noodl projects created with older versions of the editor (and older dependency versions) can be imported into the updated OpenNoodl editor without breaking changes or data loss.
## Background
### Why This Task Is Critical
Many Noodl users have **production projects** that they've built over months or years using previous versions of the Noodl editor. These projects may rely on:
- Older React version behavior (React 17 and earlier)
- Deprecated node APIs
- Legacy project file formats
- Older dependency APIs (e.g., react-instantsearch-hooks-web vs react-instantsearch)
- Previous runtime behaviors
When we update dependencies in TASK-001 (React 17 → 19, etc.), we risk breaking these existing projects. **This is unacceptable** for our user base. A user should be able to:
1. Install the new OpenNoodl editor
2. Open their 3-year-old Noodl project
3. Have it work exactly as before (or with minimal guided fixes)
### The Stakes
- Users have business-critical applications built in Noodl
- Some users may have hundreds of hours invested in their projects
- Breaking backward compatibility could permanently lose users
- Our credibility as a fork depends on being a seamless upgrade path
### How This Fits Into The Bigger Picture
This task ensures TASK-001 (dependency updates) doesn't create orphaned projects. It's a safety net that must be in place before we can confidently ship updated dependencies.
## Current State
### What We Know
- Projects are stored as JSON files (graph definitions, components, etc.)
- The runtime interprets these files at runtime
- Different Noodl versions may have different:
- Node definitions
- Property types
- Connection formats
- Metadata schemas
### What We Don't Know Yet
- Exactly which project format versions exist in the wild
- How many breaking changes exist between versions
- Which node APIs have changed over time
- Whether there's existing version metadata in project files
### Research Needed
- [ ] Analyze project file structure
- [ ] Document all project file schemas
- [ ] Compare old vs new node definitions
- [ ] Identify all breaking changes from dependency updates
## Desired State
After this task is complete:
1. **Seamless Import**: Users can open any legacy Noodl project in the new editor
2. **Auto-Migration**: Projects are automatically upgraded to the new format when opened
3. **CLI Tool**: A command-line utility exists for batch migration and validation
4. **No Breaking Changes**: All existing node connections and logic work as before
5. **Clear Warnings**: If manual intervention is needed, users see clear guidance
6. **Backup Safety**: Original projects are backed up before migration
7. **Validation**: A test suite verifies migration works with sample projects
## Scope
### In Scope
- [ ] Document all Noodl project file formats
- [ ] Create a version detection system for projects
- [ ] Build a migration engine for auto-upgrading projects
- [ ] Develop a CLI tool for import/validation of legacy projects
- [ ] Create migration handlers for known breaking changes
- [ ] Build a validation test suite with sample projects
- [ ] Add user-facing warnings and guidance for edge cases
- [ ] Implement automatic backup before migration
### Out of Scope
- Creating new node types (that's feature work)
- Fixing bugs in legacy projects (that's user responsibility)
- Supporting unofficial Noodl forks
- Migrating cloud/backend configurations (separate concern)
## Technical Approach
### Phase 1: Research & Analysis
#### Key Areas to Investigate
| Area | Files to Examine | Goal |
|------|------------------|------|
| Project Structure | Sample `.noodl` project folders | Understand file organization |
| Graph Format | `*.json` graph files | Document schema |
| Node Definitions | `packages/noodl-runtime/src/nodes/` | Map all node types |
| Component Format | Component JSON files | Document structure |
| Metadata | Project metadata files | Find version indicators |
#### Questions to Answer
1. Where is project version stored? (if at all)
2. What changed between Noodl releases?
3. Which nodes have breaking API changes?
4. What React 17 → 19 patterns affect project files?
### Phase 2: Version Detection System
Create a system to identify what version of Noodl created a project:
```typescript
interface ProjectVersion {
editorVersion: string; // e.g., "2.8.0"
formatVersion: string; // e.g., "1.2"
runtimeVersion: string; // e.g., "1.0.0"
detectedFeatures: string[]; // Feature flags found
}
function detectProjectVersion(projectPath: string): ProjectVersion {
// 1. Check explicit version metadata
// 2. Infer from file structure
// 3. Infer from node usage patterns
// 4. Default to "unknown/legacy"
}
```
### Phase 3: Migration Engine
Build a pluggable migration system:
```typescript
interface Migration {
id: string;
fromVersion: string;
toVersion: string;
description: string;
migrate: (project: ProjectData) => ProjectData;
validate: (project: ProjectData) => ValidationResult;
}
class MigrationEngine {
private migrations: Migration[] = [];
registerMigration(migration: Migration): void;
getMigrationPath(from: string, to: string): Migration[];
migrateProject(project: ProjectData, targetVersion: string): MigrationResult;
}
```
#### Known Migrations Needed
| From | To | Migration |
|------|-----|-----------|
| React 17 patterns | React 19 | Update any stored component patterns |
| Old node format | New node format | Transform node definitions |
| Legacy connections | New connections | Update connection schema |
### Phase 4: CLI Tool
Create a command-line tool for migration:
```bash
# Validate a project without modifying it
noodl-migrate validate ./my-project
# Migrate a project (creates backup first)
noodl-migrate upgrade ./my-project
# Migrate with specific target version
noodl-migrate upgrade ./my-project --to-version 3.0
# Batch migrate multiple projects
noodl-migrate batch-upgrade ./projects-folder
# Generate migration report
noodl-migrate report ./my-project > migration-report.md
```
#### CLI Implementation Location
```
packages/noodl-cli/
├── package.json
├── bin/
│ └── noodl-migrate.js
├── src/
│ ├── commands/
│ │ ├── validate.ts
│ │ ├── upgrade.ts
│ │ ├── batch-upgrade.ts
│ │ └── report.ts
│ ├── migrations/
│ │ ├── index.ts
│ │ ├── v17-to-v19-react.ts
│ │ ├── legacy-node-format.ts
│ │ └── ...
│ └── utils/
│ ├── backup.ts
│ ├── version-detect.ts
│ └── validation.ts
└── tests/
└── ...
```
### Phase 5: Editor Integration
Integrate migration into the editor's project opening flow:
```typescript
// In project loading code
async function openProject(projectPath: string): Promise<Project> {
const version = detectProjectVersion(projectPath);
if (needsMigration(version)) {
const result = await showMigrationDialog(projectPath, version);
if (result === 'migrate') {
await backupProject(projectPath);
await migrateProject(projectPath);
} else if (result === 'cancel') {
return null;
}
}
return loadProject(projectPath);
}
```
### Key Files to Create
| File | Purpose |
|------|---------|
| `packages/noodl-cli/` | New package for CLI tool |
| `packages/noodl-editor/src/editor/src/utils/migration/` | Migration engine |
| `packages/noodl-editor/src/editor/src/utils/migration/migrations/` | Individual migrations |
| `packages/noodl-editor/src/editor/src/views/MigrationDialog/` | UI for migration prompts |
### Key Files to Modify
| File | Changes |
|------|---------|
| `packages/noodl-editor/src/editor/src/models/projectmodel.js` | Add migration check on load |
| Various node definitions | Document version requirements |
| `package.json` (root) | Add noodl-cli workspace |
## Implementation Steps
### Step 1: Project Format Research (Day 1)
1. Collect sample projects from different Noodl versions
2. Document JSON schema for each file type
3. Identify version indicators in existing projects
4. Create comprehensive format documentation
5. Document in NOTES.md
### Step 2: Breaking Changes Audit (Day 1-2)
1. List all dependency updates from TASK-001
2. For each update, identify breaking changes
3. Map breaking changes to project file impact
4. Create migration requirement list
5. Update README with findings
### Step 3: Version Detection System (Day 2)
1. Create `ProjectVersion` type definitions
2. Implement version detection logic
3. Add fallback for unknown/legacy projects
4. Write unit tests for detection
5. Document in CHANGELOG.md
### Step 4: Migration Engine Core (Day 3)
1. Design migration interface
2. Implement `MigrationEngine` class
3. Create migration registration system
4. Build migration path calculator
5. Add rollback capability
6. Write unit tests
### Step 5: Individual Migrations (Day 3-4)
1. Create migration for React 17 → 19 patterns
2. Create migration for node format changes
3. Create migration for connection schema changes
4. Create migration for each identified breaking change
5. Write tests for each migration
### Step 6: CLI Tool (Day 4-5)
1. Create `noodl-cli` package structure
2. Implement `validate` command
3. Implement `upgrade` command
4. Implement `batch-upgrade` command
5. Implement `report` command
6. Add backup functionality
7. Write CLI tests
8. Create user documentation
### Step 7: Editor Integration (Day 5-6)
1. Create MigrationDialog component
2. Add migration check to project loading
3. Implement automatic backup
4. Add migration progress UI
5. Handle edge cases and errors
6. Manual testing
### Step 8: Validation & Testing (Day 6-7)
1. Create test project corpus (various versions)
2. Run migration on all test projects
3. Verify migrated projects work correctly
4. Fix any discovered issues
5. Document edge cases
## Testing Plan
### Unit Tests
- [ ] Version detection correctly identifies project versions
- [ ] Migration engine calculates correct migration paths
- [ ] Each individual migration transforms data correctly
- [ ] Backup system creates valid copies
- [ ] Rollback restores original state
### Integration Tests
- [ ] CLI tool works end-to-end
- [ ] Editor integration opens legacy projects
- [ ] Migration dialog flows work correctly
- [ ] Batch migration handles multiple projects
### Manual Testing Scenarios
- [ ] Open a project from Noodl 2.0
- [ ] Open a project from Noodl 2.5
- [ ] Open a project from the last official release
- [ ] Open a project with complex node graphs
- [ ] Open a project with custom components
- [ ] Verify all nodes still work after migration
- [ ] Verify all connections still work
- [ ] Verify preview renders correctly
- [ ] Test CLI on real legacy projects
### Test Project Corpus
Create or collect test projects representing:
- [ ] Minimal project (single page)
- [ ] Complex project (multiple pages, components)
- [ ] Project using deprecated nodes
- [ ] Project with custom JavaScript
- [ ] Project with cloud functions
- [ ] Project from each known Noodl version
## Success Criteria
- [ ] Any legacy Noodl project can be opened in the new editor
- [ ] Migration happens automatically without data loss
- [ ] CLI tool successfully migrates 100% of test corpus
- [ ] Users receive clear guidance if manual action needed
- [ ] Original projects are backed up before modification
- [ ] All migrated projects pass validation
- [ ] No runtime errors in migrated projects
- [ ] Documentation explains the migration process
## Risks & Mitigations
| Risk | Impact | Probability | Mitigation |
|------|--------|-------------|------------|
| Unknown project formats exist | High | Medium | Comprehensive testing, graceful fallbacks |
| Some migrations are impossible | High | Low | Document limitations, provide manual guides |
| Performance issues with large projects | Medium | Medium | Streaming migration, progress indicators |
| Users don't understand prompts | Medium | Medium | Clear UX, detailed documentation |
| Edge cases cause data corruption | Critical | Low | Always backup first, validation checks |
| Can't find sample legacy projects | Medium | Medium | Reach out to community, create synthetic tests |
## Rollback Plan
If migration causes issues:
1. **User-level**: Restore from automatic backup
2. **System-level**: Revert migration code, keep projects in legacy mode
3. **Feature flag**: Add ability to disable auto-migration
4. **Support path**: Document manual migration steps for edge cases
## Open Questions
1. Do we have access to legacy Noodl projects for testing?
2. Is there documentation of past Noodl version changes?
3. Should we support projects from unofficial Noodl forks?
4. What's the oldest Noodl version we need to support?
5. Should the CLI be a separate npm package or bundled?
## References
- TASK-001: Dependency Updates (lists breaking changes)
- Noodl project file documentation (if exists)
- React 19 migration guide
- Community feedback on pain points
## Notes for Future Developers
This task is **foundational** for OpenNoodl's success. Take the time to:
- Document everything you discover
- Be conservative with assumptions
- Test with real-world projects when possible
- Err on the side of not breaking things
If you're ever unsure whether a change might break legacy projects, **don't make it** without adding a migration path first.

View File

@@ -40,7 +40,7 @@ export function SideNavigationButton({
menuItems
}: SideNavigationButtonProps) {
const context = useSideNavigationContext();
const iconRef = useRef();
const iconRef = useRef<HTMLDivElement>(null);
const hasMenu = Boolean(menuItems);
const [isMenuVisible, setIsMenuVisible] = useState(false);

View File

@@ -104,10 +104,7 @@ export function Columns({
flexGrow: 0
}}
>
{
// @ts-expect-error
React.cloneElement(child)
}
{React.cloneElement(child)}
</div>
);
})}

View File

@@ -36,7 +36,7 @@ export function ContextMenu({
renderDirection
}: ContextMenuProps) {
const [isContextMenuVisible, setIsContextMenuVisible] = useState(false);
const toggleRef = useRef();
const toggleRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isContextMenuVisible) {

View File

@@ -1,4 +1,4 @@
import React, { useRef, useState, useEffect } from 'react';
import React, { useRef, useState, useEffect, isValidElement } from 'react';
import { ForEachComponent } from '../../../nodes/std-library/data/foreach';
import { Noodl, Slot } from '../../../types';
@@ -110,18 +110,21 @@ export function Columns(props: ColumnsProps) {
props.marginX
);
let children = [];
let forEachComponent = null;
let children: React.ReactElement[] = [];
let forEachComponent: React.ReactElement | null = null;
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
if (!Array.isArray(props.children)) {
// @ts-expect-error props.children.type is any
if (props.children.type !== ForEachComponent) {
if (isValidElement(props.children) && props.children.type !== ForEachComponent) {
children = [props.children]
}
} else {
children = props.children.filter((child) => child.type !== ForEachComponent);
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
children = props.children.filter((child): child is React.ReactElement =>
isValidElement(child) && child.type !== ForEachComponent
);
forEachComponent = props.children.find((child): child is React.ReactElement =>
isValidElement(child) && child.type === ForEachComponent
) || null;
}
return (

View File

@@ -22,6 +22,8 @@ export interface DragProps extends Noodl.ReactProps {
positionY?: (value: number) => void;
deltaX?: (value: number) => void;
deltaY?: (value: number) => void;
children?: React.ReactNode;
}
function setDragValues(event, props) {

View File

@@ -2,7 +2,6 @@ import BScroll from '@better-scroll/core';
import MouseWheel from '@better-scroll/mouse-wheel';
import ScrollBar from '@better-scroll/scroll-bar';
import React from 'react';
import ReactDOM from 'react-dom';
import Layout from '../../../layout';
import PointerListeners from '../../../pointerlisteners';
@@ -17,7 +16,7 @@ BScroll.use(MouseWheel);
BScroll.use(Slide);
export interface GroupProps extends Noodl.ReactProps {
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
as?: keyof React.JSX.IntrinsicElements | React.ComponentType<unknown>;
scrollSnapEnabled: boolean;
showScrollbar: boolean;
@@ -34,6 +33,8 @@ export interface GroupProps extends Noodl.ReactProps {
onScrollPositionChanged?: (value: number) => void;
onScrollStart?: () => void;
onScrollEnd?: () => void;
children?: React.ReactNode;
}
type ScrollRef = HTMLDivElement & { noodlNode?: Noodl.ReactProps['noodlNode'] };
@@ -97,8 +98,11 @@ export class Group extends React.Component<GroupProps> {
scrollToElement(noodlChild, duration) {
if (!noodlChild) return;
// eslint-disable-next-line react/no-find-dom-node
const element = ReactDOM.findDOMNode(noodlChild.getRef()) as HTMLElement;
// Get the ref - in React 19, we need to access the DOM element directly
// rather than using the deprecated findDOMNode
const ref = noodlChild.getRef();
// The ref might be a DOM element directly, or a ref object with a current property
const element = (ref instanceof HTMLElement ? ref : ref?.current) as HTMLElement | null;
if (element && element.scrollIntoView) {
if (this.iScroll) {
this.iScroll.scrollToElement(element, duration, 0, 0);

View File

@@ -5,7 +5,7 @@ import PointerListeners from '../../../pointerlisteners';
import { Noodl } from '../../../types';
export interface TextProps extends Noodl.ReactProps {
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
as?: keyof React.JSX.IntrinsicElements | React.ComponentType<unknown>;
textStyle: Noodl.TextStyle;
text: string;
@@ -21,7 +21,7 @@ export interface TextProps extends Noodl.ReactProps {
}
export function Text(props: TextProps) {
const { as: Component = 'div' } = props;
const Component = props.as || 'div';
const style = {
...props.textStyle,

View File

@@ -1,4 +1,4 @@
import React, { ReactElement, ReactFragment, ReactPortal } from 'react';
import React, { ReactElement, ReactPortal } from 'react';
import type { NodeConstructor } from '../typings/global';
@@ -33,6 +33,6 @@ export namespace Noodl {
}
}
export type SingleSlot = ReactElement<unknown> | ReactFragment | ReactPortal | boolean | null | undefined;
export type SingleSlot = ReactElement<unknown> | Iterable<React.ReactNode> | ReactPortal | boolean | null | undefined;
export type Slot = SingleSlot | SingleSlot[];