formatjs_message_collector_aspect follows configured deps and srcs, detects
Intl usage, and carries source plans and explicit catalogs in depsets. Extraction
runs only at selected roots. Libraries do not flatten transitive source sets.
Define build/i18n.bzl:
load("@rules_formatjs//formatjs:defs.bzl", "formatjs_message_collector_aspect")
rust_messages = formatjs_message_collector_aspect(
name = "rust_messages",
runtime_labels = [Label("@my_crates//:formatjs_intl")],
source_extensions = ["rs"],
id_interpolation_pattern = "[sha512:contenthash:base64:10]",
extract_source_location = True,
flatten = True,
)Select the extraction root explicitly:
bazel build //app:main \
--aspects=//build:i18n.bzl%rust_messages \
--aspects_parameters=formatjs_root=//app:main \
--output_groups=all_messagesformatjs_root must be the actual target's absolute canonical label, not an alias
or relative label. It scopes materialization to that consumer. Other nodes only
propagate source plans and existing catalogs.
Runtime labels must be caller-created Label objects. This preserves the caller's
repository mappings. Aliases, including configurable aliases, are resolved in the
target configuration through private resolver dependencies. They are not traversed
for Intl detection, so configuring a resolver does not opt unrelated graphs into
extraction. Their files are not extraction inputs and are not built for detection.
Resolution can configure runtime dependencies in otherwise unrelated graphs; those
configured targets are shared within each configuration.
Alternatively, supply is_runtime(target, ctx) to inspect native language providers
or a FormatjsRuntimeInfo marker. Predicate-only adapters add no resolver dependency
on the runtime. No language build rules are imported by this ruleset.
Rule-propagated aspects use a collect_at(target, ctx) predicate instead of the CLI
parameter. Choose application or catalog-consumer roots, not every library:
def _at_application(_target, ctx):
return ctx.label == Label("//app:main")
rust_messages = formatjs_message_collector_aspect(
name = "rust_messages",
runtime_labels = [Label("@my_crates//:formatjs_intl")],
source_extensions = ["rs"],
collect_at = _at_application,
extract_source_location = True,
)This configuration also works from the CLI without --aspects_parameters.
Consumers access FormatjsMessagesInfo.catalogs on a selected root:
load("@rules_formatjs//formatjs:defs.bzl", "FormatjsMessagesInfo")
def _catalogs_impl(ctx):
return [DefaultInfo(files = ctx.attr.target[FormatjsMessagesInfo].catalogs)]
source_catalogs = rule(
implementation = _catalogs_impl,
attrs = {"target": attr.label(aspects = [rust_messages])},
)The public provider has two fields: catalogs and uses_runtime. At intermediate
nodes, catalogs contains only explicit catalogs. A private provider carries source
plans and explicit coverage; consumers do not depend on that internal state.
Roots flatten candidate and covered sources once. Ownership, extension and explicit coverage filters run before extraction actions are registered. Excluded generated artifacts are not action inputs, so their producers do not run for catalog builds. Partial explicit metadata excludes only its declared sources.
Without source locations, uncovered sources use one batch extraction per root.
With extract_source_location = True, each file gets its own catalog: the native
CLI keeps only one location for duplicate IDs within a batch. Per-file catalogs
preserve declarations across files and allow unchanged files to reuse cached
extraction actions. Duplicate occurrences within one file retain the native CLI's
behavior. Catalog order is deterministic; no intermediate merge actions are added.
Consumers choose their duplicate-ID and source-location merge policies. Output-name
hash collisions receive deterministic suffixes instead of failing analysis.
Existing FormatjsExtractInfo catalogs are collected unchanged. Automatic outputs
are exposed through FormatjsMessagesInfo and all_messages; they do not replace
or add a singular FormatjsExtractInfo on the underlying target.
Automatic mode requires only the native FormatJS CLI toolchain. No host shell,
Python or filtering toolchain is used. Without runtime labels or a predicate, the
factory only collects existing catalogs and requires no CLI toolchain. The existing
message_collector_aspect entrypoint keeps that collection-only behavior.
By default, only targets in the main repository contribute new source candidates.
External dependencies can enable extraction and provide explicit catalogs without
having their implementation scanned. Override should_extract(target, ctx) to
change that target boundary. Rejected sources stay excluded when ancestors forward
them through source wrappers. Independent allowed declarations of the same source
remain eligible. Source plans retain configured identity, so allowing a generator
in one configuration cannot enable its rejected output in another. Plans stay in
depsets until the extraction root; each configured node is visited once there.
Only files owned by a contributing target's repository are eligible, including
when source labels point into another repository.
source_extensions defaults to ts, tsx, js, jsx, mjs, cjs, rs, and py.
dependency_attributes controls detection/collection edges; source_attributes
controls source inputs and is also traversed for extraction metadata. Both label
and label-list attributes work. Aliases and select() are handled after resolution.
Other keyword arguments are options accepted by formatjs_extract_action.
Use one configured aspect per collection pass. Mixed-language adapters can combine
runtime recognition and source extensions in that aspect. Give separate instances
unique name values to distinguish output files.
Archive ownership, translation projection, catalog merging and runtime installation remain consumer decisions.
formatjs_extract_action(ctx, srcs, out, **options) registers the native action and
returns FormatjsExtractInfo. srcs accepts a list or depset of source Files; the
caller declares the output and CLI toolchain. The existing formatjs_extract rule
uses the same helper, including its options and response-file handling.