@futurebrand/dev-tools 2.5.3 → 2.6.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/dist/commands/ai/modules/figma/api.d.ts +0 -1
- package/dist/commands/ai/modules/figma/api.js +0 -4
- package/dist/commands/ai/modules/figma/index.js +5 -11
- package/dist/commands/ai/modules/figma/types.d.ts +0 -3
- package/dist/commands/ai/modules/file-manager/index.d.ts +8 -1
- package/dist/commands/ai/modules/file-manager/index.js +145 -5
- package/dist/commands/ai/modules/file-manager/types.d.ts +8 -0
- package/dist/commands/ai/modules/generator/index.d.ts +1 -1
- package/dist/commands/ai/modules/generator/index.js +44 -26
- package/dist/commands/ai/modules/generator/types.d.ts +12 -0
- package/dist/commands/ai/modules/state/index.d.ts +2 -1
- package/dist/commands/ai/modules/state/index.js +7 -4
- package/dist/commands/ai/modules/state/types.d.ts +0 -1
- package/dist/modules/ai-server-module/index.js +17 -1
- package/dist/utils/files.d.ts +1 -1
- package/dist/utils/files.js +5 -3
- package/package.json +3 -2
|
@@ -3,6 +3,5 @@ export declare class FigmaAPI {
|
|
|
3
3
|
private state;
|
|
4
4
|
constructor(state: AIState);
|
|
5
5
|
getBlockCanvas(pageId: string): Promise<string>;
|
|
6
|
-
getVariables(pageId: string): Promise<Record<string, any>>;
|
|
7
6
|
getBlocks(pageId: string, canvasId: string): Promise<Record<string, import("./types").IFigmaNode[]>>;
|
|
8
7
|
}
|
|
@@ -10,10 +10,6 @@ class FigmaAPI {
|
|
|
10
10
|
const { id } = await this.state.fetchApi(`/figma/${pageId}/blocks`);
|
|
11
11
|
return id;
|
|
12
12
|
}
|
|
13
|
-
async getVariables(pageId) {
|
|
14
|
-
const { variables } = await this.state.fetchApi(`/figma/${pageId}/blocks`);
|
|
15
|
-
return variables;
|
|
16
|
-
}
|
|
17
13
|
async getBlocks(pageId, canvasId) {
|
|
18
14
|
const { blocks } = await this.state.fetchApi(`/figma/${pageId}/blocks/${canvasId}`);
|
|
19
15
|
return blocks;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const files_1 = require("../../../../utils/files");
|
|
4
3
|
const inquirer_1 = require("inquirer");
|
|
5
|
-
const generator_1 = require("../generator");
|
|
6
4
|
const api_1 = require("./api");
|
|
7
5
|
class FigmaModule {
|
|
8
6
|
state;
|
|
@@ -26,11 +24,11 @@ class FigmaModule {
|
|
|
26
24
|
const defaultBlocks = [];
|
|
27
25
|
const nextProject = data.projects.find((project) => project.type === 'next.js');
|
|
28
26
|
for (const block of blocks) {
|
|
29
|
-
const tempFolder = await (0, files_1.getTempFilePath)(generator_1.GENERATOR_FOLDER_NAME, block.name);
|
|
30
|
-
if (await (0, files_1.verifyPath)(tempFolder)) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
27
|
if (nextProject) {
|
|
28
|
+
const alreadyGenerated = await this.state.file.verifyBlockAlreadyGenerated(block.name, nextProject);
|
|
29
|
+
if (alreadyGenerated) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
34
32
|
const alreadyExist = await this.state.file.verifyBlockAlreadyExists(nextProject, block.name);
|
|
35
33
|
if (alreadyExist) {
|
|
36
34
|
continue;
|
|
@@ -49,10 +47,7 @@ class FigmaModule {
|
|
|
49
47
|
const canvas = await this.api.getBlockCanvas(figmaPageID);
|
|
50
48
|
console.log('- Loading pages blocks');
|
|
51
49
|
const blocksData = [];
|
|
52
|
-
const
|
|
53
|
-
this.api.getBlocks(figmaPageID, canvas),
|
|
54
|
-
this.api.getVariables(figmaPageID),
|
|
55
|
-
]);
|
|
50
|
+
const blocks = await this.api.getBlocks(figmaPageID, canvas);
|
|
56
51
|
Object.keys(blocks).forEach((blockName) => {
|
|
57
52
|
let block = blocksData.find((block) => block.name === blockName);
|
|
58
53
|
if (!block) {
|
|
@@ -88,7 +83,6 @@ class FigmaModule {
|
|
|
88
83
|
}
|
|
89
84
|
}
|
|
90
85
|
this.state.setBlocks(blocksData);
|
|
91
|
-
this.state.setVariables(variables);
|
|
92
86
|
console.log('- Figma blocks loaded successfully');
|
|
93
87
|
}
|
|
94
88
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { IProject } from '../../../../types/project';
|
|
2
|
+
import type { INextjsGenerateResponse, IStrapiGenerateResponse } from '../generator/types';
|
|
2
3
|
import type { IBlockData } from '../state/types';
|
|
4
|
+
import type { IProjectFile, IProjectFileSegment } from './types';
|
|
3
5
|
export declare const GENERATOR_FOLDER_NAME = "generated-blocks";
|
|
4
6
|
export declare const GENERATOR_STRAPI_FILE_NAME = "strapi.json";
|
|
5
7
|
export declare const GENERATOR_NEXTJS_FILE_NAME = "nextjs.tsx";
|
|
8
|
+
export declare const GENERATOR_NEXTJS_EXTRA_FILES = "nextjs-extra-files.json";
|
|
6
9
|
declare class FileManager {
|
|
7
10
|
private getBlockTempPath;
|
|
8
11
|
private getBlockTempFilePath;
|
|
@@ -14,9 +17,13 @@ declare class FileManager {
|
|
|
14
17
|
getAvailableBlocksToGenerate(blocks: IBlockData[], project: IProject): Promise<IBlockData[]>;
|
|
15
18
|
getAvailableBlocksToAdd(blocks: IBlockData[], project: IProject): Promise<IBlockData[]>;
|
|
16
19
|
createBlockTempFolder(blockName: string): Promise<void>;
|
|
17
|
-
saveGeneratedBlock(blockName: string, project: IProject, content:
|
|
20
|
+
saveGeneratedBlock(blockName: string, project: IProject, content: IStrapiGenerateResponse | INextjsGenerateResponse): Promise<void>;
|
|
18
21
|
loadGeneratedBlock(blockName: string, project: IProject): Promise<string>;
|
|
19
22
|
addNextBlockToProject(blockName: string, project: IProject): Promise<void>;
|
|
20
23
|
addStrapiBlockToProject(blockName: string, project: IProject): Promise<void>;
|
|
24
|
+
loadFilesInPath(rootPath: string, relativeTo?: string, includeSubFolders?: boolean): Promise<IProjectFile[]>;
|
|
25
|
+
loadProjectFiles(project: IProject | null, subDir: string): Promise<IProjectFileSegment[]>;
|
|
26
|
+
loadCustomPrompts(blockName: string): Promise<string>;
|
|
27
|
+
loadProjectCSS(nextProject?: IProject): Promise<string | null>;
|
|
21
28
|
}
|
|
22
29
|
export default FileManager;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GENERATOR_NEXTJS_FILE_NAME = exports.GENERATOR_STRAPI_FILE_NAME = exports.GENERATOR_FOLDER_NAME = void 0;
|
|
3
|
+
exports.GENERATOR_NEXTJS_EXTRA_FILES = exports.GENERATOR_NEXTJS_FILE_NAME = exports.GENERATOR_STRAPI_FILE_NAME = exports.GENERATOR_FOLDER_NAME = void 0;
|
|
4
4
|
const fs = require("node:fs/promises");
|
|
5
5
|
const path = require("node:path");
|
|
6
6
|
const files_1 = require("../../../../utils/files");
|
|
7
7
|
exports.GENERATOR_FOLDER_NAME = 'generated-blocks';
|
|
8
8
|
exports.GENERATOR_STRAPI_FILE_NAME = 'strapi.json';
|
|
9
9
|
exports.GENERATOR_NEXTJS_FILE_NAME = 'nextjs.tsx';
|
|
10
|
+
exports.GENERATOR_NEXTJS_EXTRA_FILES = 'nextjs-extra-files.json';
|
|
10
11
|
class FileManager {
|
|
11
12
|
async getBlockTempPath(blockName) {
|
|
12
13
|
const tempFolder = await (0, files_1.getTempFilePath)(exports.GENERATOR_FOLDER_NAME, blockName);
|
|
@@ -98,9 +99,23 @@ class FileManager {
|
|
|
98
99
|
await (0, files_1.verifyAndCreateFolder)(tempFolder);
|
|
99
100
|
}
|
|
100
101
|
async saveGeneratedBlock(blockName, project, content) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
if (project.type === 'next.js') {
|
|
103
|
+
const blockPath = await this.getBlockTempFilePath(project, blockName);
|
|
104
|
+
await fs.writeFile(blockPath, content.block);
|
|
105
|
+
const blockDir = path.dirname(blockPath);
|
|
106
|
+
const definitionPath = path.join(blockDir, 'nextjs-definition.json');
|
|
107
|
+
await fs.writeFile(definitionPath, JSON.stringify(content.definition, null, 2));
|
|
108
|
+
const extraFiles = content.extraFiles || [];
|
|
109
|
+
if (extraFiles.length > 0) {
|
|
110
|
+
const extraFilesPath = path.join(blockDir, exports.GENERATOR_NEXTJS_EXTRA_FILES);
|
|
111
|
+
await fs.writeFile(extraFilesPath, JSON.stringify(extraFiles, null, 2));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (project.type === 'strapi') {
|
|
115
|
+
const blockPath = await this.getBlockTempFilePath(project, blockName);
|
|
116
|
+
const stringContent = JSON.stringify(content, null, 2);
|
|
117
|
+
await fs.writeFile(blockPath, stringContent);
|
|
118
|
+
}
|
|
104
119
|
}
|
|
105
120
|
async loadGeneratedBlock(blockName, project) {
|
|
106
121
|
const blockPath = await this.getBlockTempFilePath(project, blockName);
|
|
@@ -113,13 +128,34 @@ class FileManager {
|
|
|
113
128
|
if (project.type !== 'next.js') {
|
|
114
129
|
throw new Error('Project is not a Next.js project');
|
|
115
130
|
}
|
|
131
|
+
const tempFolder = await this.getBlockTempPath(blockName);
|
|
132
|
+
// Verify generated block
|
|
116
133
|
const generatedPath = await this.getBlockTempFilePath(project, blockName);
|
|
117
134
|
if (!(await (0, files_1.verifyPath)(generatedPath))) {
|
|
118
135
|
throw new Error(`Generated block not found: ${generatedPath}`);
|
|
119
136
|
}
|
|
120
|
-
|
|
137
|
+
// Create block folder
|
|
121
138
|
const blockFolder = this.getNextjsBlockFolder(project, blockName);
|
|
122
139
|
await (0, files_1.verifyAndCreateFolder)(blockFolder);
|
|
140
|
+
// Add Extra files
|
|
141
|
+
try {
|
|
142
|
+
const extraFilesPath = path.join(tempFolder, exports.GENERATOR_NEXTJS_EXTRA_FILES);
|
|
143
|
+
const hasExtraFiles = await (0, files_1.verifyPath)(extraFilesPath);
|
|
144
|
+
if (hasExtraFiles) {
|
|
145
|
+
const extraFilesContent = await (0, files_1.loadFile)(extraFilesPath);
|
|
146
|
+
const extraFiles = JSON.parse(extraFilesContent);
|
|
147
|
+
if (extraFiles && extraFiles.length > 0) {
|
|
148
|
+
for (const extraFile of extraFiles) {
|
|
149
|
+
const extraFilePath = path.join(blockFolder, extraFile.name);
|
|
150
|
+
await fs.writeFile(extraFilePath, extraFile.content);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error('Failed to add extra files to Next.js block:', error);
|
|
157
|
+
}
|
|
158
|
+
const content = await (0, files_1.loadFile)(generatedPath);
|
|
123
159
|
const mainFilePath = path.join(blockFolder, `${blockName}.tsx`);
|
|
124
160
|
const indexFilePath = path.join(blockFolder, 'index.ts');
|
|
125
161
|
const indexContent = `export { default } from './${blockName}'
|
|
@@ -152,5 +188,109 @@ class FileManager {
|
|
|
152
188
|
await fs.writeFile(assetFilePath, JSON.stringify(asset, null, 2));
|
|
153
189
|
}
|
|
154
190
|
}
|
|
191
|
+
async loadFilesInPath(rootPath, relativeTo, includeSubFolders = true) {
|
|
192
|
+
const allFilesPath = await (0, files_1.getFilesInPath)(rootPath, includeSubFolders);
|
|
193
|
+
const files = [];
|
|
194
|
+
for (const filePath of allFilesPath) {
|
|
195
|
+
const content = await (0, files_1.loadFile)(filePath);
|
|
196
|
+
const relativePath = relativeTo
|
|
197
|
+
? path.relative(relativeTo, filePath)
|
|
198
|
+
: filePath;
|
|
199
|
+
files.push({ path: relativePath, content });
|
|
200
|
+
}
|
|
201
|
+
return files;
|
|
202
|
+
}
|
|
203
|
+
async loadProjectFiles(project, subDir) {
|
|
204
|
+
if (!project) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
const rootFolder = path.join(project.srcPath, subDir);
|
|
208
|
+
const segments = [];
|
|
209
|
+
try {
|
|
210
|
+
if (!(await (0, files_1.verifyPath)(rootFolder))) {
|
|
211
|
+
throw new Error('Components folder does not exist');
|
|
212
|
+
}
|
|
213
|
+
const files = await this.loadFilesInPath(rootFolder, project.srcPath, false);
|
|
214
|
+
if (files.length > 0) {
|
|
215
|
+
for (const file of files) {
|
|
216
|
+
const name = path.basename(file.path, path.extname(file.path));
|
|
217
|
+
const segment = {
|
|
218
|
+
name,
|
|
219
|
+
files: [
|
|
220
|
+
{
|
|
221
|
+
...file,
|
|
222
|
+
path: `@/${file.path}`.replace(/\/index.ts|\/index.tsx/g, ''),
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
segments.push(segment);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const dirs = await (0, files_1.getDirectoriesInPath)(rootFolder);
|
|
230
|
+
for (const dir of dirs) {
|
|
231
|
+
const dirPath = path.join(rootFolder, dir);
|
|
232
|
+
const dirFiles = (await this.loadFilesInPath(dirPath, project.srcPath)).map((file) => ({
|
|
233
|
+
...file,
|
|
234
|
+
path: `@/${file.path}`.replace(/\/index.ts|\/index.tsx/g, ''),
|
|
235
|
+
}));
|
|
236
|
+
const dirName = path.basename(dir);
|
|
237
|
+
const segment = {
|
|
238
|
+
name: dirName,
|
|
239
|
+
files: dirFiles,
|
|
240
|
+
};
|
|
241
|
+
segments.push(segment);
|
|
242
|
+
}
|
|
243
|
+
return segments;
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
console.error(error);
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async loadCustomPrompts(blockName) {
|
|
251
|
+
try {
|
|
252
|
+
const prompts = [];
|
|
253
|
+
const globalPromptPath = await (0, files_1.getTempFilePath)('prompts.md');
|
|
254
|
+
if (await (0, files_1.verifyPath)(globalPromptPath)) {
|
|
255
|
+
const globalPrompt = await (0, files_1.loadFile)(globalPromptPath);
|
|
256
|
+
if (globalPrompt) {
|
|
257
|
+
prompts.push(globalPrompt);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
await (0, files_1.writeFile)(globalPromptPath, '');
|
|
262
|
+
}
|
|
263
|
+
const blockFolder = await this.getBlockTempPath(blockName);
|
|
264
|
+
const blockPromptsPath = path.join(blockFolder, 'prompts.md');
|
|
265
|
+
if (await (0, files_1.verifyPath)(blockPromptsPath)) {
|
|
266
|
+
const blockPrompt = await (0, files_1.loadFile)(blockPromptsPath);
|
|
267
|
+
if (blockPrompt) {
|
|
268
|
+
prompts.push(blockPrompt);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return prompts.join('\n\n');
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
console.error(error);
|
|
275
|
+
return '';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async loadProjectCSS(nextProject) {
|
|
279
|
+
if (!nextProject) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
const cssFile = path.join(nextProject.srcPath, 'styles', 'index.css');
|
|
283
|
+
try {
|
|
284
|
+
const css = await fs.readFile(cssFile, 'utf-8');
|
|
285
|
+
if (!css) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
return css;
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
console.error(error);
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
155
295
|
}
|
|
156
296
|
exports.default = FileManager;
|
|
@@ -10,8 +10,8 @@ export declare class Generator {
|
|
|
10
10
|
private get data();
|
|
11
11
|
private fetchNextBlock;
|
|
12
12
|
private fetchStrapiBlock;
|
|
13
|
-
loadProjectCSS(nextProject?: IProject): Promise<string | null>;
|
|
14
13
|
private generateStrapiBlock;
|
|
14
|
+
private getNextjsProjectFiles;
|
|
15
15
|
private generateNextBlock;
|
|
16
16
|
generateBlock(blockName: string, project: IProject): Promise<IStrapiGenerateResponse | undefined>;
|
|
17
17
|
generateBlocks(blockNames: string[], project: IProject): Promise<void>;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Generator = exports.GENERATOR_NEXTJS_FILE_NAME = exports.GENERATOR_STRAPI_FILE_NAME = exports.GENERATOR_FOLDER_NAME = void 0;
|
|
4
4
|
const fs = require("node:fs/promises");
|
|
5
|
-
const path = require("node:path");
|
|
6
5
|
const files_1 = require("../../../../utils/files");
|
|
7
6
|
const cliProgress = require("cli-progress");
|
|
8
7
|
exports.GENERATOR_FOLDER_NAME = 'generated-blocks';
|
|
@@ -16,13 +15,14 @@ class Generator {
|
|
|
16
15
|
get data() {
|
|
17
16
|
return this.state.getData();
|
|
18
17
|
}
|
|
19
|
-
async fetchNextBlock(blockName, blockVariants,
|
|
18
|
+
async fetchNextBlock(blockName, blockVariants, files, strapiComponent, customPrompt) {
|
|
20
19
|
const requestBody = {
|
|
21
20
|
name: blockName.replace('block-', ''),
|
|
22
21
|
variants: blockVariants,
|
|
23
|
-
|
|
24
|
-
styles,
|
|
22
|
+
files,
|
|
25
23
|
strapiComponent,
|
|
24
|
+
customPrompt,
|
|
25
|
+
figma: this.data.configs.figma,
|
|
26
26
|
};
|
|
27
27
|
const data = await this.state.fetchApi('/generate/nextjs/block', {
|
|
28
28
|
method: 'POST',
|
|
@@ -34,12 +34,13 @@ class Generator {
|
|
|
34
34
|
if (!data.result?.block) {
|
|
35
35
|
throw new Error('Next.js block generation failed');
|
|
36
36
|
}
|
|
37
|
-
return data.result
|
|
37
|
+
return data.result;
|
|
38
38
|
}
|
|
39
39
|
async fetchStrapiBlock(blockName, blockVariants) {
|
|
40
40
|
const requestBody = {
|
|
41
41
|
name: blockName.replace('block-', ''),
|
|
42
42
|
variants: blockVariants,
|
|
43
|
+
figma: this.data.configs.figma,
|
|
43
44
|
};
|
|
44
45
|
const data = await this.state.fetchApi('/generate/strapi/block', {
|
|
45
46
|
method: 'POST',
|
|
@@ -53,23 +54,6 @@ class Generator {
|
|
|
53
54
|
}
|
|
54
55
|
return data.result;
|
|
55
56
|
}
|
|
56
|
-
async loadProjectCSS(nextProject) {
|
|
57
|
-
if (!nextProject) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const cssFile = path.join(nextProject.srcPath, 'styles', 'index.css');
|
|
61
|
-
try {
|
|
62
|
-
const css = await fs.readFile(cssFile, 'utf-8');
|
|
63
|
-
if (!css) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
return css;
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
console.error(error);
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
57
|
async generateStrapiBlock(blockName, project, blockVariants) {
|
|
74
58
|
const isAlreadyGenerated = await this.state.file.verifyBlockAlreadyGenerated(blockName, project);
|
|
75
59
|
if (isAlreadyGenerated) {
|
|
@@ -80,13 +64,26 @@ class Generator {
|
|
|
80
64
|
await this.state.file.saveGeneratedBlock(blockName, project, response);
|
|
81
65
|
return response;
|
|
82
66
|
}
|
|
67
|
+
async getNextjsProjectFiles(project) {
|
|
68
|
+
const styles = await this.state.file.loadProjectFiles(project, 'styles');
|
|
69
|
+
const components = await this.state.file.loadProjectFiles(project, 'components');
|
|
70
|
+
const types = await this.state.file.loadProjectFiles(project, 'types');
|
|
71
|
+
const layouts = await this.state.file.loadProjectFiles(project, 'layouts/shared');
|
|
72
|
+
return {
|
|
73
|
+
styles,
|
|
74
|
+
components,
|
|
75
|
+
types,
|
|
76
|
+
layouts,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
83
79
|
async generateNextBlock(blockName, project, blockVariants) {
|
|
84
80
|
const strapiProject = this.data.projects.find((p) => p.type === 'strapi');
|
|
85
81
|
const strapiBlock = strapiProject
|
|
86
82
|
? await this.generateStrapiBlock(blockName, strapiProject, blockVariants)
|
|
87
83
|
: undefined;
|
|
88
|
-
const
|
|
89
|
-
const
|
|
84
|
+
const customPrompt = await this.state.file.loadCustomPrompts(blockName);
|
|
85
|
+
const files = await this.getNextjsProjectFiles(project);
|
|
86
|
+
const nextResponse = await this.fetchNextBlock(blockName, blockVariants, files, strapiBlock, customPrompt);
|
|
90
87
|
await this.state.file.saveGeneratedBlock(blockName, project, nextResponse);
|
|
91
88
|
}
|
|
92
89
|
async generateBlock(blockName, project) {
|
|
@@ -101,7 +98,12 @@ class Generator {
|
|
|
101
98
|
}
|
|
102
99
|
}
|
|
103
100
|
catch (error) {
|
|
101
|
+
console.log('------------------------------------');
|
|
102
|
+
console.log('------------------------------------');
|
|
103
|
+
console.log(`Error generating block: ${blockName}`);
|
|
104
104
|
console.error(error);
|
|
105
|
+
console.log('------------------------------------');
|
|
106
|
+
console.log('------------------------------------');
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
async generateBlocks(blockNames, project) {
|
|
@@ -116,6 +118,7 @@ class Generator {
|
|
|
116
118
|
progressBar.start(blockNames.length, 0);
|
|
117
119
|
for (const block of blockNames) {
|
|
118
120
|
await this.generateBlock(block, project);
|
|
121
|
+
progressBar.increment();
|
|
119
122
|
}
|
|
120
123
|
progressBar.stop();
|
|
121
124
|
await this.state.save();
|
|
@@ -123,7 +126,8 @@ class Generator {
|
|
|
123
126
|
}
|
|
124
127
|
async generateStyles() {
|
|
125
128
|
console.log('- Generating Styles... Please wait');
|
|
126
|
-
|
|
129
|
+
// TODO: get variables from blocks
|
|
130
|
+
const variables = {}; // this.data.variables
|
|
127
131
|
const data = await this.state.fetchApi('/generate/styles', {
|
|
128
132
|
method: 'POST',
|
|
129
133
|
headers: {
|
|
@@ -149,13 +153,27 @@ class Generator {
|
|
|
149
153
|
console.log('- Generating Checklist... Please wait');
|
|
150
154
|
const lines = ['# Checklist', ''];
|
|
151
155
|
const blocks = this.state.getData().blocks;
|
|
156
|
+
let project = this.state.getProjectByType('next.js');
|
|
157
|
+
if (!project) {
|
|
158
|
+
project = this.state.getProjectByType('strapi');
|
|
159
|
+
if (!project) {
|
|
160
|
+
throw new Error('No suitable project found for checklist generation');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
152
163
|
for (const block of blocks) {
|
|
164
|
+
const isChecked = block.hidden ||
|
|
165
|
+
(await this.state.file.verifyBlockAlreadyExists(project, block.name));
|
|
153
166
|
const name = block.name
|
|
154
167
|
.replace('block-', '')
|
|
155
168
|
.split('-')
|
|
156
169
|
.map((word) => word.length > 1 ? word.charAt(0).toUpperCase() + word.slice(1) : word)
|
|
157
170
|
.join(' ');
|
|
158
|
-
|
|
171
|
+
let nameContent = `Block | ${name} (\`${block.name}\`)`;
|
|
172
|
+
if (block.hidden) {
|
|
173
|
+
nameContent = `~~${nameContent}~~ <sup>hidden</sup>`;
|
|
174
|
+
}
|
|
175
|
+
const checkContent = isChecked ? 'x' : ' ';
|
|
176
|
+
lines.push(`- [${checkContent}] ${nameContent}`);
|
|
159
177
|
}
|
|
160
178
|
const listText = lines.join('\n');
|
|
161
179
|
const tempPath = await (0, files_1.getTempFilePath)('checklist.md');
|
|
@@ -12,6 +12,18 @@ export interface IStrapiGenerateResponse {
|
|
|
12
12
|
block: IStrapiComponent;
|
|
13
13
|
assets: IStrapiComponent[];
|
|
14
14
|
}
|
|
15
|
+
export interface IGenerationExtraFile {
|
|
16
|
+
name: string;
|
|
17
|
+
content: string;
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}
|
|
15
21
|
export interface INextjsGenerateResponse {
|
|
16
22
|
block: string;
|
|
23
|
+
extraFiles: IGenerationExtraFile[];
|
|
24
|
+
definition: {
|
|
25
|
+
architecture: string[];
|
|
26
|
+
decisions: string[];
|
|
27
|
+
note: string;
|
|
28
|
+
};
|
|
17
29
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IProject, ProjectType } from '../../../../types/project';
|
|
1
2
|
import FileManager from '../file-manager';
|
|
2
3
|
import type { IAIFileState, IAvailableFilters } from './types';
|
|
3
4
|
declare class AIState {
|
|
@@ -13,10 +14,10 @@ declare class AIState {
|
|
|
13
14
|
save(): Promise<void>;
|
|
14
15
|
get isReady(): boolean;
|
|
15
16
|
getData(): IAIFileState;
|
|
17
|
+
getProjectByType(type: ProjectType): IProject | null;
|
|
16
18
|
getAvailableBlocks({ generated, hidden }?: IAvailableFilters): import("./types").IBlockData[];
|
|
17
19
|
getAvailableBlocksNames(filters?: IAvailableFilters): string[];
|
|
18
20
|
setBlocks(blocks: IAIFileState['blocks']): void;
|
|
19
|
-
setVariables(variables: IAIFileState['variables']): void;
|
|
20
21
|
setColors(colors: IAIFileState['colors']): void;
|
|
21
22
|
setBlockGenerated(blockName: string, generated?: boolean): void;
|
|
22
23
|
getBlockVariants(blockName: string): any[];
|
|
@@ -42,7 +42,6 @@ class AIState {
|
|
|
42
42
|
},
|
|
43
43
|
projects,
|
|
44
44
|
blocks: [],
|
|
45
|
-
variables: {},
|
|
46
45
|
};
|
|
47
46
|
}
|
|
48
47
|
async create() {
|
|
@@ -74,6 +73,10 @@ class AIState {
|
|
|
74
73
|
}
|
|
75
74
|
return this.data;
|
|
76
75
|
}
|
|
76
|
+
getProjectByType(type) {
|
|
77
|
+
const projects = this.getData().projects;
|
|
78
|
+
return projects.find((project) => project.type === type) || null;
|
|
79
|
+
}
|
|
77
80
|
getAvailableBlocks({ generated, hidden } = {}) {
|
|
78
81
|
let blocks = this.getData().blocks;
|
|
79
82
|
if (hidden != null || generated != null) {
|
|
@@ -96,10 +99,10 @@ class AIState {
|
|
|
96
99
|
setBlocks(blocks) {
|
|
97
100
|
this.data.blocks = blocks;
|
|
98
101
|
}
|
|
99
|
-
setVariables(variables) {
|
|
100
|
-
this.data.variables = variables;
|
|
101
|
-
}
|
|
102
102
|
setColors(colors) {
|
|
103
|
+
if (!this.data) {
|
|
104
|
+
throw new Error('AI State not initialized');
|
|
105
|
+
}
|
|
103
106
|
this.data.colors = colors;
|
|
104
107
|
}
|
|
105
108
|
setBlockGenerated(blockName, generated = true) {
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_net_1 = require("node:net");
|
|
3
4
|
const inquirer_1 = require("inquirer");
|
|
5
|
+
const undici_1 = require("undici");
|
|
4
6
|
const files_1 = require("../../utils/files");
|
|
5
7
|
const FILE_NAME = 'ai-server.json';
|
|
8
|
+
const TIMEOUT_LIMIT_TIME = 1000 * 60 * 60; // 60 minutes
|
|
9
|
+
const longTimeoutAgent = new undici_1.Agent({
|
|
10
|
+
connect: {
|
|
11
|
+
timeout: TIMEOUT_LIMIT_TIME,
|
|
12
|
+
},
|
|
13
|
+
headersTimeout: TIMEOUT_LIMIT_TIME,
|
|
14
|
+
bodyTimeout: TIMEOUT_LIMIT_TIME,
|
|
15
|
+
});
|
|
6
16
|
class AIServerModule {
|
|
7
17
|
filePath = '';
|
|
8
18
|
api = '';
|
|
9
19
|
token = '';
|
|
10
|
-
constructor() {
|
|
20
|
+
constructor() {
|
|
21
|
+
(0, node_net_1.setDefaultAutoSelectFamilyAttemptTimeout)(TIMEOUT_LIMIT_TIME);
|
|
22
|
+
}
|
|
11
23
|
async createConfigs() {
|
|
12
24
|
const query = await inquirer_1.default.prompt([
|
|
13
25
|
{
|
|
@@ -46,6 +58,10 @@ class AIServerModule {
|
|
|
46
58
|
Authorization: `Bearer ${this.token}`,
|
|
47
59
|
...init?.headers,
|
|
48
60
|
},
|
|
61
|
+
signal: AbortSignal.timeout(TIMEOUT_LIMIT_TIME),
|
|
62
|
+
...{
|
|
63
|
+
dispatcher: longTimeoutAgent,
|
|
64
|
+
},
|
|
49
65
|
});
|
|
50
66
|
if (!response.ok) {
|
|
51
67
|
throw new Error(`Failed to fetch ${path}`);
|
package/dist/utils/files.d.ts
CHANGED
|
@@ -9,4 +9,4 @@ export declare function writeRootFile(fileName: string, content: string): Promis
|
|
|
9
9
|
export declare function writeFile(filePath: string, content: string): Promise<void>;
|
|
10
10
|
export declare function loadFile(filePath: string): Promise<string>;
|
|
11
11
|
export declare function getDirectoriesInPath(rootPath: string): Promise<string[]>;
|
|
12
|
-
export declare function getFilesInPath(rootPath: string): Promise<string[]>;
|
|
12
|
+
export declare function getFilesInPath(rootPath: string, includeSubFolders?: boolean): Promise<string[]>;
|
package/dist/utils/files.js
CHANGED
|
@@ -102,7 +102,7 @@ async function getDirectoriesInPath(rootPath) {
|
|
|
102
102
|
return directories;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
async function getFilesInPath(rootPath) {
|
|
105
|
+
async function getFilesInPath(rootPath, includeSubFolders = true) {
|
|
106
106
|
const files = [];
|
|
107
107
|
try {
|
|
108
108
|
const allContent = await fs.readdir(rootPath);
|
|
@@ -113,8 +113,10 @@ async function getFilesInPath(rootPath) {
|
|
|
113
113
|
files.push(contentPath);
|
|
114
114
|
}
|
|
115
115
|
else if (stats.isDirectory()) {
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
if (includeSubFolders) {
|
|
117
|
+
const subFiles = await getFilesInPath(contentPath);
|
|
118
|
+
files.push(...subFiles);
|
|
119
|
+
}
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
122
|
return files;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futurebrand/dev-tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "FutureBrand Dev Tools",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc && tsc-alias",
|
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
"inquirer": "^12.6.3",
|
|
65
65
|
"prettier": "^3.6.0",
|
|
66
66
|
"prettier-plugin-tailwindcss": "^0.6.13",
|
|
67
|
-
"typescript-eslint": "^8.35.0"
|
|
67
|
+
"typescript-eslint": "^8.35.0",
|
|
68
|
+
"undici": "^7.18.2"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
70
71
|
"@types/cli-progress": "^3.11.6",
|