@stripchatdev/create-stripchat-extension 0.1.46 → 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,5 @@
1
+ import type { TManagedFile } from '../templates/types';
2
+ import type { TProjectConfig } from '../types';
3
+ import type { TMigrationExecutionPlan, TMigrationPlan } from './types';
4
+ export declare const planMigrations: (targetManagedFiles: TManagedFile[], managedFiles: TManagedFile[], lastTemplateVersion: number) => TMigrationPlan[];
5
+ export declare const planMigrationExecution: (targetProjectConfig: TProjectConfig, templateVersion: number) => TMigrationExecutionPlan;
@@ -0,0 +1,65 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { updateProjectConfig } from '../projectConfig/updateProjectConfig.js';
4
+ import { INIT_TEMPLATE_VERSION } from '../templates/constants.js';
5
+ import { getTemplateMigrationsDir } from './migration.js';
6
+ const discoverMigrationsByVersion = (type, relPath, version) => {
7
+ const migrationsDir = getTemplateMigrationsDir(type, version, relPath);
8
+ if (!existsSync(migrationsDir)) {
9
+ return [];
10
+ }
11
+ const entries = readdirSync(migrationsDir, { withFileTypes: true });
12
+ return entries
13
+ .filter((entry) => {
14
+ return entry.isFile() && !entry.name.endsWith('.d.ts');
15
+ })
16
+ .map((entry) => {
17
+ const scriptPath = join(entry.parentPath, entry.name);
18
+ const migrationPlan = {
19
+ scriptPath,
20
+ templateRelPath: relPath,
21
+ templateVersion: version,
22
+ };
23
+ return migrationPlan;
24
+ })
25
+ .sort((a, b) => {
26
+ return a.scriptPath.localeCompare(b.scriptPath);
27
+ });
28
+ };
29
+ const discoverMigrationsForTemplate = (templateFileRelPath, fromVersion, toVersion) => {
30
+ const templateMigrations = [];
31
+ let iterVersion = fromVersion;
32
+ while (iterVersion <= toVersion) {
33
+ if (iterVersion > fromVersion) {
34
+ const versionMigrations = discoverMigrationsByVersion('migrations', templateFileRelPath, iterVersion);
35
+ templateMigrations.push(...versionMigrations);
36
+ }
37
+ const versionUpdates = discoverMigrationsByVersion('updates', templateFileRelPath, iterVersion);
38
+ templateMigrations.push(...versionUpdates);
39
+ iterVersion++;
40
+ }
41
+ return templateMigrations;
42
+ };
43
+ export const planMigrations = (targetManagedFiles, managedFiles, lastTemplateVersion) => {
44
+ const targetManagedFilesMap = {};
45
+ targetManagedFiles.forEach((file) => {
46
+ targetManagedFilesMap[file.relPath] = file;
47
+ });
48
+ const allMigrations = [];
49
+ for (const managedFile of managedFiles) {
50
+ const targetManagedFile = targetManagedFilesMap[managedFile.relPath];
51
+ const targetVersion = targetManagedFile ? targetManagedFile.version : INIT_TEMPLATE_VERSION;
52
+ const templateMigrations = discoverMigrationsForTemplate(managedFile.relPath, targetVersion, lastTemplateVersion);
53
+ allMigrations.push(...templateMigrations);
54
+ }
55
+ return allMigrations;
56
+ };
57
+ export const planMigrationExecution = (targetProjectConfig, templateVersion) => {
58
+ const updatedProjectConfig = updateProjectConfig(templateVersion, targetProjectConfig);
59
+ const migrationPlans = planMigrations(targetProjectConfig.managedFiles, updatedProjectConfig.managedFiles, updatedProjectConfig.lastTemplateVersion);
60
+ return {
61
+ migrationPlans,
62
+ targetProjectConfig,
63
+ updatedProjectConfig,
64
+ };
65
+ };
@@ -0,0 +1,40 @@
1
+ import { dirname, relative } from 'node:path';
2
+ import { normalizePath } from 'vite';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { INIT_TEMPLATE_VERSION, MANAGED_TEMPLATES, } from '../templates/constants.js';
5
+ import { collectManagedFiles, LAST_TEMPLATE_VERSION, TEMPLATES_DIR } from '../templates/templates.js';
6
+ import { planMigrations } from './planner.js';
7
+ const buildTestCases = () => {
8
+ const testCases = [];
9
+ let fromVersion = INIT_TEMPLATE_VERSION;
10
+ const lastVersion = LAST_TEMPLATE_VERSION;
11
+ while (fromVersion <= lastVersion) {
12
+ const testCase = {
13
+ fromVersion,
14
+ lastVersion,
15
+ targetManagedFiles: collectManagedFiles(TEMPLATES_DIR, MANAGED_TEMPLATES, fromVersion),
16
+ managedFiles: collectManagedFiles(TEMPLATES_DIR, MANAGED_TEMPLATES, lastVersion),
17
+ };
18
+ testCases.push(testCase);
19
+ fromVersion++;
20
+ }
21
+ return testCases;
22
+ };
23
+ const normalizeMigrationPlans = (migrationPlans) => {
24
+ return migrationPlans.map((migrationPlan) => {
25
+ const scriptPathResl = normalizePath(relative(dirname(TEMPLATES_DIR), migrationPlan.scriptPath));
26
+ return {
27
+ type: 'script',
28
+ scriptPath: scriptPathResl,
29
+ templateRelPath: normalizePath(migrationPlan.templateRelPath),
30
+ templateVersion: migrationPlan.templateVersion,
31
+ };
32
+ });
33
+ };
34
+ const TEST_CASES = buildTestCases();
35
+ describe('planMigrations', { tags: ['snapshot'] }, () => {
36
+ it.each(TEST_CASES)('from version $fromVersion to last version $lastVersion', ({ lastVersion, managedFiles, targetManagedFiles }) => {
37
+ const migrationPlans = planMigrations(targetManagedFiles, managedFiles, lastVersion);
38
+ expect(normalizeMigrationPlans(migrationPlans)).toMatchSnapshot();
39
+ });
40
+ });
@@ -0,0 +1,29 @@
1
+ import type { TProjectConfig } from '../types.js';
2
+ export type TMigrationContext = {
3
+ generatedProjectDir: string;
4
+ projectDir: string;
5
+ targetProjectConfig: TProjectConfig;
6
+ templateVersion: number;
7
+ updatedProjectConfig: TProjectConfig;
8
+ };
9
+ export type TMigrationChange = {
10
+ filePath: string;
11
+ relFilePath: string;
12
+ };
13
+ export type TMigrationPlan = {
14
+ scriptPath: string;
15
+ templateRelPath: string;
16
+ templateVersion: number;
17
+ };
18
+ export type TMigrationExecutionPlan = {
19
+ migrationPlans: TMigrationPlan[];
20
+ targetProjectConfig: TProjectConfig;
21
+ updatedProjectConfig: TProjectConfig;
22
+ };
23
+ export type TMigration = {
24
+ up(ctx: TMigrationContext): TMigrationChange[];
25
+ };
26
+ export type TMigrationDiff = {
27
+ diff: string;
28
+ filePath: string;
29
+ };
@@ -0,0 +1,102 @@
1
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { CLI_COMMANDS_MAP, EXT_HELPER_VERSION } from '../../constants.js';
6
+ import { toNormalizedRelPath } from '../../helpers/normalizePath.js';
7
+ import { buildProjectConfig } from '../../projectConfig/buildProjectConfig.js';
8
+ import { executeCommands } from '../../templater/executor.js';
9
+ import { mapProjectConfigToTemplateData } from '../../templater/mappers/mapProjectConfigToTemplateModel.js';
10
+ import { planProject } from '../../templater/planner.js';
11
+ import { MANAGED_TEMPLATES } from '../../templates/constants.js';
12
+ import { collectManagedFiles, discoverVersionDirs, LAST_TEMPLATE_VERSION, TEMPLATES_DIR, } from '../../templates/templates.js';
13
+ import { executeMigrations } from '../executor.js';
14
+ import { planMigrationExecution } from '../planner.js';
15
+ const testBuildProjectConfigOptions = {
16
+ category: 'games-and-fun',
17
+ framework: 'preact',
18
+ lastTemplateVersion: LAST_TEMPLATE_VERSION,
19
+ cliCommands: {
20
+ cli: 'old-cli-command',
21
+ 'cli:update': 'old-update-command',
22
+ },
23
+ managedTemplates: MANAGED_TEMPLATES,
24
+ templatesDir: TEMPLATES_DIR,
25
+ name: 'test',
26
+ slots: ['EXTENSION_SLOT_MAIN_GAME_FUN', 'EXTENSION_SLOT_BACKGROUND'],
27
+ extHelperVersion: EXT_HELPER_VERSION,
28
+ language: 'ts',
29
+ packageManager: 'npm',
30
+ };
31
+ const compareManagedFiles = (targetProjectDir, referenceProjectDir, templateVersion, migrationPlans) => {
32
+ const managedFiles = collectManagedFiles(TEMPLATES_DIR, MANAGED_TEMPLATES, templateVersion);
33
+ const diffFileRelPaths = [];
34
+ const migrationPlansFilesSet = new Set();
35
+ migrationPlans.forEach((migrationPlan) => {
36
+ migrationPlansFilesSet.add(migrationPlan.templateRelPath);
37
+ });
38
+ for (const managedFile of managedFiles) {
39
+ if (!migrationPlansFilesSet.has(managedFile.relPath)) {
40
+ continue;
41
+ }
42
+ const targetFileContent = readFileSync(join(targetProjectDir, managedFile.relPath), 'utf8');
43
+ const referenceFileContent = readFileSync(join(referenceProjectDir, managedFile.relPath), 'utf8');
44
+ if (targetFileContent !== referenceFileContent) {
45
+ diffFileRelPaths.push(managedFile.relPath);
46
+ }
47
+ }
48
+ return diffFileRelPaths;
49
+ };
50
+ describe('updates', () => {
51
+ const versionDirs = discoverVersionDirs(TEMPLATES_DIR, LAST_TEMPLATE_VERSION, 'asc');
52
+ for (const versionDir of versionDirs) {
53
+ const templateVersion = versionDir.version;
54
+ it(`runs updates for v${templateVersion}`, async () => {
55
+ const targetProjectDir = mkdtempSync(join(tmpdir(), 'sce-test-target-project-dir'));
56
+ const referenceProjectDir = mkdtempSync(join(tmpdir(), 'sce-test-reference-project-dir'));
57
+ try {
58
+ const targetProjectConfig = buildProjectConfig({
59
+ ...testBuildProjectConfigOptions,
60
+ lastTemplateVersion: templateVersion,
61
+ });
62
+ const targetTemplateData = mapProjectConfigToTemplateData(targetProjectConfig);
63
+ const targetCommands = planProject(targetProjectDir, templateVersion, targetTemplateData);
64
+ executeCommands(targetCommands);
65
+ const referenceProjectConfig = buildProjectConfig({
66
+ ...testBuildProjectConfigOptions,
67
+ cliCommands: CLI_COMMANDS_MAP,
68
+ lastTemplateVersion: templateVersion,
69
+ });
70
+ const referenceTemplateData = mapProjectConfigToTemplateData({
71
+ ...referenceProjectConfig,
72
+ lastTemplateVersion: templateVersion,
73
+ });
74
+ const referenceCommands = planProject(referenceProjectDir, templateVersion, referenceTemplateData);
75
+ executeCommands(referenceCommands);
76
+ const executionPlan = planMigrationExecution(targetProjectConfig, templateVersion);
77
+ await executeMigrations({
78
+ projectDir: targetProjectDir,
79
+ executionPlan,
80
+ isDryRun: false,
81
+ });
82
+ const isOnlyUpdates = executionPlan.migrationPlans.every((migrationPlan) => {
83
+ return toNormalizedRelPath(migrationPlan.scriptPath).includes('/updates/');
84
+ });
85
+ expect(isOnlyUpdates).toBe(true);
86
+ const result = compareManagedFiles(targetProjectDir, referenceProjectDir, templateVersion, executionPlan.migrationPlans);
87
+ expect(result).toEqual([]);
88
+ const idempotentExecutionPlan = planMigrationExecution(targetProjectConfig, templateVersion);
89
+ const idempotentResult = await executeMigrations({
90
+ projectDir: targetProjectDir,
91
+ executionPlan: idempotentExecutionPlan,
92
+ isDryRun: false,
93
+ });
94
+ expect(idempotentResult).toEqual([]);
95
+ }
96
+ finally {
97
+ rmSync(targetProjectDir, { recursive: true, force: true });
98
+ rmSync(referenceProjectDir, { recursive: true, force: true });
99
+ }
100
+ });
101
+ }
102
+ });
@@ -0,0 +1,2 @@
1
+ import type { TMigrationChange, TMigrationContext } from '../../../types.js';
2
+ export declare const up: (ctx: TMigrationContext) => TMigrationChange[];
@@ -0,0 +1,38 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { AGENTS_MD_REL_PATH, AGENTS_TEMPLATE_END, AGENTS_TEMPLATE_START } from '../../../../constants.js';
4
+ const agentsReplaceRegexp = new RegExp(`${AGENTS_TEMPLATE_START}[\\s\\S]*${AGENTS_TEMPLATE_END}`);
5
+ export const up = (ctx) => {
6
+ const targetAgentsFilePath = join(ctx.projectDir, AGENTS_MD_REL_PATH);
7
+ const generatedAgentsPath = join(ctx.generatedProjectDir, AGENTS_MD_REL_PATH);
8
+ const generatedAgentsContent = readFileSync(generatedAgentsPath, 'utf8');
9
+ if (!existsSync(targetAgentsFilePath)) {
10
+ mkdirSync(dirname(targetAgentsFilePath), { recursive: true });
11
+ copyFileSync(generatedAgentsPath, targetAgentsFilePath);
12
+ return [
13
+ {
14
+ filePath: targetAgentsFilePath,
15
+ relFilePath: AGENTS_MD_REL_PATH,
16
+ },
17
+ ];
18
+ }
19
+ const targetAgentsContent = readFileSync(targetAgentsFilePath, 'utf8');
20
+ const generatedTrimmedContent = generatedAgentsContent.trim();
21
+ const hasGeneratedMarkers = agentsReplaceRegexp.test(targetAgentsContent);
22
+ const newAgentsContent = hasGeneratedMarkers
23
+ ? targetAgentsContent.replace(agentsReplaceRegexp, generatedTrimmedContent)
24
+ : `${generatedTrimmedContent}\n\n${targetAgentsContent}`;
25
+ if (newAgentsContent === targetAgentsContent) {
26
+ return [];
27
+ }
28
+ writeFileSync(targetAgentsFilePath, newAgentsContent, {
29
+ encoding: 'utf8',
30
+ flag: 'w',
31
+ });
32
+ return [
33
+ {
34
+ filePath: targetAgentsFilePath,
35
+ relFilePath: AGENTS_MD_REL_PATH,
36
+ },
37
+ ];
38
+ };
@@ -0,0 +1,2 @@
1
+ import type { TMigrationChange, TMigrationContext } from '../../../types.js';
2
+ export declare const up: (ctx: TMigrationContext) => TMigrationChange[];
@@ -0,0 +1,77 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { PACKAGE_JSON_REL_PATH } from '../../../../constants.js';
4
+ import { AppError } from '../../../../helpers/error.js';
5
+ const resolvePackageJson = (packageJsonPath) => {
6
+ const packageJsonContent = readFileSync(packageJsonPath, 'utf8');
7
+ let packageJson;
8
+ try {
9
+ packageJson = JSON.parse(packageJsonContent);
10
+ }
11
+ catch {
12
+ throw new AppError(`Failed to parse package json: ${packageJsonPath}`);
13
+ }
14
+ return {
15
+ packageJson,
16
+ packageJsonContent,
17
+ };
18
+ };
19
+ const updateDependencies = (packageJson, newProjectConfig) => {
20
+ const { dependencies, devDependencies } = newProjectConfig;
21
+ const newPackageJson = structuredClone(packageJson);
22
+ if (!newPackageJson.dependencies) {
23
+ newPackageJson.dependencies = {};
24
+ }
25
+ for (const packageName in dependencies) {
26
+ const packageVersion = dependencies[packageName];
27
+ if (!packageVersion) {
28
+ continue;
29
+ }
30
+ if (newPackageJson.dependencies[packageName] !== packageVersion) {
31
+ newPackageJson.dependencies[packageName] = packageVersion;
32
+ }
33
+ }
34
+ if (!newPackageJson.devDependencies) {
35
+ newPackageJson.devDependencies = {};
36
+ }
37
+ for (const packageName in devDependencies) {
38
+ const packageVersion = devDependencies[packageName];
39
+ if (!packageVersion) {
40
+ continue;
41
+ }
42
+ if (newPackageJson.devDependencies[packageName] !== packageVersion) {
43
+ newPackageJson.devDependencies[packageName] = packageVersion;
44
+ }
45
+ }
46
+ return newPackageJson;
47
+ };
48
+ export const up = (ctx) => {
49
+ const targetPackageJsonPath = join(ctx.projectDir, PACKAGE_JSON_REL_PATH);
50
+ const generatedPackageJsonPath = join(ctx.generatedProjectDir, PACKAGE_JSON_REL_PATH);
51
+ if (!existsSync(targetPackageJsonPath)) {
52
+ mkdirSync(dirname(targetPackageJsonPath), { recursive: true });
53
+ copyFileSync(generatedPackageJsonPath, targetPackageJsonPath);
54
+ return [
55
+ {
56
+ filePath: targetPackageJsonPath,
57
+ relFilePath: PACKAGE_JSON_REL_PATH,
58
+ },
59
+ ];
60
+ }
61
+ const { packageJson, packageJsonContent } = resolvePackageJson(targetPackageJsonPath);
62
+ const updatedPackageJson = updateDependencies(packageJson, ctx.updatedProjectConfig);
63
+ const newPackageJsonContent = `${JSON.stringify(updatedPackageJson, null, 2)}\n`;
64
+ if (newPackageJsonContent === packageJsonContent) {
65
+ return [];
66
+ }
67
+ writeFileSync(targetPackageJsonPath, newPackageJsonContent, {
68
+ encoding: 'utf8',
69
+ flag: 'w',
70
+ });
71
+ return [
72
+ {
73
+ filePath: targetPackageJsonPath,
74
+ relFilePath: PACKAGE_JSON_REL_PATH,
75
+ },
76
+ ];
77
+ };
@@ -0,0 +1,2 @@
1
+ import type { TMigrationChange, TMigrationContext } from '../../../types.js';
2
+ export declare const up: (ctx: TMigrationContext) => TMigrationChange[];
@@ -0,0 +1,61 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { PACKAGE_JSON_REL_PATH } from '../../../../constants.js';
4
+ import { AppError } from '../../../../helpers/error.js';
5
+ const resolvePackageJson = (packageJsonPath) => {
6
+ const packageJsonContent = readFileSync(packageJsonPath, 'utf8');
7
+ let packageJson;
8
+ try {
9
+ packageJson = JSON.parse(packageJsonContent);
10
+ }
11
+ catch {
12
+ throw new AppError(`Failed to parse package json: ${packageJsonPath}`);
13
+ }
14
+ return {
15
+ packageJson,
16
+ packageJsonContent,
17
+ };
18
+ };
19
+ const updateCliCommands = (packageJson, projectConfig, newProjectConfig) => {
20
+ const newPackageJson = structuredClone(packageJson);
21
+ if (!newPackageJson.scripts) {
22
+ throw new AppError('Section scripts in package.json not found');
23
+ }
24
+ for (const commandName in projectConfig.cliCommands) {
25
+ delete newPackageJson.scripts[commandName];
26
+ }
27
+ for (const commandName in newProjectConfig.cliCommands) {
28
+ newPackageJson.scripts[commandName] = newProjectConfig.cliCommands[commandName];
29
+ }
30
+ return newPackageJson;
31
+ };
32
+ export const up = (ctx) => {
33
+ const targetPackageJsonPath = join(ctx.projectDir, PACKAGE_JSON_REL_PATH);
34
+ const generatedPackageJsonPath = join(ctx.generatedProjectDir, PACKAGE_JSON_REL_PATH);
35
+ if (!existsSync(targetPackageJsonPath)) {
36
+ mkdirSync(dirname(targetPackageJsonPath), { recursive: true });
37
+ copyFileSync(generatedPackageJsonPath, targetPackageJsonPath);
38
+ return [
39
+ {
40
+ filePath: targetPackageJsonPath,
41
+ relFilePath: PACKAGE_JSON_REL_PATH,
42
+ },
43
+ ];
44
+ }
45
+ const { packageJson, packageJsonContent } = resolvePackageJson(targetPackageJsonPath);
46
+ const newPackageJson = updateCliCommands(packageJson, ctx.targetProjectConfig, ctx.updatedProjectConfig);
47
+ const newPackageJsonContent = `${JSON.stringify(newPackageJson, null, 2)}\n`;
48
+ if (newPackageJsonContent === packageJsonContent) {
49
+ return [];
50
+ }
51
+ writeFileSync(targetPackageJsonPath, newPackageJsonContent, {
52
+ encoding: 'utf8',
53
+ flag: 'w',
54
+ });
55
+ return [
56
+ {
57
+ filePath: targetPackageJsonPath,
58
+ relFilePath: PACKAGE_JSON_REL_PATH,
59
+ },
60
+ ];
61
+ };
@@ -0,0 +1,2 @@
1
+ import type { TMigration } from '../types';
2
+ export declare const getIsValidMigration: (value: unknown) => value is TMigration;
@@ -0,0 +1,3 @@
1
+ export const getIsValidMigration = (value) => {
2
+ return typeof value === 'object' && value !== null && 'up' in value && typeof value.up === 'function';
3
+ };
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getIsValidMigration } from './getIsValidMigration.js';
3
+ describe('getIsValidMigration', () => {
4
+ it('returns true for migration-like object with up function', () => {
5
+ const value = {
6
+ up: () => [],
7
+ };
8
+ expect(getIsValidMigration(value)).toBe(true);
9
+ });
10
+ it.each([null, undefined, 42, 'migration', true, {}, { up: 1 }, { up: 'fn' }, { up: null }])('returns false for invalid migration shape: %p', (value) => {
11
+ expect(getIsValidMigration(value)).toBe(false);
12
+ });
13
+ });
@@ -0,0 +1,15 @@
1
+ import type { TCategory, TCliCommandsMap, TFramework, TLanguage, TPackageManager, TProjectConfig, TRelPath, TSlot } from '../types.js';
2
+ export type TBuildProjectConfigOptions = {
3
+ category: TCategory;
4
+ cliCommands: TCliCommandsMap;
5
+ extHelperVersion: string;
6
+ framework: TFramework;
7
+ language: TLanguage;
8
+ lastTemplateVersion: number;
9
+ managedTemplates: TRelPath[];
10
+ name: string;
11
+ packageManager: TPackageManager;
12
+ slots: TSlot[];
13
+ templatesDir: string;
14
+ };
15
+ export declare const buildProjectConfig: (options: TBuildProjectConfigOptions) => TProjectConfig;
@@ -0,0 +1,27 @@
1
+ import { resolveDependencies } from '../dependencies/resolveDependencies.js';
2
+ import { toNormalizedRelPath } from '../helpers/normalizePath.js';
3
+ import { collectManagedFiles, TEMPLATES_DIR } from '../templates/templates.js';
4
+ export const buildProjectConfig = (options) => {
5
+ const { category, cliCommands, extHelperVersion, framework, language, lastTemplateVersion, managedTemplates, name, packageManager, slots, } = options;
6
+ const { dependencies, devDependencies } = resolveDependencies(language, framework);
7
+ const managedFiles = collectManagedFiles(TEMPLATES_DIR, managedTemplates, lastTemplateVersion).map((file) => {
8
+ return {
9
+ ...file,
10
+ relPath: toNormalizedRelPath(file.relPath),
11
+ };
12
+ });
13
+ return {
14
+ lastTemplateVersion,
15
+ name,
16
+ category,
17
+ language,
18
+ framework,
19
+ packageManager,
20
+ dependencies,
21
+ devDependencies,
22
+ slots,
23
+ cliCommands,
24
+ managedFiles: managedFiles,
25
+ extHelperVersion,
26
+ };
27
+ };
@@ -0,0 +1,2 @@
1
+ import type { TPackageManager } from '../types.js';
2
+ export declare const detectPackageManagerRuntime: () => TPackageManager;
@@ -1,4 +1,4 @@
1
- export const detectPackageManager = () => {
1
+ export const detectPackageManagerRuntime = () => {
2
2
  if ('Bun' in globalThis || process?.versions?.bun)
3
3
  return 'bun';
4
4
  const ua = process.env.npm_config_user_agent;
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable camelcase */
2
2
  import { afterEach, describe, expect, it } from 'vitest';
3
- import { detectPackageManager } from './detectPackageManager.js';
3
+ import { detectPackageManagerRuntime } from './detectPackageManagerRuntime.js';
4
4
  describe('detectPackageManager', () => {
5
5
  const originalEnv = process.env.npm_config_user_agent;
6
6
  afterEach(() => {
@@ -13,41 +13,41 @@ describe('detectPackageManager', () => {
13
13
  // @ts-expect-error -- simulating Bun runtime
14
14
  globalThis.Bun = {};
15
15
  process.env.npm_config_user_agent = 'pnpm/9.0.0';
16
- expect(detectPackageManager()).toBe('bun');
16
+ expect(detectPackageManagerRuntime()).toBe('bun');
17
17
  });
18
18
  it('returns bun when user agent contains bun', () => {
19
19
  process.env.npm_config_user_agent = 'bun/1.2.0 node/v22.12.0';
20
- expect(detectPackageManager()).toBe('bun');
20
+ expect(detectPackageManagerRuntime()).toBe('bun');
21
21
  });
22
22
  it('returns pnpm when user agent contains pnpm', () => {
23
23
  process.env.npm_config_user_agent = 'pnpm/9.0.0 node/v22.12.0';
24
- expect(detectPackageManager()).toBe('pnpm');
24
+ expect(detectPackageManagerRuntime()).toBe('pnpm');
25
25
  });
26
26
  it('returns yarn when user agent contains yarn', () => {
27
27
  process.env.npm_config_user_agent = 'yarn/4.1.0 node/v22.12.0';
28
- expect(detectPackageManager()).toBe('yarn');
28
+ expect(detectPackageManagerRuntime()).toBe('yarn');
29
29
  });
30
30
  it('returns npm when user agent contains npm', () => {
31
31
  process.env.npm_config_user_agent = 'npm/10.9.0 node/v22.12.0';
32
- expect(detectPackageManager()).toBe('npm');
32
+ expect(detectPackageManagerRuntime()).toBe('npm');
33
33
  });
34
34
  it('returns npm when user agent is undefined', () => {
35
35
  delete process.env.npm_config_user_agent;
36
- expect(detectPackageManager()).toBe('npm');
36
+ expect(detectPackageManagerRuntime()).toBe('npm');
37
37
  });
38
38
  it('prioritises bun over user agent', () => {
39
39
  // @ts-expect-error -- simulating Bun runtime
40
40
  globalThis.Bun = {};
41
41
  process.env.npm_config_user_agent = 'yarn/4.1.0';
42
- expect(detectPackageManager()).toBe('bun');
42
+ expect(detectPackageManagerRuntime()).toBe('bun');
43
43
  });
44
44
  it('prioritises pnpm over yarn in user agent', () => {
45
45
  process.env.npm_config_user_agent = 'pnpm/9.0.0 yarn/4.1.0';
46
- expect(detectPackageManager()).toBe('pnpm');
46
+ expect(detectPackageManagerRuntime()).toBe('pnpm');
47
47
  });
48
48
  it('returns bun when process.versions.bun is set', () => {
49
49
  process.versions.bun = '1.0.0';
50
50
  process.env.npm_config_user_agent = 'yarn/4.1.0';
51
- expect(detectPackageManager()).toBe('bun');
51
+ expect(detectPackageManagerRuntime()).toBe('bun');
52
52
  });
53
53
  });
@@ -0,0 +1 @@
1
+ export declare const recoverProjectConfig: (projectDir: string) => import("../types.js").TProjectConfig;
@@ -0,0 +1,34 @@
1
+ import { CLI_COMMANDS_MAP, EXT_HELPER_VERSION } from '../constants.js';
2
+ import { AppError } from '../helpers/error.js';
3
+ import { INIT_TEMPLATE_VERSION, MANAGED_TEMPLATES } from '../templates/constants.js';
4
+ import { TEMPLATES_DIR } from '../templates/templates.js';
5
+ import { buildProjectConfig } from './buildProjectConfig.js';
6
+ import { detectPackageManagerRuntime } from './detectPackageManagerRuntime.js';
7
+ import { detectPackageManagerFromFiles } from './recovery/detectPackageManagerFromFiles.js';
8
+ import { parseAgentsMd } from './recovery/parseAgentsMd.js';
9
+ import { resolveProjectNameFromDir } from './recovery/resolveProjectNameFromDir.js';
10
+ export const recoverProjectConfig = (projectDir) => {
11
+ const lastTemplateVersion = INIT_TEMPLATE_VERSION; // suppose version is 1 when we did not have a project config file in our template
12
+ const name = resolveProjectNameFromDir(projectDir);
13
+ const { category, framework, language, slots } = parseAgentsMd(projectDir, lastTemplateVersion);
14
+ let packageManager = detectPackageManagerFromFiles(projectDir);
15
+ if (!packageManager) {
16
+ packageManager = detectPackageManagerRuntime();
17
+ }
18
+ if (!category || !framework || !language || !slots) {
19
+ throw new AppError('Unable to recover project config, some information is missing');
20
+ }
21
+ return buildProjectConfig({
22
+ name,
23
+ category,
24
+ slots,
25
+ language,
26
+ framework,
27
+ packageManager,
28
+ extHelperVersion: EXT_HELPER_VERSION,
29
+ lastTemplateVersion,
30
+ cliCommands: CLI_COMMANDS_MAP,
31
+ managedTemplates: MANAGED_TEMPLATES,
32
+ templatesDir: TEMPLATES_DIR,
33
+ });
34
+ };
@@ -0,0 +1,2 @@
1
+ import type { TPackageManager } from '../../types.js';
2
+ export declare const detectPackageManagerFromFiles: (projectDir: string) => TPackageManager | null;
@@ -0,0 +1,16 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { PACKAGE_MANAGER_LOCK_FILE_MAP } from '../../constants.js';
4
+ import { getIsValidPackageManager } from '../../validators/getIsValidPackageManager.js';
5
+ export const detectPackageManagerFromFiles = (projectDir) => {
6
+ for (const packageManager in PACKAGE_MANAGER_LOCK_FILE_MAP) {
7
+ if (!getIsValidPackageManager(packageManager)) {
8
+ continue;
9
+ }
10
+ const lockFile = PACKAGE_MANAGER_LOCK_FILE_MAP[packageManager];
11
+ if (lockFile && existsSync(join(projectDir, lockFile))) {
12
+ return packageManager;
13
+ }
14
+ }
15
+ return null;
16
+ };