@hanseltime/template-repo-sync 2.1.1 → 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,17 @@
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
+
8
+ ## [2.1.2](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v2.1.1...v2.1.2) (2026-02-08)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * afterref was skipping template ignores ([270b926](https://github.com/HanseltimeIndustries/template-repo-sync/commit/270b926983ddc7070c2099cfa8fa507d1b527233))
14
+
1
15
  ## [2.1.1](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v2.1.0...v2.1.1) (2026-02-08)
2
16
 
3
17
 
@@ -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";
@@ -34,6 +34,7 @@ const ref_drivers_1 = require("./ref-drivers");
34
34
  const formatting_1 = require("./formatting");
35
35
  const commentJSON = __importStar(require("comment-json"));
36
36
  const checkout_drivers_1 = require("./checkout-drivers");
37
+ const micromatch_1 = require("micromatch");
37
38
  exports.TEMPLATE_SYNC_CONFIG = "templatesync";
38
39
  exports.TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
39
40
  async function templateSync(options) {
@@ -78,7 +79,11 @@ async function templateSync(options) {
78
79
  modified: [],
79
80
  };
80
81
  }
81
- const localSkipFiles = [];
82
+ // Apply ignore filters
83
+ filesToSync.added = filesToSync.added.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
84
+ filesToSync.modified = filesToSync.modified.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
85
+ filesToSync.deleted = filesToSync.deleted.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
86
+ const localSkipFiles = new Set();
82
87
  const localFileChanges = {};
