@hanseltime/template-repo-sync 1.0.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.
Files changed (119) hide show
  1. package/.eslintrc.js +10 -0
  2. package/.github/CODEOWNERS +6 -0
  3. package/.github/workflows/pr-checks.yaml +12 -0
  4. package/.github/workflows/release.yaml +36 -0
  5. package/.github/workflows/test-flow.yaml +58 -0
  6. package/.husky/commit-msg +4 -0
  7. package/.prettierignore +1 -0
  8. package/.prettierrc +1 -0
  9. package/CHANGELOG.md +27 -0
  10. package/README.md +93 -0
  11. package/action.yml +13 -0
  12. package/commitlint.config.js +3 -0
  13. package/jest.config.js +19 -0
  14. package/lib/cjs/clone-drivers/git-clone.d.ts +1 -0
  15. package/lib/cjs/clone-drivers/git-clone.js +14 -0
  16. package/lib/cjs/clone-drivers/index.d.ts +2 -0
  17. package/lib/cjs/clone-drivers/index.js +18 -0
  18. package/lib/cjs/clone-drivers/types.d.ts +5 -0
  19. package/lib/cjs/clone-drivers/types.js +2 -0
  20. package/lib/cjs/diff-drivers/git-diff.d.ts +1 -0
  21. package/lib/cjs/diff-drivers/git-diff.js +13 -0
  22. package/lib/cjs/diff-drivers/index.d.ts +2 -0
  23. package/lib/cjs/diff-drivers/index.js +18 -0
  24. package/lib/cjs/diff-drivers/types.d.ts +5 -0
  25. package/lib/cjs/diff-drivers/types.js +2 -0
  26. package/lib/cjs/formatting/index.d.ts +2 -0
  27. package/lib/cjs/formatting/index.js +18 -0
  28. package/lib/cjs/formatting/infer-json-indent.d.ts +1 -0
  29. package/lib/cjs/formatting/infer-json-indent.js +18 -0
  30. package/lib/cjs/formatting/sync-results-to-md.d.ts +2 -0
  31. package/lib/cjs/formatting/sync-results-to-md.js +40 -0
  32. package/lib/cjs/index.d.ts +3 -0
  33. package/lib/cjs/index.js +19 -0
  34. package/lib/cjs/load-plugin.d.ts +2 -0
  35. package/lib/cjs/load-plugin.js +63 -0
  36. package/lib/cjs/match.d.ts +10 -0
  37. package/lib/cjs/match.js +45 -0
  38. package/lib/cjs/merge-file.d.ts +29 -0
  39. package/lib/cjs/merge-file.js +99 -0
  40. package/lib/cjs/plugins/index.d.ts +4 -0
  41. package/lib/cjs/plugins/index.js +10 -0
  42. package/lib/cjs/plugins/json-merge.d.ts +3 -0
  43. package/lib/cjs/plugins/json-merge.js +185 -0
  44. package/lib/cjs/template-sync.d.ts +40 -0
  45. package/lib/cjs/template-sync.js +56 -0
  46. package/lib/cjs/test-utils/index.d.ts +2 -0
  47. package/lib/cjs/test-utils/index.js +10 -0
  48. package/lib/cjs/types.d.ts +113 -0
  49. package/lib/cjs/types.js +2 -0
  50. package/lib/esm/clone-drivers/git-clone.js +14 -0
  51. package/lib/esm/clone-drivers/index.js +18 -0
  52. package/lib/esm/clone-drivers/types.js +2 -0
  53. package/lib/esm/diff-drivers/git-diff.js +13 -0
  54. package/lib/esm/diff-drivers/index.js +18 -0
  55. package/lib/esm/diff-drivers/types.js +2 -0
  56. package/lib/esm/formatting/index.js +18 -0
  57. package/lib/esm/formatting/infer-json-indent.js +18 -0
  58. package/lib/esm/formatting/sync-results-to-md.js +40 -0
  59. package/lib/esm/index.js +19 -0
  60. package/lib/esm/load-plugin.js +40 -0
  61. package/lib/esm/match.js +45 -0
  62. package/lib/esm/merge-file.js +99 -0
  63. package/lib/esm/plugins/index.js +10 -0
  64. package/lib/esm/plugins/json-merge.js +185 -0
  65. package/lib/esm/template-sync.js +56 -0
  66. package/lib/esm/test-utils/index.js +10 -0
  67. package/lib/esm/types.js +2 -0
  68. package/package.json +60 -0
  69. package/release.config.js +34 -0
  70. package/src/clone-drivers/git-clone.ts +16 -0
  71. package/src/clone-drivers/index.ts +2 -0
  72. package/src/clone-drivers/types.ts +8 -0
  73. package/src/diff-drivers/git-diff.ts +10 -0
  74. package/src/diff-drivers/index.ts +2 -0
  75. package/src/diff-drivers/types.ts +8 -0
  76. package/src/formatting/__snapshots__/sync-results-to-md.spec.ts.snap +22 -0
  77. package/src/formatting/index.ts +2 -0
  78. package/src/formatting/infer-json-indent.spec.ts +49 -0
  79. package/src/formatting/infer-json-indent.ts +16 -0
  80. package/src/formatting/sync-results-to-md.spec.ts +25 -0
  81. package/src/formatting/sync-results-to-md.ts +46 -0
  82. package/src/index.ts +3 -0
  83. package/src/load-plugin.ts +42 -0
  84. package/src/match.spec.ts +68 -0
  85. package/src/match.ts +52 -0
  86. package/src/merge-file.spec.ts +432 -0
  87. package/src/merge-file.ts +150 -0
  88. package/src/plugins/index.ts +11 -0
  89. package/src/plugins/json-merge.spec.ts +350 -0
  90. package/src/plugins/json-merge.ts +205 -0
  91. package/src/template-sync.spec.ts +216 -0
  92. package/src/template-sync.ts +113 -0
  93. package/src/test-utils/index.ts +13 -0
  94. package/src/types.ts +124 -0
  95. package/templatesync.local.json +15 -0
  96. package/test-fixtures/downstream/README.md +3 -0
  97. package/test-fixtures/downstream/package.json +18 -0
  98. package/test-fixtures/downstream/plugins/custom-plugin.js +11 -0
  99. package/test-fixtures/downstream/src/index.js +2 -0
  100. package/test-fixtures/downstream/src/index.ts +1 -0
  101. package/test-fixtures/downstream/src/templated.js +2 -0
  102. package/test-fixtures/downstream/src/templated.ts +1 -0
  103. package/test-fixtures/downstream/templatesync.json +19 -0
  104. package/test-fixtures/downstream/templatesync.local.json +14 -0
  105. package/test-fixtures/dummy-plugin.js +8 -0
  106. package/test-fixtures/glob-test/folder1/something.js +1 -0
  107. package/test-fixtures/glob-test/folder1/something.ts +0 -0
  108. package/test-fixtures/glob-test/toplevel.js +0 -0
  109. package/test-fixtures/glob-test/toplevel.txt +0 -0
  110. package/test-fixtures/template/custom-bin/something.txt +1 -0
  111. package/test-fixtures/template/package.json +17 -0
  112. package/test-fixtures/template/src/index.js +2 -0
  113. package/test-fixtures/template/src/index.ts +1 -0
  114. package/test-fixtures/template/src/templated.js +2 -0
  115. package/test-fixtures/template/src/templated.ts +1 -0
  116. package/test-fixtures/template/templatesync.json +19 -0
  117. package/tsconfig.cjs.json +12 -0
  118. package/tsconfig.esm.json +10 -0
  119. package/tsconfig.json +22 -0
