@futurebrand/dev-tools 2.5.1 → 2.5.2

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.
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ declare const generateDOCSCommand: Command;
3
+ export default generateDOCSCommand;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const files_1 = require("../../../utils/files");
4
+ const project_1 = require("../../../utils/project");
5
+ const commander_1 = require("commander");
6
+ const inquirer_1 = require("inquirer");
7
+ const path = require("path");
8
+ const generator_1 = require("./modules/generator");
9
+ // import { Generator } from './modules/generator'
10
+ // import AIState from './modules/state'
11
+ const generateDOCSCommand = new commander_1.Command('docs')
12
+ .alias('doc')
13
+ .description('Ai Documentation Generate Command')
14
+ .action(async () => {
15
+ let projects = await (0, project_1.getProjects)();
16
+ if (!projects.length) {
17
+ console.error('No projects found');
18
+ return;
19
+ }
20
+ if (projects.length > 0) {
21
+ const projectQuery = await inquirer_1.default.prompt([
22
+ {
23
+ type: 'checkbox',
24
+ name: 'projects',
25
+ message: 'Select the projects to generate documentation for',
26
+ choices: projects.map((project) => project.name),
27
+ },
28
+ ]);
29
+ projects = projects.filter((project) => projectQuery.projects.includes(project.name));
30
+ }
31
+ const generator = new generator_1.default();
32
+ const docs = [];
33
+ for (const project of projects) {
34
+ const categories = await (0, files_1.getDirectoriesInPath)(project.srcPath);
35
+ for (const category of categories) {
36
+ const categoryPath = path.join(project.srcPath, category);
37
+ const docsDirs = await (0, files_1.getDirectoriesInPath)(categoryPath);
38
+ if (!docsDirs.length) {
39
+ continue;
40
+ }
41
+ for (const doc of docsDirs) {
42
+ const docPath = path.join(categoryPath, doc);
43
+ const files = await (0, files_1.getFilesInPath)(docPath);
44
+ if (!files.length) {
45
+ continue;
46
+ }
47
+ docs.push({
48
+ project: project.name,
49
+ name: doc,
50
+ category,
51
+ files,
52
+ projectType: project.type,
53
+ });
54
+ }
55
+ }
56
+ }
57
+ console.log('Generating documentation...');
58
+ await generator.generate(docs);
59
+ });
60
+ exports.default = generateDOCSCommand;
@@ -0,0 +1,17 @@
1
+ import type { IDocumentation } from '../../types';
2
+ export interface IDocGeneratorResponse {
3
+ title: string;
4
+ description: string;
5
+ content: string;
6
+ }
7
+ declare class DocsGenerator {
8
+ private server;
9
+ constructor();
10
+ private generateDOCDocumentation;
11
+ private getDocTempDirectory;
12
+ private saveDocFile;
13
+ private verifyDocAlreadyExists;
14
+ private getDOCFileData;
15
+ generate(docs: IDocumentation[]): Promise<void>;
16
+ }
17
+ export default DocsGenerator;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ai_server_module_1 = require("../../../../../modules/ai-server-module");
4
+ const parallel_1 = require("../../../../../modules/parallel");
5
+ const files_1 = require("../../../../../utils/files");
6
+ const cliProgress = require("cli-progress");
7
+ const fs = require("fs/promises");
8
+ const path = require("path");
9
+ const DOCS_TMP_FOLDER_NAME = 'docs';
10
+ const NUM_OF_PARALLEL_REQUESTS = 5;
11
+ class DocsGenerator {
12
+ server;
13
+ constructor() {
14
+ this.server = new ai_server_module_1.default();
15
+ }
16
+ async generateDOCDocumentation(doc, files) {
17
+ const { result } = await this.server.fetchApi('/generate/docs', {
18
+ method: 'POST',
19
+ body: JSON.stringify({
20
+ ...doc,
21
+ files,
22
+ }),
23
+ });
24
+ if (!result) {
25
+ throw new Error('Failed to generate documentation');
26
+ }
27
+ const fullContent = [
28
+ '---',
29
+ `title: "${result.title}"`,
30
+ `description: "${result.description}"`,
31
+ '---',
32
+ result.content,
33
+ ];
34
+ return fullContent.join('\n');
35
+ }
36
+ async getDocTempDirectory(doc) {
37
+ const projectFolder = doc.project.replace('@futurebrand/', '');
38
+ return (0, files_1.getTempFilePath)(DOCS_TMP_FOLDER_NAME, projectFolder, doc.category);
39
+ }
40
+ async saveDocFile(doc, content) {
41
+ const tempDir = await this.getDocTempDirectory(doc);
42
+ await (0, files_1.verifyAndCreateFolder)(tempDir);
43
+ const filePath = path.join(tempDir, `${doc.name}.md`);
44
+ await fs.writeFile(filePath, content, 'utf-8');
45
+ }
46
+ async verifyDocAlreadyExists(doc) {
47
+ const tempDir = await this.getDocTempDirectory(doc);
48
+ const filePath = path.join(tempDir, `${doc.name}.md`);
49
+ const fileExist = await (0, files_1.verifyPath)(filePath);
50
+ return fileExist;
51
+ }
52
+ async getDOCFileData(doc) {
53
+ const { files: filePaths } = doc;
54
+ const files = [];
55
+ for (const filePath of filePaths) {
56
+ const content = await fs.readFile(filePath, 'utf-8');
57
+ files.push({
58
+ path: filePath,
59
+ content,
60
+ });
61
+ }
62
+ return files;
63
+ }
64
+ async generate(docs) {
65
+ if (!this.server.isReady) {
66
+ await this.server.init();
67
+ }
68
+ const parallel = new parallel_1.default(NUM_OF_PARALLEL_REQUESTS);
69
+ const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.legacy);
70
+ progressBar.start(docs.length, 0);
71
+ for (const doc of docs) {
72
+ parallel.add(async () => {
73
+ const docExists = await this.verifyDocAlreadyExists(doc);
74
+ if (docExists) {
75
+ progressBar.increment();
76
+ return;
77
+ }
78
+ const files = await this.getDOCFileData(doc);
79
+ const content = await this.generateDOCDocumentation(doc, files);
80
+ await this.saveDocFile(doc, content);
81
+ progressBar.increment();
82
+ });
83
+ }
84
+ await parallel.execute();
85
+ progressBar.stop();
86
+ }
87
+ }
88
+ exports.default = DocsGenerator;
@@ -0,0 +1,11 @@
1
+ export interface IDocumentation {
2
+ name: string;
3
+ category: string;
4
+ files: string[];
5
+ project: string;
6
+ projectType: string;
7
+ }
8
+ export interface IFileData {
9
+ path: string;
10
+ content: string;
11
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const commander_1 = require("commander");
4
4
  const inquirer_1 = require("inquirer");
5
+ const docs_1 = require("./docs");
5
6
  const generator_1 = require("./modules/generator");
6
7
  const state_1 = require("./modules/state");
7
8
  const generateCommand = new commander_1.Command('generate')
@@ -72,4 +73,5 @@ const generateChecklistCommand = new commander_1.Command('checklist')
72
73
  });
