@git.zone/cli 1.16.7 → 1.16.8
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/mod_format/classes.baseformatter.d.ts +0 -2
- package/dist_ts/mod_format/classes.baseformatter.js +3 -22
- package/dist_ts/mod_format/classes.changecache.js +14 -29
- package/dist_ts/mod_format/classes.dependency-analyzer.js +24 -18
- package/dist_ts/mod_format/classes.diffreporter.js +4 -4
- package/dist_ts/mod_format/classes.formatcontext.d.ts +0 -13
- package/dist_ts/mod_format/classes.formatcontext.js +1 -41
- package/dist_ts/mod_format/classes.formatplanner.js +25 -45
- package/dist_ts/mod_format/classes.formatstats.js +22 -20
- package/dist_ts/mod_format/classes.rollbackmanager.js +20 -32
- package/dist_ts/mod_format/format.cleanup.js +7 -2
- package/dist_ts/mod_format/format.copy.d.ts +1 -1
- package/dist_ts/mod_format/format.copy.js +3 -3
- package/dist_ts/mod_format/format.gitignore.js +1 -1
- package/dist_ts/mod_format/format.license.js +1 -1
- package/dist_ts/mod_format/format.npmextra.js +1 -1
- package/dist_ts/mod_format/format.packagejson.js +5 -3
- package/dist_ts/mod_format/format.prettier.js +7 -2
- package/dist_ts/mod_format/format.readme.js +1 -1
- package/dist_ts/mod_format/format.templates.js +1 -1
- package/dist_ts/mod_format/format.tsconfig.js +4 -2
- package/dist_ts/mod_format/formatters/cleanup.formatter.js +8 -3
- package/dist_ts/mod_format/formatters/legacy.formatter.js +6 -4
- package/dist_ts/mod_format/formatters/prettier.formatter.js +9 -16
- package/dist_ts/mod_format/formatters/readme.formatter.js +6 -4
- package/dist_ts/mod_format/index.js +13 -71
- package/dist_ts/mod_meta/meta.classes.meta.js +1 -1
- package/dist_ts/mod_standard/index.js +1 -1
- package/dist_ts/mod_start/index.js +1 -1
- package/dist_ts/mod_template/index.js +1 -1
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/mod_format/classes.baseformatter.ts +15 -38
- package/ts/mod_format/classes.changecache.ts +74 -80
- package/ts/mod_format/classes.dependency-analyzer.ts +46 -36
- package/ts/mod_format/classes.diffreporter.ts +44 -28
- package/ts/mod_format/classes.formatcontext.ts +3 -54
- package/ts/mod_format/classes.formatplanner.ts +56 -70
- package/ts/mod_format/classes.formatstats.ts +69 -49
- package/ts/mod_format/classes.rollbackmanager.ts +123 -103
- package/ts/mod_format/format.cleanup.ts +9 -2
- package/ts/mod_format/format.copy.ts +17 -14
- package/ts/mod_format/format.gitignore.ts +2 -1
- package/ts/mod_format/format.license.ts +3 -1
- package/ts/mod_format/format.npmextra.ts +9 -2
- package/ts/mod_format/format.packagejson.ts +47 -12
- package/ts/mod_format/format.prettier.ts +10 -2
- package/ts/mod_format/format.readme.ts +2 -1
- package/ts/mod_format/format.templates.ts +15 -7
- package/ts/mod_format/format.tsconfig.ts +6 -2
- package/ts/mod_format/formatters/cleanup.formatter.ts +13 -8
- package/ts/mod_format/formatters/copy.formatter.ts +1 -1
- package/ts/mod_format/formatters/gitignore.formatter.ts +1 -1
- package/ts/mod_format/formatters/legacy.formatter.ts +19 -12
- package/ts/mod_format/formatters/license.formatter.ts +1 -1
- package/ts/mod_format/formatters/npmextra.formatter.ts +1 -1
- package/ts/mod_format/formatters/packagejson.formatter.ts +1 -1
- package/ts/mod_format/formatters/prettier.formatter.ts +50 -47
- package/ts/mod_format/formatters/readme.formatter.ts +11 -9
- package/ts/mod_format/formatters/templates.formatter.ts +1 -1
- package/ts/mod_format/formatters/tsconfig.formatter.ts +1 -1
- package/ts/mod_format/index.ts +54 -113
- package/ts/mod_format/interfaces.format.ts +3 -3
- package/ts/mod_meta/meta.classes.meta.ts +57 -19
- package/ts/mod_standard/index.ts +3 -1
- package/ts/mod_start/index.ts +3 -1
- package/ts/mod_template/index.ts +5 -2
|
@@ -18,32 +18,32 @@ export class ChangeCache {
|
|
|
18
18
|
private cacheDir: string;
|
|
19
19
|
private manifestPath: string;
|
|
20
20
|
private cacheVersion = '1.0.0';
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
constructor() {
|
|
23
23
|
this.cacheDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-cache');
|
|
24
24
|
this.manifestPath = plugins.path.join(this.cacheDir, 'manifest.json');
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
async initialize(): Promise<void> {
|
|
28
28
|
await plugins.smartfile.fs.ensureDir(this.cacheDir);
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
async getManifest(): Promise<ICacheManifest> {
|
|
32
32
|
const defaultManifest: ICacheManifest = {
|
|
33
33
|
version: this.cacheVersion,
|
|
34
34
|
lastFormat: 0,
|
|
35
|
-
files: []
|
|
35
|
+
files: [],
|
|
36
36
|
};
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
|
|
39
39
|
if (!exists) {
|
|
40
40
|
return defaultManifest;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
try {
|
|
44
44
|
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
|
|
45
45
|
const manifest = JSON.parse(content);
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
// Validate the manifest structure
|
|
48
48
|
if (this.isValidManifest(manifest)) {
|
|
49
49
|
return manifest;
|
|
@@ -52,7 +52,9 @@ export class ChangeCache {
|
|
|
52
52
|
return defaultManifest;
|
|
53
53
|
}
|
|
54
54
|
} catch (error) {
|
|
55
|
-
console.warn(
|
|
55
|
+
console.warn(
|
|
56
|
+
`Failed to read cache manifest: ${error.message}, returning default manifest`,
|
|
57
|
+
);
|
|
56
58
|
// Try to delete the corrupted file
|
|
57
59
|
try {
|
|
58
60
|
await plugins.smartfile.fs.remove(this.manifestPath);
|
|
@@ -62,168 +64,160 @@ export class ChangeCache {
|
|
|
62
64
|
return defaultManifest;
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
|
-
|
|
67
|
+
|
|
66
68
|
async saveManifest(manifest: ICacheManifest): Promise<void> {
|
|
67
69
|
// Validate before saving
|
|
68
70
|
if (!this.isValidManifest(manifest)) {
|
|
69
71
|
throw new Error('Invalid manifest structure, cannot save');
|
|
70
72
|
}
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await plugins.smartfile.memory.toFs(jsonContent, tempPath);
|
|
79
|
-
|
|
80
|
-
// Move temp file to actual manifest (atomic-like operation)
|
|
81
|
-
// Since smartfile doesn't have rename, we copy and delete
|
|
82
|
-
await plugins.smartfile.fs.copy(tempPath, this.manifestPath);
|
|
83
|
-
await plugins.smartfile.fs.remove(tempPath);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
// Clean up temp file if it exists
|
|
86
|
-
try {
|
|
87
|
-
await plugins.smartfile.fs.remove(tempPath);
|
|
88
|
-
} catch (removeError) {
|
|
89
|
-
// Ignore removal errors
|
|
90
|
-
}
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
73
|
+
|
|
74
|
+
// Ensure directory exists
|
|
75
|
+
await plugins.smartfile.fs.ensureDir(this.cacheDir);
|
|
76
|
+
|
|
77
|
+
// Write directly with proper JSON stringification
|
|
78
|
+
const jsonContent = JSON.stringify(manifest, null, 2);
|
|
79
|
+
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
|
|
93
80
|
}
|
|
94
|
-
|
|
81
|
+
|
|
95
82
|
async hasFileChanged(filePath: string): Promise<boolean> {
|
|
96
|
-
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
97
|
-
? filePath
|
|
83
|
+
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
84
|
+
? filePath
|
|
98
85
|
: plugins.path.join(paths.cwd, filePath);
|
|
99
|
-
|
|
86
|
+
|
|
100
87
|
// Check if file exists
|
|
101
88
|
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
|
|
102
89
|
if (!exists) {
|
|
103
90
|
return true; // File doesn't exist, so it's "changed" (will be created)
|
|
104
91
|
}
|
|
105
|
-
|
|
92
|
+
|
|
106
93
|
// Get current file stats
|
|
107
94
|
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
|
108
|
-
|
|
95
|
+
|
|
109
96
|
// Skip directories
|
|
110
97
|
if (stats.isDirectory()) {
|
|
111
98
|
return false; // Directories are not processed
|
|
112
99
|
}
|
|
113
|
-
|
|
100
|
+
|
|
114
101
|
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
|
115
102
|
const currentChecksum = this.calculateChecksum(content);
|
|
116
|
-
|
|
103
|
+
|
|
117
104
|
// Get cached info
|
|
118
105
|
const manifest = await this.getManifest();
|
|
119
|
-
const cachedFile = manifest.files.find(f => f.path === filePath);
|
|
120
|
-
|
|
106
|
+
const cachedFile = manifest.files.find((f) => f.path === filePath);
|
|
107
|
+
|
|
121
108
|
if (!cachedFile) {
|
|
122
109
|
return true; // Not in cache, so it's changed
|
|
123
110
|
}
|
|
124
|
-
|
|
111
|
+
|
|
125
112
|
// Compare checksums
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
return (
|
|
114
|
+
cachedFile.checksum !== currentChecksum ||
|
|
115
|
+
cachedFile.size !== stats.size ||
|
|
116
|
+
cachedFile.modified !== stats.mtimeMs
|
|
117
|
+
);
|
|
129
118
|
}
|
|
130
|
-
|
|
119
|
+
|
|
131
120
|
async updateFileCache(filePath: string): Promise<void> {
|
|
132
|
-
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
133
|
-
? filePath
|
|
121
|
+
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
122
|
+
? filePath
|
|
134
123
|
: plugins.path.join(paths.cwd, filePath);
|
|
135
|
-
|
|
124
|
+
|
|
136
125
|
// Get current file stats
|
|
137
126
|
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
|
138
|
-
|
|
127
|
+
|
|
139
128
|
// Skip directories
|
|
140
129
|
if (stats.isDirectory()) {
|
|
141
130
|
return; // Don't cache directories
|
|
142
131
|
}
|
|
143
|
-
|
|
132
|
+
|
|
144
133
|
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
|
145
134
|
const checksum = this.calculateChecksum(content);
|
|
146
|
-
|
|
135
|
+
|
|
147
136
|
// Update manifest
|
|
148
137
|
const manifest = await this.getManifest();
|
|
149
|
-
const existingIndex = manifest.files.findIndex(f => f.path === filePath);
|
|
150
|
-
|
|
138
|
+
const existingIndex = manifest.files.findIndex((f) => f.path === filePath);
|
|
139
|
+
|
|
151
140
|
const cacheEntry: IFileCache = {
|
|
152
141
|
path: filePath,
|
|
153
142
|
checksum,
|
|
154
143
|
modified: stats.mtimeMs,
|
|
155
|
-
size: stats.size
|
|
144
|
+
size: stats.size,
|
|
156
145
|
};
|
|
157
|
-
|
|
146
|
+
|
|
158
147
|
if (existingIndex !== -1) {
|
|
159
148
|
manifest.files[existingIndex] = cacheEntry;
|
|
160
149
|
} else {
|
|
161
150
|
manifest.files.push(cacheEntry);
|
|
162
151
|
}
|
|
163
|
-
|
|
152
|
+
|
|
164
153
|
manifest.lastFormat = Date.now();
|
|
165
154
|
await this.saveManifest(manifest);
|
|
166
155
|
}
|
|
167
|
-
|
|
156
|
+
|
|
168
157
|
async getChangedFiles(filePaths: string[]): Promise<string[]> {
|
|
169
158
|
const changedFiles: string[] = [];
|
|
170
|
-
|
|
159
|
+
|
|
171
160
|
for (const filePath of filePaths) {
|
|
172
161
|
if (await this.hasFileChanged(filePath)) {
|
|
173
162
|
changedFiles.push(filePath);
|
|
174
163
|
}
|
|
175
164
|
}
|
|
176
|
-
|
|
165
|
+
|
|
177
166
|
return changedFiles;
|
|
178
167
|
}
|
|
179
|
-
|
|
168
|
+
|
|
180
169
|
async clean(): Promise<void> {
|
|
181
170
|
const manifest = await this.getManifest();
|
|
182
171
|
const validFiles: IFileCache[] = [];
|
|
183
|
-
|
|
172
|
+
|
|
184
173
|
// Remove entries for files that no longer exist
|
|
185
174
|
for (const file of manifest.files) {
|
|
186
|
-
const absolutePath = plugins.path.isAbsolute(file.path)
|
|
187
|
-
? file.path
|
|
175
|
+
const absolutePath = plugins.path.isAbsolute(file.path)
|
|
176
|
+
? file.path
|
|
188
177
|
: plugins.path.join(paths.cwd, file.path);
|
|
189
|
-
|
|
178
|
+
|
|
190
179
|
if (await plugins.smartfile.fs.fileExists(absolutePath)) {
|
|
191
180
|
validFiles.push(file);
|
|
192
181
|
}
|
|
193
182
|
}
|
|
194
|
-
|
|
183
|
+
|
|
195
184
|
manifest.files = validFiles;
|
|
196
185
|
await this.saveManifest(manifest);
|
|
197
186
|
}
|
|
198
|
-
|
|
187
|
+
|
|
199
188
|
private calculateChecksum(content: string | Buffer): string {
|
|
200
189
|
return plugins.crypto.createHash('sha256').update(content).digest('hex');
|
|
201
190
|
}
|
|
202
|
-
|
|
191
|
+
|
|
203
192
|
private isValidManifest(manifest: any): manifest is ICacheManifest {
|
|
204
193
|
// Check if manifest has the required structure
|
|
205
194
|
if (!manifest || typeof manifest !== 'object') {
|
|
206
195
|
return false;
|
|
207
196
|
}
|
|
208
|
-
|
|
197
|
+
|
|
209
198
|
// Check required fields
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
199
|
+
if (
|
|
200
|
+
typeof manifest.version !== 'string' ||
|
|
201
|
+
typeof manifest.lastFormat !== 'number' ||
|
|
202
|
+
!Array.isArray(manifest.files)
|
|
203
|
+
) {
|
|
213
204
|
return false;
|
|
214
205
|
}
|
|
215
|
-
|
|
206
|
+
|
|
216
207
|
// Check each file entry
|
|
217
208
|
for (const file of manifest.files) {
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
209
|
+
if (
|
|
210
|
+
!file ||
|
|
211
|
+
typeof file !== 'object' ||
|
|
212
|
+
typeof file.path !== 'string' ||
|
|
213
|
+
typeof file.checksum !== 'string' ||
|
|
214
|
+
typeof file.modified !== 'number' ||
|
|
215
|
+
typeof file.size !== 'number'
|
|
216
|
+
) {
|
|
223
217
|
return false;
|
|
224
218
|
}
|
|
225
219
|
}
|
|
226
|
-
|
|
220
|
+
|
|
227
221
|
return true;
|
|
228
222
|
}
|
|
229
|
-
}
|
|
223
|
+
}
|
|
@@ -9,35 +9,42 @@ export interface IModuleDependency {
|
|
|
9
9
|
|
|
10
10
|
export class DependencyAnalyzer {
|
|
11
11
|
private moduleDependencies: Map<string, IModuleDependency> = new Map();
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
constructor() {
|
|
14
14
|
this.initializeDependencies();
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
private initializeDependencies(): void {
|
|
18
18
|
// Define dependencies between format modules
|
|
19
19
|
const dependencies = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
cleanup: [], // No dependencies
|
|
21
|
+
npmextra: [], // No dependencies
|
|
22
|
+
license: ['npmextra'], // Depends on npmextra for config
|
|
23
|
+
packagejson: ['npmextra'], // Depends on npmextra for config
|
|
24
|
+
templates: ['npmextra', 'packagejson'], // Depends on both
|
|
25
|
+
gitignore: ['templates'], // Depends on templates
|
|
26
|
+
tsconfig: ['packagejson'], // Depends on package.json
|
|
27
|
+
prettier: [
|
|
28
|
+
'cleanup',
|
|
29
|
+
'npmextra',
|
|
30
|
+
'packagejson',
|
|
31
|
+
'templates',
|
|
32
|
+
'gitignore',
|
|
33
|
+
'tsconfig',
|
|
34
|
+
], // Runs after most others
|
|
35
|
+
readme: ['npmextra', 'packagejson'], // Depends on project metadata
|
|
36
|
+
copy: ['npmextra'], // Depends on config
|
|
30
37
|
};
|
|
31
|
-
|
|
38
|
+
|
|
32
39
|
// Initialize all modules
|
|
33
40
|
for (const [module, deps] of Object.entries(dependencies)) {
|
|
34
41
|
this.moduleDependencies.set(module, {
|
|
35
42
|
module,
|
|
36
43
|
dependencies: new Set(deps),
|
|
37
|
-
dependents: new Set()
|
|
44
|
+
dependents: new Set(),
|
|
38
45
|
});
|
|
39
46
|
}
|
|
40
|
-
|
|
47
|
+
|
|
41
48
|
// Build reverse dependencies (dependents)
|
|
42
49
|
for (const [module, deps] of Object.entries(dependencies)) {
|
|
43
50
|
for (const dep of deps) {
|
|
@@ -48,34 +55,35 @@ export class DependencyAnalyzer {
|
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
|
-
|
|
58
|
+
|
|
52
59
|
getExecutionGroups(modules: BaseFormatter[]): BaseFormatter[][] {
|
|
53
|
-
const modulesMap = new Map(modules.map(m => [m.name, m]));
|
|
60
|
+
const modulesMap = new Map(modules.map((m) => [m.name, m]));
|
|
54
61
|
const executed = new Set<string>();
|
|
55
62
|
const groups: BaseFormatter[][] = [];
|
|
56
|
-
|
|
63
|
+
|
|
57
64
|
while (executed.size < modules.length) {
|
|
58
65
|
const currentGroup: BaseFormatter[] = [];
|
|
59
|
-
|
|
66
|
+
|
|
60
67
|
for (const module of modules) {
|
|
61
68
|
if (executed.has(module.name)) continue;
|
|
62
|
-
|
|
69
|
+
|
|
63
70
|
const dependency = this.moduleDependencies.get(module.name);
|
|
64
71
|
if (!dependency) {
|
|
65
72
|
// Unknown module, execute in isolation
|
|
66
73
|
currentGroup.push(module);
|
|
67
74
|
continue;
|
|
68
75
|
}
|
|
69
|
-
|
|
76
|
+
|
|
70
77
|
// Check if all dependencies have been executed
|
|
71
|
-
const allDepsExecuted = Array.from(dependency.dependencies)
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
const allDepsExecuted = Array.from(dependency.dependencies).every(
|
|
79
|
+
(dep) => executed.has(dep) || !modulesMap.has(dep),
|
|
80
|
+
);
|
|
81
|
+
|
|
74
82
|
if (allDepsExecuted) {
|
|
75
83
|
currentGroup.push(module);
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
|
-
|
|
86
|
+
|
|
79
87
|
if (currentGroup.length === 0) {
|
|
80
88
|
// Circular dependency or error - execute remaining modules
|
|
81
89
|
for (const module of modules) {
|
|
@@ -84,24 +92,26 @@ export class DependencyAnalyzer {
|
|
|
84
92
|
}
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
|
-
|
|
88
|
-
currentGroup.forEach(m => executed.add(m.name));
|
|
95
|
+
|
|
96
|
+
currentGroup.forEach((m) => executed.add(m.name));
|
|
89
97
|
groups.push(currentGroup);
|
|
90
98
|
}
|
|
91
|
-
|
|
99
|
+
|
|
92
100
|
return groups;
|
|
93
101
|
}
|
|
94
|
-
|
|
102
|
+
|
|
95
103
|
canRunInParallel(module1: string, module2: string): boolean {
|
|
96
104
|
const dep1 = this.moduleDependencies.get(module1);
|
|
97
105
|
const dep2 = this.moduleDependencies.get(module2);
|
|
98
|
-
|
|
106
|
+
|
|
99
107
|
if (!dep1 || !dep2) return false;
|
|
100
|
-
|
|
108
|
+
|
|
101
109
|
// Check if module1 depends on module2 or vice versa
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
return (
|
|
111
|
+
!dep1.dependencies.has(module2) &&
|
|
112
|
+
!dep2.dependencies.has(module1) &&
|
|
113
|
+
!dep1.dependents.has(module2) &&
|
|
114
|
+
!dep2.dependents.has(module1)
|
|
115
|
+
);
|
|
106
116
|
}
|
|
107
|
-
}
|
|
117
|
+
}
|
|
@@ -4,72 +4,85 @@ import { logger } from '../gitzone.logging.js';
|
|
|
4
4
|
|
|
5
5
|
export class DiffReporter {
|
|
6
6
|
private diffs: Map<string, string> = new Map();
|
|
7
|
-
|
|
8
|
-
async generateDiff(
|
|
7
|
+
|
|
8
|
+
async generateDiff(
|
|
9
|
+
filePath: string,
|
|
10
|
+
oldContent: string,
|
|
11
|
+
newContent: string,
|
|
12
|
+
): Promise<string> {
|
|
9
13
|
const diff = plugins.smartdiff.createDiff(oldContent, newContent);
|
|
10
14
|
this.diffs.set(filePath, diff);
|
|
11
15
|
return diff;
|
|
12
16
|
}
|
|
13
|
-
|
|
17
|
+
|
|
14
18
|
async generateDiffForChange(change: IPlannedChange): Promise<string | null> {
|
|
15
19
|
if (change.type !== 'modify') {
|
|
16
20
|
return null;
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
|
|
19
23
|
try {
|
|
20
24
|
const exists = await plugins.smartfile.fs.fileExists(change.path);
|
|
21
25
|
if (!exists) {
|
|
22
26
|
return null;
|
|
23
27
|
}
|
|
24
|
-
|
|
25
|
-
const currentContent = await plugins.smartfile.fs.toStringSync(
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
const currentContent = await plugins.smartfile.fs.toStringSync(
|
|
30
|
+
change.path,
|
|
31
|
+
);
|
|
32
|
+
|
|
27
33
|
// For planned changes, we need the new content
|
|
28
34
|
if (!change.content) {
|
|
29
35
|
return null;
|
|
30
36
|
}
|
|
31
|
-
|
|
32
|
-
return await this.generateDiff(
|
|
37
|
+
|
|
38
|
+
return await this.generateDiff(
|
|
39
|
+
change.path,
|
|
40
|
+
currentContent,
|
|
41
|
+
change.content,
|
|
42
|
+
);
|
|
33
43
|
} catch (error) {
|
|
34
|
-
logger.log(
|
|
44
|
+
logger.log(
|
|
45
|
+
'error',
|
|
46
|
+
`Failed to generate diff for ${change.path}: ${error.message}`,
|
|
47
|
+
);
|
|
35
48
|
return null;
|
|
36
49
|
}
|
|
37
50
|
}
|
|
38
|
-
|
|
51
|
+
|
|
39
52
|
displayDiff(filePath: string, diff?: string): void {
|
|
40
53
|
const diffToShow = diff || this.diffs.get(filePath);
|
|
41
|
-
|
|
54
|
+
|
|
42
55
|
if (!diffToShow) {
|
|
43
56
|
logger.log('warn', `No diff available for ${filePath}`);
|
|
44
57
|
return;
|
|
45
58
|
}
|
|
46
|
-
|
|
59
|
+
|
|
47
60
|
console.log(`\n${this.formatDiffHeader(filePath)}`);
|
|
48
61
|
console.log(this.colorDiff(diffToShow));
|
|
49
62
|
console.log('━'.repeat(50));
|
|
50
63
|
}
|
|
51
|
-
|
|
64
|
+
|
|
52
65
|
displayAllDiffs(): void {
|
|
53
66
|
if (this.diffs.size === 0) {
|
|
54
67
|
logger.log('info', 'No diffs to display');
|
|
55
68
|
return;
|
|
56
69
|
}
|
|
57
|
-
|
|
70
|
+
|
|
58
71
|
console.log('\nFile Changes:');
|
|
59
72
|
console.log('═'.repeat(50));
|
|
60
|
-
|
|
73
|
+
|
|
61
74
|
for (const [filePath, diff] of this.diffs) {
|
|
62
75
|
this.displayDiff(filePath, diff);
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
|
-
|
|
78
|
+
|
|
66
79
|
private formatDiffHeader(filePath: string): string {
|
|
67
80
|
return `📄 ${filePath}`;
|
|
68
81
|
}
|
|
69
|
-
|
|
82
|
+
|
|
70
83
|
private colorDiff(diff: string): string {
|
|
71
84
|
const lines = diff.split('\n');
|
|
72
|
-
const coloredLines = lines.map(line => {
|
|
85
|
+
const coloredLines = lines.map((line) => {
|
|
73
86
|
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
74
87
|
return `\x1b[32m${line}\x1b[0m`; // Green for additions
|
|
75
88
|
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
@@ -80,29 +93,32 @@ export class DiffReporter {
|
|
|
80
93
|
return line;
|
|
81
94
|
}
|
|
82
95
|
});
|
|
83
|
-
|
|
96
|
+
|
|
84
97
|
return coloredLines.join('\n');
|
|
85
98
|
}
|
|
86
|
-
|
|
99
|
+
|
|
87
100
|
async saveDiffReport(outputPath: string): Promise<void> {
|
|
88
101
|
const report = {
|
|
89
102
|
timestamp: new Date().toISOString(),
|
|
90
103
|
totalFiles: this.diffs.size,
|
|
91
104
|
diffs: Array.from(this.diffs.entries()).map(([path, diff]) => ({
|
|
92
105
|
path,
|
|
93
|
-
diff
|
|
94
|
-
}))
|
|
106
|
+
diff,
|
|
107
|
+
})),
|
|
95
108
|
};
|
|
96
|
-
|
|
97
|
-
await plugins.smartfile.memory.toFs(
|
|
109
|
+
|
|
110
|
+
await plugins.smartfile.memory.toFs(
|
|
111
|
+
JSON.stringify(report, null, 2),
|
|
112
|
+
outputPath,
|
|
113
|
+
);
|
|
98
114
|
logger.log('info', `Diff report saved to ${outputPath}`);
|
|
99
115
|
}
|
|
100
|
-
|
|
116
|
+
|
|
101
117
|
hasAnyDiffs(): boolean {
|
|
102
118
|
return this.diffs.size > 0;
|
|
103
119
|
}
|
|
104
|
-
|
|
120
|
+
|
|
105
121
|
getDiffCount(): number {
|
|
106
122
|
return this.diffs.size;
|
|
107
123
|
}
|
|
108
|
-
}
|
|
124
|
+
}
|
|
@@ -1,65 +1,14 @@
|
|
|
1
1
|
import * as plugins from './mod.plugins.js';
|
|
2
|
-
import { RollbackManager } from './classes.rollbackmanager.js';
|
|
3
|
-
import { ChangeCache } from './classes.changecache.js';
|
|
4
2
|
import { FormatStats } from './classes.formatstats.js';
|
|
5
|
-
import type { IFormatOperation, IFormatPlan } from './interfaces.format.js';
|
|
6
3
|
|
|
7
4
|
export class FormatContext {
|
|
8
|
-
private rollbackManager: RollbackManager;
|
|
9
|
-
private currentOperation: IFormatOperation | null = null;
|
|
10
|
-
private changeCache: ChangeCache;
|
|
11
5
|
private formatStats: FormatStats;
|
|
12
|
-
|
|
6
|
+
|
|
13
7
|
constructor() {
|
|
14
|
-
this.rollbackManager = new RollbackManager();
|
|
15
|
-
this.changeCache = new ChangeCache();
|
|
16
8
|
this.formatStats = new FormatStats();
|
|
17
9
|
}
|
|
18
|
-
|
|
19
|
-
async beginOperation(): Promise<void> {
|
|
20
|
-
this.currentOperation = await this.rollbackManager.createOperation();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async trackFileChange(filepath: string): Promise<void> {
|
|
24
|
-
if (!this.currentOperation) {
|
|
25
|
-
throw new Error('No operation in progress. Call beginOperation() first.');
|
|
26
|
-
}
|
|
27
|
-
await this.rollbackManager.backupFile(filepath, this.currentOperation.id);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async commitOperation(): Promise<void> {
|
|
31
|
-
if (!this.currentOperation) {
|
|
32
|
-
throw new Error('No operation in progress. Call beginOperation() first.');
|
|
33
|
-
}
|
|
34
|
-
await this.rollbackManager.markComplete(this.currentOperation.id);
|
|
35
|
-
this.currentOperation = null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async rollbackOperation(): Promise<void> {
|
|
39
|
-
if (!this.currentOperation) {
|
|
40
|
-
throw new Error('No operation in progress. Call beginOperation() first.');
|
|
41
|
-
}
|
|
42
|
-
await this.rollbackManager.rollback(this.currentOperation.id);
|
|
43
|
-
this.currentOperation = null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async rollbackTo(operationId: string): Promise<void> {
|
|
47
|
-
await this.rollbackManager.rollback(operationId);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
getRollbackManager(): RollbackManager {
|
|
51
|
-
return this.rollbackManager;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
getChangeCache(): ChangeCache {
|
|
55
|
-
return this.changeCache;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async initializeCache(): Promise<void> {
|
|
59
|
-
await this.changeCache.initialize();
|
|
60
|
-
}
|
|
61
|
-
|
|
10
|
+
|
|
62
11
|
getFormatStats(): FormatStats {
|
|
63
12
|
return this.formatStats;
|
|
64
13
|
}
|
|
65
|
-
}
|
|
14
|
+
}
|