Skip to content

Commit

Permalink
feat: add package_name to ts_library
Browse files Browse the repository at this point in the history
  • Loading branch information
gregmagolan committed Jun 30, 2021
1 parent 16a099e commit 98bca9a
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 108 deletions.
12 changes: 8 additions & 4 deletions docs/Built-ins.md
Expand Up @@ -1193,15 +1193,19 @@ Defaults to `@build_bazel_rules_nodejs//internal:node_context_data`

<h4 id="pkg_npm-package_name">package_name</h4>

(*String*): Optional package_name that this npm package may be imported as.
(*String*): The package name that the linker will link this npm package as.

If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.

Defaults to `""`

<h4 id="pkg_npm-package_path">package_path</h4>

(*String*): The directory in the workspace to link to.
If set, link this pkg_npm to the node_modules under the package path specified.
If unset, the default is to link to the node_modules root of the workspace.
(*String*): The package path in the workspace that the linker will link this npm package to.

If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.

Defaults to `""`

Expand Down
5 changes: 1 addition & 4 deletions docs/Providers.md
Expand Up @@ -168,7 +168,7 @@ Historical note: this was the typescript.es5_sources output.
**USAGE**

<pre>
LinkablePackageInfo(<a href="#LinkablePackageInfo-files">files</a>, <a href="#LinkablePackageInfo-package_name">package_name</a>, <a href="#LinkablePackageInfo-package_path">package_path</a>, <a href="#LinkablePackageInfo-path">path</a>, <a href="#LinkablePackageInfo-_tslibrary">_tslibrary</a>)
LinkablePackageInfo(<a href="#LinkablePackageInfo-files">files</a>, <a href="#LinkablePackageInfo-package_name">package_name</a>, <a href="#LinkablePackageInfo-package_path">package_path</a>, <a href="#LinkablePackageInfo-path">path</a>)
</pre>

The LinkablePackageInfo provider provides information to the linker for linking pkg_npm built packages
Expand Down Expand Up @@ -202,9 +202,6 @@ or a source file path such as,

`path/to/package` or
`external/<external_wksp>/path/to/package`
<h4 id="LinkablePackageInfo-_tslibrary">_tslibrary</h4>

For internal use only


## NodeContextInfo
Expand Down
26 changes: 23 additions & 3 deletions docs/TypeScript.md
Expand Up @@ -185,8 +185,9 @@ Defaults to `[]`
<pre>
ts_library(<a href="#ts_library-name">name</a>, <a href="#ts_library-angular_assets">angular_assets</a>, <a href="#ts_library-compiler">compiler</a>, <a href="#ts_library-data">data</a>, <a href="#ts_library-deps">deps</a>, <a href="#ts_library-devmode_module">devmode_module</a>, <a href="#ts_library-devmode_target">devmode_target</a>,
<a href="#ts_library-expected_diagnostics">expected_diagnostics</a>, <a href="#ts_library-generate_externs">generate_externs</a>, <a href="#ts_library-internal_testing_type_check_dependencies">internal_testing_type_check_dependencies</a>,
<a href="#ts_library-link_workspace_root">link_workspace_root</a>, <a href="#ts_library-module_name">module_name</a>, <a href="#ts_library-module_root">module_root</a>, <a href="#ts_library-prodmode_module">prodmode_module</a>, <a href="#ts_library-prodmode_target">prodmode_target</a>, <a href="#ts_library-runtime">runtime</a>,
<a href="#ts_library-runtime_deps">runtime_deps</a>, <a href="#ts_library-srcs">srcs</a>, <a href="#ts_library-supports_workers">supports_workers</a>, <a href="#ts_library-tsconfig">tsconfig</a>, <a href="#ts_library-tsickle_typed">tsickle_typed</a>, <a href="#ts_library-use_angular_plugin">use_angular_plugin</a>)
<a href="#ts_library-link_workspace_root">link_workspace_root</a>, <a href="#ts_library-module_name">module_name</a>, <a href="#ts_library-module_root">module_root</a>, <a href="#ts_library-package_name">package_name</a>, <a href="#ts_library-package_path">package_path</a>, <a href="#ts_library-prodmode_module">prodmode_module</a>,
<a href="#ts_library-prodmode_target">prodmode_target</a>, <a href="#ts_library-runtime">runtime</a>, <a href="#ts_library-runtime_deps">runtime_deps</a>, <a href="#ts_library-srcs">srcs</a>, <a href="#ts_library-supports_workers">supports_workers</a>, <a href="#ts_library-tsconfig">tsconfig</a>, <a href="#ts_library-tsickle_typed">tsickle_typed</a>,
<a href="#ts_library-use_angular_plugin">use_angular_plugin</a>)
</pre>

