Expose declarations only via DeclarationInfo
PiperOrigin-RevId: 289484723
diff --git a/internal/common/compilation.bzl b/internal/common/compilation.bzl
index c11b71c..514202a 100644
--- a/internal/common/compilation.bzl
+++ b/internal/common/compilation.bzl
@@ -55,7 +55,13 @@
default = [],
providers = [JsInfo],
),
- "deps": attr.label_list(aspects = DEPS_ASPECTS),
+ "deps": attr.label_list(
+ aspects = DEPS_ASPECTS,
+ # TODO(b/139705078): require all deps have a DeclarationInfo provider
+ # and remove assert_js_or_typescript_deps which is attempting
+ # to enforce the same thing as this.
+ # providers = [DeclarationInfo],
+ ),
"_additional_d_ts": _ADDITIONAL_D_TS,
}
@@ -75,85 +81,48 @@
# Fallback to `ctx.attr.deps`.
deps = deps if deps != None else ctx.attr.deps
for dep in deps:
- if not hasattr(dep, "typescript") and not JsInfo in dep:
+ if not hasattr(dep, "typescript") and not JsInfo in dep and not DeclarationInfo in dep:
allowed_deps_msg = "Dependencies must be ts_library"
fail("%s is neither a TypeScript nor a JS producing rule.\n%s\n" % (dep.label, allowed_deps_msg))
_DEPSET_TYPE = type(depset())
-def _check_ts_provider(dep):
- """Verifies the type shape of the typescript provider in dep, if it has one.
- """
-
- # Under Bazel, some third parties have created typescript providers which may not be compatible.
- # Rather than users getting an obscure error later, explicitly check them and point to the
- # target that created the bad provider.
- # TODO(alexeagle): remove this after some transition period, maybe mid-2019
- if hasattr(dep, "typescript"):
- if type(dep.typescript.declarations) != _DEPSET_TYPE:
- fail("typescript provider in %s defined declarations as a %s rather than a depset" % (
- dep.label,
- type(dep.typescript.declarations),
- ))
- if type(dep.typescript.transitive_declarations) != _DEPSET_TYPE:
- fail("typescript provider in %s defined transitive_declarations as a %s rather than a depset" % (
- dep.label,
- type(dep.typescript.transitive_declarations),
- ))
- if type(dep.typescript.type_blacklisted_declarations) != _DEPSET_TYPE:
- fail("typescript provider in %s defined type_blacklisted_declarations as a %s rather than a depset" % (
- dep.label,
- type(dep.typescript.type_blacklisted_declarations),
- ))
- return dep
-
-def _collect_dep_declarations(ctx, deps):
- """Collects .d.ts files from typescript and javascript dependencies.
+def _collect_dep_declarations(ctx, declaration_infos):
+ """Flattens DeclarationInfo from typescript and javascript dependencies.
Args:
ctx: ctx.
- deps: dependent targets, generally ctx.attr.deps
+ declaration_infos: list of DeclarationInfo collected from dependent targets
Returns:
A struct of depsets for direct, transitive and type-blacklisted declarations.
"""
- deps_and_helpers = [
- _check_ts_provider(dep)
- for dep in deps + getattr(ctx.attr, "_helpers", [])
- if hasattr(dep, "typescript")
- ]
-
# .d.ts files from direct dependencies, ok for strict deps
- direct_deps_declarations = [dep.typescript.declarations for dep in deps_and_helpers]
+ direct_deps_declarations = [dep.declarations for dep in declaration_infos]
# all reachable .d.ts files from dependencies.
- transitive_deps_declarations = [
- dep.typescript.transitive_declarations
- for dep in deps_and_helpers
- ]
+ transitive_deps_declarations = [dep.transitive_declarations for dep in declaration_infos]
# all reachable .d.ts files from node_modules attribute (if it has a typescript provider)
- if hasattr(ctx.attr, "node_modules") and hasattr(ctx.attr.node_modules, "typescript"):
- transitive_deps_declarations += [ctx.attr.node_modules.typescript.transitive_declarations]
+ if hasattr(ctx.attr, "node_modules"):
+ if DeclarationInfo in ctx.attr.node_modules:
+ transitive_deps_declarations.append(ctx.attr.node_modules[DeclarationInfo].transitive_declarations)
+ elif hasattr(ctx.attr.node_modules, "typescript"):
+ # TODO(b/139705078): remove this case after bazel BUILD file generation for node_modules is updated
+ transitive_deps_declarations.append([ctx.attr.node_modules.typescript.transitive_declarations])
# .d.ts files whose types tsickle will not emit (used for ts_declaration(generate_externs=False).
- type_blacklisted_declarations = [
- dep.typescript.type_blacklisted_declarations
- for dep in deps_and_helpers
- ]
+ type_blacklisted_declarations = [dep.type_blacklisted_declarations for dep in declaration_infos]
# If a tool like github.com/angular/clutz can create .d.ts from type annotated .js
# its output will be collected here.
- return struct(
- direct = depset(transitive = direct_deps_declarations),
- transitive = depset(
- [extra for extra in ctx.files._additional_d_ts],
- transitive = transitive_deps_declarations,
- ),
- type_blacklisted = depset(transitive = type_blacklisted_declarations),
+ return DeclarationInfo(
+ declarations = depset(transitive = direct_deps_declarations),
+ transitive_declarations = depset(ctx.files._additional_d_ts, transitive = transitive_deps_declarations),
+ type_blacklisted_declarations = depset(transitive = type_blacklisted_declarations),
)
def _should_generate_externs(ctx):
@@ -224,7 +193,7 @@
ctx,
is_library,
srcs = None,
- deps = None,
+ declaration_infos = None,
compile_action = None,
devmode_compile_action = None,
jsx_factory = None,
@@ -239,7 +208,7 @@
ctx: ctx.
is_library: boolean. False if only compiling .dts files.
srcs: label list. Explicit list of sources to be used instead of ctx.attr.srcs.
- deps: label list. Explicit list of deps to be used instead of ctx.attr.deps.
+ declaration_infos: list of DeclarationInfo. Explicit list of declarations to be used instead of those on ctx.attr.deps.
compile_action: function. Creates the compilation action.
devmode_compile_action: function. Creates the compilation action
for devmode.
@@ -254,16 +223,28 @@
### Collect srcs and outputs.
srcs = srcs if srcs != None else ctx.attr.srcs
- deps = deps if deps != None else ctx.attr.deps
+ if declaration_infos == None:
+ if not hasattr(ctx.attr, "deps"):
+ fail("compile_ts must either be called from a rule with a deps attr, or must be given declaration_infos")
+
+ # Validate the user inputs.
+ # TODO(b/139705078): remove this when we require DeclarationInfo provider on deps
+ assert_js_or_typescript_deps(ctx, ctx.attr.deps)
+
+ # By default, we collect dependencies from the ctx, when used as a rule
+ declaration_infos = [
+ d[DeclarationInfo]
+ for d in ctx.attr.deps + getattr(ctx.attr, "_helpers", [])
+ # TODO(b/139705078): remove this when we require DeclarationInfo provider on deps
+ if DeclarationInfo in d
+ ]
+
tsconfig = tsconfig if tsconfig != None else ctx.outputs.tsconfig
srcs_files = [f for t in srcs for f in t.files.to_list()]
src_declarations = [] # d.ts found in inputs.
tsickle_externs = [] # externs.js generated by tsickle, if any.
has_sources = False
- # Validate the user inputs.
- assert_js_or_typescript_deps(ctx, deps)
-
for src in srcs:
if src.label.package != ctx.label.package:
# Sources can be in sub-folders, but not in sub-packages.
@@ -298,9 +279,9 @@
# Note: setting this variable controls whether tsickle is run at all.
tsickle_externs = [ctx.actions.declare_file(ctx.label.name + ".externs.js")]
- dep_declarations = _collect_dep_declarations(ctx, deps)
- input_declarations = depset(src_declarations, transitive = [dep_declarations.transitive])
- type_blacklisted_declarations = dep_declarations.type_blacklisted
+ dep_declarations = _collect_dep_declarations(ctx, declaration_infos)
+
+ type_blacklisted_declarations = dep_declarations.type_blacklisted_declarations
if not is_library and not _should_generate_externs(ctx):
type_blacklisted_declarations = depset(srcs_files, transitive = [type_blacklisted_declarations])
@@ -319,7 +300,7 @@
if "TYPESCRIPT_PERF_TRACE_TARGET" in ctx.var:
perf_trace = str(ctx.label) == ctx.var["TYPESCRIPT_PERF_TRACE_TARGET"]
- compilation_inputs = dep_declarations.transitive.to_list() + srcs_files
+ compilation_inputs = dep_declarations.transitive_declarations.to_list() + srcs_files
tsickle_externs_path = tsickle_externs[0] if tsickle_externs else None
# Calculate allowed dependencies for strict deps enforcement.
@@ -327,7 +308,7 @@
# A target's sources may depend on each other,
srcs_files,
# or on a .d.ts from a direct dependency
- transitive = [dep_declarations.direct],
+ transitive = [dep_declarations.declarations],
)
tsconfig_es6 = tsc_wrapped_tsconfig(
@@ -429,7 +410,7 @@
# TODO(martinprobst): Merge the generated .d.ts files, and enforce strict
# deps (do not re-export transitive types from the transitive closure).
- transitive_decls = depset(src_declarations + gen_declarations, transitive = [dep_declarations.transitive])
+ transitive_decls = depset(src_declarations + gen_declarations, transitive = [dep_declarations.transitive_declarations])
# both ts_library and ts_declarations generate .mjs files:
# - for libraries, this is the ES6/production code
@@ -451,9 +432,8 @@
if not srcs_files:
# Re-export sources from deps.
# TODO(b/30018387): introduce an "exports" attribute.
- for dep in deps:
- if hasattr(dep, "typescript"):
- declarations_depsets.append(dep.typescript.declarations)
+ for dep in declaration_infos:
+ declarations_depsets.append(dep.declarations)
files_depsets.extend(declarations_depsets)
# If this is a ts_declaration, add tsickle_externs to the outputs list to
@@ -463,11 +443,17 @@
files_depsets.append(depset(tsickle_externs))
transitive_es6_sources_sets = [es6_sources]
- for dep in deps:
+ for dep in getattr(ctx.attr, "deps", []):
if hasattr(dep, "typescript"):
transitive_es6_sources_sets += [dep.typescript.transitive_es6_sources]
transitive_es6_sources = depset(transitive = transitive_es6_sources_sets)
+ declarations_provider = DeclarationInfo(
+ declarations = depset(transitive = declarations_depsets),
+ transitive_declarations = transitive_decls,
+ type_blacklisted_declarations = type_blacklisted_declarations,
+ )
+
return {
"providers": [
DefaultInfo(
@@ -484,12 +470,11 @@
es5_sources = es5_sources,
es6_sources = es6_sources,
),
- # TODO(martinprobst): Prune transitive deps, see go/dtspruning
- DeclarationInfo(
- declarations = depset(transitive = declarations_depsets),
- transitive_declarations = transitive_decls,
- ),
+ declarations_provider,
],
+ # Also expose the DeclarationInfo as a named provider so that aspect implementations can reference it
+ # Otherwise they would be forced to reference it by a numeric index out of the "providers" list above.
+ "declarations": declarations_provider,
"instrumented_files": {
"dependency_attributes": ["deps", "runtime_deps"],
"extensions": ["ts"],
@@ -502,21 +487,16 @@
# Expose the tags so that a Skylark aspect can access them.
"tags": ctx.attr.tags if hasattr(ctx.attr, "tags") else ctx.rule.attr.tags,
"typescript": {
- # TODO(b/139705078): remove when consumers migrated to DeclarationInfo
- "declarations": depset(transitive = declarations_depsets),
"devmode_manifest": devmode_manifest,
"es5_sources": es5_sources,
"es6_sources": es6_sources,
"replay_params": replay_params,
- # TODO(b/139705078): remove when consumers migrated to DeclarationInfo
- "transitive_declarations": transitive_decls,
"transitive_es6_sources": transitive_es6_sources,
"tsickle_externs": tsickle_externs,
- "type_blacklisted_declarations": type_blacklisted_declarations,
},
}
-def ts_providers_dict_to_struct(d, preserve_js_struct_field = False):
+def ts_providers_dict_to_struct(d):
""" Converts a dict to a struct, recursing into a single level of nested dicts.
This allows users of compile_ts to modify or augment the returned dict before
@@ -524,17 +504,30 @@
Args:
d: the dict to convert
- preserve_js_struct_field: whether to preserve the js provider as a "js" field.
- Please only set this to True if you are using this struct in another provider.
- e.g. MyProvider(some_field = ts_providers_dict_to_struct(d, preserve_js_struct_field = True))
- *Do not use* if returning the struct from a rule.
Returns:
An immutable struct created from the input dict
"""
+ # These keys are present in the dict so that aspects can reference them,
+ # however they should not be output as legacy providers since we have modern
+ # symbol-typed providers for them.
+ js_provider = d.pop("js", None)
+ declarations_provider = d.pop("declarations", None)
+
+ # Promote the "js" string-typed provider to a modern provider
+ if js_provider:
+ # Create a new providers list rather than modify the existing list
+ d["providers"] = d.get("providers", []) + [js_provider]
for key, value in d.items():
if key != "output_groups" and type(value) == type({}):
d[key] = struct(**value)
- return struct(**d)
+ result = struct(**d)
+
+ # Restore the elements we removed, to avoid side-effect of mutating the argument
+ if js_provider:
+ d["js"] = js_provider
+ if declarations_provider:
+ d["declarations"] = declarations_provider
+ return result