@starodubenko/fsd-gen 0.1.0 → 1.0.0-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.
- package/README.md +172 -0
- package/dist/config/defineConfig.d.ts +1 -1
- package/dist/config/defineConfig.d.ts.map +1 -1
- package/dist/config/types.d.ts +59 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/generators/generate.d.ts +1 -5
- package/dist/lib/generators/generate.d.ts.map +1 -1
- package/dist/lib/generators/generate.js +2 -2
- package/dist/lib/helpers/presetHelpers.d.ts +1 -27
- package/dist/lib/helpers/presetHelpers.d.ts.map +1 -1
- package/dist/lib/helpers/presetHelpers.js +24 -9
- package/dist/lib/helpers/presetHelpers.spec.d.ts +2 -0
- package/dist/lib/helpers/presetHelpers.spec.d.ts.map +1 -0
- package/dist/lib/helpers/presetHelpers.spec.js +36 -0
- package/dist/lib/helpers/templateHelpers.d.ts +20 -0
- package/dist/lib/helpers/templateHelpers.d.ts.map +1 -0
- package/dist/lib/helpers/templateHelpers.js +23 -0
- package/dist/lib/jsx/components.d.ts +38 -0
- package/dist/lib/jsx/components.d.ts.map +1 -0
- package/dist/lib/jsx/components.js +40 -0
- package/dist/lib/jsx/jsx-runtime.d.ts +20 -0
- package/dist/lib/jsx/jsx-runtime.d.ts.map +1 -0
- package/dist/lib/jsx/jsx-runtime.js +7 -0
- package/dist/lib/jsx/renderer.d.ts +2 -0
- package/dist/lib/jsx/renderer.d.ts.map +1 -0
- package/dist/lib/jsx/renderer.js +24 -0
- package/dist/lib/preset/actionExecution.d.ts +2 -2
- package/dist/lib/preset/actionExecution.d.ts.map +1 -1
- package/dist/lib/preset/actionExecution.js +39 -4
- package/dist/lib/preset/actionExecution.spec.d.ts +2 -0
- package/dist/lib/preset/actionExecution.spec.d.ts.map +1 -0
- package/dist/lib/preset/actionExecution.spec.js +178 -0
- package/dist/lib/preset/presetDiscovery.d.ts +10 -4
- package/dist/lib/preset/presetDiscovery.d.ts.map +1 -1
- package/dist/lib/preset/presetDiscovery.js +29 -21
- package/dist/lib/preset/presetLoading.d.ts.map +1 -1
- package/dist/lib/preset/presetLoading.js +2 -1
- package/dist/lib/templates/templateLoader.d.ts +7 -5
- package/dist/lib/templates/templateLoader.d.ts.map +1 -1
- package/dist/lib/templates/templateLoader.js +44 -7
- package/package.json +2 -2
- package/templates/entity/model-ui-basic/Component.tsx +18 -7
- package/templates/feature/ui-model-basic/Component.tsx +18 -7
- package/templates/page/ui-basic/Component.tsx +18 -7
- package/templates/preset/table/entity/model.ts +10 -4
- package/templates/preset/table/feature/buttons.tsx +12 -6
- package/templates/preset/table/page/page.tsx +30 -8
- package/templates/preset/table/widget/table.tsx +28 -11
- package/templates/shared/ui-basic/Component.tsx +17 -6
- package/templates/widget/ui-basic/Component.tsx +18 -7
- package/templates/entity/model-ui-basic/Component.styles.ts +0 -1
- package/templates/feature/ui-model-basic/Component.styles.ts +0 -1
- package/templates/page/ui-basic/Component.styles.ts +0 -1
- package/templates/shared/ui-basic/Component.styles.ts +0 -1
- package/templates/widget/ui-basic/Component.styles.ts +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Fragment } from './jsx-runtime.js';
|
|
2
|
+
export function render(node) {
|
|
3
|
+
if (node === null || node === undefined || typeof node === 'boolean') {
|
|
4
|
+
return '';
|
|
5
|
+
}
|
|
6
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
7
|
+
return String(node);
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(node)) {
|
|
10
|
+
return node.map(render).join('');
|
|
11
|
+
}
|
|
12
|
+
if (node.type) {
|
|
13
|
+
const vnode = node;
|
|
14
|
+
if (vnode.type === Fragment) {
|
|
15
|
+
return render(vnode.props.children);
|
|
16
|
+
}
|
|
17
|
+
if (typeof vnode.type === 'function') {
|
|
18
|
+
return render(vnode.type(vnode.props));
|
|
19
|
+
}
|
|
20
|
+
// Handle undefined type or other cases if necessary
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PresetAction, PresetComponentAction, PresetFileAction, FsdGenConfig } from '../../config/types.js';
|
|
1
|
+
import { PresetAction, PresetComponentAction, PresetFileAction, FsdGenConfig, TemplateContext } from '../../config/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Prepare variables for an action by merging global and action-specific variables
|
|
4
4
|
*/
|
|
@@ -19,7 +19,7 @@ export declare function executeStylesAction(action: any, variables: Record<strin
|
|
|
19
19
|
/**
|
|
20
20
|
* Load a file template from the templates directory
|
|
21
21
|
*/
|
|
22
|
-
export declare function loadFileTemplate(templatePath: string, customTemplatesDir?: string): Promise<string>;
|
|
22
|
+
export declare function loadFileTemplate(templatePath: string, customTemplatesDir?: string): Promise<string | ((context: TemplateContext) => string)>;
|
|
23
23
|
/**
|
|
24
24
|
* Execute a file action (create a file from template)
|
|
25
25
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionExecution.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/actionExecution.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"actionExecution.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/actionExecution.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7H;;GAEG;AACH,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAOrB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,qBAAqB,EAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACnC,MAAM,EAAE,GAAG,EAAE,iEAAiE;AAC9E,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,GAAG,EACX,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAClC,YAAY,EAAE,MAAM,EACpB,kBAAkB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,CAAC,CAiC1D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACnC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAChC,OAAO,EAAE,YAAY,EAAE,EACvB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { join, resolve, dirname, basename } from 'path';
|
|
8
8
|
import { writeFile, mkdir, readFile } from 'fs/promises';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
|
+
import { createJiti } from 'jiti';
|
|
10
11
|
import { updateBarrel } from '../barrels/updateBarrels.js';
|
|
11
12
|
import { ACTION_TYPES, FILE_EXTENSIONS } from '../constants.js';
|
|
12
13
|
import { generateComponent, generateHook, generateStyles } from '../generators/generate.js';
|
|
@@ -14,6 +15,7 @@ import { resolveFsdPaths } from '../naming/resolvePaths.js';
|
|
|
14
15
|
import { processTemplate } from '../templates/templateLoader.js';
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = dirname(__filename);
|
|
18
|
+
const jiti = createJiti(__filename);
|
|
17
19
|
/**
|
|
18
20
|
* Prepare variables for an action by merging global and action-specific variables
|
|
19
21
|
*/
|
|
@@ -36,7 +38,11 @@ export async function executeComponentAction(action, variables, config) {
|
|
|
36
38
|
...variables,
|
|
37
39
|
componentName,
|
|
38
40
|
sliceName,
|
|
39
|
-
|
|
41
|
+
template: {
|
|
42
|
+
componentName,
|
|
43
|
+
sliceName,
|
|
44
|
+
layer: action.layer,
|
|
45
|
+
},
|
|
40
46
|
};
|
|
41
47
|
await generateComponent(paths, context, action.template, config.templatesDir);
|
|
42
48
|
}
|
|
@@ -52,7 +58,11 @@ variables, config) {
|
|
|
52
58
|
...variables,
|
|
53
59
|
componentName,
|
|
54
60
|
sliceName,
|
|
55
|
-
|
|
61
|
+
template: {
|
|
62
|
+
componentName,
|
|
63
|
+
sliceName,
|
|
64
|
+
layer: action.layer,
|
|
65
|
+
},
|
|
56
66
|
};
|
|
57
67
|
await generateHook(paths, context, action.template, config.templatesDir);
|
|
58
68
|
}
|
|
@@ -67,7 +77,11 @@ export async function executeStylesAction(action, variables, config) {
|
|
|
67
77
|
...variables,
|
|
68
78
|
componentName,
|
|
69
79
|
sliceName,
|
|
70
|
-
|
|
80
|
+
template: {
|
|
81
|
+
componentName,
|
|
82
|
+
sliceName,
|
|
83
|
+
layer: action.layer,
|
|
84
|
+
},
|
|
71
85
|
};
|
|
72
86
|
await generateStyles(paths, context, action.template, config.templatesDir);
|
|
73
87
|
}
|
|
@@ -83,6 +97,19 @@ export async function loadFileTemplate(templatePath, customTemplatesDir) {
|
|
|
83
97
|
pathsToCheck.push(join(internalTemplatesDir, templatePath));
|
|
84
98
|
for (const p of pathsToCheck) {
|
|
85
99
|
try {
|
|
100
|
+
// Try loading as a module first if it's a TS/JS file
|
|
101
|
+
if (p.endsWith('.ts') || p.endsWith('.tsx') || p.endsWith('.js')) {
|
|
102
|
+
try {
|
|
103
|
+
const module = await jiti.import(p);
|
|
104
|
+
if (typeof module.default === 'function') {
|
|
105
|
+
console.log(`Loaded file template (module) from: ${p}`);
|
|
106
|
+
return module.default;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Not a module or failed to load, fall back to string read
|
|
111
|
+
}
|
|
112
|
+
}
|
|
86
113
|
const content = await readFile(p, 'utf-8');
|
|
87
114
|
console.log(`Loaded file template from: ${p}`);
|
|
88
115
|
return content;
|
|
@@ -100,8 +127,16 @@ export async function executeFileAction(action, variables, config) {
|
|
|
100
127
|
const targetPath = join(process.cwd(), config.rootDir, processTemplate(action.path, variables));
|
|
101
128
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
102
129
|
if (action.template) {
|
|
130
|
+
const context = {
|
|
131
|
+
...variables,
|
|
132
|
+
template: {
|
|
133
|
+
componentName: variables.componentName,
|
|
134
|
+
sliceName: variables.sliceName || '',
|
|
135
|
+
layer: typeof variables.layer === 'string' ? variables.layer : '',
|
|
136
|
+
},
|
|
137
|
+
};
|
|
103
138
|
const content = await loadFileTemplate(action.template, config.templatesDir);
|
|
104
|
-
const processed = processTemplate(content,
|
|
139
|
+
const processed = processTemplate(content, context);
|
|
105
140
|
await writeFile(targetPath, processed);
|
|
106
141
|
console.log(`Created ${targetPath}`);
|
|
107
142
|
// Update barrel for the file (only for TypeScript files)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionExecution.spec.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/actionExecution.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { executeComponentAction, executeHookAction, executeStylesAction, executeFileAction } from './actionExecution';
|
|
3
|
+
import { ACTION_TYPES, FSD_LAYERS } from '../constants';
|
|
4
|
+
import * as generateModule from '../generators/generate';
|
|
5
|
+
import * as templateLoader from '../templates/templateLoader';
|
|
6
|
+
import * as resolvePaths from '../naming/resolvePaths';
|
|
7
|
+
// Mock dependencies
|
|
8
|
+
vi.mock('../generators/generate');
|
|
9
|
+
vi.mock('../templates/templateLoader');
|
|
10
|
+
vi.mock('../naming/resolvePaths');
|
|
11
|
+
describe('actionExecution', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
// Setup default mocks
|
|
15
|
+
vi.spyOn(templateLoader, 'processTemplate').mockImplementation((tmpl) => tmpl);
|
|
16
|
+
vi.spyOn(resolvePaths, 'resolveFsdPaths').mockReturnValue({
|
|
17
|
+
layerPath: '/mock/entities',
|
|
18
|
+
slicePath: '/mock/entities/User',
|
|
19
|
+
uiPath: '/mock/entities/User/ui',
|
|
20
|
+
componentPath: '/mock/entities/User/ui/UserCard'
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('executeComponentAction', () => {
|
|
24
|
+
it('should pass a complete GeneratorContext to generateComponent', async () => {
|
|
25
|
+
const config = { rootDir: 'src', templatesDir: '.templates' };
|
|
26
|
+
const action = {
|
|
27
|
+
type: ACTION_TYPES.COMPONENT,
|
|
28
|
+
layer: FSD_LAYERS.ENTITY,
|
|
29
|
+
slice: 'User',
|
|
30
|
+
name: 'UserCard',
|
|
31
|
+
template: 'user-card.tsx'
|
|
32
|
+
};
|
|
33
|
+
// Mimic variables object populated by createPresetHelpers + globalVars
|
|
34
|
+
const variables = {
|
|
35
|
+
base: {
|
|
36
|
+
baseName: 'User',
|
|
37
|
+
name: 'User',
|
|
38
|
+
},
|
|
39
|
+
layer: {
|
|
40
|
+
entity: {
|
|
41
|
+
importPath: '@entities/User',
|
|
42
|
+
apiPath: '@entities/User/ui',
|
|
43
|
+
},
|
|
44
|
+
features: {
|
|
45
|
+
slice: 'ManageUser',
|
|
46
|
+
importPath: '@features/ManageUser',
|
|
47
|
+
},
|
|
48
|
+
widget: {
|
|
49
|
+
slice: 'UserTable',
|
|
50
|
+
importPath: '@widgets/UserTable',
|
|
51
|
+
},
|
|
52
|
+
page: {
|
|
53
|
+
slice: 'UserPage',
|
|
54
|
+
importPath: '@pages/UserPage',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
extraGlobal: 'something'
|
|
58
|
+
};
|
|
59
|
+
await executeComponentAction(action, variables, config);
|
|
60
|
+
// Assert generateComponent was called
|
|
61
|
+
expect(generateModule.generateComponent).toHaveBeenCalled();
|
|
62
|
+
// Capture the context passed to generateComponent
|
|
63
|
+
const callArgs = vi.mocked(generateModule.generateComponent).mock.calls[0];
|
|
64
|
+
const context = callArgs[1];
|
|
65
|
+
// Verify template keys
|
|
66
|
+
expect(context.template.componentName).toBe('UserCard');
|
|
67
|
+
expect(context.template.sliceName).toBe('User');
|
|
68
|
+
expect(context.template.layer).toBe(FSD_LAYERS.ENTITY);
|
|
69
|
+
// Verify keys from PresetHelpers (passed in via variables)
|
|
70
|
+
expect(context.base.name).toBe('User');
|
|
71
|
+
expect(context.layer.entity.apiPath).toBe('@entities/User/ui');
|
|
72
|
+
// Verify it retains other variables
|
|
73
|
+
expect(context).toHaveProperty('extraGlobal', 'something');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
// Shared variables for all tests
|
|
77
|
+
const commonVariables = {
|
|
78
|
+
base: {
|
|
79
|
+
baseName: 'User',
|
|
80
|
+
name: 'User',
|
|
81
|
+
},
|
|
82
|
+
layer: {
|
|
83
|
+
entity: {
|
|
84
|
+
importPath: '@entities/User',
|
|
85
|
+
apiPath: '@entities/User/ui',
|
|
86
|
+
},
|
|
87
|
+
features: {
|
|
88
|
+
slice: 'ManageUser',
|
|
89
|
+
importPath: '@features/ManageUser',
|
|
90
|
+
},
|
|
91
|
+
widget: {
|
|
92
|
+
slice: 'UserTable',
|
|
93
|
+
importPath: '@widgets/UserTable',
|
|
94
|
+
},
|
|
95
|
+
page: {
|
|
96
|
+
slice: 'UserPage',
|
|
97
|
+
importPath: '@pages/UserPage',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
extraGlobal: 'something'
|
|
101
|
+
};
|
|
102
|
+
const commonConfig = { rootDir: 'src', templatesDir: '.templates' };
|
|
103
|
+
describe('executeHookAction', () => {
|
|
104
|
+
it('should pass a complete GeneratorContext to generateHook', async () => {
|
|
105
|
+
const action = {
|
|
106
|
+
type: ACTION_TYPES.HOOK,
|
|
107
|
+
layer: FSD_LAYERS.ENTITY,
|
|
108
|
+
slice: 'User',
|
|
109
|
+
name: 'useUser',
|
|
110
|
+
template: 'use-user.ts'
|
|
111
|
+
};
|
|
112
|
+
await executeHookAction(action, commonVariables, commonConfig);
|
|
113
|
+
expect(generateModule.generateHook).toHaveBeenCalled();
|
|
114
|
+
const callArgs = vi.mocked(generateModule.generateHook).mock.calls[0];
|
|
115
|
+
const context = callArgs[1];
|
|
116
|
+
// Verify template keys
|
|
117
|
+
expect(context.template.componentName).toBe('useUser');
|
|
118
|
+
expect(context.template.sliceName).toBe('User');
|
|
119
|
+
expect(context.template.layer).toBe(FSD_LAYERS.ENTITY);
|
|
120
|
+
// Verify keys from PresetHelpers
|
|
121
|
+
expect(context.base.name).toBe('User');
|
|
122
|
+
expect(context.layer.entity.apiPath).toBe('@entities/User/ui');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('executeStylesAction', () => {
|
|
126
|
+
it('should pass a complete GeneratorContext to generateStyles', async () => {
|
|
127
|
+
const action = {
|
|
128
|
+
type: ACTION_TYPES.STYLES,
|
|
129
|
+
layer: FSD_LAYERS.ENTITY,
|
|
130
|
+
slice: 'User',
|
|
131
|
+
name: 'UserCard',
|
|
132
|
+
template: 'user-card.module.css'
|
|
133
|
+
};
|
|
134
|
+
await executeStylesAction(action, commonVariables, commonConfig);
|
|
135
|
+
expect(generateModule.generateStyles).toHaveBeenCalled();
|
|
136
|
+
const callArgs = vi.mocked(generateModule.generateStyles).mock.calls[0];
|
|
137
|
+
const context = callArgs[1];
|
|
138
|
+
// Verify template keys (name falls back to slice if not provided, but here we provide it)
|
|
139
|
+
expect(context.template.componentName).toBe('UserCard');
|
|
140
|
+
expect(context.template.sliceName).toBe('User');
|
|
141
|
+
expect(context.template.layer).toBe(FSD_LAYERS.ENTITY);
|
|
142
|
+
// Verify keys from PresetHelpers
|
|
143
|
+
expect(context.base.name).toBe('User');
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe('executeFileAction', () => {
|
|
147
|
+
it('should pass a complete GeneratorContext to template processing', async () => {
|
|
148
|
+
const action = {
|
|
149
|
+
type: ACTION_TYPES.FILE,
|
|
150
|
+
path: 'model/types.ts',
|
|
151
|
+
template: 'types.ts'
|
|
152
|
+
};
|
|
153
|
+
// loadFileTemplate mock to return a string template
|
|
154
|
+
vi.spyOn(require('./actionExecution'), 'loadFileTemplate').mockResolvedValue('template content');
|
|
155
|
+
// Mock mkDir and writeFile to avoid FS errors
|
|
156
|
+
const fsMock = await import('fs/promises');
|
|
157
|
+
vi.spyOn(fsMock, 'mkdir').mockResolvedValue(undefined);
|
|
158
|
+
vi.spyOn(fsMock, 'writeFile').mockResolvedValue(undefined);
|
|
159
|
+
// executeFileAction uses processTemplate to resolve the path AND the content
|
|
160
|
+
// We want to capture the context used when processing the CONTENT
|
|
161
|
+
// The first call is for path resolution, second for content
|
|
162
|
+
await executeFileAction(action, { ...commonVariables, componentName: 'User', sliceName: 'User' }, commonConfig);
|
|
163
|
+
expect(templateLoader.processTemplate).toHaveBeenCalledTimes(2);
|
|
164
|
+
// The second call to processTemplate should receive the context
|
|
165
|
+
const contentCallArgs = vi.mocked(templateLoader.processTemplate).mock.calls[1];
|
|
166
|
+
const context = contentCallArgs[1];
|
|
167
|
+
// Verify template keys
|
|
168
|
+
expect(context.template).toBeDefined();
|
|
169
|
+
expect(context.template.componentName).toBe('User');
|
|
170
|
+
expect(context.template.sliceName).toBe('User');
|
|
171
|
+
// Layer might be empty string for generic files if not provided in variables
|
|
172
|
+
expect(context.template.layer).toBeDefined();
|
|
173
|
+
// Verify keys from PresetHelpers
|
|
174
|
+
expect(context.base.name).toBe('User');
|
|
175
|
+
expect(context.layer.entity.apiPath).toBe('@entities/User/ui');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -19,15 +19,21 @@ export declare function createEntityApiActions(apiDir: string, entityName: strin
|
|
|
19
19
|
/**
|
|
20
20
|
* Create component actions for feature buttons
|
|
21
21
|
*/
|
|
22
|
-
export declare function createFeatureButtonActions(buttonsDir: string, entityName: string, presetName: string, conventions: ConventionConfig): Promise<PresetAction[]>;
|
|
23
22
|
/**
|
|
24
|
-
* Create component
|
|
23
|
+
* Create component actions for feature sub-components (grouped by directory)
|
|
24
|
+
*/
|
|
25
|
+
export declare function createFeatureActions(featureDir: string, entityName: string, presetName: string, conventions: ConventionConfig): Promise<PresetAction[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Create component action for widget
|
|
28
|
+
*/
|
|
29
|
+
export declare function createWidgetAction(entityName: string, presetName: string, conventions: ConventionConfig, templateDir: string): PresetComponentAction;
|
|
30
|
+
/**
|
|
31
|
+
* Create component action for page
|
|
25
32
|
*/
|
|
26
|
-
export declare function createWidgetTableAction(entityName: string, presetName: string, conventions: ConventionConfig): PresetComponentAction;
|
|
27
33
|
/**
|
|
28
34
|
* Create component action for page
|
|
29
35
|
*/
|
|
30
|
-
export declare function createPageAction(entityName: string, presetName: string, conventions: ConventionConfig): PresetComponentAction;
|
|
36
|
+
export declare function createPageAction(entityName: string, presetName: string, conventions: ConventionConfig, templateDir: string): PresetComponentAction;
|
|
31
37
|
/**
|
|
32
38
|
* Create component action for shared
|
|
33
39
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presetDiscovery.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/presetDiscovery.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAYhH;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW5F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,gBAAgB,CASlB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAChC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,qBAAqB,CAQvB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,OAAO,CAAC,YAAY,EAAE,CAAC,CAuBzB;AAED;;GAEG;AACH,wBAAsB,
|
|
1
|
+
{"version":3,"file":"presetDiscovery.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/presetDiscovery.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAYhH;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW5F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,gBAAgB,CASlB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAChC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,qBAAqB,CAQvB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,OAAO,CAAC,YAAY,EAAE,CAAC,CAuBzB;AAED;;GAEG;AACH;;GAEG;AACH,wBAAsB,oBAAoB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,gBAAgB,GAC9B,OAAO,CAAC,YAAY,EAAE,CAAC,CAoBzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAC9B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,gBAAgB,EAC7B,WAAW,EAAE,MAAM,GACpB,qBAAqB,CAUvB;AAED;;GAEG;AACH;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,gBAAgB,EAC7B,WAAW,EAAE,MAAM,GACpB,qBAAqB,CAUvB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACnB,qBAAqB,CAUvB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACnC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,GAAE,gBAAqB,GACnC,OAAO,CAAC,YAAY,EAAE,CAAC,CAuDzB"}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Converts the file system structure of a preset into a list of executable generator actions.
|
|
12
12
|
*/
|
|
13
13
|
import { readdir, stat } from 'fs/promises';
|
|
14
|
-
import { join } from 'path';
|
|
14
|
+
import { join, basename } from 'path';
|
|
15
15
|
import { LAYER_PLURALS, FSD_LAYERS, FSD_SEGMENTS, FILE_EXTENSIONS, PRESET_DIRS, API_HOOK_PREFIXES, API_OPERATIONS, ACTION_TYPES } from '../constants.js';
|
|
16
16
|
/**
|
|
17
17
|
* Scan a layer directory for entries
|
|
@@ -79,49 +79,55 @@ export async function createEntityApiActions(apiDir, entityName, presetName) {
|
|
|
79
79
|
/**
|
|
80
80
|
* Create component actions for feature buttons
|
|
81
81
|
*/
|
|
82
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Create component actions for feature sub-components (grouped by directory)
|
|
84
|
+
*/
|
|
85
|
+
export async function createFeatureActions(featureDir, entityName, presetName, conventions) {
|
|
83
86
|
const actions = [];
|
|
84
87
|
const featurePrefix = conventions.featureSlicePrefix ?? 'Manage';
|
|
85
|
-
const
|
|
86
|
-
for (const
|
|
87
|
-
if (
|
|
88
|
-
const
|
|
89
|
-
const
|
|
88
|
+
const featureEntries = await readdir(featureDir, { withFileTypes: true });
|
|
89
|
+
for (const featureEntry of featureEntries) {
|
|
90
|
+
if (featureEntry.isDirectory()) {
|
|
91
|
+
const featureName = featureEntry.name;
|
|
92
|
+
const capitalizedName = featureName.charAt(0).toUpperCase() + featureName.slice(1);
|
|
90
93
|
actions.push({
|
|
91
94
|
type: ACTION_TYPES.COMPONENT,
|
|
92
95
|
layer: FSD_LAYERS.FEATURE,
|
|
93
96
|
slice: `${featurePrefix}${entityName}`,
|
|
94
|
-
name: `${
|
|
95
|
-
template: `${PRESET_DIRS.ROOT}/${presetName}/${FSD_LAYERS.FEATURE}/${
|
|
97
|
+
name: `${capitalizedName}${entityName}`,
|
|
98
|
+
template: `${PRESET_DIRS.ROOT}/${presetName}/${FSD_LAYERS.FEATURE}/${basename(featureDir)}/${featureEntry.name}`
|
|
96
99
|
});
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
102
|
return actions;
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
|
-
* Create component action for widget
|
|
105
|
+
* Create component action for widget
|
|
103
106
|
*/
|
|
104
|
-
export function
|
|
107
|
+
export function createWidgetAction(entityName, presetName, conventions, templateDir) {
|
|
105
108
|
const widgetSuffix = conventions.widgetSliceSuffix ?? 'Table';
|
|
106
109
|
return {
|
|
107
110
|
type: ACTION_TYPES.COMPONENT,
|
|
108
111
|
layer: FSD_LAYERS.WIDGET,
|
|
109
112
|
slice: `${entityName}${widgetSuffix}`,
|
|
110
113
|
name: `${entityName}${widgetSuffix}`,
|
|
111
|
-
template: `${PRESET_DIRS.ROOT}/${presetName}/${FSD_LAYERS.WIDGET}/${
|
|
114
|
+
template: `${PRESET_DIRS.ROOT}/${presetName}/${FSD_LAYERS.WIDGET}/${templateDir}`
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
/**
|
|
115
118
|
* Create component action for page
|
|
116
119
|
*/
|
|
117
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Create component action for page
|
|
122
|
+
*/
|
|
123
|
+
export function createPageAction(entityName, presetName, conventions, templateDir) {
|
|
118
124
|
const pageSuffix = conventions.pageSliceSuffix ?? 'Page';
|
|
119
125
|
return {
|
|
120
126
|
type: ACTION_TYPES.COMPONENT,
|
|
121
127
|
layer: FSD_LAYERS.PAGE,
|
|
122
128
|
slice: `${entityName}${pageSuffix}`,
|
|
123
129
|
name: `${entityName}${pageSuffix}`,
|
|
124
|
-
template: `${PRESET_DIRS.ROOT}/${presetName}/${FSD_LAYERS.PAGE}/${
|
|
130
|
+
template: `${PRESET_DIRS.ROOT}/${presetName}/${FSD_LAYERS.PAGE}/${templateDir}`
|
|
125
131
|
};
|
|
126
132
|
}
|
|
127
133
|
/**
|
|
@@ -166,17 +172,19 @@ export async function discoverTemplates(presetDir, presetName, entityName, conve
|
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
// Feature layer
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
// Feature layer
|
|
176
|
+
else if (layer === FSD_LAYERS.FEATURE) {
|
|
177
|
+
// Treat any directory in feature layer as a potential specific feature group
|
|
178
|
+
const featureActions = await createFeatureActions(fullPath, entityName, presetName, conventions);
|
|
179
|
+
actions.push(...featureActions);
|
|
172
180
|
}
|
|
173
181
|
// Widget layer
|
|
174
|
-
else if (layer === FSD_LAYERS.WIDGET
|
|
175
|
-
actions.push(
|
|
182
|
+
else if (layer === FSD_LAYERS.WIDGET) {
|
|
183
|
+
actions.push(createWidgetAction(entityName, presetName, conventions, entry.name));
|
|
176
184
|
}
|
|
177
185
|
// Page layer
|
|
178
|
-
else if (layer === FSD_LAYERS.PAGE
|
|
179
|
-
actions.push(createPageAction(entityName, presetName, conventions));
|
|
186
|
+
else if (layer === FSD_LAYERS.PAGE) {
|
|
187
|
+
actions.push(createPageAction(entityName, presetName, conventions, entry.name));
|
|
180
188
|
}
|
|
181
189
|
// Shared layer
|
|
182
190
|
else if (layer === FSD_LAYERS.SHARED) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presetLoading.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/presetLoading.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErG;;;GAGG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,cAAc,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"presetLoading.d.ts","sourceRoot":"","sources":["../../../src/lib/preset/presetLoading.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErG;;;GAGG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,cAAc,GAAG,IAAI,CAAC,CAcnG;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAUpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,YAAY,GAAG,cAAc,EACrC,IAAI,EAAE,gBAAgB,GACvB,YAAY,CAKd;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAClC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAgB9B"}
|
|
@@ -20,7 +20,8 @@ export async function loadPresetTs(presetDir) {
|
|
|
20
20
|
const imported = await jiti.import(presetTsPath, { default: true });
|
|
21
21
|
return imported.default || imported;
|
|
22
22
|
}
|
|
23
|
-
catch {
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error(`Failed to load preset content from ${presetTsPath}:`, error);
|
|
24
25
|
return null;
|
|
25
26
|
}
|
|
26
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TemplateContext } from '../../config/types.js';
|
|
1
2
|
/**
|
|
2
3
|
* Resolve the list of directories to search for templates
|
|
3
4
|
* Custom directory is checked first, then internal templates
|
|
@@ -10,22 +11,23 @@ export declare function resolveTemplateDirs(customTemplatesDir?: string): string
|
|
|
10
11
|
export declare function findTemplateDir(layer: string, type: string, searchDirs: string[]): Promise<string | null>;
|
|
11
12
|
/**
|
|
12
13
|
* Read the component template file
|
|
14
|
+
* Supports .tsx, .ts, .js for dynamic templates, falls back to static string reading
|
|
13
15
|
*/
|
|
14
|
-
export declare function readComponentTemplate(templateDir: string): Promise<string>;
|
|
16
|
+
export declare function readComponentTemplate(templateDir: string): Promise<string | ((context: TemplateContext) => string)>;
|
|
15
17
|
/**
|
|
16
18
|
* Read the styles template file (optional)
|
|
17
19
|
* @returns The styles content or empty string if not found
|
|
18
20
|
*/
|
|
19
|
-
export declare function readStylesTemplate(templateDir: string): Promise<string>;
|
|
21
|
+
export declare function readStylesTemplate(templateDir: string): Promise<string | ((context: TemplateContext) => string)>;
|
|
20
22
|
/**
|
|
21
23
|
* Load a template for a given layer and type
|
|
22
24
|
* Orchestrates finding and reading template files
|
|
23
25
|
*/
|
|
24
26
|
export declare function loadTemplate(layer: string, type?: string, customTemplatesDir?: string): Promise<{
|
|
25
|
-
component: string;
|
|
26
|
-
styles: string;
|
|
27
|
+
component: string | ((context: TemplateContext) => string);
|
|
28
|
+
styles: string | ((context: TemplateContext) => string);
|
|
27
29
|
}>;
|
|
28
|
-
export declare function processTemplate(content: string, variables: Record<string, any>): string;
|
|
30
|
+
export declare function processTemplate(content: string | ((context: TemplateContext) => string), variables: Record<string, any> | TemplateContext): string;
|
|
29
31
|
export declare function listPresets(customTemplatesDir?: string): Promise<string[]>;
|
|
30
32
|
export declare function resolvePresetDir(presetName: string, customTemplatesDir?: string): Promise<string | null>;
|
|
31
33
|
//# sourceMappingURL=templateLoader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templateLoader.d.ts","sourceRoot":"","sources":["../../../src/lib/templates/templateLoader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templateLoader.d.ts","sourceRoot":"","sources":["../../../src/lib/templates/templateLoader.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAOxD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQzE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAexB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,CAAC,CAuBzH;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,CAAC,CA0BtH;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAa,EACnB,kBAAkB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,CAAA;CAAE,CAAC,CAclI;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,eAAe,GAAG,MAAM,CAKlJ;AAED,wBAAsB,WAAW,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBhF;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiB9G"}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template loading and processing.
|
|
3
|
-
*
|
|
4
|
-
* Responsible for finding, reading, and processing template files. Handles variable
|
|
5
|
-
* substitution in template content and resolves template paths based on layers and types.
|
|
6
|
-
*/
|
|
7
1
|
import { readFile, readdir, stat } from 'fs/promises';
|
|
8
|
-
import { join, dirname } from 'path';
|
|
2
|
+
import { join, dirname, resolve } from 'path';
|
|
9
3
|
import { fileURLToPath } from 'url';
|
|
4
|
+
import { createJiti } from 'jiti';
|
|
10
5
|
import { TEMPLATE_FILES, PRESET_DIRS } from '../constants.js';
|
|
11
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
7
|
const __dirname = dirname(__filename);
|
|
8
|
+
const jiti = createJiti(__filename);
|
|
13
9
|
/**
|
|
14
10
|
* Resolve the list of directories to search for templates
|
|
15
11
|
* Custom directory is checked first, then internal templates
|
|
@@ -42,8 +38,28 @@ export async function findTemplateDir(layer, type, searchDirs) {
|
|
|
42
38
|
}
|
|
43
39
|
/**
|
|
44
40
|
* Read the component template file
|
|
41
|
+
* Supports .tsx, .ts, .js for dynamic templates, falls back to static string reading
|
|
45
42
|
*/
|
|
46
43
|
export async function readComponentTemplate(templateDir) {
|
|
44
|
+
// Try to find dynamic template files
|
|
45
|
+
const extensions = ['.tsx', '.ts', '.js'];
|
|
46
|
+
const baseName = TEMPLATE_FILES.COMPONENT.replace('.tsx', ''); // Removing extension if present in constant to try different ones
|
|
47
|
+
// Check for Component.tsx, Component.ts, Component.js that are modules
|
|
48
|
+
for (const ext of extensions) {
|
|
49
|
+
try {
|
|
50
|
+
const modulePath = resolve(templateDir, `Component${ext}`);
|
|
51
|
+
await stat(modulePath);
|
|
52
|
+
// Found a module file, load it with jiti
|
|
53
|
+
const module = await jiti.import(modulePath);
|
|
54
|
+
if (typeof module.default === 'function') {
|
|
55
|
+
return module.default;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Continue
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Fallback to reading as text (original behavior)
|
|
47
63
|
const componentPath = join(templateDir, TEMPLATE_FILES.COMPONENT);
|
|
48
64
|
return await readFile(componentPath, 'utf-8');
|
|
49
65
|
}
|
|
@@ -52,6 +68,24 @@ export async function readComponentTemplate(templateDir) {
|
|
|
52
68
|
* @returns The styles content or empty string if not found
|
|
53
69
|
*/
|
|
54
70
|
export async function readStylesTemplate(templateDir) {
|
|
71
|
+
// Try to find dynamic template files
|
|
72
|
+
const extensions = ['.ts', '.js'];
|
|
73
|
+
// Check for Component.styles.ts, Component.styles.js
|
|
74
|
+
for (const ext of extensions) {
|
|
75
|
+
try {
|
|
76
|
+
// Adjust this if TEMPLATE_FILES.STYLES differs
|
|
77
|
+
const modulePath = join(templateDir, `Component.styles${ext}`);
|
|
78
|
+
await stat(modulePath);
|
|
79
|
+
// Found a module file, load it with jiti
|
|
80
|
+
const module = await jiti.import(modulePath);
|
|
81
|
+
if (typeof module.default === 'function') {
|
|
82
|
+
return module.default;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Continue
|
|
87
|
+
}
|
|
88
|
+
}
|
|
55
89
|
const stylesPath = join(templateDir, TEMPLATE_FILES.STYLES);
|
|
56
90
|
try {
|
|
57
91
|
return await readFile(stylesPath, 'utf-8');
|
|
@@ -77,6 +111,9 @@ export async function loadTemplate(layer, type = 'ui', customTemplatesDir) {
|
|
|
77
111
|
return { component, styles };
|
|
78
112
|
}
|
|
79
113
|
export function processTemplate(content, variables) {
|
|
114
|
+
if (typeof content === 'function') {
|
|
115
|
+
return content(variables);
|
|
116
|
+
}
|
|
80
117
|
return content.replace(/\{\s*\{\s*(\w+)\s*\}\s*\}/g, (_, key) => String(variables[key] ?? ''));
|
|
81
118
|
}
|
|
82
119
|
export async function listPresets(customTemplatesDir) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starodubenko/fsd-gen",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-0",
|
|
4
4
|
"description": "A powerful CLI tool for scaffolding Feature-Sliced Design (FSD) components, slices, and layers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/
|
|
22
|
+
"url": "git+https://github.com/Starodubenko/fsd-generator.git"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"fsd",
|