diff --git a/src/cli/commands/sync.ts b/src/cli/commands/sync.ts index 09cca36..7bd844a 100644 --- a/src/cli/commands/sync.ts +++ b/src/cli/commands/sync.ts @@ -9,6 +9,8 @@ import { del, download, get, getManifest, handleError, makeUrl, post, put } from import { confirm, prompt } from '../prompts' import { getAppName, getAppPackage, isApp, resolveAppName } from '../name' +const s = (n: number) => n === 1 ? '' : 's' + function notAppError(): string { const pkg = getAppPackage() if (!pkg) return 'No package.json found. Use `toes get ` to grab one.' @@ -76,7 +78,7 @@ export async function historyApp(name?: string) { console.log(` ${color.green('+')} ${file}`) } for (const file of entry.modified) { - console.log(` ${color.yellow('~')} ${file}`) + console.log(` ${color.magenta('*')} ${file}`) } for (const file of entry.deleted) { console.log(` ${color.red('-')} ${file}`) @@ -104,7 +106,7 @@ export async function getApp(name: string) { mkdirSync(appPath, { recursive: true }) const files = Object.keys(result.manifest.files) - console.log(`Downloading ${files.length} files...`) + console.log(`Downloading ${files.length} file${s(files.length)}...`) for (const file of files) { const content = await download(`/api/sync/apps/${name}/files/${file}`) @@ -208,7 +210,7 @@ export async function pushApp(options: { quiet?: boolean, force?: boolean } = {} const actualDeletes = toDelete.filter(f => !renamedDeletes.has(f)) if (renames.length > 0) { - console.log(`Renaming ${renames.length} files...`) + console.log(`Renaming ${renames.length} file${s(renames.length)}...`) for (const { from, to } of renames) { const content = readFileSync(join(process.cwd(), to)) const uploadOk = await put(`/api/sync/apps/${appName}/files/${to}?version=${version}`, content) @@ -222,7 +224,7 @@ export async function pushApp(options: { quiet?: boolean, force?: boolean } = {} } if (actualUploads.length > 0) { - console.log(`Uploading ${actualUploads.length} files...`) + console.log(`Uploading ${actualUploads.length} file${s(actualUploads.length)}...`) let failedUploads = 0 for (const file of actualUploads) { @@ -237,7 +239,7 @@ export async function pushApp(options: { quiet?: boolean, force?: boolean } = {} } if (failedUploads > 0) { - console.error(`Failed to upload ${failedUploads} file(s). Deployment aborted.`) + console.error(`Failed to upload ${failedUploads} file${s(failedUploads)}. Deployment aborted.`) console.error(`Incomplete version ${version} left on server (not activated).`) return } @@ -245,11 +247,11 @@ export async function pushApp(options: { quiet?: boolean, force?: boolean } = {} // 3. Delete files that no longer exist locally if (actualDeletes.length > 0) { - console.log(`Deleting ${actualDeletes.length} files...`) + console.log(`Deleting ${actualDeletes.length} file${s(actualDeletes.length)}...`) for (const file of actualDeletes) { const success = await del(`/api/sync/apps/${appName}/files/${file}?version=${version}`) if (success) { - console.log(` ${color.red('✗')} ${file}`) + console.log(` ${color.red('-')} ${file}`) } else { console.log(` ${color.red('✗')} ${file} (failed)`) } @@ -307,7 +309,7 @@ export async function pullApp(options: { force?: boolean, quiet?: boolean } = {} if (hasDiffs && !options.force) { console.error('Cannot pull: you have local changes that would be overwritten') for (const file of changed) { - console.error(` ${color.yellow('~')} ${file}`) + console.error(` ${color.magenta('*')} ${file}`) } for (const file of localOnly) { console.error(` ${color.green('+')} ${file} (local only)`) @@ -333,7 +335,7 @@ export async function pullApp(options: { force?: boolean, quiet?: boolean } = {} console.log(`Pulling ${color.bold(appName)} from server...`) if (toDownload.length > 0) { - console.log(`Downloading ${toDownload.length} files...`) + console.log(`Downloading ${toDownload.length} file${s(toDownload.length)}...`) for (const file of toDownload) { const content = await download(`/api/sync/apps/${appName}/files/${file}`) if (!content) { @@ -354,12 +356,12 @@ export async function pullApp(options: { force?: boolean, quiet?: boolean } = {} } if (toDelete.length > 0) { - console.log(`Deleting ${toDelete.length} local files...`) + console.log(`Deleting ${toDelete.length} local file${s(toDelete.length)}...`) for (const file of toDelete) { const fullPath = join(process.cwd(), file) if (existsSync(fullPath)) { unlinkSync(fullPath) - console.log(` ${color.red('✗')} ${file}`) + console.log(` ${color.red('-')} ${file}`) } } } @@ -500,7 +502,7 @@ export async function statusApp() { if (!remoteManifest) { console.log(color.yellow('App does not exist on server')) const localFileCount = Object.keys(localManifest.files).length - console.log(`\nWould create new app with ${localFileCount} files on push\n`) + console.log(`\nWould create new app with ${localFileCount} file${s(localFileCount)} on push\n`) return } @@ -525,7 +527,7 @@ export async function statusApp() { console.log(` ${color.cyan('→')} ${from} → ${to}`) } for (const file of changed) { - console.log(` ${color.green('↑')} ${file}`) + console.log(` ${color.magenta('*')} ${file}`) } for (const file of localOnly) { console.log(` ${color.green('+')} ${file}`) @@ -542,7 +544,7 @@ export async function statusApp() { console.log(` ${color.cyan('→')} ${from} → ${to}`) } for (const file of changed) { - console.log(` ${color.yellow('~')} ${file}`) + console.log(` ${color.magenta('*')} ${file}`) } for (const file of localOnly) { console.log(` ${color.green('+')} ${file} (local only)`) @@ -798,12 +800,12 @@ export async function stashApp() { const content = readFileSync(srcPath) writeFileSync(destPath, content) - console.log(` ${color.yellow('→')} ${file}`) + console.log(` ${color.magenta('*')} ${file}`) } // Restore changed files from server if (changed.length > 0) { - console.log(`\nRestoring ${changed.length} changed files from server...`) + console.log(`\nRestoring ${changed.length} changed file${s(changed.length)} from server...`) for (const file of changed) { const content = await download(`/api/sync/apps/${appName}/files/${file}`) if (content) { @@ -814,13 +816,13 @@ export async function stashApp() { // Delete local-only files if (localOnly.length > 0) { - console.log(`Removing ${localOnly.length} local-only files...`) + console.log(`Removing ${localOnly.length} local-only file${s(localOnly.length)}...`) for (const file of localOnly) { unlinkSync(join(process.cwd(), file)) } } - console.log(color.green(`\n✓ Stashed ${toStash.length} file(s)`)) + console.log(color.green(`\n✓ Stashed ${toStash.length} file${s(toStash.length)}`)) } export async function stashListApp() { @@ -846,7 +848,7 @@ export async function stashListApp() { files: string[] } const date = new Date(metadata.timestamp).toLocaleString() - console.log(` ${color.bold(stash.name)} ${color.gray(date)} ${color.gray(`(${metadata.files.length} files)`)}`) + console.log(` ${color.bold(stash.name)} ${color.gray(date)} ${color.gray(`(${metadata.files.length} file${s(metadata.files.length)})`)}`) } else { console.log(` ${color.bold(stash.name)} ${color.gray('(invalid)')}`) } @@ -908,7 +910,7 @@ export async function stashPopApp() { // Remove stash directory rmSync(stashDir, { recursive: true }) - console.log(color.green(`\n✓ Restored ${metadata.files.length} file(s)`)) + console.log(color.green(`\n✓ Restored ${metadata.files.length} file${s(metadata.files.length)}`)) } export async function cleanApp(options: { force?: boolean, dryRun?: boolean } = {}) { @@ -934,7 +936,7 @@ export async function cleanApp(options: { force?: boolean, dryRun?: boolean } = if (options.dryRun) { console.log('Would remove:') for (const file of localOnly) { - console.log(` ${color.red('✗')} ${file}`) + console.log(` ${color.red('-')} ${file}`) } return } @@ -942,20 +944,20 @@ export async function cleanApp(options: { force?: boolean, dryRun?: boolean } = if (!options.force) { console.log('Files not on server:') for (const file of localOnly) { - console.log(` ${color.red('✗')} ${file}`) + console.log(` ${color.red('-')} ${file}`) } console.log() - const ok = await confirm(`Remove ${localOnly.length} file(s)?`) + const ok = await confirm(`Remove ${localOnly.length} file${s(localOnly.length)}?`) if (!ok) return } for (const file of localOnly) { const fullPath = join(process.cwd(), file) unlinkSync(fullPath) - console.log(` ${color.red('✗')} ${file}`) + console.log(` ${color.red('-')} ${file}`) } - console.log(color.green(`✓ Removed ${localOnly.length} file(s)`)) + console.log(color.green(`✓ Removed ${localOnly.length} file${s(localOnly.length)}`)) } interface VersionsResponse {