# Copyright Google LLC. All Rights Reserved.
#
# Use of this source code is governed by an MIT-style license that can be
# found in the LICENSE file at https://angular.dev/license
load("@bazel_lib//lib:utils.bzl", "to_label")
load("@jq.bzl//jq:jq.bzl", "jq")

def link_package_json_to_tarballs(name, src, pkg_deps, out):
    """Substitute tar paths into a package.json file for the packages it depends on.

    src and pkg_deps must be labels in the bazel-out tree for the derived path to the npm_package_archive.tgz to be correct.

    Args:
        name: Name of the rule
        src: package.json file to perform substitions on
        pkg_deps: package.json files of dependencies to substitute
        out: Output package.json file
    """

    src_pkg = to_label(src).package

    # Generate partial jq filters for each dependent package that, when run
    # against a package.json file, can replace its dependency with a tar path.
    filter_files = []
    for i, pkg_dep in enumerate(pkg_deps):
        pkg_dep_name = "%s_%s.name" % (name, i)
        pkg_dep_filter = "%s_%s.filter" % (name, i)
        jq(
            name = "%s_%s_name" % (name, i),
            srcs = [pkg_dep],
            filter = ".name",
            out = pkg_dep_name,
        )

        srcs = [
            pkg_dep_name,
            pkg_dep,
        ]

        # Add dependent tars as srcs to include them in the dependency graph, except
        # for the tar for this package as that would create a circular dependency.
        pkg_label = to_label(pkg_dep)
        if pkg_label.package != src_pkg:
            pkg_tar = "@@%s//%s:npm_package_archive.tgz" % (pkg_label.repo_name, pkg_label.package)
            srcs.append(pkg_tar)

        # Deriving the absolute path to the tar in the execroot requries different
        # commands depending on whether or not the action is sandboxed.
        abs_path_sandbox = "readlink $(execpath {pkg_dep})".format(pkg_dep = pkg_dep)
        abs_path_nosandbox = "(cd $$(dirname $(execpath {pkg_dep})) && pwd)".format(pkg_dep = pkg_dep)

        native.genrule(
            name = "%s_%s_filter" % (name, i),
            srcs = srcs,
            cmd = """
                TAR=$$(dirname $$({abs_path_sandbox} || {abs_path_nosandbox}))/npm_package_archive.tgz
                PKGNAME=$$(cat $(execpath {pkg_name}))
                if [[ "$$TAR" != *bazel-out* ]]; then
                    echo "ERROR: package.json passed to substitute_tar_deps must be in the output tree. You can use copy_to_bin to copy a source file to the output tree."
                    exit 1
                fi
                echo "(..|objects|select(has($${{PKGNAME}})))[$${{PKGNAME}}] |= \\"file:$${{TAR}}\\"" > $@
            """.format(
                pkg_name = pkg_dep_name,
                abs_path_sandbox = abs_path_sandbox,
                abs_path_nosandbox = abs_path_nosandbox,
            ),
            outs = [pkg_dep_filter],
        )
        filter_files.append(pkg_dep_filter)

    # Combine all of the filter files into a single filter by joining with |
    filter = "%s.filter" % name
    native.genrule(
        name = "%s_filter" % name,
        srcs = filter_files,
        cmd = "cat $(SRCS) | sed '$$!s#$$# |#' > $@",
        outs = [filter],
    )

    # Generate final package.json with tar substitutions using the above filter
    jq(
        name = name,
        srcs = [src],
        filter_file = filter,
        out = out,
    )
