@stripchatdev/create-stripchat-extension 0.1.45 → 0.1.47

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 (214) hide show
  1. package/dist/bin/cli/actions/updateAction.d.ts +6 -0
  2. package/dist/bin/cli/actions/updateAction.js +33 -0
  3. package/dist/bin/cli/helpers/printCliError.d.ts +1 -0
  4. package/dist/bin/cli/helpers/printCliError.js +21 -0
  5. package/dist/bin/cli/index.js +70 -0
  6. package/dist/bin/create.d.ts +2 -0
  7. package/dist/bin/create.js +238 -0
  8. package/dist/package.json +5 -2
  9. package/dist/src/constants.d.ts +14 -9
  10. package/dist/src/constants.js +53 -8
  11. package/dist/src/dependencies/constants.d.ts +2 -0
  12. package/dist/src/dependencies/constants.js +11 -0
  13. package/dist/src/dependencies/resolveDependencies.d.ts +6 -0
  14. package/dist/src/dependencies/resolveDependencies.js +43 -0
  15. package/dist/src/dependencies/resolveDependencies.test.js +74 -0
  16. package/dist/src/helpers/error.d.ts +3 -0
  17. package/dist/src/helpers/error.js +9 -0
  18. package/dist/src/helpers/error.test.js +15 -0
  19. package/dist/src/helpers/getIsDirectoryExists.d.ts +7 -0
  20. package/dist/src/helpers/getIsDirectoryExists.js +23 -0
  21. package/dist/src/helpers/getIsDirectoryExists.test.js +34 -0
  22. package/dist/src/helpers/getIsJsxFramework.d.ts +2 -0
  23. package/dist/src/helpers/getIsJsxFramework.js +2 -0
  24. package/dist/src/helpers/getIsJsxFramework.test.js +10 -0
  25. package/dist/src/helpers/getIsTypescript.d.ts +2 -0
  26. package/dist/src/helpers/getIsTypescript.js +1 -0
  27. package/dist/src/helpers/getIsTypescript.test.js +10 -0
  28. package/dist/src/helpers/isSubPath.d.ts +1 -0
  29. package/dist/src/helpers/isSubPath.js +5 -0
  30. package/dist/src/helpers/normalizePath.d.ts +2 -0
  31. package/dist/src/helpers/normalizePath.js +4 -0
  32. package/dist/src/helpers/normalizePath.test.js +19 -0
  33. package/dist/src/migrator/executor.d.ts +9 -0
  34. package/dist/src/migrator/executor.js +112 -0
  35. package/dist/src/migrator/migration.d.ts +3 -0
  36. package/dist/src/migrator/migration.js +18 -0
  37. package/dist/src/migrator/migration.test.js +74 -0
  38. package/dist/src/migrator/migrations/migrations.test.js +97 -0
  39. package/dist/src/migrator/migrations/v2/AGENTS_md/001_recreate-with-generated-part.d.ts +2 -0
  40. package/dist/src/migrator/migrations/v2/AGENTS_md/001_recreate-with-generated-part.js +14 -0
  41. package/dist/src/migrator/migrations/v2/_project-config_json/001_create.d.ts +2 -0
  42. package/dist/src/migrator/migrations/v2/_project-config_json/001_create.js +14 -0
  43. package/dist/src/migrator/migrations/v2/vite_config_ts/001_turn_off_minify_and_assets_inline.d.ts +2 -0
  44. package/dist/src/migrator/migrations/v2/vite_config_ts/001_turn_off_minify_and_assets_inline.js +118 -0
  45. package/dist/src/migrator/planner.d.ts +5 -0
  46. package/dist/src/migrator/planner.js +65 -0
  47. package/dist/src/migrator/planner.test.js +40 -0
  48. package/dist/src/migrator/types.d.ts +29 -0
  49. package/dist/src/migrator/updates/updates.test.js +102 -0
  50. package/dist/src/migrator/updates/v2/AGENTS_md/001_update-generated-part.d.ts +2 -0
  51. package/dist/src/migrator/updates/v2/AGENTS_md/001_update-generated-part.js +38 -0
  52. package/dist/src/migrator/updates/v2/package_json/001_update-versions.d.ts +2 -0
  53. package/dist/src/migrator/updates/v2/package_json/001_update-versions.js +77 -0
  54. package/dist/src/migrator/updates/v2/package_json/002_add-cli-commands.d.ts +2 -0
  55. package/dist/src/migrator/updates/v2/package_json/002_add-cli-commands.js +61 -0
  56. package/dist/src/migrator/validators/getIsValidMigration.d.ts +2 -0
  57. package/dist/src/migrator/validators/getIsValidMigration.js +3 -0
  58. package/dist/src/migrator/validators/getIsValidMigration.test.js +13 -0
  59. package/dist/src/projectConfig/buildProjectConfig.d.ts +15 -0
  60. package/dist/src/projectConfig/buildProjectConfig.js +27 -0
  61. package/dist/src/projectConfig/detectPackageManagerRuntime.d.ts +2 -0
  62. package/dist/src/{helpers/detectPackageManager.js → projectConfig/detectPackageManagerRuntime.js} +1 -1
  63. package/dist/src/{helpers/detectPackageManager.test.js → projectConfig/detectPackageManagerRuntime.test.js} +10 -10
  64. package/dist/src/projectConfig/recoverProjectConfig.d.ts +1 -0
  65. package/dist/src/projectConfig/recoverProjectConfig.js +34 -0
  66. package/dist/src/projectConfig/recovery/detectPackageManagerFromFiles.d.ts +2 -0
  67. package/dist/src/projectConfig/recovery/detectPackageManagerFromFiles.js +16 -0
  68. package/dist/src/projectConfig/recovery/detectPackageManagerFromFiles.test.d.ts +1 -0
  69. package/dist/src/projectConfig/recovery/detectPackageManagerFromFiles.test.js +37 -0
  70. package/dist/src/projectConfig/recovery/parseAgentsMd.d.ts +10 -0
  71. package/dist/src/projectConfig/recovery/parseAgentsMd.js +52 -0
  72. package/dist/src/projectConfig/recovery/parseAgentsMd.test.d.ts +1 -0
  73. package/dist/src/projectConfig/recovery/parseAgentsMd.test.js +67 -0
  74. package/dist/src/projectConfig/recovery/resolveProjectNameFromDir.d.ts +1 -0
  75. package/dist/src/projectConfig/recovery/resolveProjectNameFromDir.js +11 -0
  76. package/dist/src/projectConfig/recovery/resolveProjectNameFromDir.test.d.ts +1 -0
  77. package/dist/src/projectConfig/recovery/resolveProjectNameFromDir.test.js +21 -0
  78. package/dist/src/projectConfig/resolveExistsProjectConfig.d.ts +2 -0
  79. package/dist/src/projectConfig/resolveExistsProjectConfig.js +34 -0
  80. package/dist/src/projectConfig/updateProjectConfig.d.ts +2 -0
  81. package/dist/src/projectConfig/updateProjectConfig.js +20 -0
  82. package/dist/src/projectConfig/validators/getIsValidProjectConfig.d.ts +2 -0
  83. package/dist/src/projectConfig/validators/getIsValidProjectConfig.js +6 -0
  84. package/dist/src/projectConfig/validators/getIsValidProjectConfig.test.d.ts +1 -0
  85. package/dist/src/projectConfig/validators/getIsValidProjectConfig.test.js +33 -0
  86. package/dist/src/projectConfig/validators/validateProjectConfig.d.ts +2 -0
  87. package/dist/src/projectConfig/validators/validateProjectConfig.js +30 -0
  88. package/dist/src/projectConfig/validators/validateProjectConfig.test.d.ts +1 -0
  89. package/dist/src/projectConfig/validators/validateProjectConfig.test.js +215 -0
  90. package/dist/src/templater/executor.d.ts +2 -0
  91. package/dist/src/templater/executor.js +22 -0
  92. package/dist/src/templater/executor.test.d.ts +1 -0
  93. package/dist/src/{executor.test.js → templater/executor.test.js} +25 -11
  94. package/dist/src/templater/helpers/getIsTypescriptOnlyFile.d.ts +1 -0
  95. package/dist/src/templater/helpers/getIsTypescriptOnlyFile.js +10 -0
  96. package/dist/src/templater/helpers/getIsTypescriptOnlyFile.test.d.ts +1 -0
  97. package/dist/src/templater/helpers/getIsTypescriptOnlyFile.test.js +25 -0
  98. package/dist/src/templater/helpers/resolveTemplateExtension.d.ts +1 -0
  99. package/dist/src/templater/helpers/resolveTemplateExtension.js +16 -0
  100. package/dist/src/templater/helpers/resolveTemplateExtension.test.d.ts +1 -0
  101. package/dist/src/{mappers → templater/mappers}/mapProjectConfigToTemplateModel.d.ts +1 -1
  102. package/dist/src/{mappers → templater/mappers}/mapProjectConfigToTemplateModel.js +6 -8
  103. package/dist/src/templater/mappers/mapProjectConfigToTemplateModel.test.d.ts +1 -0
  104. package/dist/src/{mappers → templater/mappers}/mapProjectConfigToTemplateModel.test.js +29 -23
  105. package/dist/src/templater/planner.d.ts +2 -0
  106. package/dist/src/templater/planner.js +104 -0
  107. package/dist/src/templater/planner.test.d.ts +1 -0
  108. package/dist/src/templater/planner.test.js +64 -0
  109. package/dist/src/templates/constants.d.ts +6 -0
  110. package/dist/src/templates/constants.js +11 -0
  111. package/dist/src/templates/helpers/resolveTemplatesDir.d.ts +1 -0
  112. package/dist/src/templates/helpers/resolveTemplatesDir.js +16 -0
  113. package/dist/src/templates/templates.d.ts +10 -0
  114. package/dist/src/templates/templates.js +133 -0
  115. package/dist/src/templates/templates.test.d.ts +1 -0
  116. package/dist/src/templates/templates.test.js +263 -0
  117. package/dist/src/templates/types.d.ts +21 -0
  118. package/dist/src/templates/types.js +1 -0
  119. package/dist/src/types.d.ts +28 -9
  120. package/dist/src/validators/getIsValidCategory.d.ts +2 -0
  121. package/dist/src/validators/getIsValidCategory.js +2 -0
  122. package/dist/src/validators/getIsValidCategory.test.d.ts +1 -0
  123. package/dist/src/validators/{isValidCategory.test.js → getIsValidCategory.test.js} +3 -3
  124. package/dist/src/validators/getIsValidFramework.d.ts +2 -0
  125. package/dist/src/validators/getIsValidFramework.js +2 -0
  126. package/dist/src/validators/getIsValidFramework.test.d.ts +1 -0
  127. package/dist/src/validators/{isValidFramework.test.js → getIsValidFramework.test.js} +3 -3
  128. package/dist/src/validators/getIsValidLanguage.d.ts +2 -0
  129. package/dist/src/validators/getIsValidLanguage.js +2 -0
  130. package/dist/src/validators/getIsValidLanguage.test.d.ts +1 -0
  131. package/dist/src/validators/{isValidLanguage.test.js → getIsValidLanguage.test.js} +3 -3
  132. package/dist/src/validators/getIsValidPackageManager.d.ts +2 -0
  133. package/dist/src/validators/getIsValidPackageManager.js +4 -0
  134. package/dist/src/validators/getIsValidPackageManager.test.d.ts +1 -0
  135. package/dist/src/validators/getIsValidPackageManager.test.js +11 -0
  136. package/dist/src/validators/getIsValidSlot.d.ts +2 -0
  137. package/dist/src/validators/getIsValidSlot.js +4 -0
  138. package/dist/src/validators/getIsValidSlot.test.d.ts +1 -0
  139. package/dist/src/validators/getIsValidSlot.test.js +26 -0
  140. package/dist/src/validators/validateSlots.js +40 -8
  141. package/dist/src/validators/validateSlots.test.js +125 -34
  142. package/dist/tsconfig.tsbuildinfo +1 -1
  143. package/package.json +5 -2
  144. package/templates/v1/vite.config.ts.ejs +27 -0
  145. package/templates/v2/.project-config.json.ejs +1 -0
  146. package/templates/v2/AGENTS.md.ejs +712 -0
  147. package/templates/v2/README.md.ejs +31 -0
  148. package/templates/v2/package.json.ejs +26 -0
  149. package/dist/bin/cli.js +0 -221
  150. package/dist/src/executor.d.ts +0 -2
  151. package/dist/src/executor.js +0 -22
  152. package/dist/src/helpers/detectPackageManager.d.ts +0 -2
  153. package/dist/src/helpers/isDirectory.d.ts +0 -1
  154. package/dist/src/helpers/isDirectory.js +0 -9
  155. package/dist/src/helpers/isDirectory.test.js +0 -22
  156. package/dist/src/helpers/isTypescriptOnlyFile.d.ts +0 -1
  157. package/dist/src/helpers/isTypescriptOnlyFile.js +0 -5
  158. package/dist/src/helpers/isTypescriptOnlyFile.test.js +0 -22
  159. package/dist/src/helpers/normalizeRelPath.d.ts +0 -1
  160. package/dist/src/helpers/normalizeRelPath.js +0 -1
  161. package/dist/src/helpers/normalizeRelPath.test.js +0 -19
  162. package/dist/src/helpers/resolveTemplateExtension.d.ts +0 -1
  163. package/dist/src/helpers/resolveTemplateExtension.js +0 -17
  164. package/dist/src/helpers/resolveTemplatesDir.d.ts +0 -1
  165. package/dist/src/helpers/resolveTemplatesDir.js +0 -11
  166. package/dist/src/helpers/resolveTemplatesDir.test.js +0 -21
  167. package/dist/src/planner.d.ts +0 -2
  168. package/dist/src/planner.js +0 -126
  169. package/dist/src/planner.test.js +0 -58
  170. package/dist/src/validators/index.d.ts +0 -6
  171. package/dist/src/validators/index.js +0 -6
  172. package/dist/src/validators/isValidCategory.d.ts +0 -2
  173. package/dist/src/validators/isValidCategory.js +0 -2
  174. package/dist/src/validators/isValidFramework.d.ts +0 -2
  175. package/dist/src/validators/isValidFramework.js +0 -2
  176. package/dist/src/validators/isValidLanguage.d.ts +0 -2
  177. package/dist/src/validators/isValidLanguage.js +0 -2
  178. package/dist/src/validators/validateProjectDir.d.ts +0 -1
  179. package/dist/src/validators/validateProjectDir.js +0 -6
  180. package/dist/src/validators/validateProjectDir.test.js +0 -19
  181. package/dist/bin/{cli.d.ts → cli/index.d.ts} +0 -0
  182. package/dist/src/{executor.test.d.ts → dependencies/resolveDependencies.test.d.ts} +0 -0
  183. package/dist/src/helpers/{detectPackageManager.test.d.ts → error.test.d.ts} +0 -0
  184. package/dist/src/helpers/{isDirectory.test.d.ts → getIsDirectoryExists.test.d.ts} +0 -0
  185. package/dist/src/helpers/{isTypescriptOnlyFile.test.d.ts → getIsJsxFramework.test.d.ts} +0 -0
  186. package/dist/src/helpers/{normalizeRelPath.test.d.ts → getIsTypescript.test.d.ts} +0 -0
  187. package/dist/src/helpers/{resolveTemplateExtension.test.d.ts → normalizePath.test.d.ts} +0 -0
  188. package/dist/src/{helpers/resolveTemplatesDir.test.d.ts → migrator/migration.test.d.ts} +0 -0
  189. package/dist/src/{mappers/mapProjectConfigToTemplateModel.test.d.ts → migrator/migrations/migrations.test.d.ts} +0 -0
  190. package/dist/src/{planner.test.d.ts → migrator/planner.test.d.ts} +0 -0
  191. package/dist/src/{validators/isValidCategory.test.d.ts → migrator/types.js} +0 -0
  192. package/dist/src/{validators/isValidFramework.test.d.ts → migrator/updates/updates.test.d.ts} +0 -0
  193. package/dist/src/{validators/isValidLanguage.test.d.ts → migrator/validators/getIsValidMigration.test.d.ts} +0 -0
  194. package/dist/src/{validators/validateProjectDir.test.d.ts → projectConfig/detectPackageManagerRuntime.test.d.ts} +0 -0
  195. package/dist/src/{helpers → templater/helpers}/resolveTemplateExtension.test.js +0 -0
  196. package/templates/{.gitignore.ejs → v1/.gitignore.ejs} +0 -0
  197. package/templates/{AGENTS.md.ejs → v1/AGENTS.md.ejs} +0 -0
  198. package/templates/{CLAUDE.md → v1/CLAUDE.md} +0 -0
  199. package/templates/{README.md.ejs → v1/README.md.ejs} +0 -0
  200. package/templates/{package.json.ejs → v1/package.json.ejs} +0 -0
  201. package/templates/{public → v1/public}/icon.svg +0 -0
  202. package/templates/{public → v1/public}/manifest.json.ejs +0 -0
  203. package/templates/{public → v1/public}/resolveSlotPage.js.ejs +0 -0
  204. package/templates/{src → v1/src}/slots/background/app.ts.ejs +0 -0
  205. package/templates/{src → v1/src}/slots/background/background.html.ejs +0 -0
  206. package/templates/{src → v1/src}/slots/background/main.ts.ejs +0 -0
  207. package/templates/{src/slots/slot/slot.html.ejs → v1/src/slots/slot/__SLOT_KEY__.html.ejs} +0 -0
  208. package/templates/{src → v1/src}/slots/slot/app.ts.ejs +0 -0
  209. package/templates/{src → v1/src}/slots/slot/app.tsx.ejs +0 -0
  210. package/templates/{src → v1/src}/slots/slot/main.css.ejs +0 -0
  211. package/templates/{src → v1/src}/slots/slot/main.ts.ejs +0 -0
  212. package/templates/{src → v1/src}/slots/slot/main.tsx.ejs +0 -0
  213. package/templates/{tsconfig.json.ejs → v1/tsconfig.json.ejs} +0 -0
  214. package/templates/{vite.config.ts.ejs → v2/vite.config.ts.ejs} +1 -1
