22import { BuildHandler , BuildResult } from 'src/build-system/types' ;
33import { BuilderContext } from 'src/build-system/context' ;
44import { Logger } from '@nestjs/common' ;
5+ import {
6+ generateFilesDependency ,
7+ createFile ,
8+ } from '../../utils/file_generator_util' ;
9+ import { VirtualDirectory } from '../../virtual-dir' ;
10+ import normalizePath from 'normalize-path' ;
11+ import * as path from 'path' ;
12+ import { readFile } from 'fs/promises' ;
513
614// Utility functions (similar to your parseGenerateTag, removeCodeBlockFences)
715import {
@@ -20,6 +28,7 @@ import { generateFrontEndCodePrompt } from './prompt';
2028export class FrontendCodeHandler implements BuildHandler < string > {
2129 readonly id = 'op:FRONTEND:CODE' ;
2230 readonly logger : Logger = new Logger ( 'FrontendCodeHandler' ) ;
31+ private virtualDir : VirtualDirectory ;
2332
2433 /**
2534 * Executes the handler to generate frontend code.
@@ -34,56 +43,95 @@ export class FrontendCodeHandler implements BuildHandler<string> {
3443 const sitemapDoc = context . getNodeData ( 'op:UX:SMD' ) ;
3544 const uxDataMapDoc = context . getNodeData ( 'op:UX:DATAMAP:DOC' ) ;
3645 const backendRequirementDoc = context . getNodeData ( 'op:BACKEND:REQ' ) ;
46+ const fileArchDoc = context . getNodeData ( 'op:FILE:ARCH' ) ;
3747
3848 // 2. Grab any globally stored context as needed
39- const currentFilePath =
40- context . getGlobalContext ( 'currentFrontendFile' ) ||
41- 'src/pages/Home/index.tsx' ;
42- const dependencyFilePath =
43- context . getGlobalContext ( 'frontendDependencyFile' ) || 'dependencies.json' ;
44- const dependenciesContext =
45- context . getGlobalContext ( 'frontendDependenciesContext' ) || '' ;
46-
47- // 3. Generate the prompt
48- const frontendCodePrompt = generateFrontEndCodePrompt (
49- sitemapDoc ,
50- uxDataMapDoc ,
51- backendRequirementDoc . overview ,
52- currentFilePath ,
53- dependencyFilePath ,
54- dependenciesContext ,
55- ) ;
49+ this . virtualDir = context . virtualDirectory ;
50+ const frontendPath = context . getGlobalContext ( 'frontendPath' ) ;
5651
57- this . logger . debug ( 'Generated frontend code prompt.' ) ;
52+ // Dependency
53+ const { sortedFiles, fileInfos } = await generateFilesDependency (
54+ fileArchDoc ,
55+ this . virtualDir ,
56+ ) ;
5857
59- try {
60- // 4. Call the model
61- const modelResponse = await context . model . chatSync (
62- {
63- content : frontendCodePrompt ,
64- } ,
65- 'gpt-4o-mini' , // or whichever model you need
58+ // Iterate the sortedFiles
59+ for ( const file of sortedFiles ) {
60+ const currentFullFilePath = normalizePath (
61+ path . resolve ( frontendPath , file ) ,
62+ ) ;
63+ this . logger . log (
64+ `Generating file in dependency order: ${ currentFullFilePath } ` ,
6665 ) ;
6766
68- // 5. Parse the output
69- const generatedCode = removeCodeBlockFences (
70- parseGenerateTag ( modelResponse ) ,
67+ // Retrieve the direct dependencies for this file
68+ const directDepsArray = fileInfos [ file ] ?. dependsOn || [ ] ;
69+
70+ //gather the contents of each dependency into a single string.
71+ let dependenciesContext = '' ;
72+ for ( const dep of directDepsArray ) {
73+ try {
74+ // Resolve against frontendPath to get the absolute path
75+ const resolvedDepPath = normalizePath (
76+ path . resolve ( frontendPath , dep ) ,
77+ ) ;
78+
79+ // Read the file. (may want to guard so only read certain file types.)
80+ const fileContent = await readFile ( resolvedDepPath , 'utf-8' ) ;
81+
82+ //just append a code:
83+ dependenciesContext += `\n\n[Dependency: ${ dep } ]\n\`\`\`\n${ fileContent } \n\`\`\`\n` ;
84+ } catch ( readError ) {
85+ // If the file doesn't exist or can't be read, log a warning.
86+ this . logger . warn (
87+ `Failed to read dependency "${ dep } " for file "${ file } ": ${ readError } ` ,
88+ ) ;
89+ }
90+ }
91+
92+ // Format for the prompt
93+ const directDependencies = directDepsArray . join ( '\n' ) ;
94+
95+ // Generate the prompt
96+ const frontendCodePrompt = generateFrontEndCodePrompt (
97+ sitemapDoc ,
98+ uxDataMapDoc ,
99+ backendRequirementDoc . overview ,
100+ currentFullFilePath ,
101+ directDependencies ,
102+ dependenciesContext ,
71103 ) ;
104+ this . logger . debug ( 'Generated frontend code prompt.' ) ;
105+
106+ let generatedCode = '' ;
107+ try {
108+ // Call the model
109+ const modelResponse = await context . model . chatSync (
110+ {
111+ content : frontendCodePrompt ,
112+ } ,
113+ 'gpt-4o-mini' , // or whichever model you need
114+ ) ;
72115
73- this . logger . debug ( 'Frontend code generated and parsed successfully.' ) ;
74-
75- // 6. Return success
76- return {
77- success : true ,
78- data : generatedCode ,
79- } ;
80- } catch ( error ) {
81- // 7. Return error
82- this . logger . error ( 'Error during frontend code generation:' , error ) ;
83- return {
84- success : false ,
85- error : new Error ( 'Failed to generate frontend code.' ) ,
86- } ;
116+ // Parse the output
117+ generatedCode = removeCodeBlockFences ( parseGenerateTag ( modelResponse ) ) ;
118+
119+ this . logger . debug ( 'Frontend code generated and parsed successfully.' ) ;
120+ } catch ( error ) {
121+ // Return error
122+ this . logger . error ( 'Error during frontend code generation:' , error ) ;
123+ return {
124+ success : false ,
125+ error : new Error ( 'Failed to generate frontend code.' ) ,
126+ } ;
127+ }
128+
129+ await createFile ( currentFullFilePath , generatedCode ) ;
87130 }
131+
132+ return {
133+ success : true ,
134+ error : new Error ( 'Frontend code generated and parsed successfully.' ) ,
135+ } ;
88136 }
89137}
0 commit comments