@starodubenko/fsd-gen 0.1.0

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 (81) hide show
  1. package/README.md +61 -0
  2. package/cli.js +114 -0
  3. package/dist/config/defineConfig.d.ts +8 -0
  4. package/dist/config/defineConfig.d.ts.map +1 -0
  5. package/dist/config/defineConfig.js +8 -0
  6. package/dist/config/types.d.ts +98 -0
  7. package/dist/config/types.d.ts.map +1 -0
  8. package/dist/config/types.js +9 -0
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +3 -0
  12. package/dist/lib/barrels/updateBarrels.d.ts +2 -0
  13. package/dist/lib/barrels/updateBarrels.d.ts.map +1 -0
  14. package/dist/lib/barrels/updateBarrels.js +28 -0
  15. package/dist/lib/config/loadConfig.d.ts +21 -0
  16. package/dist/lib/config/loadConfig.d.ts.map +1 -0
  17. package/dist/lib/config/loadConfig.js +61 -0
  18. package/dist/lib/config/validateConfig.d.ts +13 -0
  19. package/dist/lib/config/validateConfig.d.ts.map +1 -0
  20. package/dist/lib/config/validateConfig.js +19 -0
  21. package/dist/lib/constants.d.ts +113 -0
  22. package/dist/lib/constants.d.ts.map +1 -0
  23. package/dist/lib/constants.js +129 -0
  24. package/dist/lib/generators/generate.d.ts +45 -0
  25. package/dist/lib/generators/generate.d.ts.map +1 -0
  26. package/dist/lib/generators/generate.js +104 -0
  27. package/dist/lib/generators/generatePreset.d.ts +6 -0
  28. package/dist/lib/generators/generatePreset.d.ts.map +1 -0
  29. package/dist/lib/generators/generatePreset.js +102 -0
  30. package/dist/lib/helpers/presetHelpers.d.ts +55 -0
  31. package/dist/lib/helpers/presetHelpers.d.ts.map +1 -0
  32. package/dist/lib/helpers/presetHelpers.js +53 -0
  33. package/dist/lib/naming/names.d.ts +10 -0
  34. package/dist/lib/naming/names.d.ts.map +1 -0
  35. package/dist/lib/naming/names.js +15 -0
  36. package/dist/lib/naming/resolvePaths.d.ts +33 -0
  37. package/dist/lib/naming/resolvePaths.d.ts.map +1 -0
  38. package/dist/lib/naming/resolvePaths.js +58 -0
  39. package/dist/lib/preset/actionExecution.d.ts +32 -0
  40. package/dist/lib/preset/actionExecution.d.ts.map +1 -0
  41. package/dist/lib/preset/actionExecution.js +138 -0
  42. package/dist/lib/preset/presetDiscovery.d.ts +40 -0
  43. package/dist/lib/preset/presetDiscovery.d.ts.map +1 -0
  44. package/dist/lib/preset/presetDiscovery.js +189 -0
  45. package/dist/lib/preset/presetLoading.d.ts +22 -0
  46. package/dist/lib/preset/presetLoading.d.ts.map +1 -0
  47. package/dist/lib/preset/presetLoading.js +69 -0
  48. package/dist/lib/routing/injectRoute.d.ts +17 -0
  49. package/dist/lib/routing/injectRoute.d.ts.map +1 -0
  50. package/dist/lib/routing/injectRoute.js +66 -0
  51. package/dist/lib/templates/templateLoader.d.ts +31 -0
  52. package/dist/lib/templates/templateLoader.d.ts.map +1 -0
  53. package/dist/lib/templates/templateLoader.js +122 -0
  54. package/package.json +64 -0
  55. package/templates/entity/model-ui-basic/Component.styles.ts +1 -0
  56. package/templates/entity/model-ui-basic/Component.tsx +18 -0
  57. package/templates/feature/ui-model-basic/Component.styles.ts +1 -0
  58. package/templates/feature/ui-model-basic/Component.tsx +17 -0
  59. package/templates/page/ui-basic/Component.styles.ts +1 -0
  60. package/templates/page/ui-basic/Component.tsx +17 -0
  61. package/templates/preset/table/entity/api/create/Component.tsx +19 -0
  62. package/templates/preset/table/entity/api/delete/Component.tsx +17 -0
  63. package/templates/preset/table/entity/api/get/Component.tsx +34 -0
  64. package/templates/preset/table/entity/api/update/Component.tsx +18 -0
  65. package/templates/preset/table/entity/model.ts +9 -0
  66. package/templates/preset/table/entity/ui/Component.tsx +11 -0
  67. package/templates/preset/table/feature/buttons/create/Component.styles.ts +1 -0
  68. package/templates/preset/table/feature/buttons/create/Component.tsx +30 -0
  69. package/templates/preset/table/feature/buttons/delete/Component.styles.ts +1 -0
  70. package/templates/preset/table/feature/buttons/delete/Component.tsx +30 -0
  71. package/templates/preset/table/feature/buttons/edit/Component.styles.ts +1 -0
  72. package/templates/preset/table/feature/buttons/edit/Component.tsx +30 -0
  73. package/templates/preset/table/feature/buttons.tsx +11 -0
  74. package/templates/preset/table/page/page/Component.tsx +19 -0
  75. package/templates/preset/table/page/page.tsx +19 -0
  76. package/templates/preset/table/widget/table/Component.tsx +50 -0
  77. package/templates/preset/table/widget/table.tsx +50 -0
  78. package/templates/shared/ui-basic/Component.styles.ts +1 -0
  79. package/templates/shared/ui-basic/Component.tsx +18 -0
  80. package/templates/widget/ui-basic/Component.styles.ts +1 -0
  81. package/templates/widget/ui-basic/Component.tsx +18 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * FSD Layer names and their plural forms
