binhonglee/GlobeTrotte

View on GitHub
build_defs/npm/npm.build_defs

Summary

Maintainability
Test Coverage
subinclude("//build_defs/gen")
subinclude("//build_defs/sh")

def npm_run_build(
  name:str,
  cmd:str,
  srcs:list,
  outs:list,
  make_copy:bool=False,
  copy_path:str="",
  exported_deps:list=[],
  deps:list=[],
  test_only:bool=False,
  # pnpm doesn't like it when you move / link node_modules around
  at_source:bool=False,
  visibility:list=[],
):
  this_cmd = " && ".join([
    _home_path(),
    _set_node_modules(),
    "$TOOL run " + cmd
  ])

  if at_source:
    this_cmd = " && ".join([
      _home_path(),
      _go_to_top_level(),
      "cd $PKG_DIR",
      "$TOOL run " + cmd,
      _for_loop("$OUTS", "cp -r $ITEM $TMP_DIR"),
    ])
  elif make_copy:
    this_cmd += " && cp $OUTS " + _top_level_dir() + copy_path

  return build_rule(
    name = name,
    srcs = srcs,
    outs = outs,
    cmd = this_cmd,
    exported_deps = exported_deps,
    deps = deps,
    test_only = test_only,
    visibility = visibility,
    tools=[_get_npm_tool()],
    requires = ["npm-run"],
  )

def ts_run_build(
  name:str,
  script:str,
  srcs:list=[],
  outs:list=[],
  make_copy:bool=False,
  copy_path:str="",
  deps:list=[],
  test_only:bool=False,
  visibility:list=[],
  # pnpm complains about  ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  without first running install
  run_install:bool=False,
  root:str=".",
):
  cmd = " && ".join([
    _set_node_modules(),
    _home_path(),
    "cd " + root,
  ])

  if run_install:
    cmd += " && $TOOLS_NPM i"

  cmd = " && ".join([
    cmd,
    _gen_run_typescript(script),
    _for_loop("$OUTS", "cp -r $ITEM $TMP_DIR"),
  ])

  if make_copy:
      cmd += " && " +  _for_loop("$OUTS", "cp -r $ITEM " + _top_level_dir() + copy_path)
  
  return build_rule(
    name = name,
    srcs = srcs +  [script],
    tools = {
      "npx": _get_npx_tool(),
      "npm": _get_npm_tool(),
      "node": _get_node_tool(),
    },
    cmd = cmd,
    outs = outs,
    deps = deps,
    test_only = test_only,
    visibility = visibility,
    requires = ["npm-run"],
  )

def npm_install(name:str, srcs:list=[], deps:list=[]):
  out = "node_modules"

  cmd = " ".join([
    "$TOOL",
    "install",
    "--no-color",
    "--reporter append-only",
    "--prefer-offline",
  ])

  cmd = " && ".join([
    _home_path(),
    _go_to_top_level(),
    cmd,
    "ln -s " + out + " $TMP_DIR/node_modules",
  ])

  return build_rule(
    name = name,
    cmd = cmd,
    srcs = srcs,
    deps = deps,
    outs = [out],
    visibility = ["PUBLIC"],
    tools=[_get_npm_tool()],
    requires = ["npm-run"],
  )

def npm_run(
  name:str,
  cmd:str,
  srcs:list=[],
  deps:list=[],
  visibility:list=None,
  test_only:bool=False,
):
  return sh_tools_cmd(
    name = name,
    srcs = srcs,
    cmd = "cd $PKG_DIR && $TOOL run " + cmd,
    deps = deps,
    visibility = visibility,
    test_only = test_only,
    tools = [_get_npm_tool()],
    requires = ["npm-run"],
  )

def ts_binary(
  name:str,
  script:str,
  srcs:list=[],
  deps:list=[],
  post_run:list=[],
  visibility:list=None,
  test_only:bool=False,
):
  return sh_tools_cmd(
    name = name,
    srcs = srcs,
    cmd = " && ".join([_gen_run_typescript(script)] + post_run),
    tools = {
      "npx": _get_npx_tool(),
      "node": _get_node_tool(),
    },
    deps = deps,
    visibility = visibility,
    test_only = test_only,
    requires = ["npm-run"],
  )