type-check and compile a set of TypeScript sources to JavaScript.
Expand Down Expand Up @@ -338,7 +339,8 @@ Defaults to `False`
<h4 id="ts_library-link_workspace_root">link_workspace_root</h4>

(*Boolean*): Link the workspace root to the bin_dir to support absolute requires like 'my_wksp/path/to/file'.
If source files need to be required then they can be copied to the bin_dir with copy_to_bin.

If source files need to be required then they can be copied to the bin_dir with copy_to_bin.

Defaults to `False`

Expand All @@ -354,6 +356,24 @@ Defaults to `""`

Defaults to `""`

<h4 id="ts_library-package_name">package_name</h4>

(*String*): The package name that the linker will link this ts_library output as.

If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.

Defaults to `""`

<h4 id="ts_library-package_path">package_path</h4>

(*String*): The package path in the workspace that the linker will link this ts_library output to.

If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.

Defaults to `""`

<h4 id="ts_library-prodmode_module">prodmode_module</h4>

(*String*): Set the typescript `module` compiler option for prodmode output.
Expand Down
Expand Up @@ -822,7 +822,7 @@ index a38f6a8..436d83b 100755
"NpmPackageInfo",
"TsConfigInfo",
"compile_ts",
@@ -629,6 +630,15 @@ def _ng_module_impl(ctx):
@@ -629,6 +630,14 @@ def _ng_module_impl(ctx):
# once it is no longer needed.
])

