Skip to content

Commit 06a9f80

Browse files
committed
feat: implement API route for file generation with IDE and framework parameters
1 parent 8001b2d commit 06a9f80

File tree

2 files changed

+139
-7
lines changed

2 files changed

+139
-7
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { readFile } from 'fs/promises'
3+
import path from 'path'
4+
import type { WizardResponses } from '@/types/wizard'
5+
import { getTemplateConfig, type TemplateKey } from '@/lib/template-config'
6+
7+
// Helper function to map output file types to template types
8+
function mapOutputFileToTemplateType(outputFile: string): string {
9+
const mapping: Record<string, string> = {
10+
'instructions-md': 'copilot-instructions',
11+
'cursor-rules': 'cursor-rules',
12+
'json-rules': 'json-rules',
13+
'agents-md': 'agents'
14+
}
15+
return mapping[outputFile] || outputFile
16+
}
17+
18+
export async function POST(
19+
request: NextRequest,
20+
{ params }: { params: { ide: string; framework: string; fileName: string } }
21+
) {
22+
try {
23+
const { ide, framework, fileName } = params
24+
const body = await request.json()
25+
const responses: WizardResponses = body
26+
27+
// Determine template configuration based on the request
28+
let templateConfig
29+
30+
const frameworkFromPath = framework && !['general', 'none', 'undefined'].includes(framework)
31+
? framework
32+
: undefined
33+
34+
if (ide) {
35+
const templateKeyFromParams: TemplateKey = {
36+
ide,
37+
templateType: mapOutputFileToTemplateType(fileName),
38+
framework: frameworkFromPath
39+
}
40+
templateConfig = getTemplateConfig(templateKeyFromParams)
41+
}
42+
43+
// Check if this is a combination-based request
44+
if (!templateConfig && responses.preferredIde && responses.outputFile) {
45+
const templateKey: TemplateKey = {
46+
ide: responses.preferredIde,
47+
templateType: mapOutputFileToTemplateType(responses.outputFile),
48+
framework: responses.frameworkSelection || undefined
49+
}
50+
templateConfig = getTemplateConfig(templateKey)
51+
}
52+
53+
// Fallback to legacy fileName-based approach
54+
if (!templateConfig) {
55+
templateConfig = getTemplateConfig(fileName)
56+
}
57+
58+
if (!templateConfig) {
59+
return NextResponse.json(
60+
{ error: `Template not found for fileName: ${fileName}` },
61+
{ status: 404 }
62+
)
63+
}
64+
65+
// Read the template file
66+
const templatePath = path.join(process.cwd(), 'file-templates', templateConfig.template)
67+
const template = await readFile(templatePath, 'utf-8')
68+
69+
// Replace template variables with actual values
70+
let generatedContent = template
71+
72+
// Helper function to replace template variables gracefully
73+
const replaceVariable = (key: keyof WizardResponses, fallback: string = 'Not specified') => {
74+
const value = responses[key]
75+
const placeholder = `{{${key}}}`
76+
77+
if (value === null || value === undefined || value === '') {
78+
generatedContent = generatedContent.replace(placeholder, fallback)
79+
} else {
80+
generatedContent = generatedContent.replace(placeholder, String(value))
81+
}
82+
}
83+
84+
// Replace all template variables
85+
replaceVariable('preferredIde')
86+
replaceVariable('frameworkSelection')
87+
replaceVariable('tooling')
88+
replaceVariable('language')
89+
replaceVariable('projectPriority')
90+
replaceVariable('codeStyle')
91+
replaceVariable('variableNaming')
92+
replaceVariable('fileNaming')
93+
replaceVariable('componentNaming')
94+
replaceVariable('exports')
95+
replaceVariable('comments')
96+
replaceVariable('collaboration')
97+
replaceVariable('fileStructure')
98+
replaceVariable('styling')
99+
replaceVariable('stateManagement')
100+
replaceVariable('apiLayer')
101+
replaceVariable('folders')
102+
replaceVariable('testingUT')
103+
replaceVariable('testingE2E')
104+
replaceVariable('dataFetching')
105+
replaceVariable('reactPerf')
106+
replaceVariable('auth')
107+
replaceVariable('validation')
108+
replaceVariable('logging')
109+
replaceVariable('commitStyle')
110+
replaceVariable('prRules')
111+
112+
// Return the generated content
113+
return NextResponse.json({
114+
content: generatedContent,
115+
fileName: templateConfig.outputFileName
116+
})
117+
118+
} catch (error) {
119+
console.error('Error generating file:', error)
120+
return NextResponse.json(
121+
{ error: 'Failed to generate file' },
122+
{ status: 500 }
123+
)
124+
}
125+
}

components/instructions-wizard.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -593,14 +593,21 @@ export function InstructionsWizard({ onClose }: InstructionsWizardProps) {
593593

594594
// Call the API to generate the instructions file
595595
if (questionsAndAnswers.outputFile) {
596+
const ideSegment = questionsAndAnswers.preferredIde ?? 'unknown'
597+
const frameworkSegment = questionsAndAnswers.frameworkSelection ?? 'general'
598+
const fileNameSegment = questionsAndAnswers.outputFile
599+
596600
try {
597-
const response = await fetch(`/api/generate/${questionsAndAnswers.outputFile}`, {
598-
method: 'POST',
599-
headers: {
600-
'Content-Type': 'application/json',
601-
},
602-
body: JSON.stringify(questionsAndAnswers),
603-
})
601+
const response = await fetch(
602+
`/api/generate/${encodeURIComponent(ideSegment)}/${encodeURIComponent(frameworkSegment)}/${encodeURIComponent(fileNameSegment)}`,
603+
{
604+
method: 'POST',
605+
headers: {
606+
'Content-Type': 'application/json',
607+
},
608+
body: JSON.stringify(questionsAndAnswers),
609+
}
610+
)
604611

605612
if (response.ok) {
606613
const data = await response.json()

0 commit comments

Comments
 (0)