def npm_test(
  name:str,
  cmd:str,
  srcs:list=[],
  outs:list=[],
  deps:list=[],
  raw:bool=False,
  set_home:bool=False,
  result_dir:str=None,
  result_file:str=None,
  visibility:list=None,
  requires_server:int=None,
  server_start_cmd:str=None,
  needs_transitive_deps:bool=False,
):
  if raw:
    cmd = f"$TOOLS_NPX {cmd}"
  else:
    cmd = f"$TOOLS_NPM run {cmd}"

  if result_dir is not None and result_file is not None:
    cmd = "echo \"Please only set either result_dir or result_file and not both.\" && exit 1"

  if set_home:
    cmd = " && ".join([
      _home_path(),
      cmd,
    ])

  if requires_server is not None:
    cmd = " && ".join([
      _check_port_running(
        requires_server,
        "$TOOLS_NPM run " + server_start_cmd if server_start_cmd is not None else None,
      ),
      cmd,
    ])

  if result_dir is not None:
    cmd = " && ".join([
      "rm -rf " + result_dir,
      "mkdir -p " + result_dir,
      cmd,
      "cat " + result_dir + "/* > \\\$TMP_DIR/test.results",
    ])
    
  if result_file is not None:
    cmd = " && ".join([
      "rm " + result_file,
      cmd,
      "cat " + result_file + " > \\\$TMP_DIR/test.results",
    ])

  cmd = " && ".join([
    _go_to_top_level(),
    "cd $PKG_DIR",
    cmd,
  ])

  return build_rule(
    name = name,
    srcs = srcs,
    cmd = _scriptify("\n".join(cmd.split(" && "))),
    outs = [name + ".sh"],
    deps = deps,
    test = True,
    test_cmd = f"chmod +x {name}.sh && ./{name}.sh",
    test_only = True,
    tools = {
      "npm": _get_npm_tool(),
      "npx": _get_npx_tool(),
    },
    visibility = visibility,
    no_test_output = (result_dir == None and result_file == None),
    needs_transitive_deps = needs_transitive_deps,
    requires = ["npm-test"],
  )

def npm_lint(
  name:str,
  cmd:str,
  srcs:list=[],
  deps:list=[],
  raw:bool=False,
  visibility:list=None,
  needs_transitive_deps:bool=False,
):
  if raw:
    cmd = f"$TOOLS_NPX {cmd}"
  else:
    cmd = f"$TOOLS_NPM run {cmd}"
  return sh_tools_cmd(
    name = name,
    srcs = srcs,
    cmd = cmd,
    tools = {
      "npm": _get_npm_tool(),
      "npx": _get_npx_tool(),
    },
    deps = deps,
    visibility = visibility,
    requires = ["npm-lint"],
  )

def pnpm(
  name:str,
  version:str,
):
  cmd = " && ".join([
    _get_fnm_dir(),
    "VERSION='" + version + "'",
    "curl -f https://get.pnpm.io/v6.14.js | $TOOLS_NODE - add --global pnpm",
    "ln -s $FNM_DIR/node-versions/$VERSION/installation/pnpm-global/5/node_modules/pnpm/bin $TMP_DIR",
  ])

  node_version(
    name = name,
    version = version,
    node_only = True,
  )

  pnpm_bin = build_rule(
    name = name,
    cmd = cmd,
    outs = ["bin"],
    tools = {
      "NODE": _get_node_tool(),
    },
    binary = True,
    visibility = ["PUBLIC"],
  )

  build_rule(
    name = f"_{name}#npm",
    srcs = [pnpm_bin],
    cmd = " && ".join([
      _get_fnm_dir(),
      "VERSION='" + version + "'",
      "ln -s $FNM_DIR/node-versions/$VERSION/installation/pnpm-global/5/node_modules/pnpm/bin/pnpm.cjs $TMP_DIR",
      "cd $TMP_DIR",
      "mv pnpm.cjs pnpm",
    ]),
    outs = ["pnpm"],
    visibility = ["PUBLIC"],
  )

  build_rule(
    name = f"_{name}#npx",
    srcs = [pnpm_bin],
    cmd = " && ".join([
      _get_fnm_dir(),
      "VERSION='" + version + "'",
      "ln -s $FNM_DIR/node-versions/$VERSION/installation/pnpm-global/5/node_modules/pnpm/bin/pnpx.cjs $TMP_DIR",
      "cd $TMP_DIR",
      "mv pnpx.cjs pnpx",
    ]),
    outs = ["pnpx"],
    visibility = ["PUBLIC"],
  )

  return pnpm_bin

