@prnv/tuck 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +167 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -365,12 +365,22 @@ var init_ui = __esm({
|
|
|
365
365
|
|
|
366
366
|
// src/constants.ts
|
|
367
367
|
import { homedir } from "os";
|
|
368
|
-
import { join } from "path";
|
|
369
|
-
|
|
368
|
+
import { join, dirname } from "path";
|
|
369
|
+
import { readFileSync } from "fs";
|
|
370
|
+
import { fileURLToPath } from "url";
|
|
371
|
+
var __dirname, packageJsonPath, VERSION_VALUE, VERSION, DESCRIPTION, HOME_DIR, DEFAULT_TUCK_DIR, MANIFEST_FILE, CONFIG_FILE, BACKUP_DIR, FILES_DIR, CATEGORIES;
|
|
370
372
|
var init_constants = __esm({
|
|
371
373
|
"src/constants.ts"() {
|
|
372
374
|
"use strict";
|
|
373
|
-
|
|
375
|
+
__dirname = dirname(fileURLToPath(import.meta.url));
|
|
376
|
+
packageJsonPath = join(__dirname, "..", "package.json");
|
|
377
|
+
VERSION_VALUE = "1.0.0";
|
|
378
|
+
try {
|
|
379
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
380
|
+
VERSION_VALUE = pkg.version;
|
|
381
|
+
} catch {
|
|
382
|
+
}
|
|
383
|
+
VERSION = VERSION_VALUE;
|
|
374
384
|
DESCRIPTION = "Modern dotfiles manager with a beautiful CLI";
|
|
375
385
|
HOME_DIR = homedir();
|
|
376
386
|
DEFAULT_TUCK_DIR = join(HOME_DIR, ".tuck");
|
|
@@ -430,23 +440,12 @@ var init_constants = __esm({
|
|
|
430
440
|
icon: "-"
|
|
431
441
|
}
|
|
432
442
|
};
|
|
433
|
-
COMMON_DOTFILES = [
|
|
434
|
-
{ path: "~/.zshrc", category: "shell" },
|
|
435
|
-
{ path: "~/.bashrc", category: "shell" },
|
|
436
|
-
{ path: "~/.bash_profile", category: "shell" },
|
|
437
|
-
{ path: "~/.gitconfig", category: "git" },
|
|
438
|
-
{ path: "~/.config/nvim", category: "editors" },
|
|
439
|
-
{ path: "~/.vimrc", category: "editors" },
|
|
440
|
-
{ path: "~/.tmux.conf", category: "terminal" },
|
|
441
|
-
{ path: "~/.ssh/config", category: "ssh" },
|
|
442
|
-
{ path: "~/.config/starship.toml", category: "terminal" }
|
|
443
|
-
];
|
|
444
443
|
}
|
|
445
444
|
});
|
|
446
445
|
|
|
447
446
|
// src/lib/paths.ts
|
|
448
447
|
import { homedir as homedir2 } from "os";
|
|
449
|
-
import { join as join2, basename, dirname, relative, isAbsolute, resolve } from "path";
|
|
448
|
+
import { join as join2, basename, dirname as dirname2, relative, isAbsolute, resolve } from "path";
|
|
450
449
|
import { stat, access } from "fs/promises";
|
|
451
450
|
import { constants } from "fs";
|
|
452
451
|
var expandPath, collapsePath, getTuckDir, getManifestPath, getConfigPath, getFilesDir, getCategoryDir, getDestinationPath, getRelativeDestination, sanitizeFilename, detectCategory, pathExists, isDirectory, isPathWithinHome, validateSafeSourcePath, generateFileId;
|
|
@@ -1160,9 +1159,9 @@ var init_git = __esm({
|
|
|
1160
1159
|
const remote = options?.remote || "origin";
|
|
1161
1160
|
const branch = options?.branch;
|
|
1162
1161
|
if (branch) {
|
|
1163
|
-
await git.push([remote, branch
|
|
1162
|
+
await git.push([...args, remote, branch]);
|
|
1164
1163
|
} else {
|
|
1165
|
-
await git.push([
|
|
1164
|
+
await git.push([...args, remote]);
|
|
1166
1165
|
}
|
|
1167
1166
|
} catch (error) {
|
|
1168
1167
|
throw new GitError("Failed to push", String(error));
|
|
@@ -1234,9 +1233,15 @@ var init_git = __esm({
|
|
|
1234
1233
|
try {
|
|
1235
1234
|
const git = createGit(dir);
|
|
1236
1235
|
const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
1237
|
-
return branch;
|
|
1238
|
-
} catch
|
|
1239
|
-
|
|
1236
|
+
return branch.trim();
|
|
1237
|
+
} catch {
|
|
1238
|
+
try {
|
|
1239
|
+
const git = createGit(dir);
|
|
1240
|
+
const ref = await git.raw(["symbolic-ref", "--short", "HEAD"]);
|
|
1241
|
+
return ref.trim();
|
|
1242
|
+
} catch {
|
|
1243
|
+
return "main";
|
|
1244
|
+
}
|
|
1240
1245
|
}
|
|
1241
1246
|
};
|
|
1242
1247
|
hasRemote = async (dir, name = "origin") => {
|
|
@@ -1480,7 +1485,7 @@ var init_github = __esm({
|
|
|
1480
1485
|
import { createHash } from "crypto";
|
|
1481
1486
|
import { readFile as readFile5, stat as stat4, readdir as readdir3, copyFile, symlink, unlink, rm as rm3 } from "fs/promises";
|
|
1482
1487
|
import { copy as copy3, ensureDir as ensureDir3 } from "fs-extra";
|
|
1483
|
-
import { join as join7, dirname as
|
|
1488
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
1484
1489
|
var getFileChecksum, getFileInfo, getDirectoryFiles, getDirectoryFileCount, copyFileOrDir, createSymlink, deleteFileOrDir;
|
|
1485
1490
|
var init_files = __esm({
|
|
1486
1491
|
"src/lib/files.ts"() {
|
|
@@ -1546,7 +1551,7 @@ var init_files = __esm({
|
|
|
1546
1551
|
if (!await pathExists(expandedSource)) {
|
|
1547
1552
|
throw new FileNotFoundError(source);
|
|
1548
1553
|
}
|
|
1549
|
-
await ensureDir3(
|
|
1554
|
+
await ensureDir3(dirname4(expandedDest));
|
|
1550
1555
|
const sourceIsDir = await isDirectory(expandedSource);
|
|
1551
1556
|
try {
|
|
1552
1557
|
if (sourceIsDir) {
|
|
@@ -1574,7 +1579,7 @@ var init_files = __esm({
|
|
|
1574
1579
|
if (!await pathExists(expandedTarget)) {
|
|
1575
1580
|
throw new FileNotFoundError(target);
|
|
1576
1581
|
}
|
|
1577
|
-
await ensureDir3(
|
|
1582
|
+
await ensureDir3(dirname4(expandedLink));
|
|
1578
1583
|
if (options?.overwrite && await pathExists(expandedLink)) {
|
|
1579
1584
|
await unlink(expandedLink);
|
|
1580
1585
|
}
|
|
@@ -2239,7 +2244,7 @@ var detectDotfiles = async () => {
|
|
|
2239
2244
|
// src/lib/timemachine.ts
|
|
2240
2245
|
init_paths();
|
|
2241
2246
|
init_errors();
|
|
2242
|
-
import { join as join5, dirname as
|
|
2247
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
2243
2248
|
import { readdir as readdir2, readFile as readFile3, writeFile as writeFile3, rm, stat as stat3 } from "fs/promises";
|
|
2244
2249
|
import { copy, ensureDir, pathExists as pathExists2 } from "fs-extra";
|
|
2245
2250
|
import { homedir as homedir3 } from "os";
|
|
@@ -2273,7 +2278,7 @@ var createSnapshot = async (filePaths, reason, profile) => {
|
|
|
2273
2278
|
const backupPath = join5(snapshotPath, "files", backupRelativePath);
|
|
2274
2279
|
const existed = await pathExists(expandedPath);
|
|
2275
2280
|
if (existed) {
|
|
2276
|
-
await ensureDir(
|
|
2281
|
+
await ensureDir(dirname3(backupPath));
|
|
2277
2282
|
await copy(expandedPath, backupPath, { overwrite: true, preserveTimestamps: true });
|
|
2278
2283
|
}
|
|
2279
2284
|
files.push({
|
|
@@ -2382,7 +2387,7 @@ var restoreSnapshot = async (snapshotId) => {
|
|
|
2382
2387
|
continue;
|
|
2383
2388
|
}
|
|
2384
2389
|
if (await pathExists2(file.backupPath)) {
|
|
2385
|
-
await ensureDir(
|
|
2390
|
+
await ensureDir(dirname3(file.originalPath));
|
|
2386
2391
|
await copy(file.backupPath, file.originalPath, { overwrite: true, preserveTimestamps: true });
|
|
2387
2392
|
restoredFiles.push(file.originalPath);
|
|
2388
2393
|
}
|
|
@@ -2410,7 +2415,7 @@ var restoreFileFromSnapshot = async (snapshotId, filePath) => {
|
|
|
2410
2415
|
if (!await pathExists2(file.backupPath)) {
|
|
2411
2416
|
throw new BackupError(`Backup file is missing: ${file.backupPath}`);
|
|
2412
2417
|
}
|
|
2413
|
-
await ensureDir(
|
|
2418
|
+
await ensureDir(dirname3(file.originalPath));
|
|
2414
2419
|
await copy(file.backupPath, file.originalPath, { overwrite: true, preserveTimestamps: true });
|
|
2415
2420
|
return true;
|
|
2416
2421
|
};
|
|
@@ -3070,30 +3075,52 @@ var runInteractiveInit = async () => {
|
|
|
3070
3075
|
prompts.log.info("Run `tuck restore --all` to restore all dotfiles");
|
|
3071
3076
|
}
|
|
3072
3077
|
} else {
|
|
3073
|
-
const existingDotfiles = [];
|
|
3074
|
-
for (const df of COMMON_DOTFILES) {
|
|
3075
|
-
const fullPath = expandPath(df.path);
|
|
3076
|
-
if (await pathExists(fullPath)) {
|
|
3077
|
-
existingDotfiles.push({
|
|
3078
|
-
path: df.path,
|
|
3079
|
-
label: `${df.path} (${df.category})`
|
|
3080
|
-
});
|
|
3081
|
-
}
|
|
3082
|
-
}
|
|
3083
3078
|
await initFromScratch(tuckDir, {});
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3079
|
+
const scanSpinner = prompts.spinner();
|
|
3080
|
+
scanSpinner.start("Scanning for dotfiles...");
|
|
3081
|
+
const detectedFiles = await detectDotfiles();
|
|
3082
|
+
const nonSensitiveFiles = detectedFiles.filter((f) => !f.sensitive);
|
|
3083
|
+
scanSpinner.stop(`Found ${nonSensitiveFiles.length} dotfiles on your system`);
|
|
3084
|
+
if (nonSensitiveFiles.length > 0) {
|
|
3085
|
+
const grouped = {};
|
|
3086
|
+
for (const file of nonSensitiveFiles) {
|
|
3087
|
+
if (!grouped[file.category]) grouped[file.category] = [];
|
|
3088
|
+
grouped[file.category].push(file);
|
|
3089
|
+
}
|
|
3090
|
+
console.log();
|
|
3091
|
+
const categoryOrder = ["shell", "git", "editors", "terminal", "ssh", "misc"];
|
|
3092
|
+
const sortedCategories = Object.keys(grouped).sort((a, b) => {
|
|
3093
|
+
const aIdx = categoryOrder.indexOf(a);
|
|
3094
|
+
const bIdx = categoryOrder.indexOf(b);
|
|
3095
|
+
if (aIdx === -1 && bIdx === -1) return a.localeCompare(b);
|
|
3096
|
+
if (aIdx === -1) return 1;
|
|
3097
|
+
if (bIdx === -1) return -1;
|
|
3098
|
+
return aIdx - bIdx;
|
|
3099
|
+
});
|
|
3100
|
+
for (const category of sortedCategories) {
|
|
3101
|
+
const files = grouped[category];
|
|
3102
|
+
const config = DETECTION_CATEGORIES[category] || { icon: "-", name: category };
|
|
3103
|
+
console.log(` ${config.icon} ${config.name}: ${files.length} files`);
|
|
3104
|
+
}
|
|
3105
|
+
console.log();
|
|
3106
|
+
const trackNow = await prompts.confirm("Would you like to track some of these now?", true);
|
|
3107
|
+
if (trackNow) {
|
|
3108
|
+
const options = nonSensitiveFiles.slice(0, 25).map((f) => ({
|
|
3088
3109
|
value: f.path,
|
|
3089
|
-
label: f.
|
|
3090
|
-
}))
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
`Run the following to track these files:
|
|
3095
|
-
tuck add ${selectedFiles.join(" ")}`
|
|
3110
|
+
label: `${collapsePath(f.path)} (${f.category})`
|
|
3111
|
+
}));
|
|
3112
|
+
const selectedFiles = await prompts.multiselect(
|
|
3113
|
+
"Select files to track:",
|
|
3114
|
+
options
|
|
3096
3115
|
);
|
|
3116
|
+
if (selectedFiles.length > 0) {
|
|
3117
|
+
prompts.log.step(
|
|
3118
|
+
`Run the following to track these files:
|
|
3119
|
+
tuck add ${selectedFiles.join(" ")}`
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
} else {
|
|
3123
|
+
prompts.log.info("Run 'tuck scan' later to interactively add files");
|
|
3097
3124
|
}
|
|
3098
3125
|
}
|
|
3099
3126
|
const wantsRemote = await prompts.confirm("Would you like to set up a remote repository?");
|
|
@@ -3618,13 +3645,32 @@ var runInteractivePush = async (tuckDir) => {
|
|
|
3618
3645
|
return;
|
|
3619
3646
|
}
|
|
3620
3647
|
const needsUpstream = !status.tracking;
|
|
3621
|
-
|
|
3622
|
-
await
|
|
3623
|
-
|
|
3624
|
-
|
|
3648
|
+
try {
|
|
3649
|
+
await withSpinner("Pushing...", async () => {
|
|
3650
|
+
await push(tuckDir, {
|
|
3651
|
+
setUpstream: needsUpstream,
|
|
3652
|
+
branch: needsUpstream ? branch : void 0
|
|
3653
|
+
});
|
|
3625
3654
|
});
|
|
3626
|
-
|
|
3627
|
-
|
|
3655
|
+
prompts.log.success("Pushed successfully!");
|
|
3656
|
+
} catch (error) {
|
|
3657
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3658
|
+
if (errorMsg.includes("Permission denied") || errorMsg.includes("publickey")) {
|
|
3659
|
+
prompts.log.error("Authentication failed");
|
|
3660
|
+
prompts.log.info("Check your SSH keys with: ssh -T git@github.com");
|
|
3661
|
+
prompts.log.info("Or try switching to HTTPS: git remote set-url origin https://...");
|
|
3662
|
+
} else if (errorMsg.includes("Could not resolve host") || errorMsg.includes("Network")) {
|
|
3663
|
+
prompts.log.error("Network error - could not reach remote");
|
|
3664
|
+
prompts.log.info("Check your internet connection and try again");
|
|
3665
|
+
} else if (errorMsg.includes("rejected") || errorMsg.includes("non-fast-forward")) {
|
|
3666
|
+
prompts.log.error("Push rejected - remote has changes");
|
|
3667
|
+
prompts.log.info("Run 'tuck pull' first, then push again");
|
|
3668
|
+
prompts.log.info("Or use 'tuck push --force' to overwrite (use with caution)");
|
|
3669
|
+
} else {
|
|
3670
|
+
prompts.log.error(`Push failed: ${errorMsg}`);
|
|
3671
|
+
}
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3628
3674
|
if (remoteUrl) {
|
|
3629
3675
|
let viewUrl = remoteUrl;
|
|
3630
3676
|
if (remoteUrl.startsWith("git@github.com:")) {
|
|
@@ -3651,14 +3697,27 @@ var runPush = async (options) => {
|
|
|
3651
3697
|
throw new GitError("No remote configured", "Run 'tuck init -r <url>' or add a remote manually");
|
|
3652
3698
|
}
|
|
3653
3699
|
const branch = await getCurrentBranch(tuckDir);
|
|
3654
|
-
|
|
3655
|
-
await
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3700
|
+
try {
|
|
3701
|
+
await withSpinner("Pushing...", async () => {
|
|
3702
|
+
await push(tuckDir, {
|
|
3703
|
+
force: options.force,
|
|
3704
|
+
setUpstream: Boolean(options.setUpstream),
|
|
3705
|
+
branch: options.setUpstream || branch
|
|
3706
|
+
});
|
|
3659
3707
|
});
|
|
3660
|
-
|
|
3661
|
-
|
|
3708
|
+
logger.success("Pushed successfully!");
|
|
3709
|
+
} catch (error) {
|
|
3710
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3711
|
+
if (errorMsg.includes("Permission denied") || errorMsg.includes("publickey")) {
|
|
3712
|
+
throw new GitError("Authentication failed", "Check your SSH keys: ssh -T git@github.com");
|
|
3713
|
+
} else if (errorMsg.includes("Could not resolve host") || errorMsg.includes("Network")) {
|
|
3714
|
+
throw new GitError("Network error", "Check your internet connection");
|
|
3715
|
+
} else if (errorMsg.includes("rejected") || errorMsg.includes("non-fast-forward")) {
|
|
3716
|
+
throw new GitError("Push rejected", "Run 'tuck pull' first, or use --force");
|
|
3717
|
+
} else {
|
|
3718
|
+
throw new GitError("Push failed", errorMsg);
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3662
3721
|
};
|
|
3663
3722
|
var pushCommand = new Command5("push").description("Push changes to remote repository").option("-f, --force", "Force push").option("--set-upstream <name>", "Set upstream branch").action(async (options) => {
|
|
3664
3723
|
await runPush(options);
|
|
@@ -3999,8 +4058,10 @@ init_manifest();
|
|
|
3999
4058
|
init_git();
|
|
4000
4059
|
init_files();
|
|
4001
4060
|
init_errors();
|
|
4061
|
+
init_constants();
|
|
4002
4062
|
import { Command as Command8 } from "commander";
|
|
4003
4063
|
import chalk13 from "chalk";
|
|
4064
|
+
import boxen2 from "boxen";
|
|
4004
4065
|
var detectFileChanges = async (tuckDir) => {
|
|
4005
4066
|
const files = await getAllTrackedFiles(tuckDir);
|
|
4006
4067
|
const changes = [];
|
|
@@ -4055,6 +4116,10 @@ var getFullStatus = async (tuckDir) => {
|
|
|
4055
4116
|
}
|
|
4056
4117
|
}
|
|
4057
4118
|
const fileChanges = await detectFileChanges(tuckDir);
|
|
4119
|
+
const categoryCounts = {};
|
|
4120
|
+
for (const file of Object.values(manifest.files)) {
|
|
4121
|
+
categoryCounts[file.category] = (categoryCounts[file.category] || 0) + 1;
|
|
4122
|
+
}
|
|
4058
4123
|
return {
|
|
4059
4124
|
tuckDir,
|
|
4060
4125
|
branch,
|
|
@@ -4063,6 +4128,7 @@ var getFullStatus = async (tuckDir) => {
|
|
|
4063
4128
|
ahead: gitStatus.ahead,
|
|
4064
4129
|
behind: gitStatus.behind,
|
|
4065
4130
|
trackedCount: Object.keys(manifest.files).length,
|
|
4131
|
+
categoryCounts,
|
|
4066
4132
|
changes: fileChanges,
|
|
4067
4133
|
gitChanges: {
|
|
4068
4134
|
staged: gitStatus.staged,
|
|
@@ -4072,33 +4138,58 @@ var getFullStatus = async (tuckDir) => {
|
|
|
4072
4138
|
};
|
|
4073
4139
|
};
|
|
4074
4140
|
var printStatus = (status) => {
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4141
|
+
const headerLines = [
|
|
4142
|
+
`${chalk13.bold.cyan("tuck")} ${chalk13.dim(`v${VERSION}`)}`,
|
|
4143
|
+
"",
|
|
4144
|
+
`${chalk13.dim("Repository:")} ${collapsePath(status.tuckDir)}`,
|
|
4145
|
+
`${chalk13.dim("Branch:")} ${chalk13.cyan(status.branch)}`
|
|
4146
|
+
];
|
|
4147
|
+
if (status.remote) {
|
|
4148
|
+
const shortRemote = status.remote.length > 40 ? status.remote.replace(/^https?:\/\//, "").replace(/\.git$/, "") : status.remote;
|
|
4149
|
+
headerLines.push(`${chalk13.dim("Remote:")} ${shortRemote}`);
|
|
4150
|
+
} else {
|
|
4151
|
+
headerLines.push(`${chalk13.dim("Remote:")} ${chalk13.yellow("not configured")}`);
|
|
4152
|
+
}
|
|
4153
|
+
console.log(boxen2(headerLines.join("\n"), {
|
|
4154
|
+
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
4155
|
+
borderColor: "cyan",
|
|
4156
|
+
borderStyle: "round"
|
|
4157
|
+
}));
|
|
4079
4158
|
if (status.remote) {
|
|
4080
|
-
console.log(chalk13.dim("Remote:"), status.remote);
|
|
4081
4159
|
let remoteInfo = "";
|
|
4082
4160
|
switch (status.remoteStatus) {
|
|
4083
4161
|
case "up-to-date":
|
|
4084
|
-
remoteInfo = chalk13.green("
|
|
4162
|
+
remoteInfo = chalk13.green("\u2713 Up to date with remote");
|
|
4085
4163
|
break;
|
|
4086
4164
|
case "ahead":
|
|
4087
|
-
remoteInfo = chalk13.yellow(
|
|
4165
|
+
remoteInfo = chalk13.yellow(`\u2191 ${status.ahead} commit${status.ahead > 1 ? "s" : ""} ahead of remote`);
|
|
4088
4166
|
break;
|
|
4089
4167
|
case "behind":
|
|
4090
|
-
remoteInfo = chalk13.yellow(
|
|
4168
|
+
remoteInfo = chalk13.yellow(`\u2193 ${status.behind} commit${status.behind > 1 ? "s" : ""} behind remote`);
|
|
4091
4169
|
break;
|
|
4092
4170
|
case "diverged":
|
|
4093
|
-
remoteInfo = chalk13.red(
|
|
4171
|
+
remoteInfo = chalk13.red(`\u26A0 Diverged (${status.ahead} ahead, ${status.behind} behind)`);
|
|
4094
4172
|
break;
|
|
4095
4173
|
}
|
|
4096
|
-
console.log(
|
|
4097
|
-
} else {
|
|
4098
|
-
console.log(chalk13.dim("Remote:"), chalk13.yellow("not configured"));
|
|
4174
|
+
console.log("\n" + remoteInfo);
|
|
4099
4175
|
}
|
|
4100
4176
|
console.log();
|
|
4101
|
-
console.log(chalk13.
|
|
4177
|
+
console.log(chalk13.bold(`Tracked Files: ${status.trackedCount}`));
|
|
4178
|
+
const categoryOrder = ["shell", "git", "editors", "terminal", "ssh", "misc"];
|
|
4179
|
+
const sortedCategories = Object.keys(status.categoryCounts).sort((a, b) => {
|
|
4180
|
+
const aIdx = categoryOrder.indexOf(a);
|
|
4181
|
+
const bIdx = categoryOrder.indexOf(b);
|
|
4182
|
+
if (aIdx === -1 && bIdx === -1) return a.localeCompare(b);
|
|
4183
|
+
if (aIdx === -1) return 1;
|
|
4184
|
+
if (bIdx === -1) return -1;
|
|
4185
|
+
return aIdx - bIdx;
|
|
4186
|
+
});
|
|
4187
|
+
if (sortedCategories.length > 0) {
|
|
4188
|
+
for (const category of sortedCategories) {
|
|
4189
|
+
const count = status.categoryCounts[category];
|
|
4190
|
+
console.log(chalk13.dim(` ${category}: ${count} file${count > 1 ? "s" : ""}`));
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4102
4193
|
if (status.changes.length > 0) {
|
|
4103
4194
|
console.log();
|
|
4104
4195
|
console.log(chalk13.bold("Changes detected:"));
|
|
@@ -4938,8 +5029,8 @@ var applyWithMerge = async (files, dryRun) => {
|
|
|
4938
5029
|
} else {
|
|
4939
5030
|
const { writeFile: writeFile5 } = await import("fs/promises");
|
|
4940
5031
|
const { ensureDir: ensureDir6 } = await import("fs-extra");
|
|
4941
|
-
const { dirname:
|
|
4942
|
-
await ensureDir6(
|
|
5032
|
+
const { dirname: dirname5 } = await import("path");
|
|
5033
|
+
await ensureDir6(dirname5(file.destination));
|
|
4943
5034
|
await writeFile5(file.destination, mergeResult.content, "utf-8");
|
|
4944
5035
|
logger.file("merge", collapsePath(file.destination));
|
|
4945
5036
|
}
|