All files index.js

100% Statements 46/46
100% Branches 10/10
100% Functions 5/5
100% Lines 43/43
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125          1x   1x 1x 1x 1x                                                 1x 22x 22x     1x 11x 11x 10x 10x     1x     1x 9x 9x     1x 7x 7x 7x 7x 7x   7x       1x     18x   18x   9x   9x         9x   9x         9x   9x   3x   3x   3x   3x             9x   9x   3x   3x   3x   3x                    
import { dirname, join, relative, resolve } from "path";
import debugConstructor from "debug";
import { execFileSync, spawn } from "child_process";
import { StringLiteral } from "babel-types";
 
const debug = debugConstructor("babel-plugin-bucklescript");
const watching =
  process.argv.indexOf("-w") + process.argv.indexOf("--watch") >= -1;
const globalPath = process.cwd();
const compileDir = "lib";
const fileRegex = /\.(re|ml)$/;
 
let bsb;
 
/* istanbul ignore next */
try {
  bsb = require.resolve("bs-platform/bin/bsb.exe");
} catch (_) {
  bsb = "bsb";
}
 
/* istanbul ignore next */
debug(`Spawning bsb ${watching ? "watch " : "make world"}process`);
 
/* istanbul ignore next */
const watcher = spawn(bsb, [watching ? "-w" : "-make-world"]);
 
/* istanbul ignore next */
process.on("exit", () => {
  if (watching) {
    debug("Terminating bsb process");
    watcher.kill();
  }
});
 
export const isBuckleScriptFile = path => {
  debug(`Checking if the file is a bs file: " ${path}"`);
  return fileRegex.test(path);
};
 
export const traverseExpression = (t, arg) => {
  debug("Traversing the AST expression");
  if (t.isStringLiteral(arg)) {
    debug("AST expression is a string");
    return arg;
  }
 
  return null;
};
 
export const getModuleType = state => {
  debug("Checking configured module type");
  return state.opts.module || "js";
};
 
export const getNewPath = (path, state) => {
  debug("Generating new path");
  const sourcePath = dirname(state.file.opts.filenameRelative);
  const requirePath = resolve(sourcePath, path);
  const rootPath = requirePath.replace(globalPath, "");
  const newPath = join(globalPath, compileDir, getModuleType(state), rootPath);
 
  return newPath.replace(fileRegex, ".js");
};
 
export default babel => {
  return {
    visitor: {
      CallExpression(path, state) {
        debug("Evaluating call expression");
 
        if (path.node.callee.name !== "require") return;
 
        debug("Call expression is a require call");
 
        const args = path.node.arguments;
 
        /* istanbul ignore next */
        if (!args.length) return;
 
        debug("Call expression has at least one argument");
 
        const firstArg = traverseExpression(babel.types, args[0]);
 
        /* istanbul ignore next */
        if (!firstArg) return;
 
        debug("Call expression has a valid first argument");
 
        if (!isBuckleScriptFile(firstArg.value)) return;
 
        debug("Path is a bucklescript file");
 
        const newPath = getNewPath(firstArg.value, state);
 
        debug(`Setting new path: "${newPath}"`);
 
        path.replaceWith(
          babel.types.callExpression(path.node.callee, [
            babel.types.stringLiteral(newPath)
          ])
        );
      },
      ImportDeclaration(path, state) {
        debug("Evaluating import declaration");
 
        if (!isBuckleScriptFile(path.node.source.value)) return;
 
        debug("Path is a bucklescript file");
 
        const newPath = getNewPath(path.node.source.value, state);
 
        debug(`Setting new path: "${newPath}"`);
 
        path.replaceWith(
          babel.types.importDeclaration(
            path.node.specifiers,
            babel.types.stringLiteral(newPath)
          )
        );
      }
    }
  };
};