Source: bin/commands/init.js

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { readTemplate } from '../utils.js';
import { runWizard } from '../wizard.js';
import { getInitialHtml } from './serve.js';
import { bold, cyan, green } from '../colors.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8'));

/**
 * Initializes a new Avenx project structure.
 * @param {object} cli - AvenxCLI instance.
 * @param {string[]} [args] - CLI arguments.
 */
export async function initProject(cli, args = []) {
  const { stylePreprocessor, layoutTemplate, isInteractive } = await runWizard(args);

  console.log(bold(cyan(`🚀 Initializing new Avenx-JS project (Style: ${stylePreprocessor}, Layout: ${layoutTemplate})...`)));

  // Write avenx.config.json if preprocessor option is configured
  const configPath = path.join(cli.baseDir, 'avenx.config.json');
  if (!fs.existsSync(configPath)) {
    const userConfig = {
      style: {
        preprocessor: stylePreprocessor,
      },
    };
    fs.writeFileSync(configPath, JSON.stringify(userConfig, null, 2) + '\n');
    console.log('  Created: avenx.config.json');
    cli.config = { ...cli.config, ...userConfig };
  }

  const dirs = [
    `${cli.config.srcDir}/components`,
    `${cli.config.srcDir}/pages`,
    `${cli.config.srcDir}/global`,
    `${cli.config.srcDir}/guards`,
    cli.config.distDir,
    '.vscode',
  ];

  dirs.forEach((dir) => {
    const fullPath = path.join(cli.baseDir, dir);
    const created = fs.mkdirSync(fullPath, { recursive: true });
    if (created) {
      console.log(`  Created: ${dir}`);
    }
  });

  // Create initial .vscode files
  const jsConfigPath = path.join(cli.baseDir, '.vscode/jsconfig.json');
  if (!fs.existsSync(jsConfigPath)) {
    const template = readTemplate(cli.baseDir, cli.config, cli.frameworkDir, 'vscode', 'jsconfig.json.template');
    fs.writeFileSync(jsConfigPath, template);
    console.log('  Created: .vscode/jsconfig.json');
  }

  // Teaches the editor's HTML mode about Avenx's own tags, which is what turns
  // component files from "markup the editor does not understand" into files
  // with completion and hover documentation. Referenced by settings.json.
  const htmlDataPath = path.join(cli.baseDir, '.vscode/avenx.html-data.json');
  if (!fs.existsSync(htmlDataPath)) {
    const template = readTemplate(cli.baseDir, cli.config, cli.frameworkDir, 'vscode', 'avenx.html-data.json.template');
    fs.writeFileSync(htmlDataPath, template);
    console.log('  Created: .vscode/avenx.html-data.json');
  }

  const settingsPath = path.join(cli.baseDir, '.vscode/settings.json');
  if (!fs.existsSync(settingsPath)) {
    const template = readTemplate(cli.baseDir, cli.config, cli.frameworkDir, 'vscode', 'settings.json.template');
    fs.writeFileSync(settingsPath, template);
    console.log('  Created: .vscode/settings.json');
  }

  // Create initial index.html
  const indexHtmlPath = path.join(cli.baseDir, 'index.html');
  if (!fs.existsSync(indexHtmlPath)) {
    fs.writeFileSync(indexHtmlPath, getInitialHtml(cli));
    console.log('  Created: index.html');
  }

  // Create initial main.app.js
  const mainAppPath = path.join(cli.baseDir, cli.config.srcDir, 'main.app.js');
  if (!fs.existsSync(mainAppPath)) {
    if (layoutTemplate === 'routing') {
      fs.writeFileSync(
        mainAppPath,
        "import { AvenxApp } from 'avenx-core/runtime';\n" +
          "import Navbar from './components/navbar/navbar.component.js';\n\n" +
          "const app = new AvenxApp({ target: '#app' });\n\n" +
          "app.register('Navbar', Navbar);\n\n" +
          'app.initRouter({\n' +
          "  '': 'Home',\n" +
          "  '#/': 'Home',\n" +
          "  '#/about': 'About',\n" +
          '});\n',
      );
    } else {
      fs.writeFileSync(
        mainAppPath,
        "import { AvenxApp } from 'avenx-core/runtime';\n\nconst app = new AvenxApp({ target: '#app' });\n",
      );
    }
    console.log(`  Created: ${cli.config.srcDir}/main.app.js`);
  }

  if (layoutTemplate === 'routing') {
    const homePageJsPath = path.join(cli.baseDir, cli.config.srcDir, 'pages', 'home.page.js');
    const homePageCssPath = path.join(cli.baseDir, cli.config.srcDir, 'pages', 'home.page.css');
    const aboutPageJsPath = path.join(cli.baseDir, cli.config.srcDir, 'pages', 'about.page.js');
    const aboutPageCssPath = path.join(cli.baseDir, cli.config.srcDir, 'pages', 'about.page.css');

    const navbarDir = path.join(cli.baseDir, cli.config.srcDir, 'components', 'navbar');
    if (!fs.existsSync(navbarDir)) {
      fs.mkdirSync(navbarDir, { recursive: true });
    }
    const navbarJsPath = path.join(navbarDir, 'navbar.component.js');
    const navbarCssPath = path.join(navbarDir, 'navbar.component.css');

    // Write Home page
    if (!fs.existsSync(homePageJsPath)) {
      fs.writeFileSync(
        homePageJsPath,
        '<state title="Home" />\n\n' +
          '<Navbar />\n\n' +
          '<div class="page-container">\n' +
          '    <h1>Home Page</h1>\n' +
          '    <p>Welcome to the home page of your new Avenx application!</p>\n' +
          '    <p>This layout template demonstrates hash-based routing using AvenxRouter.</p>\n' +
          '</div>\n',
      );
      console.log(`  Created: ${cli.config.srcDir}/pages/home.page.js`);
    }
    if (!fs.existsSync(homePageCssPath)) {
      fs.writeFileSync(
        homePageCssPath,
        '<@css>\n' +
          '.page-container {\n' +
          '    padding: 20px;\n' +
          '    font-family: sans-serif;\n' +
          '    max-width: 800px;\n' +
          '    margin: 0 auto;\n' +
          '}\n' +
          '</@css>\n',
      );
      console.log(`  Created: ${cli.config.srcDir}/pages/home.page.css`);
    }

    // Write About page
    if (!fs.existsSync(aboutPageJsPath)) {
      fs.writeFileSync(
        aboutPageJsPath,
        '<state title="About" />\n\n' +
          '<Navbar />\n\n' +
          '<div class="page-container">\n' +
          '    <h1>About Page</h1>\n' +
          '    <p>Welcome to the about page.</p>\n' +
          '</div>\n',
      );
      console.log(`  Created: ${cli.config.srcDir}/pages/about.page.js`);
    }
    if (!fs.existsSync(aboutPageCssPath)) {
      fs.writeFileSync(
        aboutPageCssPath,
        '<@css>\n' +
          '.page-container {\n' +
          '    padding: 20px;\n' +
          '    font-family: sans-serif;\n' +
          '    max-width: 800px;\n' +
          '    margin: 0 auto;\n' +
          '}\n' +
          '</@css>\n',
      );
      console.log(`  Created: ${cli.config.srcDir}/pages/about.page.css`);
    }

    // Write Navbar component
    if (!fs.existsSync(navbarJsPath)) {
      fs.writeFileSync(
        navbarJsPath,
        '<state activeRoute="" />\n\n' +
          '<nav>\n' +
          '    <@css container />\n' +
          '    <a @css link href="#/">Home</a>\n' +
          '    <a @css link href="#/about">About</a>\n' +
          '</nav>\n',
      );
      console.log(`  Created: ${cli.config.srcDir}/components/navbar/navbar.component.js`);
    }
    if (!fs.existsSync(navbarCssPath)) {
      fs.writeFileSync(
        navbarCssPath,
        '<@global>\n' +
          '    @def primary #6366f1;\n' +
          '    @def dark #1e1b4b;\n' +
          '    @def gray #e2e8f0;\n' +
          '</@global>\n\n' +
          '<@css>\n' +
          '    container {\n' +
          '        display: flex;\n' +
          '        gap: 1.5rem;\n' +
          '        padding: 1rem 2rem;\n' +
          '        background: @dark;\n' +
          '        border-bottom: 2px solid @primary;\n' +
          '    }\n\n' +
          '    link {\n' +
          '        color: white;\n' +
          '        text-decoration: none;\n' +
          '        font-weight: 500;\n' +
          '        font-family: sans-serif;\n' +
          '    }\n\n' +
          '    link:hover {\n' +
          '        color: @primary;\n' +
          '    }\n' +
          '</@css>\n',
      );
      console.log(`  Created: ${cli.config.srcDir}/components/navbar/navbar.component.css`);
    }
  }

  // Create initial package.json
  const packageJsonPath = path.join(cli.baseDir, 'package.json');
  if (!fs.existsSync(packageJsonPath)) {
    const projectName =
      path
        .basename(cli.baseDir)
        .toLowerCase()
        .replace(/[^a-z0-9-_]/g, '') || 'avenx-app';
    const packageContent = {
      name: projectName,
      version: '1.0.0',
      type: 'module',
      scripts: {
        dev: 'avenx serve',
        build: 'avenx build',
        serve: 'avenx serve',
      },
      dependencies: {
        'avenx-core': `^${packageJson.version}`,
      },
    };
    fs.writeFileSync(packageJsonPath, JSON.stringify(packageContent, null, 2) + '\n');
    console.log('  Created: package.json');
  }

  // Create initial .gitignore
  const gitignorePath = path.join(cli.baseDir, '.gitignore');

  if (!fs.existsSync(gitignorePath)) {
    fs.writeFileSync(gitignorePath, `node_modules/\n${cli.config.distDir}/\n.DS_Store\n`);
    console.log('  Created: .gitignore');
  }
  console.log(green('✅ Project initialized successfully!'));
  if (isInteractive) {
    process.stdin.pause();
  }

  // Create initial .prettierrc
  const prettierPath = path.join(cli.baseDir, '.prettierrc');

  if (!fs.existsSync(prettierPath)) {
    const template = readTemplate(cli.baseDir, cli.config, cli.frameworkDir, 'init', '.prettierrc');

    fs.writeFileSync(prettierPath, template);

    console.log('  Created: .prettierrc');
  }
}