#!/usr/bin/env node /** * Migration script to update state references in +page.svelte * This helps automate the bulk replacement of old state variable references * with the new nested state structure. */ import { readFileSync, writeFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const rootDir = join(__dirname, '..'); const targetFile = join(rootDir, 'src/routes/repos/[npub]/[repo]/+page.svelte'); // Migration mappings: old reference -> new reference const migrations = [ // Loading states { pattern: /\bloading\b/g, replacement: 'state.loading.main' }, { pattern: /\bloadingReadme\b/g, replacement: 'state.loading.readme' }, { pattern: /\bloadingCommits\b/g, replacement: 'state.loading.commits' }, { pattern: /\bloadingIssues\b/g, replacement: 'state.loading.issues' }, { pattern: /\bloadingIssueReplies\b/g, replacement: 'state.loading.issueReplies' }, { pattern: /\bloadingPRs\b/g, replacement: 'state.loading.prs' }, { pattern: /\bloadingPatches\b/g, replacement: 'state.loading.patches' }, { pattern: /\bloadingPatchHighlights\b/g, replacement: 'state.loading.patchHighlights' }, { pattern: /\bloadingDocs\b/g, replacement: 'state.loading.docs' }, { pattern: /\bloadingDiscussions\b/g, replacement: 'state.loading.discussions' }, { pattern: /\bloadingReleases\b/g, replacement: 'state.loading.releases' }, { pattern: /\bloadingCodeSearch\b/g, replacement: 'state.loading.codeSearch' }, { pattern: /\bloadingBookmark\b/g, replacement: 'state.loading.bookmark' }, { pattern: /\bloadingMaintainerStatus\b/g, replacement: 'state.loading.maintainerStatus' }, { pattern: /\bloadingMaintainers\b/g, replacement: 'state.loading.maintainers' }, { pattern: /\bloadingReachability\b/g, replacement: 'state.loading.reachability' }, { pattern: /\bloadingVerification\b/g, replacement: 'state.loading.verification' }, // UI state { pattern: /\bactiveTab\b/g, replacement: 'state.ui.activeTab' }, { pattern: /\bshowRepoMenu\b/g, replacement: 'state.ui.showRepoMenu' }, { pattern: /\bshowFileListOnMobile\b/g, replacement: 'state.ui.showFileListOnMobile' }, { pattern: /\bshowLeftPanelOnMobile\b/g, replacement: 'state.ui.showLeftPanelOnMobile' }, { pattern: /\bwordWrap\b/g, replacement: 'state.ui.wordWrap' }, { pattern: /\bexpandedThreads\b/g, replacement: 'state.ui.expandedThreads' }, // User state { pattern: /\buserPubkey\b/g, replacement: 'state.user.pubkey' }, { pattern: /\buserPubkeyHex\b/g, replacement: 'state.user.pubkeyHex' }, // Files { pattern: /\bfiles\b/g, replacement: 'state.files.list' }, { pattern: /\bcurrentPath\b/g, replacement: 'state.files.currentPath' }, { pattern: /\bcurrentFile\b/g, replacement: 'state.files.currentFile' }, { pattern: /\bfileContent\b/g, replacement: 'state.files.content' }, { pattern: /\bfileLanguage\b/g, replacement: 'state.files.language' }, { pattern: /\beditedContent\b/g, replacement: 'state.files.editedContent' }, { pattern: /\bhasChanges\b/g, replacement: 'state.files.hasChanges' }, { pattern: /\bpathStack\b/g, replacement: 'state.files.pathStack' }, // Preview { pattern: /\breadmeContent\b/g, replacement: 'state.preview.readme.content' }, { pattern: /\breadmePath\b/g, replacement: 'state.preview.readme.path' }, { pattern: /\breadmeIsMarkdown\b/g, replacement: 'state.preview.readme.isMarkdown' }, { pattern: /\breadmeHtml\b/g, replacement: 'state.preview.readme.html' }, { pattern: /\bhighlightedFileContent\b/g, replacement: 'state.preview.file.highlightedContent' }, { pattern: /\bfileHtml\b/g, replacement: 'state.preview.file.html' }, { pattern: /\bshowFilePreview\b/g, replacement: 'state.preview.file.showPreview' }, { pattern: /\bcopyingFile\b/g, replacement: 'state.preview.copying' }, { pattern: /\bisImageFile\b/g, replacement: 'state.preview.file.isImage' }, { pattern: /\bimageUrl\b/g, replacement: 'state.preview.file.imageUrl' }, // Git { pattern: /\bbranches\b/g, replacement: 'state.git.branches' }, { pattern: /\bcurrentBranch\b/g, replacement: 'state.git.currentBranch' }, { pattern: /\bdefaultBranch\b/g, replacement: 'state.git.defaultBranch' }, { pattern: /\bcommits\b/g, replacement: 'state.git.commits' }, { pattern: /\bselectedCommit\b/g, replacement: 'state.git.selectedCommit' }, { pattern: /\bshowDiff\b/g, replacement: 'state.git.showDiff' }, { pattern: /\bdiffData\b/g, replacement: 'state.git.diffData' }, { pattern: /\bverifyingCommits\b/g, replacement: 'state.git.verifyingCommits' }, { pattern: /\btags\b/g, replacement: 'state.git.tags' }, { pattern: /\bselectedTag\b/g, replacement: 'state.git.selectedTag' }, // Forms { pattern: /\bnewFileName\b/g, replacement: 'state.forms.file.fileName' }, { pattern: /\bnewFileContent\b/g, replacement: 'state.forms.file.content' }, { pattern: /\bnewBranchName\b/g, replacement: 'state.forms.branch.name' }, { pattern: /\bnewBranchFrom\b/g, replacement: 'state.forms.branch.from' }, { pattern: /\bdefaultBranchName\b/g, replacement: 'state.forms.branch.defaultName' }, { pattern: /\bnewTagName\b/g, replacement: 'state.forms.tag.name' }, { pattern: /\bnewTagMessage\b/g, replacement: 'state.forms.tag.message' }, { pattern: /\bnewTagRef\b/g, replacement: 'state.forms.tag.ref' }, { pattern: /\bcommitMessage\b/g, replacement: 'state.forms.commit.message' }, { pattern: /\bnewIssueSubject\b/g, replacement: 'state.forms.issue.subject' }, { pattern: /\bnewIssueContent\b/g, replacement: 'state.forms.issue.content' }, { pattern: /\bnewIssueLabels\b/g, replacement: 'state.forms.issue.labels' }, { pattern: /\bnewPRSubject\b/g, replacement: 'state.forms.pr.subject' }, { pattern: /\bnewPRContent\b/g, replacement: 'state.forms.pr.content' }, { pattern: /\bnewPRCommitId\b/g, replacement: 'state.forms.pr.commitId' }, { pattern: /\bnewPRBranchName\b/g, replacement: 'state.forms.pr.branchName' }, { pattern: /\bnewPRLabels\b/g, replacement: 'state.forms.pr.labels' }, { pattern: /\bnewPatchContent\b/g, replacement: 'state.forms.patch.content' }, { pattern: /\bnewPatchSubject\b/g, replacement: 'state.forms.patch.subject' }, { pattern: /\bnewReleaseTagName\b/g, replacement: 'state.forms.release.tagName' }, { pattern: /\bnewReleaseTagHash\b/g, replacement: 'state.forms.release.tagHash' }, { pattern: /\bnewReleaseNotes\b/g, replacement: 'state.forms.release.notes' }, { pattern: /\bnewReleaseIsDraft\b/g, replacement: 'state.forms.release.isDraft' }, { pattern: /\bnewReleaseIsPrerelease\b/g, replacement: 'state.forms.release.isPrerelease' }, { pattern: /\bnewThreadTitle\b/g, replacement: 'state.forms.discussion.threadTitle' }, { pattern: /\bnewThreadContent\b/g, replacement: 'state.forms.discussion.threadContent' }, { pattern: /\breplyContent\b/g, replacement: 'state.forms.discussion.replyContent' }, { pattern: /\bselectedPatchText\b/g, replacement: 'state.forms.patchHighlight.text' }, { pattern: /\bselectedPatchStartLine\b/g, replacement: 'state.forms.patchHighlight.startLine' }, { pattern: /\bselectedPatchEndLine\b/g, replacement: 'state.forms.patchHighlight.endLine' }, { pattern: /\bselectedPatchStartPos\b/g, replacement: 'state.forms.patchHighlight.startPos' }, { pattern: /\bselectedPatchEndPos\b/g, replacement: 'state.forms.patchHighlight.endPos' }, { pattern: /\bpatchHighlightComment\b/g, replacement: 'state.forms.patchHighlight.comment' }, { pattern: /\bpatchCommentContent\b/g, replacement: 'state.forms.patchComment.content' }, { pattern: /\breplyingToPatchComment\b/g, replacement: 'state.forms.patchComment.replyingTo' }, // Dialogs { pattern: /\bshowCreateFileDialog\b/g, replacement: "state.openDialog === 'createFile'" }, { pattern: /\bshowCreateBranchDialog\b/g, replacement: "state.openDialog === 'createBranch'" }, { pattern: /\bshowCreateTagDialog\b/g, replacement: "state.openDialog === 'createTag'" }, { pattern: /\bshowCommitDialog\b/g, replacement: "state.openDialog === 'commit'" }, { pattern: /\bshowCreateIssueDialog\b/g, replacement: "state.openDialog === 'createIssue'" }, { pattern: /\bshowCreatePRDialog\b/g, replacement: "state.openDialog === 'createPR'" }, { pattern: /\bshowCreatePatchDialog\b/g, replacement: "state.openDialog === 'createPatch'" }, { pattern: /\bshowCreateReleaseDialog\b/g, replacement: "state.openDialog === 'createRelease'" }, { pattern: /\bshowCreateThreadDialog\b/g, replacement: "state.openDialog === 'createThread'" }, { pattern: /\bshowReplyDialog\b/g, replacement: "state.openDialog === 'reply'" }, { pattern: /\bshowVerificationDialog\b/g, replacement: "state.openDialog === 'verification'" }, { pattern: /\bshowCloneUrlVerificationDialog\b/g, replacement: "state.openDialog === 'cloneUrlVerification'" }, { pattern: /\bshowPatchHighlightDialog\b/g, replacement: "state.openDialog === 'patchHighlight'" }, { pattern: /\bshowPatchCommentDialog\b/g, replacement: "state.openDialog === 'patchComment'" }, // Selected items { pattern: /\bselectedIssue\b/g, replacement: 'state.selected.issue' }, { pattern: /\bselectedPR\b/g, replacement: 'state.selected.pr' }, { pattern: /\bselectedPatch\b/g, replacement: 'state.selected.patch' }, { pattern: /\bselectedDiscussion\b/g, replacement: 'state.selected.discussion' }, // Creating flags { pattern: /\bcreatingPatch\b/g, replacement: 'state.creating.patch' }, { pattern: /\bcreatingThread\b/g, replacement: 'state.creating.thread' }, { pattern: /\bcreatingReply\b/g, replacement: 'state.creating.reply' }, { pattern: /\bcreatingRelease\b/g, replacement: 'state.creating.release' }, { pattern: /\bcreatingPatchHighlight\b/g, replacement: 'state.creating.patchHighlight' }, { pattern: /\bcreatingPatchComment\b/g, replacement: 'state.creating.patchComment' }, { pattern: /\bdeletingAnnouncement\b/g, replacement: 'state.creating.announcement' }, // Maintainers { pattern: /\bisMaintainer\b/g, replacement: 'state.maintainers.isMaintainer' }, { pattern: /\ballMaintainers\b/g, replacement: 'state.maintainers.all' }, { pattern: /\bmaintainersLoaded\b/g, replacement: 'state.maintainers.loaded' }, { pattern: /\bmaintainersEffectRan\b/g, replacement: 'state.maintainers.effectRan' }, { pattern: /\blastRepoKey\b/g, replacement: 'state.maintainers.lastRepoKey' }, // Clone { pattern: /\bisRepoCloned\b/g, replacement: 'state.clone.isCloned' }, { pattern: /\bcheckingCloneStatus\b/g, replacement: 'state.clone.checking' }, { pattern: /\bcloning\b/g, replacement: 'state.clone.cloning' }, { pattern: /\bcopyingCloneUrl\b/g, replacement: 'state.clone.copyingUrl' }, { pattern: /\bapiFallbackAvailable\b/g, replacement: 'state.clone.apiFallbackAvailable' }, { pattern: /\bcloneUrlsExpanded\b/g, replacement: 'state.clone.urlsExpanded' }, { pattern: /\bshowAllCloneUrls\b/g, replacement: 'state.clone.showAllUrls' }, { pattern: /\bcloneUrlReachability\b/g, replacement: 'state.clone.reachability' }, { pattern: /\bcheckingReachability\b/g, replacement: 'state.clone.checkingReachability' }, // Verification { pattern: /\bverificationStatus\b/g, replacement: 'state.verification.status' }, { pattern: /\bverificationFileContent\b/g, replacement: 'state.verification.fileContent' }, { pattern: /\bverifyingCloneUrl\b/g, replacement: 'state.verification.selectedCloneUrl' }, { pattern: /\bselectedCloneUrlForVerification\b/g, replacement: 'state.verification.selectedCloneUrl' }, // Docs { pattern: /\bdocumentationContent\b/g, replacement: 'state.docs.content' }, { pattern: /\bdocumentationHtml\b/g, replacement: 'state.docs.html' }, { pattern: /\bdocumentationKind\b/g, replacement: 'state.docs.kind' }, // Code search { pattern: /\bcodeSearchQuery\b/g, replacement: 'state.codeSearch.query' }, { pattern: /\bcodeSearchResults\b/g, replacement: 'state.codeSearch.results' }, { pattern: /\bcodeSearchScope\b/g, replacement: 'state.codeSearch.scope' }, // Fork/Bookmark { pattern: /\bforkInfo\b/g, replacement: 'state.fork.info' }, { pattern: /\bforking\b/g, replacement: 'state.fork.forking' }, { pattern: /\bisBookmarked\b/g, replacement: 'state.bookmark.isBookmarked' }, // Metadata { pattern: /\brepoAddress\b/g, replacement: 'state.metadata.address' }, { pattern: /\brepoImage\b/g, replacement: 'state.metadata.image' }, { pattern: /\brepoBanner\b/g, replacement: 'state.metadata.banner' }, { pattern: /\brepoOwnerPubkeyState\b/g, replacement: 'state.metadata.ownerPubkey' }, { pattern: /\breadmeAutoLoadAttempted\b/g, replacement: 'state.metadata.readmeAutoLoadAttempted' }, // Discussion { pattern: /\breplyingToThread\b/g, replacement: 'state.discussion.replyingToThread' }, { pattern: /\breplyingToComment\b/g, replacement: 'state.discussion.replyingToComment' }, { pattern: /\bdiscussionEvents\b/g, replacement: 'state.discussion.events' }, { pattern: /\bnostrLinkEvents\b/g, replacement: 'state.discussion.nostrLinkEvents' }, { pattern: /\bnostrLinkProfiles\b/g, replacement: 'state.discussion.nostrLinkProfiles' }, // Other { pattern: /\bpatchEditor\b/g, replacement: 'state.patchEditor' }, { pattern: /\bsaving\b/g, replacement: 'state.saving' }, { pattern: /\bisMounted\b/g, replacement: 'state.isMounted' }, { pattern: /\brepoNotFound\b/g, replacement: 'state.repoNotFound' }, { pattern: /\berror\b/g, replacement: 'state.error' }, // Status updates { pattern: /\bupdatingIssueStatus\b/g, replacement: 'state.statusUpdates.issue' }, { pattern: /\bupdatingPatchStatus\b/g, replacement: 'state.statusUpdates.patch' }, // Data collections (keep as-is but ensure they're accessed via state) { pattern: /\bissues\b/g, replacement: 'state.issues' }, { pattern: /\bissueReplies\b/g, replacement: 'state.issueReplies' }, { pattern: /\bprs\b/g, replacement: 'state.prs' }, { pattern: /\bpatches\b/g, replacement: 'state.patches' }, { pattern: /\bpatchHighlights\b/g, replacement: 'state.patchHighlights' }, { pattern: /\bpatchComments\b/g, replacement: 'state.patchComments' }, { pattern: /\bdiscussions\b/g, replacement: 'state.discussions' }, { pattern: /\breleases\b/g, replacement: 'state.releases' }, ]; // Special cases that need context-aware replacement const contextAwareReplacements = [ // Dialog assignments { pattern: /(showCreateFileDialog|showCreateBranchDialog|showCreateTagDialog|showCommitDialog|showCreateIssueDialog|showCreatePRDialog|showCreatePatchDialog|showCreateReleaseDialog|showCreateThreadDialog|showReplyDialog|showVerificationDialog|showCloneUrlVerificationDialog|showPatchHighlightDialog|showPatchCommentDialog)\s*=\s*(true|false)/g, replacement: (match, varName, value) => { const dialogMap = { showCreateFileDialog: 'createFile', showCreateBranchDialog: 'createBranch', showCreateTagDialog: 'createTag', showCommitDialog: 'commit', showCreateIssueDialog: 'createIssue', showCreatePRDialog: 'createPR', showCreatePatchDialog: 'createPatch', showCreateReleaseDialog: 'createRelease', showCreateThreadDialog: 'createThread', showReplyDialog: 'reply', showVerificationDialog: 'verification', showCloneUrlVerificationDialog: 'cloneUrlVerification', showPatchHighlightDialog: 'patchHighlight', showPatchCommentDialog: 'patchComment' }; const dialogType = dialogMap[varName]; return value === 'true' ? `state.openDialog = '${dialogType}'` : `state.openDialog = null`; } } ]; function migrateFile(content) { let migrated = content; // Apply simple replacements for (const { pattern, replacement } of migrations) { migrated = migrated.replace(pattern, replacement); } // Apply context-aware replacements for (const { pattern, replacement } of contextAwareReplacements) { if (typeof replacement === 'function') { migrated = migrated.replace(pattern, replacement); } else { migrated = migrated.replace(pattern, replacement); } } return migrated; } // Read file console.log(`Reading ${targetFile}...`); let content = readFileSync(targetFile, 'utf-8'); // Backup original const backupFile = targetFile + '.backup'; writeFileSync(backupFile, content, 'utf-8'); console.log(`Backup created: ${backupFile}`); // Migrate console.log('Applying migrations...'); const migrated = migrateFile(content); // Write migrated file writeFileSync(targetFile, migrated, 'utf-8'); console.log(`Migration complete!`); console.log(`\n⚠️ Please review the changes carefully.`); console.log(` Some replacements may need manual adjustment.`); console.log(` Backup saved to: ${backupFile}`);