Skip to content

Commit 9622443

Browse files
committed
fix(typescript): add build_test to ensure typecheck is run under --build_tests_only (#3196)
1 parent 9b2c08b commit 9622443

File tree

5 files changed

+423
-2
lines changed

5 files changed

+423
-2
lines changed

docs/TypeScript.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,13 @@ When a custom transpiler is used, then the `ts_project` macro expands to these t
492492
is not needed to produce the default outputs.
493493
This is considered a feature, as it allows you to have a faster development mode where type-checking
494494
is not on the critical path.
495-
- `[name]_typecheck` - this target will fail to build if the type-checking fails, useful for CI.
495+
- `[name]_typecheck` - provides typings (`.d.ts` files) as the default output,
496+
therefore building this target always causes the typechecker to run.
497+
- `[name]_typecheck_test` - a
498+
[`build_test`](https://github.com/bazelbuild/bazel-skylib/blob/main/rules/build_test.bzl)
499+
target which simply depends on the `[name]_typecheck` target.
500+
This ensures that typechecking will be run under `bazel test` with
501+
[`--build_tests_only`](https://docs.bazel.build/versions/main/user-manual.html#flag--build_tests_only).
496502
- `[name]_typings` - internal target which runs the binary from the `tsc` attribute
497503
- Any additional target(s) the custom transpiler rule/macro produces.
498504
Some rules produce one target per TypeScript input file.

packages/typescript/internal/ts_project.bzl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "run
55
load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "module_mappings_aspect")
66
load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_binary")
77
load("@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel-skylib:lib/partial.bzl", "partial")
8+
load("@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel-skylib:rules/build_test.bzl", "build_test")
89
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
910
load(":ts_config.bzl", "TsConfigInfo", "write_tsconfig")
1011

@@ -574,7 +575,13 @@ def ts_project_macro(
574575
is not needed to produce the default outputs.
575576
This is considered a feature, as it allows you to have a faster development mode where type-checking
576577
is not on the critical path.
577-
- `[name]_typecheck` - this target will fail to build if the type-checking fails, useful for CI.
578+
- `[name]_typecheck` - provides typings (`.d.ts` files) as the default output,
579+
therefore building this target always causes the typechecker to run.
580+
- `[name]_typecheck_test` - a
581+
[`build_test`](https://github.com/bazelbuild/bazel-skylib/blob/main/rules/build_test.bzl)
582+
target which simply depends on the `[name]_typecheck` target.
583+
This ensures that typechecking will be run under `bazel test` with
584+
[`--build_tests_only`](https://docs.bazel.build/versions/main/user-manual.html#flag--build_tests_only).
578585
- `[name]_typings` - internal target which runs the binary from the `tsc` attribute
579586
- Any additional target(s) the custom transpiler rule/macro produces.
580587
Some rules produce one target per TypeScript input file.
@@ -818,6 +825,7 @@ def ts_project_macro(
818825
tsc_target_name = "%s_typings" % name
819826
transpile_target_name = "%s_transpile" % name
820827
typecheck_target_name = "%s_typecheck" % name
828+
test_target_name = "%s_typecheck_test" % name
821829

822830
common_kwargs = {
823831
"tags": kwargs.get("tags", []),
@@ -853,6 +861,13 @@ def ts_project_macro(
853861
**common_kwargs
854862
)
855863

864+
# Ensures the target above gets built under `bazel test --build_tests_only`
865+
build_test(
866+
name = test_target_name,
867+
targets = [typecheck_target_name],
868+
**common_kwargs
869+
)
870+
856871
# Default target produced by the macro gives the js and map outs, with the transitive dependencies.
857872
js_library(
858873
name = name,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright 2017 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Skylib module containing functions that operate on dictionaries."""
16+
17+
def _add(*dictionaries, **kwargs):
18+
"""Returns a new `dict` that has all the entries of the given dictionaries.
19+
20+
If the same key is present in more than one of the input dictionaries, the
21+
last of them in the argument list overrides any earlier ones.
22+
23+
This function is designed to take zero or one arguments as well as multiple
24+
dictionaries, so that it follows arithmetic identities and callers can avoid
25+
special cases for their inputs: the sum of zero dictionaries is the empty
26+
dictionary, and the sum of a single dictionary is a copy of itself.
27+
28+
Args:
29+
*dictionaries: Zero or more dictionaries to be added.
30+
**kwargs: Additional dictionary passed as keyword args.
31+
32+
Returns:
33+
A new `dict` that has all the entries of the given dictionaries.
34+
"""
35+
result = {}
36+
for d in dictionaries:
37+
result.update(d)
38+
result.update(kwargs)
39+
return result
40+
41+
dicts = struct(
42+
add = _add,
43+
)
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Copyright 2018 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Skylib module containing common hash-set algorithms.
16+
17+
An empty set can be created using: `sets.make()`, or it can be created with some starting values
18+
if you pass it an sequence: `sets.make([1, 2, 3])`. This returns a struct containing all of the
19+
values as keys in a dictionary - this means that all passed in values must be hashable. The
20+
values in the set can be retrieved using `sets.to_list(my_set)`.
21+
22+
An arbitrary object can be tested whether it is a set generated by `sets.make()` or not with the
23+
`types.is_set()` method in types.bzl.
24+
"""
25+
26+
load("//third_party/github.com/bazelbuild/bazel-skylib:lib/dicts.bzl", "dicts")
27+
28+
def _make(elements = None):
29+
"""Creates a new set.
30+
31+
All elements must be hashable.
32+
33+
Args:
34+
elements: Optional sequence to construct the set out of.
35+
36+
Returns:
37+
A set containing the passed in values.
38+
"""
39+
40+
# If you change the structure of a set, you need to also update the _is_set method
41+
# in types.bzl.
42+
elements = elements if elements else []
43+
return struct(_values = {e: None for e in elements})
44+
45+
def _copy(s):
46+
"""Creates a new set from another set.
47+
48+
Args:
49+
s: A set, as returned by `sets.make()`.
50+
51+
Returns:
52+
A new set containing the same elements as `s`.
53+
"""
54+
return struct(_values = dict(s._values))
55+
56+
def _to_list(s):
57+
"""Creates a list from the values in the set.
58+
59+
Args:
60+
s: A set, as returned by `sets.make()`.
61+
62+
Returns:
63+
A list of values inserted into the set.
64+
"""
65+
return s._values.keys()
66+
67+
def _insert(s, e):
68+
"""Inserts an element into the set.
69+
70+
Element must be hashable. This mutates the original set.
71+
72+
Args:
73+
s: A set, as returned by `sets.make()`.
74+
e: The element to be inserted.
75+
76+
Returns:
77+
The set `s` with `e` included.
78+
"""
79+
s._values[e] = None
80+
return s
81+
82+
def _remove(s, e):
83+
"""Removes an element from the set.
84+
85+
Element must be hashable. This mutates the original set.
86+
87+
Args:
88+
s: A set, as returned by `sets.make()`.
89+
e: The element to be removed.
90+
91+
Returns:
92+
The set `s` with `e` removed.
93+
"""
94+
s._values.pop(e)
95+
return s
96+
97+
def _contains(a, e):
98+
"""Checks for the existence of an element in a set.
99+
100+
Args:
101+
a: A set, as returned by `sets.make()`.
102+
e: The element to look for.
103+
104+
Returns:
105+
True if the element exists in the set, False if the element does not.
106+
"""
107+
return e in a._values
108+
109+
def _get_shorter_and_longer(a, b):
110+
"""Returns two sets in the order of shortest and longest.
111+
112+
Args:
113+
a: A set, as returned by `sets.make()`.
114+
b: A set, as returned by `sets.make()`.
115+
116+
Returns:
117+
`a`, `b` if `a` is shorter than `b` - or `b`, `a` if `b` is shorter than `a`.
118+
"""
119+
if _length(a) < _length(b):
120+
return a, b
121+
return b, a
122+
123+
def _is_equal(a, b):
124+
"""Returns whether two sets are equal.
125+
126+
Args:
127+
a: A set, as returned by `sets.make()`.
128+
b: A set, as returned by `sets.make()`.
129+
130+
Returns:
131+
True if `a` is equal to `b`, False otherwise.
132+
"""
133+
return a._values == b._values
134+
135+
def _is_subset(a, b):
136+
"""Returns whether `a` is a subset of `b`.
137+
138+
Args:
139+
a: A set, as returned by `sets.make()`.
140+
b: A set, as returned by `sets.make()`.
141+
142+
Returns:
143+
True if `a` is a subset of `b`, False otherwise.
144+
"""
145+
for e in a._values.keys():
146+
if e not in b._values:
147+
return False
148+
return True
149+
150+
def _disjoint(a, b):
151+
"""Returns whether two sets are disjoint.
152+
153+
Two sets are disjoint if they have no elements in common.
154+
155+
Args:
156+
a: A set, as returned by `sets.make()`.
157+
b: A set, as returned by `sets.make()`.
158+
159+
Returns:
160+
True if `a` and `b` are disjoint, False otherwise.
161+
"""
162+
shorter, longer = _get_shorter_and_longer(a, b)
163+
for e in shorter._values.keys():
164+
if e in longer._values:
165+
return False
166+
return True
167+
168+
def _intersection(a, b):
169+
"""Returns the intersection of two sets.
170+
171+
Args:
172+
a: A set, as returned by `sets.make()`.
173+
b: A set, as returned by `sets.make()`.
174+
175+
Returns:
176+
A set containing the elements that are in both `a` and `b`.
177+
"""
178+
shorter, longer = _get_shorter_and_longer(a, b)
179+
return struct(_values = {e: None for e in shorter._values.keys() if e in longer._values})
180+
181+
def _union(*args):
182+
"""Returns the union of several sets.
183+
184+
Args:
185+
*args: An arbitrary number of sets.
186+
187+
Returns:
188+
The set union of all sets in `*args`.
189+
"""
190+
return struct(_values = dicts.add(*[s._values for s in args]))
191+
192+
def _difference(a, b):
193+
"""Returns the elements in `a` that are not in `b`.
194+
195+
Args:
196+
a: A set, as returned by `sets.make()`.
197+
b: A set, as returned by `sets.make()`.
198+
199+
Returns:
200+
A set containing the elements that are in `a` but not in `b`.
201+
"""
202+
return struct(_values = {e: None for e in a._values.keys() if e not in b._values})
203+
204+
def _length(s):
205+
"""Returns the number of elements in a set.
206+
207+
Args:
208+
s: A set, as returned by `sets.make()`.
209+
210+
Returns:
211+
An integer representing the number of elements in the set.
212+
"""
213+
return len(s._values)
214+
215+
def _repr(s):
216+
"""Returns a string value representing the set.
217+
218+
Args:
219+
s: A set, as returned by `sets.make()`.
220+
221+
Returns:
222+
A string representing the set.
223+
"""
224+
return repr(s._values.keys())
225+
226+
sets = struct(
227+
make = _make,
228+
copy = _copy,
229+
to_list = _to_list,
230+
insert = _insert,
231+
contains = _contains,
232+
is_equal = _is_equal,
233+
is_subset = _is_subset,
234+
disjoint = _disjoint,
235+
intersection = _intersection,
236+
union = _union,
237+
difference = _difference,
238+
length = _length,
239+
remove = _remove,
240+
repr = _repr,
241+
str = _repr,
242+
# is_set is declared in types.bzl
243+
)

0 commit comments

Comments
 (0)