"""Bun frontend build helpers for Wails.""" load("//wails:private/common.bzl", "bash_launcher", "write_manifest") def _node_modules_root_from_path(path): marker = "/node_modules/" marker_index = path.find(marker) if marker_index < 0: return None return path[:marker_index + len("/node_modules")] def _dirname(path): index = path.rfind("/") if index < 0: return "." return path[:index] or "." def _wails_bun_frontend_dist_impl(ctx): out_dir = ctx.actions.declare_directory(ctx.label.name) manifest_srcs = list(ctx.files.srcs) if ctx.file.package_json.path not in [src.path for src in manifest_srcs]: manifest_srcs.append(ctx.file.package_json) manifest = write_manifest( ctx, ctx.label.name + ".manifest", manifest_srcs, ctx.attr.strip_prefix, ) if not ctx.files.node_modules: fail("wails_bun_frontend_dist requires a non-empty node_modules tree") workspace_marker = "" if ctx.attr.workspace_dir: workspace_marker = "/%s/node_modules/" % ctx.attr.workspace_dir.strip("/") shortest_path = None shared_node_modules_root = None workspace_node_modules_root = None for src in ctx.files.node_modules: if shortest_path == None or len(src.path) < len(shortest_path): shortest_path = src.path if workspace_marker and workspace_marker in src.path and workspace_node_modules_root == None: workspace_node_modules_root = _node_modules_root_from_path(src.path) if shortest_path: shared_node_modules_root = _node_modules_root_from_path(shortest_path) node_modules_root = workspace_node_modules_root or shared_node_modules_root if node_modules_root == None or shared_node_modules_root == None: fail("unable to determine node_modules roots from node_modules inputs") package_json_rel = ctx.file.package_json.short_path if ctx.attr.strip_prefix and package_json_rel.startswith(ctx.attr.strip_prefix): package_json_rel = package_json_rel[len(ctx.attr.strip_prefix):] package_dir = _dirname(package_json_rel) args = ctx.actions.args() args.add("--build-script", ctx.attr.build_script) args.add("--manifest", manifest.path) args.add("--node-modules-root", node_modules_root) args.add("--out", out_dir.path) args.add("--package-dir", package_dir) args.add("--shared-node-modules-root", shared_node_modules_root) args.add("--bun-darwin-aarch64", ctx.file._bun_darwin_aarch64.path) args.add("--bun-darwin-x64", ctx.file._bun_darwin_x64.path) args.add("--bun-linux-aarch64", ctx.file._bun_linux_aarch64.path) args.add("--bun-linux-x64", ctx.file._bun_linux_x64.path) args.add("--bun-windows-x64", ctx.file._bun_windows_x64.path) if ctx.attr.workspace_dir: args.add("--workspace-dir", ctx.attr.workspace_dir) ctx.actions.run( executable = ctx.executable._tool, arguments = [args], inputs = depset( manifest_srcs + ctx.files.node_modules + [ manifest, ctx.file._bun_darwin_aarch64, ctx.file._bun_darwin_x64, ctx.file._bun_linux_aarch64, ctx.file._bun_linux_x64, ctx.file._bun_windows_x64, ], ), outputs = [out_dir], tools = [ctx.attr._tool[DefaultInfo].files_to_run], mnemonic = "WailsBunFrontendDist", progress_message = "Building Bun frontend dist for %s" % ctx.label, ) return [DefaultInfo(files = depset([out_dir]))] def _wails_bun_frontend_dev_impl(ctx): launcher = ctx.actions.declare_file(ctx.label.name) package_dir = _dirname(ctx.file.package_json.short_path) resolve_lines = """ tool="$(resolve_runfile "{tool_short_path}")" bun_darwin_aarch64="$(resolve_runfile "{bun_darwin_aarch64_short_path}")" bun_darwin_x64="$(resolve_runfile "{bun_darwin_x64_short_path}")" bun_linux_aarch64="$(resolve_runfile "{bun_linux_aarch64_short_path}")" bun_linux_x64="$(resolve_runfile "{bun_linux_x64_short_path}")" bun_windows_x64="$(resolve_runfile "{bun_windows_x64_short_path}")" """.format( tool_short_path = ctx.executable._tool.short_path, bun_darwin_aarch64_short_path = ctx.file._bun_darwin_aarch64.short_path, bun_darwin_x64_short_path = ctx.file._bun_darwin_x64.short_path, bun_linux_aarch64_short_path = ctx.file._bun_linux_aarch64.short_path, bun_linux_x64_short_path = ctx.file._bun_linux_x64.short_path, bun_windows_x64_short_path = ctx.file._bun_windows_x64.short_path, ) command_lines = """ exec "$tool" \ --bun-darwin-aarch64 "$bun_darwin_aarch64" \ --bun-darwin-x64 "$bun_darwin_x64" \ --bun-linux-aarch64 "$bun_linux_aarch64" \ --bun-linux-x64 "$bun_linux_x64" \ --bun-windows-x64 "$bun_windows_x64" \ --package-dir {package_dir} \ --script {script} """.format( package_dir = _shell_quote(ctx.attr.workspace_dir or package_dir), script = _shell_quote(ctx.attr.script), ) ctx.actions.write( output = launcher, is_executable = True, content = bash_launcher(resolve_lines, command_lines), ) runfiles = ctx.runfiles( files = [ ctx.executable._tool, ctx.file._bun_darwin_aarch64, ctx.file._bun_darwin_x64, ctx.file._bun_linux_aarch64, ctx.file._bun_linux_x64, ctx.file._bun_windows_x64, ] + ([ctx.file.package_json] if ctx.file.package_json else []), transitive_files = depset(transitive = [ ctx.attr._tool[DefaultInfo].default_runfiles.files, ]), ) return [DefaultInfo(executable = launcher, runfiles = runfiles)] def _shell_quote(value): return "'" + value.replace("'", "'\"'\"'") + "'" wails_bun_frontend_dist = rule( implementation = _wails_bun_frontend_dist_impl, attrs = { "build_script": attr.string(default = "build"), "node_modules": attr.label( mandatory = True, allow_files = True, ), "package_json": attr.label( mandatory = True, allow_single_file = True, ), "srcs": attr.label_list( mandatory = True, allow_files = True, ), "strip_prefix": attr.string(default = ""), "workspace_dir": attr.string(default = ""), "_tool": attr.label( default = "//wails_bun/tools:frontend_dist_action", cfg = "exec", executable = True, ), "_bun_darwin_aarch64": attr.label( default = "@bun_darwin_aarch64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_darwin_x64": attr.label( default = "@bun_darwin_x64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_linux_aarch64": attr.label( default = "@bun_linux_aarch64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_linux_x64": attr.label( default = "@bun_linux_x64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_windows_x64": attr.label( default = "@bun_windows_x64//:bun", cfg = "exec", allow_single_file = True, ), }, ) _wails_bun_frontend_dev = rule( implementation = _wails_bun_frontend_dev_impl, attrs = { "data": attr.label_list(allow_files = True), "node_modules": attr.label( mandatory = True, allow_files = True, ), "package_json": attr.label( mandatory = True, allow_single_file = True, ), "script": attr.string(default = "dev"), "workspace_dir": attr.string(default = ""), "_tool": attr.label( default = "//wails_bun/tools:frontend_dev_server", cfg = "exec", executable = True, ), "_bun_darwin_aarch64": attr.label( default = "@bun_darwin_aarch64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_darwin_x64": attr.label( default = "@bun_darwin_x64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_linux_aarch64": attr.label( default = "@bun_linux_aarch64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_linux_x64": attr.label( default = "@bun_linux_x64//:bun", cfg = "exec", allow_single_file = True, ), "_bun_windows_x64": attr.label( default = "@bun_windows_x64//:bun", cfg = "exec", allow_single_file = True, ), }, executable = True, ) def wails_bun_frontend_dev( name, data, node_modules, package_json, script = "dev", workspace_dir = "", tags = None, visibility = None): _wails_bun_frontend_dev( name = name, data = data, node_modules = node_modules, package_json = package_json, script = script, tags = tags, visibility = visibility, workspace_dir = workspace_dir, )