Skip to content

Commit 4fe1a17

Browse files
authored
feat(builtin): new js_library rule (#2109)
Removes `node_module_library` private API and renames it to `js_library` the latter will be promoted to a public API in a followup change
1 parent 83688a1 commit 4fe1a17

23 files changed

Lines changed: 249 additions & 216 deletions

File tree

WORKSPACE

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ yarn_install(
5151
# The @npm//:node_modules_filegroup generated by manual_build_file_contents
5252
# is used in the //packages/typescript/test/reference_types_directive:tsconfig_types
5353
# test. For now we're still supporting node_modules as a filegroup tho this may
54-
# change in the future. The default generated //:node_modules target is a node_module_library
55-
# rule which provides NpmPackageInfo but that rule is not yet in the public API and we
56-
# have not yet dropped support for filegroup based node_modules target.
54+
# change in the future. The default generated //:node_modules target is a js_library
55+
# rule which provides NpmPackageInfo but we have not yet dropped support for
56+
# filegroup based node_modules target.
5757
manual_build_file_contents = """
5858
filegroup(
5959
name = "node_modules_filegroup",

internal/js_library/js_library.bzl

Lines changed: 160 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,27 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""js_library allows defining a set of javascript sources and assigning a package_name.
15+
"""js_library can be used to expose and share any library package.
1616
17-
DO NOT USE - this is not fully designed, and exists only to enable testing within this repo.
17+
DO NOT USE - this is not fully designed yet and it is a work in progress.
1818
"""
1919

20-
load("//:providers.bzl", "LinkablePackageInfo", "declaration_info", "js_module_info")
21-
load("//third_party/github.com/bazelbuild/bazel-skylib:rules/private/copy_file_private.bzl", "copy_bash", "copy_cmd")
20+
load(
21+
"//:providers.bzl",
22+
"DeclarationInfo",
23+
"JSModuleInfo",
24+
"JSNamedModuleInfo",
25+
"LinkablePackageInfo",
26+
"NpmPackageInfo",
27+
"declaration_info",
28+
"js_module_info",
29+
"js_named_module_info",
30+
)
31+
load(
32+
"//third_party/github.com/bazelbuild/bazel-skylib:rules/private/copy_file_private.bzl",
33+
"copy_bash",
34+
"copy_cmd",
35+
)
2236

2337
_AMD_NAMES_DOC = """Mapping from require module names to global variables.
2438
This allows devmode JS sources to load unnamed UMD bundles from third-party libraries."""
@@ -50,90 +64,201 @@ def write_amd_names_shim(actions, amd_names_shim, targets):
5064
actions.write(amd_names_shim, amd_names_shim_content)
5165

5266
def _impl(ctx):
53-
files = []
67+
input_files = ctx.files.srcs + ctx.files.named_module_srcs
68+
all_files = []
5469
typings = []
5570
js_files = []
71+
named_module_files = []
72+
include_npm_package_info = False
5673

57-
for src in ctx.files.srcs:
58-
if src.is_source and not src.path.startswith("external/"):
59-
dst = ctx.actions.declare_file(src.basename, sibling = src)
74+
for idx, f in enumerate(input_files):
75+
file = f
76+
77+
# copy files into bin if needed
78+
if file.is_source and not file.path.startswith("external/"):
79+
dst = ctx.actions.declare_file(file.basename, sibling = file)
6080
if ctx.attr.is_windows:
61-
copy_cmd(ctx, src, dst)
62-
else:
63-
copy_bash(ctx, src, dst)
64-
if dst.basename.endswith(".d.ts"):
65-
typings.append(dst)
81+
copy_cmd(ctx, file, dst)
6682
else:
67-
files.append(dst)
68-
elif src.basename.endswith(".d.ts"):
69-
typings.append(src)
70-
else:
71-
files.append(src)
83+
copy_bash(ctx, file, dst)
84+
85+
# re-assign file to the one now copied into the bin folder
86+
file = dst
87+
88+
# register js files
89+
if file.basename.endswith(".js") or file.basename.endswith(".js.map") or file.basename.endswith(".json"):
90+
js_files.append(file)
91+
92+
# register typings
93+
if (
94+
(
95+
file.path.endswith(".d.ts") or
96+
file.path.endswith(".d.ts.map") or
97+
# package.json may be required to resolve "typings" key
98+
file.path.endswith("/package.json")
99+
) and
100+
# exclude eg. external/npm/node_modules/protobufjs/node_modules/@types/node/index.d.ts
101+
# these would be duplicates of the typings provided directly in another dependency.
102+
# also exclude all /node_modules/typescript/lib/lib.*.d.ts files as these are determined by
103+
# the tsconfig "lib" attribute
104+
len(file.path.split("/node_modules/")) < 3 and file.path.find("/node_modules/typescript/lib/lib.") == -1
105+
):
106+
typings.append(file)
72107

73-
for p in files:
74-
if p.basename.endswith(".js") or p.basename.endswith(".js.map") or p.basename.endswith(".json"):
75-
js_files.append(p)
108+
# auto detect if it entirely an npm package
109+
#
110+
# NOTE: it probably can be removed once we support node_modules from more than
111+
# a single workspace
112+
if file.is_source and file.path.startswith("external/"):
113+
# We cannot always expose the NpmPackageInfo as the linker
114+
# only allow us to reference node modules from a single workspace at a time.
115+
# Here we are automatically decide if we should or not including that provider
116+
# by running through the sources and check if we have a src coming from an external
117+
# workspace which indicates we should include the provider.
118+
include_npm_package_info = True
76119

77-
files_depset = depset(files)
120+
# ctx.files.named_module_srcs are merged after ctx.files.srcs
121+
if idx >= len(ctx.files.srcs):
122+
named_module_files.append(file)
123+
124+
# every single file on bin should be added here
125+
all_files.append(file)
126+
127+
files_depset = depset(all_files)
128+
js_files_depset = depset(js_files)
129+
named_module_files_depset = depset(named_module_files)
130+
typings_depset = depset(typings)
131+
132+
files_depsets = [files_depset]
133+
npm_sources_depsets = [files_depset]
134+
direct_sources_depsets = [files_depset]
135+
direct_named_module_sources_depsets = [named_module_files_depset]
136+
typings_depsets = [typings_depset]
137+
js_files_depsets = [js_files_depset]
138+
139+
for dep in ctx.attr.deps:
140+
if NpmPackageInfo in dep:
141+
npm_sources_depsets.append(dep[NpmPackageInfo].sources)
142+
else:
143+
if JSModuleInfo in dep:
144+
js_files_depsets.append(dep[JSModuleInfo].direct_sources)
145+
direct_sources_depsets.append(dep[JSModuleInfo].direct_sources)
146+
if JSNamedModuleInfo in dep:
147+
direct_named_module_sources_depsets.append(dep[JSNamedModuleInfo].direct_sources)
148+
direct_sources_depsets.append(dep[JSNamedModuleInfo].direct_sources)
149+
if DeclarationInfo in dep:
150+
typings_depsets.append(dep[DeclarationInfo].declarations)
151+
direct_sources_depsets.append(dep[DeclarationInfo].declarations)
152+
if DefaultInfo in dep:
153+
files_depsets.append(dep[DefaultInfo].files)
78154

79155
providers = [
80156
DefaultInfo(
81-
files = files_depset,
82-
runfiles = ctx.runfiles(files = ctx.files.srcs),
157+
files = depset(transitive = files_depsets),
158+
runfiles = ctx.runfiles(
159+
files = all_files,
160+
transitive_files = depset(transitive = files_depsets),
161+
),
83162
),
84163
AmdNamesInfo(names = ctx.attr.amd_names),
85-
js_module_info(depset(js_files)),
164+
js_module_info(
165+
sources = depset(transitive = js_files_depsets),
166+
deps = ctx.attr.deps,
167+
),
168+
js_named_module_info(
169+
sources = depset(transitive = direct_named_module_sources_depsets),
170+
deps = ctx.attr.deps,
171+
),
86172
]
87173

88174
if ctx.attr.package_name:
89175
path = "/".join([p for p in [ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package] if p])
90176
providers.append(LinkablePackageInfo(
91177
package_name = ctx.attr.package_name,
92178
path = path,
93-
files = files_depset,
179+
files = depset(transitive = direct_sources_depsets),
180+
))
181+
182+
if include_npm_package_info:
183+
workspace_name = ctx.label.workspace_name if ctx.label.workspace_name else ctx.workspace_name
184+
providers.append(NpmPackageInfo(
185+
direct_sources = depset(transitive = direct_sources_depsets),
186+
sources = depset(transitive = npm_sources_depsets),
187+
workspace = workspace_name,
94188
))
95189

96190
# Don't provide DeclarationInfo if there are no typings to provide.
97191
# Improves error messaging downstream if DeclarationInfo is required.
98192
if len(typings):
99-
providers.append(declaration_info(depset(typings)))
193+
providers.append(declaration_info(
194+
declarations = depset(transitive = typings_depsets),
195+
deps = ctx.attr.deps,
196+
))
100197

101198
return providers
102199

103200
_js_library = rule(
104201
implementation = _impl,
105202
attrs = {
106-
"amd_names": attr.string_dict(doc = _AMD_NAMES_DOC),
107-
"is_windows": attr.bool(mandatory = True, doc = "Automatically set by macro"),
203+
"amd_names": attr.string_dict(
204+
doc = _AMD_NAMES_DOC,
205+
),
206+
"deps": attr.label_list(
207+
doc = """Transitive dependencies of the package.
208+
It should include fine grained npm dependencies from the sources
209+
or other targets we want to include in the library but also propagate their own deps.""",
210+
),
211+
"is_windows": attr.bool(
212+
doc = "Automatically set by macro",
213+
mandatory = True,
214+
),
108215
# module_name for legacy ts_library module_mapping support
216+
# which is still being used in a couple of tests
109217
# TODO: remove once legacy module_mapping is removed
110-
"module_name": attr.string(),
111-
"package_name": attr.string(),
218+
"module_name": attr.string(
219+
doc = "Internal use only. It will be removed soon.",
220+
),
221+
"named_module_srcs": attr.label_list(
222+
doc = """A subset of srcs that are javascript named-UMD or
223+
named-AMD for use in rules such as ts_devserver.
224+
They will be copied into the package bin folder if needed.""",
225+
allow_files = True,
226+
),
227+
"package_name": attr.string(
228+
doc = """Optional package_name that this package may be imported as.""",
229+
),
112230
"srcs": attr.label_list(
231+
doc = """The list of files that comprise the package.
232+
They will be copied into the package bin folder if needed.""",
113233
allow_files = True,
114-
mandatory = True,
115234
),
116235
},
236+
doc = "Defines a js_library package",
117237
)
118238

119239
def js_library(
120240
name,
121-
srcs,
241+
srcs = [],
122242
amd_names = {},
123243
package_name = None,
244+
deps = [],
245+
named_module_srcs = [],
124246
**kwargs):
125-
"""Internal use only. May be published to the public API in a future release."""
247+
"""Internal use only yet. It will be released into a public API in a future release."""
126248
module_name = kwargs.pop("module_name", None)
127249
if module_name:
128250
fail("use package_name instead of module_name in target //%s:%s" % (native.package_name(), name))
129251
if kwargs.pop("is_windows", None):
130252
fail("is_windows is set by the js_library macro and should not be set explicitely")
131253
_js_library(
132254
name = name,
133-
srcs = srcs,
134255
amd_names = amd_names,
256+
srcs = srcs,
257+
named_module_srcs = named_module_srcs,
258+
deps = deps,
135259
package_name = package_name,
136260
# module_name for legacy ts_library module_mapping support
261+
# which is still being used in a couple of tests
137262
# TODO: remove once legacy module_mapping is removed
138263
module_name = package_name,
139264
is_windows = select({

internal/node/test/lib2/BUILD.bazel

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ js_library(
1313
name = "lib2",
1414
package_name = "lib2",
1515
srcs = [
16-
"main.js",
1716
"package.json",
1817
"src/some.js",
1918
],
19+
deps = [
20+
":tsconfig",
21+
],
2022
)

internal/npm_install/generate_build_file.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
/**
1818
* @fileoverview This script generates BUILD.bazel files by analyzing
1919
* the node_modules folder layed out by yarn or npm. It generates
20-
* fine grained Bazel `node_module_library` targets for each root npm package
20+
* fine grained Bazel `js_library` targets for each root npm package
2121
* and all files for that package and its transitive deps are included
2222
* in the target. For example, `@<workspace>//jasmine` would
2323
* include all files in the jasmine npm package and all of its
@@ -28,7 +28,7 @@
2828
* target will be generated for the `jasmine` binary in the `jasmine`
2929
* npm package.
3030
*
31-
* Additionally, a `@<workspace>//:node_modules` `node_module_library`
31+
* Additionally, a `@<workspace>//:node_modules` `js_library`
3232
* is generated that includes all packages under node_modules
3333
* as well as the .bin folder.
3434
*
@@ -158,16 +158,16 @@ function generateRootBuildFile(pkgs: Dep[]) {
158158
})});
159159

160160
let buildFile = BUILD_FILE_HEADER +
161-
`load("@build_bazel_rules_nodejs//internal/npm_install:node_module_library.bzl", "node_module_library")
161+
`load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
162162
163163
exports_files([
164164
${exportsStarlark}])
165165
166-
# The node_modules directory in one catch-all node_module_library.
166+
# The node_modules directory in one catch-all js_library.
167167
# NB: Using this target may have bad performance implications if
168168
# there are many files in target.
169169
# See https://github.com/bazelbuild/bazel/issues/5153.
170-
node_module_library(
170+
js_library(
171171
name = "node_modules",${pkgFilesStarlark}${depsStarlark}
172172
)
173173
@@ -861,7 +861,7 @@ function findFile(pkg: Dep, m: string) {
861861
}
862862

863863
/**
864-
* Given a pkg, return the skylark `node_module_library` targets for the package.
864+
* Given a pkg, return the skylark `js_library` targets for the package.
865865
*/
866866
function printPackage(pkg: Dep) {
867867
function starlarkFiles(attr: string, files: string[], comment: string = '') {
@@ -919,8 +919,7 @@ function printPackage(pkg: Dep) {
919919
const depsStarlark =
920920
deps.map(dep => `"//${dep._dir}:${dep._name}__contents",`).join('\n ');
921921

922-
let result =
923-
`load("@build_bazel_rules_nodejs//internal/npm_install:node_module_library.bzl", "node_module_library")
922+
let result = `load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
924923
925924
# Generated targets for npm package "${pkg._dir}"
926925
${printJson(pkg)}
@@ -955,7 +954,7 @@ filegroup(
955954
)
956955
957956
# The primary target for this package for use in rule deps
958-
node_module_library(
957+
js_library(
959958
name = "${pkg._name}",
960959
# direct sources listed for strict deps support
961960
srcs = [":${pkg._name}__files"],
@@ -967,14 +966,14 @@ node_module_library(
967966
)
968967
969968
# Target is used as dep for main targets to prevent circular dependencies errors
970-
node_module_library(
969+
js_library(
971970
name = "${pkg._name}__contents",
972971
srcs = [":${pkg._name}__files", ":${pkg._name}__nested_node_modules"],${namedSourcesStarlark}
973972
visibility = ["//:__subpackages__"],
974973
)
975974
976975
# Typings files that are part of the npm package not including nested node_modules
977-
node_module_library(
976+
js_library(
978977
name = "${pkg._name}__typings",${dtsStarlark}
979978
)
980979
@@ -1137,7 +1136,7 @@ type Dep = {
11371136
}
11381137

11391138
/**
1140-
* Given a scope, return the skylark `node_module_library` target for the scope.
1139+
* Given a scope, return the skylark `js_library` target for the scope.
11411140
*/
11421141
function printScope(scope: string, pkgs: Dep[]) {
11431142
pkgs = pkgs.filter(pkg => !pkg._isNested && pkg._dir.startsWith(`${scope}/`));
@@ -1168,10 +1167,10 @@ function printScope(scope: string, pkgs: Dep[]) {
11681167
],`;
11691168
}
11701169

1171-
return `load("@build_bazel_rules_nodejs//internal/npm_install:node_module_library.bzl", "node_module_library")
1170+
return `load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
11721171
11731172
# Generated target for npm scope ${scope}
1174-
node_module_library(
1173+
js_library(
11751174
name = "${scope}",${pkgFilesStarlark}${depsStarlark}
11761175
)
11771176

0 commit comments

Comments
 (0)