build_defs/npm/npm.build_defs
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", "")