@@ -0,0 +1,432 @@
1
+ import { join, resolve } from "path";
2
+ import { mergeFile } from "./merge-file";
3
+ import { tempDir, TEST_FIXTURES_DIR } from "./test-utils";
4
+ import { mkdtemp, readFile, rm } from "fs/promises";
5
+ import { copySync } from "fs-extra";
6
+ import { JsonFileMergeOptions } from "./types";
7
+
8
+ const testTemplateDir = resolve(TEST_FIXTURES_DIR, "template");
9
+ const testDownstreamDir = resolve(TEST_FIXTURES_DIR, "downstream");
10
+
11
+ describe("mergeFile", () => {
12
+ let tmpDir: string;
13
+ beforeEach(async () => {
14
+ tmpDir = await mkdtemp(tempDir());
15
+ copySync(testDownstreamDir, tmpDir);
16
+ });
17
+ afterEach(async () => {
18
+ await rm(tmpDir, {
19
+ force: true,
20
+ recursive: true,
21
+ });
22
+ });
23
+ // Note: we use the "ignore" from the templateSync to constrain files we iterate over so it doeesn't happen here
24
+ // it('skips the file if it is part of template config ignroe', async () => {
25
+ // expect(await mergeFile('package.json', {
26
+ // cwd: tmpDir,
27
+ // tempCloneDir: testTemplateDir,
28
+ // localTemplateSyncConfig: {
29
+ // ignore: [],
30
+ // merge: {
31
+ // }
32
+ // },
33
+ // templateSyncConfig: {
34
+ // ignore: ['**/package.json'],
35
+ // }
36
+ // })).toBe(false)
37
+ // })
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
+ });
71
+
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 () => {
79
+ expect(
80
+ await mergeFile("package.json", {
81
+ cwd: tmpDir,
82
+ tempCloneDir: testTemplateDir,
83
+ localTemplateSyncConfig: {
84
+ ignore: [],
85
+ },
86
+ templateSyncConfig: {
87
+ ignore: ["**/*.txt"],
88
+ merge: {
89
+ ".json": {
90
+ // no plugins
91
+ rules: [
92
+ {
93
+ glob: "**/package.json",
94
+ options: "merge-current",
95
+ },
96
+ {
97
+ glob: "**/package.json",
98
+ options: "merge-template",
99
+ },
100
+ ],
101
+ },
102
+ },
103
+ },
104
+ }),
105
+ ).toEqual({
106
+ ignoredDueToLocal: false,
107
+ localChanges: [],
108
+ });
109
+
110
+ // Ensure we overwrote
111
+ expect(
112
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
113
+ ).toEqual({
114
+ name: "mypkg",
115
+ description: "my description",
116
+ dependencies: {
117
+ mypackage: "^1.2.0",
118
+ newpacakge: "^22.2.2",
119
+ package2: "3.22.1",
120
+ huh: "^2.30.0",
121
+ },
122
+ engines: {
123
+ node: ">=20",
124
+ },
125
+ scripts: {
126
+ build: "build",
127
+ test: "jest",
128
+ myscript: "somescript",
129
+ },
130
+ version: "new-version",
131
+ });
132
+ });
133
+ it("[inverse] applies default [.json] merge with first rules if merge applies to file", async () => {
134
+ expect(
135
+ await mergeFile("package.json", {
136
+ cwd: tmpDir,
137
+ tempCloneDir: testTemplateDir,
138
+ localTemplateSyncConfig: {
139
+ ignore: [],
140
+ },
141
+ templateSyncConfig: {
142
+ ignore: ["**/*.txt"],
143
+ merge: {
144
+ ".json": {
145
+ // no plugins
146
+ rules: [
147
+ {
148
+ glob: "**/package.json",
149
+ options: "merge-template",
150
+ },
151
+ {
152
+ glob: "**/package.json",
153
+ options: "merge-current",
154
+ },
155
+ ],
156
+ },
157
+ },
158
+ },
159
+ }),
160
+ ).toEqual({
161
+ ignoredDueToLocal: false,
162
+ localChanges: [],
163
+ });
164
+
165
+ // Ensure we overwrote
166
+ expect(
167
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
168
+ ).toEqual({
169
+ name: "some-stub-name",
170
+ description: "some-stub-description",
171
+ dependencies: {
172
+ mypackage: "^1.2.0",
173
+ newpacakge: "^22.2.2",
174
+ package2: "3.22.1",
175
+ huh: "~1.0.0",
176
+ },
177
+ engines: {
178
+ node: ">=15",
179
+ },
180
+ scripts: {
181
+ build: "build",
182
+ test: "fill this in yourself",
183
+ myscript: "somescript",
184
+ },
185
+ version: "new-version",
186
+ });
187
+ });
188
+ it("[inverse] applies default [.json] merge with sync and then local override", async () => {
189
+ expect(
190
+ await mergeFile("package.json", {
191
+ cwd: tmpDir,
192
+ tempCloneDir: testTemplateDir,
193
+ localTemplateSyncConfig: {
194
+ ignore: [],
195
+ merge: {
196
+ ".json": {
197
+ rules: [
198
+ {
199
+ glob: "**/package.json",
200
+ options: {
201
+ paths: [
202
+ // Do not touch huh
203
+ ["$.dependencies.huh", "merge-current"],
204
+ ],
205
+ },
206
+ },
207
+ ],
208
+ },
209
+ },
210
+ },
211
+ templateSyncConfig: {
212
+ ignore: ["**/*.txt"],
213
+ merge: {
214
+ ".json": {
215
+ // no plugins
216
+ rules: [
217
+ {
218
+ glob: "**/package.json",
219
+ options: {
220
+ missingIsDelete: true,
221
+ paths: [
222
+ // Merge all template dependencies
223
+ ["$.dependencies", "merge-template"],
224
+ ],
225
+ } as JsonFileMergeOptions,
226
+ },
227
+ {
228
+ glob: "**/package.json",
229
+ options: "merge-current",
230
+ },
231
+ ],
232
+ },
233
+ },
234
+ },
235
+ }),
236
+ ).toEqual({
237
+ ignoredDueToLocal: false,
238
+ localChanges: expect.arrayContaining([
239
+ {
240
+ added: undefined,
241
+ count: 1,
242
+ removed: true,
243
+ value: ' "huh": "~1.0.0"\n',
244
+ },
245
+ {
246
+ added: true,
247
+ count: 1,
248
+ removed: undefined,
249
+ value: ' "huh": "^2.30.0"\n',
250
+ },
251
+ ]),
252
+ });
253
+
254
+ // Ensure we overwrote
255
+ expect(
256
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
257
+ ).toEqual({
258
+ name: "mypkg",
259
+ description: "my description",
260
+ dependencies: {
261
+ mypackage: "^1.2.0",
262
+ newpacakge: "^22.2.2",
263
+ package2: "3.22.1",
264
+ huh: "^2.30.0",
265
+ },
266
+ engines: {
267
+ node: ">=20",
268
+ },
269
+ scripts: {
270
+ build: "build",
271
+ test: "jest",
272
+ myscript: "somescript",
273
+ },
274
+ version: "new-version",
275
+ });
276
+ });
277
+ it("[inverse] applies default [.json] and custom merge for local with sync and then local override", async () => {
278
+ expect(
279
+ await mergeFile("package.json", {
280
+ cwd: tmpDir,
281
+ tempCloneDir: testTemplateDir,
282
+ localTemplateSyncConfig: {
283
+ ignore: [],
284
+ merge: {
285
+ ".json": {
286
+ // simulates a "node" plugin since we're pulling from the current context
287
+ plugin: "../test-fixtures/dummy-plugin.js",
288
+ rules: [
289
+ {
290
+ glob: "**/package.json",
291
+ options: {
292
+ paths: [
293
+ // Do not touch huh
294
+ ["$.dependencies.huh", "merge-current"],
295
+ ],
296
+ },
297
+ },
298
+ ],
299
+ },
300
+ },
301
+ },
302
+ templateSyncConfig: {
303
+ ignore: ["**/*.txt"],
304
+ merge: {
305
+ ".json": {
306
+ // no plugins
307
+ rules: [
308
+ {
309
+ glob: "**/package.json",
310
+ options: {
311
+ missingIsDelete: true,
312
+ paths: [
313
+ // Merge all template dependencies
314
+ ["$.dependencies", "merge-template"],
315
+ ],
316
+ } as JsonFileMergeOptions,
317
+ },
318
+ {
319
+ glob: "**/package.json",
320
+ options: "merge-current",
321
+ },
322
+ ],
323
+ },
324
+ },
325
+ },
326
+ }),
327
+ ).toEqual({
328
+ ignoredDueToLocal: false,
329
+ // TODO: I don't
330
+ localChanges: expect.arrayContaining([
331
+ {
332
+ added: undefined,
333
+ count: 17,
334
+ removed: true,
335
+ value: ` "name": "mypkg",
336
+ "description": "my description",
337
+ "dependencies": {
338
+ "mypackage": "^1.2.0",
339
+ "newpacakge": "^22.2.2",
340
+ "package2": "3.22.1",
341
+ "huh": "~1.0.0"
342
+ },
343
+ "engines": {
344
+ "node": ">=20"
345
+ },
346
+ "scripts": {
347
+ "build": "build",
348
+ "test": "jest",
349
+ "myscript": "somescript"
350
+ },
351
+ "version": "new-version"\n`,
352
+ },
353
+ {
354
+ added: true,
355
+ count: 1,
356
+ removed: undefined,
357
+ value: ` "tested": true\n`,
358
+ },
359
+ ]),
360
+ });
361
+
362
+ // Ensure we overwrote
363
+ expect(
364
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
365
+ ).toEqual({
366
+ tested: true,
367
+ });
368
+ });
369
+ it("[inverse] applies default [.json] and custom merge for local with sync plugin and then local override", async () => {
370
+ expect(
371
+ await mergeFile("package.json", {
372
+ cwd: tmpDir,
373
+ tempCloneDir: testTemplateDir,
374
+ localTemplateSyncConfig: {
375
+ ignore: [],
376
+ merge: {
377
+ ".json": {
378
+ // This is a relative path to ht
379
+ plugin: "plugins/custom-plugin.js",
380
+ rules: [
381
+ {
382
+ glob: "**/package.json",
383
+ options: {
384
+ paths: [
385
+ // Do not touch huh
386
+ ["$.dependencies.huh", "merge-current"],
387
+ ],
388
+ },
389
+ },
390
+ ],
391
+ },
392
+ },
393
+ },
394
+ templateSyncConfig: {
395
+ ignore: ["**/*.txt"],
396
+ merge: {
397
+ ".json": {
398
+ // no plugins
399
+ rules: [
400
+ {
401
+ glob: "**/package.json",
402
+ options: {
403
+ missingIsDelete: true,
404
+ paths: [
405
+ // Merge all template dependencies
406
+ ["$.dependencies", "merge-template"],
407
+ ],
408
+ } as JsonFileMergeOptions,
409
+ },
410
+ {
411
+ glob: "**/package.json",
412
+ options: "merge-current",
413
+ },
414
+ ],
415
+ },
416
+ },
417
+ },
418
+ }),
419
+ ).toEqual({
420
+ ignoredDueToLocal: false,
421
+ // We are just making sure plugin look up happens here
422
+ localChanges: expect.arrayContaining([]),
423
+ });
424
+
425
+ // Ensure we overwrote
426
+ expect(
427
+ JSON.parse((await readFile(join(tmpDir, "package.json"))).toString()),
428
+ ).toEqual({
429
+ downstream: true,
430
+ });
431
+ });
432
+ });
@@ -0,0 +1,150 @@
1
+ import { isMatch, some } from "micromatch";
2
+ import { Config, MergeContext, MergePlugin } from "./types";
3
+ import { extname, join } from "path";
4
+ import { existsSync } from "fs";
5
+ import { readFile } from "fs/promises";
6
+ import { loadPlugin } from "./load-plugin";
7
+ import { Change, diffLines } from "diff";
8
+ import { outputFile } from "fs-extra";
9
+
10
+ interface MergeFileOptions {
11
+ localTemplateSyncConfig: Config;
12
+ templateSyncConfig: Config;
13
+ tempCloneDir: string;
14
+ cwd: string;
15
+ }
16
+
17
+ interface MergeFileReturn {
18
+ /**
19
+ * If the file was ignored due to the local config
20
+ */
21
+ ignoredDueToLocal: boolean;
22
+ /**
23
+ * Only available if the file wasn't ignored, this is a list of lineDiffs
24
+ * from the the diff library that were applied to what would've been removed
25
+ */
26
+ localChanges?: Change[];
27
+ }
28
+
29
+ /**
30
+ * Applies the merge to a file according to the context information.
31
+ *
32
+ * Returns true if merged and false if skipped
33
+ * @param relPath
34
+ * @param context
35
+ * @returns
36
+ */
37
+ export async function mergeFile(
38
+ relPath: string,
39
+ context: MergeFileOptions,
40
+ ): Promise<MergeFileReturn> {
41
+ const { localTemplateSyncConfig, templateSyncConfig, tempCloneDir, cwd } =
42
+ context;
43
+
44
+ if (some(relPath, localTemplateSyncConfig.ignore)) {
45
+ return {
46
+ ignoredDueToLocal: true,
47
+ };
48
+ }
49
+
50
+ const ext = extname(relPath);
51
+ const filePath = join(cwd, relPath);
52
+ const templatePath = join(tempCloneDir, relPath);
53
+
54
+ const mergeConfig = templateSyncConfig.merge?.[ext];
55
+ const localMergeConfig = localTemplateSyncConfig.merge?.[ext];
56
+
57
+ // Either write the merge or write
58
+ let fileContents: string;
59
+ const localChanges: Change[] = [];
60
+ if (existsSync(filePath) && (mergeConfig || localMergeConfig)) {
61
+ const originalCurrentFile = (await readFile(filePath)).toString();
62
+ if (mergeConfig) {
63
+ // Apply the template's most recent merges
64
+ const handler = await loadPlugin(mergeConfig, ext, tempCloneDir);
65
+
66
+ const mergeOptions = mergeConfig.rules.find((rule) => {
67
+ return isMatch(relPath, rule.glob);
68
+ });
69
+
70
+ if (mergeOptions) {
71
+ fileContents = await safeMerge(
72
+ handler,
73
+ mergeConfig.plugin ?? `default for ${ext}`,
74
+ originalCurrentFile,
75
+ (await readFile(templatePath)).toString(),
76
+ {
77
+ relFilePath: relPath,
78
+ mergeArguments: mergeOptions.options,
79
+ isLocalOptions: true,
80
+ },
81
+ );
82
+ } else {
83
+ // Apply overwrite if we didn't set up merge
84
+ fileContents = (await readFile(templatePath)).toString();
85
+ }
86
+ } else {
87
+ // Apply overwrite if we didn't set up merge
88
+ fileContents = (await readFile(templatePath)).toString();
89
+ }
90
+
91
+ // We apply the localMerge Config to the fileContent output by the template merge
92
+ if (localMergeConfig) {
93
+ const handler = await loadPlugin(localMergeConfig, ext, cwd);
94
+
95
+ const mergeOptions = localMergeConfig.rules.find((rule) => {
96
+ return isMatch(relPath, rule.glob);
97
+ });
98
+
99
+ if (mergeOptions) {
100
+ const localContents = await safeMerge(
101
+ handler,
102
+ localMergeConfig.plugin ?? `default for ${ext}`,
103
+ originalCurrentFile,
104
+ fileContents,
105
+ {
106
+ relFilePath: relPath,
107
+ mergeArguments: mergeOptions.options,
108
+ isLocalOptions: true,
109
+ },
110
+ );
111
+ localChanges.push(...diffLines(fileContents, localContents));
112
+ fileContents = localContents;
113
+ }
114
+ }
115
+ } else {
116
+ // Just perform simple overwrite
117
+ fileContents = (await readFile(templatePath)).toString();
118
+ }
119
+ await outputFile(filePath, fileContents);
120
+ return {
121
+ ignoredDueToLocal: false,
122
+ localChanges,
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Simple helper function to ensure that we don't let bad plugins corrupt the call flow
128
+ * @param plugin
129
+ */
130
+ async function safeMerge(
131
+ plugin: MergePlugin<unknown>,
132
+ pluginPath: string,
133
+ current: string,
134
+ fromTemplate: string,
135
+ context: MergeContext,
136
+ ) {
137
+ const ret = await plugin.merge(current, fromTemplate, context);
138
+ if (typeof ret !== "string") {
139
+ throw new Error(
140
+ `Plugin ${pluginPath} did not return string for merge function! This is not allowed!`,
141
+ );
142
+ }
143
+
144
+ if (!ret) {
145
+ throw new Error(
146
+ `Plugin ${pluginPath} should not make a merge be an empty string!`,
147
+ );
148
+ }
149
+ return ret as string;
150
+ }
@@ -0,0 +1,11 @@
1
+ import { JsonFileMergeOptions, MergePlugin } from "../types";
2
+ import { merge as jsonMerge, validate as jsonValidate } from "./json-merge";
3
+
4
+ export const defaultExtensionMap = {
5
+ ".json": {
6
+ merge: jsonMerge,
7
+ validate: jsonValidate,
8
+ },
9
+ } as {
10
+ [ext: string]: MergePlugin<JsonFileMergeOptions>;
11
+ };