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 bb9fd46
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 109 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
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",
},
)
1 change: 1 addition & 0 deletions packages/esbuild/test/bundle/BUILD.bazel
Expand Up @@ -12,6 +12,7 @@ ts_library(
name = "lib",
srcs = ["a.ts"],
deps = [":index"],
package_name = "lib",
)

esbuild(
Expand Down
49 changes: 47 additions & 2 deletions packages/esbuild/test/bundle/bundle.golden.txt
@@ -1,8 +1,53 @@
(() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __require = (x) => {
if (typeof require !== "undefined")
return require(x);
throw new Error('Dynamic require of "' + x + '" is not supported');
};
var __commonJS = (cb, mod) => function __require2() {
return mod || (0, cb[Object.keys(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __reExport = (target, module, desc) => {
if (module && typeof module === "object" || typeof module === "function") {
for (let key of __getOwnPropNames(module))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, { get: () => module[key], enumerable: !(desc = __getOwnPropDesc(module, key)) || desc.enumerable });
}
return target;
};
var __toModule = (module) => {
return __reExport(__markAsModule(__defProp(module != null ? __create(__getProtoOf(module)) : {}, "default", module && module.__esModule && "default" in module ? { get: () => module.default, enumerable: true } : { value: module, enumerable: true })), module);
};


var NAME = "bazel";
var require_lib = __commonJS({
"node_modules/lib/index.js"(exports, module) {
(function(factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(__require, exports);
if (v !== void 0)
module.exports = v;
} else if (typeof define === "function" && define.amd) {
define("lib", ["require", "exports"], factory);
}
})(function(require2, exports2) {
"use strict";
Object.defineProperty(exports2, "__esModule", { value: true });
exports2.NAME = void 0;
exports2.NAME = "bazel";
});
}
});


console.log(`Hello ${NAME}`);
var import_lib = __toModule(require_lib());
console.log(`Hello ${import_lib.NAME}`);
})();
//# sourceMappingURL=bundle.js.map
2 changes: 1 addition & 1 deletion packages/esbuild/test/bundle/bundle.min.golden.txt
@@ -1,2 +1,2 @@
(()=>{var o="bazel";console.log(`Hello ${o}`);})();
(()=>{var c=Object.create;var n=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var s=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var a=e=>n(e,"__esModule",{value:!0});var r=e=>{if(typeof require!="undefined")return require(e);throw new Error('Dynamic require of "'+e+'" is not supported')};var j=(e,i)=>()=>(i||e((i={exports:{}}).exports,i),i.exports);var m=(e,i,t)=>{if(i&&typeof i=="object"||typeof i=="function")for(let o of p(i))!M.call(e,o)&&o!=="default"&&n(e,o,{get:()=>i[o],enumerable:!(t=b(i,o))||t.enumerable});return e},q=e=>m(a(n(e!=null?c(s(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var l=j((u,f)=>{(function(e){if(typeof f=="object"&&typeof f.exports=="object"){var i=e(r,u);i!==void 0&&(f.exports=i)}else typeof define=="function"&&define.amd&&define("lib",["require","exports"],e)})(function(e,i){"use strict";Object.defineProperty(i,"__esModule",{value:!0}),i.NAME=void 0,i.NAME="bazel"})});var d=q(l());console.log(`Hello ${d.NAME}`);})();
//# sourceMappingURL=bundle.min.js.map

0 comments on commit bb9fd46

Please sign in to comment.