contentascode/docsmith

View on GitHub
src/docsmith/init.js

Summary

Maintainability
A
3 hrs
Test Coverage
const debug = require('debug')('docsmith:init');
const fs = require('fs-extra');
const read = require('fs').readFileSync;
const path = require('path');
// var npmi = require('npmi');
const yaml = require('js-yaml').safeLoad;

const chalk = require('chalk');
require('longjohn');

const settings = require('./utils/settings');
const packages = require('./init/packages');
// const workspaces = require('./init/workspaces');

const pad = (string, char, length) => (string + char.repeat(length)).slice(0, length);

function init({ template, config, link, defaults, verbose }) {
  // Content as code CLI tool (i.e. not the bare `content` command)
  fs.pathExists(path.join(config, './content.yml'), (err, exists) => {
    if (err) return exit('Error while checking if content.yml exists', err); // => null
    if (exists) {
      // There is a content.yml file.
      if (template) {
        // TODO: Deal with instance templates
        return exit('\nError while initialising: template instances are not yet supported');
      }
      const promzard = require('promzard');
      const file = path.resolve(__dirname, './utils/prompt.js');
      const content = yaml(read(path.join(config, './content.yml'), 'utf8'));

      console.log(
        '\n' +
          '\n' +
          chalk.grey('============================================================================') +
          '\n' +
          chalk.grey('===========                                                      ===========') +
          '\n' +
          chalk.grey('===========') +
          '            ' +
          pad((settings.description || settings.instance) + ' Initialisation', ' ', 42) +
          chalk.grey('===========') +
          '\n' +
          chalk.grey('===========                                                      ===========') +
          '\n' +
          chalk.grey('============================================================================') +
          '\n' +
          '\n' +
          'This script will initialise the current directory as your workspace.' +
          '\n' +
          '\n' +
          'It will also update your global `.content` repository' +
          '\n' +
          'and install the following content packages and their dependencies:' +
          '\n' +
          '\n' +
          Object.keys(content.packages).map(repo => '\t' + repo + ': ' + content.packages[repo] + '\n') +
          '\n' +
          "Please answer a few questions below and you'll be up and running in no time" +
          '\n' +
          '\n' +
          chalk.yellow(
            'NOTE: This is a prototype, please accept defaults by hitting enter\n or hit Ctrl-C at any time to exit.'
          ) +
          '\n'
      );

      promzard(file, { content, defaults }, (err, responses) => {
        if ((err && err.message == 'canceled') || (responses && responses.confirm !== 'yes')) {
          console.log('\nExiting without initialising. See ya!');
          process.exit(0);
        }
        if (err) exit('Error while processing prompt results', err);

        fs.existsSync(responses.repository) || fs.mkdirSync(responses.repository);
        debug('> Content repository: ', responses.repository);

        const pkgs = path.join(responses.repository, 'packages');
        fs.existsSync(pkgs) || fs.mkdirSync(pkgs);
        debug('> Content packages directory: ', pkgs);

        // Change working directory temporarily as npm api is insufficient.
        const current = process.cwd();
        try {
          process.chdir(pkgs);
          debug('changed directory: ', pkgs);
        } catch (err) {
          exit('\nError while changing directory', err);
        }
        packages.install(
          { packages: content.packages, repository: responses.repository, link, verbose },
          (err, installed) => {
            if (err) return exit('\nError while installing packages', err);
            // console.log('Content packages installed:' + JSON.stringify(installed));
            // Deploying worksaces

            // restore working directory
            try {
              process.chdir(current);
              debug('changed directory: ', current);
            } catch (err) {
              exit('\nError while changing directory', err);
            }

            // Deploy symlinks.
            installed.forEach(({ name, content: { workspace } }) => {
              Object.keys(workspace)
                .reduce(
                  (acc, workspace) => (acc.includes(workspace.split('/')[0]) ? acc : [...acc, workspace.split('/')[0]]),
                  []
                )
                .forEach(group => {
                  try {
                    fs.ensureSymlinkSync(
                      path.join(responses.repository, 'packages', name, 'content'),
                      path.join(current, group)
                    );
                    console.log(
                      'Symlinked',
                      path.join(responses.repository, 'packages', name, 'content') + ' to ' + path.join(current, group)
                    );
                  } catch (e) {
                    exit(
                      '\nError while creating synlink from ' +
                        path.join(responses.repository, 'packages', name, 'content') +
                        ' to ' +
                        path.join(current, group),
                      e
                    );
                  }
                });
            });

            // For now, do not deploy the init workspace until the workspace approach is fleshed out.
            //
            // workspaces.deploy(
            //   installed.map(({ name, content: { workspace } }) => ({ name, workspace })),
            //   responses.repository,
            //   err => {
            //     if (err) exit('\nError while deploying workspaces', err);
            //
            //     console.log(
            //       '\n' +
            //         chalk.grey('============================================================================') +
            //         '\n' +
            //         chalk.grey('===========                                                      ===========') +
            //         '\n' +
            //         chalk.grey('===========') +
            //         '   Initialisation complete.                           ' +
            //         chalk.grey('===========') +
            //         '\n' +
            //         chalk.grey('===========') +
            //         '   - use ' +
            //         chalk.yellow(settings.instance + ' start') +
            //         pad(' to open the ' + settings.description, ' ', 19 + (settings.instance + ' start').length) +
            //         chalk.grey('===========') +
            //         '\n' +
            //         chalk.grey('===========                                                      ===========') +
            //         '\n' +
            //         chalk.grey('============================================================================') +
            //         '\n'
            //     );
            //
            //     // I'll want to save the location of the workspace to the content repo to allow
            //     // launching `safetag start` from anywhere.
            //
            //     return;
            //   }
            // );
          }
        );
      });

      //   - Use `safetag-toolkit` as its default init mode.
      //   - npm install `content.yml` dependencies in `~/.content/packages`
      //   - `bootstrap`
      //     - version controlled staging directory
      //       - ask about fork origin remote.
      //         - if no entry then point to doc and setup only `upstream` remote
      //       - create git repo in `~/.content/staging/@safetag/*` for each dependent modules and publication containers.
      //       - content.yml manages versions?
      //     - working directory
      //       - render taxonomy from `content` folder in `toolkit` package. (metalsmith workspace deploy config)
    } else {
      console.error(
        'Error: CLI tool does not have a content.yml configuration file. Please report the error to the developer.'
      );
      process.exit(1, '');
    }
  });
}

const exit = (message, error) => {
  {
    console.log(chalk.red('\n' + message + '\n'));
    if (error) console.log('error', error);
    console.log(
      chalk.grey('\n==================================================================\n\n') +
        chalk.red(
          'Please alert the developer by submitting an issue \nat https://github.com/contentascode/safetag/issues and copy the whole output of the command above.\n\nApologies for the inconvenience!\n'
        )
    );
    process.exit(1);
  }
};

module.exports.run = init;