73
74
  generateCommand.addCommand(generateStyleCommand);
74
75
  generateCommand.addCommand(generateChecklistCommand);
76
+ generateCommand.addCommand(docs_1.default);
75
77
  exports.default = generateCommand;
@@ -24,48 +24,36 @@ class Generator {
24
24
  styles,
25
25
  strapiComponent,
26
26
  };
27
- try {
28
- const data = await this.state.fetchApi('/generate/nextjs/block', {
29
- method: 'POST',
30
- headers: {
31
- 'Content-Type': 'application/json',
32
- },
33
- body: JSON.stringify(requestBody),
34
- });
35
- if (data.result?.block) {
36
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
37
- return data.result.block;
38
- }
39
- return null;
40
- }
41
- catch (error) {
42
- console.error(error);
43
- return null;
27
+ const data = await this.state.fetchApi('/generate/nextjs/block', {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ },
32
+ body: JSON.stringify(requestBody),
33
+ });
34
+ if (!data.result?.block) {
35
+ throw new Error('Next.js block generation failed');
44
36
  }
37
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
38
+ return data.result.block;
45
39
  }
46
40
  async generateStrapiBlock(blockName, blockVariants) {
47
41
  const requestBody = {
48
42
  name: blockName.replace('block-', ''),
49
43
  variants: blockVariants,
50
44
  };
51
- try {
52
- const data = await this.state.fetchApi('/generate/strapi/block', {
53
- method: 'POST',
54
- headers: {
55
- 'Content-Type': 'application/json',
56
- },
57
- body: JSON.stringify(requestBody),
58
- });
59
- if (data.result) {
60
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
61
- return data.result;
62
- }
63
- return null;
64
- }
65
- catch (error) {
66
- console.error(error);
67
- return null;
45
+ const data = await this.state.fetchApi('/generate/strapi/block', {
46
+ method: 'POST',
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ body: JSON.stringify(requestBody),
51
+ });
52
+ if (!data.result) {
53
+ throw new Error('Strapi block generation failed');
68
54
  }
55
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
56
+ return data.result;
69
57
  }
70
58
  async loadProjectCSS(nextProject) {
71
59
  if (!nextProject) {
@@ -93,18 +81,14 @@ class Generator {
93
81
  if (strapiProject) {
94
82
  strapiResponse = await this.generateStrapiBlock(blockName, blockVariants);
95
83
  onGenerateCallback();
96
- if (strapiResponse) {
97
- const cachePath = path.join(tempFolder, exports.GENERATOR_STRAPI_FILE_NAME);
98
- await fs.writeFile(cachePath, JSON.stringify(strapiResponse, null, 2));
99
- }
84
+ const cachePath = path.join(tempFolder, exports.GENERATOR_STRAPI_FILE_NAME);
85
+ await fs.writeFile(cachePath, JSON.stringify(strapiResponse, null, 2));
100
86
  }
101
87
  if (nextProject) {
102
88
  const nextResponse = await this.generateNextBlock(blockName, blockVariants, this.data.variables, styles, strapiResponse);
103
89
  onGenerateCallback();
104
- if (nextResponse) {
105
- const cachePath = path.join(tempFolder, exports.GENERATOR_NEXTJS_FILE_NAME);
106
- await fs.writeFile(cachePath, nextResponse);
107
- }
90
+ const cachePath = path.join(tempFolder, exports.GENERATOR_NEXTJS_FILE_NAME);
91
+ await fs.writeFile(cachePath, nextResponse);
108
92
  }
109
93
  this.state.setBlockGenerated(blockName);
110
94
  }
@@ -18,7 +18,8 @@ class ProjectAdder {
18
18
  throw new Error('Strapi project not found');
19
19
  // return
20
20
  }
21
- const componentsFolder = path.join(strapiProject.path, 'src', 'components');
21
+ const srcPath = strapiProject.srcPath || path.join(strapiProject.path, 'src');
22
+ const componentsFolder = path.join(srcPath, 'components');
22
23
  const blocksFolder = path.join(componentsFolder, 'blocks');
23
24
  const blocksAssetsFolder = path.join(componentsFolder, 'blocks-assets');
24
25
  const strapiResult = await (0, files_1.loadJSONFile)(filePath);
@@ -43,7 +44,7 @@ class ProjectAdder {
43
44
  throw new Error('Nextjs project not found');
44
45
  // return
45
46
  }
46
- const blockFolder = path.join(project.path, 'layouts', 'blocks', block.name);
47
+ const blockFolder = path.join(project.srcPath || project.path, 'layouts', 'blocks', block.name);
47
48
  await (0, files_1.verifyAndCreateFolder)(blockFolder);
48
49
  const nextjsResult = await (0, files_1.loadFile)(filePath);
49
50
  await fs.writeFile(path.join(blockFolder, `${block.name}.tsx`), nextjsResult);
@@ -2,6 +2,7 @@ import type { IAIFileState, IAvailableFilters } from './types';
2
2
  declare class AIState {
3
3
  private filePath;
4
4
  private data;
5
+ private server;
5
6
  constructor();
6
7
  private createOptions;
7
8
  create(): Promise<void>;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ai_server_module_1 = require("../../../../modules/ai-server-module");
3
4
  const project_1 = require("../../../../utils/project");
4
5
  const inquirer_1 = require("inquirer");
5
6
  const files_1 = require("../../../../utils/files");
@@ -7,7 +8,10 @@ const FILE_NAME = 'ai-state.json';
7
8
  class AIState {
8
9
  filePath = '';
9
10
  data = null;
10
- constructor() { }
11
+ server;
12
+ constructor() {
13
+ this.server = new ai_server_module_1.default();
14
+ }
11
15
  async createOptions() {
12
16
  const query = await inquirer_1.default.prompt([
13
17
  {
@@ -40,34 +44,23 @@ class AIState {
40
44
  }
41
45
  async create() {
42
46
  this.data = await this.createOptions();
47
+ this.server.setApiConfigs(this.data.configs);
43
48
  }
44
49
  async fetchApi(path, init) {
45
- let apiUrl = this.getData().configs.api;
46
- if (apiUrl.endsWith('/')) {
47
- apiUrl = apiUrl.slice(0, -1);
48
- }
49
- const response = await fetch(`${apiUrl}${path}`, {
50
- ...init,
51
- headers: {
52
- 'Content-Type': 'application/json',
53
- Authorization: `Bearer ${this.getData().configs.token}`,
54
- ...init?.headers,
55
- },
56
- });
57
- if (!response.ok) {
58
- throw new Error(`Failed to fetch ${path}`);
59
- }
60
- const data = await response.json();
61
- return data;
50
+ return this.server.fetchApi(path, init);
62
51
  }
63
52
  async init() {
64
53
  this.filePath = await (0, files_1.getTempFilePath)(FILE_NAME);
65
54
  if (await (0, files_1.verifyPath)(this.filePath)) {
66
55
  this.data = await (0, files_1.loadJSONFile)(this.filePath);
56
+ this.server.setApiConfigs(this.data.configs);
67
57
  }
68
58
  }
69
59
  async save() {
70
- await (0, files_1.writeJSONFile)(this.filePath, this.data);
60
+ if (this.data) {
61
+ await (0, files_1.writeJSONFile)(this.filePath, this.data);
62
+ await this.server.save();
63
+ }
71
64
  }
72
65
  get isReady() {
73
66
  return this.data !== null;
@@ -1,8 +1,7 @@
1
+ import type { IServerConfigs } from '../../../../modules/ai-server-module/types';
1
2
  import type { IProject } from '../../../../types/project';
2
3
  import type { IFigmaNode } from '../figma/types';
3
- export interface IStateConfigs {
4
- api: string;
5
- token: string;
4
+ export interface IStateConfigs extends IServerConfigs {
6
5
  figma: string;
7
6
  }
8
7
  export interface IBlockData {
@@ -0,0 +1,15 @@
1
+ import type { IServerConfigs } from './types';
2
+ declare class AIServerModule implements IServerConfigs {
3
+ private filePath;
4
+ api: string;
5
+ token: string;
6
+ constructor();
7
+ private createConfigs;
8
+ create(): Promise<void>;
9
+ fetchApi<T = any>(path: string, init?: RequestInit): Promise<T>;
10
+ init(): Promise<void>;
11
+ setApiConfigs(configs: IServerConfigs): void;
12
+ save(): Promise<void>;
13
+ get isReady(): boolean;
14
+ }
15
+ export default AIServerModule;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const inquirer_1 = require("inquirer");
4
+ const files_1 = require("../../utils/files");
5
+ const FILE_NAME = 'ai-server.json';
6
+ class AIServerModule {
7
+ filePath = '';
8
+ api = '';
9
+ token = '';
10
+ constructor() { }
11
+ async createConfigs() {
12
+ const query = await inquirer_1.default.prompt([
13
+ {
14
+ type: 'input',
15
+ name: 'apiUrl',
16
+ message: 'What is the AI API url?',
17
+ },
18
+ {
19
+ type: 'input',
20
+ name: 'apiToken',
21
+ message: 'What is the AI API token?',
22
+ },
23
+ ]);
24
+ return {
25
+ api: query.apiUrl,
26
+ token: query.apiToken,
27
+ };
28
+ }
29
+ async create() {
30
+ const configs = await this.createConfigs();
31
+ this.api = configs.api;
32
+ this.token = configs.token;
33
+ }
34
+ async fetchApi(path, init) {
35
+ if (!this.isReady) {
36
+ await this.init();
37
+ }
38
+ let apiUrl = this.api;
39
+ if (apiUrl.endsWith('/')) {
40
+ apiUrl = apiUrl.slice(0, -1);
41
+ }
42
+ const response = await fetch(`${apiUrl}${path}`, {
43
+ ...init,
44
+ headers: {
45
+ 'Content-Type': 'application/json',
46
+ Authorization: `Bearer ${this.token}`,
47
+ ...init?.headers,
48
+ },
49
+ });
50
+ if (!response.ok) {
51
+ throw new Error(`Failed to fetch ${path}`);
52
+ }
53
+ const data = await response.json();
54
+ return data;
55
+ }
56
+ async init() {
57
+ this.filePath = await (0, files_1.getTempFilePath)(FILE_NAME);
58
+ if (await (0, files_1.verifyPath)(this.filePath)) {
59
+ const configs = await (0, files_1.loadJSONFile)(this.filePath);
60
+ this.setApiConfigs(configs);
61
+ }
62
+ else {
63
+ const configs = await this.createConfigs();
64
+ this.setApiConfigs(configs);
65
+ await this.save();
66
+ }
67
+ }
68
+ setApiConfigs(configs) {
69
+ this.api = configs.api;
70
+ this.token = configs.token;
71
+ }
72
+ async save() {
73
+ if (!this.isReady) {
74
+ throw new Error('AIServerModule is not ready. Please initialize it first.');
75
+ }
76
+ const filePath = this.filePath || (await (0, files_1.getTempFilePath)(FILE_NAME));
77
+ await (0, files_1.writeJSONFile)(filePath, {
78
+ api: this.api,
79
+ token: this.token,
80
+ });
81
+ }
82
+ get isReady() {
83
+ return this.api !== '' && this.token !== '';
84
+ }
85
+ }
86
+ exports.default = AIServerModule;
@@ -0,0 +1,4 @@
1
+ export interface IServerConfigs {
2
+ api: string;
3
+ token: string;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,16 +1,11 @@
1
1
  type ParallelCallback = () => Promise<void>;
2
- type CycleCallback = (index: number, total: number) => void;
3
2
  declare class ParallelModule {
4
3
  private readonly parallelCalls;
5
4
  private promises;
6
5
  private running;
7
- private index;
8
6
  constructor(parallelCalls: number);
9
7
  add(callback: ParallelCallback): void;
10
- private next;
11
- private wait;
12
- get total(): number;
13
- execute(callback: CycleCallback): Promise<void>;
8
+ execute(): Promise<void>;
14
9
  reset(): void;
15
10
  }
16
11
  export default ParallelModule;
@@ -1,50 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const sleep_1 = require("../../utils/sleep");
4
3
  class ParallelModule {
5
4
  parallelCalls;
6
5
  promises;
7
6
  running;
8
- index;
9
7
  constructor(parallelCalls) {
10
8
  this.parallelCalls = parallelCalls;
11
9
  this.promises = [];
12
- this.running = 0;
13
- this.index = 0;
10
+ this.running = new Set();
14
11
  }
15
12
  add(callback) {
16
13
  this.promises.push(callback);
17
14
  }
18
- async next(callback) {
19
- const promise = this.promises.shift();
20
- if (promise) {
21
- this.running++;
22
- await promise();
23
- this.running--;
24
- }
25
- this.index++;
26
- callback(this.index, this.total);
27
- }
28
- async wait() {
29
- while (this.running >= this.parallelCalls) {
30
- await (0, sleep_1.sleep)(0);
31
- }
32
- }
33
- get total() {
34
- return this.promises.length;
35
- }
36
- async execute(callback) {
37
- while (this.promises.length > 0) {
38
- if (this.running >= this.parallelCalls) {
39
- await this.wait();
15
+ async execute() {
16
+ for (const promise of this.promises) {
17
+ const task = promise().then(() => {
18
+ this.running.delete(task);
19
+ });
20
+ this.running.add(task);
21
+ if (this.running.size >= this.parallelCalls) {
22
+ await Promise.race(this.running);
40
23
  }
41
- void this.next(callback);
42
24
  }
25
+ await Promise.all(this.running);
43
26
  }
44
27
  reset() {
45
28
  this.promises = [];
46
- this.running = 0;
47
- this.index = 0;
29
+ this.running = new Set();
48
30
  }
49
31
  }
50
32
  exports.default = ParallelModule;
@@ -2,6 +2,7 @@ export type ProjectType = 'next.js' | 'react' | 'strapi' | 'node';
2
2
  interface IDefaultProject {
3
3
  name: string;
4
4
  path: string;
5
+ srcPath: string;
5
6
  relativePath: string;
6
7
  type: ProjectType;
7
8
  }
@@ -8,3 +8,5 @@ export declare function writeJSONFile(filePath: string, data: unknown): Promise<
8
8
  export declare function writeRootFile(fileName: string, content: string): Promise<void>;
9
9
  export declare function writeFile(filePath: string, content: string): Promise<void>;
10
10
  export declare function loadFile(filePath: string): Promise<string>;
11
+ export declare function getDirectoriesInPath(rootPath: string): Promise<string[]>;
12
+ export declare function getFilesInPath(rootPath: string): Promise<string[]>;
@@ -10,6 +10,8 @@ exports.writeJSONFile = writeJSONFile;
10
10
  exports.writeRootFile = writeRootFile;
11
11
  exports.writeFile = writeFile;
12
12
  exports.loadFile = loadFile;
13
+ exports.getDirectoriesInPath = getDirectoriesInPath;
14
+ exports.getFilesInPath = getFilesInPath;
13
15
  const fs = require("node:fs/promises");
14
16
  const path = require("path");
15
17
  async function verifyPath(filePath) {
@@ -82,3 +84,43 @@ async function loadFile(filePath) {
82
84
  throw error;
83
85
  }
84
86
  }
87
+ async function getDirectoriesInPath(rootPath) {
88
+ const directories = [];
89
+ try {
90
+ const allContent = await fs.readdir(rootPath);
91
+ for (const content of allContent) {
92
+ const contentPath = path.join(rootPath, content);
93
+ const stats = await fs.stat(contentPath);
94
+ if (stats.isDirectory()) {
95
+ directories.push(content);
96
+ }
97
+ }
98
+ return directories;
99
+ }
100
+ catch (error) {
101
+ console.error(`Error reading directory ${rootPath}:`, error);
102
+ return directories;
103
+ }
104
+ }
105
+ async function getFilesInPath(rootPath) {
106
+ const files = [];
107
+ try {
108
+ const allContent = await fs.readdir(rootPath);
109
+ for (const content of allContent) {
110
+ const contentPath = path.join(rootPath, content);
111
+ const stats = await fs.stat(contentPath);
112
+ if (stats.isFile()) {
113
+ files.push(contentPath);
114
+ }
115
+ else if (stats.isDirectory()) {
116
+ const subFiles = await getFilesInPath(contentPath);
117
+ files.push(...subFiles);
118
+ }
119
+ }
120
+ return files;
121
+ }
122
+ catch (error) {
123
+ console.error(`Error reading directory ${rootPath}:`, error);
124
+ return files;
125
+ }
126
+ }
@@ -5,35 +5,55 @@ const path = require("path");
5
5
  const files_1 = require("./files");
6
6
  const package_manager_1 = require("./package-manager");
7
7
  const project_type_1 = require("./project-type");
8
- const PROJECTS_DIRS = ['', 'backend', 'frontend', 'server', 'scripts'];
9
- async function verifyProjectPath(relativePath) {
8
+ async function getProjectSRCPath(projectPath) {
9
+ const srcPath = path.join(projectPath, 'src');
10
+ if (await (0, files_1.verifyPath)(srcPath)) {
11
+ return srcPath;
12
+ }
13
+ const libPath = path.join(projectPath, 'lib');
14
+ if (await (0, files_1.verifyPath)(libPath)) {
15
+ return libPath;
16
+ }
17
+ return projectPath;
18
+ }
19
+ async function verifyProjectPath(relativePath = '') {
10
20
  const projectPath = (0, files_1.getRootPath)(relativePath);
11
21
  const packageJson = await (0, package_manager_1.loadPackageJson)(projectPath);
12
22
  if (!packageJson) {
13
23
  return null;
14
24
  }
15
25
  const projectType = await (0, project_type_1.getProjectType)(projectPath);
26
+ const srcPath = await getProjectSRCPath(projectPath);
27
+ const name = packageJson.name;
16
28
  if (projectType === 'next.js') {
17
29
  const projectAppFolder = path.join(projectPath, 'app');
18
30
  const isAppProject = await (0, files_1.verifyPath)(projectAppFolder);
19
31
  return {
20
- name: packageJson.name,
32
+ name,
21
33
  relativePath: `./${relativePath}`,
34
+ srcPath,
22
35
  path: projectPath,
23
36
  type: projectType,
24
37
  appDir: isAppProject,
25
38
  };
26
39
  }
27
40
  return {
28
- name: packageJson.name,
41
+ name,
42
+ srcPath,
29
43
  relativePath: `./${relativePath}`,
30
44
  path: projectPath,
31
45
  type: projectType,
32
46
  };
33
47
  }
34
48
  async function getProjects() {
49
+ const rootProject = await verifyProjectPath();
50
+ if (rootProject) {
51
+ return [rootProject];
52
+ }
53
+ const rootPath = (0, files_1.getRootPath)();
35
54
  const projects = [];
36
- for (const relativePath of PROJECTS_DIRS) {
55
+ const folders = await (0, files_1.getDirectoriesInPath)(rootPath);
56
+ for (const relativePath of folders) {
37
57
  const project = await verifyProjectPath(relativePath);
38
58
  if (project) {
39
59
  projects.push(project);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@futurebrand/dev-tools",
3
- "version": "2.5.1",
3
+ "version": "2.5.2",
4
4
  "description": "FutureBrand Dev Tools",
5
5
  "scripts": {
6
6
  "build": "tsc && tsc-alias",