Files
connectai/src/scaffolder/templates.ts
T

155 lines
4.8 KiB
TypeScript

/**
* Scaffolder template catalog.
*
* Templates are pure data — `(projectName) => { [relativePath]: contents }`. New
* templates are added by appending to `TEMPLATES`; the rest of the scaffolder
* (validation, IO, command UX) does not need to change.
*
* This intentionally mirrors the static/vite-vanilla/vite-react options that
* Connect_origin's Developer agent ships, but split out of the giant inline
* function so each template body is grep-friendly.
*/
export type ProjectTemplateId = 'static' | 'vite-vanilla' | 'vite-react';
export interface ProjectTemplate {
id: ProjectTemplateId;
label: string;
detail: string;
/** Returns map of `relativePath -> fileContents`. Project name is already sanitized. */
files(name: string): Record<string, string>;
}
const README = (name: string, template: string) =>
`# ${name}\n\n` +
`Astra의 Project Scaffolder가 ${new Date().toISOString().slice(0, 10)}\`${template}\` 템플릿으로 생성한 프로젝트입니다.\n`;
export const TEMPLATES: ProjectTemplate[] = [
{
id: 'static',
label: 'static',
detail: 'index.html 한 장 (Tailwind CDN)',
files: (name) => ({
'site/index.html':
`<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>${name}</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-zinc-950 text-zinc-100 min-h-screen flex items-center justify-center">
<main class="text-center space-y-4">
<h1 class="text-4xl font-bold">${name}</h1>
<p class="text-zinc-400">Astra · Project Scaffolder</p>
</main>
</body>
</html>
`,
'README.md': README(name, 'static'),
}),
},
{
id: 'vite-vanilla',
label: 'vite-vanilla',
detail: 'Vite + 순수 JS',
files: (name) => ({
'site/package.json': JSON.stringify({
name,
private: true,
type: 'module',
scripts: { dev: 'vite', build: 'vite build', preview: 'vite preview' },
devDependencies: { vite: '^5.0.0' },
}, null, 2) + '\n',
'site/index.html':
`<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>${name}</title>
</head>
<body>
<h1>${name}</h1>
<script type="module" src="/main.js"></script>
</body>
</html>
`,
'site/main.js':
`document.querySelector('h1').addEventListener('click', () => {
console.log('hi from ${name}');
});
`,
'README.md': README(name, 'vite-vanilla'),
}),
},
{
id: 'vite-react',
label: 'vite-react',
detail: 'Vite + React + TypeScript',
files: (name) => ({
'site/package.json': JSON.stringify({
name,
private: true,
type: 'module',
scripts: { dev: 'vite', build: 'tsc && vite build', preview: 'vite preview' },
dependencies: { react: '^18.3.0', 'react-dom': '^18.3.0' },
devDependencies: {
'@types/react': '^18.3.0',
'@types/react-dom': '^18.3.0',
'@vitejs/plugin-react': '^4.3.0',
typescript: '^5.4.0',
vite: '^5.0.0',
},
}, null, 2) + '\n',
'site/tsconfig.json': JSON.stringify({
compilerOptions: {
target: 'ES2020',
useDefineForClassFields: true,
lib: ['ES2020', 'DOM', 'DOM.Iterable'],
module: 'ESNext',
skipLibCheck: true,
moduleResolution: 'bundler',
allowImportingTsExtensions: true,
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'react-jsx',
strict: true,
},
include: ['src'],
}, null, 2) + '\n',
'site/vite.config.ts':
`import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()] });
`,
'site/index.html':
`<!doctype html>
<html lang="ko">
<head><meta charset="utf-8"><title>${name}</title></head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
`,
'site/src/main.tsx':
`import React from 'react';
import { createRoot } from 'react-dom/client';
function App() {
return <h1>${name}</h1>;
}
createRoot(document.getElementById('root')!).render(<App />);
`,
'README.md': README(name, 'vite-react'),
}),
},
];
export function findTemplate(id: string): ProjectTemplate | undefined {
return TEMPLATES.find(t => t.id === id);
}