@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,216 @@
1
+ import { copy } from "fs-extra";
2
+ import { mkdtemp, readFile, rm } from "fs/promises";
3
+ import { templateSync } from "./template-sync";
4
+ import { tempDir, TEST_FIXTURES_DIR } from "./test-utils";
5
+ import { join, resolve } from "path";
6
+ import { existsSync, readFileSync, writeFileSync } from "fs";
7
+
8
+ // Just return the test-fixture directory
9
+ const dummyCloneDriver = async () => {
10
+ return resolve(TEST_FIXTURES_DIR, "template");
11
+ };
12
+
13
+ const downstreamDir = resolve(TEST_FIXTURES_DIR, "downstream");
14
+
15
+ describe("templateSync", () => {
16
+ let tmpDir: string;
17
+ beforeEach(async () => {
18
+ tmpDir = await mkdtemp(tempDir());
19
+ await copy(downstreamDir, tmpDir);
20
+ });
21
+ afterEach(async () => {
22
+ await rm(tmpDir, {
23
+ force: true,
24
+ recursive: true,
25
+ });
26
+ });
27
+ it("appropriately merges according to just the templatesync config file into an empty dir", async () => {
28
+ const emptyTmpDir = await mkdtemp(tempDir());
29
+ expect(
30
+ await templateSync({
31
+ tmpCloneDir: "stubbed-by-driver",
32
+ cloneDriver: dummyCloneDriver,
33
+ repoUrl: "not-important",
34
+ repoDir: emptyTmpDir,
35
+ }),
36
+ ).toEqual({
37
+ // Expect no changes since there was no local sync file
38
+ localSkipFiles: [],
39
+ localFileChanges: {},
40
+ });
41
+
42
+ // Verify the files
43
+ await fileMatchTemplate(emptyTmpDir, "templatesync.json");
44
+ await fileMatchTemplate(emptyTmpDir, "package.json");
45
+ await fileMatchTemplate(emptyTmpDir, "src/templated.ts");
46
+
47
+ // Expect the ignores to not be a problem
48
+ expect(existsSync(resolve(emptyTmpDir, "src/index.ts"))).toBeFalsy();
49
+ expect(existsSync(resolve(emptyTmpDir, "src/custom-bin"))).toBeFalsy();
50
+ });
51
+ it("appropriately merges according to just the templatesync config file in an existing repo", async () => {
52
+ // Remove the local sync overrides
53
+ await rm(join(tmpDir, "templatesync.local.json"));
54
+
55
+ const result = await templateSync({
56
+ tmpCloneDir: "stubbed-by-driver",
57
+ cloneDriver: dummyCloneDriver,
58
+ repoUrl: "not-important",
59
+ repoDir: tmpDir,
60
+ });
61
+
62
+ expect(result.localSkipFiles).toEqual([]);
63
+ expect(result.localFileChanges).toEqual({});
64
+
65
+ // Verify the files
66
+ await fileMatchTemplate(tmpDir, "templatesync.json");
67
+ await fileMatchTemplate(tmpDir, "src/templated.ts");
68
+ const packageJson = JSON.parse(
69
+ readFileSync(resolve(tmpDir, "package.json")).toString(),
70
+ );
71
+
72
+ expect(packageJson).toEqual({
73
+ name: "mypkg",
74
+ description: "my description",
75
+ dependencies: {
76
+ mypackage: "^1.2.0",
77
+ newpacakge: "^22.2.2",
78
+ package2: "3.22.1",
79
+ huh: "~1.0.0",
80
+ },
81
+ engines: {
82
+ node: ">=15",
83
+ },
84
+ scripts: {
85
+ build: "build",
86
+ test: "jest",
87
+ myscript: "somescript",
88
+ },
89
+ // By default we add new top-level fields
90
+ version: "new-version",
91
+ });
92
+
93
+ // Expect the ignores to not be a problem
94
+ await fileMatchDownstream(tmpDir, "src/index.ts");
95
+ await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
96
+ });
97
+ it("appropriately merges according to the templatesync config file and the local config in an existing repo", async () => {
98
+ // Remove the local sync overrides
99
+ await rm(join(tmpDir, "templatesync.local.json"));
100
+
101
+ writeFileSync(
102
+ join(tmpDir, "templatesync.local.json"),
103
+ JSON.stringify({
104
+ ignore: [
105
+ // Ignores the templated.ts
106
+ "**/*.ts",
107
+ // We don't have a need for this in here, but it's an example of keeping things cleaner for our custom plugins
108
+ "plugins/**",
109
+ ],
110
+ merge: {
111
+ ".json": {
112
+ // Let's nuke package.json with this plugin
113
+ plugin: "plugins/custom-plugin.js",
114
+ rules: [
115
+ {
116
+ glob: "package.json",
117
+ options: {},
118
+ },
119
+ ],
120
+ },
121
+ },
122
+ }),
123
+ );
124
+
125
+ const result = await templateSync({
126
+ tmpCloneDir: "stubbed-by-driver",
127
+ cloneDriver: dummyCloneDriver,
128
+ repoUrl: "not-important",
129
+ repoDir: tmpDir,
130
+ });
131
+
132
+ expect(result.localSkipFiles).toEqual(["src/templated.ts"]);
133
+ // TODO: more rigorous testing around diff changes
134
+ expect(result.localFileChanges).toEqual(
135
+ expect.objectContaining({
136
+ "package.json": expect.arrayContaining([]),
137
+ }),
138
+ );
139
+
140
+ // Verify the files
141
+ await fileMatchTemplate(tmpDir, "templatesync.json");
142
+ await fileMatchDownstream(tmpDir, "src/templated.ts");
143
+ const packageJson = JSON.parse(
144
+ readFileSync(resolve(tmpDir, "package.json")).toString(),
145
+ );
146
+
147
+ // The plugin nuked this
148
+ expect(packageJson).toEqual({
149
+ downstream: true,
150
+ });
151
+
152
+ // Expect the ignores to not be a problem
153
+ await fileMatchDownstream(tmpDir, "src/index.ts");
154
+ await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
155
+ });
156
+ it("appropriately merges according to the templatesync config file and the local config in an existing repo with afterRef", async () => {
157
+ // Remove the local sync overrides
158
+ await rm(join(tmpDir, "templatesync.local.json"));
159
+
160
+ writeFileSync(
161
+ join(tmpDir, "templatesync.local.json"),
162
+ JSON.stringify({
163
+ afterRef: "dummySha",
164
+ ignore: [
165
+ // We don't have a need for this in here, but it's an example of keeping things cleaner for our custom plugins
166
+ "plugins/**",
167
+ ],
168
+ }),
169
+ );
170
+
171
+ // We will only update the templated.ts
172
+ const mockDiffDriver = jest
173
+ .fn()
174
+ .mockImplementation(async () => ["src/templated.ts"]);
175
+ const result = await templateSync({
176
+ tmpCloneDir: "stubbed-by-driver",
177
+ cloneDriver: dummyCloneDriver,
178
+ repoUrl: "not-important",
179
+ repoDir: tmpDir,
180
+ diffDriver: mockDiffDriver,
181
+ });
182
+
183
+ // since there was no override for this file, not changes from the local file
184
+ expect(result.localFileChanges).toEqual(expect.objectContaining({}));
185
+
186
+ // Verify the files
187
+ await fileMatchTemplate(tmpDir, "templatesync.json");
188
+ await fileMatchTemplate(tmpDir, "src/templated.ts");
189
+
190
+ // Expect the none of the diff files to work
191
+ await fileMatchDownstream(tmpDir, "src/index.ts");
192
+ await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
193
+ await fileMatchDownstream(tmpDir, "package.json");
194
+ });
195
+ });
196
+
197
+ // helper
198
+ async function fileMatchTemplate(tmpDir: string, relPath: string) {
199
+ return fileMatch(tmpDir, relPath, "template");
200
+ }
201
+
202
+ async function fileMatchDownstream(tmpDir: string, relPath: string) {
203
+ return fileMatch(tmpDir, relPath, "downstream");
204
+ }
205
+
206
+ async function fileMatch(
207
+ tmpDir: string,
208
+ relPath: string,
209
+ source: "downstream" | "template",
210
+ ) {
211
+ const dir =
212
+ source === "downstream" ? downstreamDir : await dummyCloneDriver();
213
+ expect((await readFile(resolve(tmpDir, relPath))).toString()).toEqual(
214
+ (await readFile(resolve(dir, relPath))).toString(),
215
+ );
216
+ }
@@ -0,0 +1,113 @@
1
+ import { join } from "path";
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { getAllFilesInDir } from "./match";
4
+ import { Config, LocalConfig } from "./types";
5
+ import { mergeFile } from "./merge-file";
6
+ import { gitClone } from "./clone-drivers/git-clone";
7
+ import { Change } from "diff";
8
+ import { TemplateCloneDriverFn } from "./clone-drivers";
9
+ import { TemplateDiffDriverFn, gitDiff } from "./diff-drivers";
10
+
11
+ export interface TemplateSyncOptions {
12
+ repoUrl: string;
13
+
14
+ /**
15
+ * The directory for cloning our template repo into via the cloneDriver
16
+ */
17
+ tmpCloneDir: string;
18
+
19
+ /**
20
+ * The repo directory path that we are going to merge toward
21
+ */
22
+ repoDir: string;
23
+
24
+ /**
25
+ * Defaults to using git clone
26
+ */
27
+ cloneDriver?: TemplateCloneDriverFn;
28
+
29
+ /**
30
+ * Defaults to using git diff
31
+ */
32
+ diffDriver?: TemplateDiffDriverFn;
33
+ }
34
+
35
+ export interface TemplateSyncReturn {
36
+ /**
37
+ * An array of files that were skipped outright due to a templatesync.local ignore glob
38
+ */
39
+ localSkipFiles: string[];
40
+ /**
41
+ * An object mapping all file paths to any merge rules that would've overridden the merge rules
42
+ * of the template sync file.
43
+ *
44
+ * Note: right now, this shows non-changed diffs as well so you have to look for added or removed
45
+ */
46
+ localFileChanges: {
47
+ [filePath: string]: Change[];
48
+ };
49
+ }
50
+
51
+ export const TEMPLATE_SYNC_CONFIG = "templatesync";
52
+ export const TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
53
+
54
+ export async function templateSync(
55
+ options: TemplateSyncOptions,
56
+ ): Promise<TemplateSyncReturn> {
57
+ const cloneDriver = options.cloneDriver ?? gitClone;
58
+ const diffDriver = options.diffDriver ?? gitDiff;
59
+ const tempCloneDir = await cloneDriver(options.tmpCloneDir, options.repoUrl);
60
+
61
+ // Get the clone Config
62
+ const cloneConfigPath = join(tempCloneDir, `${TEMPLATE_SYNC_CONFIG}.json`);
63
+ const templateSyncConfig: Config = existsSync(cloneConfigPath)
64
+ ? JSON.parse(readFileSync(cloneConfigPath).toString())
65
+ : {};
66
+
67
+ const localConfigPath = join(
68
+ options.repoDir,
69
+ `${TEMPLATE_SYNC_LOCAL_CONFIG}.json`,
70
+ );
71
+ const localTemplateSyncConfig = existsSync(localConfigPath)
72
+ ? JSON.parse(readFileSync(localConfigPath).toString())
73
+ : ({} as LocalConfig);
74
+
75
+ let filesToSync: string[];
76
+ if (localTemplateSyncConfig.afterRef) {
77
+ filesToSync = await diffDriver(
78
+ tempCloneDir,
79
+ localTemplateSyncConfig.afterRef,
80
+ );
81
+ } else {
82
+ filesToSync = getAllFilesInDir(tempCloneDir, [
83
+ ...templateSyncConfig.ignore,
84
+ ".git/**",
85
+ ]);
86
+ }
87
+
88
+ const localSkipFiles: string[] = [];
89
+ const localFileChanges: {
90
+ [filePath: string]: Change[];
91
+ } = {};
92
+
93
+ await Promise.all(
94
+ filesToSync.map(async (f) => {
95
+ const result = await mergeFile(f, {
96
+ localTemplateSyncConfig,
97
+ templateSyncConfig,
98
+ tempCloneDir,
99
+ cwd: options.repoDir,
100
+ });
101
+ if (result.ignoredDueToLocal) {
102
+ localSkipFiles.push(f);
103
+ } else if (result?.localChanges && result.localChanges.length > 0) {
104
+ localFileChanges[f] = result.localChanges;
105
+ }
106
+ }),
107
+ );
108
+
109
+ return {
110
+ localSkipFiles,
111
+ localFileChanges,
112
+ };
113
+ }
@@ -0,0 +1,13 @@
1
+ import { resolve } from "path";
2
+ import { tmpdir } from "os";
3
+
4
+ export const TEST_FIXTURES_DIR = resolve(
5
+ __dirname,
6
+ "..",
7
+ "..",
8
+ "test-fixtures",
9
+ );
10
+
11
+ export function tempDir(): string {
12
+ return process.env.RUNNER_TEMP ?? tmpdir();
13
+ }
package/src/types.ts ADDED
@@ -0,0 +1,124 @@
1
+ /**
2
+ * How we want to merge json
3
+ *
4
+ * overwrite - the template sync overwrites completely
5
+ * merge-template - we merge template into the current json with overrides on matching values happening from the template
6
+ * merge-current - we merge the current json into the template json with overrides on matching values happening from the current json
7
+ */
8
+ export type BaseJsonMergeOptions =
9
+ | "overwrite"
10
+ | "merge-template"
11
+ | "merge-current";
12
+ export interface JsonPathOverrides {
13
+ /**
14
+ * If set to true, this means we won't add new properties from the template
15
+ */
16
+ ignoreNewProperties?: boolean;
17
+ /**
18
+ * If set to true, overwrite will apply undefined values as deleted for the jsonpaths
19
+ * or for values that are supposed to be merged on top of other values
20
+ */
21
+ missingIsDelete?: boolean;
22
+ /**
23
+ * Note, if multiple json paths match a rule, we pick the first one in the list that matches
24
+ */
25
+ paths: /**
26
+ * We only override jsonpaths. Anything not specified is kept the same.
27
+ */
28
+ [jsonPath: `$.${string}`, options: BaseJsonMergeOptions][];
29
+ }
30
+ export type JsonFileMergeOptions = BaseJsonMergeOptions | JsonPathOverrides;
31
+
32
+ // Sum of all basic file merge options
33
+ type MergePluginOptions = JsonFileMergeOptions;
34
+
35
+ /**
36
+ * Configuration object for a given file type merge configuration
37
+ */
38
+ export interface MergeConfig<T> {
39
+ /**
40
+ * The node module, available on the calling node context, that you want to run.
41
+ * If not provided, we try to use any built-in merge plugins.
42
+ */
43
+ plugin?: string;
44
+ /**
45
+ * An array of first match file globs that will
46
+ */
47
+ rules: { glob: string; options: MergePluginOptions | T }[];
48
+ }
49
+
50
+ /**
51
+ * The shape of a template sync config file
52
+ */
53
+ export interface Config<T = unknown> {
54
+ ignore: string[];
55
+ /**
56
+ * If there is no merge config, then we will always just overwrite the file for the diff
57
+ */
58
+ merge?: {
59
+ [fileExt: string]: MergeConfig<T>;
60
+ };
61
+ }
62
+
63
+ /**
64
+ * The shape of a local template sync config file that overrides the root template repo
65
+ */
66
+ export interface LocalConfig<T = unknown> {
67
+ /**
68
+ * This is the ref that we compare against. If empty, we use all files that have changed since the
69
+ * beginning of the template repo.
70
+ */
71
+ afterRef?: string;
72
+
73
+ ignore: string[];
74
+ /**
75
+ * If there is no merge config, then we will always just overwrite the file for the diff
76
+ */
77
+ merge?: {
78
+ [fileExt: string]: MergeConfig<T>;
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Information around the file we are trying to merge and that arguments for that merge
84
+ * from matching a merge configuration
85
+ */
86
+ export interface MergeContext<PluginOptions = unknown> {
87
+ relFilePath: string;
88
+ /**
89
+ * If you have provided a custom merge plugin, this will be the "options" section of
90
+ * the matching glob
91
+ */
92
+ mergeArguments: PluginOptions;
93
+ /**
94
+ * If this run is for the local config. Keep in mind that any local config overrides will
95
+ * be run after the template sync options and we would like to report any changes that
96
+ * are done
97
+ */
98
+ isLocalOptions?: boolean;
99
+ }
100
+
101
+ export interface MergePlugin<PluginOptions> {
102
+ /**
103
+ * This method will be called when a file from the template and it's analog in the downstream repo
104
+ * have some differences. The plugin must perform the merge and return the appropriate file contents
105
+ * as a string
106
+ *
107
+ * TODO: we may create a V2 plugin that could deal with large files and not pass around strings in memory,
108
+ * but for now, this is the current implementation
109
+ *
110
+ * @param current - The downstream repos current file contents
111
+ * @param fromTemplateRepo - the current
112
+ * @param context - an object defining the context around the file and the specific options
113
+ */
114
+ merge(
115
+ current: string,
116
+ fromTemplateRepo: string,
117
+ context: MergeContext<PluginOptions>,
118
+ ): Promise<string>;
119
+ /**
120
+ * Given an options object for the merge, this validates the options object and returns error messages if there is anything wrong.
121
+ * @param options any json value that the user provided - must be validated against the expected options
122
+ */
123
+ validate(options: unknown): string[] | undefined;
124
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "ignore": ["**/*.md"],
3
+ "merge": {
4
+ ".json": {
5
+ "rules": [
6
+ {
7
+ "glob": "package.json",
8
+ "options": {
9
+ "paths": [["$.repository", "merge-template"]]
10
+ }
11
+ }
12
+ ]
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ # My custom readme
2
+
3
+ This is a custom readme for my file
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "mypkg",
3
+ "description": "my description",
4
+ "dependencies": {
5
+ "mypackage": "^1.2.0",
6
+ "newpacakge": "^22.2.2",
7
+ "package2": "3.22.1",
8
+ "huh": "^2.30.0"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "scripts": {
14
+ "build": "build",
15
+ "test": "jest",
16
+ "myscript": "somescript"
17
+ }
18
+ }
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ merge: () => {
3
+ return JSON.stringify(
4
+ {
5
+ downstream: true,
6
+ },
7
+ null,
8
+ 4,
9
+ );
10
+ },
11
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ console.log("This is my custom stuff");
@@ -0,0 +1 @@
1
+ console.log("This is my custom stuff");
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ console.log("this is templated stuff that should not change");
@@ -0,0 +1 @@
1
+ console.log("this is templated stuff that should not change");
@@ -0,0 +1,19 @@
1
+ {
2
+ "ignore": ["src/!(templated).ts", "custom-bin/**"],
3
+ "merge": {
4
+ ".json": {
5
+ "rules": [
6
+ {
7
+ "glob": "package.json",
8
+ "options": {
9
+ "paths": [
10
+ ["$.dependencies", "merge-template"],
11
+ ["$.devDependencies", "merge-template"],
12
+ ["$.engines", "merge-template"]
13
+ ]
14
+ }
15
+ }
16
+ ]
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "ignore": ["custom-bin/**"],
3
+ "merge": {
4
+ ".json": {
5
+ "plugin": "plugins/custom-plugin.js",
6
+ "rules": [
7
+ {
8
+ "glob": "package.json",
9
+ "options": {}
10
+ }
11
+ ]
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ merge: () => {
3
+ return JSON.stringify({ tested: true }, null, 4);
4
+ },
5
+ validate: () => {
6
+ return [];
7
+ },
8
+ };
@@ -0,0 +1 @@
1
+ "use strict";
File without changes
File without changes
File without changes
@@ -0,0 +1 @@
1
+ This is a file that is only meant for the base template maintainers
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "some-stub-name",
3
+ "description": "some-stub-description",
4
+ "dependencies": {
5
+ "mypackage": "^1.2.0",
6
+ "package2": "3.22.1",
7
+ "huh": "~1.0.0"
8
+ },
9
+ "engines": {
10
+ "node": ">=15"
11
+ },
12
+ "scripts": {
13
+ "build": "build",
14
+ "test": "fill this in yourself"
15
+ },
16
+ "version": "new-version"
17
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ console.log("replace this in the downstream");
@@ -0,0 +1 @@
1
+ console.log("replace this in the downstream");
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ console.log("we changed this up and want to push the changes");
@@ -0,0 +1 @@
1
+ console.log("we changed this up and want to push the changes");
@@ -0,0 +1,19 @@
1
+ {
2
+ "ignore": ["src/!(templated).ts", "custom-bin/**"],
3
+ "merge": {
4
+ ".json": {
5
+ "rules": [
6
+ {
7
+ "glob": "package.json",
8
+ "options": {
9
+ "paths": [
10
+ ["$.dependencies", "merge-template"],
11
+ ["$.devDependencies", "merge-template"],
12
+ ["$.engines", "merge-template"]
13
+ ]
14
+ }
15
+ }
16
+ ]
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,12 @@
1
+ // Overrides for building to cjs
2
+ {
3
+ "extends": "./tsconfig.esm.json",
4
+ "compilerOptions": {
5
+ /* Visit https://aka.ms/tsconfig to read more about this file */
6
+
7
+ "module": "CommonJS" /* Specify what module code is generated. */,
8
+ "outDir": "./lib/cjs",
9
+ "moduleResolution": "Node",
10
+ "declaration": true
11
+ }
12
+ }
@@ -0,0 +1,10 @@
1
+ // Base ESM tsconfig file for builds
2
+ {
3
+ "extends": "./tsconfig.json",
4
+ "exclude": [
5
+ "node_modules/**/*",
6
+ "test-fixtures/**/*",
7
+ "lib/**/*",
8
+ "src/**/*.spec.ts"
9
+ ]
10
+ }