hexojs/hexo-cli

View on GitHub
lib/console/init.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import BlueBirdPromise from 'bluebird';
import { join, resolve } from 'path';
import { magenta } from 'picocolors';
import { existsSync, readdirSync, rmdir, unlink, copyDir, readdir, stat } from 'hexo-fs';
import tildify from 'tildify';
import spawn from 'hexo-util/dist/spawn'; // for rewire
import { sync as commandExistsSync } from 'command-exists';
import type Context from '../context';

const ASSET_DIR = join(__dirname, '../../assets');
const GIT_REPO_URL = 'https://github.com/hexojs/hexo-starter.git';

interface InitArgs {
  _: string[];
  install?: boolean;
  clone?: boolean;
}

async function initConsole(this: Context, args: InitArgs) {
  args = Object.assign({ install: true, clone: true }, args);

  const baseDir = this.base_dir;
  const target = args._[0] ? resolve(baseDir, args._[0]) : baseDir;
  const { log } = this;

  if (existsSync(target) && readdirSync(target).length !== 0) {
    log.fatal(`${magenta(tildify(target))} not empty, please run \`hexo init\` on an empty folder and then copy your files into it`);
    await BlueBirdPromise.reject(new Error('target not empty'));
  }

  log.info('Cloning hexo-starter', GIT_REPO_URL);

  if (args.clone) {
    try {
      await spawn('git', ['clone', '--recurse-submodules', '--depth=1', '--quiet', GIT_REPO_URL, target], {
        stdio: 'inherit'
      });
    } catch (err) {
      log.warn('git clone failed. Copying data instead');
      await copyAsset(target);
    }
  } else {
    await copyAsset(target);
  }

  await BlueBirdPromise.all([
    removeGitDir(target),
    removeGitModules(target)
  ]);
  if (!args.install) return;

  log.info('Install dependencies');

  let npmCommand = 'npm';
  if (commandExistsSync('yarn')) {
    npmCommand = 'yarn';
  } else if (commandExistsSync('pnpm')) {
    npmCommand = 'pnpm';
  }

  try {
    if (npmCommand === 'yarn') {
      const yarnVer = await spawn(npmCommand, ['--version'], {
        cwd: target
      });
      if (typeof yarnVer === 'string' && yarnVer.startsWith('1')) {
        await spawn(npmCommand, ['install', '--production', '--ignore-optional', '--silent'], {
          cwd: target,
          stdio: 'inherit'
        });
      } else {
        npmCommand = 'npm';
      }
    } else if (npmCommand === 'pnpm') {
      await spawn(npmCommand, ['install', '--prod', '--no-optional', '--silent'], {
        cwd: target,
        stdio: 'inherit'
      });
    }

    if (npmCommand === 'npm') {
      await spawn(npmCommand, ['install', '--only=production', '--optional=false', '--silent'], {
        cwd: target,
        stdio: 'inherit'
      });
    }
    log.info('Start blogging with Hexo!');
  } catch (err) {
    log.warn(`Failed to install dependencies. Please run 'npm install' in "${target}" folder.`);
  }
}

async function copyAsset(target: string) {
  await copyDir(ASSET_DIR, target, { ignoreHidden: false });
}

function removeGitDir(target: string) {
  const gitDir = join(target, '.git');

  return stat(gitDir).catch(err => {
    if (err && err.code === 'ENOENT') return;
    throw err;
  }).then(stats => {
    if (stats) {
      return stats.isDirectory() ? rmdir(gitDir) : unlink(gitDir);
    }
  }).then(() => readdir(target)).map(path => join(target, path)).filter(path => stat(path).then(stats => stats.isDirectory())).each(removeGitDir);
}

async function removeGitModules(target: string) {
  try {
    await unlink(join(target, '.gitmodules'));
  } catch (err) {
    if (err && err.code === 'ENOENT') return;
    throw err;
  }
}

export = initConsole;