Expand All @@ -832,7 +832,6 @@ index a38f6a8..436d83b 100755
+ package_name = ctx.attr.module_name,
+ path = path,
+ files = ts_providers["typescript"]["es5_sources"],
+ _tslibrary = True,
+ ))
+
return ts_providers_dict_to_struct(ts_providers)
Expand Down
14 changes: 12 additions & 2 deletions internal/js_library/js_library.bzl
Expand Up @@ -56,8 +56,18 @@ _ATTRS = {
They will be copied into the package bin folder if needed.""",
allow_files = True,
),
"package_name": attr.string(),
"package_path": attr.string(),
"package_name": attr.string(
doc = """The package name that the linker will link this js_library as.
If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.""",
),
"package_path": attr.string(
doc = """The package path in the workspace that the linker will link this js_library to.
If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.""",
),
"srcs": attr.label_list(allow_files = True),
"strip_prefix": attr.string(
doc = "Path components to strip from the start of the package import path",
Expand Down
96 changes: 28 additions & 68 deletions internal/linker/link_node_modules.bzl
Expand Up @@ -39,29 +39,17 @@ def add_arg(args, arg):
def _link_mapping(label, mappings, k, v):
# Check that two package name mapping do not map to two different source paths
package_name = k.split(":")[0]
source_path = v[1]

# Special case for ts_library module_name for legacy behavior and for AMD name work-around
# Mapping is tagged as "__tslibrary__".
# See longer comment below in _get_module_mappings for more details.
if v[0] != "__tslibrary__":
for iter_key, iter_values in mappings.items():
# Map key is of format "package_name:package_path"
# Map values are of format [deprecated, source_path]
iter_package_name = iter_key.split(":")[0]
iter_source_path = iter_values[1]
if iter_values[0] != "__tslibrary__" and package_name == iter_package_name and source_path != iter_source_path:
fail("conflicting mapping at '%s': '%s' and '%s' map to conflicting %s and %s" % (label, k, iter_key, source_path, iter_source_path))

# Allow __tslibrary__ special case to be overridden other matching mappings
if k in mappings and mappings[k] != v:
if mappings[k][0] == "__tslibrary__":
return True
elif v[0] == "__tslibrary__":
return False
fail(("conflicting mapping at '%s': '%s' maps to both %s and %s" % (label, k, mappings[k], v)), "deps")
else:
return True
link_path = v

for iter_key, iter_values in mappings.items():
# Map key is of format "package_name:package_path"
# Map values are of format [deprecated, link_path]
iter_package_name = iter_key.split(":")[0]
iter_source_path = iter_values
if package_name == iter_package_name and link_path != iter_source_path:
fail("conflicting mapping at '%s': '%s' and '%s' map to conflicting %s and %s" % (label, k, iter_key, link_path, iter_source_path))

return True

def write_node_modules_manifest(ctx, extra_data = [], mnemonic = None, link_workspace_root = False):
"""Writes a manifest file read by the linker, containing info about resolving runtime dependencies
Expand All @@ -74,7 +62,7 @@ def write_node_modules_manifest(ctx, extra_data = [], mnemonic = None, link_work
If source files need to be required then they can be copied to the bin_dir with copy_to_bin.
"""

mappings = {ctx.workspace_name: ["__link__", ctx.bin_dir.path]} if link_workspace_root else {}
mappings = {ctx.workspace_name: ctx.bin_dir.path} if link_workspace_root else {}
node_modules_roots = {}

# Look through data/deps attributes to find the root directories for the third-party node_modules;
Expand Down Expand Up @@ -104,7 +92,7 @@ def write_node_modules_manifest(ctx, extra_data = [], mnemonic = None, link_work
# Convert mappings to a module sets (modules per package package_path)
# {
# "package_path": {
# "package_name": "source_path",
# "package_name": "link_path",
# ...
# },
# ...
Expand All @@ -114,10 +102,10 @@ def write_node_modules_manifest(ctx, extra_data = [], mnemonic = None, link_work
map_key_split = k.split(":")
package_name = map_key_split[0]
package_path = map_key_split[1] if len(map_key_split) > 1 else ""
source_path = v[1]
link_path = v
if package_path not in module_sets:
module_sets[package_path] = {}
module_sets[package_path][package_name] = source_path
module_sets[package_path][package_name] = link_path

# Write the result to a file, and use the magic node option --bazel_node_modules_manifest
# The launcher.sh will peel off this argument and pass it to the linker rather than the program.
Expand All @@ -135,19 +123,15 @@ def write_node_modules_manifest(ctx, extra_data = [], mnemonic = None, link_work
return modules_manifest

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`.
"""Gathers module mappings from LinkablePackageInfo which maps "package_name:package_path" to link_path.
Args:
target: target
ctx: ctx
Returns:
The module mappings
Returns module mappings of shape:
{ "package_name:package_path": link_path, ... }
"""
mappings = {}

Expand All @@ -165,50 +149,26 @@ def _get_module_mappings(target, ctx):
_debug(ctx.var, "No LinkablePackageInfo for", target.label)
return mappings

linkable_package_info = target[LinkablePackageInfo]

# LinkablePackageInfo may be provided without a package_name so check for that case as well
if not target[LinkablePackageInfo].package_name:
if not linkable_package_info.package_name:
# No mappings contributed here, short-circuit with the transitive ones we collected
_debug(ctx.var, "No package_name in LinkablePackageInfo for", target.label)
return mappings

linkable_package_info = target[LinkablePackageInfo]
map_key = "%s:%s" % (linkable_package_info.package_name, linkable_package_info.package_path)
map_value = linkable_package_info.path

if hasattr(linkable_package_info, "package_path") and linkable_package_info.package_path:
mn = "%s:%s" % (linkable_package_info.package_name, linkable_package_info.package_path)
else:
# legacy (root linked) style mapping
# TODO(4.0): remove this else condition and always use "%s:%s" style
mn = linkable_package_info.package_name
mr = ["__link__", linkable_package_info.path]

# Special case for ts_library module_name for legacy behavior and for AMD name work-around
# Tag the mapping as "__tslibrary__" so it can be overridden by any other mapping if found.
#
# In short, ts_library module_name attribute results in both setting the AMD name (which
# desired and necessary in devmode which outputs UMD) and in making a linkable mapping. Because
# of this, you can get in the situation where a ts_library module_name and a downstream pkg_name
# package_name conflict and result in duplicate mappings. This work-around will make this
# situation work however it is not a recommended pattern since a ts_library can be a dep of a
# pkg_npm but not vice-verse at the moment since ts_library cannot handle directory artifacts as
# deps.
#
# TODO(4.0): In a future major release, ts_library will get a package_name attribute to enable the linker
# and the __tslibrary__ special case can be factored out.
# This is planned for 4.0: https://github.com/bazelbuild/rules_nodejs/issues/2450.
if hasattr(linkable_package_info, "_tslibrary") and linkable_package_info._tslibrary:
mr[0] = "__tslibrary__"

if _link_mapping(target.label, mappings, mn, mr):
_debug(ctx.var, "target %s adding module mapping %s: %s" % (target.label, mn, mr))
mappings[mn] = mr
if _link_mapping(target.label, mappings, map_key, map_value):
_debug(ctx.var, "target %s adding module mapping %s: %s" % (target.label, map_key, map_value))
mappings[map_key] = map_value

# Returns mappings of shape:
# {
# "package_name": [legacy_tslibary_usage, source_path],
# "package_name:package_path": [legacy_tslibary_usage, source_path],
# "package_name:package_path": link_path,
# ...
# }
# TODO(4.0): simplify to { "package_name:package_path": source_path, ... } once __tslibrary__ is no longer needed
return mappings

def _module_mappings_aspect_impl(target, ctx):
Expand Down
2 changes: 0 additions & 2 deletions internal/pkg_npm/BUILD.bazel
Expand Up @@ -16,8 +16,6 @@ nodejs_binary(
name = "packager",
data = ["//third_party/github.com/gjtorikian/isBinaryFile"],
entry_point = ":packager.js",
# TODO: figure out why isbinaryfile is not linked in a way this can resolve
templated_args = ["--bazel_patch_module_resolver"],
)

nodejs_binary(
Expand Down
12 changes: 8 additions & 4 deletions internal/pkg_npm/pkg_npm.bzl
Expand Up @@ -103,12 +103,16 @@ PKG_NPM_ATTRS = dict(NODE_CONTEXT_ATTRS, **{
allow_files = True,
),
"package_name": attr.string(
doc = """Optional package_name that this npm package may be imported as.""",
doc = """The package name that the linker will link this npm package as.
If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.""",
),
"package_path": attr.string(
doc = """The directory in the workspace to link to.
If set, link this pkg_npm to the node_modules under the package path specified.
If unset, the default is to link to the node_modules root of the workspace.""",
doc = """The package path in the workspace that the linker will link this npm package to.
If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.""",
),
"srcs": attr.label_list(
doc = """Files inside this directory which are simply copied into the package.""",
Expand Down
4 changes: 0 additions & 4 deletions internal/providers/linkable_package_info.bzl
Expand Up @@ -40,9 +40,5 @@ or a source file path such as,
`path/to/package` or
`external/<external_wksp>/path/to/package`
""",
# TODO(4.0): In a future major release, ts_library will get a package_name attribute to enable the linker
# and the _tslibrary special case can be removed.
# This is planned for 4.0: https://github.com/bazelbuild/rules_nodejs/issues/2450.
"_tslibrary": "For internal use only",
},
)
30 changes: 19 additions & 11 deletions packages/typescript/internal/build_defs.bzl
Expand Up @@ -347,18 +347,13 @@ def _ts_library_impl(ctx):
# once it is no longer needed.
])

