@stripchatdev/create-stripchat-extension 0.1.48 → 0.2.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 (31) hide show
  1. package/dist/bin/create.js +21 -5
  2. package/dist/package.json +1 -1
  3. package/dist/src/helpers/hasUI.d.ts +2 -0
  4. package/dist/src/helpers/hasUI.js +1 -0
  5. package/dist/src/helpers/hasUI.test.d.ts +1 -0
  6. package/dist/src/helpers/hasUI.test.js +15 -0
  7. package/dist/src/projectConfig/buildProjectConfig.d.ts +1 -0
  8. package/dist/src/projectConfig/buildProjectConfig.js +2 -1
  9. package/dist/src/projectConfig/updateProjectConfig.js +2 -1
  10. package/dist/src/projectConfig/validators/getIsValidProjectConfig.test.js +1 -0
  11. package/dist/src/templater/executor.test.js +39 -2
  12. package/dist/src/templater/mappers/mapProjectConfigToTemplateModel.test.js +35 -6
  13. package/dist/src/templater/planner.js +8 -5
  14. package/dist/src/templater/planner.test.js +51 -3
  15. package/dist/src/types.d.ts +2 -0
  16. package/dist/tsconfig.tsbuildinfo +1 -1
  17. package/package.json +1 -1
  18. package/templates/v1/AGENTS.md.ejs +59 -133
  19. package/templates/v1/README.md.ejs +6 -1
  20. package/templates/v1/package.json.ejs +1 -1
  21. package/templates/v1/public/manifest.json.ejs +3 -0
  22. package/templates/v1/src/slots/background/app.ts.ejs +4 -4
  23. package/templates/v1/src/slots/settings/app.ts.ejs +84 -0
  24. package/templates/v1/src/slots/settings/app.tsx.ejs +112 -0
  25. package/templates/v1/src/slots/settings/main.css.ejs +117 -0
  26. package/templates/v1/src/slots/settings/main.ts.ejs +3 -0
  27. package/templates/v1/src/slots/settings/main.tsx.ejs +9 -0
  28. package/templates/v1/src/slots/settings/settings.html.ejs +30 -0
  29. package/templates/v1/vite.config.ts.ejs +3 -0
  30. package/templates/v2/AGENTS.md.ejs +61 -18
  31. package/templates/v2/vite.config.ts.ejs +3 -0
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { checkbox, input, select } from '@inquirer/prompts';
2
+ import { checkbox, confirm, input, select } from '@inquirer/prompts';
3
3
  import { existsSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
5
  import { parseArgs } from 'node:util';
6
6
  import { CATEGORIES, CATEGORY_LABELS, FRAMEWORK_LABELS, FRAMEWORKS, LANGUAGE_LABELS, LANGUAGES, SLOT_RULES_BY_CATEGORY, SLOT_LABELS, EXT_HELPER_VERSION, CLI_COMMANDS_MAP, VERSION, } from '../src/constants.js';
7
7
  import { AppError } from '../src/helpers/error.js';
8
+ import { hasUi } from '../src/helpers/hasUI.js';
8
9
  import { buildProjectConfig } from '../src/projectConfig/buildProjectConfig.js';
9
10
  import { detectPackageManagerRuntime } from '../src/projectConfig/detectPackageManagerRuntime.js';
10
11
  import { executeCommands } from '../src/templater/executor.js';
@@ -26,6 +27,7 @@ Options:
26
27
  --name <name> Project name (used as a directory and package name)
27
28
  --category <category> Extension category (${CATEGORIES.join(', ')})
28
29
  --slots <items> Comma-separated slots (e.g. "EXTENSION_SLOT_MAIN_GAME_FUN,EXTENSION_SLOT_RIGHT_OVERLAY") or empty string
30
+ --settings <boolean> Include a model-facing settings page (exactly true or false)
29
31
  --framework <framework> UI framework (${FRAMEWORKS.join(', ')})
30
32
  --language <lang> Language (${LANGUAGES.join(', ')})
31
33
  -h, --help Display this help message
@@ -41,6 +43,7 @@ const parseCliArgs = () => {
41
43
  help: { short: 'h', type: 'boolean' },
42
44
  language: { type: 'string' },
43
45
  name: { type: 'string' },
46
+ settings: { type: 'string' },
44
47
  slots: { type: 'string' },
45
48
  version: { short: 'v', type: 'boolean' },
46
49
  templateVersion: { short: 't', type: 'string' },
@@ -150,10 +153,19 @@ const resolveSlots = async (flagValue, category) => {
150
153
  });
151
154
  return [...requiredSlots, ...selectedSlots];
152
155
  };