83
88
  const fileSyncFactory = (op) => {
84
89
  return async (f) => {
@@ -90,7 +95,7 @@ async function templateSync(options) {
90
95
  fileOperation: op,
91
96
  });
92
97
  if (result.ignoredDueToLocal) {
93
- localSkipFiles.push(f);
98
+ localSkipFiles.add(f);
94
99
  }
95
100
  else if (result?.localChanges && result.localChanges.length > 0) {
96
101
  localFileChanges[f] = result.localChanges;
@@ -101,6 +106,16 @@ async function templateSync(options) {
101
106
  await Promise.all(filesToSync.added.map(fileSyncFactory("added")));
102
107
  await Promise.all(filesToSync.modified.map(fileSyncFactory("modified")));
103
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
+ };
104
119
  // apply after ref
105
120
  if (options.updateAfterRef) {
106
121
  const ref = await currentRefDriver({
@@ -117,8 +132,9 @@ async function templateSync(options) {
117
132
  }
118
133
  }
119
134
  return {
120
- localSkipFiles,
135
+ localSkipFiles: Array.from(localSkipFiles),
121
136
  localFileChanges,
137
+ modifiedFiles: modifiedFiles,
122
138
  };
123
139
  }
124
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;
@@ -34,6 +34,7 @@ const ref_drivers_1 = require("./ref-drivers");
34
34
  const formatting_1 = require("./formatting");
35
35
  const commentJSON = __importStar(require("comment-json"));
36
36
  const checkout_drivers_1 = require("./checkout-drivers");
37
+ const micromatch_1 = require("micromatch");
37
38
  exports.TEMPLATE_SYNC_CONFIG = "templatesync";
38
39
  exports.TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
39
40
  async function templateSync(options) {
@@ -78,7 +79,11 @@ async function templateSync(options) {
78
79
  modified: [],
79
80
  };
80
81
  }
81
- const localSkipFiles = [];
82
+ // Apply ignore filters
83
+ filesToSync.added = filesToSync.added.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
84
+ filesToSync.modified = filesToSync.modified.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
85
+ filesToSync.deleted = filesToSync.deleted.filter((f) => !(0, micromatch_1.some)(f, templateSyncConfig.ignore));
86
+ const localSkipFiles = new Set();
82
87
  const localFileChanges = {};
83
88
  const fileSyncFactory = (op) => {
84
89
  return async (f) => {
@@ -90,7 +95,7 @@ async function templateSync(options) {
90
95
  fileOperation: op,
91
96
  });
92
97
  if (result.ignoredDueToLocal) {
93
- localSkipFiles.push(f);
98
+ localSkipFiles.add(f);
94
99
  }
95
100
  else if (result?.localChanges && result.localChanges.length > 0) {
96
101
  localFileChanges[f] = result.localChanges;
@@ -101,6 +106,16 @@ async function templateSync(options) {
101
106
  await Promise.all(filesToSync.added.map(fileSyncFactory("added")));
102
107
  await Promise.all(filesToSync.modified.map(fileSyncFactory("modified")));
103
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
+ };
104
119
  // apply after ref
105
120
  if (options.updateAfterRef) {
106
121
  const ref = await currentRefDriver({
@@ -117,8 +132,9 @@ async function templateSync(options) {
117
132
  }
118
133
  }
119
134
  return {
120
- localSkipFiles,
135
+ localSkipFiles: Array.from(localSkipFiles),
121
136
  localFileChanges,
137
+ modifiedFiles: modifiedFiles,
122
138
  };
123
139
  }
124
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.1",
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");
@@ -256,7 +292,7 @@ describe("templateSync", () => {
256
292
  // We will only update the templated.ts
257
293
  const mockDiffDriver = jest.fn().mockImplementation(async () => ({
258
294
  added: ["src/templated.ts"],
259
- modified: [],
295
+ modified: ["src/index.ts"], // Add index.ts so we make sure it is still ignored - due to a bug
260
296
  deleted: [],
261
297
  }));
262
298
  const mockCurrentRefDriver = jest
@@ -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
@@ -12,6 +12,7 @@ import { TemplateRefDriverFn } from "./ref-drivers/types";
12
12
  import { inferJSONIndent } from "./formatting";
13
13
  import * as commentJSON from "comment-json";
14
14
  import { TemplateCheckoutDriverFn, gitCheckout } from "./checkout-drivers";
15
+ import { some } from "micromatch";
15
16
 
16
17
  export interface TemplateSyncOptions {
17
18
  /**
@@ -76,6 +77,16 @@ export interface TemplateSyncReturn {
76
77
  localFileChanges: {
77
78
  [filePath: string]: Change[];
78
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
+ };
79
90
  }
80
91
 
81
92
  export const TEMPLATE_SYNC_CONFIG = "templatesync";
@@ -141,7 +152,18 @@ export async function templateSync(
141
152
  };
142
153
  }
143
154
 
144
- const localSkipFiles: string[] = [];
155
+ // Apply ignore filters
156
+ filesToSync.added = filesToSync.added.filter(
157
+ (f) => !some(f, templateSyncConfig.ignore),
158
+ );
159
+ filesToSync.modified = filesToSync.modified.filter(
160
+ (f) => !some(f, templateSyncConfig.ignore),
161
+ );
162
+ filesToSync.deleted = filesToSync.deleted.filter(
163
+ (f) => !some(f, templateSyncConfig.ignore),
164
+ );
165
+
166
+ const localSkipFiles: Set<string> = new Set();
145
167
  const localFileChanges: {
146
168
  [filePath: string]: Change[];
147
169
  } = {};
@@ -156,7 +178,7 @@ export async function templateSync(
156
178
  fileOperation: op,
157
179
  });
158
180
  if (result.ignoredDueToLocal) {
159
- localSkipFiles.push(f);
181
+ localSkipFiles.add(f);
160
182
  } else if (result?.localChanges && result.localChanges.length > 0) {
161
183
  localFileChanges[f] = result.localChanges;
162
184
  }
@@ -168,6 +190,21 @@ export async function templateSync(
168
190
  await Promise.all(filesToSync.modified.map(fileSyncFactory("modified")));
169
191
  await Promise.all(filesToSync.deleted.map(fileSyncFactory("deleted")));
170
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
+
171
208
  // apply after ref
172
209
  if (options.updateAfterRef) {
173
210
  const ref = await currentRefDriver({
@@ -191,7 +228,8 @@ export async function templateSync(
191
228
  }
192
229
 
193
230
  return {
194
- localSkipFiles,
231
+ localSkipFiles: Array.from(localSkipFiles),
195
232
  localFileChanges,
233
+ modifiedFiles: modifiedFiles,
196
234
  };
197
235
  }
@@ -1,2 +1,2 @@
1
1
  "use strict";
2
- console.log("this is templated stuff that should not change");
2
+ console.log("this is downstream stuff that should not change");
@@ -1 +1 @@
1
- console.log("this is templated stuff that should not change");
1
+ console.log("this is downstream stuff that should not change");