Compare commits
No commits in common. "a96aa1d2dcd4eeee87d572b8d72a5ccd679ee235" and "0e3699da5a4cb79f7ab919612e46a7b362032dd7" have entirely different histories.
a96aa1d2dc
...
0e3699da5a
|
|
@ -9,8 +9,6 @@ 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 <app>` to grab one.'
|
||||
|
|
@ -78,7 +76,7 @@ export async function historyApp(name?: string) {
|
|||
console.log(` ${color.green('+')} ${file}`)
|
||||
}
|
||||
for (const file of entry.modified) {
|
||||
console.log(` ${color.magenta('*')} ${file}`)
|
||||
console.log(` ${color.yellow('~')} ${file}`)
|
||||
}
|
||||
for (const file of entry.deleted) {
|
||||
console.log(` ${color.red('-')} ${file}`)
|
||||
|
|
@ -106,7 +104,7 @@ export async function getApp(name: string) {
|
|||
mkdirSync(appPath, { recursive: true })
|
||||
|
||||
const files = Object.keys(result.manifest.files)
|
||||
console.log(`Downloading ${files.length} file${s(files.length)}...`)
|
||||
console.log(`Downloading ${files.length} files...`)
|
||||
|
||||
for (const file of files) {
|
||||
const content = await download(`/api/sync/apps/${name}/files/${file}`)
|
||||
|
|
@ -210,7 +208,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} file${s(renames.length)}...`)
|
||||
console.log(`Renaming ${renames.length} files...`)
|
||||
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)
|
||||
|
|
@ -224,7 +222,7 @@ export async function pushApp(options: { quiet?: boolean, force?: boolean } = {}
|
|||
}
|
||||
|
||||
if (actualUploads.length > 0) {
|
||||
console.log(`Uploading ${actualUploads.length} file${s(actualUploads.length)}...`)
|
||||
console.log(`Uploading ${actualUploads.length} files...`)
|
||||
let failedUploads = 0
|
||||
|
||||
for (const file of actualUploads) {
|
||||
|
|
@ -239,7 +237,7 @@ export async function pushApp(options: { quiet?: boolean, force?: boolean } = {}
|
|||
}
|
||||
|
||||
if (failedUploads > 0) {
|
||||
console.error(`Failed to upload ${failedUploads} file${s(failedUploads)}. Deployment aborted.`)
|
||||
console.error(`Failed to upload ${failedUploads} file(s). Deployment aborted.`)
|
||||
console.error(`Incomplete version ${version} left on server (not activated).`)
|
||||
return
|
||||
}
|
||||
|
|
@ -247,11 +245,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} file${s(actualDeletes.length)}...`)
|
||||
console.log(`Deleting ${actualDeletes.length} files...`)
|
||||
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)`)
|
||||
}
|
||||
|
|
@ -309,7 +307,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.magenta('*')} ${file}`)
|
||||
console.error(` ${color.yellow('~')} ${file}`)
|
||||
}
|
||||
for (const file of localOnly) {
|
||||
console.error(` ${color.green('+')} ${file} (local only)`)
|
||||
|
|
@ -335,7 +333,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} file${s(toDownload.length)}...`)
|
||||
console.log(`Downloading ${toDownload.length} files...`)
|
||||
for (const file of toDownload) {
|
||||
const content = await download(`/api/sync/apps/${appName}/files/${file}`)
|
||||
if (!content) {
|
||||
|
|
@ -356,12 +354,12 @@ export async function pullApp(options: { force?: boolean, quiet?: boolean } = {}
|
|||
}
|
||||
|
||||
if (toDelete.length > 0) {
|
||||
console.log(`Deleting ${toDelete.length} local file${s(toDelete.length)}...`)
|
||||
console.log(`Deleting ${toDelete.length} local files...`)
|
||||
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}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -399,23 +397,14 @@ export async function diffApp() {
|
|||
console.log(color.gray('─'.repeat(60)))
|
||||
}
|
||||
|
||||
// Fetch all changed files in parallel (skip binary files)
|
||||
// Fetch all changed files in parallel
|
||||
const remoteContents = await Promise.all(
|
||||
changed.map(file => isBinary(file) ? null : download(`/api/sync/apps/${appName}/files/${file}`))
|
||||
changed.map(file => download(`/api/sync/apps/${appName}/files/${file}`))
|
||||
)
|
||||
|
||||
// Show diffs for changed files
|
||||
for (let i = 0; i < changed.length; i++) {
|
||||
const file = changed[i]!
|
||||
|
||||
console.log(color.bold(`\n${file}`))
|
||||
console.log(color.gray('─'.repeat(60)))
|
||||
|
||||
if (isBinary(file)) {
|
||||
console.log(color.gray('Binary file changed'))
|
||||
continue
|
||||
}
|
||||
|
||||
const remoteContent = remoteContents[i]
|
||||
const localContent = readFileSync(join(process.cwd(), file), 'utf-8')
|
||||
|
||||
|
|
@ -425,6 +414,9 @@ export async function diffApp() {
|
|||
}
|
||||
|
||||
const remoteText = new TextDecoder().decode(remoteContent)
|
||||
|
||||
console.log(color.bold(`\n${file}`))
|
||||
console.log(color.gray('─'.repeat(60)))
|
||||
showDiff(remoteText, localContent)
|
||||
}
|
||||
|
||||
|
|
@ -433,12 +425,6 @@ export async function diffApp() {
|
|||
console.log(color.green('\nNew file (local only)'))
|
||||
console.log(color.bold(`${file}`))
|
||||
console.log(color.gray('─'.repeat(60)))
|
||||
|
||||
if (isBinary(file)) {
|
||||
console.log(color.gray('Binary file'))
|
||||
continue
|
||||
}
|
||||
|
||||
const content = readFileSync(join(process.cwd(), file), 'utf-8')
|
||||
const lines = content.split('\n')
|
||||
for (let i = 0; i < Math.min(lines.length, 10); i++) {
|
||||
|
|
@ -449,9 +435,9 @@ export async function diffApp() {
|
|||
}
|
||||
}
|
||||
|
||||
// Fetch all remote-only files in parallel (skip binary files)
|
||||
// Fetch all remote-only files in parallel
|
||||
const remoteOnlyContents = await Promise.all(
|
||||
remoteOnly.map(file => isBinary(file) ? null : download(`/api/sync/apps/${appName}/files/${file}`))
|
||||
remoteOnly.map(file => download(`/api/sync/apps/${appName}/files/${file}`))
|
||||
)
|
||||
|
||||
// Show remote-only files
|
||||
|
|
@ -462,12 +448,6 @@ export async function diffApp() {
|
|||
console.log(color.bold(`\n${file}`))
|
||||
console.log(color.gray('─'.repeat(60)))
|
||||
console.log(color.red('Remote only'))
|
||||
|
||||
if (isBinary(file)) {
|
||||
console.log(color.gray('Binary file'))
|
||||
continue
|
||||
}
|
||||
|
||||
if (content) {
|
||||
const text = new TextDecoder().decode(content)
|
||||
const lines = text.split('\n')
|
||||
|
|
@ -502,7 +482,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} file${s(localFileCount)} on push\n`)
|
||||
console.log(`\nWould create new app with ${localFileCount} files on push\n`)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -527,7 +507,7 @@ export async function statusApp() {
|
|||
console.log(` ${color.cyan('→')} ${from} → ${to}`)
|
||||
}
|
||||
for (const file of changed) {
|
||||
console.log(` ${color.magenta('*')} ${file}`)
|
||||
console.log(` ${color.green('↑')} ${file}`)
|
||||
}
|
||||
for (const file of localOnly) {
|
||||
console.log(` ${color.green('+')} ${file}`)
|
||||
|
|
@ -544,7 +524,7 @@ export async function statusApp() {
|
|||
console.log(` ${color.cyan('→')} ${from} → ${to}`)
|
||||
}
|
||||
for (const file of changed) {
|
||||
console.log(` ${color.magenta('*')} ${file}`)
|
||||
console.log(` ${color.yellow('~')} ${file}`)
|
||||
}
|
||||
for (const file of localOnly) {
|
||||
console.log(` ${color.green('+')} ${file} (local only)`)
|
||||
|
|
@ -800,12 +780,12 @@ export async function stashApp() {
|
|||
|
||||
const content = readFileSync(srcPath)
|
||||
writeFileSync(destPath, content)
|
||||
console.log(` ${color.magenta('*')} ${file}`)
|
||||
console.log(` ${color.yellow('→')} ${file}`)
|
||||
}
|
||||
|
||||
// Restore changed files from server
|
||||
if (changed.length > 0) {
|
||||
console.log(`\nRestoring ${changed.length} changed file${s(changed.length)} from server...`)
|
||||
console.log(`\nRestoring ${changed.length} changed files from server...`)
|
||||
for (const file of changed) {
|
||||
const content = await download(`/api/sync/apps/${appName}/files/${file}`)
|
||||
if (content) {
|
||||
|
|
@ -816,13 +796,13 @@ export async function stashApp() {
|
|||
|
||||
// Delete local-only files
|
||||
if (localOnly.length > 0) {
|
||||
console.log(`Removing ${localOnly.length} local-only file${s(localOnly.length)}...`)
|
||||
console.log(`Removing ${localOnly.length} local-only files...`)
|
||||
for (const file of localOnly) {
|
||||
unlinkSync(join(process.cwd(), file))
|
||||
}
|
||||
}
|
||||
|
||||
console.log(color.green(`\n✓ Stashed ${toStash.length} file${s(toStash.length)}`))
|
||||
console.log(color.green(`\n✓ Stashed ${toStash.length} file(s)`))
|
||||
}
|
||||
|
||||
export async function stashListApp() {
|
||||
|
|
@ -848,7 +828,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} file${s(metadata.files.length)})`)}`)
|
||||
console.log(` ${color.bold(stash.name)} ${color.gray(date)} ${color.gray(`(${metadata.files.length} files)`)}`)
|
||||
} else {
|
||||
console.log(` ${color.bold(stash.name)} ${color.gray('(invalid)')}`)
|
||||
}
|
||||
|
|
@ -910,7 +890,7 @@ export async function stashPopApp() {
|
|||
// Remove stash directory
|
||||
rmSync(stashDir, { recursive: true })
|
||||
|
||||
console.log(color.green(`\n✓ Restored ${metadata.files.length} file${s(metadata.files.length)}`))
|
||||
console.log(color.green(`\n✓ Restored ${metadata.files.length} file(s)`))
|
||||
}
|
||||
|
||||
export async function cleanApp(options: { force?: boolean, dryRun?: boolean } = {}) {
|
||||
|
|
@ -936,7 +916,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
|
||||
}
|
||||
|
|
@ -944,20 +924,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(localOnly.length)}?`)
|
||||
const ok = await confirm(`Remove ${localOnly.length} file(s)?`)
|
||||
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(localOnly.length)}`))
|
||||
console.log(color.green(`✓ Removed ${localOnly.length} file(s)`))
|
||||
}
|
||||
|
||||
interface VersionsResponse {
|
||||
|
|
@ -1107,19 +1087,6 @@ async function getManifestDiff(appName: string): Promise<ManifestDiff | null> {
|
|||
}
|
||||
}
|
||||
|
||||
const BINARY_EXTENSIONS = new Set([
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.avif', '.heic', '.tiff',
|
||||
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
||||
'.mp3', '.mp4', '.wav', '.ogg', '.webm', '.avi', '.mov',
|
||||
'.pdf', '.zip', '.tar', '.gz', '.br', '.zst',
|
||||
'.wasm', '.exe', '.dll', '.so', '.dylib',
|
||||
])
|
||||
|
||||
const isBinary = (filename: string) => {
|
||||
const ext = filename.slice(filename.lastIndexOf('.')).toLowerCase()
|
||||
return BINARY_EXTENSIONS.has(ext)
|
||||
}
|
||||
|
||||
function showDiff(remote: string, local: string) {
|
||||
const changes = diffLines(remote, local)
|
||||
let lineCount = 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user