Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 44 additions & 25 deletions backend/src/build-system/handlers/file-manager/file-arch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import {
ModelUnavailableError,
} from 'src/build-system/errors';
import { VirtualDirectory } from 'src/build-system/virtual-dir';

import { FileStructureHandler } from '../file-structure';
import { UXDMDHandler } from '../../ux/datamap';
import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager';
import {
buildDependencyGraph,
validateAgainstVirtualDirectory,
} from 'src/build-system/utils/file_generator_util';
import { FileStructureHandler } from '../file-structure';
import { UXDMDHandler } from '../../ux/datamap';
import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager';

@BuildNode()
@BuildNodeRequire([FileStructureHandler, UXDMDHandler])
Expand All @@ -36,6 +37,7 @@ export class FileFAHandler implements BuildHandler<string> {
const fileStructure = context.getNodeData(FileStructureHandler);
const datamapDoc = context.getNodeData(UXDMDHandler);

this.logger.log('fileStructure:', fileStructure);
if (!fileStructure || !datamapDoc) {
throw new InvalidParameterError(
'Missing required parameters: fileStructure or datamapDoc.',
Expand All @@ -57,7 +59,7 @@ export class FileFAHandler implements BuildHandler<string> {

${datamapDoc}

Next, I'll provide the **Directory Structure**.`,
Next, I will provide the **Directory Structure** to help you understand the full project architecture.`,
},
{
role: 'user' as const,
Expand All @@ -67,13 +69,15 @@ export class FileFAHandler implements BuildHandler<string> {

${fileStructure}

Please generate the full File Architecture JSON object now, ensuring adherence to all the rules.`,
Based on this structure and the analysis provided earlier, please generate the File Architecture JSON object. Ensure the output adheres to all rules and guidelines specified in the system prompt.`,
},
{
role: 'user' as const,
content: `**Final Check:**
- Ensure the JSON structure is correct.
- Ensure all files and dependencies are included.`,
content: `**Final Check**
Before returning the output, ensure the following:
- The JSON structure is correctly formatted and wrapped in <GENERATE></GENERATE> tags.
- File extensions and paths match those in the Directory Structure.
- All files and dependencies are included.`,
},
];

Expand All @@ -93,28 +97,43 @@ export class FileFAHandler implements BuildHandler<string> {
throw new ModelUnavailableError('Model is unavailable:' + error);
}

const tagContent = parseGenerateTag(fileArchContent);
const jsonData = extractJsonFromText(tagContent);
try {
const tagContent = parseGenerateTag(fileArchContent);
const jsonData = extractJsonFromText(tagContent);

if (!jsonData) {
this.logger.error('Failed to extract JSON from text');
throw new ResponseParsingError('Failed to extract JSON from text.');
}
if (!jsonData) {
this.logger.error('Failed to extract JSON from text');
throw new ResponseParsingError('Failed to extract JSON from text.');
}

if (!this.validateJsonData(jsonData)) {
this.logger.error('File architecture JSON validation failed.');
throw new ResponseParsingError(
'File architecture JSON validation failed.',
if (!this.validateJsonData(jsonData)) {
this.logger.error('File architecture JSON validation failed.');
throw new ResponseParsingError(
'File architecture JSON validation failed.',
);
}

// validate with virutual dir
const { graph, nodes, fileInfos } = buildDependencyGraph(jsonData);

const invalidFiles = validateAgainstVirtualDirectory(
nodes,
this.virtualDir,
);
}

console.log(jsonData);
// validate with virutual dir
const { graph, nodes, fileInfos } = buildDependencyGraph(jsonData);
if (!validateAgainstVirtualDirectory(nodes, this.virtualDir)) {
this.logger.error('Validate Against Virtual Directory Fail !!!');
if (invalidFiles) {
this.logger.error('Validate Against Virtual Directory Fail !!!');
this.logger.error(`Invalid files detected:\n${invalidFiles}`);
this.logger.error(`${fileArchContent}`);
this.logger.error(`${fileStructure}`);
throw new ResponseParsingError(
'Failed to validate against virtualDirectory.',
);
}
} catch (error) {
this.logger.error('File architecture validation failed.');
throw new ResponseParsingError(
'Failed to validate against virtualDirectory.',
`File architecture JSON validation failed. ${error.message}`,
);
}

Expand Down
27 changes: 13 additions & 14 deletions backend/src/build-system/handlers/file-manager/file-arch/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ export const generateFileArchPrompt = (): string => {
### Instructions
1. **Analyze the Inputs**:
- Use the directory structure to identify all files and folders.
- Do not assume any additional files or paths. The structure must be based exclusively on the given list.
- Leverage the page-by-page analysis to understand the roles and interactions of different components and pages.
- Determine the role of each file based on its path and the provided analysis (e.g., page, component, context, hook, styles).
- Identify direct dependencies for each file by considering typical imports based on roles, naming conventions, and the provided analysis.

2. **Generate File Dependency JSON**:
- Each file must be represented using its full path starting from src/.
- Ensure dependencies are strictly limited to files in the "Paths" array.
- Use absolute file paths from "Paths" for all "dependsOn" values.
Do not use relative paths (./, ../).
Every dependency must match exactly one of the files in "Paths".
- Any file without dependencies should have "dependsOn": [].
- For each file, list its direct dependencies as an array of relative paths in the \`dependsOn\` field.
- Use relative paths for dependencies whenever possible. For example:
- If a file \`index.tsx\` references a CSS file \`index.css\` in the same folder, the dependency should be listed as \`"./index.css"\`.
- If a file references another file in its parent folder, use \`"../filename"\`.
- Only use absolute paths (e.g., starting with \`src/\`) when no shorter relative path is available.
- Include CSS/SCSS files as dependencies for any JavaScript or TypeScript files that reference them (e.g., through imports or implied usage).
- Include files that have no dependencies with an empty \`dependsOn\` array.
- Organize the output in a \`files\` object where keys are file paths, and values are their dependency objects.
- For the router, remember to include all the page components as dependencies, as the router imports them to define the application routes.
- For the src/index.tsx, remember to include router.ts.
Expand All @@ -37,20 +38,18 @@ export const generateFileArchPrompt = (): string => {
</GENERATE>
\`\`\`
- Keys: Every file must be represented with its full path, starting from src/.
- Dependencies:
Files with no dependencies must have "dependsOn": [].
Every file except src/index.ts must appear in at least one dependsOn array.
- Use relative paths for dependencies wherever possible (./filename for same-folder dependencies, ../filename for parent-folder dependencies).
- Dependency Rules:
All dependencies must exist in the "Paths" array.
No inferred or assumed files should be added.
- Wrap the JSON output with \`<GENERATE></GENERATE>\` tags.

### Notes
- **CSS Dependencies**: Any file that relies on a CSS/SCSS module file (e.g., \`Header.module.css\`) must list it in the \`dependsOn\` array.
- **Use Relative Paths When Possible**: Ensure dependencies are listed using the shortest possible path (e.g., \`"./filename"\` for files in the same folder).
- **Dependency Inclusion Rule**: All files, except for \`src/index.ts\`, must be depended upon by at least one other file. This means they should appear in the \`dependsOn\` array of at least one other file.
- The \`dependsOn\` field should reflect logical dependencies inferred from both the directory structure and the page-by-page analysis.
- Use common project patterns to deduce dependencies (e.g., pages depend on components, contexts, hooks, and styles).
- Include all files in the output, even if they have no dependencies.

### Output
Return only the JSON object wrapped in \`<GENERATE></GENERATE>\` tags.`;
Return only the JSON object wrapped in \`<GENERATE></GENERATE>\` tags.
Do not forget <GENERATE></GENERATE> tags.
`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ export class FileGeneratorHandler implements BuildHandler<string> {
const currentDir = path.dirname(currentFile);
const hasExtension = path.extname(dependency).length > 0;

if (!hasExtension) {
dependency = path.join(dependency, 'index.ts');
}
// if (!hasExtension) {
// dependency = path.join(dependency, 'index.ts');
// }

const resolvedPath = path.join(currentDir, dependency).replace(/\\/g, '/');
this.logger.log(`Resolved dependency: ${resolvedPath}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { BuildHandler, BuildOpts, BuildResult } from 'src/build-system/types';
import { BuilderContext } from 'src/build-system/context';
import { prompts } from './prompt';
import { Logger } from '@nestjs/common';
import { removeCodeBlockFences } from 'src/build-system/utils/strings';
import {
parseGenerateTag,
removeCodeBlockFences,
} from 'src/build-system/utils/strings';
import { chatSyncWithClocker } from 'src/build-system/utils/handler-helper';
import {
ResponseParsingError,
MissingConfigurationError,
} from 'src/build-system/errors';
import { UXSMDHandler } from '../../ux/sitemap-document';
import { UXDMDHandler } from '../../ux/datamap';
import { parseGenerateTag } from 'src/build-system/utils/strings';
import { BuildNode, BuildNodeRequire } from 'src/build-system/hanlder-manager';

/**
Expand All @@ -35,7 +37,7 @@ export class FileStructureHandler implements BuildHandler<string> {
const sitemapDoc = context.getNodeData(UXSMDHandler);
const datamapDoc = context.getNodeData(UXDMDHandler);
// const projectPart = opts?.projectPart ?? 'frontend';
const projectPart = 'frontend';
const projectPart = opts?.projectPart ?? 'frontend';
const framework = context.getGlobalContext('framework') ?? 'react';

// Validate required arguments
Expand Down Expand Up @@ -88,6 +90,7 @@ export class FileStructureHandler implements BuildHandler<string> {
},
];

// Get the generated file structure content
let fileStructureContent: string;
try {
fileStructureContent = await chatSyncWithClocker(
Expand All @@ -106,73 +109,60 @@ export class FileStructureHandler implements BuildHandler<string> {
);
}
} catch (error) {
return { success: false, error };
this.logger.error(
`Failed to generate file structure: ${error.message}`,
error.stack,
);
return {
success: false,
error: new ResponseParsingError(
`File structure generation failed. ${error.message}`,
),
};
}

// Convert the tree structure to JSON
// const convertToJsonPrompt =
// prompts.convertTreeToJsonPrompt(fileStructureContent);

// let fileStructureJsonContent: string;
// try {
// fileStructureJsonContent = await chatSyncWithClocker(
// context,
// {
// model: 'gpt-4o-mini',
// messages: [{ content: convertToJsonPrompt, role: 'system' }],
// },
// 'convertToJsonPrompt',
// this.id,
// );

// if (!fileStructureJsonContent || fileStructureJsonContent.trim() === '') {
// throw new ResponseParsingError(
// `Generated content is empty during op:FILE:STRUCT 2.`,
// );
// }
// } catch (error) {
// return { success: false, error };
// }

// Build the virtual directory
// Parse the file structure content
let fileStructureJsonContent = '';
try {
fileStructureJsonContent = parseGenerateTag(fileStructureContent);
} catch (error) {
return {
success: false,
error: new ResponseParsingError(
'Failed to parse file Structure Json Content.',
`Failed to parse file Structure Json Content. ${error.message}`,
),
};
}

// Build the virtual directory
this.logger.log('start building');
try {
const successBuild = context.buildVirtualDirectory(
fileStructureJsonContent,
);
if (!successBuild) {
this.logger.error(
'Failed to build virtual directory.' + fileStructureJsonContent,
);
throw new ResponseParsingError('Failed to build virtual directory.');
}
} catch (error) {
this.logger.error(
'Non-retryable error during virtual directory build:',
error,
);
return {
success: false,
error: new ResponseParsingError('Failed to build virtual directory.'),
error: new ResponseParsingError(
`Failed to build virtual directory. ${error.message}`,
),
};
}

this.logger.log(
`File structure JSON content and virtual directory built successfully.
${removeCodeBlockFences(fileStructureJsonContent)}`,
);
//debug script print all files
context.virtualDirectory.getAllFiles().forEach((file) => {
this.logger.log(file);
});

return {
success: true,
data: removeCodeBlockFences(fileStructureJsonContent),
data: removeCodeBlockFences(fileStructureContent),
};
}

Expand Down
Loading
Loading