#!/usr/bin/env node

/*
 * This script is used to compile a bundled compiler file into a standalone JS bundle.
 * The compiled output (including the compiled stdlib files), will be put in the defined
 * `PLAYGROUND` directory.
 *
 * Example:
 *
 * ```
 * mkdir playground && mkdir playground/stdlib
 * PLAYGROUND=../playground node scripts/repl.js
 * ```
 *
 * You may also pass an alternative JSOO entrypoint (we have multiple defined
 * in `jscomp/snapshot.ninja`) as a first parameter:
 *
 * ```
 * # Builds the "simpler" JS bundle
 * mkdir playground && mkdir playground/stdlib
 * PLAYGROUND=../playground node scripts/repl.js js_compiler
 * ```
 */

//@ts-check
var child_process = require("child_process");
var fs = require("fs");
var path = require("path");

var ocamlVersion = "4.06.1";
var jscompDir = path.join(__dirname, "..", "jscomp");
var sourceDir = path.join(__dirname, "..", "lib", ocamlVersion, "unstable");

var config = {
  cwd: jscompDir,
  encoding: "utf8",
  stdio: [0, 1, 2],
  shell: true,
};
/**
 *
 * @param {string} cmd
 */
function e(cmd) {
  console.log(`>>>>>> running command: ${cmd}`);
  child_process.execSync(cmd, config);
  console.log(`<<<<<<`);
}

if (!process.env.PLAYGROUND) {
  var defaultPlayground = path.join(__dirname, "..", "playground");
  console.warn(`PLAYGROUND env var unset, defaulting to ${defaultPlayground}`);
  process.env.PLAYGROUND = defaultPlayground;
}

const BUILD_THIRD_PARTY = process.env.BUILD_THIRD_PARTY || false;
var playground = process.env.PLAYGROUND;
var OCAMLC = `ocamlc.opt`;

// This mini project is needed to build cmij files for third party deps like @rescript/react
const PLAYGROUND_BUNDLING = path.join(
  __dirname,
  "..",
  "packages",
  "playground-bundling"
);

var JSOO = `js_of_ocaml`;

function prepare(isDev, targetCompilerFile) {
  var [env, ocamlFlag, jsooFlag] = isDev
    ? ["development", "-g ", "--pretty "]
    : ["production", "", ""];
  console.log(
    `building byte code version of target compiler file '${targetCompilerFile}' [${env}]`
  );

  if (!fs.existsSync(playground)) {
    console.log(`Creating output folder "${playground}"`);

    // Create both, the `playground` and `playground/stdlib` dir
    const stdlibDir = path.join(playground, "stdlib");
    fs.mkdirSync(stdlibDir, { recursive: true });
  }

  const mliFile = path.join(sourceDir, targetCompilerFile + ".mli");
  const mlFile = path.join(sourceDir, targetCompilerFile + ".ml");

  // There may be a situation where the .mli file doesn't exist (mostly when
  // the snapshot hasn't been checked into `lib/4.06.1/unstable`
  e(`touch ${mliFile}`);
  e(
    `${OCAMLC} ${ocamlFlag}-w -30-40 -no-check-prims -I ${sourceDir} ${mliFile} ${mlFile} -o jsc.byte `
  );
  console.log(
    `building js version for compiler target '${targetCompilerFile}'`
  );
  e(`${JSOO} compile jsc.byte ${jsooFlag}-o compiler.js`);
  console.log("copy js artifacts");
  e(`cp ../lib/js/*.js ${playground}/stdlib`);
  e(`mv ./compiler.js ${playground}`);

  // BUILDING THIRD PARTY CMIJ FILES
  if (BUILD_THIRD_PARTY) {
    console.log("Building third party packages...");
    e(`cd ${PLAYGROUND_BUNDLING} && npm run build`);
  }
}

function prepublish() {
  var mainPackageJson = JSON.parse(
    fs.readFileSync(path.join(__dirname, "..", "package.json"))
  );
  var packageJson = JSON.stringify(
    {
      name: "rescript-compiler",
      version: mainPackageJson.version,
      license: mainPackageJson.license,
      description: mainPackageJson.description,
      repository: mainPackageJson.repository,
      author: mainPackageJson.author,
      maintainers: mainPackageJson.maintainers,
      bugs: mainPackageJson.bugs,
      homepage: mainPackageJson.homepage,
      main: "compiler.js",
    },
    null,
    2
  );

  fs.writeFileSync(jscompDir + `/${playground}/package.json`, packageJson, {
    encoding: "utf8",
  });
}

// Relevant target compiler files can be found in jscomp/snapshot.ninja
let targetCompilerFile = "js_playground_compiler";

// Let's derive the target file to compile from the last argument list.
if (process.argv.length > 2) {
  const lastArg = process.argv[process.argv.length - 1];

  if (!lastArg.startsWith("-")) {
    targetCompilerFile = lastArg;
  }
}

prepare(process.argv.includes("-development"), targetCompilerFile);
if (process.argv.includes("-prepublish")) {
  prepublish();
}