Source: compile-module.js

import { property } from 'safe-identifier';
import Compiler from './compiler';

function stringifyRuntime(runtime) {
  const imports = {};
  const vars = {};

  for (const [name, fn] of Object.entries(runtime)) {
    if (fn.module) {
      const prev = imports[fn.module];
      imports[fn.module] = prev ? [...prev, name] : [name];
    } else {
      vars[name] = String(fn);
    }
  }

  const is = Object.entries(imports).map(
    ([module, names]) =>
      `import { ${names.sort().join(', ')} } from '${module}';`
  );
  const vs = Object.entries(vars).map(([id, value]) =>
    new RegExp(`^function ${id}\\b`).test(value)
      ? value
      : `const ${id} = ${value};`
  );

  if (is.length > 0 && vs.length > 0) is.push('');
  return is.concat(vs).join('\n');
}

function stringifyObject(obj, level = 0) {
  if (typeof obj !== 'object') return obj;
  const indent = '  '.repeat(level);
  const o = Object.keys(obj).map(key => {
    const v = stringifyObject(obj[key], level + 1);
    return `\n${indent}  ${property(null, key)}: ${v}`;
  });
  return `{${o.join(',')}\n${indent}}`;
}

/**
 * Compile a collection of messages into an ES module.
 *
 * ```
 * import compileModule from 'messageformat/compile-module'
 * ```
 *
 * With `messages` as a hierarchical structure of ICU MessageFormat strings,
 * the output of `compileModule()` will be the source code of an ES module with
 * a default export matching the input structure, with each string replaced by
 * its corresponding JS function.
 *
 * If this MessageFormat instance has been initialized with support for more
 * than one locale, using a key that matches the locale's identifier at any
 * depth of a `messages` object will set its child elements to use that locale.
 *
 * @function compileModule
 * @param {MessageFormat} messageformat - A MessageFormat instance
 * @param {object} messages - The input messages to be compiled
 * @returns {string} - String representation of the compiled module
 *
 * @example
 * import fs from 'fs'
 * import MessageFormat from 'messageformat'
 * import compileModule from 'messageformat/compile-module'
 *
 * const mf = new MessageFormat('en')
 * const msgSet = {
 *   a: 'A {TYPE} example.',
 *   b: 'This has {COUNT, plural, one{one member} other{# members}}.',
 *   c: 'We have {P, number, percent} code coverage.'
 * }
 * const msgModule = compileModule(mf, msgSet)
 * fs.writeFileSync('messages.js', msgModule)
 *
 * ...
 *
 * import messages from './messages'
 *
 * messages.a({ TYPE: 'more complex' })  // 'A more complex example.'
 * messages.b({ COUNT: 3 })              // 'This has 3 members.'
 */
export default function compileModule(messageformat, messages) {
  const { plurals } = messageformat;
  const cp = {};
  if (plurals.length > 1)
    for (const pl of plurals) cp[pl.lc] = cp[pl.locale] = pl;
  const compiler = new Compiler(messageformat.options);
  const msgObj = compiler.compile(messages, plurals[0], cp);
  const msgStr = stringifyObject(msgObj);
  const rtStr = stringifyRuntime(compiler.runtime);
  return `${rtStr}\nexport default ${msgStr}`;
}