@@ -13,7 +13,7 @@ _DEFAULT_TSC = (
1313 "//typescript/bin:tsc"
1414)
1515
16- _ATTRS = {
16+ _ATTRS = dict ( _validate_lib . attrs , ** {
1717 "args" : attr .string_list (),
1818 "data" : attr .label_list (default = [], allow_files = True ),
1919 "declaration_dir" : attr .string (),
@@ -25,7 +25,6 @@ _ATTRS = {
2525 ],
2626 aspects = [module_mappings_aspect ],
2727 ),
28- "extends" : attr .label (allow_files = [".json" ]),
2928 "link_workspace_root" : attr .bool (),
3029 "out_dir" : attr .string (),
3130 "root_dir" : attr .string (),
@@ -36,9 +35,9 @@ _ATTRS = {
3635 "srcs" : attr .label_list (allow_files = True , mandatory = True ),
3736 "supports_workers" : attr .bool (default = False ),
3837 "tsc" : attr .label (default = Label (_DEFAULT_TSC ), executable = True , cfg = "exec" ),
39- "transpile" : attr .bool (doc = "whether tsc should be used to produce .js outputs" ),
38+ "transpile" : attr .bool (doc = "whether tsc should be used to produce .js outputs" , default = True ),
4039 "tsconfig" : attr .label (mandatory = True , allow_single_file = [".json" ]),
41- }
40+ })
4241
4342# tsc knows how to produce the following kinds of output files.
4443# NB: the macro `ts_project_macro` will set these outputs based on user
@@ -57,6 +56,19 @@ def _join(*elements):
5756 return "/" .join (segments )
5857 return "."
5958
59+ def _relative_to_package (path , ctx ):
60+ for prefix in (ctx .bin_dir .path , ctx .label .package ):
61+ prefix += "/"
62+ if path .startswith (prefix ):
63+ path = path [len (prefix ):]
64+ return path
65+
66+ def _declare_outputs (ctx , paths ):
67+ return [
68+ ctx .actions .declare_file (path )
69+ for path in paths
70+ ]
71+
6072def _calculate_root_dir (ctx ):
6173 some_generated_path = None
6274 some_source_path = None
@@ -91,6 +103,19 @@ def _calculate_root_dir(ctx):
91103 )
92104
93105def _ts_project_impl (ctx ):
106+ srcs = [_relative_to_package (src .path , ctx ) for src in ctx .files .srcs ]
107+
108+ # Recalculate outputs inside the rule implementation.
109+ # The outs are first calculated in the macro in order to try to predetermine outputs so they can be declared as
110+ # outputs on the rule. This provides the benefit of being able to reference an output file with a label.
111+ # However, it is not possible to evaluate files in outputs of other rules such as filegroup, therefore the outs are
112+ # recalculated here.
113+ typings_out_dir = ctx .attr .declaration_dir or ctx .attr .out_dir
114+ js_outs = _declare_outputs (ctx , [] if not ctx .attr .transpile else _calculate_js_outs (srcs , ctx .attr .out_dir , ctx .attr .root_dir , ctx .attr .allow_js , ctx .attr .preserve_jsx , ctx .attr .emit_declaration_only ))
115+ map_outs = _declare_outputs (ctx , [] if not ctx .attr .transpile else _calculate_map_outs (srcs , ctx .attr .out_dir , ctx .attr .root_dir , ctx .attr .source_map , ctx .attr .preserve_jsx , ctx .attr .emit_declaration_only ))
116+ typings_outs = _declare_outputs (ctx , _calculate_typings_outs (srcs , typings_out_dir , ctx .attr .root_dir , ctx .attr .declaration , ctx .attr .composite , ctx .attr .allow_js ))
117+ typing_maps_outs = _declare_outputs (ctx , _calculate_typing_maps_outs (srcs , typings_out_dir , ctx .attr .root_dir , ctx .attr .declaration_map , ctx .attr .allow_js ))
118+
94119 arguments = ctx .actions .args ()
95120 execution_requirements = {}
96121 progress_prefix = "Compiling TypeScript project"
@@ -114,7 +139,7 @@ def _ts_project_impl(ctx):
114139 "--rootDir" ,
115140 _calculate_root_dir (ctx ),
116141 ])
117- if len (ctx . outputs . typings_outs ) > 0 :
142+ if len (typings_outs ) > 0 :
118143 declaration_dir = ctx .attr .declaration_dir if ctx .attr .declaration_dir else ctx .attr .out_dir
119144 arguments .add_all ([
120145 "--declarationDir" ,
@@ -162,7 +187,7 @@ def _ts_project_impl(ctx):
162187 # However tsc will copy .json srcs to the output tree so we want to declare these outputs to include along with .js Default outs
163188 # NB: We don't have emit_declaration_only setting here, so use presence of any JS outputs as an equivalent.
164189 # tsc will only produce .json if it also produces .js
165- if len (ctx . outputs . js_outs ):
190+ if len (js_outs ):
166191 pkg_len = len (ctx .label .package ) + 1 if len (ctx .label .package ) else 0
167192 json_outs = [
168193 ctx .actions .declare_file (_join (ctx .attr .out_dir , src .short_path [pkg_len :]))
@@ -172,15 +197,30 @@ def _ts_project_impl(ctx):
172197 else :
173198 json_outs = []
174199
175- outputs = json_outs + ctx . outputs . js_outs + ctx . outputs . map_outs + ctx . outputs . typings_outs + ctx . outputs . typing_maps_outs
200+ outputs = json_outs + js_outs + map_outs + typings_outs + typing_maps_outs
176201 if ctx .outputs .buildinfo_out :
177202 arguments .add_all ([
178203 "--tsBuildInfoFile" ,
179204 ctx .outputs .buildinfo_out .path ,
180205 ])
181206 outputs .append (ctx .outputs .buildinfo_out )
182- runtime_outputs = json_outs + ctx .outputs .js_outs + ctx .outputs .map_outs
183- typings_outputs = ctx .outputs .typings_outs + ctx .outputs .typing_maps_outs + [s for s in ctx .files .srcs if s .path .endswith (".d.ts" )]
207+ runtime_outputs = json_outs + js_outs + map_outs
208+ typings_outputs = typings_outs + typing_maps_outs + [s for s in ctx .files .srcs if s .path .endswith (".d.ts" )]
209+
210+ if not js_outs and not typings_outputs and not ctx .attr .deps :
211+ label = "//{}:{}" .format (ctx .label .package , ctx .label .name )
212+ if ctx .attr .transpile :
213+ no_outs_msg = """ts_project target %s is configured to produce no outputs.
214+
215+ This might be because
216+ - you configured it with `noEmit`
217+ - the `srcs` are empty
218+ """ % label
219+ else :
220+ no_outs_msg = "ts_project target %s with custom transpiler needs `declaration = True`." % label
221+ fail (no_outs_msg + """
222+ This is an error because Bazel does not run actions unless their outputs are needed for the requested targets to build.
223+ """ )
184224
185225 if ctx .attr .transpile :
186226 default_outputs_depset = depset (runtime_outputs ) if len (runtime_outputs ) else depset (typings_outputs )
@@ -274,21 +314,59 @@ def _replace_ext(f, ext_map):
274314 return new_ext
275315 return None
276316
277- def _out_paths (srcs , outdir , rootdir , allow_js , ext_map ):
278- rootdir_replace_pattern = rootdir + "/" if rootdir else ""
317+ def _out_paths (srcs , out_dir , root_dir , allow_js , ext_map ):
318+ rootdir_replace_pattern = root_dir + "/" if root_dir else ""
279319 outs = []
280320 for f in srcs :
281321 if _is_ts_src (f , allow_js ):
282- out = _join (outdir , f [:f .rindex ("." )].replace (rootdir_replace_pattern , "" ) + _replace_ext (f , ext_map ))
322+ out = _join (out_dir , f [:f .rindex ("." )].replace (rootdir_replace_pattern , "" ) + _replace_ext (f , ext_map ))
283323
284324 # Don't declare outputs that collide with inputs
285325 # for example, a.js -> a.js
286326 if out != f :
287327 outs .append (out )
288328 return outs
289329
330+ def _calculate_js_outs (srcs , out_dir , root_dir , allow_js , preserve_jsx , emit_declaration_only ):
331+ if emit_declaration_only :
332+ return []
333+
334+ exts = {
335+ "*" : ".js" ,
336+ ".jsx" : ".jsx" ,
337+ ".tsx" : ".jsx" ,
338+ } if preserve_jsx else {"*" : ".js" }
339+ return _out_paths (srcs , out_dir , root_dir , allow_js , exts )
340+
341+ def _calculate_map_outs (srcs , out_dir , root_dir , source_map , preserve_jsx , emit_declaration_only ):
342+ if not source_map or emit_declaration_only :
343+ return []
344+
345+ exts = {
346+ "*" : ".js.map" ,
347+ ".tsx" : ".jsx.map" ,
348+ } if preserve_jsx else {"*" : ".js.map" }
349+ return _out_paths (srcs , out_dir , root_dir , False , exts )
350+
351+ def _calculate_typings_outs (srcs , typings_out_dir , root_dir , declaration , composite , allow_js , include_srcs = True ):
352+ if not (declaration or composite ):
353+ return []
354+
355+ return _out_paths (srcs , typings_out_dir , root_dir , allow_js , {"*" : ".d.ts" })
356+
357+ def _calculate_typing_maps_outs (srcs , typings_out_dir , root_dir , declaration_map , allow_js ):
358+ if not declaration_map :
359+ return []
360+
361+ exts = {"*" : ".d.ts.map" }
362+ return _out_paths (srcs , typings_out_dir , root_dir , allow_js , exts )
363+
290364lib = struct (
291365 is_ts_src = _is_ts_src ,
292366 is_json_src = _is_json_src ,
293367 out_paths = _out_paths ,
368+ calculate_js_outs = _calculate_js_outs ,
369+ calculate_map_outs = _calculate_map_outs ,
370+ calculate_typings_outs = _calculate_typings_outs ,
371+ calculate_typing_maps_outs = _calculate_typing_maps_outs ,
294372)
0 commit comments