@hanseltime/template-repo-sync 2.1.2 → 2.2.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [2.2.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v2.1.2...v2.2.0) (2026-02-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * add modified files output to track actual sync changes ([050ffd0](https://github.com/HanseltimeIndustries/template-repo-sync/commit/050ffd0ff74195943e60fc8dbf4354191959c0be))
7
+
1
8
  ## [2.1.2](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v2.1.1...v2.1.2) (2026-02-08)
2
9
 
3
10
 
@@ -21,6 +21,23 @@ ${result.localFileChanges[file].reduce((diffS, change) => {
21
21
  }, "")}
22
22
  \`\`\``;
23
23
  }, "")}
24
+
25
+ ## Files Modified (${result.modifiedFiles.total})
26
+
27
+ Added:
28
+ ${result.modifiedFiles.added.reduce((s, f) => {
29
+ return `${s}\n- ${f}`;
30
+ }, "")}
31
+
32
+ Modified:
33
+ ${result.modifiedFiles.modified.reduce((s, f) => {
34
+ return `${s}\n- ${f}`;
35
+ }, "")}
36
+
37
+ Deleted:
38
+ ${result.modifiedFiles.deleted.reduce((s, f) => {
39
+ return `${s}\n- ${f}`;
40
+ }, "")}
24
41
  `;
25
42
  }
26
43
  exports.syncResultsToMd = syncResultsToMd;
@@ -14,7 +14,8 @@ interface MergeFileReturn {
14
14
  ignoredDueToLocal: boolean;
15
15
  /**
16
16
  * Only available if the file wasn't ignored, this is a list of lineDiffs
17
- * from the the diff library that were applied to what would've been removed
17
+ * from the diff library that were applied to what would've been changed by
18
+ * the base templatesync repo
18
19
  */
19
20
  localChanges?: Change[];
20
21
  }
@@ -1,6 +1,6 @@
1
1
  import { Change } from "diff";
2
2
  import { TemplateCloneDriverFn } from "./clone-drivers";
3
- import { TemplateDiffDriverFn } from "./diff-drivers";
3
+ import { DiffResult, TemplateDiffDriverFn } from "./diff-drivers";
4
4
  import { TemplateRefDriverFn } from "./ref-drivers/types";
5
5
  import { TemplateCheckoutDriverFn } from "./checkout-drivers";
6
6
  export interface TemplateSyncOptions {
@@ -57,6 +57,16 @@ export interface TemplateSyncReturn {
57
57
  localFileChanges: {
58
58
  [filePath: string]: Change[];
59
59
  };
60
+ /**
61
+ * A list of all files that are modified by this operation. You can use total to quickly check if
62
+ * there was an actual meaningful change.
63
+ *
64
+ * Please note, ther may be localSkipfiles and total: 0, which indicates there were changes BUT the local template
65
+ * sync file rendered them meaningless.
66
+ */
67
+ modifiedFiles: DiffResult & {
68
+ total: number;
69
+ };
60
70
  }
61
71
  export declare const TEMPLATE_SYNC_CONFIG = "templatesync";
62
72
  export declare const TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
@@ -83,7 +83,7 @@ async function templateSync(options) {
83
83
  filesToSync.added = filesToSync.added.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
84
84
  filesToSync.modified = filesToSync.modified.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
85
85
  filesToSync.deleted = filesToSync.deleted.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
86
- const localSkipFiles = [];
86
+ const localSkipFiles = new Set();
87
87
  const localFileChanges = {};
88
88
  const fileSyncFactory = (op) => {
89
89
  return async (f) => {
@@ -95,7 +95,7 @@ async function templateSync(options) {
95
95
  fileOperation: op,
96
96
  });
97
97
  if (result.ignoredDueToLocal) {
98
- localSkipFiles.push(f);
98
+ localSkipFiles.add(f);
99
99
  }
100
100
  else if (result?.localChanges && result.localChanges.length > 0) {
101
101
  localFileChanges[f] = result.localChanges;
@@ -106,6 +106,16 @@ async function templateSync(options) {
106
106
  await Promise.all(filesToSync.added.map(fileSyncFactory("added")));
107
107
  await Promise.all(filesToSync.modified.map(fileSyncFactory("modified")));
108
108
  await Promise.all(filesToSync.deleted.map(fileSyncFactory("deleted")));
109
+ // Report the files that changed in general
110
+ const actualAdded = filesToSync.added.filter((f) => !localSkipFiles.has(f));
111
+ const actualModified = filesToSync.modified.filter((f) => !localSkipFiles.has(f));
112
+ const actualDeleted = filesToSync.deleted.filter((f) => !localSkipFiles.has(f));
113
+ const modifiedFiles = {
114
+ added: actualAdded,
115
+ modified: actualModified,
116
+ deleted: actualDeleted,
117
+ total: actualAdded.length + actualModified.length + actualDeleted.length,
118
+ };
109
119
  // apply after ref
110
120
  if (options.updateAfterRef) {
111
121
  const ref = await currentRefDriver({
@@ -122,8 +132,9 @@ async function templateSync(options) {
122
132
  }
123
133
  }
124
134
  return {
125
- localSkipFiles,
135
+ localSkipFiles: Array.from(localSkipFiles),
126
136
  localFileChanges,
137
+ modifiedFiles: modifiedFiles,
127
138
  };
128
139
  }
129
140
  exports.templateSync = templateSync;
@@ -21,6 +21,23 @@ ${result.localFileChanges[file].reduce((diffS, change) => {
21
21
  }, "")}
22
22
  \`\`\``;
