blob: 9f14b95f1816913a23671b3e949870bb8619c949 [file]
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Definitions for handling path re-mapping, to support short module names.
# See pathMapping doc: https://github.com/Microsoft/TypeScript/issues/5039
#
# This reads the module_root and module_name attributes from typescript rules in
# the transitive closure, rolling these up to provide a mapping to the
# TypeScript compiler and to editors.
#
"""Helper function and aspect to get module mappings from deps
"""
def _get_deps(attrs, names):
return [
d
for n in names
if hasattr(attrs, n)
for d in getattr(attrs, n)
]
# Traverse 'srcs' in addition so that we can go across a genrule
_MODULE_MAPPINGS_DEPS_NAMES = (
["deps", "srcs"]
)
def _debug(vars, *args):
if "VERBOSE_LOGS" in vars.keys():
print("[module_mappings.bzl]", *args)
def _get_module_mappings(target, ctx):
"""Returns the module_mappings from the given attrs.
Collects a {module_name - module_root} hash from all transitive dependencies,
checking for collisions. If a module has a non-empty `module_root` attribute,
all sources underneath it are treated as if they were rooted at a folder
`module_name`.
Args:
target: target
ctx: ctx
Returns:
The module mappings
"""
mappings = dict()
mappings_attr = "runfiles_module_mappings"
workspace_name = target.label.workspace_name if target.label.workspace_name else ctx.workspace_name
all_deps = _get_deps(ctx.rule.attr, names = _MODULE_MAPPINGS_DEPS_NAMES)
for dep in all_deps:
if not hasattr(dep, mappings_attr):
continue
for k, v in getattr(dep, mappings_attr).items():
if k in mappings and mappings[k] != v:
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
(target.label, k, mappings[k], v)), "deps")
mappings[k] = v
if hasattr(ctx.rule.attr, "module_name") and ctx.rule.attr.module_name:
mn = ctx.rule.attr.module_name
# When building a mapping for use at runtime, we need paths to be relative to
# the runfiles directory. This requires the workspace_name to be prefixed on
# each module root.
mr = "/".join([p for p in [workspace_name, target.label.package] if p])
if hasattr(ctx.rule.attr, "strip_prefix") and ctx.rule.attr.strip_prefix:
mr += "/" + ctx.rule.attr.strip_prefix
if hasattr(ctx.rule.attr, "module_root") and ctx.rule.attr.module_root and ctx.rule.attr.module_root != ".":
if ctx.rule.attr.module_root.endswith(".ts"):
# Validate that sources are underneath the module root.
# module_roots ending in .ts are a special case, they are used to
# restrict what's exported from a build rule, e.g. only exports from a
# specific index.d.ts file. For those, not every source must be under the
# given module root.
#
# .d.ts module_root means we should be able to load in two ways:
# module_name -> module_path/module_root.js
# module_name/foo -> module_path/foo
# So we add two mappings. The one with the trailing slash is longer,
# so the loader should prefer it for any deep imports. The mapping
# without the trailing slash will be used only when importing from the
# bare module_name.
mappings[mn + "/"] = mr + "/"
mr = "%s/%s" % (mr, ctx.rule.attr.module_root.replace(".d.ts", ".js"))
else:
mr = "%s/%s" % (mr, ctx.rule.attr.module_root)
if mn in mappings and mappings[mn] != mr:
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
(target.label, mn, mappings[mn], mr)), "deps")
mappings[mn] = mr
_debug(ctx.var, "Mappings at %s: %s" % (target.label, mappings))
return mappings
def _module_mappings_runtime_aspect_impl(target, ctx):
mappings = _get_module_mappings(target, ctx)
return struct(runfiles_module_mappings = mappings)
module_mappings_runtime_aspect = aspect(
_module_mappings_runtime_aspect_impl,
attr_aspects = _MODULE_MAPPINGS_DEPS_NAMES,
)