3
+ */
4
+ export const FSD_LAYERS = {
5
+ ENTITY: 'entity',
6
+ FEATURE: 'feature',
7
+ WIDGET: 'widget',
8
+ PAGE: 'page',
9
+ SHARED: 'shared',
10
+ };
11
+ export const LAYER_PLURALS = {
12
+ [FSD_LAYERS.ENTITY]: 'entities',
13
+ [FSD_LAYERS.FEATURE]: 'features',
14
+ [FSD_LAYERS.WIDGET]: 'widgets',
15
+ [FSD_LAYERS.PAGE]: 'pages',
16
+ [FSD_LAYERS.SHARED]: 'shared',
17
+ };
18
+ /**
19
+ * FSD segment names (directories within slices)
20
+ */
21
+ export const FSD_SEGMENTS = {
22
+ UI: 'ui',
23
+ MODEL: 'model',
24
+ API: 'api',
25
+ LIB: 'lib',
26
+ CONFIG: 'config',
27
+ };
28
+ /**
29
+ * Template types for different layers
30
+ */
31
+ export const DEFAULT_TEMPLATES = {
32
+ [FSD_LAYERS.SHARED]: 'ui-basic',
33
+ [FSD_LAYERS.ENTITY]: 'model-ui-basic',
34
+ [FSD_LAYERS.FEATURE]: 'ui-model-basic',
35
+ [FSD_LAYERS.WIDGET]: 'ui-basic',
36
+ [FSD_LAYERS.PAGE]: 'ui-basic',
37
+ };
38
+ /**
39
+ * File extensions
40
+ */
41
+ export const FILE_EXTENSIONS = {
42
+ TYPESCRIPT: '.ts',
43
+ TSX: '.tsx',
44
+ STYLES: '.styles.ts',
45
+ JSON: '.json',
46
+ CONFIG: 'config.ts',
47
+ INDEX: 'index.ts',
48
+ };
49
+ /**
50
+ * Configuration file names
51
+ */
52
+ export const CONFIG_FILES = {
53
+ FSD_GEN: 'fsdgen.config.ts',
54
+ PRESET_TS: 'preset.ts',
55
+ PRESET_JSON: 'preset.json',
56
+ };
57
+ /**
58
+ * Preset directory names
59
+ */
60
+ export const PRESET_DIRS = {
61
+ ROOT: 'preset',
62
+ BUTTONS: 'buttons',
63
+ TABLE: 'table',
64
+ };
65
+ /**
66
+ * API hook operation names
67
+ */
68
+ export const API_OPERATIONS = {
69
+ GET: 'get',
70
+ CREATE: 'create',
71
+ UPDATE: 'update',
72
+ DELETE: 'delete',
73
+ };
74
+ /**
75
+ * API hook name prefixes
76
+ */
77
+ export const API_HOOK_PREFIXES = {
78
+ [API_OPERATIONS.GET]: 'useGet',
79
+ [API_OPERATIONS.CREATE]: 'useCreate',
80
+ [API_OPERATIONS.UPDATE]: 'useUpdate',
81
+ [API_OPERATIONS.DELETE]: 'useDelete',
82
+ };
83
+ /**
84
+ * Default fallback template
85
+ */
86
+ export const DEFAULT_TEMPLATE = 'ui-basic';
87
+ /**
88
+ * Default root directory
89
+ */
90
+ export const DEFAULT_ROOT_DIR = 'src';
91
+ /**
92
+ * Preset action types
93
+ */
94
+ export const ACTION_TYPES = {
95
+ COMPONENT: 'component',
96
+ FILE: 'file',
97
+ HOOK: 'hook',
98
+ STYLES: 'styles',
99
+ };
100
+ /**
101
+ * Discovery modes
102
+ */
103
+ export const DISCOVERY_MODES = {
104
+ AUTO: 'auto',
105
+ MANUAL: 'manual',
106
+ };
107
+ /**
108
+ * Naming convention modes
109
+ */
110
+ export const NAMING_MODES = {
111
+ ERROR: 'error',
112
+ WARN: 'warn',
113
+ AUTO_FIX: 'autoFix',
114
+ };
115
+ /**
116
+ * Routing configuration constants
117
+ */
118
+ export const ROUTING = {
119
+ MARKER: '{/* ROUTES_INJECTION_POINT */}',
120
+ IMPORTS_REGEX: /^(?:import|export)\s/m,
121
+ APP_FILE: 'App.tsx',
122
+ };
123
+ /**
124
+ * Template file names
125
+ */
126
+ export const TEMPLATE_FILES = {
127
+ COMPONENT: 'Component.tsx',
128
+ STYLES: 'Component.styles.ts',
129
+ };
@@ -0,0 +1,45 @@
1
+ import { FsdPaths } from '../naming/resolvePaths.js';
2
+ import { ACTION_TYPES } from '../constants.js';
3
+ export interface TemplateContext {
4
+ componentName: string;
5
+ sliceName: string;
6
+ layer: string;
7
+ }
8
+ /**
9
+ * Resolve which template to use for the given layer
10
+ * @returns Object with templatePath and layerArg for template loading
11
+ */
12
+ export declare function resolveTemplateType(layer: string, templateOverride?: string): {
13
+ templatePath: string;
14
+ layerArg: string;
15
+ };
16
+ /**
17
+ * Ensure directory exists
18
+ */
19
+ export declare function ensureDirectory(dir: string): Promise<void>;
20
+ /**
21
+ * Write component file to disk
22
+ */
23
+ export declare function writeComponentFile(path: string, content: string): Promise<void>;
24
+ /**
25
+ * Write styles file to disk (only if content is non-empty)
26
+ */
27
+ export declare function writeStylesFile(path: string, content: string): Promise<void>;
28
+ /**
29
+ * Update barrel files for a generated block
30
+ */
31
+ export declare function updateBarrelsForBlock(slicePath: string, uiPath: string, componentName: string, exportName: string, type: (typeof ACTION_TYPES)[keyof typeof ACTION_TYPES]): void;
32
+ /**
33
+ * Generate a hook from a template
34
+ */
35
+ export declare function generateHook(paths: FsdPaths, context: TemplateContext, templatePath: string, templatesDir?: string): Promise<void>;
36
+ /**
37
+ * Generate styles from a template
38
+ */
39
+ export declare function generateStyles(paths: FsdPaths, context: TemplateContext, templatePath: string, templatesDir?: string): Promise<void>;
40
+ /**
41
+ * Generate a component from a template
42
+ * Orchestrates template loading, rendering, and file writing
43
+ */
44
+ export declare function generateComponent(paths: FsdPaths, context: TemplateContext, templateOverride?: string, templatesDir?: string): Promise<void>;
45
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/lib/generators/generate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAsE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnH,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,gBAAgB,CAAC,EAAE,MAAM,GACxB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAO5C;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKlF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,GACrD,IAAI,CAMN;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,iBAatB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,iBAYtB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,eAAe,EACxB,gBAAgB,CAAC,EAAE,MAAM,EACzB,YAAY,CAAC,EAAE,MAAM,iBA2BtB"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Core generator functions for creating FSD components, slices, and blocks.
3
+ *
4
+ * Orchestrates the creation of directories, files, and barrel updates for individual
5
+ * entities (components, styles, hooks) within a specific layer and slice.
6
+ */
7
+ import { mkdir, writeFile } from 'fs/promises';
8
+ import { basename } from 'path';
9
+ import { updateBarrel } from '../barrels/updateBarrels.js';
10
+ import { loadTemplate, processTemplate } from '../templates/templateLoader.js';
11
+ import { DEFAULT_TEMPLATES, DEFAULT_TEMPLATE, FSD_SEGMENTS, FILE_EXTENSIONS, ACTION_TYPES } from '../constants.js';
12
+ import { loadConfig } from '../config/loadConfig.js';
13
+ /**
14
+ * Resolve which template to use for the given layer
15
+ * @returns Object with templatePath and layerArg for template loading
16
+ */
17
+ export function resolveTemplateType(layer, templateOverride) {
18
+ if (templateOverride) {
19
+ return { templatePath: templateOverride, layerArg: '' };
20
+ }
21
+ const templatePath = DEFAULT_TEMPLATES[layer] || DEFAULT_TEMPLATE;
22
+ return { templatePath, layerArg: layer };
23
+ }
24
+ /**
25
+ * Ensure directory exists
26
+ */
27
+ export async function ensureDirectory(dir) {
28
+ await mkdir(dir, { recursive: true });
29
+ }
30
+ /**
31
+ * Write component file to disk
32
+ */
33
+ export async function writeComponentFile(path, content) {
34
+ await writeFile(path, content);
35
+ console.log(`Created ${path}`);
36
+ }
37
+ /**
38
+ * Write styles file to disk (only if content is non-empty)
39
+ */
40
+ export async function writeStylesFile(path, content) {
41
+ if (content && content.trim().length > 0) {
42
+ await writeFile(path, content);
43
+ console.log(`Created ${path}`);
44
+ }
45
+ }
46
+ /**
47
+ * Update barrel files for a generated block
48
+ */
49
+ export function updateBarrelsForBlock(slicePath, uiPath, componentName, exportName, type) {
50
+ updateBarrel(uiPath, componentName, exportName);
51
+ if (type === ACTION_TYPES.COMPONENT || type === ACTION_TYPES.HOOK || type === ACTION_TYPES.STYLES) {
52
+ updateBarrel(slicePath, FSD_SEGMENTS.UI, FSD_SEGMENTS.UI);
53
+ }
54
+ }
55
+ /**
56
+ * Generate a hook from a template
57
+ */
58
+ export async function generateHook(paths, context, templatePath, templatesDir) {
59
+ const effectiveTemplatesDir = templatesDir || (await loadConfig()).templatesDir;
60
+ const { component: hookTemplate } = await loadTemplate('', templatePath, effectiveTemplatesDir);
61
+ const content = processTemplate(hookTemplate, context);
62
+ const hookFile = `${paths.componentPath}${FILE_EXTENSIONS.TYPESCRIPT}`;
63
+ await ensureDirectory(paths.uiPath);
64
+ await writeFile(hookFile, content);
65
+ console.log(`Created ${hookFile}`);
66
+ updateBarrelsForBlock(paths.slicePath, paths.uiPath, context.componentName, context.componentName, ACTION_TYPES.HOOK);
67
+ }
68
+ /**
69
+ * Generate styles from a template
70
+ */
71
+ export async function generateStyles(paths, context, templatePath, templatesDir) {
72
+ const effectiveTemplatesDir = templatesDir || (await loadConfig()).templatesDir;
73
+ const { styles: stylesTemplate } = await loadTemplate('', templatePath, effectiveTemplatesDir);
74
+ const content = processTemplate(stylesTemplate, context);
75
+ const stylesFile = `${paths.componentPath}${FILE_EXTENSIONS.STYLES}`;
76
+ await ensureDirectory(paths.uiPath);
77
+ await writeStylesFile(stylesFile, content);
78
+ updateBarrelsForBlock(paths.slicePath, paths.uiPath, context.componentName, context.componentName, ACTION_TYPES.STYLES);
79
+ }
80
+ /**
81
+ * Generate a component from a template
82
+ * Orchestrates template loading, rendering, and file writing
83
+ */
84
+ export async function generateComponent(paths, context, templateOverride, templatesDir) {
85
+ // Step 1: Resolve template type
86
+ const { templatePath, layerArg } = resolveTemplateType(context.layer, templateOverride);
87
+ console.log(`Using template: ${templateOverride ? templatePath : context.layer + '/' + templatePath}`);
88
+ // Step 2: Get templates directory
89
+ const effectiveTemplatesDir = templatesDir || (await loadConfig()).templatesDir;
90
+ // Step 3: Load template
91
+ const { component, styles } = await loadTemplate(layerArg, templatePath, effectiveTemplatesDir);
92
+ // Step 4: Render templates
93
+ const componentContent = processTemplate(component, context);
94
+ const stylesContent = processTemplate(styles, context);
95
+ // Step 5: Write files
96
+ const componentFile = `${paths.componentPath}${FILE_EXTENSIONS.TSX}`;
97
+ const stylesFile = `${paths.componentPath}${FILE_EXTENSIONS.STYLES}`;
98
+ await ensureDirectory(paths.uiPath);
99
+ await writeComponentFile(componentFile, componentContent);
100
+ await writeStylesFile(stylesFile, stylesContent);
101
+ // Step 6: Update barrels
102
+ const exportName = basename(paths.componentPath);
103
+ updateBarrelsForBlock(paths.slicePath, paths.uiPath, context.componentName, exportName, ACTION_TYPES.COMPONENT);
104
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Generate files from a preset
3
+ * Main orchestrator function that coordinates all preset generation steps
4
+ */
5
+ export declare function generatePreset(presetName: string, name: string): Promise<void>;
6
+ //# sourceMappingURL=generatePreset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generatePreset.d.ts","sourceRoot":"","sources":["../../../src/lib/generators/generatePreset.ts"],"names":[],"mappings":"AAmGA;;;GAGG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCpF"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Generator logic for presets.
3
+ *
4
+ * Handles the high-level orchestration of generating complete vertical slices
5
+ * based on predefined or custom presets. Resolves the preset configuration,
6
+ * discovers defined actions (files, components), and executes them.
7
+ */
8
+ /**
9
+ * Generator logic for presets.
10
+ *
11
+ * Handles the high-level orchestration of generating complete vertical slices
12
+ * based on predefined or custom presets. Resolves the preset configuration,
13
+ * discovers defined actions (files, components), and executes them.
14
+ */
15
+ import { resolve } from 'path';
16
+ import { loadConfig } from '../config/loadConfig.js';
17
+ import { resolvePresetDir, processTemplate } from '../templates/templateLoader.js';
18
+ import { loadPresetConfig } from '../preset/presetLoading.js';
19
+ import { discoverTemplates } from '../preset/presetDiscovery.js';
20
+ import { executeActions } from '../preset/actionExecution.js';
21
+ import { injectRoute } from '../routing/injectRoute.js';
22
+ import { DISCOVERY_MODES, ACTION_TYPES, FSD_LAYERS } from '../constants.js';
23
+ /**
24
+ * Resolve preset actions based on discovery mode
25
+ */
26
+ async function resolvePresetActions(presetConfig, presetDir, presetName, name) {
27
+ if (presetConfig.discoveryMode === DISCOVERY_MODES.AUTO) {
28
+ console.log('Auto-discovering templates...');
29
+ const discoveredActions = await discoverTemplates(presetDir, presetName, name, presetConfig.conventions);
30
+ console.log(`Found ${discoveredActions.length} templates`);
31
+ return discoveredActions;
32
+ }
33
+ return presetConfig.actions || [];
34
+ }
35
+ async function handleRouteInjection(presetConfig, actions, name, globalVars, config) {
36
+ if (!presetConfig.routing) {
37
+ return;
38
+ }
39
+ // Find all page actions
40
+ const pageActions = actions.filter(action => action.type === ACTION_TYPES.COMPONENT && action.layer === FSD_LAYERS.PAGE);
41
+ if (pageActions.length === 0) {
42
+ console.warn('⚠️ Warning: Routing config provided but no page template found');
43
+ return;
44
+ }
45
+ for (const pageAction of pageActions) {
46
+ // Prepare variables for this specific action
47
+ const variables = {
48
+ name,
49
+ componentName: name,
50
+ nameLower: name.toLowerCase(),
51
+ nameUpper: name.toUpperCase(),
52
+ nameKebab: name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(),
53
+ ...globalVars,
54
+ ...pageAction.variables
55
+ };
56
+ const pageSlice = processTemplate(pageAction.slice, variables);
57
+ const componentName = presetConfig.routing.componentName ||
58
+ processTemplate(pageAction.name || pageAction.slice, variables);
59
+ const importPath = presetConfig.routing.importPath || `@pages/${pageSlice}`;
60
+ // Support dynamic paths using variables (e.g. /{{name}})
61
+ const routePath = processTemplate(presetConfig.routing.path, variables);
62
+ await injectRoute({
63
+ rootDir: config.rootDir || 'src',
64
+ path: routePath,
65
+ importPath,
66
+ componentName,
67
+ appFile: presetConfig.routing.appFile
68
+ });
69
+ }
70
+ }
71
+ /**
72
+ * Generate files from a preset
73
+ * Main orchestrator function that coordinates all preset generation steps
74
+ */
75
+ export async function generatePreset(presetName, name) {
76
+ console.log(`Generating preset '${presetName}' for '${name}'...`);
77
+ // Step 1: Load configuration
78
+ const config = await loadConfig();
79
+ const templatesDir = config.templatesDir ? resolve(process.cwd(), config.templatesDir) : undefined;
80
+ // Step 2: Resolve preset directory
81
+ const presetDir = await resolvePresetDir(presetName, templatesDir);
82
+ if (!presetDir) {
83
+ throw new Error(`Preset '${presetName}' not found`);
84
+ }
85
+ // Step 3: Load preset configuration
86
+ const presetConfig = await loadPresetConfig(presetDir, name, config);
87
+ if (!presetConfig) {
88
+ throw new Error(`No preset configuration found in ${presetDir}`);
89
+ }
90
+ // Step 4: Determine actions (auto-discovery or manual)
91
+ const actions = await resolvePresetActions(presetConfig, presetDir, presetName, name);
92
+ if (!actions || actions.length === 0) {
93
+ console.warn('No actions found in preset configuration');
94
+ return;
95
+ }
96
+ // Step 5: Execute all actions
97
+ const globalVars = presetConfig.variables || {};
98
+ await executeActions(actions, name, globalVars, config);
99
+ // Step 6: Handle routing injection if configured
100
+ await handleRouteInjection(presetConfig, actions, name, globalVars, config);
101
+ console.log('Preset generation complete.');
102
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Helper types and interfaces for defining and using presets.
3
+ *
4
+ * Provides utility functions for creating preset helpers that standardize naming conventions,
5
+ * path resolution, and variable generation for preset authors.
6
+ */
7
+ import { FsdGenConfig } from '../../config/types.js';
8
+ export interface PresetHelpers {
9
+ /** Base name (same as input name) */
10
+ baseName: string;
11
+ /** Import path for entity layer (with or without alias) */
12
+ entityImportPath: string;
13
+ /** Feature slice name (e.g., 'ManageUser') */
14
+ featureSlice: string;
15
+ /** Import path for feature layer */
16
+ featureImportPath: string;
17
+ /** Widget slice name (e.g., 'UserTable') */
18
+ widgetSlice: string;
19
+ /** Import path for widget layer */
20
+ widgetImportPath: string;
21
+ /** Page slice name (e.g., 'UserPage') */
22
+ pageSlice: string;
23
+ /** Import path for page layer */
24
+ pageImportPath: string;
25
+ }
26
+ export interface PresetHelperOptions {
27
+ /** Prefix for feature slice names (default: 'Manage') */
28
+ featurePrefix?: string;
29
+ /** Suffix for widget slice names (default: 'Table') */
30
+ widgetSuffix?: string;
31
+ /** Suffix for page slice names (default: 'Page') */
32
+ pageSuffix?: string;
33
+ }
34
+ /**
35
+ * Helper function to resolve import paths and slice names for presets.
36
+ * Automatically handles FSD layer aliases if configured.
37
+ *
38
+ * @param name - The entity name (e.g., 'User', 'Product')
39
+ * @param config - The loaded FSD generator configuration
40
+ * @param options - Optional naming conventions
41
+ * @returns Object with computed import paths and slice names
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * export default definePreset(({ name, config }) => {
46
+ * const helpers = createPresetHelpers(name, config);
47
+ * return {
48
+ * variables: { ...helpers },
49
+ * // ...
50
+ * };
51
+ * });
52
+ * ```
53
+ */
54
+ export declare function createPresetHelpers(name: string, config: FsdGenConfig, options?: PresetHelperOptions): PresetHelpers;
55
+ //# sourceMappingURL=presetHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presetHelpers.d.ts","sourceRoot":"","sources":["../../../src/lib/helpers/presetHelpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC1B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAChC,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,mBAAwB,GAClC,aAAa,CA0Cf"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Helper function to resolve import paths and slice names for presets.
3
+ * Automatically handles FSD layer aliases if configured.
4
+ *
5
+ * @param name - The entity name (e.g., 'User', 'Product')
6
+ * @param config - The loaded FSD generator configuration
7
+ * @param options - Optional naming conventions
8
+ * @returns Object with computed import paths and slice names
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * export default definePreset(({ name, config }) => {
13
+ * const helpers = createPresetHelpers(name, config);
14
+ * return {
15
+ * variables: { ...helpers },
16
+ * // ...
17
+ * };
18
+ * });
19
+ * ```
20
+ */
21
+ export function createPresetHelpers(name, config, options = {}) {
22
+ const { featurePrefix = 'Manage', widgetSuffix = 'Table', pageSuffix = 'Page' } = options;
23
+ const aliases = config.aliases || {};
24
+ const hasEntAlias = !!aliases['@entities'];
25
+ const hasFeatAlias = !!aliases['@features'];
26
+ const hasWidgetAlias = !!aliases['@widgets'];
27
+ const hasPageAlias = !!aliases['@pages'];
28
+ const entityImportPath = hasEntAlias
29
+ ? `@entities/${name}`
30
+ : `../../../entities/${name}`;
31
+ const featureSlice = `${featurePrefix}${name}`;
32
+ const featureImportPath = hasFeatAlias
33
+ ? `@features/${featureSlice}`
34
+ : `../../../features/${featureSlice}`;
35
+ const widgetSlice = `${name}${widgetSuffix}`;
36
+ const widgetImportPath = hasWidgetAlias
37
+ ? `@widgets/${widgetSlice}`
38
+ : `../../../widgets/${widgetSlice}`;
39
+ const pageSlice = `${name}${pageSuffix}`;
40
+ const pageImportPath = hasPageAlias
41
+ ? `@pages/${pageSlice}`
42
+ : `../../../pages/${pageSlice}`;
43
+ return {
44
+ baseName: name,
45
+ entityImportPath,
46
+ featureSlice,
47
+ featureImportPath,
48
+ widgetSlice,
49
+ widgetImportPath,
50
+ pageSlice,
51
+ pageImportPath
52
+ };
53
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Naming utility functions.
3
+ *
4
+ * Provides standard functions for string manipulation, such as converting strings
5
+ * to PascalCase for component names or handling other case conversions required by FSD.
6
+ */
7
+ export declare function toPascalCase(str: string): string;
8
+ export declare function toCamelCase(str: string): string;
9
+ export declare function toKebabCase(str: string): string;
10
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../src/lib/naming/names.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Naming utility functions.
3
+ *
4
+ * Provides standard functions for string manipulation, such as converting strings
5
+ * to PascalCase for component names or handling other case conversions required by FSD.
6
+ */
7
+ export function toPascalCase(str) {
8
+ return str.replace(/(^\w|-\w)/g, (clear) => clear.replace(/-/, '').toUpperCase());
9
+ }
10
+ export function toCamelCase(str) {
11
+ return str.replace(/-\w/g, (clear) => clear[1].toUpperCase());
12
+ }
13
+ export function toKebabCase(str) {
14
+ return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
15
+ }
@@ -0,0 +1,33 @@
1
+ export interface FsdPaths {
2
+ layerPath: string;
3
+ slicePath: string;
4
+ uiPath: string;
5
+ componentPath: string;
6
+ }
7
+ /**
8
+ * Map layer name to plural directory name
9
+ */
10
+ export declare function getLayerPlural(layer: string): string;
11
+ /**
12
+ * Build the full path to the layer directory
13
+ */
14
+ export declare function buildLayerPath(rootDir: string, layer: string): string;
15
+ /**
16
+ * Build the path to the slice directory
17
+ */
18
+ export declare function buildSlicePath(layerPath: string, slice: string): string;
19
+ /**
20
+ * Build the path to the UI directory
21
+ * Handles special case for shared layer (no extra ui folder)
22
+ */
23
+ export declare function buildUiPath(slicePath: string, layer: string): string;
24
+ /**
25
+ * Build the path to the component directory
26
+ */
27
+ export declare function buildComponentPath(uiPath: string, componentName: string): string;
28
+ /**
29
+ * Resolve all FSD paths for a component
30
+ * Orchestrates building individual path segments
31
+ */
32
+ export declare function resolveFsdPaths(rootDir: string, layer: string, slice: string, componentName: string): FsdPaths;
33
+ //# sourceMappingURL=resolvePaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvePaths.d.ts","sourceRoot":"","sources":["../../../src/lib/naming/resolvePaths.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,QAAQ;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACtB,QAAQ,CAcV"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * FSD path resolution logic.
3
+ *
4
+ * Determines the correct file system paths for entities based on their layer, slice,
5
+ * and name. Handles the structural rules of Feature-Sliced Design (e.g. shared vs page vs entity).
6
+ */
7
+ import { join } from 'path';
8
+ import { LAYER_PLURALS, FSD_SEGMENTS, FSD_LAYERS } from '../constants.js';
9
+ /**
10
+ * Map layer name to plural directory name
11
+ */
12
+ export function getLayerPlural(layer) {
13
+ return LAYER_PLURALS[layer] || layer;
14
+ }
15
+ /**
16
+ * Build the full path to the layer directory
17
+ */
18
+ export function buildLayerPath(rootDir, layer) {
19
+ const layerDir = getLayerPlural(layer);
20
+ return join(rootDir, layerDir);
21
+ }
22
+ /**
23
+ * Build the path to the slice directory
24
+ */
25
+ export function buildSlicePath(layerPath, slice) {
26
+ return join(layerPath, slice);
27
+ }
28
+ /**
29
+ * Build the path to the UI directory
30
+ * Handles special case for shared layer (no extra ui folder)
31
+ */
32
+ export function buildUiPath(slicePath, layer) {
33
+ const isShared = layer === FSD_LAYERS.SHARED;
34
+ return isShared ? slicePath : join(slicePath, FSD_SEGMENTS.UI);
35
+ }
36
+ /**
37
+ * Build the path to the component directory
38
+ */
39
+ export function buildComponentPath(uiPath, componentName) {
40
+ return join(uiPath, componentName);
41
+ }
42
+ /**
43
+ * Resolve all FSD paths for a component
44
+ * Orchestrates building individual path segments
45
+ */
46
+ export function resolveFsdPaths(rootDir, layer, slice, componentName) {
47
+ const layerPath = buildLayerPath(rootDir, layer);
48
+ const slicePath = buildSlicePath(layerPath, slice);
49
+ const uiPath = buildUiPath(slicePath, layer);
50
+ const componentPath = buildComponentPath(uiPath, componentName);
51
+ console.log('ResolvePaths:', { layer, slice, componentName, isShared: layer === FSD_LAYERS.SHARED, uiPath, componentPath });
52
+ return {
53
+ layerPath,
54
+ slicePath,
55
+ uiPath,
56
+ componentPath,
57
+ };
58
+ }
@@ -0,0 +1,32 @@
1
+ import { PresetAction, PresetComponentAction, PresetFileAction, FsdGenConfig } from '../../config/types.js';
2
+ /**
3
+ * Prepare variables for an action by merging global and action-specific variables
4
+ */
5
+ export declare function prepareActionVariables(action: PresetAction, name: string, globalVars: Record<string, any>): Record<string, any>;
6
+ /**
7
+ * Execute a component action (generate a component)
8
+ */
9
+ export declare function executeComponentAction(action: PresetComponentAction, variables: Record<string, any>, config: FsdGenConfig): Promise<void>;
10
+ /**
11
+ * Execute a hook action
12
+ */
13
+ export declare function executeHookAction(action: any, // Using any for now to avoid complexity with union types in loop
14
+ variables: Record<string, any>, config: FsdGenConfig): Promise<void>;
15
+ /**
16
+ * Execute a styles action
17
+ */
18
+ export declare function executeStylesAction(action: any, variables: Record<string, any>, config: FsdGenConfig): Promise<void>;
19
+ /**
20
+ * Load a file template from the templates directory
21
+ */
22
+ export declare function loadFileTemplate(templatePath: string, customTemplatesDir?: string): Promise<string>;
23
+ /**
24
+ * Execute a file action (create a file from template)
25
+ */
26
+ export declare function executeFileAction(action: PresetFileAction, variables: Record<string, any>, config: FsdGenConfig): Promise<void>;
27
+ /**
28
+ * Execute all preset actions
29
+ * Orchestrates executing component and file actions
30
+ */
31
+ export declare function executeActions(actions: PresetAction[], name: string, globalVars: Record<string, any>, config: FsdGenConfig): Promise<void>;
32
+ //# sourceMappingURL=actionExecution.d.ts.map