2026-04-21 19:09:09 +00:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
import fs from 'fs';
|
|
|
|
|
import path from 'path';
|
|
|
|
|
import { execSync } from 'child_process';
|
|
|
|
|
import { fileURLToPath } from 'url';
|
|
|
|
|
|
|
|
|
|
const EXCLUDE = [
|
|
|
|
|
'.git',
|
|
|
|
|
'node_modules',
|
|
|
|
|
'.next',
|
|
|
|
|
'.sisyphus',
|
2026-05-17 16:48:34 +00:00
|
|
|
'.sisyphus',
|
|
|
|
|
'.memsearch',
|
|
|
|
|
'.playwright-mcp',
|
2026-04-21 19:09:09 +00:00
|
|
|
'pnpm-lock.yaml',
|
|
|
|
|
'bin',
|
2026-05-17 16:48:34 +00:00
|
|
|
'features.md',
|
|
|
|
|
'.env.local',
|
|
|
|
|
'DEVELOPMENT.md',
|
|
|
|
|
'tmuxi.template.yml',
|
2026-04-21 19:09:09 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function copyRecursive(src, dest, replacements) {
|
|
|
|
|
const stat = fs.statSync(src);
|
|
|
|
|
if (stat.isDirectory()) {
|
|
|
|
|
if (EXCLUDE.includes(path.basename(src))) return;
|
|
|
|
|
fs.mkdirSync(dest, { recursive: true });
|
|
|
|
|
for (const entry of fs.readdirSync(src)) {
|
|
|
|
|
copyRecursive(path.join(src, entry), path.join(dest, entry), replacements);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2026-05-17 16:48:34 +00:00
|
|
|
if (EXCLUDE.includes(path.basename(src))) return;
|
2026-04-21 19:09:09 +00:00
|
|
|
let content = fs.readFileSync(src, 'utf-8');
|
|
|
|
|
for (const [key, value] of Object.entries(replacements)) {
|
|
|
|
|
content = content.split(key).join(value);
|
|
|
|
|
}
|
|
|
|
|
fs.writeFileSync(dest, content);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function main() {
|
|
|
|
|
const projectName = process.argv[2];
|
|
|
|
|
|
|
|
|
|
if (!projectName) {
|
2026-05-17 16:48:34 +00:00
|
|
|
console.error('Usage: node bin/init-template.mjs <project-name>');
|
2026-04-21 19:09:09 +00:00
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
|
|
|
|
|
|
|
|
if (fs.existsSync(targetDir)) {
|
|
|
|
|
console.error(`Directory already exists: ${targetDir}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
|
const templateDir = path.resolve(__dirname, '..');
|
|
|
|
|
|
|
|
|
|
const replacements = {
|
|
|
|
|
'convex-next-saas': projectName,
|
|
|
|
|
'https://convex-backend.mentat.ovh': 'https://your-convex-backend.example.com',
|
|
|
|
|
'https://backend-site-olnjg91x5ervt6j6owwgnlha.mentat.ovh': 'https://your-convex-site.example.com',
|
|
|
|
|
'self-hosted-convex|01eea0ecf04bed0f70b73021564229f7f08eecff003f7294e5f9279faa4c19ffd5c501b0ac': 'self-hosted-convex|YOUR_ADMIN_KEY',
|
|
|
|
|
'http://localhost:3000': 'http://localhost:3000',
|
|
|
|
|
'SaaS Template': projectName,
|
|
|
|
|
'Create SaaS in a day!': `${projectName} — built with convex-next-saas`,
|
2026-05-15 16:29:52 +00:00
|
|
|
'https://convex-backend.mentat.ovh:3210': 'https://your-convex-backend.example.com:3210',
|
|
|
|
|
'https://backend-site-xxx.mentat.ovh:3211': 'https://your-convex-site.example.com:3211',
|
|
|
|
|
'/home/nxtkofi/.config/tmuxinator/': '~/.config/tmuxinator/',
|
|
|
|
|
'~/vaults/mentat/': '~/workspace/',
|
2026-04-21 19:09:09 +00:00
|
|
|
};
|
|
|
|
|
|
2026-05-17 16:48:34 +00:00
|
|
|
// Safety: exclude target dir name to prevent recursion if run inside template
|
|
|
|
|
EXCLUDE.push(projectName);
|
|
|
|
|
|
|
|
|
|
const srcDir = path.join(targetDir, 'src');
|
|
|
|
|
const docsDir = path.join(targetDir, 'docs');
|
|
|
|
|
|
2026-04-21 19:09:09 +00:00
|
|
|
console.log(`Creating ${projectName}...`);
|
|
|
|
|
|
2026-05-17 16:48:34 +00:00
|
|
|
// Copy template files into <name>/src/
|
|
|
|
|
copyRecursive(templateDir, srcDir, replacements);
|
|
|
|
|
|
|
|
|
|
// Move README.md to docs/readme.md
|
|
|
|
|
const readmeSrc = path.join(srcDir, 'README.md');
|
|
|
|
|
const readmeDest = path.join(docsDir, 'readme.md');
|
|
|
|
|
if (fs.existsSync(readmeSrc)) {
|
|
|
|
|
fs.mkdirSync(docsDir, { recursive: true });
|
|
|
|
|
fs.renameSync(readmeSrc, readmeDest);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Initializing git...');
|
2026-04-21 19:09:09 +00:00
|
|
|
execSync('git init', { cwd: targetDir, stdio: 'inherit' });
|
|
|
|
|
|
|
|
|
|
console.log('Installing dependencies...');
|
2026-05-17 16:48:34 +00:00
|
|
|
execSync('pnpm install', { cwd: srcDir, stdio: 'inherit' });
|
2026-04-21 19:09:09 +00:00
|
|
|
|
2026-05-17 16:48:34 +00:00
|
|
|
console.log(`\nDone!`);
|
|
|
|
|
console.log(` cd ${projectName}/src && pnpm dev --webpack`);
|
2026-04-21 19:09:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main();
|