153
- const resolveFramework = (flagValue, category) => {
154
- if (category === 'chat-bots') {
155
- return Promise.resolve('vanilla');
156
+ const resolveSettings = (flagValue) => {
157
+ if (flagValue !== undefined) {
158
+ if (flagValue !== 'true' && flagValue !== 'false') {
159
+ throw new AppError(`Invalid settings value "${flagValue}". Choose one of: true, false`);
160
+ }
161
+ return Promise.resolve(flagValue === 'true');
156
162
  }
163
+ return confirm({
164
+ default: false,
165
+ message: 'Include a settings page?',
166
+ });
167
+ };
168
+ const resolveFramework = (flagValue) => {
157
169
  if (flagValue) {
158
170
  if (!getIsValidFramework(flagValue)) {
159
171
  throw new AppError(`Invalid framework "${flagValue}". Choose one of: ${FRAMEWORKS.join(', ')}`);
@@ -210,7 +222,10 @@ const main = async () => {
210
222
  const name = await resolveName(opts.name);
211
223
  const category = await resolveCategory(opts.category);
212
224
  const slots = await resolveSlots(opts.slots, category);
213
- const framework = await resolveFramework(opts.framework, category);
225
+ const hasSettings = await resolveSettings(opts.settings);
226
+ const framework = hasUi(hasSettings, slots)
227
+ ? await resolveFramework(opts.framework)
228
+ : 'vanilla';
214
229
  const language = await resolveLanguage(opts.language);
215
230
  const projectDir = resolveProjectDir(name);
216
231
  const templateVersion = resolveTemplateVersion(opts.templateVersion);
@@ -220,6 +235,7 @@ const main = async () => {
220
235
  category,
221
236
  slots,
222
237
  framework,
238
+ hasSettings,
223
239
  language,
224
240
  packageManager: detectPackageManagerRuntime(),
225
241
  extHelperVersion: EXT_HELPER_VERSION,
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stripchatdev/create-stripchat-extension",
3
- "version": "0.1.48",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -0,0 +1,2 @@
1
+ import type { TSlot } from '../types';
2
+ export declare const hasUi: (hasSettings: boolean, slots: readonly TSlot[]) => boolean;
@@ -0,0 +1 @@
1
+ export const hasUi = (hasSettings, slots) => hasSettings || slots.some((slot) => slot !== 'EXTENSION_SLOT_BACKGROUND');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { hasUi } from './hasUI.js';
3
+ describe('settings configuration', () => {
4
+ it.each([
5
+ [false, ['EXTENSION_SLOT_BACKGROUND'], false],
6
+ [
7
+ false,
8
+ ['EXTENSION_SLOT_BACKGROUND', 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY'],
9
+ true,
10
+ ],
11
+ [true, ['EXTENSION_SLOT_BACKGROUND'], true],
12
+ ])('detects UI from settings and non-background slots', (hasSettings, slots, expected) => {
13
+ expect(hasUi(hasSettings, slots)).toBe(expected);
14
+ });
15
+ });
@@ -4,6 +4,7 @@ export type TBuildProjectConfigOptions = {
4
4
  cliCommands: TCliCommandsMap;
5
5
  extHelperVersion: string;
6
6
  framework: TFramework;
7
+ hasSettings?: boolean;
7
8
  language: TLanguage;
8
9
  lastTemplateVersion: number;
9
10
  managedTemplates: TRelPath[];
@@ -2,7 +2,7 @@ import { resolveDependencies } from '../dependencies/resolveDependencies.js';
2
2
  import { toNormalizedRelPath } from '../helpers/normalizePath.js';
3
3
  import { collectManagedFiles, TEMPLATES_DIR } from '../templates/templates.js';
4
4
  export const buildProjectConfig = (options) => {
5
- const { category, cliCommands, extHelperVersion, framework, language, lastTemplateVersion, managedTemplates, name, packageManager, slots, } = options;
5
+ const { category, cliCommands, extHelperVersion, framework, hasSettings = false, language, lastTemplateVersion, managedTemplates, name, packageManager, slots, } = options;
6
6
  const { dependencies, devDependencies } = resolveDependencies(language, framework);
7
7
  const managedFiles = collectManagedFiles(TEMPLATES_DIR, managedTemplates, lastTemplateVersion).map((file) => {
8
8
  return {
@@ -16,6 +16,7 @@ export const buildProjectConfig = (options) => {
16
16
  category,
17
17
  language,
18
18
  framework,
19
+ hasSettings,
19
20
  packageManager,
20
21
  dependencies,
21
22
  devDependencies,
@@ -3,12 +3,13 @@ import { MANAGED_TEMPLATES } from '../templates/constants.js';
3
3
  import { TEMPLATES_DIR } from '../templates/templates.js';
4
4
  import { buildProjectConfig } from './buildProjectConfig.js';
5
5
  export const updateProjectConfig = (lastTemplateVersion, projectConfigUserProps) => {
6
- const { category, framework, language, name, packageManager, slots } = projectConfigUserProps;
6
+ const { category, framework, hasSettings, language, name, packageManager, slots, } = projectConfigUserProps;
7
7
  return buildProjectConfig({
8
8
  name,
9
9
  category,
10
10
  language,
11
11
  framework,
12
+ hasSettings,
12
13
  packageManager,
13
14
  slots,
14
15
  extHelperVersion: EXT_HELPER_VERSION,
@@ -12,6 +12,7 @@ describe('isValidProjectConfig', () => {
12
12
  name: 'project-name',
13
13
  packageManager: 'npm',
14
14
  framework,
15
+ hasSettings: false,
15
16
  language,
16
17
  category: 'chat-bots',
17
18
  dependencies,
@@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import { CLI_COMMANDS_MAP, EXT_HELPER_VERSION } from '../constants.js';
6
6
  import { buildProjectConfig } from '../projectConfig/buildProjectConfig.js';
7
7
  import { MANAGED_TEMPLATES } from '../templates/constants.js';
8
- import { LAST_TEMPLATE_VERSION, TEMPLATES_DIR } from '../templates/templates.js';
8
+ import { LAST_TEMPLATE_VERSION, TEMPLATES_DIR, } from '../templates/templates.js';
9
9
  import { executeCommands } from './executor.js';
10
10
  import { mapProjectConfigToTemplateData } from './mappers/mapProjectConfigToTemplateModel.js';
11
11
  import { planProject } from './planner.js';
@@ -34,6 +34,7 @@ describe('executeCommands', () => {
34
34
  const projectConfig = buildProjectConfig({
35
35
  category: 'games-and-fun',
36
36
  framework: 'vanilla',
37
+ hasSettings: false,
37
38
  language: 'ts',
38
39
  name: 'my-ext',
39
40
  packageManager: 'npm',
@@ -60,15 +61,51 @@ describe('executeCommands', () => {
60
61
  readFile(command.destPath),
61
62
  ]);
62
63
  expect(dest.equals(src)).toBe(true);
63
- continue;
64
64
  }
65
65
  }
66
66
  expect(cb).toHaveBeenCalledTimes(commands.length);
67
67
  });
68
+ it.each(new Array(LAST_TEMPLATE_VERSION).fill(0).map((_, index) => index + 1))('generates non-empty settings files for template version %s', async (templateVersion) => {
69
+ const projectConfig = buildProjectConfig({
70
+ category: 'chat-bots',
71
+ framework: 'vanilla',
72
+ hasSettings: true,
73
+ language: 'ts',
74
+ name: 'my-ext',
75
+ packageManager: 'npm',
76
+ slots: ['EXTENSION_SLOT_BACKGROUND'],
77
+ extHelperVersion: EXT_HELPER_VERSION,
78
+ lastTemplateVersion: templateVersion,
79
+ cliCommands: CLI_COMMANDS_MAP,
80
+ managedTemplates: MANAGED_TEMPLATES,
81
+ templatesDir: TEMPLATES_DIR,
82
+ });
83
+ const commands = buildCommands(projectConfig, tmpDir);
84
+ const settingsCommands = commands.filter((command) => command.destPath.includes('/settings'));
85
+ expect(settingsCommands.map(({ destPath }) => destPath)).toEqual(expect.arrayContaining([
86
+ join(tmpDir, 'settings.html'),
87
+ join(tmpDir, 'src/slots/settings/app.ts'),
88
+ join(tmpDir, 'src/slots/settings/main.css'),
89
+ join(tmpDir, 'src/slots/settings/main.ts'),
90
+ ]));
91
+ executeCommands(commands);
92
+ for (const command of settingsCommands) {
93
+ const content = await readFile(command.destPath, 'utf-8');
94
+ expect(content.trim().length).toBeGreaterThan(0);
95
+ expect(content).not.toMatch(EJS_TAG_PATTERN);
96
+ }
97
+ const [manifest, viteConfig] = await Promise.all([
98
+ readFile(join(tmpDir, 'public/manifest.json'), 'utf-8'),
99
+ readFile(join(tmpDir, 'vite.config.ts'), 'utf-8'),
100
+ ]);
101
+ expect(manifest).toContain('"settings": "settings.html"');
102
+ expect(viteConfig).toContain("'settings': resolve(__dirname, 'settings.html')");
103
+ });
68
104
  it('rejects with ENOENT when a source template is missing', () => {
69
105
  const projectConfig = buildProjectConfig({
70
106
  category: 'sex-toys',
71
107
  framework: 'vanilla',
108
+ hasSettings: true,
72
109
  language: 'js',
73
110
  name: 'my-ext',
74
111
  packageManager: 'npm',
@@ -3,7 +3,7 @@ import { CLI_COMMANDS_MAP, EXT_HELPER_VERSION } from '../../constants.js';
3
3
  import { DEPENDENCIES } from '../../dependencies/constants.js';
4
4
  import { buildProjectConfig } from '../../projectConfig/buildProjectConfig.js';
5
5
  import { MANAGED_TEMPLATES } from '../../templates/constants.js';
6
- import { LAST_TEMPLATE_VERSION, TEMPLATES_DIR } from '../../templates/templates.js';
6
+ import { LAST_TEMPLATE_VERSION, TEMPLATES_DIR, } from '../../templates/templates.js';
7
7
  import { mapProjectConfigToTemplateData } from './mapProjectConfigToTemplateModel.js';
8
8
  const makeConfig = (overrides = {}) => {
9
9
  const language = overrides.language ?? 'ts';
@@ -58,7 +58,10 @@ describe('mapProjectConfigToTemplateData', () => {
58
58
  expect(result.slotsEntries).toEqual([
59
59
  { slot: 'EXTENSION_SLOT_MAIN_GAME_FUN', slotCamelCased: 'mainGameFun' },
60
60
  { slot: 'EXTENSION_SLOT_BACKGROUND', slotCamelCased: 'background' },
61
- { slot: 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY', slotCamelCased: 'videoDecorativeOverlay' },
61
+ {
62
+ slot: 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY',
63
+ slotCamelCased: 'videoDecorativeOverlay',
64
+ },
62
65
  ]);
63
66
  });
64
67
  it('maps broadcast-tools category slots correctly', () => {
@@ -67,23 +70,35 @@ describe('mapProjectConfigToTemplateData', () => {
67
70
  slots: ['EXTENSION_SLOT_MOVEABLE_OVERLAY', 'EXTENSION_SLOT_BACKGROUND'],
68
71
  }));
69
72
  expect(result.slotsEntries).toEqual([
70
- { slot: 'EXTENSION_SLOT_MOVEABLE_OVERLAY', slotCamelCased: 'moveableOverlay' },
73
+ {
74
+ slot: 'EXTENSION_SLOT_MOVEABLE_OVERLAY',
75
+ slotCamelCased: 'moveableOverlay',
76
+ },
71
77
  { slot: 'EXTENSION_SLOT_BACKGROUND', slotCamelCased: 'background' },
72
78
  ]);
73
79
  });
74
80
  it('maps sex-toys category slots correctly', () => {
75
81
  const result = mapProjectConfigToTemplateData(makeConfig({
76
82
  category: 'sex-toys',
77
- slots: ['EXTENSION_SLOT_MAIN_SEX_TOY', 'EXTENSION_SLOT_BACKGROUND', 'EXTENSION_SLOT_MOVEABLE_OVERLAY'],
83
+ slots: [
84
+ 'EXTENSION_SLOT_MAIN_SEX_TOY',
85
+ 'EXTENSION_SLOT_BACKGROUND',
86
+ 'EXTENSION_SLOT_MOVEABLE_OVERLAY',
87
+ ],
78
88
  }));
79
89
  expect(result.slotsEntries).toEqual([
80
90
  { slot: 'EXTENSION_SLOT_MAIN_SEX_TOY', slotCamelCased: 'mainSexToy' },
81
91
  { slot: 'EXTENSION_SLOT_BACKGROUND', slotCamelCased: 'background' },
82
- { slot: 'EXTENSION_SLOT_MOVEABLE_OVERLAY', slotCamelCased: 'moveableOverlay' },
92
+ {
93
+ slot: 'EXTENSION_SLOT_MOVEABLE_OVERLAY',
94
+ slotCamelCased: 'moveableOverlay',
95
+ },
83
96
  ]);
84
97
  });
85
98
  it('preserves slot order from config', () => {
86
- const result = mapProjectConfigToTemplateData(makeConfig({ slots: ['EXTENSION_SLOT_BACKGROUND', 'EXTENSION_SLOT_MAIN_GAME_FUN'] }));
99
+ const result = mapProjectConfigToTemplateData(makeConfig({
100
+ slots: ['EXTENSION_SLOT_BACKGROUND', 'EXTENSION_SLOT_MAIN_GAME_FUN'],
101
+ }));
87
102
  expect(result.slotsEntries[0].slot).toBe('EXTENSION_SLOT_BACKGROUND');
88
103
  expect(result.slotsEntries[1].slot).toBe('EXTENSION_SLOT_MAIN_GAME_FUN');
89
104
  });
@@ -91,6 +106,20 @@ describe('mapProjectConfigToTemplateData', () => {
91
106
  const result = mapProjectConfigToTemplateData(makeConfig({ slots: [] }));
92
107
  expect(result.slotsEntries).toEqual([]);
93
108
  });
109
+ it('passes config reference unchanged', () => {
110
+ const config = makeConfig();
111
+ const result = mapProjectConfigToTemplateData(config);
112
+ expect(result.config).toBe(config);
113
+ });
114
+ it('preserves settings without creating a slot entry', () => {
115
+ const result = mapProjectConfigToTemplateData(makeConfig({ hasSettings: true }));
116
+ expect(result.hasSettings).toBe(true);
117
+ expect(result.config.hasSettings).toBe(true);
118
+ expect(result.slotsEntries).toEqual([
119
+ { slot: 'EXTENSION_SLOT_MAIN_GAME_FUN', slotCamelCased: 'mainGameFun' },
120
+ { slot: 'EXTENSION_SLOT_BACKGROUND', slotCamelCased: 'background' },
121
+ ]);
122
+ });
94
123
  it('passes name, framework, language, and slots from config', () => {
95
124
  const config = makeConfig({
96
125
  framework: 'preact',
@@ -1,4 +1,3 @@
1
- import { existsSync } from 'node:fs';
2
1
  import { join } from 'node:path';
3
2
  import { isSubPath } from '../helpers/isSubPath.js';
4
3
  import { GENERAL_SLOT_NAME_PLACEHOLDER, TEMPLATE_EXT, } from '../templates/constants.js';
@@ -64,10 +63,11 @@ const buildSlotTemplateData = (data, slotKey) => {
64
63
  };
65
64
  const planSlotTemplates = (projectDir, targetVersion, data, slotKey) => {
66
65
  const specificDir = join(SLOTS_TEMPLATE_DIR_REL, slotKey);
67
- const hasSpecificDir = existsSync(specificDir);
68
- const slotTemplatedDirRel = hasSpecificDir ? specificDir : GENERAL_SLOT_TEMPLATE_DIR_REL;
66
+ const specificFiles = collectTemplateFiles(TEMPLATES_DIR, targetVersion, specificDir);
67
+ const files = specificFiles.length > 0
68
+ ? specificFiles
69
+ : collectTemplateFiles(TEMPLATES_DIR, targetVersion, GENERAL_SLOT_TEMPLATE_DIR_REL);
69
70
  const slotData = buildSlotTemplateData(data, slotKey);
70
- const files = collectTemplateFiles(TEMPLATES_DIR, targetVersion, slotTemplatedDirRel);
71
71
  const allRelPaths = new Set(files.map((f) => f.relPath));
72
72
  const commands = [];
73
73
  for (const file of files) {
@@ -100,5 +100,8 @@ const planSlotTemplates = (projectDir, targetVersion, data, slotKey) => {
100
100
  export const planProject = (projectDir, targetVersion, data) => {
101
101
  const globalCommands = planGlobalTemplates(projectDir, targetVersion, data);
102
102
  const slotCommands = data.slotsEntries.flatMap(({ slotCamelCased }) => planSlotTemplates(projectDir, targetVersion, data, slotCamelCased));
103
- return [...globalCommands, ...slotCommands];
103
+ const settingsCommands = data.hasSettings
104
+ ? planSlotTemplates(projectDir, targetVersion, data, 'settings')
105
+ : [];
106
+ return [...globalCommands, ...slotCommands, ...settingsCommands];
104
107
  };
@@ -4,7 +4,7 @@ import { CATEGORIES, CLI_COMMANDS_MAP, EXT_HELPER_VERSION, FRAMEWORKS, LANGUAGES
4
4
  import { toNormalizedRelPath } from '../helpers/normalizePath.js';
5
5
  import { buildProjectConfig } from '../projectConfig/buildProjectConfig.js';
6
6
  import { MANAGED_TEMPLATES } from '../templates/constants.js';
7
- import { LAST_TEMPLATE_VERSION, TEMPLATES_DIR } from '../templates/templates.js';
7
+ import { LAST_TEMPLATE_VERSION, TEMPLATES_DIR, } from '../templates/templates.js';
8
8
  import { mapProjectConfigToTemplateData } from './mappers/mapProjectConfigToTemplateModel.js';
9
9
  import { planProject } from './planner.js';
10
10
  const PROJECT_DIR = '/output/my-ext';
@@ -18,7 +18,15 @@ const buildSlotCombos = (category) => {
18
18
  return combos;
19
19
  }
20
20
  if (category === 'sex-toys') {
21
- combos.push([...base, 'EXTENSION_SLOT_MOVEABLE_OVERLAY'], [...base, 'EXTENSION_SLOT_RIGHT_OVERLAY'], [...base, 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY'], [...base, 'EXTENSION_SLOT_MOVEABLE_OVERLAY', 'EXTENSION_SLOT_RIGHT_OVERLAY'], [...base, 'EXTENSION_SLOT_MOVEABLE_OVERLAY', 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY']);
21
+ combos.push([...base, 'EXTENSION_SLOT_MOVEABLE_OVERLAY'], [...base, 'EXTENSION_SLOT_RIGHT_OVERLAY'], [...base, 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY'], [
22
+ ...base,
23
+ 'EXTENSION_SLOT_MOVEABLE_OVERLAY',
24
+ 'EXTENSION_SLOT_RIGHT_OVERLAY',
25
+ ], [
26
+ ...base,
27
+ 'EXTENSION_SLOT_MOVEABLE_OVERLAY',
28
+ 'EXTENSION_SLOT_VIDEO_DECORATIVE_OVERLAY',
29
+ ]);
22
30
  }
23
31
  return combos;
24
32
  };
@@ -43,7 +51,7 @@ const normalizeCommands = (commands) => commands.map((cmd) => {
43
51
  return { type: 'render', src: srcNormalized, dest: destNormalized };
44
52
  });
45
53
  describe('planProject', { tags: ['snapshot'] }, () => {
46
- it.each(TEST_CASES)('template version $templateVersion + $language + $framework, $category ($slotsStr)', ({ category, framework, language, slots, templateVersion, }) => {
54
+ it.each(TEST_CASES)('template version $templateVersion + $language + $framework, $category ($slotsStr)', ({ category, framework, language, slots, templateVersion }) => {
47
55
  const projectConfig = buildProjectConfig({
48
56
  category,
49
57
  framework,
@@ -61,4 +69,44 @@ describe('planProject', { tags: ['snapshot'] }, () => {
61
69
  const commands = planProject(PROJECT_DIR, templateVersion, data);
62
70
  expect(normalizeCommands(commands)).toMatchSnapshot();
63
71
  });
72
+ it.each(new Array(LAST_TEMPLATE_VERSION).fill(0).map((_, index) => index + 1))('adds settings templates for template version %s', (templateVersion) => {
73
+ const projectConfig = buildProjectConfig({
74
+ category: 'chat-bots',
75
+ framework: 'vanilla',
76
+ hasSettings: true,
77
+ language: 'ts',
78
+ name: basename(PROJECT_DIR),
79
+ packageManager: 'npm',
80
+ slots: ['EXTENSION_SLOT_BACKGROUND'],
81
+ extHelperVersion: EXT_HELPER_VERSION,
82
+ lastTemplateVersion: templateVersion,
83
+ cliCommands: CLI_COMMANDS_MAP,
84
+ managedTemplates: MANAGED_TEMPLATES,
85
+ templatesDir: TEMPLATES_DIR,
86
+ });
87
+ const commands = planProject(PROJECT_DIR, templateVersion, mapProjectConfigToTemplateData(projectConfig));
88
+ const settingsCommands = normalizeCommands(commands).filter(({ dest }) => dest.includes('settings'));
89
+ expect(settingsCommands).toEqual([
90
+ {
91
+ dest: 'my-ext/src/slots/settings/app.ts',
92
+ src: 'templates/v1/src/slots/settings/app.ts.ejs',
93
+ type: 'render',
94
+ },
95
+ {
96
+ dest: 'my-ext/src/slots/settings/main.css',
97
+ src: 'templates/v1/src/slots/settings/main.css.ejs',
98
+ type: 'render',
99
+ },
100
+ {
101
+ dest: 'my-ext/src/slots/settings/main.ts',
102
+ src: 'templates/v1/src/slots/settings/main.ts.ejs',
103
+ type: 'render',
104
+ },
105
+ {
106
+ dest: 'my-ext/settings.html',
107
+ src: 'templates/v1/src/slots/settings/settings.html.ejs',
108
+ type: 'render',
109
+ },
110
+ ]);
111
+ });
64
112
  });
@@ -14,6 +14,7 @@ export type TCliCommandsMap = Record<string, string>;
14
14
  export type TProjectConfigUserProps = {
15
15
  category: TCategory;
16
16
  framework: TFramework;
17
+ hasSettings: boolean;
17
18
  language: TLanguage;
18
19
  name: string;
19
20
  packageManager: TPackageManager;
@@ -42,6 +43,7 @@ export type TTemplateData = {
42
43
  config: TProjectConfig;
43
44
  ext: 'ts' | 'js';
44
45
  framework: TFramework;
46
+ hasSettings: boolean;
45
47
  isJsxFramework: boolean;
46
48
  isTypescript: boolean;
47
49
  jsxExt: 'tsx' | 'jsx';