23
23
  }, "")}
24
+
25
+ ## Files Modified (${result.modifiedFiles.total})
26
+
27
+ Added:
28
+ ${result.modifiedFiles.added.reduce((s, f) => {
29
+ return `${s}\n- ${f}`;
30
+ }, "")}
31
+
32
+ Modified:
33
+ ${result.modifiedFiles.modified.reduce((s, f) => {
34
+ return `${s}\n- ${f}`;
35
+ }, "")}
36
+
37
+ Deleted:
38
+ ${result.modifiedFiles.deleted.reduce((s, f) => {
39
+ return `${s}\n- ${f}`;
40
+ }, "")}
24
41
  `;
25
42
  }
26
43
  exports.syncResultsToMd = syncResultsToMd;
@@ -83,7 +83,7 @@ async function templateSync(options) {
83
83
  filesToSync.added = filesToSync.added.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
84
84
  filesToSync.modified = filesToSync.modified.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
85
85
  filesToSync.deleted = filesToSync.deleted.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
86
- const localSkipFiles = [];
86
+ const localSkipFiles = new Set();
87
87
  const localFileChanges = {};
88
88
  const fileSyncFactory = (op) => {
89
89
  return async (f) => {
@@ -95,7 +95,7 @@ async function templateSync(options) {
95
95
  fileOperation: op,
96
96
  });
97
97
  if (result.ignoredDueToLocal) {
98
- localSkipFiles.push(f);
98
+ localSkipFiles.add(f);
99
99
  }
100
100
  else if (result?.localChanges && result.localChanges.length > 0) {
101
101
  localFileChanges[f] = result.localChanges;
@@ -106,6 +106,16 @@ async function templateSync(options) {
106
106
  await Promise.all(filesToSync.added.map(fileSyncFactory("added")));
107
107
  await Promise.all(filesToSync.modified.map(fileSyncFactory("modified")));
108
108
  await Promise.all(filesToSync.deleted.map(fileSyncFactory("deleted")));
109
+ // Report the files that changed in general
110
+ const actualAdded = filesToSync.added.filter((f) => !localSkipFiles.has(f));
111
+ const actualModified = filesToSync.modified.filter((f) => !localSkipFiles.has(f));
112
+ const actualDeleted = filesToSync.deleted.filter((f) => !localSkipFiles.has(f));
113
+ const modifiedFiles = {
114
+ added: actualAdded,
115
+ modified: actualModified,
116
+ deleted: actualDeleted,
117
+ total: actualAdded.length + actualModified.length + actualDeleted.length,
118
+ };
109
119
  // apply after ref
110
120
  if (options.updateAfterRef) {
111
121
  const ref = await currentRefDriver({
@@ -122,8 +132,9 @@ async function templateSync(options) {
122
132
  }
123
133
  }
124
134
  return {
125
- localSkipFiles,
135
+ localSkipFiles: Array.from(localSkipFiles),
126
136
  localFileChanges,
137
+ modifiedFiles: modifiedFiles,
127
138
  };
128
139
  }
129
140
  exports.templateSync = templateSync;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanseltime/template-repo-sync",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "description": "An npm library that enables pluggable, customizable synchronization between template repos",
5
5
  "main": "lib/cjs/index.js",
6
6
  "types": "lib/cjs/index.d.ts",
@@ -18,5 +18,22 @@ package.json
18
18
  -your thang
19
19
 
20
20
  \`\`\`
21
+
22
+ ## Files Modified (6)
23
+
24
+ Added:
25
+
26
+ - src/something.ts
27
+ - new_settings.toml
28
+
29
+ Modified:
30
+
31
+ - something.txt
32
+ - .env
33
+
34
+ Deleted:
35
+
36
+ - TODO.md
37
+ - throwaway.ts
21
38
  "
22
39
  `;
