Home Reference Source

src/generator/getCode.js

// @flow
/* eslint-disable max-len */
/* eslint-disable no-console */
/* eslint-disable no-unused-vars */

import Handlebars from 'handlebars';
import { getContext } from './getContext';
import { getPartials } from './getPartials';
import { getName } from './getName';
import type { configCodeType } from '../constants';

import {
  ENCODING,
  USER_LITERAL,
  TEMPLATE_EXTENSION,
  TEMPLATES_DIR,
  TEMPLATES_MODEL_DIR,
  TEMPLATES_COMMON_DIR,
  TEMPLATES_DEFAULT_DIR,
  TEMPLATES_AUTH_DIR,
  TEMPLATES_DEFAULT_TEMPLATE,
  MODEL,
  RESOLVER
} from '../constants';

/**
 * get generated code from template partials
 * @public
 * @param {string} codeType - MODEL or RESOLVER run
 * @param {object} config - configuration object
 * @property {object} inputSchema - schema of the type 
 * @property {string} userType - the user type
 * @property {string} defaultTemplate - name of the start template
 * @property {array} basePath - path to the base templates directory
 * @property {string} baseExtension - file extension '.template'
 * @property {string} baseEncoding - base file encoding 'utf8'
 * @property {string} baseCommonDir - commonly used template partials
 * @property {string} baseDefaultDir - default directory for templates
 * @property {function} baseGetNameFunc - calculate the name of a partial
 * @property {array} authPath - path to the authorization templates directory
 * @property {string} authExtension - auth file encoding 'utf8'
 * @property {string} authEncoding - auth file encoding
 * @property {string} authCommonDir - commonly used auth template partials
 * @property {string} authDefaultDir - default directory for auth templates
 * @property {function} authGetNameFunc - calculate tne name of a partial
 * @return {string} code - generated code for a model
 */

export function getCode(
  codeType: string = MODEL,
  {
    userType = USER_LITERAL,
    inputSchema = {},
    defaultTemplate = TEMPLATES_DEFAULT_TEMPLATE,
    basePath = [TEMPLATES_DIR, TEMPLATES_MODEL_DIR, TEMPLATES_DEFAULT_DIR],
    baseExtension = TEMPLATE_EXTENSION,
    baseEncoding = ENCODING,
    baseCommonDir = TEMPLATES_COMMON_DIR,
    baseDefaultDir = TEMPLATES_DEFAULT_DIR,
    baseGetNameFunc = getName,
    authPath = [TEMPLATES_DIR, TEMPLATES_MODEL_DIR, TEMPLATES_AUTH_DIR],
    authExtension = TEMPLATE_EXTENSION,
    authEncoding = ENCODING,
    authCommonDir = TEMPLATES_COMMON_DIR,
    authDefaultDir = TEMPLATES_DEFAULT_DIR,
    authGetNameFunc = getName
  }: configCodeType
): string {
  // partials dictionary for template resolution
  const partials = {};

  // adds helpers to handlebars
  registerHandlebarsHelpers();

  // define the compiler
  function compile(templates) {
    templates.forEach(partial => {
      partials[partial.name] = Handlebars.compile(partial.source);
      Handlebars.registerPartial(partial.name, partials[partial.name]);
    });
  }

  // getting data context
  const context = getContext(inputSchema, userType, codeType);
  const TypeName = context.TypeName;
  const typeName = context.typeName;
  let startTemplate = typeName;

  // getting auth common partial templates (might be in an npm module)
  const authCommonPartials = getPartials({
    basePath: authPath,
    directoryPath: [authCommonDir],
    extension: authExtension,
    encoding: authEncoding,
    getNameFunc: authGetNameFunc
  });

  // getting auth type specific partial templates (might be in an npm module)
  let authTypePartials = getPartials({
    basePath: authPath,
    directoryPath: [typeName],
    extension: authExtension,
    encoding: authEncoding,
    getNameFunc: authGetNameFunc
  });

  // fallback to auth default partial templates (might be in an npm module)
  if (authTypePartials.length === 0) {
    authTypePartials = getPartials({
      basePath: authPath,
      directoryPath: [authDefaultDir],
      extension: authExtension,
      encoding: authEncoding,
      getNameFunc: authGetNameFunc
    });
  }

  // getting common partial templates
  const baseCommonPartials = getPartials({
    basePath,
    directoryPath: [baseCommonDir],
    extension: baseExtension,
    encoding: baseEncoding,
    getNameFunc: baseGetNameFunc
  });

  // getting type specific partial templates
  let baseTypePartials = getPartials({
    basePath,
    directoryPath: [typeName],
    extension: baseExtension,
    encoding: baseEncoding,
    getNameFunc: baseGetNameFunc
  });

  // fallback to default partial templates,
  // if there are no type specific templates found
  if (baseTypePartials.length === 0) {
    baseTypePartials = getPartials({
      basePath,
      directoryPath: [baseDefaultDir],
      extension: baseExtension,
      encoding: baseEncoding,
      getNameFunc: baseGetNameFunc
    });
    // reset start template to the default template,
    // as type specific template does not exist
    startTemplate = defaultTemplate;
  }

  // compile all auth partials
  compile(authCommonPartials);
  compile(authTypePartials);

  // compile all base partials
  compile(baseCommonPartials);
  compile(baseTypePartials);

  console.log(
    `Generating ${codeType} for type "${TypeName}" with template "${startTemplate}"`
  );

  // run start template with data context
  const code = partials[startTemplate](context);

  // return the final code
  return code;
}

/**
 * registers a helper, which could be used in the templates
 * @example
 * {{#foreach}}
 *     {{#if $last}} console.log('this was the last element') {{/if}}
 *     {{#if $notLast}} console.log('this was not the last one') {{/if}}
 * {{/foreach}}
 */

function registerHandlebarsHelpers() {
  Handlebars.registerHelper('foreach', function(arr, options) {
    if (options.inverse && !arr.length) {
      return options.inverse(this);
    }
    return arr
      .map(function(item, index) {
        item.$index = index;
        item.$first = index === 0;
        item.$last = index === arr.length - 1;
        item.$notFirst = index !== 0;
        item.$notLast = index !== arr.length - 1;
        return options.fn(item);
      })
      .join('');
  });
}