def node_version(
  name:str,
  version:str,
  node_only:bool=False,
):
  CONFIG["NODE_VERSION"] = version
  cmd = " && ".join([
    "VERSION='" + CONFIG.NODE_VERSION + "'",
    _get_fnm_dir(),
    "$TOOL install $VERSION --fnm-dir $FNM_DIR",
    "NODE_BIN_DIR=$FNM_DIR/node-versions/$VERSION/installation/bin",
    "ln -s $NODE_BIN_DIR/node $TMP_DIR",
  ])

  fnm = fnm(
    name = f"_{name}#fnm",
  )

  node = build_rule(
    name = f"_{name}#node",
    cmd = cmd,
    outs = ["node"],
    binary = True,
    tools = [fnm],
    visibility = ["PUBLIC"],
  )

  srcs = [node]

  if not node_only:
    srcs += build_rule(
      name = f"_{name}#npm",
      srcs = [node],
      cmd = " && ".join([
        "VERSION='" + CONFIG.NODE_VERSION + "'",
        _get_fnm_dir(),
        "$TOOL install $VERSION --fnm-dir $FNM_DIR",
        "NODE_BIN_DIR=$FNM_DIR/node-versions/$VERSION/installation/bin",
        "cp $NODE_BIN_DIR/npm $TMP_DIR",
      ]),
      outs = ["npm"],
      visibility = ["PUBLIC"],
    )

    srcs += build_rule(
      name = f"_{name}#npx",
      srcs = [node],
      cmd = " && ".join([
        "VERSION='" + CONFIG.NODE_VERSION + "'",
        _get_fnm_dir(),
        "$TOOL install $VERSION --fnm-dir $FNM_DIR",
        "NODE_BIN_DIR=$FNM_DIR/node-versions/$VERSION/installation/bin",
        "cp $NODE_BIN_DIR/npx $TMP_DIR",
      ]),
      outs = ["npx"],
      visibility = ["PUBLIC"],
    )

  if node_only:
    return node
  else:
    return filegroup(
      name = name,
      srcs = srcs,
    )

def fnm(
  name:str
):
  return build_rule(
    name = name,
    cmd = " && ".join([
      _get_fnm_dir(),
      "curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir $FNM_DIR --skip-shell",
      "cp $FNM_DIR/fnm $OUT",
    ]),
    outs = ["fnm"],
    binary = True,
    visibility = ["PUBLIC"],
  )

def _gen_run_typescript(script_name:str):
  ts_runner = ["ts-eager", "ts-node"]
  if CONFIG.TYPESCRIPT_TOOL in ts_runner:
    return " ".join([
      "$TOOLS_NPX",
      CONFIG.TYPESCRIPT_TOOL,
      "$TMP_DIR/$PKG_DIR/" + script_name,
    ])

  out_name = script_name[:-3] + ".js"

  if CONFIG.TYPESCRIPT_TOOL == "esbuild":
    return " ".join([
      "$TOOLS_NPX",
      CONFIG.TYPESCRIPT_TOOL,
      "--bundle",
      "$TMP_DIR/$PKG_DIR/" + script_name,
      "--platform=node",
      "--outdir=" + CONFIG.TYPESCRIPT_OUT_DIR,
      "&&",
      "$TOOLS_NODE",
      CONFIG.TYPESCRIPT_OUT_DIR + "/" + out_name,
      "\\\$*",
    ])

  if CONFIG.TYPESCRIPT_TOOL == "tsc":
    return " ".join([
      "$TOOLS_NPX",
      CONFIG.TYPESCRIPT_TOOL,
      "$TMP_DIR/$PKG_DIR/" + script_name,
      "--outDir",
      CONFIG.TYPESCRIPT_OUT_DIR,
      "&&",
      "$TOOLS_NODE",
      CONFIG.TYPESCRIPT_OUT_DIR + "/" + out_name,
      "\\\$*",
    ])
  
  fail()

def _set_node_modules():
  return " &&  ".join([
    "rm -rf node_modules || echo \"Existing 'node_modules' not found.\"",
    "cp -r " + _top_level_dir() + "node_modules ./",
  ])

def _get_fnm_dir():
  return " && ".join([
    _home_path(),
    "FNM_DIR=" + _top_level_dir() + "plz-out/.fnm",
  ])

def _get_node_tool():
  if CONFIG.NPM_RULE != "":
    return ":_".join(CONFIG.NPM_RULE.split(":")) + "#node"
  else:
    return CONFIG.NODE_TOOL

def _get_npm_tool():
  if CONFIG.NPM_RULE != "":
    return ":_".join(CONFIG.NPM_RULE.split(":")) + "#npm"
  else:
    return CONFIG.NPM_TOOL

def _get_npx_tool():
  if CONFIG.NPM_RULE != "":
    return ":_".join(CONFIG.NPM_RULE.split(":")) + "#npx"
  else:
    return CONFIG.NPX_TOOL

# def _get_bin_dir():
#   return "export PATH=" + _top_level_dir() + "plz-out/bin:$PATH"

CONFIG.setdefault("NPM_TOOL", "npm")
CONFIG.setdefault("NPX_TOOL", "npx")
CONFIG.setdefault("NODE_TOOL", "node")
CONFIG.setdefault("NPM_RULE", "")
CONFIG.setdefault("NODE_VERSION", "node")
CONFIG.setdefault("TYPESCRIPT_TOOL", "tsc")
CONFIG.setdefault("TYPESCRIPT_OUT_DIR", "./plz-out/tsc")

CONFIG.setdefault("CORE_FILES", "")