@@ -0,0 +1,263 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { toNormalizedRelPath } from '../helpers/normalizePath.js';
6
+ import { discoverVersionDirs, collectManagedFiles, collectTemplateFiles } from './templates.js';
7
+ const getVersionsFromDirs = (dirs) => dirs.map(({ version }) => version);
8
+ const sortManagedFiles = (files) => [...files].sort((a, b) => a.relPath.localeCompare(b.relPath));
9
+ const normalizeCollectedManagedFiles = (files) => sortManagedFiles(files).map((file) => ({
10
+ ...file,
11
+ deleted: file.deleted === true,
12
+ relPath: toNormalizedRelPath(file.relPath),
13
+ }));
14
+ const normalizeExpectedManagedFiles = (files) => sortManagedFiles(files).map((file) => ({
15
+ ...file,
16
+ deleted: file.deleted === true,
17
+ }));
18
+ const sortTemplateFiles = (files) => [...files].sort((a, b) => a.relPath.localeCompare(b.relPath));
19
+ const normalizeCollectedTemplateFiles = (files) => sortTemplateFiles(files).map((file) => ({
20
+ ...file,
21
+ deleted: file.deleted === true,
22
+ relPath: toNormalizedRelPath(file.relPath),
23
+ }));
24
+ const normalizeExpectedTemplateFiles = (files) => sortTemplateFiles(files).map((file) => ({
25
+ ...file,
26
+ deleted: file.deleted === true,
27
+ }));
28
+ describe('discoverVersionDirs', () => {
29
+ it('discover only version dirs in correct order', () => {
30
+ const templatesDir = mkdtempSync(join(tmpdir(), 'sce-template-versions-'));
31
+ try {
32
+ mkdirSync(join(templatesDir, 'v2'));
33
+ mkdirSync(join(templatesDir, 'v10'));
34
+ mkdirSync(join(templatesDir, 'v1'));
35
+ mkdirSync(join(templatesDir, 'draft'));
36
+ writeFileSync(join(templatesDir, 'v3.txt'), 'not a directory');
37
+ const ascResult = discoverVersionDirs(templatesDir, 10, 'asc');
38
+ const descResult = discoverVersionDirs(templatesDir, 10, 'desc');
39
+ const limitedResult = discoverVersionDirs(templatesDir, 2, 'asc');
40
+ expect(getVersionsFromDirs(ascResult)).toEqual([1, 2, 10]);
41
+ expect(getVersionsFromDirs(descResult)).toEqual([10, 2, 1]);
42
+ expect(getVersionsFromDirs(limitedResult)).toEqual([1, 2]);
43
+ expect(ascResult.every(({ dirPath }) => dirPath.startsWith(templatesDir))).toBe(true);
44
+ }
45
+ finally {
46
+ rmSync(templatesDir, { recursive: true, force: true });
47
+ }
48
+ });
49
+ });
50
+ describe('collectManagedFiles', () => {
51
+ it('selects files from highest available version up to targetVersion', () => {
52
+ const templatesDir = mkdtempSync(join(tmpdir(), 'sce-collectManagedFiles-'));
53
+ try {
54
+ // v1: package.json, AGENTS.md, .project-config.json
55
+ mkdirSync(join(templatesDir, 'v1'));
56
+ writeFileSync(join(templatesDir, 'v1', 'package.json.ejs'), '{}');
57
+ writeFileSync(join(templatesDir, 'v1', 'AGENTS.md.ejs'), 'agents v1');
58
+ writeFileSync(join(templatesDir, 'v1', '.project-config.json.ejs'), '{}');
59
+ writeFileSync(join(templatesDir, 'v1', 'README.md'), 'readme v1');
60
+ mkdirSync(join(templatesDir, 'v2'));
61
+ writeFileSync(join(templatesDir, 'v2', 'vite.config.ts.ejs'), 'export default {}');
62
+ writeFileSync(join(templatesDir, 'v2', 'README.md.deleted'), '');
63
+ // v3: higher version of vite.config.ts (should be picked over v2 if targetVersion >= 3)
64
+ mkdirSync(join(templatesDir, 'v3'));
65
+ writeFileSync(join(templatesDir, 'v3', 'vite.config.ts.ejs'), 'export default { minify: false }');
66
+ const managedTemplates = [
67
+ 'package.json.ejs',
68
+ 'AGENTS.md.ejs',
69
+ '.project-config.json.ejs',
70
+ 'vite.config.ts.ejs',
71
+ 'README.md',
72
+ ];
73
+ const expectedV1 = [
74
+ {
75
+ relPath: 'package.json',
76
+ version: 1,
77
+ },
78
+ {
79
+ relPath: 'AGENTS.md',
80
+ version: 1,
81
+ },
82
+ {
83
+ relPath: '.project-config.json',
84
+ version: 1,
85
+ },
86
+ {
87
+ relPath: 'README.md',
88
+ version: 1,
89
+ },
90
+ ];
91
+ const resultV1 = collectManagedFiles(templatesDir, managedTemplates, 1);
92
+ expect(normalizeCollectedManagedFiles(resultV1)).toEqual(normalizeExpectedManagedFiles(expectedV1));
93
+ const expectedV2 = [
94
+ {
95
+ relPath: 'package.json',
96
+ version: 1,
97
+ },
98
+ {
99
+ relPath: 'AGENTS.md',
100
+ version: 1,
101
+ },
102
+ {
103
+ relPath: '.project-config.json',
104
+ version: 1,
105
+ },
106
+ {
107
+ relPath: 'vite.config.ts',
108
+ version: 2,
109
+ },
110
+ {
111
+ relPath: 'README.md',
112
+ version: 2,
113
+ deleted: true,
114
+ },
115
+ ];
116
+ const resultV2 = collectManagedFiles(templatesDir, managedTemplates, 2);
117
+ expect(normalizeCollectedManagedFiles(resultV2)).toEqual(normalizeExpectedManagedFiles(expectedV2));
118
+ const expectedV3 = [
119
+ {
120
+ relPath: 'package.json',
121
+ version: 1,
122
+ },
123
+ {
124
+ relPath: 'AGENTS.md',
125
+ version: 1,
126
+ },
127
+ {
128
+ relPath: '.project-config.json',
129
+ version: 1,
130
+ },
131
+ {
132
+ relPath: 'vite.config.ts',
133
+ version: 3,
134
+ },
135
+ {
136
+ relPath: 'README.md',
137
+ version: 2,
138
+ deleted: true,
139
+ },
140
+ ];
141
+ const resultV3 = collectManagedFiles(templatesDir, managedTemplates, 3);
142
+ expect(normalizeCollectedManagedFiles(resultV3)).toEqual(normalizeExpectedManagedFiles(expectedV3));
143
+ }
144
+ finally {
145
+ rmSync(templatesDir, { recursive: true, force: true });
146
+ }
147
+ });
148
+ });
149
+ describe('collectTemplateFiles', () => {
150
+ it('selects files from highest available version up to targetVersion and skips deleted', () => {
151
+ const templatesDir = mkdtempSync(join(tmpdir(), 'sce-collectTemplateFiles-'));
152
+ try {
153
+ mkdirSync(join(templatesDir, 'v1'));
154
+ writeFileSync(join(templatesDir, 'v1', 'package.json.ejs'), '{}');
155
+ writeFileSync(join(templatesDir, 'v1', 'AGENTS.md.ejs'), 'agents v1');
156
+ writeFileSync(join(templatesDir, 'v1', '.project-config.json.ejs'), '{}');
157
+ writeFileSync(join(templatesDir, 'v1', 'README.md.ejs'), 'readme v1');
158
+ mkdirSync(join(templatesDir, 'v2'));
159
+ writeFileSync(join(templatesDir, 'v2', 'vite.config.ts.ejs'), 'export default {}');
160
+ writeFileSync(join(templatesDir, 'v2', 'README.md.ejs.deleted'), '');
161
+ mkdirSync(join(templatesDir, 'v3'));
162
+ writeFileSync(join(templatesDir, 'v3', 'vite.config.ts.ejs'), 'export default { minify: false }');
163
+ const expectedV1 = [
164
+ {
165
+ relPath: '.project-config.json.ejs',
166
+ srcPath: join(templatesDir, 'v1', '.project-config.json.ejs'),
167
+ version: 1,
168
+ },
169
+ {
170
+ relPath: 'AGENTS.md.ejs',
171
+ srcPath: join(templatesDir, 'v1', 'AGENTS.md.ejs'),
172
+ version: 1,
173
+ },
174
+ {
175
+ relPath: 'README.md.ejs',
176
+ srcPath: join(templatesDir, 'v1', 'README.md.ejs'),
177
+ version: 1,
178
+ },
179
+ {
180
+ relPath: 'package.json.ejs',
181
+ srcPath: join(templatesDir, 'v1', 'package.json.ejs'),
182
+ version: 1,
183
+ },
184
+ ];
185
+ const resultV1 = collectTemplateFiles(templatesDir, 1);
186
+ expect(normalizeCollectedTemplateFiles(resultV1)).toEqual(normalizeExpectedTemplateFiles(expectedV1));
187
+ const expectedV2 = [
188
+ {
189
+ relPath: '.project-config.json.ejs',
190
+ srcPath: join(templatesDir, 'v1', '.project-config.json.ejs'),
191
+ version: 1,
192
+ },
193
+ {
194
+ relPath: 'AGENTS.md.ejs',
195
+ srcPath: join(templatesDir, 'v1', 'AGENTS.md.ejs'),
196
+ version: 1,
197
+ },
198
+ {
199
+ relPath: 'package.json.ejs',
200
+ srcPath: join(templatesDir, 'v1', 'package.json.ejs'),
201
+ version: 1,
202
+ },
203
+ {
204
+ relPath: 'vite.config.ts.ejs',
205
+ srcPath: join(templatesDir, 'v2', 'vite.config.ts.ejs'),
206
+ version: 2,
207
+ },
208
+ ];
209
+ const resultV2 = collectTemplateFiles(templatesDir, 2);
210
+ expect(normalizeCollectedTemplateFiles(resultV2)).toEqual(normalizeExpectedTemplateFiles(expectedV2));
211
+ const expectedV3 = [
212
+ {
213
+ relPath: '.project-config.json.ejs',
214
+ srcPath: join(templatesDir, 'v1', '.project-config.json.ejs'),
215
+ version: 1,
216
+ },
217
+ {
218
+ relPath: 'AGENTS.md.ejs',
219
+ srcPath: join(templatesDir, 'v1', 'AGENTS.md.ejs'),
220
+ version: 1,
221
+ },
222
+ {
223
+ relPath: 'package.json.ejs',
224
+ srcPath: join(templatesDir, 'v1', 'package.json.ejs'),
225
+ version: 1,
226
+ },
227
+ {
228
+ relPath: 'vite.config.ts.ejs',
229
+ srcPath: join(templatesDir, 'v3', 'vite.config.ts.ejs'),
230
+ version: 3,
231
+ },
232
+ ];
233
+ const resultV3 = collectTemplateFiles(templatesDir, 3);
234
+ expect(normalizeCollectedTemplateFiles(resultV3)).toEqual(normalizeExpectedTemplateFiles(expectedV3));
235
+ }
236
+ finally {
237
+ rmSync(templatesDir, { recursive: true, force: true });
238
+ }
239
+ });
240
+ it('collects template files from subDir with version overrides and delete markers', () => {
241
+ const templatesDir = mkdtempSync(join(tmpdir(), 'sce-collectTemplateFiles-subdir-'));
242
+ try {
243
+ mkdirSync(join(templatesDir, 'v1', 'public'), { recursive: true });
244
+ writeFileSync(join(templatesDir, 'v1', 'public', 'manifest.json.ejs'), '{"v":1}');
245
+ writeFileSync(join(templatesDir, 'v1', 'public', 'resolveSlotPage.js.ejs'), 'export default 1');
246
+ mkdirSync(join(templatesDir, 'v2', 'public'), { recursive: true });
247
+ writeFileSync(join(templatesDir, 'v2', 'public', 'manifest.json.ejs'), '{"v":2}');
248
+ writeFileSync(join(templatesDir, 'v2', 'public', 'resolveSlotPage.js.ejs.deleted'), '');
249
+ const result = collectTemplateFiles(templatesDir, 2, 'public');
250
+ const expected = [
251
+ {
252
+ relPath: 'manifest.json.ejs',
253
+ srcPath: join(templatesDir, 'v2', 'public', 'manifest.json.ejs'),
254
+ version: 2,
255
+ },
256
+ ];
257
+ expect(normalizeCollectedTemplateFiles(result)).toEqual(normalizeExpectedTemplateFiles(expected));
258
+ }
259
+ finally {
260
+ rmSync(templatesDir, { recursive: true, force: true });
261
+ }
262
+ });
263
+ });
@@ -0,0 +1,21 @@
1
+ import type { TNormalizedRelPath, TRelPath } from '../types.js';
2
+ export type TTemplateFile = {
3
+ deleted?: boolean;
4
+ relPath: TRelPath;
5
+ srcPath: string;
6
+ version: number;
7
+ };
8
+ export type TManagedFile = {
9
+ deleted?: boolean;
10
+ relPath: TRelPath;
11
+ version: number;
12
+ };
13
+ export type TNormalizedManagedFile = {
14
+ deleted?: boolean;
15
+ relPath: TNormalizedRelPath;
16
+ version: number;
17
+ };
18
+ export type TTemplateVersionDir = {
19
+ dirPath: string;
20
+ version: number;
21
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,9 +1,17 @@
1
- export type TCategory = 'games-and-fun' | 'sex-toys' | 'chat-bots' | 'broadcast-tools';
2
- export type TFramework = 'vanilla' | 'preact';
3
- export type TLanguage = 'ts' | 'js';
4
- export type TSlot = 'EXTENSION_SLOT_MAIN_GAME_FUN' | 'EXTENSION_SLOT_MAIN_SEX_TOY' | 'EXTENSION_SLOT_RIGHT_OVERLAY' | 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY' | 'EXTENSION_SLOT_MOVEABLE_OVERLAY' | 'EXTENSION_SLOT_BACKGROUND';
5
- export type TPackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
6
- export type TProjectConfig = {
1
+ import type { CATEGORIES, FRAMEWORKS, LANGUAGES, PACKAGE_MANAGERS, SLOTS } from './constants';
2
+ import type { TNormalizedManagedFile } from './templates/types';
3
+ export type TCategory = typeof CATEGORIES[number];
4
+ export type TRelPath = string;
5
+ export type TNormalizedRelPath = TRelPath & {
6
+ readonly __normalizedRelPath: true;
7
+ };
8
+ export type TFramework = typeof FRAMEWORKS[number];
9
+ export type TLanguage = typeof LANGUAGES[number];
10
+ export type TSlot = typeof SLOTS[number];
11
+ export type TPackageManager = typeof PACKAGE_MANAGERS[number];
12
+ export type TPackageDependenciesMap = Record<string, string>;
13
+ export type TCliCommandsMap = Record<string, string>;
14
+ export type TProjectConfigUserProps = {
7
15
  category: TCategory;
8
16
  framework: TFramework;
9
17
  language: TLanguage;
@@ -11,6 +19,14 @@ export type TProjectConfig = {
11
19
  packageManager: TPackageManager;
12
20
  slots: TSlot[];
13
21
  };
22
+ export type TProjectConfig = TProjectConfigUserProps & {
23
+ cliCommands: TCliCommandsMap;
24
+ dependencies: TPackageDependenciesMap;
25
+ devDependencies: TPackageDependenciesMap;
26
+ extHelperVersion: string;
27
+ lastTemplateVersion: number;
28
+ managedFiles: TNormalizedManagedFile[];
29
+ };
14
30
  export type TSlotEntry = {
15
31
  slot: TSlot;
16
32
  /** The camel cased version of the slot (e.g. `mainGameFun`). It's used as html file names */
@@ -20,12 +36,11 @@ export type TPackageManagerCommands = {
20
36
  build: string;
21
37
  dev: string;
22
38
  install: string;
39
+ templateUpdate: string;
23
40
  };
24
41
  export type TTemplateData = {
25
42
  config: TProjectConfig;
26
- entryModulePath?: string;
27
43
  ext: 'ts' | 'js';
28
- extHelperVersion: string;
29
44
  framework: TFramework;
30
45
  isJsxFramework: boolean;
31
46
  isTypescript: boolean;
@@ -34,8 +49,12 @@ export type TTemplateData = {
34
49
  name: string;
35
50
  packageManager: TPackageManager;
36
51
  packageManagerCommands: TPackageManagerCommands;
37
- slot?: string;
52
+ slots: TSlot[];
38
53
  slotsEntries: TSlotEntry[];
54
+ };
55
+ export type TTemplateSlotData = TTemplateData & {
56
+ entryModulePath?: string;
57
+ slot?: string;
39
58
  stylePath?: string;
40
59
  };
41
60
  export type TTemplateCommand = {
@@ -0,0 +1,2 @@
1
+ import type { TCategory } from '../types.js';
2
+ export declare const getIsValidCategory: (value: string) => value is TCategory;
@@ -0,0 +1,2 @@
1
+ import { CATEGORIES } from '../constants.js';
2
+ export const getIsValidCategory = (value) => CATEGORIES.includes(value);
@@ -0,0 +1 @@
1
+ export {};
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { isValidCategory } from './isValidCategory.js';
2
+ import { getIsValidCategory } from './getIsValidCategory.js';
3
3
  describe('isValidCategory', () => {
4
4
  it.each(['games-and-fun', 'sex-toys', 'chat-bots', 'broadcast-tools'])('returns true for valid category: %s', (cat) => {
5
- expect(isValidCategory(cat)).toBe(true);
5
+ expect(getIsValidCategory(cat)).toBe(true);
6
6
  });
7
7
  it.each(['invalid', '', 'Games-and-Fun', 'games_and_fun'])('returns false for invalid category: %s', (cat) => {
8
- expect(isValidCategory(cat)).toBe(false);
8
+ expect(getIsValidCategory(cat)).toBe(false);
9
9
  });
10
10
  });
@@ -0,0 +1,2 @@
1
+ import type { TFramework } from '../types.js';
2
+ export declare const getIsValidFramework: (value: string) => value is TFramework;
@@ -0,0 +1,2 @@
1
+ import { FRAMEWORKS } from '../constants.js';
2
+ export const getIsValidFramework = (value) => FRAMEWORKS.includes(value);
@@ -0,0 +1 @@
1
+ export {};
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { isValidFramework } from './isValidFramework.js';
2
+ import { getIsValidFramework } from './getIsValidFramework.js';
3
3
  describe('isValidFramework', () => {
4
4
  it.each(['vanilla', 'preact'])('returns true for valid framework: %s', (fw) => {
5
- expect(isValidFramework(fw)).toBe(true);
5
+ expect(getIsValidFramework(fw)).toBe(true);
6
6
  });
7
7
  it.each(['react', '', 'Vanilla', 'vue'])('returns false for invalid framework: %s', (fw) => {
8
- expect(isValidFramework(fw)).toBe(false);
8
+ expect(getIsValidFramework(fw)).toBe(false);
9
9
  });
10
10
  });
@@ -0,0 +1,2 @@
1
+ import type { TLanguage } from '../types.js';
2
+ export declare const getIsValidLanguage: (value: string) => value is TLanguage;
@@ -0,0 +1,2 @@
1
+ import { LANGUAGES } from '../constants.js';
2
+ export const getIsValidLanguage = (value) => LANGUAGES.includes(value);
@@ -0,0 +1 @@
1
+ export {};
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { isValidLanguage } from './isValidLanguage.js';
2
+ import { getIsValidLanguage } from './getIsValidLanguage.js';
3
3
  describe('isValidLanguage', () => {
4
4
  it.each(['ts', 'js'])('returns true for valid language: %s', (lang) => {
5
- expect(isValidLanguage(lang)).toBe(true);
5
+ expect(getIsValidLanguage(lang)).toBe(true);
6
6
  });
7
7
  it.each(['typescript', '', 'TS', 'javascript'])('returns false for invalid language: %s', (lang) => {
8
- expect(isValidLanguage(lang)).toBe(false);
8
+ expect(getIsValidLanguage(lang)).toBe(false);
9
9
  });
10
10
  });
@@ -0,0 +1,2 @@
1
+ import type { TPackageManager } from '../types.js';
2
+ export declare const getIsValidPackageManager: (value: unknown) => value is TPackageManager;
@@ -0,0 +1,4 @@
1
+ import { PACKAGE_MANAGERS } from '../constants.js';
2
+ export const getIsValidPackageManager = (value) => {
3
+ return typeof value === 'string' && PACKAGE_MANAGERS.includes(value);
4
+ };
@@ -0,0 +1,11 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { PACKAGE_MANAGERS } from '../constants.js';
3
+ import { getIsValidPackageManager } from './getIsValidPackageManager.js';
4
+ describe('isValidPackageManager', () => {
5
+ it.each(PACKAGE_MANAGERS)('return true if package manager is valid', (packageManager) => {
6
+ expect(getIsValidPackageManager(packageManager)).toBe(true);
7
+ });
8
+ it.each(['some-invalid-page-manager', ''])('return false if package manager is invalid', (packageManager) => {
9
+ expect(getIsValidPackageManager(packageManager)).toBe(false);
10
+ });
11
+ });
@@ -0,0 +1,2 @@
1
+ import type { TSlot } from '../types.js';
2
+ export declare const getIsValidSlot: (value: unknown) => value is TSlot;
@@ -0,0 +1,4 @@
1
+ import { SLOTS } from '../constants.js';
2
+ export const getIsValidSlot = (value) => {
3
+ return typeof value === 'string' && SLOTS.includes(value);
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getIsValidSlot } from './getIsValidSlot.js';
3
+ describe('getIsValidSlot', () => {
4
+ it.each([
5
+ 'EXTENSION_SLOT_MAIN_GAME_FUN',
6
+ 'EXTENSION_SLOT_MAIN_SEX_TOY',
7
+ 'EXTENSION_SLOT_RIGHT_OVERLAY',
8
+ 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY',
9
+ 'EXTENSION_SLOT_MOVEABLE_OVERLAY',
10
+ 'EXTENSION_SLOT_BACKGROUND',
11
+ ])('returns true for valid slot: %s', (slot) => {
12
+ expect(getIsValidSlot(slot)).toBe(true);
13
+ });
14
+ it.each([
15
+ 'mainGameFun',
16
+ 'EXTENSION_SLOT_MAIN_GAMEFUN',
17
+ 'EXTENSION_SLOT_MOVEABLE_OVERLAY ',
18
+ 'extension_slot_background',
19
+ '',
20
+ ])('returns false for invalid slot string: %s', (slot) => {
21
+ expect(getIsValidSlot(slot)).toBe(false);
22
+ });
23
+ it.each([null, undefined, 1, true, {}, []])('returns false for non-string slot value: %o', (slot) => {
24
+ expect(getIsValidSlot(slot)).toBe(false);
25
+ });
26
+ });
@@ -1,24 +1,43 @@
1
1
  import { SLOT_RULES_BY_CATEGORY } from '../constants.js';
2
+ import { getIsValidSlot } from './getIsValidSlot.js';
2
3
  const formatGroupMessage = (kind, slots) => {
3
- const prefix = kind === 'exactly-one' ? 'Choose exactly one of' : 'Choose at most one of';
4
+ let prefix = '';
5
+ switch (kind) {
6
+ case 'exactly-one':
7
+ prefix = 'Choose exactly one of';
8
+ break;
9
+ case 'at-most-one':
10
+ prefix = 'Choose at most one of';
11
+ break;
12
+ case 'required':
13
+ prefix = 'Missing required slots';
14
+ break;
15
+ }
4
16
  return `${prefix}: ${slots.join(', ')}.`;
5
17
  };
6
18
  export const validateSlots = (slots, category) => {
7
19
  const rules = SLOT_RULES_BY_CATEGORY[category];
8
20
  const allowed = new Set(rules.optional);
9
- const selected = [];
10
- const invalid = [];
21
+ const requiredBase = new Set(rules.requiredBase);
22
+ const selected = new Set();
23
+ const invalid = new Set();
11
24
  for (const slot of slots) {
25
+ if (!getIsValidSlot(slot)) {
26
+ invalid.add(slot);
27
+ continue;
28
+ }
12
29
  if (allowed.has(slot)) {
13
- selected.push(slot);
30
+ selected.add(slot);
31
+ }
32
+ else if (requiredBase.has(slot)) {
33
+ selected.add(slot);
14
34
  }
15
35
  else {
16
- invalid.push(slot);
36
+ invalid.add(slot);
17
37
  }
18
38
  }
19
- const selectedSet = new Set(selected);
20
39
  const violations = rules.groups.flatMap((group) => {
21
- const count = group.slots.filter((slot) => selectedSet.has(slot)).length;
40
+ const count = group.slots.filter((slot) => selected.has(slot)).length;
22
41
  if (group.kind === 'exactly-one' && count !== 1) {
23
42
  return [formatGroupMessage(group.kind, group.slots)];
24
43
  }
@@ -27,5 +46,18 @@ export const validateSlots = (slots, category) => {
27
46
  }
28
47
  return [];
29
48
  });
30
- return { invalid, selected, violations };
49
+ const missingRequiredSlots = [];
50
+ requiredBase.forEach((requiredSlot) => {
51
+ if (!selected.has(requiredSlot)) {
52
+ missingRequiredSlots.push(requiredSlot);
53
+ }
54
+ });
55
+ if (missingRequiredSlots.length) {
56
+ violations.unshift(formatGroupMessage('required', missingRequiredSlots));
57
+ }
58
+ return {
59
+ invalid: Array.from(invalid),
60
+ selected: Array.from(selected),
61
+ violations,
62
+ };
31
63
  };