@claudiu-ceia/spatch 0.3.0 → 0.3.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/README.md +187 -114
- package/dist/app.d.ts +1 -0
- package/dist/app.js +28 -0
- package/dist/cli.js +3 -119
- package/dist/command/flags.d.ts +64 -0
- package/dist/command/flags.js +73 -0
- package/dist/command/interactive/path-resolution.d.ts +5 -0
- package/dist/command/interactive/path-resolution.js +55 -0
- package/dist/command/interactive/run.d.ts +3 -0
- package/dist/command/interactive/run.js +166 -0
- package/dist/command/interactive/terminal.d.ts +32 -0
- package/dist/command/interactive/terminal.js +79 -0
- package/dist/command/interactive/types.d.ts +13 -0
- package/dist/command/interactive/types.js +0 -0
- package/dist/command/interactive/validation.d.ts +2 -0
- package/dist/command/interactive/validation.js +19 -0
- package/dist/command/interactive.d.ts +1 -0
- package/dist/command/interactive.js +1 -0
- package/dist/command/output.d.ts +11 -0
- package/dist/command/output.js +82 -0
- package/dist/command.d.ts +22 -25
- package/dist/command.js +36 -334
- package/dist/file-write.d.ts +24 -0
- package/dist/file-write.js +50 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +2 -3
- package/dist/internal/command.d.ts +1 -0
- package/dist/internal/command.js +1 -0
- package/dist/phases/output.d.ts +2 -1
- package/dist/phases/parse.d.ts +2 -2
- package/dist/phases/parse.js +6 -6
- package/dist/phases/patch-document.d.ts +6 -0
- package/dist/{patch-document.js → phases/patch-document.js} +6 -15
- package/dist/phases/rewrite.js +128 -33
- package/dist/replacement-spans.d.ts +7 -0
- package/dist/replacement-spans.js +26 -0
- package/dist/spatch.d.ts +0 -1
- package/dist/spatch.js +1 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -13
- package/src/app.ts +34 -0
- package/src/cli.ts +3 -143
- package/src/command/flags.ts +85 -0
- package/src/command/interactive/path-resolution.ts +72 -0
- package/src/command/interactive/run.ts +207 -0
- package/src/command/interactive/terminal.ts +134 -0
- package/src/command/interactive/types.ts +20 -0
- package/src/command/interactive/validation.ts +36 -0
- package/src/command/interactive.ts +1 -0
- package/src/command/output.ts +109 -0
- package/src/command.ts +82 -484
- package/src/file-write.ts +80 -0
- package/src/index.ts +3 -21
- package/src/internal/command.ts +1 -0
- package/src/phases/output.ts +1 -1
- package/src/phases/parse.ts +7 -7
- package/src/{patch-document.ts → phases/patch-document.ts} +16 -30
- package/src/phases/rewrite.ts +177 -53
- package/src/replacement-spans.ts +37 -0
- package/src/spatch.ts +1 -6
- package/dist/patch-document.d.ts +0 -9
- package/dist/template.d.ts +0 -2
- package/dist/template.js +0 -1
- package/src/template.ts +0 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import chalk, { Chalk, type ChalkInstance } from "chalk";
|
|
2
|
+
import type { SpatchResult } from "../types.ts";
|
|
3
|
+
|
|
4
|
+
export type FormatPatchOutputOptions = {
|
|
5
|
+
color?: boolean;
|
|
6
|
+
chalkInstance?: ChalkInstance;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function formatPatchOutput(
|
|
10
|
+
result: SpatchResult,
|
|
11
|
+
options: FormatPatchOutputOptions = {},
|
|
12
|
+
): string {
|
|
13
|
+
const chalkInstance = buildChalk(options);
|
|
14
|
+
const useColor = chalkInstance.level > 0;
|
|
15
|
+
const lines: string[] = [];
|
|
16
|
+
const changedFiles = result.files.filter((file) => file.replacementCount > 0);
|
|
17
|
+
|
|
18
|
+
for (const file of changedFiles) {
|
|
19
|
+
const safeFile = escapeTerminalText(file.file);
|
|
20
|
+
const headerPrefix = useColor ? chalkInstance.bold : (value: string) => value;
|
|
21
|
+
lines.push(headerPrefix(`diff --git a/${safeFile} b/${safeFile}`));
|
|
22
|
+
lines.push(useColor ? chalkInstance.gray(`--- a/${safeFile}`) : `--- a/${safeFile}`);
|
|
23
|
+
lines.push(useColor ? chalkInstance.gray(`+++ b/${safeFile}`) : `+++ b/${safeFile}`);
|
|
24
|
+
|
|
25
|
+
for (const occurrence of file.occurrences) {
|
|
26
|
+
if (occurrence.matched === occurrence.replacement) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const oldCount = countLines(occurrence.matched);
|
|
31
|
+
const newCount = countLines(occurrence.replacement);
|
|
32
|
+
const hunkHeader = `@@ -${occurrence.line},${oldCount} +${occurrence.line},${newCount} @@`;
|
|
33
|
+
lines.push(useColor ? chalkInstance.cyan(hunkHeader) : hunkHeader);
|
|
34
|
+
|
|
35
|
+
for (const oldLine of splitDiffLines(occurrence.matched)) {
|
|
36
|
+
const line = `-${escapeTerminalText(oldLine)}`;
|
|
37
|
+
lines.push(useColor ? chalkInstance.red(line) : line);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const newLine of splitDiffLines(occurrence.replacement)) {
|
|
41
|
+
const line = `+${escapeTerminalText(newLine)}`;
|
|
42
|
+
lines.push(useColor ? chalkInstance.green(line) : line);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (changedFiles.length === 0) {
|
|
48
|
+
lines.push(useColor ? chalkInstance.gray("No changes.") : "No changes.");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const summary = [
|
|
52
|
+
`${result.filesChanged} ${pluralize("file", result.filesChanged)} changed`,
|
|
53
|
+
`${result.totalReplacements} ${pluralize("replacement", result.totalReplacements)}`,
|
|
54
|
+
result.dryRun ? "(dry-run)" : null,
|
|
55
|
+
]
|
|
56
|
+
.filter((part) => part !== null)
|
|
57
|
+
.join(", ");
|
|
58
|
+
lines.push(useColor ? chalkInstance.gray(summary) : summary);
|
|
59
|
+
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function buildChalk(options: FormatPatchOutputOptions): ChalkInstance {
|
|
64
|
+
if (options.chalkInstance) {
|
|
65
|
+
return options.chalkInstance;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const shouldColor = options.color ?? false;
|
|
69
|
+
if (!shouldColor) {
|
|
70
|
+
return new Chalk({ level: 0 });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const level = chalk.level > 0 ? chalk.level : 1;
|
|
74
|
+
return new Chalk({ level });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function splitDiffLines(text: string): string[] {
|
|
78
|
+
const normalized = text.replaceAll("\r\n", "\n");
|
|
79
|
+
if (normalized.length === 0) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (normalized.endsWith("\n")) {
|
|
84
|
+
return normalized.slice(0, -1).split("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return normalized.split("\n");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function countLines(text: string): number {
|
|
91
|
+
return splitDiffLines(text).length;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function escapeTerminalText(text: string): string {
|
|
95
|
+
let output = "";
|
|
96
|
+
for (const char of text) {
|
|
97
|
+
const code = char.charCodeAt(0);
|
|
98
|
+
if (code === 0x1b || code < 0x20 || code === 0x7f) {
|
|
99
|
+
output += `\\x${code.toString(16).padStart(2, "0")}`;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
output += char;
|
|
103
|
+
}
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function pluralize(word: string, count: number): string {
|
|
108
|
+
return count === 1 ? word : `${word}s`;
|
|
109
|
+
}
|