@hanseltime/template-repo-sync 2.0.2 → 2.1.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.
@@ -3,22 +3,30 @@ import { mergeFile } from "./merge-file";
3
3
  import { tempDir, TEST_FIXTURES_DIR } from "./test-utils";
4
4
  import { mkdtemp, readFile, rm } from "fs/promises";
5
5
  import { copySync } from "fs-extra";
6
- import { JsonFileMergeOptions } from "./types";
6
+ import { FileOperation, JsonFileMergeOptions } from "./types";
7
+ import { existsSync } from "fs";
7
8
 
8
9
  const testTemplateDir = resolve(TEST_FIXTURES_DIR, "template");
9
10
  const testDownstreamDir = resolve(TEST_FIXTURES_DIR, "downstream");
10
11
 
11
12
  describe("mergeFile", () => {
12
13
  let tmpDir: string;
14
+ let tmpTargetDir: string;
13
15
  beforeEach(async () => {
14
16
  tmpDir = await mkdtemp(tempDir());
15
17
  copySync(testDownstreamDir, tmpDir);
18
+ tmpTargetDir = await mkdtemp(tempDir());
19
+ copySync(testTemplateDir, tmpTargetDir);
16
20
  });
17
21
  afterEach(async () => {
18
22
  await rm(tmpDir, {
19
23
  force: true,
20
24
  recursive: true,
21
25
  });
26
+ await rm(tmpTargetDir, {
27
+ force: true,
28
+ recursive: true,
29
+ });
22
30
  });
23
31
  // Note: we use the "ignore" from the templateSync to constrain files we iterate over so it doeesn't happen here
24
32
  // it('skips the file if it is part of template config ignroe', async () => {
@@ -35,69 +43,65 @@ describe("mergeFile", () => {
35
43
  // }
36
44
  // })).toBe(false)
37
45
  // })
38
- it("skips the file if it is part of local config ignore", async () => {
39
- expect(
40
- await mergeFile("package.json", {
41
- cwd: tmpDir,
42
- tempCloneDir: testTemplateDir,
43
- localTemplateSyncConfig: {
44
- ignore: ["**/package.json"],
45
- merge: [],
46
- },
47
- templateSyncConfig: {
48
- ignore: ["**/*.txt"],
49
- },
50
- }),
51
- ).toEqual({
52
- ignoredDueToLocal: true,
53
- });
54
- });
55
- it("overwrites if no merge config for files", async () => {
56
- expect(
57
- await mergeFile("package.json", {
58
- cwd: tmpDir,
59
- tempCloneDir: testTemplateDir,
60
- localTemplateSyncConfig: {
61
- ignore: [],
62
- },
63
- templateSyncConfig: {
64
- ignore: ["**/*.txt"],
65
- },
66
- }),
67
- ).toEqual({
68
- ignoredDueToLocal: false,
69
- localChanges: [],
70
- });
46
+ it.each([["added"], ["deleted"], ["modified"]])(
47
+ "skips the [%s] file if it is part of local config ignore",
48
+ async (op) => {
49
+ expect(
50
+ await mergeFile("package.json", {
51
+ cwd: tmpDir,
52
+ tempCloneDir: tmpTargetDir,
53
+ localTemplateSyncConfig: {
54
+ ignore: ["**/package.json"],
55
+ merge: [],
56
+ },
57
+ templateSyncConfig: {
58
+ ignore: ["**/*.txt"],
59
+ },
60
+ fileOperation: op as FileOperation,
61
+ }),
62
+ ).toEqual({
63
+ ignoredDueToLocal: true,
64
+ });
65
+ },
66
+ );
67
+ it.each([["added"], ["modified"]])(
68
+ "overwrites if no merge config for [%s] files",
69
+ async (op) => {
70
+ expect(
71
+ await mergeFile("package.json", {
72
+ cwd: tmpDir,
73
+ tempCloneDir: tmpTargetDir,
74
+ localTemplateSyncConfig: {
75
+ ignore: [],
76
+ },
77
+ templateSyncConfig: {
78
+ ignore: ["**/*.txt"],
79
+ },
80
+ fileOperation: op as FileOperation,
81
+ }),
82
+ ).toEqual({
83
+ ignoredDueToLocal: false,
84
+ localChanges: [],
85
+ });
71
86
 
72
- // Ensure we overwrote
73
- expect(await readFile(join(tmpDir, "package.json"))).toEqual(
74
- await readFile(join(testTemplateDir, "package.json")),
75
- );
76
- });
77
- // Yea... these are more integration tests but I'm kinda untrusting of mocks for this
78
- it("applies default [.json] merge with first rules if merge applies to file", async () => {
87
+ // Ensure we overwrote
88
+ expect(await readFile(join(tmpDir, "package.json"))).toEqual(
89
+ await readFile(join(tmpTargetDir, "package.json")),
90
+ );
91
+ },
92
+ );
93
+ it("removes the template deleted files", async () => {
79
94
  expect(
80
95
  await mergeFile("package.json", {
81
96
  cwd: tmpDir,
82
- tempCloneDir: testTemplateDir,
97
+ tempCloneDir: tmpTargetDir,
83
98
  localTemplateSyncConfig: {
84
99
  ignore: [],
85
100
  },
86
101
  templateSyncConfig: {
87
102
  ignore: ["**/*.txt"],
88
- merge: [
89
- {
90
- glob: "**/package.json",
91
- options: "merge-current",
92
- plugin: "_json",
93
- },
94
- {
95
- glob: "**/package.json",
96
- options: "merge-template",
97
- plugin: "_json",
98
- },
99
- ],
100
103
  },
104
+ fileOperation: "deleted", // This is normally inferred by the top-level code
101
105
  }),
102
106
  ).toEqual({
103
107
  ignoredDueToLocal: false,
@@ -105,85 +109,15 @@ describe("mergeFile", () => {
105
109
  });
106
110
 
107
111
  // Ensure we overwrote
108
- expect(
109
- JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
110
- ).toEqual({
111
- name: "mypkg",
112
- description: "my description",
113
- dependencies: {
114
- mypackage: "^1.2.0",
115
- newpacakge: "^22.2.2",
116
- package2: "3.22.1",
117
- huh: "^2.30.0",
118
- },
119
- engines: {
120
- node: ">=20",
121
- },
122
- scripts: {
123
- build: "build",
124
- test: "jest",
125
- myscript: "somescript",
126
- },
127
- version: "new-version",
128
- });
112
+ expect(existsSync(join(tmpTargetDir, "package.json"))).toBeFalsy();
129
113
  });
130
- it("[inverse] applies default [.json] merge with first rules if merge applies to file", async () => {
131
- expect(
132
- await mergeFile("package.json", {
133
- cwd: tmpDir,
134
- tempCloneDir: testTemplateDir,
135
- localTemplateSyncConfig: {
136
- ignore: [],
137
- },
138
- templateSyncConfig: {
139
- ignore: ["**/*.txt"],
140
- merge: [
141
- {
142
- glob: "**/package.json",
143
- options: "merge-template",
144
- plugin: "_json",
145
- },
146
- {
147
- glob: "**/package.json",
148
- options: "merge-current",
149
- plugin: "_json",
150
- },
151
- ],
152
- },
153
- }),
154
- ).toEqual({
155
- ignoredDueToLocal: false,
156
- localChanges: [],
157
- });
158
114
 
159
- // Ensure we overwrote
160
- expect(
161
- JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
162
- ).toEqual({
163
- name: "some-stub-name",
164
- description: "some-stub-description",
165
- dependencies: {
166
- mypackage: "^1.2.0",
167
- newpacakge: "^22.2.2",
168
- package2: "3.22.1",
169
- huh: "~1.0.0",
170
- },
171
- engines: {
172
- node: ">=15",
173
- },
174
- scripts: {
175
- build: "build",
176
- test: "fill this in yourself",
177
- myscript: "somescript",
178
- },
179
- version: "new-version",
180
- });
181
- });
182
- it("[inverse] applies default [.json] merge with sync and then local override", async () => {
115
+ // TODO - this could change if there's a use case
116
+ it("does not apply plugins on deleted", async () => {
183
117
  expect(
184
118
  await mergeFile("package.json", {
185
119
  cwd: tmpDir,
186
- tempCloneDir: testTemplateDir,
120
+ tempCloneDir: tmpTargetDir,
187
121
  localTemplateSyncConfig: {
188
122
  ignore: [],
189
123
  merge: [
@@ -215,94 +149,253 @@ describe("mergeFile", () => {
215
149
  },
216
150
  ],
217
151
  },
152
+ fileOperation: "deleted", // This is normally inferred by the top-level code
218
153
  }),
219
154
  ).toEqual({
220
155
  ignoredDueToLocal: false,
221
- localChanges: expect.arrayContaining([
222
- {
223
- added: undefined,
224
- count: 1,
225
- removed: true,
226
- value: ' "huh": "~1.0.0"\n',
227
- },
228
- {
229
- added: true,
230
- count: 1,
231
- removed: undefined,
232
- value: ' "huh": "^2.30.0"\n',
233
- },
234
- ]),
156
+ localChanges: [],
235
157
  });
236
158
 
237
159
  // Ensure we overwrote
238
- expect(
239
- JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
240
- ).toEqual({
241
- name: "mypkg",
242
- description: "my description",
243
- dependencies: {
244
- mypackage: "^1.2.0",
245
- newpacakge: "^22.2.2",
246
- package2: "3.22.1",
247
- huh: "^2.30.0",
248
- },
249
- engines: {
250
- node: ">=20",
251
- },
252
- scripts: {
253
- build: "build",
254
- test: "jest",
255
- myscript: "somescript",
256
- },
257
- version: "new-version",
258
- });
160
+ expect(existsSync(join(tmpTargetDir, "package.json"))).toBeFalsy();
259
161
  });
260
- it("[inverse] applies default [.json] and custom merge for local with sync and then local override", async () => {
261
- expect(
262
- await mergeFile("package.json", {
263
- cwd: tmpDir,
264
- tempCloneDir: testTemplateDir,
265
- localTemplateSyncConfig: {
266
- ignore: [],
267
- merge: [
162
+
163
+ describe.each([["added" as FileOperation], ["modified" as FileOperation]])(
164
+ "[%s] file",
165
+ (op: FileOperation) => {
166
+ // Yea... these are more integration tests but I'm kinda untrusting of mocks for this
167
+ it("applies default [.json] merge with first rules if merge applies to file", async () => {
168
+ expect(
169
+ await mergeFile("package.json", {
170
+ cwd: tmpDir,
171
+ tempCloneDir: testTemplateDir,
172
+ localTemplateSyncConfig: {
173
+ ignore: [],
174
+ },
175
+ fileOperation: op,
176
+ templateSyncConfig: {
177
+ ignore: ["**/*.txt"],
178
+ merge: [
179
+ {
180
+ glob: "**/package.json",
181
+ options: "merge-current",
182
+ plugin: "_json",
183
+ },
184
+ {
185
+ glob: "**/package.json",
186
+ options: "merge-template",
187
+ plugin: "_json",
188
+ },
189
+ ],
190
+ },
191
+ }),
192
+ ).toEqual({
193
+ ignoredDueToLocal: false,
194
+ localChanges: [],
195
+ });
196
+
197
+ // Ensure we overwrote
198
+ expect(
199
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
200
+ ).toEqual({
201
+ name: "mypkg",
202
+ description: "my description",
203
+ dependencies: {
204
+ mypackage: "^1.2.0",
205
+ newpacakge: "^22.2.2",
206
+ package2: "3.22.1",
207
+ huh: "^2.30.0",
208
+ },
209
+ engines: {
210
+ node: ">=20",
211
+ },
212
+ scripts: {
213
+ build: "build",
214
+ test: "jest",
215
+ myscript: "somescript",
216
+ },
217
+ version: "new-version",
218
+ });
219
+ });
220
+ it("[inverse] applies default [.json] merge with first rules if merge applies to file", async () => {
221
+ expect(
222
+ await mergeFile("package.json", {
223
+ cwd: tmpDir,
224
+ tempCloneDir: testTemplateDir,
225
+ localTemplateSyncConfig: {
226
+ ignore: [],
227
+ },
228
+ fileOperation: op,
229
+ templateSyncConfig: {
230
+ ignore: ["**/*.txt"],
231
+ merge: [
232
+ {
233
+ glob: "**/package.json",
234
+ options: "merge-template",
235
+ plugin: "_json",
236
+ },
237
+ {
238
+ glob: "**/package.json",
239
+ options: "merge-current",
240
+ plugin: "_json",
241
+ },
242
+ ],
243
+ },
244
+ }),
245
+ ).toEqual({
246
+ ignoredDueToLocal: false,
247
+ localChanges: [],
248
+ });
249
+
250
+ // Ensure we overwrote
251
+ expect(
252
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
253
+ ).toEqual({
254
+ name: "some-stub-name",
255
+ description: "some-stub-description",
256
+ dependencies: {
257
+ mypackage: "^1.2.0",
258
+ newpacakge: "^22.2.2",
259
+ package2: "3.22.1",
260
+ huh: "~1.0.0",
261
+ },
262
+ engines: {
263
+ node: ">=15",
264
+ },
265
+ scripts: {
266
+ build: "build",
267
+ test: "fill this in yourself",
268
+ myscript: "somescript",
269
+ },
270
+ version: "new-version",
271
+ });
272
+ });
273
+ it("[inverse] applies default [.json] merge with sync and then local override", async () => {
274
+ expect(
275
+ await mergeFile("package.json", {
276
+ cwd: tmpDir,
277
+ tempCloneDir: testTemplateDir,
278
+ fileOperation: op,
279
+ localTemplateSyncConfig: {
280
+ ignore: [],
281
+ merge: [
282
+ {
283
+ plugin: "_json",
284
+ glob: "**/package.json",
285
+ options: {
286
+ paths: [
287
+ // Do not touch huh
288
+ ["$.dependencies.huh", "merge-current"],
289
+ ],
290
+ },
291
+ },
292
+ ],
293
+ },
294
+ templateSyncConfig: {
295
+ ignore: ["**/*.txt"],
296
+ merge: [
297
+ {
298
+ glob: "**/package.json",
299
+ plugin: "_json",
300
+ options: {
301
+ missingIsDelete: true,
302
+ paths: [
303
+ // Merge all template dependencies
304
+ ["$.dependencies", "merge-template"],
305
+ ],
306
+ } as JsonFileMergeOptions,
307
+ },
308
+ ],
309
+ },
310
+ }),
311
+ ).toEqual({
312
+ ignoredDueToLocal: false,
313
+ localChanges: expect.arrayContaining([
268
314
  {
269
- plugin: "../test-fixtures/dummy-plugin.js",
270
- options: {
271
- paths: [
272
- // Do not touch huh
273
- ["$.dependencies.huh", "merge-current"],
274
- ],
275
- },
276
- glob: "**/package.json",
315
+ added: undefined,
316
+ count: 1,
317
+ removed: true,
318
+ value: ' "huh": "~1.0.0"\n',
277
319
  },
278
- ],
279
- },
280
- templateSyncConfig: {
281
- ignore: ["**/*.txt"],
282
- merge: [
283
320
  {
284
- plugin: "_json",
285
- glob: "**/package.json",
286
- options: {
287
- missingIsDelete: true,
288
- paths: [
289
- // Merge all template dependencies
290
- ["$.dependencies", "merge-template"],
291
- ],
292
- } as JsonFileMergeOptions,
321
+ added: true,
322
+ count: 1,
323
+ removed: undefined,
324
+ value: ' "huh": "^2.30.0"\n',
293
325
  },
294
- ],
295
- },
296
- }),
297
- ).toEqual({
298
- ignoredDueToLocal: false,
299
- // TODO: I don't
300
- localChanges: expect.arrayContaining([
301
- {
302
- added: undefined,
303
- count: 17,
304
- removed: true,
305
- value: ` "name": "mypkg",
326
+ ]),
327
+ });
328
+
329
+ // Ensure we overwrote
330
+ expect(
331
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
332
+ ).toEqual({
333
+ name: "mypkg",
334
+ description: "my description",
335
+ dependencies: {
336
+ mypackage: "^1.2.0",
337
+ newpacakge: "^22.2.2",
338
+ package2: "3.22.1",
339
+ huh: "^2.30.0",
340
+ },
341
+ engines: {
342
+ node: ">=20",
343
+ },
344
+ scripts: {
345
+ build: "build",
346
+ test: "jest",
347
+ myscript: "somescript",
348
+ },
349
+ version: "new-version",
350
+ });
351
+ });
352
+ it("[inverse] applies default [.json] and custom merge for local with sync and then local override", async () => {
353
+ expect(
354
+ await mergeFile("package.json", {
355
+ cwd: tmpDir,
356
+ tempCloneDir: testTemplateDir,
357
+ fileOperation: op,
358
+ localTemplateSyncConfig: {
359
+ ignore: [],
360
+ merge: [
361
+ {
362
+ plugin: "../test-fixtures/dummy-plugin.js",
363
+ options: {
364
+ paths: [
365
+ // Do not touch huh
366
+ ["$.dependencies.huh", "merge-current"],
367
+ ],
368
+ },
369
+ glob: "**/package.json",
370
+ },
371
+ ],
372
+ },
373
+ templateSyncConfig: {
374
+ ignore: ["**/*.txt"],
375
+ merge: [
376
+ {
377
+ plugin: "_json",
378
+ glob: "**/package.json",
379
+ options: {
380
+ missingIsDelete: true,
381
+ paths: [
382
+ // Merge all template dependencies
383
+ ["$.dependencies", "merge-template"],
384
+ ],
385
+ } as JsonFileMergeOptions,
386
+ },
387
+ ],
388
+ },
389
+ }),
390
+ ).toEqual({
391
+ ignoredDueToLocal: false,
392
+ // TODO: I don't
393
+ localChanges: expect.arrayContaining([
394
+ {
395
+ added: undefined,
396
+ count: 17,
397
+ removed: true,
398
+ value: ` "name": "mypkg",
306
399
  "description": "my description",
307
400
  "dependencies": {
308
401
  "mypackage": "^1.2.0",
@@ -319,92 +412,95 @@ describe("mergeFile", () => {
319
412
  "myscript": "somescript"
320
413
  },
321
414
  "version": "new-version"\n`,
322
- },
323
- {
324
- added: true,
325
- count: 1,
326
- removed: undefined,
327
- value: ` "tested": true\n`,
328
- },
329
- ]),
330
- });
331
-
332
- // Ensure we overwrote
333
- expect(
334
- JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
335
- ).toEqual({
336
- tested: true,
337
- });
338
- });
339
- it("[inverse] applies builtin _json and custom merge for local with sync plugin and then local override", async () => {
340
- expect(
341
- await mergeFile("package.json", {
342
- cwd: tmpDir,
343
- tempCloneDir: testTemplateDir,
344
- localTemplateSyncConfig: {
345
- ignore: [],
346
- merge: [
347
- {
348
- glob: "**/package.json",
349
- plugin: "plugins/custom-plugin.js",
350
- options: {
351
- paths: [
352
- // Do not touch huh
353
- ["$.dependencies.huh", "merge-current"],
354
- ],
355
- },
356
415
  },
357
- ],
358
- },
359
- templateSyncConfig: {
360
- ignore: ["**/*.txt"],
361
- merge: [
362
416
  {
363
- glob: "**/package.json",
364
- // TODO - we need to handle the weird implicit case I added here. It's dumb
365
- options: {
366
- missingIsDelete: true,
367
- paths: [
368
- // Merge all template dependencies
369
- ["$.dependencies", "merge-template"],
370
- ],
371
- } as JsonFileMergeOptions,
372
- plugin: "_json",
417
+ added: true,
418
+ count: 1,
419
+ removed: undefined,
420
+ value: ` "tested": true\n`,
373
421
  },
374
- ],
375
- // ".json": {
376
- // // no plugins
377
- // rules: [
378
- // {
379
- // glob: "**/package.json",
380
- // options: {
381
- // missingIsDelete: true,
382
- // paths: [
383
- // // Merge all template dependencies
384
- // ["$.dependencies", "merge-template"],
385
- // ],
386
- // } as JsonFileMergeOptions,
387
- // },
388
- // {
389
- // glob: "**/package.json",
390
- // options: "merge-current",
391
- // },
392
- // ],
393
- // },
394
- // },
395
- },
396
- }),
397
- ).toEqual({
398
- ignoredDueToLocal: false,
399
- // We are just making sure plugin look up happens here
400
- localChanges: expect.arrayContaining([]),
401
- });
422
+ ]),
423
+ });
402
424
 
403
- // Ensure we overwrote
404
- expect(
405
- JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
406
- ).toEqual({
407
- downstream: true,
408
- });
409
- });
425
+ // Ensure we overwrote
426
+ expect(
427
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
428
+ ).toEqual({
429
+ tested: true,
430
+ });
431
+ });
432
+ it("[inverse] applies builtin _json and custom merge for local with sync plugin and then local override", async () => {
433
+ expect(
434
+ await mergeFile("package.json", {
435
+ cwd: tmpDir,
436
+ tempCloneDir: testTemplateDir,
437
+ fileOperation: op,
438
+ localTemplateSyncConfig: {
439
+ ignore: [],
440
+ merge: [
441
+ {
442
+ glob: "**/package.json",
443
+ plugin: "plugins/custom-plugin.js",
444
+ options: {
445
+ paths: [
446
+ // Do not touch huh
447
+ ["$.dependencies.huh", "merge-current"],
448
+ ],
449
+ },
450
+ },
451
+ ],
452
+ },
453
+ templateSyncConfig: {
454
+ ignore: ["**/*.txt"],
455
+ merge: [
456
+ {
457
+ glob: "**/package.json",
458
+ // TODO - we need to handle the weird implicit case I added here. It's dumb
459
+ options: {
460
+ missingIsDelete: true,
461
+ paths: [
462
+ // Merge all template dependencies
463
+ ["$.dependencies", "merge-template"],
464
+ ],
465
+ } as JsonFileMergeOptions,
466
+ plugin: "_json",
467
+ },
468
+ ],
469
+ // ".json": {
470
+ // // no plugins
471
+ // rules: [
472
+ // {
473
+ // glob: "**/package.json",
474
+ // options: {
475
+ // missingIsDelete: true,
476
+ // paths: [
477
+ // // Merge all template dependencies
478
+ // ["$.dependencies", "merge-template"],
479
+ // ],
480
+ // } as JsonFileMergeOptions,
481
+ // },
482
+ // {
483
+ // glob: "**/package.json",
484
+ // options: "merge-current",
485
+ // },
486
+ // ],
487
+ // },
488
+ // },
489
+ },
490
+ }),
491
+ ).toEqual({
492
+ ignoredDueToLocal: false,
493
+ // We are just making sure plugin look up happens here
494
+ localChanges: expect.arrayContaining([]),
495
+ });
496
+
497
+ // Ensure we overwrote
498
+ expect(
499
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
500
+ ).toEqual({
501
+ downstream: true,
502
+ });
503
+ });
504
+ },
505
+ );
410
506
  });