if ctx.attr.module_name:
path = "/".join([p for p in [ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package] if p])
if ctx.attr.package_name:
link_path = "/".join([p for p in [ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package] if p])
ts_providers["providers"].append(LinkablePackageInfo(
package_name = ctx.attr.module_name,
# TODO(4.0): ts_library doesn't support multi-linked first party deps yet.
# it can be added in 4.0 on the next set of breaking change when we
# also add the package_name attribute to separate turning on the linker
# from the module_name attribute
package_path = "",
path = path,
package_name = ctx.attr.package_name,
package_path = ctx.attr.package_path,
path = link_path,
files = ts_providers["typescript"]["es5_sources"],
_tslibrary = True,
))

return ts_providers_dict_to_struct(ts_providers)
Expand Down Expand Up @@ -434,7 +429,20 @@ This value will override the `target` option in the user supplied tsconfig.""",
"internal_testing_type_check_dependencies": attr.bool(default = False, doc = "Testing only, whether to type check inputs that aren't srcs."),
"link_workspace_root": attr.bool(
doc = """Link the workspace root to the bin_dir to support absolute requires like 'my_wksp/path/to/file'.
If source files need to be required then they can be copied to the bin_dir with copy_to_bin.""",
If source files need to be required then they can be copied to the bin_dir with copy_to_bin.""",
),
"package_name": attr.string(
doc = """The package name that the linker will link this ts_library output as.
If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.""",
),
"package_path": attr.string(
doc = """The package path in the workspace that the linker will link this ts_library output to.
If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>.
If package_path is not set the this will be the root node_modules of the workspace.""",
),
"prodmode_module": attr.string(
doc = """Set the typescript `module` compiler option for prodmode output.
Expand Down

0 comments on commit 98bca9a

Please sign in to comment.