@@ -19,6 +19,12 @@ describe("syncResultsToMd", () => {
19
19
  },
20
20
  ],
21
21
  },
22
+ modifiedFiles: {
23
+ added: ["src/something.ts", "new_settings.toml"],
24
+ deleted: ["TODO.md", "throwaway.ts"],
25
+ modified: ["something.txt", ".env"],
26
+ total: 6,
27
+ },
22
28
  }),
23
29
  ).toMatchSnapshot();
24
30
  });
@@ -23,6 +23,23 @@ ${result.localFileChanges[file].reduce((diffS, change) => {
23
23
  }, "")}
24
24
  \`\`\``;
25
25
  }, "")}
26
+
27
+ ## Files Modified (${result.modifiedFiles.total})
28
+
29
+ Added:
30
+ ${result.modifiedFiles.added.reduce((s, f) => {
31
+ return `${s}\n- ${f}`;
32
+ }, "")}
33
+
34
+ Modified:
35
+ ${result.modifiedFiles.modified.reduce((s, f) => {
36
+ return `${s}\n- ${f}`;
37
+ }, "")}
38
+
39
+ Deleted:
40
+ ${result.modifiedFiles.deleted.reduce((s, f) => {
41
+ return `${s}\n- ${f}`;
42
+ }, "")}
26
43
  `;
27
44
  }
28
45
 
package/src/merge-file.ts CHANGED
@@ -22,7 +22,8 @@ interface MergeFileReturn {
22
22
  ignoredDueToLocal: boolean;
23
23
  /**
24
24
  * Only available if the file wasn't ignored, this is a list of lineDiffs
25
- * from the the diff library that were applied to what would've been removed
25
+ * from the diff library that were applied to what would've been changed by
26
+ * the base templatesync repo
26
27
  */
27
28
  localChanges?: Change[];
28
29
  }
@@ -44,6 +44,18 @@ describe("templateSync", () => {
44
44
  // Expect no changes since there was no local sync file
45
45
  localSkipFiles: [],
46
46
  localFileChanges: {},
47
+ modifiedFiles: {
48
+ added: [
49
+ "package.json",
50
+ "src/index.js",
51
+ "src/templated.js",
52
+ "src/templated.ts",
53
+ "templatesync.json",
54
+ ],
55
+ deleted: [],
56
+ modified: [],
57
+ total: 5,
58
+ },
47
59
  });
48
60
 
49
61
  // Verify the files
@@ -72,6 +84,18 @@ describe("templateSync", () => {
72
84
  // Expect no changes since there was no local sync file
73
85
  localSkipFiles: [],
74
86
  localFileChanges: {},
87
+ modifiedFiles: {
88
+ added: [
89
+ "package.json",
90
+ "src/index.js",
91
+ "src/templated.js",
92
+ "src/templated.ts",
93
+ "templatesync.json",
94
+ ],
95
+ deleted: [],
96
+ modified: [],
97
+ total: 5,
98
+ },
75
99
  });
76
100
 
77
101
  // Verify the files
@@ -175,6 +199,18 @@ describe("templateSync", () => {
175
199
  "package.json": expect.arrayContaining([]),
176
200
  }),
177
201
  );
202
+ // Make sure the result captures the changes
203
+ expect(result.modifiedFiles).toEqual({
204
+ added: [
205
+ "package.json",
206
+ "src/index.js",
207
+ "src/templated.js",
208
+ "templatesync.json",
209
+ ],
210
+ deleted: [],
211
+ modified: [],
212
+ total: 4,
213
+ });
178
214
 
179
215
  // Verify the files
180
216
  await fileMatchTemplate(tmpDir, "templatesync.json");
@@ -273,7 +309,7 @@ describe("templateSync", () => {
273
309
  checkoutDriver: dummyCheckoutDriver,
274
310
  });
275
311
 
276
- // since there was no override for this file, not changes from the local file
312
+ // since there was no override for this file, no changes from the local file
277
313
  expect(result.localFileChanges).toEqual(expect.objectContaining({}));
278
314
 
279
315
  // Verify the files
@@ -318,7 +354,7 @@ describe("templateSync", () => {
318
354
  checkoutDriver: dummyCheckoutDriver,
319
355
  });
320
356
 
321
- // since there was no override for this file, not changes from the local file
357
+ // since there was no override for this file, no changes from the local file
322
358
  expect(result.localFileChanges).toEqual(expect.objectContaining({}));
323
359
 
324
360
  // Verify the files
@@ -77,6 +77,16 @@ export interface TemplateSyncReturn {
77
77
  localFileChanges: {
78
78
  [filePath: string]: Change[];
79
79
  };
80
+ /**
81
+ * A list of all files that are modified by this operation. You can use total to quickly check if
82
+ * there was an actual meaningful change.
83
+ *
84
+ * Please note, ther may be localSkipfiles and total: 0, which indicates there were changes BUT the local template
85
+ * sync file rendered them meaningless.
86
+ */
87
+ modifiedFiles: DiffResult & {
88
+ total: number;
89
+ };
80
90
  }
81
91
 
82
92
  export const TEMPLATE_SYNC_CONFIG = "templatesync";
@@ -153,7 +163,7 @@ export async function templateSync(
153
163
  (f) => !some(f, templateSyncConfig.ignore),
154
164
  );
155
165
 
156
- const localSkipFiles: string[] = [];
166
+ const localSkipFiles: Set<string> = new Set();
157
167
  const localFileChanges: {
158
168
  [filePath: string]: Change[];
159
169
  } = {};
@@ -168,7 +178,7 @@ export async function templateSync(
168
178
  fileOperation: op,
169
179
  });
170
180
  if (result.ignoredDueToLocal) {
171
- localSkipFiles.push(f);
181
+ localSkipFiles.add(f);
172
182
  } else if (result?.localChanges && result.localChanges.length > 0) {
173
183
  localFileChanges[f] = result.localChanges;
174
184
  }
@@ -180,6 +190,21 @@ export async function templateSync(
180
190
  await Promise.all(filesToSync.modified.map(fileSyncFactory("modified")));
181
191
  await Promise.all(filesToSync.deleted.map(fileSyncFactory("deleted")));
182
192
 
193
+ // Report the files that changed in general
194
+ const actualAdded = filesToSync.added.filter((f) => !localSkipFiles.has(f));
195
+ const actualModified = filesToSync.modified.filter(
196
+ (f) => !localSkipFiles.has(f),
197
+ );
198
+ const actualDeleted = filesToSync.deleted.filter(
199
+ (f) => !localSkipFiles.has(f),
200
+ );
201
+ const modifiedFiles = {
202
+ added: actualAdded,
203
+ modified: actualModified,
204
+ deleted: actualDeleted,
205
+ total: actualAdded.length + actualModified.length + actualDeleted.length,
206
+ };
207
+
183
208
  // apply after ref
184
209
  if (options.updateAfterRef) {
185
210
  const ref = await currentRefDriver({
@@ -203,7 +228,8 @@ export async function templateSync(
203
228
  }
204
229
 
205
230
  return {
206
- localSkipFiles,
231
+ localSkipFiles: Array.from(localSkipFiles),
207
232
  localFileChanges,
233
+ modifiedFiles: modifiedFiles,
208
234
  };
209
235
  }