Files
core/src/cli/setup.js
T
hykocx 81172bda94 chore: rename package from @hykocx/zen to @zen/core
Update all references across source files, documentation, and
configuration to reflect the new package scope and name. This includes
updating `.npmrc` registry config, install instructions, module
examples, and all import path comments throughout the codebase.
2026-04-12 15:09:26 -04:00

252 lines
6.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Zen Setup CLI
* Command-line tool for setting up Zen in a Next.js project
*/
import { mkdir, writeFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import readline from 'readline';
// File templates
const templates = {
instrumentation: `// instrumentation.js
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { initializeZen } = await import('@zen/core');
await initializeZen();
}
}
`,
authRedirect: `import { redirect } from 'next/navigation';
export default function Redirect() {
redirect('/auth/login/');
}
`,
authCatchAll: `export { default } from '@zen/core/auth/page';
`,
adminRedirect: `import { redirect } from 'next/navigation';
export default function Redirect() {
redirect('/admin/dashboard');
}
`,
adminCatchAll: `export { default } from '@zen/core/admin/page';
`,
zenApiRoute: `export { GET, POST, PUT, DELETE, PATCH } from '@zen/core/zen/api';
`,
zenPageRoute: `export { default, generateMetadata } from '@zen/core/modules/page';
`,
nextConfig: `// next.config.js
module.exports = {
experimental: {
instrumentationHook: true,
},
};
`,
};
// File definitions
const files = [
{
path: 'instrumentation.js',
template: 'instrumentation',
description: 'Instrumentation file (initialize Zen)',
},
{
path: 'app/(auth)/auth/page.js',
template: 'authRedirect',
description: 'Auth redirect page',
},
{
path: 'app/(auth)/auth/[...auth]/page.js',
template: 'authCatchAll',
description: 'Auth catch-all route',
},
{
path: 'app/(admin)/admin/page.js',
template: 'adminRedirect',
description: 'Admin redirect page',
},
{
path: 'app/(admin)/admin/[...admin]/page.js',
template: 'adminCatchAll',
description: 'Admin catch-all route',
},
{
path: 'app/zen/api/[...path]/route.js',
template: 'zenApiRoute',
description: 'Zen API catch-all route',
},
{
path: 'app/zen/[...zen]/page.js',
template: 'zenPageRoute',
description: 'Zen public pages catch-all route',
},
];
async function createFile(filePath, content, force = false) {
const fullPath = resolve(process.cwd(), filePath);
// Check if file already exists
if (existsSync(fullPath) && !force) {
console.log(`⏭️ Skipped (already exists): ${filePath}`);
return { created: false, skipped: true };
}
// Create directory if it doesn't exist
const dir = dirname(fullPath);
await mkdir(dir, { recursive: true });
// Write the file
await writeFile(fullPath, content, 'utf-8');
console.log(`✅ Created: ${filePath}`);
return { created: true, skipped: false };
}
async function setupZen(options = {}) {
const { force = false } = options;
console.log('🚀 Setting up Zen for your Next.js project...\n');
let created = 0;
let skipped = 0;
for (const file of files) {
const result = await createFile(
file.path,
templates[file.template],
force
);
if (result.created) created++;
if (result.skipped) skipped++;
}
console.log('\n📝 Summary:');
console.log(` ✅ Created: ${created} file${created !== 1 ? 's' : ''}`);
console.log(` ⏭️ Skipped: ${skipped} file${skipped !== 1 ? 's' : ''}`);
// Check if next.config.js needs updating
const nextConfigPath = resolve(process.cwd(), 'next.config.js');
const nextConfigExists = existsSync(nextConfigPath);
if (!nextConfigExists) {
console.log('\n⚠️ Note: next.config.js not found.');
console.log(' Make sure to enable instrumentation in your Next.js config:');
console.log(' experimental: { instrumentationHook: true }');
}
console.log('\n🎉 Setup complete!');
console.log('\nNext steps:');
console.log(' 1. Add Zen styles to your globals.css:');
console.log(' @import \'@zen/core/styles/zen.css\';');
console.log(' 2. Configure environment variables (see .env.example)');
console.log(' 3. Initialize the database:');
console.log(' npx zen-db init');
console.log('\nFor more information, check the INSTALL.md file.');
}
async function listFiles() {
console.log('📋 Files that will be created:\n');
for (const file of files) {
const exists = existsSync(resolve(process.cwd(), file.path));
const status = exists ? '✓ exists' : '✗ missing';
console.log(` ${status} ${file.path}`);
console.log(` ${file.description}`);
}
console.log('\nRun "npx zen-setup init" to create missing files.');
}
async function runCLI() {
const command = process.argv[2];
const flags = process.argv.slice(3);
const force = flags.includes('--force') || flags.includes('-f');
if (!command || command === 'help') {
console.log(`
Zen Setup CLI
Usage:
npx zen-setup <command> [options]
Commands:
init Create all required files for Zen setup
list List all files that will be created
help Show this help message
Options:
--force, -f Force overwrite existing files
Examples:
npx zen-setup init # Create missing files
npx zen-setup init --force # Overwrite all files
npx zen-setup list # List all files
`);
process.exit(0);
}
try {
switch (command) {
case 'init':
if (force) {
console.log('⚠️ WARNING: --force flag will overwrite existing files!\n');
console.log('Type "yes" to confirm or Ctrl+C to cancel...');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Confirm (yes/no): ', async (answer) => {
if (answer.toLowerCase() === 'yes') {
await setupZen({ force: true });
} else {
console.log('❌ Operation cancelled.');
}
rl.close();
process.exit(0);
});
return; // Don't exit yet
} else {
await setupZen({ force: false });
}
break;
case 'list':
await listFiles();
break;
default:
console.log(`❌ Unknown command: ${command}`);
console.log('Run "npx zen-setup help" for usage information.');
process.exit(1);
}
process.exit(0);
} catch (error) {
console.error('❌ Error:', error.message);
console.error(error.stack);
process.exit(1);
}
}
// Run CLI if called directly
import { fileURLToPath } from 'url';
import { realpathSync } from 'node:fs';
const __filename = realpathSync(fileURLToPath(import.meta.url));
const isMainModule = process.argv[1] && realpathSync(process.argv[1]) === __filename;
if (isMainModule) {
runCLI();
}
export { runCLI, setupZen };